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  }