mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-22 19:40:37 +00:00
Verify Block Signatures On Insertion Into Pending Queue (#13183)
* add check for bad signatures via gossip * edge case handled
This commit is contained in:
parent
da2212f6cc
commit
80526a1899
@ -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()
|
||||
|
@ -554,7 +554,8 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
|
||||
FinalizedCheckPoint: ðpb.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, ðpb.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: ðpb.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) {
|
||||
|
Loading…
Reference in New Issue
Block a user