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 }