github.com/cosmos/cosmos-sdk@v0.50.1/crypto/keys/multisig/multisig_test.go (about)

     1  package multisig_test
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"cosmossdk.io/core/address"
    10  	"cosmossdk.io/depinject"
    11  	"cosmossdk.io/log"
    12  
    13  	"github.com/cosmos/cosmos-sdk/codec"
    14  	addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
    15  	"github.com/cosmos/cosmos-sdk/codec/legacy"
    16  	"github.com/cosmos/cosmos-sdk/codec/types"
    17  	cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
    18  	kmultisig "github.com/cosmos/cosmos-sdk/crypto/keys/multisig"
    19  	"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
    20  	cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
    21  	"github.com/cosmos/cosmos-sdk/crypto/types/multisig"
    22  	"github.com/cosmos/cosmos-sdk/runtime"
    23  	"github.com/cosmos/cosmos-sdk/testutil/configurator"
    24  	"github.com/cosmos/cosmos-sdk/types/tx/signing"
    25  	"github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx"
    26  )
    27  
    28  func TestNewMultiSig(t *testing.T) {
    29  	require := require.New(t)
    30  	pk1 := secp256k1.GenPrivKey().PubKey()
    31  	pks := []cryptotypes.PubKey{pk1, pk1}
    32  
    33  	require.NotNil(kmultisig.NewLegacyAminoPubKey(1, pks),
    34  		"Should support not unique public keys")
    35  }
    36  
    37  func TestAddress(t *testing.T) {
    38  	pubKeys := generatePubKeys(5)
    39  	multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubKeys)
    40  
    41  	require.Len(t, multisigKey.Address().Bytes(), 20)
    42  }
    43  
    44  func TestEquals(t *testing.T) {
    45  	pubKey1 := secp256k1.GenPrivKey().PubKey()
    46  	pubKey2 := secp256k1.GenPrivKey().PubKey()
    47  
    48  	multisigKey := kmultisig.NewLegacyAminoPubKey(1, []cryptotypes.PubKey{pubKey1, pubKey2})
    49  	otherMultisigKey := kmultisig.NewLegacyAminoPubKey(1, []cryptotypes.PubKey{pubKey1, multisigKey})
    50  
    51  	testCases := []struct {
    52  		msg      string
    53  		other    cryptotypes.PubKey
    54  		expectEq bool
    55  	}{
    56  		{
    57  			"equals with proto pub key",
    58  			&kmultisig.LegacyAminoPubKey{Threshold: 1, PubKeys: multisigKey.PubKeys},
    59  			true,
    60  		},
    61  		{
    62  			"different threshold",
    63  			&kmultisig.LegacyAminoPubKey{Threshold: 2, PubKeys: multisigKey.PubKeys},
    64  			false,
    65  		},
    66  		{
    67  			"different pub keys length",
    68  			&kmultisig.LegacyAminoPubKey{Threshold: 1, PubKeys: []*types.Any{multisigKey.PubKeys[0]}},
    69  			false,
    70  		},
    71  		{
    72  			"different pub keys",
    73  			otherMultisigKey,
    74  			false,
    75  		},
    76  		{
    77  			"different types",
    78  			secp256k1.GenPrivKey().PubKey(),
    79  			false,
    80  		},
    81  		{
    82  			"ensure that reordering pubkeys is treated as a different pubkey",
    83  			reorderPubKey(multisigKey),
    84  			false,
    85  		},
    86  	}
    87  
    88  	for _, tc := range testCases {
    89  		t.Run(tc.msg, func(t *testing.T) {
    90  			eq := multisigKey.Equals(tc.other)
    91  			require.Equal(t, eq, tc.expectEq)
    92  		})
    93  	}
    94  }
    95  
    96  func TestVerifyMultisignature(t *testing.T) {
    97  	var (
    98  		pk  multisig.PubKey
    99  		sig *signing.MultiSignatureData
   100  	)
   101  	msg := []byte{1, 2, 3, 4}
   102  	signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil }
   103  
   104  	testCases := []struct {
   105  		msg        string
   106  		malleate   func(*require.Assertions)
   107  		expectPass bool
   108  	}{
   109  		{
   110  			"nested multisignature",
   111  			func(require *require.Assertions) {
   112  				genPk, genSig := generateNestedMultiSignature(3, msg)
   113  				sig = genSig
   114  				pk = genPk
   115  			},
   116  			true,
   117  		},
   118  		{
   119  			"wrong size for sig bit array",
   120  			func(require *require.Assertions) {
   121  				pubKeys := generatePubKeys(3)
   122  				pk = kmultisig.NewLegacyAminoPubKey(3, pubKeys)
   123  				sig = multisig.NewMultisig(1)
   124  			},
   125  			false,
   126  		},
   127  		{
   128  			"single signature data, expects the first k signatures to be valid",
   129  			func(require *require.Assertions) {
   130  				k := 2
   131  				signingIndices := []int{0, 3, 1}
   132  				pubKeys, sigs := generatePubKeysAndSignatures(8, msg)
   133  				pk = kmultisig.NewLegacyAminoPubKey(k, pubKeys)
   134  				sig = multisig.NewMultisig(len(pubKeys))
   135  				signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil }
   136  
   137  				for i := 0; i < k-1; i++ {
   138  					signingIndex := signingIndices[i]
   139  					require.NoError(
   140  						multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys),
   141  					)
   142  					require.Error(
   143  						pk.VerifyMultisignature(signBytesFn, sig),
   144  						"multisig passed when i < k, i %d", i,
   145  					)
   146  					require.NoError(
   147  						multisig.AddSignatureFromPubKey(sig, sigs[signingIndex], pubKeys[signingIndex], pubKeys),
   148  					)
   149  					require.Equal(
   150  						i+1,
   151  						len(sig.Signatures),
   152  						"adding a signature for the same pubkey twice increased signature count by 2, index %d", i,
   153  					)
   154  				}
   155  				require.Error(
   156  					pk.VerifyMultisignature(signBytesFn, sig),
   157  					"multisig passed with k - 1 sigs",
   158  				)
   159  				require.NoError(
   160  					multisig.AddSignatureFromPubKey(
   161  						sig,
   162  						sigs[signingIndices[k]],
   163  						pubKeys[signingIndices[k]],
   164  						pubKeys,
   165  					),
   166  				)
   167  				require.NoError(
   168  					pk.VerifyMultisignature(signBytesFn, sig),
   169  					"multisig failed after k good signatures",
   170  				)
   171  			},
   172  			true,
   173  		},
   174  		{
   175  			"duplicate signatures",
   176  			func(require *require.Assertions) {
   177  				pubKeys, sigs := generatePubKeysAndSignatures(5, msg)
   178  				pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys)
   179  				sig = multisig.NewMultisig(5)
   180  
   181  				require.Error(pk.VerifyMultisignature(signBytesFn, sig))
   182  				require.NoError(multisig.AddSignatureFromPubKey(sig, sigs[0], pubKeys[0], pubKeys))
   183  				// Add second signature manually
   184  				sig.Signatures = append(sig.Signatures, sigs[0])
   185  			},
   186  			false,
   187  		},
   188  		{
   189  			"duplicated key",
   190  			func(require *require.Assertions) {
   191  				// here we test an edge case where we create a multi sig with two same
   192  				// keys. It  should work.
   193  				pubkeys, sigs := generatePubKeysAndSignatures(3, msg)
   194  				pubkeys[1] = pubkeys[0]
   195  				pk = kmultisig.NewLegacyAminoPubKey(2, pubkeys)
   196  				sig = multisig.NewMultisig(len(pubkeys))
   197  				multisig.AddSignature(sig, sigs[0], 0)
   198  				multisig.AddSignature(sig, sigs[0], 1)
   199  			},
   200  			true,
   201  		},
   202  		{
   203  			"same key used twice",
   204  			func(require *require.Assertions) {
   205  				pubkeys, sigs := generatePubKeysAndSignatures(3, msg)
   206  				pk = kmultisig.NewLegacyAminoPubKey(2, pubkeys)
   207  				sig = multisig.NewMultisig(len(pubkeys))
   208  				multisig.AddSignature(sig, sigs[0], 0)
   209  				multisig.AddSignature(sig, sigs[0], 1)
   210  			},
   211  			false,
   212  		},
   213  		{
   214  			"unable to verify signature",
   215  			func(require *require.Assertions) {
   216  				pubKeys := generatePubKeys(2)
   217  				_, sigs := generatePubKeysAndSignatures(2, msg)
   218  				pk = kmultisig.NewLegacyAminoPubKey(2, pubKeys)
   219  				sig = multisig.NewMultisig(2)
   220  				require.NoError(multisig.AddSignatureFromPubKey(sig, sigs[0], pubKeys[0], pubKeys))
   221  				require.NoError(multisig.AddSignatureFromPubKey(sig, sigs[1], pubKeys[1], pubKeys))
   222  			},
   223  			false,
   224  		},
   225  	}
   226  
   227  	for _, tc := range testCases {
   228  		t.Run(tc.msg, func(t *testing.T) {
   229  			tc.malleate(require.New(t))
   230  			err := pk.VerifyMultisignature(signBytesFn, sig)
   231  			if tc.expectPass {
   232  				require.NoError(t, err)
   233  			} else {
   234  				require.Error(t, err)
   235  			}
   236  		})
   237  	}
   238  }
   239  
   240  func TestAddSignatureFromPubKeyNilCheck(t *testing.T) {
   241  	pkSet, sigs := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4})
   242  	multisignature := multisig.NewMultisig(5)
   243  
   244  	// verify no error is returned with all non-nil values
   245  	err := multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], pkSet)
   246  	require.NoError(t, err)
   247  	// verify error is returned when key value is nil
   248  	err = multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], nil)
   249  	require.Error(t, err)
   250  	// verify error is returned when pubkey value is nil
   251  	err = multisig.AddSignatureFromPubKey(multisignature, sigs[0], nil, pkSet)
   252  	require.Error(t, err)
   253  	// verify error is returned when signature value is nil
   254  	err = multisig.AddSignatureFromPubKey(multisignature, nil, pkSet[0], pkSet)
   255  	require.Error(t, err)
   256  	// verify error is returned when multisignature value is nil
   257  	err = multisig.AddSignatureFromPubKey(nil, sigs[0], pkSet[0], pkSet)
   258  	require.Error(t, err)
   259  }
   260  
   261  func TestMultiSigMigration(t *testing.T) {
   262  	msg := []byte{1, 2, 3, 4}
   263  	pkSet, sigs := generatePubKeysAndSignatures(2, msg)
   264  	multisignature := multisig.NewMultisig(2)
   265  
   266  	multisigKey := kmultisig.NewLegacyAminoPubKey(2, pkSet)
   267  	signBytesFn := func(mode signing.SignMode) ([]byte, error) { return msg, nil }
   268  
   269  	cdc := codec.NewLegacyAmino()
   270  
   271  	require.NoError(t, multisig.AddSignatureFromPubKey(multisignature, sigs[0], pkSet[0], pkSet))
   272  
   273  	// create a StdSignature for msg, and convert it to sigV2
   274  	sig := legacytx.StdSignature{PubKey: pkSet[1], Signature: sigs[1].(*signing.SingleSignatureData).Signature} //nolint:staticcheck // SA1019: legacytx.StdSignature is deprecated: use Tx.Msgs, Signatures and Memo instead.
   275  	sigV2, err := legacytx.StdSignatureToSignatureV2(cdc, sig)
   276  	require.NoError(t, multisig.AddSignatureV2(multisignature, sigV2, pkSet))
   277  
   278  	require.NoError(t, err)
   279  	require.NotNil(t, sigV2)
   280  
   281  	require.NoError(t, multisigKey.VerifyMultisignature(signBytesFn, multisignature))
   282  }
   283  
   284  func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) {
   285  	pubkeys := generatePubKeys(5)
   286  	multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubkeys)
   287  
   288  	ab, err := legacy.Cdc.MarshalLengthPrefixed(multisigKey)
   289  	require.NoError(t, err)
   290  	// like other cryptotypes.Pubkey implementations (e.g. ed25519.PubKey),
   291  	// LegacyAminoPubKey should be deserializable into a cryptotypes.LegacyAminoPubKey:
   292  	var pubKey kmultisig.LegacyAminoPubKey
   293  	err = legacy.Cdc.UnmarshalLengthPrefixed(ab, &pubKey)
   294  	require.NoError(t, err)
   295  
   296  	require.Equal(t, multisigKey.Equals(&pubKey), true)
   297  }
   298  
   299  func generatePubKeys(n int) []cryptotypes.PubKey {
   300  	pks := make([]cryptotypes.PubKey, n)
   301  	for i := 0; i < n; i++ {
   302  		pks[i] = secp256k1.GenPrivKey().PubKey()
   303  	}
   304  	return pks
   305  }
   306  
   307  func generatePubKeysAndSignatures(n int, msg []byte) (pubKeys []cryptotypes.PubKey, signatures []signing.SignatureData) {
   308  	pubKeys = make([]cryptotypes.PubKey, n)
   309  	signatures = make([]signing.SignatureData, n)
   310  
   311  	for i := 0; i < n; i++ {
   312  		privkey := secp256k1.GenPrivKey()
   313  		pubKeys[i] = privkey.PubKey()
   314  
   315  		sig, _ := privkey.Sign(msg)
   316  		signatures[i] = &signing.SingleSignatureData{Signature: sig}
   317  	}
   318  	return
   319  }
   320  
   321  func generateNestedMultiSignature(n int, msg []byte) (multisig.PubKey, *signing.MultiSignatureData) {
   322  	pubKeys := make([]cryptotypes.PubKey, n)
   323  	signatures := make([]signing.SignatureData, n)
   324  	bitArray := cryptotypes.NewCompactBitArray(n)
   325  	for i := 0; i < n; i++ {
   326  		nestedPks, nestedSigs := generatePubKeysAndSignatures(5, msg)
   327  		nestedBitArray := cryptotypes.NewCompactBitArray(5)
   328  		for j := 0; j < 5; j++ {
   329  			nestedBitArray.SetIndex(j, true)
   330  		}
   331  		nestedSig := &signing.MultiSignatureData{
   332  			BitArray:   nestedBitArray,
   333  			Signatures: nestedSigs,
   334  		}
   335  		signatures[i] = nestedSig
   336  		pubKeys[i] = kmultisig.NewLegacyAminoPubKey(5, nestedPks)
   337  		bitArray.SetIndex(i, true)
   338  	}
   339  	return kmultisig.NewLegacyAminoPubKey(n, pubKeys), &signing.MultiSignatureData{
   340  		BitArray:   bitArray,
   341  		Signatures: signatures,
   342  	}
   343  }
   344  
   345  func reorderPubKey(pk *kmultisig.LegacyAminoPubKey) (other *kmultisig.LegacyAminoPubKey) {
   346  	pubkeysCpy := make([]*types.Any, len(pk.PubKeys))
   347  	copy(pubkeysCpy, pk.PubKeys)
   348  	pubkeysCpy[0] = pk.PubKeys[1]
   349  	pubkeysCpy[1] = pk.PubKeys[0]
   350  	other = &kmultisig.LegacyAminoPubKey{Threshold: 2, PubKeys: pubkeysCpy}
   351  	return
   352  }
   353  
   354  func TestDisplay(t *testing.T) {
   355  	require := require.New(t)
   356  	pubKeys := generatePubKeys(3)
   357  	msig := kmultisig.NewLegacyAminoPubKey(2, pubKeys)
   358  
   359  	require.NotEmpty(msig.String())
   360  	var cdc codec.Codec
   361  	err := depinject.Inject(
   362  		depinject.Configs(
   363  			configurator.NewAppConfig(),
   364  			depinject.Supply(log.NewNopLogger(),
   365  				func() address.Codec { return addresscodec.NewBech32Codec("cosmos") },
   366  				func() runtime.ValidatorAddressCodec { return addresscodec.NewBech32Codec("cosmosvaloper") },
   367  				func() runtime.ConsensusAddressCodec { return addresscodec.NewBech32Codec("cosmosvalcons") },
   368  			),
   369  		), &cdc)
   370  	require.NoError(err)
   371  	bz, err := cdc.MarshalInterfaceJSON(msig)
   372  	require.NoError(err)
   373  	expectedPrefix := `{"@type":"/cosmos.crypto.multisig.LegacyAminoPubKey","threshold":2,"public_keys":[{"@type":"/cosmos.crypto.secp256k1.PubKey"`
   374  	require.True(strings.HasPrefix(string(bz), expectedPrefix))
   375  	// Example output:
   376  	// {"@type":"/cosmos.crypto.multisig.LegacyAminoPubKey","threshold":2,"public_keys":[{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AymUY3J2HKIyy9cbpGKcBFUTuDQsRH9NO/orKF/0WQ76"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"AkvnCDzSYF+tQV/FoI217V7CDIRPzjJj7zBE2nw7x3xT"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A0yiqgcM5EB1i0h79+sQp+C0jLPFnT3+dFmdZmGa+H1s"}]}
   377  }
   378  
   379  func TestAminoBinary(t *testing.T) {
   380  	pubkeys := generatePubKeys(2)
   381  	msig := kmultisig.NewLegacyAminoPubKey(2, pubkeys)
   382  
   383  	// Do a round-trip key->bytes->key.
   384  	bz, err := legacy.Cdc.Marshal(msig)
   385  	require.NoError(t, err)
   386  	var newMsig cryptotypes.PubKey
   387  	err = legacy.Cdc.Unmarshal(bz, &newMsig)
   388  	require.NoError(t, err)
   389  	require.Equal(t, msig.Threshold, newMsig.(*kmultisig.LegacyAminoPubKey).Threshold)
   390  }
   391  
   392  func TestAminoMarshalJSON(t *testing.T) {
   393  	pubkeys := generatePubKeys(2)
   394  	multisigKey := kmultisig.NewLegacyAminoPubKey(2, pubkeys)
   395  	bz, err := legacy.Cdc.MarshalJSON(multisigKey)
   396  	require.NoError(t, err)
   397  
   398  	// Note the quotes around `"2"`. They are present because we are overriding
   399  	// the Amino JSON marshaling of LegacyAminoPubKey (using tmMultisig).
   400  	// Without the override, there would not be any quotes.
   401  	require.Contains(t, string(bz), "\"threshold\":\"2\"")
   402  }
   403  
   404  func TestAminoUnmarshalJSON(t *testing.T) {
   405  	// This is a real multisig from the Akash chain. It has been exported from
   406  	// v0.39, hence the `threshold` field as a string.
   407  	// We are testing that when unmarshaling this JSON into a LegacyAminoPubKey
   408  	// with amino, there's no error.
   409  	// ref: https://github.com/cosmos/cosmos-sdk/issues/8776
   410  	pkJSON := `{
   411  	"type": "tendermint/PubKeyMultisigThreshold",
   412  	"value": {
   413  		"pubkeys": [
   414  			{
   415  			"type": "tendermint/PubKeySecp256k1",
   416  			"value": "AzYxq2VNeD10TyABwOgV36OVWDIMn8AtI4OFA0uQX2MK"
   417  			},
   418  			{
   419  			"type": "tendermint/PubKeySecp256k1",
   420  			"value": "A39cdsrm00bTeQ3RVZVqjkH8MvIViO9o99c8iLiNO35h"
   421  			},
   422  			{
   423  			"type": "tendermint/PubKeySecp256k1",
   424  			"value": "A/uLLCZph8MkFg2tCxqSMGwFfPHdt1kkObmmrqy9aiYD"
   425  			},
   426  			{
   427  			"type": "tendermint/PubKeySecp256k1",
   428  			"value": "A4mOMhM5gPDtBAkAophjRs6uDGZm4tD4Dbok3ai4qJi8"
   429  			},
   430  			{
   431  			"type": "tendermint/PubKeySecp256k1",
   432  			"value": "A90icFucrjNNz2SAdJWMApfSQcARIqt+M2x++t6w5fFs"
   433  			}
   434  		],
   435  		"threshold": "3"
   436  	}
   437  }`
   438  
   439  	cdc := codec.NewLegacyAmino()
   440  	cryptocodec.RegisterCrypto(cdc)
   441  
   442  	var pk cryptotypes.PubKey
   443  	err := cdc.UnmarshalJSON([]byte(pkJSON), &pk)
   444  	require.NoError(t, err)
   445  	lpk := pk.(*kmultisig.LegacyAminoPubKey)
   446  	require.Equal(t, uint32(3), lpk.Threshold)
   447  	require.Equal(t, 5, len(pk.(*kmultisig.LegacyAminoPubKey).PubKeys))
   448  
   449  	for _, key := range pk.(*kmultisig.LegacyAminoPubKey).PubKeys {
   450  		require.NotNil(t, key)
   451  		pk := secp256k1.PubKey{}
   452  		err := pk.Unmarshal(key.Value)
   453  		require.NoError(t, err)
   454  	}
   455  }