github.com/ava-labs/avalanchego@v1.11.11/vms/platformvm/warp/signature_test.go (about)

     1  // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved.
     2  // See the file LICENSE for licensing terms.
     3  
     4  package warp
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"math"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/require"
    13  	"go.uber.org/mock/gomock"
    14  
    15  	"github.com/ava-labs/avalanchego/ids"
    16  	"github.com/ava-labs/avalanchego/snow/validators"
    17  	"github.com/ava-labs/avalanchego/snow/validators/validatorsmock"
    18  	"github.com/ava-labs/avalanchego/utils"
    19  	"github.com/ava-labs/avalanchego/utils/constants"
    20  	"github.com/ava-labs/avalanchego/utils/crypto/bls"
    21  	"github.com/ava-labs/avalanchego/utils/set"
    22  )
    23  
    24  const pChainHeight uint64 = 1337
    25  
    26  var (
    27  	_ utils.Sortable[*testValidator] = (*testValidator)(nil)
    28  
    29  	errTest       = errors.New("non-nil error")
    30  	sourceChainID = ids.GenerateTestID()
    31  	subnetID      = ids.GenerateTestID()
    32  
    33  	testVdrs []*testValidator
    34  )
    35  
    36  type testValidator struct {
    37  	nodeID ids.NodeID
    38  	sk     *bls.SecretKey
    39  	vdr    *Validator
    40  }
    41  
    42  func (v *testValidator) Compare(o *testValidator) int {
    43  	return v.vdr.Compare(o.vdr)
    44  }
    45  
    46  func newTestValidator() *testValidator {
    47  	sk, err := bls.NewSecretKey()
    48  	if err != nil {
    49  		panic(err)
    50  	}
    51  
    52  	nodeID := ids.GenerateTestNodeID()
    53  	pk := bls.PublicFromSecretKey(sk)
    54  	return &testValidator{
    55  		nodeID: nodeID,
    56  		sk:     sk,
    57  		vdr: &Validator{
    58  			PublicKey:      pk,
    59  			PublicKeyBytes: bls.PublicKeyToUncompressedBytes(pk),
    60  			Weight:         3,
    61  			NodeIDs:        []ids.NodeID{nodeID},
    62  		},
    63  	}
    64  }
    65  
    66  func init() {
    67  	testVdrs = []*testValidator{
    68  		newTestValidator(),
    69  		newTestValidator(),
    70  		newTestValidator(),
    71  	}
    72  	utils.Sort(testVdrs)
    73  }
    74  
    75  func TestNumSigners(t *testing.T) {
    76  	tests := map[string]struct {
    77  		generateSignature func() *BitSetSignature
    78  		count             int
    79  		err               error
    80  	}{
    81  		"empty signers": {
    82  			generateSignature: func() *BitSetSignature {
    83  				return &BitSetSignature{}
    84  			},
    85  		},
    86  		"invalid signers": {
    87  			generateSignature: func() *BitSetSignature {
    88  				return &BitSetSignature{
    89  					Signers: make([]byte, 1),
    90  				}
    91  			},
    92  			err: ErrInvalidBitSet,
    93  		},
    94  		"no signers": {
    95  			generateSignature: func() *BitSetSignature {
    96  				signers := set.NewBits()
    97  				return &BitSetSignature{
    98  					Signers: signers.Bytes(),
    99  				}
   100  			},
   101  		},
   102  		"1 signer": {
   103  			generateSignature: func() *BitSetSignature {
   104  				signers := set.NewBits()
   105  				signers.Add(2)
   106  				return &BitSetSignature{
   107  					Signers: signers.Bytes(),
   108  				}
   109  			},
   110  			count: 1,
   111  		},
   112  		"multiple signers": {
   113  			generateSignature: func() *BitSetSignature {
   114  				signers := set.NewBits()
   115  				signers.Add(2)
   116  				signers.Add(11)
   117  				signers.Add(55)
   118  				signers.Add(93)
   119  				return &BitSetSignature{
   120  					Signers: signers.Bytes(),
   121  				}
   122  			},
   123  			count: 4,
   124  		},
   125  	}
   126  
   127  	for name, tt := range tests {
   128  		t.Run(name, func(t *testing.T) {
   129  			require := require.New(t)
   130  			sig := tt.generateSignature()
   131  			count, err := sig.NumSigners()
   132  			require.Equal(tt.count, count)
   133  			require.ErrorIs(err, tt.err)
   134  		})
   135  	}
   136  }
   137  
   138  func TestSignatureVerification(t *testing.T) {
   139  	vdrs := map[ids.NodeID]*validators.GetValidatorOutput{
   140  		testVdrs[0].nodeID: {
   141  			NodeID:    testVdrs[0].nodeID,
   142  			PublicKey: testVdrs[0].vdr.PublicKey,
   143  			Weight:    testVdrs[0].vdr.Weight,
   144  		},
   145  		testVdrs[1].nodeID: {
   146  			NodeID:    testVdrs[1].nodeID,
   147  			PublicKey: testVdrs[1].vdr.PublicKey,
   148  			Weight:    testVdrs[1].vdr.Weight,
   149  		},
   150  		testVdrs[2].nodeID: {
   151  			NodeID:    testVdrs[2].nodeID,
   152  			PublicKey: testVdrs[2].vdr.PublicKey,
   153  			Weight:    testVdrs[2].vdr.Weight,
   154  		},
   155  	}
   156  
   157  	tests := []struct {
   158  		name      string
   159  		networkID uint32
   160  		stateF    func(*gomock.Controller) validators.State
   161  		quorumNum uint64
   162  		quorumDen uint64
   163  		msgF      func(*require.Assertions) *Message
   164  		err       error
   165  	}{
   166  		{
   167  			name:      "can't get subnetID",
   168  			networkID: constants.UnitTestID,
   169  			stateF: func(ctrl *gomock.Controller) validators.State {
   170  				state := validatorsmock.NewState(ctrl)
   171  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, errTest)
   172  				return state
   173  			},
   174  			quorumNum: 1,
   175  			quorumDen: 2,
   176  			msgF: func(require *require.Assertions) *Message {
   177  				unsignedMsg, err := NewUnsignedMessage(
   178  					constants.UnitTestID,
   179  					sourceChainID,
   180  					nil,
   181  				)
   182  				require.NoError(err)
   183  
   184  				msg, err := NewMessage(
   185  					unsignedMsg,
   186  					&BitSetSignature{},
   187  				)
   188  				require.NoError(err)
   189  				return msg
   190  			},
   191  			err: errTest,
   192  		},
   193  		{
   194  			name:      "can't get validator set",
   195  			networkID: constants.UnitTestID,
   196  			stateF: func(ctrl *gomock.Controller) validators.State {
   197  				state := validatorsmock.NewState(ctrl)
   198  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   199  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(nil, errTest)
   200  				return state
   201  			},
   202  			quorumNum: 1,
   203  			quorumDen: 2,
   204  			msgF: func(require *require.Assertions) *Message {
   205  				unsignedMsg, err := NewUnsignedMessage(
   206  					constants.UnitTestID,
   207  					sourceChainID,
   208  					nil,
   209  				)
   210  				require.NoError(err)
   211  
   212  				msg, err := NewMessage(
   213  					unsignedMsg,
   214  					&BitSetSignature{},
   215  				)
   216  				require.NoError(err)
   217  				return msg
   218  			},
   219  			err: errTest,
   220  		},
   221  		{
   222  			name:      "weight overflow",
   223  			networkID: constants.UnitTestID,
   224  			stateF: func(ctrl *gomock.Controller) validators.State {
   225  				state := validatorsmock.NewState(ctrl)
   226  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   227  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
   228  					testVdrs[0].nodeID: {
   229  						NodeID:    testVdrs[0].nodeID,
   230  						PublicKey: testVdrs[0].vdr.PublicKey,
   231  						Weight:    math.MaxUint64,
   232  					},
   233  					testVdrs[1].nodeID: {
   234  						NodeID:    testVdrs[1].nodeID,
   235  						PublicKey: testVdrs[1].vdr.PublicKey,
   236  						Weight:    math.MaxUint64,
   237  					},
   238  				}, nil)
   239  				return state
   240  			},
   241  			quorumNum: 1,
   242  			quorumDen: 2,
   243  			msgF: func(*require.Assertions) *Message {
   244  				return &Message{
   245  					UnsignedMessage: UnsignedMessage{
   246  						NetworkID:     constants.UnitTestID,
   247  						SourceChainID: sourceChainID,
   248  					},
   249  					Signature: &BitSetSignature{
   250  						Signers: make([]byte, 8),
   251  					},
   252  				}
   253  			},
   254  			err: ErrWeightOverflow,
   255  		},
   256  		{
   257  			name:      "invalid bit set index",
   258  			networkID: constants.UnitTestID,
   259  			stateF: func(ctrl *gomock.Controller) validators.State {
   260  				state := validatorsmock.NewState(ctrl)
   261  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   262  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   263  				return state
   264  			},
   265  			quorumNum: 1,
   266  			quorumDen: 2,
   267  			msgF: func(require *require.Assertions) *Message {
   268  				unsignedMsg, err := NewUnsignedMessage(
   269  					constants.UnitTestID,
   270  					sourceChainID,
   271  					[]byte{1, 2, 3},
   272  				)
   273  				require.NoError(err)
   274  
   275  				msg, err := NewMessage(
   276  					unsignedMsg,
   277  					&BitSetSignature{
   278  						Signers:   make([]byte, 1),
   279  						Signature: [bls.SignatureLen]byte{},
   280  					},
   281  				)
   282  				require.NoError(err)
   283  				return msg
   284  			},
   285  			err: ErrInvalidBitSet,
   286  		},
   287  		{
   288  			name:      "unknown index",
   289  			networkID: constants.UnitTestID,
   290  			stateF: func(ctrl *gomock.Controller) validators.State {
   291  				state := validatorsmock.NewState(ctrl)
   292  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   293  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   294  				return state
   295  			},
   296  			quorumNum: 1,
   297  			quorumDen: 2,
   298  			msgF: func(require *require.Assertions) *Message {
   299  				unsignedMsg, err := NewUnsignedMessage(
   300  					constants.UnitTestID,
   301  					sourceChainID,
   302  					[]byte{1, 2, 3},
   303  				)
   304  				require.NoError(err)
   305  
   306  				signers := set.NewBits()
   307  				signers.Add(3) // vdr oob
   308  
   309  				msg, err := NewMessage(
   310  					unsignedMsg,
   311  					&BitSetSignature{
   312  						Signers:   signers.Bytes(),
   313  						Signature: [bls.SignatureLen]byte{},
   314  					},
   315  				)
   316  				require.NoError(err)
   317  				return msg
   318  			},
   319  			err: ErrUnknownValidator,
   320  		},
   321  		{
   322  			name:      "insufficient weight",
   323  			networkID: constants.UnitTestID,
   324  			stateF: func(ctrl *gomock.Controller) validators.State {
   325  				state := validatorsmock.NewState(ctrl)
   326  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   327  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   328  				return state
   329  			},
   330  			quorumNum: 1,
   331  			quorumDen: 1,
   332  			msgF: func(require *require.Assertions) *Message {
   333  				unsignedMsg, err := NewUnsignedMessage(
   334  					constants.UnitTestID,
   335  					sourceChainID,
   336  					[]byte{1, 2, 3},
   337  				)
   338  				require.NoError(err)
   339  
   340  				// [signers] has weight from [vdr[0], vdr[1]],
   341  				// which is 6, which is less than 9
   342  				signers := set.NewBits()
   343  				signers.Add(0)
   344  				signers.Add(1)
   345  
   346  				unsignedBytes := unsignedMsg.Bytes()
   347  				vdr0Sig := bls.Sign(testVdrs[0].sk, unsignedBytes)
   348  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   349  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr1Sig})
   350  				require.NoError(err)
   351  				aggSigBytes := [bls.SignatureLen]byte{}
   352  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   353  
   354  				msg, err := NewMessage(
   355  					unsignedMsg,
   356  					&BitSetSignature{
   357  						Signers:   signers.Bytes(),
   358  						Signature: aggSigBytes,
   359  					},
   360  				)
   361  				require.NoError(err)
   362  				return msg
   363  			},
   364  			err: ErrInsufficientWeight,
   365  		},
   366  		{
   367  			name:      "can't parse sig",
   368  			networkID: constants.UnitTestID,
   369  			stateF: func(ctrl *gomock.Controller) validators.State {
   370  				state := validatorsmock.NewState(ctrl)
   371  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   372  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   373  				return state
   374  			},
   375  			quorumNum: 1,
   376  			quorumDen: 2,
   377  			msgF: func(require *require.Assertions) *Message {
   378  				unsignedMsg, err := NewUnsignedMessage(
   379  					constants.UnitTestID,
   380  					sourceChainID,
   381  					[]byte{1, 2, 3},
   382  				)
   383  				require.NoError(err)
   384  
   385  				signers := set.NewBits()
   386  				signers.Add(0)
   387  				signers.Add(1)
   388  
   389  				msg, err := NewMessage(
   390  					unsignedMsg,
   391  					&BitSetSignature{
   392  						Signers:   signers.Bytes(),
   393  						Signature: [bls.SignatureLen]byte{},
   394  					},
   395  				)
   396  				require.NoError(err)
   397  				return msg
   398  			},
   399  			err: ErrParseSignature,
   400  		},
   401  		{
   402  			name:      "no validators",
   403  			networkID: constants.UnitTestID,
   404  			stateF: func(ctrl *gomock.Controller) validators.State {
   405  				state := validatorsmock.NewState(ctrl)
   406  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   407  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(nil, nil)
   408  				return state
   409  			},
   410  			quorumNum: 1,
   411  			quorumDen: 2,
   412  			msgF: func(require *require.Assertions) *Message {
   413  				unsignedMsg, err := NewUnsignedMessage(
   414  					constants.UnitTestID,
   415  					sourceChainID,
   416  					[]byte{1, 2, 3},
   417  				)
   418  				require.NoError(err)
   419  
   420  				unsignedBytes := unsignedMsg.Bytes()
   421  				vdr0Sig := bls.Sign(testVdrs[0].sk, unsignedBytes)
   422  				aggSigBytes := [bls.SignatureLen]byte{}
   423  				copy(aggSigBytes[:], bls.SignatureToBytes(vdr0Sig))
   424  
   425  				msg, err := NewMessage(
   426  					unsignedMsg,
   427  					&BitSetSignature{
   428  						Signers:   nil,
   429  						Signature: aggSigBytes,
   430  					},
   431  				)
   432  				require.NoError(err)
   433  				return msg
   434  			},
   435  			err: bls.ErrNoPublicKeys,
   436  		},
   437  		{
   438  			name:      "invalid signature (substitute)",
   439  			networkID: constants.UnitTestID,
   440  			stateF: func(ctrl *gomock.Controller) validators.State {
   441  				state := validatorsmock.NewState(ctrl)
   442  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   443  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   444  				return state
   445  			},
   446  			quorumNum: 3,
   447  			quorumDen: 5,
   448  			msgF: func(require *require.Assertions) *Message {
   449  				unsignedMsg, err := NewUnsignedMessage(
   450  					constants.UnitTestID,
   451  					sourceChainID,
   452  					[]byte{1, 2, 3},
   453  				)
   454  				require.NoError(err)
   455  
   456  				signers := set.NewBits()
   457  				signers.Add(0)
   458  				signers.Add(1)
   459  
   460  				unsignedBytes := unsignedMsg.Bytes()
   461  				vdr0Sig := bls.Sign(testVdrs[0].sk, unsignedBytes)
   462  				// Give sig from vdr[2] even though the bit vector says it
   463  				// should be from vdr[1]
   464  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   465  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr2Sig})
   466  				require.NoError(err)
   467  				aggSigBytes := [bls.SignatureLen]byte{}
   468  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   469  
   470  				msg, err := NewMessage(
   471  					unsignedMsg,
   472  					&BitSetSignature{
   473  						Signers:   signers.Bytes(),
   474  						Signature: aggSigBytes,
   475  					},
   476  				)
   477  				require.NoError(err)
   478  				return msg
   479  			},
   480  			err: ErrInvalidSignature,
   481  		},
   482  		{
   483  			name:      "invalid signature (missing one)",
   484  			networkID: constants.UnitTestID,
   485  			stateF: func(ctrl *gomock.Controller) validators.State {
   486  				state := validatorsmock.NewState(ctrl)
   487  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   488  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   489  				return state
   490  			},
   491  			quorumNum: 3,
   492  			quorumDen: 5,
   493  			msgF: func(require *require.Assertions) *Message {
   494  				unsignedMsg, err := NewUnsignedMessage(
   495  					constants.UnitTestID,
   496  					sourceChainID,
   497  					[]byte{1, 2, 3},
   498  				)
   499  				require.NoError(err)
   500  
   501  				signers := set.NewBits()
   502  				signers.Add(0)
   503  				signers.Add(1)
   504  
   505  				unsignedBytes := unsignedMsg.Bytes()
   506  				vdr0Sig := bls.Sign(testVdrs[0].sk, unsignedBytes)
   507  				// Don't give the sig from vdr[1]
   508  				aggSigBytes := [bls.SignatureLen]byte{}
   509  				copy(aggSigBytes[:], bls.SignatureToBytes(vdr0Sig))
   510  
   511  				msg, err := NewMessage(
   512  					unsignedMsg,
   513  					&BitSetSignature{
   514  						Signers:   signers.Bytes(),
   515  						Signature: aggSigBytes,
   516  					},
   517  				)
   518  				require.NoError(err)
   519  				return msg
   520  			},
   521  			err: ErrInvalidSignature,
   522  		},
   523  		{
   524  			name:      "invalid signature (extra one)",
   525  			networkID: constants.UnitTestID,
   526  			stateF: func(ctrl *gomock.Controller) validators.State {
   527  				state := validatorsmock.NewState(ctrl)
   528  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   529  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   530  				return state
   531  			},
   532  			quorumNum: 3,
   533  			quorumDen: 5,
   534  			msgF: func(require *require.Assertions) *Message {
   535  				unsignedMsg, err := NewUnsignedMessage(
   536  					constants.UnitTestID,
   537  					sourceChainID,
   538  					[]byte{1, 2, 3},
   539  				)
   540  				require.NoError(err)
   541  
   542  				signers := set.NewBits()
   543  				signers.Add(0)
   544  				signers.Add(1)
   545  
   546  				unsignedBytes := unsignedMsg.Bytes()
   547  				vdr0Sig := bls.Sign(testVdrs[0].sk, unsignedBytes)
   548  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   549  				// Give sig from vdr[2] even though the bit vector doesn't have
   550  				// it
   551  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   552  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr0Sig, vdr1Sig, vdr2Sig})
   553  				require.NoError(err)
   554  				aggSigBytes := [bls.SignatureLen]byte{}
   555  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   556  
   557  				msg, err := NewMessage(
   558  					unsignedMsg,
   559  					&BitSetSignature{
   560  						Signers:   signers.Bytes(),
   561  						Signature: aggSigBytes,
   562  					},
   563  				)
   564  				require.NoError(err)
   565  				return msg
   566  			},
   567  			err: ErrInvalidSignature,
   568  		},
   569  		{
   570  			name:      "valid signature",
   571  			networkID: constants.UnitTestID,
   572  			stateF: func(ctrl *gomock.Controller) validators.State {
   573  				state := validatorsmock.NewState(ctrl)
   574  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   575  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   576  				return state
   577  			},
   578  			quorumNum: 1,
   579  			quorumDen: 2,
   580  			msgF: func(require *require.Assertions) *Message {
   581  				unsignedMsg, err := NewUnsignedMessage(
   582  					constants.UnitTestID,
   583  					sourceChainID,
   584  					[]byte{1, 2, 3},
   585  				)
   586  				require.NoError(err)
   587  
   588  				// [signers] has weight from [vdr[1], vdr[2]],
   589  				// which is 6, which is greater than 4.5
   590  				signers := set.NewBits()
   591  				signers.Add(1)
   592  				signers.Add(2)
   593  
   594  				unsignedBytes := unsignedMsg.Bytes()
   595  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   596  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   597  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
   598  				require.NoError(err)
   599  				aggSigBytes := [bls.SignatureLen]byte{}
   600  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   601  
   602  				msg, err := NewMessage(
   603  					unsignedMsg,
   604  					&BitSetSignature{
   605  						Signers:   signers.Bytes(),
   606  						Signature: aggSigBytes,
   607  					},
   608  				)
   609  				require.NoError(err)
   610  				return msg
   611  			},
   612  			err: nil,
   613  		},
   614  		{
   615  			name:      "valid signature (boundary)",
   616  			networkID: constants.UnitTestID,
   617  			stateF: func(ctrl *gomock.Controller) validators.State {
   618  				state := validatorsmock.NewState(ctrl)
   619  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   620  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(vdrs, nil)
   621  				return state
   622  			},
   623  			quorumNum: 2,
   624  			quorumDen: 3,
   625  			msgF: func(require *require.Assertions) *Message {
   626  				unsignedMsg, err := NewUnsignedMessage(
   627  					constants.UnitTestID,
   628  					sourceChainID,
   629  					[]byte{1, 2, 3},
   630  				)
   631  				require.NoError(err)
   632  
   633  				// [signers] has weight from [vdr[1], vdr[2]],
   634  				// which is 6, which meets the minimum 6
   635  				signers := set.NewBits()
   636  				signers.Add(1)
   637  				signers.Add(2)
   638  
   639  				unsignedBytes := unsignedMsg.Bytes()
   640  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   641  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   642  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
   643  				require.NoError(err)
   644  				aggSigBytes := [bls.SignatureLen]byte{}
   645  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   646  
   647  				msg, err := NewMessage(
   648  					unsignedMsg,
   649  					&BitSetSignature{
   650  						Signers:   signers.Bytes(),
   651  						Signature: aggSigBytes,
   652  					},
   653  				)
   654  				require.NoError(err)
   655  				return msg
   656  			},
   657  			err: nil,
   658  		},
   659  		{
   660  			name:      "valid signature (missing key)",
   661  			networkID: constants.UnitTestID,
   662  			stateF: func(ctrl *gomock.Controller) validators.State {
   663  				state := validatorsmock.NewState(ctrl)
   664  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   665  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
   666  					testVdrs[0].nodeID: {
   667  						NodeID:    testVdrs[0].nodeID,
   668  						PublicKey: nil,
   669  						Weight:    testVdrs[0].vdr.Weight,
   670  					},
   671  					testVdrs[1].nodeID: {
   672  						NodeID:    testVdrs[1].nodeID,
   673  						PublicKey: testVdrs[1].vdr.PublicKey,
   674  						Weight:    testVdrs[1].vdr.Weight,
   675  					},
   676  					testVdrs[2].nodeID: {
   677  						NodeID:    testVdrs[2].nodeID,
   678  						PublicKey: testVdrs[2].vdr.PublicKey,
   679  						Weight:    testVdrs[2].vdr.Weight,
   680  					},
   681  				}, nil)
   682  				return state
   683  			},
   684  			quorumNum: 1,
   685  			quorumDen: 3,
   686  			msgF: func(require *require.Assertions) *Message {
   687  				unsignedMsg, err := NewUnsignedMessage(
   688  					constants.UnitTestID,
   689  					sourceChainID,
   690  					[]byte{1, 2, 3},
   691  				)
   692  				require.NoError(err)
   693  
   694  				// [signers] has weight from [vdr2, vdr3],
   695  				// which is 6, which is greater than 3
   696  				signers := set.NewBits()
   697  				// Note: the bits are shifted because vdr[0]'s key was zeroed
   698  				signers.Add(0) // vdr[1]
   699  				signers.Add(1) // vdr[2]
   700  
   701  				unsignedBytes := unsignedMsg.Bytes()
   702  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   703  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   704  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
   705  				require.NoError(err)
   706  				aggSigBytes := [bls.SignatureLen]byte{}
   707  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   708  
   709  				msg, err := NewMessage(
   710  					unsignedMsg,
   711  					&BitSetSignature{
   712  						Signers:   signers.Bytes(),
   713  						Signature: aggSigBytes,
   714  					},
   715  				)
   716  				require.NoError(err)
   717  				return msg
   718  			},
   719  			err: nil,
   720  		},
   721  		{
   722  			name:      "valid signature (duplicate key)",
   723  			networkID: constants.UnitTestID,
   724  			stateF: func(ctrl *gomock.Controller) validators.State {
   725  				state := validatorsmock.NewState(ctrl)
   726  				state.EXPECT().GetSubnetID(gomock.Any(), sourceChainID).Return(subnetID, nil)
   727  				state.EXPECT().GetValidatorSet(gomock.Any(), pChainHeight, subnetID).Return(map[ids.NodeID]*validators.GetValidatorOutput{
   728  					testVdrs[0].nodeID: {
   729  						NodeID:    testVdrs[0].nodeID,
   730  						PublicKey: nil,
   731  						Weight:    testVdrs[0].vdr.Weight,
   732  					},
   733  					testVdrs[1].nodeID: {
   734  						NodeID:    testVdrs[1].nodeID,
   735  						PublicKey: testVdrs[2].vdr.PublicKey,
   736  						Weight:    testVdrs[1].vdr.Weight,
   737  					},
   738  					testVdrs[2].nodeID: {
   739  						NodeID:    testVdrs[2].nodeID,
   740  						PublicKey: testVdrs[2].vdr.PublicKey,
   741  						Weight:    testVdrs[2].vdr.Weight,
   742  					},
   743  				}, nil)
   744  				return state
   745  			},
   746  			quorumNum: 2,
   747  			quorumDen: 3,
   748  			msgF: func(require *require.Assertions) *Message {
   749  				unsignedMsg, err := NewUnsignedMessage(
   750  					constants.UnitTestID,
   751  					sourceChainID,
   752  					[]byte{1, 2, 3},
   753  				)
   754  				require.NoError(err)
   755  
   756  				// [signers] has weight from [vdr2, vdr3],
   757  				// which is 6, which meets the minimum 6
   758  				signers := set.NewBits()
   759  				// Note: the bits are shifted because vdr[0]'s key was zeroed
   760  				// Note: vdr[1] and vdr[2] were combined because of a shared pk
   761  				signers.Add(0) // vdr[1] + vdr[2]
   762  
   763  				unsignedBytes := unsignedMsg.Bytes()
   764  				// Because vdr[1] and vdr[2] share a key, only one of them sign.
   765  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   766  				aggSigBytes := [bls.SignatureLen]byte{}
   767  				copy(aggSigBytes[:], bls.SignatureToBytes(vdr2Sig))
   768  
   769  				msg, err := NewMessage(
   770  					unsignedMsg,
   771  					&BitSetSignature{
   772  						Signers:   signers.Bytes(),
   773  						Signature: aggSigBytes,
   774  					},
   775  				)
   776  				require.NoError(err)
   777  				return msg
   778  			},
   779  			err: nil,
   780  		},
   781  		{
   782  			name:      "incorrect networkID",
   783  			networkID: constants.UnitTestID,
   784  			stateF: func(ctrl *gomock.Controller) validators.State {
   785  				state := validatorsmock.NewState(ctrl)
   786  				return state
   787  			},
   788  			quorumNum: 1,
   789  			quorumDen: 2,
   790  			msgF: func(require *require.Assertions) *Message {
   791  				unsignedMsg, err := NewUnsignedMessage(
   792  					constants.UnitTestID+1,
   793  					sourceChainID,
   794  					[]byte{1, 2, 3},
   795  				)
   796  				require.NoError(err)
   797  
   798  				// [signers] has weight from [vdr[1], vdr[2]],
   799  				// which is 6, which is greater than 4.5
   800  				signers := set.NewBits()
   801  				signers.Add(1)
   802  				signers.Add(2)
   803  
   804  				unsignedBytes := unsignedMsg.Bytes()
   805  				vdr1Sig := bls.Sign(testVdrs[1].sk, unsignedBytes)
   806  				vdr2Sig := bls.Sign(testVdrs[2].sk, unsignedBytes)
   807  				aggSig, err := bls.AggregateSignatures([]*bls.Signature{vdr1Sig, vdr2Sig})
   808  				require.NoError(err)
   809  				aggSigBytes := [bls.SignatureLen]byte{}
   810  				copy(aggSigBytes[:], bls.SignatureToBytes(aggSig))
   811  
   812  				msg, err := NewMessage(
   813  					unsignedMsg,
   814  					&BitSetSignature{
   815  						Signers:   signers.Bytes(),
   816  						Signature: aggSigBytes,
   817  					},
   818  				)
   819  				require.NoError(err)
   820  				return msg
   821  			},
   822  			err: ErrWrongNetworkID,
   823  		},
   824  	}
   825  
   826  	for _, tt := range tests {
   827  		t.Run(tt.name, func(t *testing.T) {
   828  			require := require.New(t)
   829  			ctrl := gomock.NewController(t)
   830  
   831  			msg := tt.msgF(require)
   832  			pChainState := tt.stateF(ctrl)
   833  
   834  			err := msg.Signature.Verify(
   835  				context.Background(),
   836  				&msg.UnsignedMessage,
   837  				tt.networkID,
   838  				pChainState,
   839  				pChainHeight,
   840  				tt.quorumNum,
   841  				tt.quorumDen,
   842  			)
   843  			require.ErrorIs(err, tt.err)
   844  		})
   845  	}
   846  }