github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/pending_attestations_queue_test.go (about) 1 package sync 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/ethereum/go-ethereum/p2p/enr" 9 lru "github.com/hashicorp/golang-lru" 10 "github.com/libp2p/go-libp2p-core/network" 11 types "github.com/prysmaticlabs/eth2-types" 12 "github.com/prysmaticlabs/go-bitfield" 13 mock "github.com/prysmaticlabs/prysm/beacon-chain/blockchain/testing" 14 "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" 15 dbtest "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" 16 "github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations" 17 "github.com/prysmaticlabs/prysm/beacon-chain/p2p/peers" 18 p2ptest "github.com/prysmaticlabs/prysm/beacon-chain/p2p/testing" 19 pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" 20 ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1" 21 "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1/wrapper" 22 "github.com/prysmaticlabs/prysm/shared/abool" 23 "github.com/prysmaticlabs/prysm/shared/attestationutil" 24 "github.com/prysmaticlabs/prysm/shared/bls" 25 "github.com/prysmaticlabs/prysm/shared/bytesutil" 26 "github.com/prysmaticlabs/prysm/shared/params" 27 "github.com/prysmaticlabs/prysm/shared/testutil" 28 "github.com/prysmaticlabs/prysm/shared/testutil/assert" 29 "github.com/prysmaticlabs/prysm/shared/testutil/require" 30 "github.com/prysmaticlabs/prysm/shared/timeutils" 31 logTest "github.com/sirupsen/logrus/hooks/test" 32 ) 33 34 func TestProcessPendingAtts_NoBlockRequestBlock(t *testing.T) { 35 hook := logTest.NewGlobal() 36 db := dbtest.SetupDB(t) 37 p1 := p2ptest.NewTestP2P(t) 38 p2 := p2ptest.NewTestP2P(t) 39 p1.Connect(p2) 40 assert.Equal(t, 1, len(p1.BHost.Network().Peers()), "Expected peers to be connected") 41 p1.Peers().Add(new(enr.Record), p2.PeerID(), nil, network.DirOutbound) 42 p1.Peers().SetConnectionState(p2.PeerID(), peers.PeerConnected) 43 p1.Peers().SetChainState(p2.PeerID(), &pb.Status{}) 44 45 r := &Service{ 46 cfg: &Config{P2P: p1, DB: db, Chain: &mock.ChainService{Genesis: timeutils.Now(), FinalizedCheckPoint: ðpb.Checkpoint{}}}, 47 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 48 chainStarted: abool.New(), 49 } 50 51 a := ðpb.AggregateAttestationAndProof{Aggregate: ðpb.Attestation{Data: ðpb.AttestationData{Target: ðpb.Checkpoint{Root: make([]byte, 32)}}}} 52 r.blkRootToPendingAtts[[32]byte{'A'}] = []*ethpb.SignedAggregateAttestationAndProof{{Message: a}} 53 require.NoError(t, r.processPendingAtts(context.Background())) 54 require.LogsContain(t, hook, "Requesting block for pending attestation") 55 } 56 57 func TestProcessPendingAtts_HasBlockSaveUnAggregatedAtt(t *testing.T) { 58 hook := logTest.NewGlobal() 59 db := dbtest.SetupDB(t) 60 p1 := p2ptest.NewTestP2P(t) 61 validators := uint64(256) 62 63 beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) 64 65 sb := testutil.NewBeaconBlock() 66 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) 67 root, err := sb.Block.HashTreeRoot() 68 require.NoError(t, err) 69 70 aggBits := bitfield.NewBitlist(8) 71 aggBits.SetBitAt(1, true) 72 att := ðpb.Attestation{ 73 Data: ðpb.AttestationData{ 74 BeaconBlockRoot: root[:], 75 Source: ðpb.Checkpoint{Epoch: 0, Root: bytesutil.PadTo([]byte("hello-world"), 32)}, 76 Target: ðpb.Checkpoint{Epoch: 0, Root: root[:]}, 77 }, 78 AggregationBits: aggBits, 79 } 80 81 committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) 82 assert.NoError(t, err) 83 attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee) 84 require.NoError(t, err) 85 assert.NoError(t, err) 86 attesterDomain, err := helpers.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot()) 87 require.NoError(t, err) 88 hashTreeRoot, err := helpers.ComputeSigningRoot(att.Data, attesterDomain) 89 assert.NoError(t, err) 90 for _, i := range attestingIndices { 91 att.Signature = privKeys[i].Sign(hashTreeRoot[:]).Marshal() 92 } 93 94 // Arbitrary aggregator index for testing purposes. 95 aggregatorIndex := committee[0] 96 sszUint := types.SSZUint64(att.Data.Slot) 97 sig, err := helpers.ComputeDomainAndSign(beaconState, 0, &sszUint, params.BeaconConfig().DomainSelectionProof, privKeys[aggregatorIndex]) 98 require.NoError(t, err) 99 aggregateAndProof := ðpb.AggregateAttestationAndProof{ 100 SelectionProof: sig, 101 Aggregate: att, 102 AggregatorIndex: aggregatorIndex, 103 } 104 aggreSig, err := helpers.ComputeDomainAndSign(beaconState, 0, aggregateAndProof, params.BeaconConfig().DomainAggregateAndProof, privKeys[aggregatorIndex]) 105 require.NoError(t, err) 106 107 require.NoError(t, beaconState.SetGenesisTime(uint64(time.Now().Unix()))) 108 109 c, err := lru.New(10) 110 require.NoError(t, err) 111 r := &Service{ 112 cfg: &Config{ 113 P2P: p1, 114 DB: db, 115 Chain: &mock.ChainService{Genesis: time.Now(), 116 State: beaconState, 117 FinalizedCheckPoint: ðpb.Checkpoint{ 118 Root: aggregateAndProof.Aggregate.Data.BeaconBlockRoot, 119 Epoch: 0, 120 }}, 121 AttPool: attestations.NewPool(), 122 }, 123 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 124 seenUnAggregatedAttestationCache: c, 125 } 126 127 sb = testutil.NewBeaconBlock() 128 r32, err := sb.Block.HashTreeRoot() 129 require.NoError(t, err) 130 require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) 131 s, err := testutil.NewBeaconState() 132 require.NoError(t, err) 133 require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32)) 134 135 r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} 136 require.NoError(t, r.processPendingAtts(context.Background())) 137 138 atts, err := r.cfg.AttPool.UnaggregatedAttestations() 139 require.NoError(t, err) 140 assert.Equal(t, 1, len(atts), "Did not save unaggregated att") 141 assert.DeepEqual(t, att, atts[0], "Incorrect saved att") 142 assert.Equal(t, 0, len(r.cfg.AttPool.AggregatedAttestations()), "Did save aggregated att") 143 require.LogsContain(t, hook, "Verified and saved pending attestations to pool") 144 } 145 146 func TestProcessPendingAtts_NoBroadcastWithBadSignature(t *testing.T) { 147 db := dbtest.SetupDB(t) 148 p1 := p2ptest.NewTestP2P(t) 149 150 s, _ := testutil.DeterministicGenesisState(t, 256) 151 r := &Service{ 152 cfg: &Config{ 153 P2P: p1, 154 DB: db, 155 Chain: &mock.ChainService{State: s, Genesis: timeutils.Now(), FinalizedCheckPoint: ðpb.Checkpoint{Root: make([]byte, 32)}}, 156 AttPool: attestations.NewPool(), 157 }, 158 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 159 } 160 161 priv, err := bls.RandKey() 162 require.NoError(t, err) 163 a := ðpb.AggregateAttestationAndProof{ 164 Aggregate: ðpb.Attestation{ 165 Signature: priv.Sign([]byte("foo")).Marshal(), 166 AggregationBits: bitfield.Bitlist{0x02}, 167 Data: testutil.HydrateAttestationData(ðpb.AttestationData{}), 168 }, 169 SelectionProof: make([]byte, 96), 170 } 171 172 b := testutil.NewBeaconBlock() 173 r32, err := b.Block.HashTreeRoot() 174 require.NoError(t, err) 175 require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(b))) 176 require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32)) 177 178 r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: a, Signature: make([]byte, 96)}} 179 require.NoError(t, r.processPendingAtts(context.Background())) 180 181 assert.Equal(t, false, p1.BroadcastCalled, "Broadcasted bad aggregate") 182 // Clear pool. 183 err = r.cfg.AttPool.DeleteUnaggregatedAttestation(a.Aggregate) 184 require.NoError(t, err) 185 186 validators := uint64(256) 187 188 _, privKeys := testutil.DeterministicGenesisState(t, validators) 189 aggBits := bitfield.NewBitlist(8) 190 aggBits.SetBitAt(1, true) 191 att := ðpb.Attestation{ 192 Data: ðpb.AttestationData{ 193 BeaconBlockRoot: r32[:], 194 Source: ðpb.Checkpoint{Epoch: 0, Root: bytesutil.PadTo([]byte("hello-world"), 32)}, 195 Target: ðpb.Checkpoint{Epoch: 0, Root: r32[:]}, 196 }, 197 AggregationBits: aggBits, 198 } 199 committee, err := helpers.BeaconCommitteeFromState(s, att.Data.Slot, att.Data.CommitteeIndex) 200 assert.NoError(t, err) 201 attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee) 202 require.NoError(t, err) 203 assert.NoError(t, err) 204 attesterDomain, err := helpers.Domain(s.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, s.GenesisValidatorRoot()) 205 require.NoError(t, err) 206 hashTreeRoot, err := helpers.ComputeSigningRoot(att.Data, attesterDomain) 207 assert.NoError(t, err) 208 for _, i := range attestingIndices { 209 att.Signature = privKeys[i].Sign(hashTreeRoot[:]).Marshal() 210 } 211 212 // Arbitrary aggregator index for testing purposes. 213 aggregatorIndex := committee[0] 214 sszSlot := types.SSZUint64(att.Data.Slot) 215 sig, err := helpers.ComputeDomainAndSign(s, 0, &sszSlot, params.BeaconConfig().DomainSelectionProof, privKeys[aggregatorIndex]) 216 require.NoError(t, err) 217 aggregateAndProof := ðpb.AggregateAttestationAndProof{ 218 SelectionProof: sig, 219 Aggregate: att, 220 AggregatorIndex: aggregatorIndex, 221 } 222 aggreSig, err := helpers.ComputeDomainAndSign(s, 0, aggregateAndProof, params.BeaconConfig().DomainAggregateAndProof, privKeys[aggregatorIndex]) 223 require.NoError(t, err) 224 225 require.NoError(t, s.SetGenesisTime(uint64(time.Now().Unix()))) 226 c, err := lru.New(10) 227 require.NoError(t, err) 228 r = &Service{ 229 cfg: &Config{ 230 P2P: p1, 231 DB: db, 232 Chain: &mock.ChainService{Genesis: time.Now(), 233 State: s, 234 FinalizedCheckPoint: ðpb.Checkpoint{ 235 Root: aggregateAndProof.Aggregate.Data.BeaconBlockRoot, 236 Epoch: 0, 237 }}, 238 AttPool: attestations.NewPool(), 239 }, 240 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 241 seenUnAggregatedAttestationCache: c, 242 } 243 244 r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} 245 require.NoError(t, r.processPendingAtts(context.Background())) 246 247 assert.Equal(t, true, p1.BroadcastCalled, "Could not broadcast the good aggregate") 248 } 249 250 func TestProcessPendingAtts_HasBlockSaveAggregatedAtt(t *testing.T) { 251 hook := logTest.NewGlobal() 252 db := dbtest.SetupDB(t) 253 p1 := p2ptest.NewTestP2P(t) 254 validators := uint64(256) 255 256 beaconState, privKeys := testutil.DeterministicGenesisState(t, validators) 257 258 sb := testutil.NewBeaconBlock() 259 require.NoError(t, db.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) 260 root, err := sb.Block.HashTreeRoot() 261 require.NoError(t, err) 262 263 aggBits := bitfield.NewBitlist(validators / uint64(params.BeaconConfig().SlotsPerEpoch)) 264 aggBits.SetBitAt(0, true) 265 aggBits.SetBitAt(1, true) 266 att := ðpb.Attestation{ 267 Data: ðpb.AttestationData{ 268 BeaconBlockRoot: root[:], 269 Source: ðpb.Checkpoint{Epoch: 0, Root: bytesutil.PadTo([]byte("hello-world"), 32)}, 270 Target: ðpb.Checkpoint{Epoch: 0, Root: root[:]}, 271 }, 272 AggregationBits: aggBits, 273 } 274 275 committee, err := helpers.BeaconCommitteeFromState(beaconState, att.Data.Slot, att.Data.CommitteeIndex) 276 assert.NoError(t, err) 277 attestingIndices, err := attestationutil.AttestingIndices(att.AggregationBits, committee) 278 require.NoError(t, err) 279 assert.NoError(t, err) 280 attesterDomain, err := helpers.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconAttester, beaconState.GenesisValidatorRoot()) 281 require.NoError(t, err) 282 hashTreeRoot, err := helpers.ComputeSigningRoot(att.Data, attesterDomain) 283 assert.NoError(t, err) 284 sigs := make([]bls.Signature, len(attestingIndices)) 285 for i, indice := range attestingIndices { 286 sig := privKeys[indice].Sign(hashTreeRoot[:]) 287 sigs[i] = sig 288 } 289 att.Signature = bls.AggregateSignatures(sigs).Marshal() 290 291 // Arbitrary aggregator index for testing purposes. 292 aggregatorIndex := committee[0] 293 sszUint := types.SSZUint64(att.Data.Slot) 294 sig, err := helpers.ComputeDomainAndSign(beaconState, 0, &sszUint, params.BeaconConfig().DomainSelectionProof, privKeys[aggregatorIndex]) 295 require.NoError(t, err) 296 aggregateAndProof := ðpb.AggregateAttestationAndProof{ 297 SelectionProof: sig, 298 Aggregate: att, 299 AggregatorIndex: aggregatorIndex, 300 } 301 aggreSig, err := helpers.ComputeDomainAndSign(beaconState, 0, aggregateAndProof, params.BeaconConfig().DomainAggregateAndProof, privKeys[aggregatorIndex]) 302 require.NoError(t, err) 303 304 require.NoError(t, beaconState.SetGenesisTime(uint64(time.Now().Unix()))) 305 306 c, err := lru.New(10) 307 require.NoError(t, err) 308 r := &Service{ 309 cfg: &Config{ 310 P2P: p1, 311 DB: db, 312 Chain: &mock.ChainService{Genesis: time.Now(), 313 State: beaconState, 314 FinalizedCheckPoint: ðpb.Checkpoint{ 315 Root: aggregateAndProof.Aggregate.Data.BeaconBlockRoot, 316 Epoch: 0, 317 }}, 318 AttPool: attestations.NewPool(), 319 }, 320 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 321 seenAggregatedAttestationCache: c, 322 } 323 324 sb = testutil.NewBeaconBlock() 325 r32, err := sb.Block.HashTreeRoot() 326 require.NoError(t, err) 327 require.NoError(t, r.cfg.DB.SaveBlock(context.Background(), wrapper.WrappedPhase0SignedBeaconBlock(sb))) 328 s, err := testutil.NewBeaconState() 329 require.NoError(t, err) 330 require.NoError(t, r.cfg.DB.SaveState(context.Background(), s, r32)) 331 332 r.blkRootToPendingAtts[r32] = []*ethpb.SignedAggregateAttestationAndProof{{Message: aggregateAndProof, Signature: aggreSig}} 333 require.NoError(t, r.processPendingAtts(context.Background())) 334 335 assert.Equal(t, 1, len(r.cfg.AttPool.AggregatedAttestations()), "Did not save aggregated att") 336 assert.DeepEqual(t, att, r.cfg.AttPool.AggregatedAttestations()[0], "Incorrect saved att") 337 atts, err := r.cfg.AttPool.UnaggregatedAttestations() 338 require.NoError(t, err) 339 assert.Equal(t, 0, len(atts), "Did save aggregated att") 340 require.LogsContain(t, hook, "Verified and saved pending attestations to pool") 341 } 342 343 func TestValidatePendingAtts_CanPruneOldAtts(t *testing.T) { 344 s := &Service{ 345 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 346 } 347 348 // 100 Attestations per block root. 349 r1 := [32]byte{'A'} 350 r2 := [32]byte{'B'} 351 r3 := [32]byte{'C'} 352 353 for i := types.Slot(0); i < 100; i++ { 354 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 355 Message: ðpb.AggregateAttestationAndProof{ 356 AggregatorIndex: types.ValidatorIndex(i), 357 Aggregate: ðpb.Attestation{ 358 Data: ðpb.AttestationData{Slot: i, BeaconBlockRoot: r1[:]}}}}) 359 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 360 Message: ðpb.AggregateAttestationAndProof{ 361 AggregatorIndex: types.ValidatorIndex(i*2 + i), 362 Aggregate: ðpb.Attestation{ 363 Data: ðpb.AttestationData{Slot: i, BeaconBlockRoot: r2[:]}}}}) 364 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 365 Message: ðpb.AggregateAttestationAndProof{ 366 AggregatorIndex: types.ValidatorIndex(i*3 + i), 367 Aggregate: ðpb.Attestation{ 368 Data: ðpb.AttestationData{Slot: i, BeaconBlockRoot: r3[:]}}}}) 369 } 370 371 assert.Equal(t, 100, len(s.blkRootToPendingAtts[r1]), "Did not save pending atts") 372 assert.Equal(t, 100, len(s.blkRootToPendingAtts[r2]), "Did not save pending atts") 373 assert.Equal(t, 100, len(s.blkRootToPendingAtts[r3]), "Did not save pending atts") 374 375 // Set current slot to 50, it should prune 19 attestations. (50 - 31) 376 s.validatePendingAtts(context.Background(), 50) 377 assert.Equal(t, 81, len(s.blkRootToPendingAtts[r1]), "Did not delete pending atts") 378 assert.Equal(t, 81, len(s.blkRootToPendingAtts[r2]), "Did not delete pending atts") 379 assert.Equal(t, 81, len(s.blkRootToPendingAtts[r3]), "Did not delete pending atts") 380 381 // Set current slot to 100 + slot_duration, it should prune all the attestations. 382 s.validatePendingAtts(context.Background(), 100+params.BeaconConfig().SlotsPerEpoch) 383 assert.Equal(t, 0, len(s.blkRootToPendingAtts[r1]), "Did not delete pending atts") 384 assert.Equal(t, 0, len(s.blkRootToPendingAtts[r2]), "Did not delete pending atts") 385 assert.Equal(t, 0, len(s.blkRootToPendingAtts[r3]), "Did not delete pending atts") 386 387 // Verify the keys are deleted. 388 assert.Equal(t, 0, len(s.blkRootToPendingAtts), "Did not delete block keys") 389 } 390 391 func TestValidatePendingAtts_NoDuplicatingAggregatorIndex(t *testing.T) { 392 s := &Service{ 393 blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof), 394 } 395 396 r1 := [32]byte{'A'} 397 r2 := [32]byte{'B'} 398 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 399 Message: ðpb.AggregateAttestationAndProof{ 400 AggregatorIndex: 1, 401 Aggregate: ðpb.Attestation{ 402 Data: ðpb.AttestationData{Slot: 1, BeaconBlockRoot: r1[:]}}}}) 403 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 404 Message: ðpb.AggregateAttestationAndProof{ 405 AggregatorIndex: 2, 406 Aggregate: ðpb.Attestation{ 407 Data: ðpb.AttestationData{Slot: 2, BeaconBlockRoot: r2[:]}}}}) 408 s.savePendingAtt(ðpb.SignedAggregateAttestationAndProof{ 409 Message: ðpb.AggregateAttestationAndProof{ 410 AggregatorIndex: 2, 411 Aggregate: ðpb.Attestation{ 412 Data: ðpb.AttestationData{Slot: 3, BeaconBlockRoot: r2[:]}}}}) 413 414 assert.Equal(t, 1, len(s.blkRootToPendingAtts[r1]), "Did not save pending atts") 415 assert.Equal(t, 1, len(s.blkRootToPendingAtts[r2]), "Did not save pending atts") 416 }