diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index a9fd6684f..e662defbb 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -46,6 +46,7 @@ type ChainService struct { opNotifier opfeed.Notifier ValidAttestation bool ForkChoiceStore *protoarray.Store + VerifyBlkDescendantErr error } // StateNotifier mocks the same method in the chain service. @@ -350,5 +351,5 @@ func (ms *ChainService) HeadGenesisValidatorRoot() [32]byte { // VerifyBlkDescendant mocks VerifyBlkDescendant and always returns nil. func (ms *ChainService) VerifyBlkDescendant(ctx context.Context, root [32]byte) error { - return nil + return ms.VerifyBlkDescendantErr } diff --git a/beacon-chain/sync/validate_beacon_blocks.go b/beacon-chain/sync/validate_beacon_blocks.go index 02d06bef0..aff11c4e6 100644 --- a/beacon-chain/sync/validate_beacon_blocks.go +++ b/beacon-chain/sync/validate_beacon_blocks.go @@ -110,6 +110,11 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms return pubsub.ValidationIgnore } + if err := s.chain.VerifyBlkDescendant(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot)); err != nil { + log.WithError(err).Warn("Rejecting block") + return pubsub.ValidationReject + } + hasStateSummaryDB := s.db.HasStateSummary(ctx, bytesutil.ToBytes32(blk.Block.ParentRoot)) hasStateSummaryCache := s.stateSummaryCache.Has(bytesutil.ToBytes32(blk.Block.ParentRoot)) if !hasStateSummaryDB && !hasStateSummaryCache { diff --git a/beacon-chain/sync/validate_beacon_blocks_test.go b/beacon-chain/sync/validate_beacon_blocks_test.go index 2739b50ac..d58adce08 100644 --- a/beacon-chain/sync/validate_beacon_blocks_test.go +++ b/beacon-chain/sync/validate_beacon_blocks_test.go @@ -3,6 +3,7 @@ package sync import ( "bytes" "context" + "errors" "reflect" "testing" "time" @@ -632,3 +633,96 @@ func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) { r.validateBeaconBlockPubSub(context.Background(), "", m) testutil.AssertLogsDoNotContain(t, hook, "Block slot older/equal than last finalized epoch start slot, rejecting itt") } + +func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) { + hook := logTest.NewGlobal() + db, stateSummaryCache := dbtest.SetupDB(t) + p := p2ptest.NewTestP2P(t) + ctx := context.Background() + beaconState, privKeys := testutil.DeterministicGenesisState(t, 100) + parentBlock := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ProposerIndex: 0, + Slot: 0, + }, + } + if err := db.SaveBlock(ctx, parentBlock); err != nil { + t.Fatal(err) + } + bRoot, err := stateutil.BlockRoot(parentBlock.Block) + if err := db.SaveState(ctx, beaconState, bRoot); err != nil { + t.Fatal(err) + } + if err := db.SaveStateSummary(ctx, &pb.StateSummary{ + Root: bRoot[:], + }); err != nil { + t.Fatal(err) + } + copied := beaconState.Copy() + if err := copied.SetSlot(1); err != nil { + t.Fatal(err) + } + proposerIdx, err := helpers.BeaconProposerIndex(copied) + if err != nil { + t.Fatal(err) + } + msg := ðpb.SignedBeaconBlock{ + Block: ðpb.BeaconBlock{ + ProposerIndex: proposerIdx, + Slot: 1, + ParentRoot: bRoot[:], + }, + } + + domain, err := helpers.Domain(beaconState.Fork(), 0, params.BeaconConfig().DomainBeaconProposer, beaconState.GenesisValidatorRoot()) + if err != nil { + t.Fatal(err) + } + signingRoot, err := helpers.ComputeSigningRoot(msg.Block, domain) + if err != nil { + t.Error(err) + } + blockSig := privKeys[proposerIdx].Sign(signingRoot[:]).Marshal() + msg.Signature = blockSig[:] + + c, err := lru.New(10) + if err != nil { + t.Fatal(err) + } + stateGen := stategen.New(db, stateSummaryCache) + chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0), + State: beaconState, + FinalizedCheckPoint: ðpb.Checkpoint{ + Epoch: 0, + }, + VerifyBlkDescendantErr: errors.New("not part of finalized chain"), + } + r := &Service{ + db: db, + p2p: p, + initialSync: &mockSync.Sync{IsSyncing: false}, + chain: chainService, + blockNotifier: chainService.BlockNotifier(), + seenBlockCache: c, + slotToPendingBlocks: make(map[uint64]*ethpb.SignedBeaconBlock), + seenPendingBlocks: make(map[[32]byte]bool), + stateSummaryCache: stateSummaryCache, + stateGen: stateGen, + } + buf := new(bytes.Buffer) + if _, err := p.Encoding().EncodeGossip(buf, msg); err != nil { + t.Fatal(err) + } + m := &pubsub.Message{ + Message: &pubsubpb.Message{ + Data: buf.Bytes(), + TopicIDs: []string{ + p2p.GossipTypeMapping[reflect.TypeOf(msg)], + }, + }, + } + if res := r.validateBeaconBlockPubSub(ctx, "", m); res != pubsub.ValidationReject { + t.Error("Wrong validation result returned") + } + testutil.AssertLogsContain(t, hook, "not part of finalized chain") +}