feat: sync: validate (early) that blocks fall within range (#10691)

This will reject blocks in pubsub validation if they're either:

1. Too far into the future (5 blocks beyond the expected head).
2. Too far into the past (before finality with respect to our current
   head).

Specifically:

1. We were previously rejecting future blocks in the sync logic, but not
   in pubsub itself.
2. We never used to check if a block was too _old_.

Motivation: Blocks that are too new/too old can cause us to perform
quite a bit of unnecessary work.
This commit is contained in:
Steven Allen 2023-04-22 10:15:31 -07:00 committed by GitHub
parent 875c09840b
commit 784214ae05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 23 additions and 6 deletions

View File

@ -382,13 +382,21 @@ func (filec *FilecoinEC) VerifyWinningPoStProof(ctx context.Context, nv network.
return nil
}
func (filec *FilecoinEC) IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool {
func (filec *FilecoinEC) IsEpochInConsensusRange(epoch abi.ChainEpoch) bool {
if filec.genesis == nil {
return true
}
// Don't try to sync anything before finality. Don't propagate such blocks either.
//
// We use _our_ current head, not the expected head, because the network's head can lag on
// catch-up (after a network outage).
if epoch < filec.store.GetHeaviestTipSet().Height()-build.Finality {
return false
}
now := uint64(build.Clock.Now().Unix())
return epoch > (abi.ChainEpoch((now-filec.genesis.MinTimestamp())/build.BlockDelaySecs) + MaxHeightDrift)
return epoch <= (abi.ChainEpoch((now-filec.genesis.MinTimestamp())/build.BlockDelaySecs) + MaxHeightDrift)
}
func (filec *FilecoinEC) minerIsValid(ctx context.Context, maddr address.Address, baseTs *types.TipSet) error {

View File

@ -32,9 +32,10 @@ type Consensus interface {
// the block (signature verifications, VRF checks, message validity, etc.)
ValidateBlock(ctx context.Context, b *types.FullBlock) (err error)
// IsEpochBeyondCurrMax is used to configure the fork rules for longest-chain
// consensus protocols.
IsEpochBeyondCurrMax(epoch abi.ChainEpoch) bool
// IsEpochInConsensusRange returns true if the epoch is "in range" for consensus. That is:
// - It's not before finality.
// - It's not too far in the future.
IsEpochInConsensusRange(epoch abi.ChainEpoch) bool
// CreateBlock implements all the logic required to propose and assemble a new Filecoin block.
//
@ -71,6 +72,14 @@ func ValidateBlockPubsub(ctx context.Context, cns Consensus, self bool, msg *pub
return pubsub.ValidationReject, what
}
if !cns.IsEpochInConsensusRange(blk.Header.Height) {
// We ignore these blocks instead of rejecting to avoid breaking the network if
// we're recovering from an outage (e.g., where nobody agrees on where "head" is
// currently).
log.Warnf("received block outside of consensus range (%d)", blk.Header.Height)
return pubsub.ValidationIgnore, "invalid_block_height"
}
// validate the block meta: the Message CID in the header must match the included messages
err = validateMsgMeta(ctx, blk)
if err != nil {

View File

@ -208,7 +208,7 @@ func (syncer *Syncer) InformNewHead(from peer.ID, fts *store.FullTipSet) bool {
return false
}
if syncer.consensus.IsEpochBeyondCurrMax(fts.TipSet().Height()) {
if !syncer.consensus.IsEpochInConsensusRange(fts.TipSet().Height()) {
log.Errorf("Received block with impossibly large height %d", fts.TipSet().Height())
return false
}