Reject a block which its parent is not part of the finalized chain (#6549)

* Reject a block which its parent is not part of the finalized chain
* Refactor verifyBlkDescendant to be public and omit the slot argument, which was only used for error messages
* Merge branch 'refactor-verifyBlkDescendant-signature' into reject-bad-block
* fix
* impl in mock
* impl in mock
* gofmt
* Merge refs/heads/master into refactor-verifyBlkDescendant-signature
* Merge branch 'refactor-verifyBlkDescendant-signature' into reject-bad-block
* fix test
* add test
* Merge branch 'refactor-verifyBlkDescendant-signature' into reject-bad-block
* gofmt
* Merge branch 'master' of github.com:prysmaticlabs/prysm into reject-bad-block
* move up in the validation pipeline, @terencechain offline feedback
* Merge refs/heads/master into reject-bad-block
This commit is contained in:
Preston Van Loon 2020-07-10 20:57:42 -07:00 committed by GitHub
parent c9ca5857f8
commit 1f35384578
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 1 deletions

View File

@ -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
}

View File

@ -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 {

View File

@ -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 := &ethpb.SignedBeaconBlock{
Block: &ethpb.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 := &ethpb.SignedBeaconBlock{
Block: &ethpb.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: &ethpb.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")
}