Verify Block Signatures On Insertion Into Pending Queue (#13183)

* add check for bad signatures via gossip

* edge case handled
This commit is contained in:
Nishant Das 2023-11-28 11:13:59 +08:00 committed by GitHub
parent da2212f6cc
commit 80526a1899
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 5 deletions

View File

@ -156,6 +156,10 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
// Process the block if the clock jitter is less than MAXIMUM_GOSSIP_CLOCK_DISPARITY.
// Otherwise queue it for processing in the right slot.
if isBlockQueueable(genesisTime, blk.Block().Slot(), receivedTime) {
if res, err := s.verifyPendingBlockSignature(ctx, blk, blockRoot); err != nil {
log.WithError(err).WithFields(getBlockFields(blk)).Debug("Could not verify block signature")
return res, err
}
s.pendingQueueLock.Lock()
if err := s.insertBlockToPendingQueue(blk.Block().Slot(), blk, blockRoot); err != nil {
s.pendingQueueLock.Unlock()
@ -170,6 +174,10 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
// Handle block when the parent is unknown.
if !s.cfg.chain.HasBlock(ctx, blk.Block().ParentRoot()) {
if res, err := s.verifyPendingBlockSignature(ctx, blk, blockRoot); err != nil {
log.WithError(err).WithFields(getBlockFields(blk)).Debug("Could not verify block signature")
return res, err
}
s.pendingQueueLock.Lock()
if err := s.insertBlockToPendingQueue(blk.Block().Slot(), blk, blockRoot); err != nil {
s.pendingQueueLock.Unlock()
@ -343,6 +351,24 @@ func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState
return nil
}
// Verifies the signature of the pending block with respect to the current head state.
func (s *Service) verifyPendingBlockSignature(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock, blkRoot [32]byte) (pubsub.ValidationResult, error) {
roState, err := s.cfg.chain.HeadStateReadOnly(ctx)
if err != nil {
return pubsub.ValidationIgnore, err
}
// Ignore block in the event of non-existent proposer.
_, err = roState.ValidatorAtIndex(blk.Block().ProposerIndex())
if err != nil {
return pubsub.ValidationIgnore, err
}
if err := blocks.VerifyBlockSignatureUsingCurrentFork(roState, blk); err != nil {
s.setBadBlock(ctx, blkRoot)
return pubsub.ValidationReject, err
}
return pubsub.ValidationAccept, nil
}
// Returns true if the block is not the first block proposed for the proposer for the slot.
func (s *Service) hasSeenBlockIndexSlot(slot primitives.Slot, proposerIdx primitives.ValidatorIndex) bool {
s.seenBlockLock.RLock()

View File

@ -554,7 +554,8 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
}}
},
State: beaconState}
r := &Service{
cfg: &config{
p2p: p,
@ -950,8 +951,8 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
},
}
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
require.ErrorContains(t, "unknown parent for block", err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block with invalid parent should be ignored")
require.ErrorContains(t, "could not unmarshal bytes into signature", err)
assert.Equal(t, res, pubsub.ValidationReject, "block with invalid signature should be rejected")
require.NoError(t, copied.SetSlot(2))
proposerIdx, err = helpers.BeaconProposerIndex(ctx, copied)
@ -961,6 +962,8 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
msg.Block.Slot = 2
msg.Block.ProposerIndex = proposerIdx
msg.Block.ParentRoot = currBlockRoot[:]
msg.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
buf = new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@ -980,9 +983,73 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
r.cfg.clock = startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)
res, err = r.validateBeaconBlockPubSub(ctx, "", m)
require.ErrorContains(t, "unknown parent for block", err)
require.ErrorContains(t, "has an invalid parent", err)
// Expect block with bad parent to fail too
assert.Equal(t, res, pubsub.ValidationIgnore, "block with invalid parent should be ignored")
assert.Equal(t, res, pubsub.ValidationReject, "block with invalid parent should be ignored")
}
func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
db := dbtest.SetupDB(t)
p := p2ptest.NewTestP2P(t)
ctx := context.Background()
beaconState, privKeys := util.DeterministicGenesisState(t, 100)
parentBlock := util.NewBeaconBlock()
util.SaveBlock(t, ctx, db, parentBlock)
bRoot, err := parentBlock.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, db.SaveState(ctx, beaconState, bRoot))
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: bRoot[:]}))
copied := beaconState.Copy()
require.NoError(t, copied.SetSlot(1))
proposerIdx, err := helpers.BeaconProposerIndex(ctx, copied)
require.NoError(t, err)
msg := util.NewBeaconBlock()
msg.Block.ProposerIndex = proposerIdx
msg.Block.Slot = 1
msg.Block.ParentRoot = bRoot[:]
msg.Signature, err = signing.ComputeDomainAndSign(beaconState, 0, msg.Block, params.BeaconConfig().DomainBeaconProposer, privKeys[proposerIdx])
require.NoError(t, err)
stateGen := stategen.New(db, doublylinkedtree.New())
chainService := &mock.ChainService{Genesis: time.Unix(time.Now().Unix()-int64(params.BeaconConfig().SecondsPerSlot), 0),
State: beaconState,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
topic := p2p.GossipTypeMapping[reflect.TypeOf(msg)]
digest, err := r.currentForkDigest()
assert.NoError(t, err)
topic = r.addDigestToTopic(topic, digest)
m := &pubsub.Message{
Message: &pubsubpb.Message{
Data: buf.Bytes(),
Topic: &topic,
},
}
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
require.ErrorContains(t, "unknown parent for block", err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block with unknown parent should be ignored")
bRoot, err = msg.Block.HashTreeRoot()
assert.NoError(t, err)
assert.Equal(t, true, r.seenPendingBlocks[bRoot])
}
func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {