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 }