github.com/koko1123/flow-go-1@v0.29.6/consensus/hotstuff/signature/packer_test.go (about)

     1  package signature
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/mock"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/koko1123/flow-go-1/consensus/hotstuff"
    11  	"github.com/koko1123/flow-go-1/consensus/hotstuff/mocks"
    12  	"github.com/koko1123/flow-go-1/consensus/hotstuff/model"
    13  	"github.com/koko1123/flow-go-1/model/flow"
    14  	"github.com/koko1123/flow-go-1/module/signature"
    15  	"github.com/koko1123/flow-go-1/utils/unittest"
    16  )
    17  
    18  func newPacker(identities flow.IdentityList) *ConsensusSigDataPacker {
    19  	// mock consensus committee
    20  	committee := &mocks.Committee{}
    21  	committee.On("Identities", mock.Anything).Return(
    22  		func(blockID flow.Identifier) flow.IdentityList {
    23  			return identities
    24  		},
    25  		nil,
    26  	)
    27  
    28  	return NewConsensusSigDataPacker(committee)
    29  }
    30  
    31  func makeBlockSigData(committee flow.IdentityList) *hotstuff.BlockSignatureData {
    32  	blockSigData := &hotstuff.BlockSignatureData{
    33  		StakingSigners: []flow.Identifier{
    34  			committee[0].NodeID, // A
    35  			committee[2].NodeID, // C
    36  		},
    37  		RandomBeaconSigners: []flow.Identifier{
    38  			committee[3].NodeID, // D
    39  			committee[5].NodeID, // F
    40  		},
    41  		AggregatedStakingSig:         unittest.SignatureFixture(),
    42  		AggregatedRandomBeaconSig:    unittest.SignatureFixture(),
    43  		ReconstructedRandomBeaconSig: unittest.SignatureFixture(),
    44  	}
    45  	return blockSigData
    46  }
    47  
    48  // test that a packed data can be unpacked
    49  // given the consensus committee [A, B, C, D, E, F]
    50  // [B,D,F] are random beacon nodes
    51  // [A,C,E] are non-random beacon nodes
    52  // aggregated staking sigs are from [A,C]
    53  // aggregated random beacon sigs are from [D,F]
    54  func TestPackUnpack(t *testing.T) {
    55  	// prepare data for testing
    56  	committee := unittest.IdentityListFixture(6, unittest.WithRole(flow.RoleConsensus))
    57  	blockID := unittest.IdentifierFixture()
    58  	blockSigData := makeBlockSigData(committee)
    59  
    60  	// create packer with the committee
    61  	packer := newPacker(committee)
    62  
    63  	// pack & unpack
    64  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
    65  	require.NoError(t, err)
    66  
    67  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
    68  	require.NoError(t, err)
    69  
    70  	unpacked, err := packer.Unpack(signers, sig)
    71  	require.NoError(t, err)
    72  
    73  	// check that the unpacked data match with the original data
    74  	require.Equal(t, blockSigData.StakingSigners, unpacked.StakingSigners)
    75  	require.Equal(t, blockSigData.RandomBeaconSigners, unpacked.RandomBeaconSigners)
    76  	require.Equal(t, blockSigData.AggregatedStakingSig, unpacked.AggregatedStakingSig)
    77  	require.Equal(t, blockSigData.AggregatedRandomBeaconSig, unpacked.AggregatedRandomBeaconSig)
    78  	require.Equal(t, blockSigData.ReconstructedRandomBeaconSig, unpacked.ReconstructedRandomBeaconSig)
    79  
    80  	// check the packed signer IDs
    81  	var expectedSignerIDs flow.IdentifierList
    82  	expectedSignerIDs = append(expectedSignerIDs, blockSigData.StakingSigners...)
    83  	expectedSignerIDs = append(expectedSignerIDs, blockSigData.RandomBeaconSigners...)
    84  	require.Equal(t, expectedSignerIDs, signers.NodeIDs())
    85  }
    86  
    87  // if signed by 60 staking nodes, and 50 random beacon nodes among a 200 nodes committee,
    88  // it's able to pack and unpack
    89  func TestPackUnpackManyNodes(t *testing.T) {
    90  	// prepare data for testing
    91  	committee := unittest.IdentityListFixture(200, unittest.WithRole(flow.RoleConsensus))
    92  	blockID := unittest.IdentifierFixture()
    93  	blockSigData := makeBlockSigData(committee)
    94  	stakingSigners := make([]flow.Identifier, 0)
    95  	for i := 0; i < 60; i++ {
    96  		stakingSigners = append(stakingSigners, committee[i].NodeID)
    97  	}
    98  	randomBeaconSigners := make([]flow.Identifier, 0)
    99  	for i := 100; i < 100+50; i++ {
   100  		randomBeaconSigners = append(randomBeaconSigners, committee[i].NodeID)
   101  	}
   102  	blockSigData.StakingSigners = stakingSigners
   103  	blockSigData.RandomBeaconSigners = randomBeaconSigners
   104  
   105  	// create packer with the committee
   106  	packer := newPacker(committee)
   107  
   108  	// pack & unpack
   109  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
   110  	require.NoError(t, err)
   111  
   112  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
   113  	require.NoError(t, err)
   114  
   115  	unpacked, err := packer.Unpack(signers, sig)
   116  	require.NoError(t, err)
   117  
   118  	// check that the unpack data match with the original data
   119  	require.Equal(t, blockSigData.StakingSigners, unpacked.StakingSigners)
   120  	require.Equal(t, blockSigData.RandomBeaconSigners, unpacked.RandomBeaconSigners)
   121  	require.Equal(t, blockSigData.AggregatedStakingSig, unpacked.AggregatedStakingSig)
   122  	require.Equal(t, blockSigData.AggregatedRandomBeaconSig, unpacked.AggregatedRandomBeaconSig)
   123  	require.Equal(t, blockSigData.ReconstructedRandomBeaconSig, unpacked.ReconstructedRandomBeaconSig)
   124  
   125  	// check the packed signer IDs
   126  	var expectedSignerIDs flow.IdentifierList
   127  	expectedSignerIDs = append(expectedSignerIDs, blockSigData.StakingSigners...)
   128  	expectedSignerIDs = append(expectedSignerIDs, blockSigData.RandomBeaconSigners...)
   129  	require.Equal(t, expectedSignerIDs, signers.NodeIDs())
   130  }
   131  
   132  // if the sig data can not be decoded, return model.InvalidFormatError
   133  func TestFailToDecode(t *testing.T) {
   134  	// prepare data for testing
   135  	committee := unittest.IdentityListFixture(6, unittest.WithRole(flow.RoleConsensus))
   136  	blockID := unittest.IdentifierFixture()
   137  	blockSigData := makeBlockSigData(committee)
   138  
   139  	// create packer with the committee
   140  	packer := newPacker(committee)
   141  
   142  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
   143  	require.NoError(t, err)
   144  
   145  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
   146  	require.NoError(t, err)
   147  
   148  	// prepare invalid data by modifying the valid data and unpack:
   149  	invalidSigData := sig[1:]
   150  	_, err = packer.Unpack(signers, invalidSigData)
   151  	require.True(t, model.IsInvalidFormatError(err))
   152  }
   153  
   154  // TestMismatchSignerIDs
   155  // if the signer IDs doesn't match, return InvalidFormatError
   156  func TestMismatchSignerIDs(t *testing.T) {
   157  	// prepare data for testing
   158  	committee := unittest.IdentityListFixture(9, unittest.WithRole(flow.RoleConsensus))
   159  	blockID := unittest.IdentifierFixture()
   160  	blockSigData := makeBlockSigData(committee[:6])
   161  
   162  	// create packer with the committee
   163  	packer := newPacker(committee)
   164  
   165  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
   166  	require.NoError(t, err)
   167  
   168  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
   169  	require.NoError(t, err)
   170  
   171  	// prepare invalid signers by modifying the valid signers
   172  	// remove the first signer
   173  	invalidSignerIDs := signers[1:]
   174  
   175  	_, err = packer.Unpack(invalidSignerIDs, sig)
   176  	require.True(t, model.IsInvalidFormatError(err))
   177  
   178  	// with additional signer
   179  	// 9 nodes committee would require two bytes for sig type, the additional byte
   180  	// would cause the sig type and signer IDs to be mismatch
   181  	invalidSignerIDs = committee
   182  	misPacked, err := packer.Unpack(invalidSignerIDs, sig)
   183  	require.Error(t, err, fmt.Sprintf("packed signers: %v", misPacked))
   184  	require.True(t, model.IsInvalidFormatError(err))
   185  }
   186  
   187  // if sig type doesn't match, return InvalidFormatError
   188  func TestInvalidSigType(t *testing.T) {
   189  	// prepare data for testing
   190  	committee := unittest.IdentityListFixture(6, unittest.WithRole(flow.RoleConsensus))
   191  	blockID := unittest.IdentifierFixture()
   192  	blockSigData := makeBlockSigData(committee)
   193  
   194  	// create packer with the committee
   195  	packer := newPacker(committee)
   196  
   197  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
   198  	require.NoError(t, err)
   199  
   200  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
   201  	require.NoError(t, err)
   202  
   203  	data, err := packer.Decode(sig)
   204  	require.NoError(t, err)
   205  
   206  	data.SigType = []byte{1}
   207  
   208  	encoded, err := packer.Encode(data)
   209  	require.NoError(t, err)
   210  
   211  	_, err = packer.Unpack(signers, encoded)
   212  	require.True(t, model.IsInvalidFormatError(err))
   213  }
   214  
   215  // TestPackUnpackWithoutRBAggregatedSig test that a packed data without random beacon signers and
   216  // aggregated random beacon sig can be correctly packed and unpacked
   217  // given the consensus committee [A, B, C]
   218  // [A, B, C] are non-random beacon nodes
   219  // aggregated staking sigs are from [A,B,C]
   220  // no aggregated random beacon sigs
   221  // no random beacon signers
   222  func TestPackUnpackWithoutRBAggregatedSig(t *testing.T) {
   223  	// prepare data for testing
   224  	committee := unittest.IdentityListFixture(3, unittest.WithRole(flow.RoleConsensus))
   225  	blockID := unittest.IdentifierFixture()
   226  
   227  	blockSigData := &hotstuff.BlockSignatureData{
   228  		StakingSigners:               committee.NodeIDs(),
   229  		RandomBeaconSigners:          nil,
   230  		AggregatedStakingSig:         unittest.SignatureFixture(),
   231  		AggregatedRandomBeaconSig:    nil,
   232  		ReconstructedRandomBeaconSig: unittest.SignatureFixture(),
   233  	}
   234  
   235  	// create packer with the committee
   236  	packer := newPacker(committee)
   237  
   238  	// pack & unpack
   239  	signerIndices, sig, err := packer.Pack(blockID, blockSigData)
   240  	require.NoError(t, err)
   241  
   242  	signers, err := signature.DecodeSignerIndicesToIdentities(committee, signerIndices)
   243  	require.NoError(t, err)
   244  
   245  	unpacked, err := packer.Unpack(signers, sig)
   246  	require.NoError(t, err)
   247  
   248  	// check that the unpack data match with the original data
   249  	require.Equal(t, blockSigData.StakingSigners, unpacked.StakingSigners)
   250  	require.Equal(t, blockSigData.AggregatedStakingSig, unpacked.AggregatedStakingSig)
   251  	require.Equal(t, blockSigData.ReconstructedRandomBeaconSig, unpacked.ReconstructedRandomBeaconSig)
   252  
   253  	// we need to specifically test if it's empty, it has to be by test definition
   254  	require.Empty(t, unpacked.RandomBeaconSigners)
   255  	require.Empty(t, unpacked.AggregatedRandomBeaconSig)
   256  
   257  	// check the packed signer IDs
   258  	expectedSignerIDs := append(flow.IdentifierList{}, blockSigData.StakingSigners...)
   259  	require.Equal(t, expectedSignerIDs, signers.NodeIDs())
   260  }
   261  
   262  // TestPackWithoutRBAggregatedSig tests that packer correctly handles BlockSignatureData
   263  // with different structure format, more specifically there is no difference between
   264  // nil and empty slices for RandomBeaconSigners and AggregatedRandomBeaconSig.
   265  func TestPackWithoutRBAggregatedSig(t *testing.T) {
   266  	identities := unittest.IdentityListFixture(3, unittest.WithRole(flow.RoleConsensus))
   267  	committee := identities.NodeIDs()
   268  
   269  	// prepare data for testing
   270  	blockID := unittest.IdentifierFixture()
   271  
   272  	aggregatedSig := unittest.SignatureFixture()
   273  	reconstructedSig := unittest.SignatureFixture()
   274  
   275  	blockSigDataWithEmptySlices := &hotstuff.BlockSignatureData{
   276  		StakingSigners:               committee,
   277  		RandomBeaconSigners:          []flow.Identifier{},
   278  		AggregatedStakingSig:         aggregatedSig,
   279  		AggregatedRandomBeaconSig:    []byte{},
   280  		ReconstructedRandomBeaconSig: reconstructedSig,
   281  	}
   282  
   283  	blockSigDataWithNils := &hotstuff.BlockSignatureData{
   284  		StakingSigners:               committee,
   285  		RandomBeaconSigners:          nil,
   286  		AggregatedStakingSig:         aggregatedSig,
   287  		AggregatedRandomBeaconSig:    nil,
   288  		ReconstructedRandomBeaconSig: reconstructedSig,
   289  	}
   290  
   291  	// create packer with the committee
   292  	packer := newPacker(identities)
   293  
   294  	// pack
   295  	signerIDs_A, sig_A, err := packer.Pack(blockID, blockSigDataWithEmptySlices)
   296  	require.NoError(t, err)
   297  
   298  	signerIDs_B, sig_B, err := packer.Pack(blockID, blockSigDataWithNils)
   299  	require.NoError(t, err)
   300  
   301  	// should be the same
   302  	require.Equal(t, signerIDs_A, signerIDs_B)
   303  	require.Equal(t, sig_A, sig_B)
   304  }