diff --git a/beacon-chain/blockchain/process_attestation.go b/beacon-chain/blockchain/process_attestation.go index a2cd4acce1f2..258f661bd357 100644 --- a/beacon-chain/blockchain/process_attestation.go +++ b/beacon-chain/blockchain/process_attestation.go @@ -80,7 +80,7 @@ func (s *Service) onAttestation(ctx context.Context, a *ethpb.Attestation) ([]ui } // Verify beacon node has seen the target block before. - if !s.beaconDB.HasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) { + if !s.hasBlock(ctx, bytesutil.ToBytes32(tgt.Root)) { return nil, ErrTargetRootNotInDB } diff --git a/beacon-chain/blockchain/process_attestation_test.go b/beacon-chain/blockchain/process_attestation_test.go index c43069e7f53b..181c1133db9b 100644 --- a/beacon-chain/blockchain/process_attestation_test.go +++ b/beacon-chain/blockchain/process_attestation_test.go @@ -11,6 +11,7 @@ import ( "github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/beacon-chain/core/state" testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" + "github.com/prysmaticlabs/prysm/beacon-chain/forkchoice/protoarray" beaconstate "github.com/prysmaticlabs/prysm/beacon-chain/state" stateTrie "github.com/prysmaticlabs/prysm/beacon-chain/state" pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" @@ -24,7 +25,7 @@ func TestStore_OnAttestation(t *testing.T) { db := testDB.SetupDB(t) defer testDB.TeardownDB(t, db) - cfg := &Config{BeaconDB: db} + cfg := &Config{BeaconDB: db, ForkChoiceStore: protoarray.New(0, 0, [32]byte{})} service, err := NewService(ctx, cfg) if err != nil { t.Fatal(err) diff --git a/beacon-chain/blockchain/receive_attestation.go b/beacon-chain/blockchain/receive_attestation.go index 587f77a06587..e41f732ca092 100644 --- a/beacon-chain/blockchain/receive_attestation.go +++ b/beacon-chain/blockchain/receive_attestation.go @@ -74,7 +74,7 @@ func (s *Service) processAttestation() { atts := s.attPool.ForkchoiceAttestations() for _, a := range atts { hasState := s.beaconDB.HasState(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) && s.beaconDB.HasState(ctx, bytesutil.ToBytes32(a.Data.Target.Root)) - hasBlock := s.beaconDB.HasBlock(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) + hasBlock := s.hasBlock(ctx, bytesutil.ToBytes32(a.Data.BeaconBlockRoot)) if !(hasState && hasBlock) { continue } diff --git a/beacon-chain/blockchain/service.go b/beacon-chain/blockchain/service.go index d0328ecf6f97..b06ac3366565 100644 --- a/beacon-chain/blockchain/service.go +++ b/beacon-chain/blockchain/service.go @@ -434,3 +434,15 @@ func (s *Service) resumeForkChoice(justifiedCheckpoint *ethpb.Checkpoint, finali store := protoarray.New(justifiedCheckpoint.Epoch, finalizedCheckpoint.Epoch, bytesutil.ToBytes32(finalizedCheckpoint.Root)) s.forkChoiceStore = store } + +// This returns true if block has been processed before. Two ways to verify the block has been processed: +// 1.) Check fork choice store. +// 2.) Check DB. +// Checking 1.) is ten times faster than checking 2.) +func (s *Service) hasBlock(ctx context.Context, root [32]byte) bool { + if s.forkChoiceStore.HasNode(root) { + return true + } + + return s.beaconDB.HasBlock(ctx, root) +} diff --git a/beacon-chain/blockchain/service_test.go b/beacon-chain/blockchain/service_test.go index 66825f68cac5..d5de66fd3508 100644 --- a/beacon-chain/blockchain/service_test.go +++ b/beacon-chain/blockchain/service_test.go @@ -453,3 +453,75 @@ func TestChainService_PruneOldStates(t *testing.T) { } } } + +func TestHasBlock_ForkChoiceAndDB(t *testing.T) { + ctx := context.Background() + db := testDB.SetupDB(t) + defer testDB.TeardownDB(t, db) + s := &Service{ + forkChoiceStore: protoarray.New(0, 0, [32]byte{}), + finalizedCheckpt: ðpb.Checkpoint{}, + beaconDB: db, + } + block := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}}} + r, _ := ssz.HashTreeRoot(block.Block) + bs := &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{}} + state, _ := beaconstate.InitializeFromProto(bs) + if err := s.insertBlockToForkChoiceStore(ctx, block.Block, r, state); err != nil { + t.Fatal(err) + } + + if s.hasBlock(ctx, [32]byte{}) { + t.Error("Should not have block") + } + + if !s.hasBlock(ctx, r) { + t.Error("Should have block") + } +} + +func BenchmarkHasBlockDB(b *testing.B) { + db := testDB.SetupDB(b) + defer testDB.TeardownDB(b, db) + ctx := context.Background() + s := &Service{ + beaconDB: db, + } + block := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{}} + if err := s.beaconDB.SaveBlock(ctx, block); err != nil { + b.Fatal(err) + } + r, _ := ssz.HashTreeRoot(block.Block) + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if !s.beaconDB.HasBlock(ctx, r) { + b.Fatal("Block is not in DB") + } + } +} + +func BenchmarkHasBlockForkChoiceStore(b *testing.B) { + ctx := context.Background() + db := testDB.SetupDB(b) + defer testDB.TeardownDB(b, db) + s := &Service{ + forkChoiceStore: protoarray.New(0, 0, [32]byte{}), + finalizedCheckpt: ðpb.Checkpoint{}, + beaconDB: db, + } + block := ðpb.SignedBeaconBlock{Block: ðpb.BeaconBlock{Body: ðpb.BeaconBlockBody{}}} + r, _ := ssz.HashTreeRoot(block.Block) + bs := &pb.BeaconState{FinalizedCheckpoint: ðpb.Checkpoint{}, CurrentJustifiedCheckpoint: ðpb.Checkpoint{}} + state, _ := beaconstate.InitializeFromProto(bs) + if err := s.insertBlockToForkChoiceStore(ctx, block.Block, r, state); err != nil { + b.Fatal(err) + } + + b.ResetTimer() + for i := 0; i < b.N; i++ { + if !s.forkChoiceStore.HasNode(r) { + b.Fatal("Block is not in fork choice store") + } + } +}