github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/multisig/threshold_pubkey_test.go (about) 1 package multisig 2 3 import ( 4 "math/rand" 5 "testing" 6 7 "github.com/stretchr/testify/require" 8 9 "github.com/gnolang/gno/tm2/pkg/amino" 10 "github.com/gnolang/gno/tm2/pkg/crypto" 11 "github.com/gnolang/gno/tm2/pkg/crypto/ed25519" 12 "github.com/gnolang/gno/tm2/pkg/crypto/secp256k1" 13 ) 14 15 // This tests multisig functionality, but it expects the first k signatures to be valid 16 // TODO: Adapt it to give more flexibility about first k signatures being valid 17 func TestThresholdMultisigValidCases(t *testing.T) { 18 t.Parallel() 19 20 pkSet1, sigSet1 := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4}) 21 cases := []struct { 22 msg []byte 23 k int 24 pubkeys []crypto.PubKey 25 signingIndices []int 26 // signatures should be the same size as signingIndices. 27 signatures [][]byte 28 passAfterKSignatures []bool 29 }{ 30 { 31 msg: []byte{1, 2, 3, 4}, 32 k: 2, 33 pubkeys: pkSet1, 34 signingIndices: []int{0, 3, 1}, 35 signatures: sigSet1, 36 passAfterKSignatures: []bool{false}, 37 }, 38 } 39 for tcIndex, tc := range cases { 40 multisigKey := NewPubKeyMultisigThreshold(tc.k, tc.pubkeys) 41 multisignature := NewMultisig(len(tc.pubkeys)) 42 43 for i := 0; i < tc.k-1; i++ { 44 signingIndex := tc.signingIndices[i] 45 require.NoError( 46 t, 47 multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), 48 ) 49 require.False( 50 t, 51 multisigKey.VerifyBytes(tc.msg, amino.MustMarshal(multisignature)), 52 "multisig passed when i < k, tc %d, i %d", tcIndex, i, 53 ) 54 require.NoError( 55 t, 56 multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), 57 ) 58 require.Equal( 59 t, 60 i+1, 61 len(multisignature.Sigs), 62 "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, 63 ) 64 } 65 66 require.False( 67 t, 68 multisigKey.VerifyBytes(tc.msg, amino.MustMarshal(multisignature)), 69 "multisig passed with k - 1 sigs, tc %d", tcIndex, 70 ) 71 require.NoError( 72 t, 73 multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys), 74 ) 75 require.True( 76 t, 77 multisigKey.VerifyBytes(tc.msg, amino.MustMarshal(multisignature)), 78 "multisig failed after k good signatures, tc %d", tcIndex, 79 ) 80 81 for i := tc.k + 1; i < len(tc.signingIndices); i++ { 82 signingIndex := tc.signingIndices[i] 83 84 require.NoError( 85 t, 86 multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), 87 ) 88 require.Equal( 89 t, 90 tc.passAfterKSignatures[i-tc.k-1], 91 multisigKey.VerifyBytes(tc.msg, amino.MustMarshal(multisignature)), 92 "multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i, 93 ) 94 require.NoError( 95 t, 96 multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), 97 ) 98 require.Equal( 99 t, 100 i+1, 101 len(multisignature.Sigs), 102 "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, 103 ) 104 } 105 } 106 } 107 108 // TODO: Fully replace this test with table driven tests 109 func TestThresholdMultisigDuplicateSignatures(t *testing.T) { 110 t.Parallel() 111 112 msg := []byte{1, 2, 3, 4, 5} 113 pubkeys, sigs := generatePubKeysAndSignatures(5, msg) 114 multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) 115 multisignature := NewMultisig(5) 116 require.False(t, multisigKey.VerifyBytes(msg, amino.MustMarshal(multisignature))) 117 multisignature.AddSignatureFromPubKey(sigs[0], pubkeys[0], pubkeys) 118 // Add second signature manually 119 multisignature.Sigs = append(multisignature.Sigs, sigs[0]) 120 require.False(t, multisigKey.VerifyBytes(msg, amino.MustMarshal(multisignature))) 121 } 122 123 // TODO: Fully replace this test with table driven tests 124 func TestMultiSigPubKeyEquality(t *testing.T) { 125 t.Parallel() 126 127 msg := []byte{1, 2, 3, 4} 128 pubkeys, _ := generatePubKeysAndSignatures(5, msg) 129 multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) 130 var unmarshalledMultisig crypto.PubKey 131 amino.MustUnmarshal(multisigKey.Bytes(), &unmarshalledMultisig) 132 require.True(t, multisigKey.Equals(unmarshalledMultisig)) 133 134 // Ensure that reordering pubkeys is treated as a different pubkey 135 pubkeysCpy := make([]crypto.PubKey, 5) 136 copy(pubkeysCpy, pubkeys) 137 pubkeysCpy[4] = pubkeys[3] 138 pubkeysCpy[3] = pubkeys[4] 139 multisigKey2 := NewPubKeyMultisigThreshold(2, pubkeysCpy) 140 require.False(t, multisigKey.Equals(multisigKey2)) 141 } 142 143 func TestAddress(t *testing.T) { 144 t.Parallel() 145 146 msg := []byte{1, 2, 3, 4} 147 pubkeys, _ := generatePubKeysAndSignatures(5, msg) 148 multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) 149 require.Len(t, multisigKey.Address(), 20) 150 } 151 152 func TestPubKeyMultisigThresholdAminoToIface(t *testing.T) { 153 t.Parallel() 154 155 msg := []byte{1, 2, 3, 4} 156 pubkeys, _ := generatePubKeysAndSignatures(5, msg) 157 multisigKey := NewPubKeyMultisigThreshold(2, pubkeys) 158 159 ab, err := amino.MarshalAnySized(multisigKey) 160 require.NoError(t, err) 161 // like other crypto.Pubkey implementations (e.g. ed25519.PubKeyEd25519), 162 // PubKeyMultisigThreshold should be deserializable into a crypto.PubKey: 163 var pubKey crypto.PubKey 164 err = amino.UnmarshalSized(ab, &pubKey) 165 require.NoError(t, err) 166 167 require.Equal(t, multisigKey, pubKey) 168 } 169 170 func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) { 171 pubkeys = make([]crypto.PubKey, n) 172 signatures = make([][]byte, n) 173 for i := 0; i < n; i++ { 174 var privkey crypto.PrivKey 175 if rand.Int63()%2 == 0 { 176 privkey = ed25519.GenPrivKey() 177 } else { 178 privkey = secp256k1.GenPrivKey() 179 } 180 pubkeys[i] = privkey.PubKey() 181 signatures[i], _ = privkey.Sign(msg) 182 } 183 return 184 }