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