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  }