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