Ignore blocks that skip a large distance from their parent (#1530)

## Proposed Changes

To mitigate the impact of minority forks on RAM and disk usage, this change rejects blocks whose parent lies more than 320 slots (10 epochs, ~1 hour) in the past. The behaviour is configurable via `lighthouse bn --max-skip-slots N`, and can be turned off entirely using `--max-skip-slots none`.

Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
Michael Sproul
2020-08-17 10:54:58 +00:00
parent a58aa6ee55
commit 719a69aee0
13 changed files with 131 additions and 4 deletions

View File

@@ -220,6 +220,12 @@ pub enum Error {
///
/// The peer has sent an invalid message.
Invalid(AttestationValidationError),
/// The attestation head block is too far behind the attestation slot, causing many skip slots.
/// This is deemed a DoS risk.
TooManySkippedSlots {
head_block_slot: Slot,
attestation_slot: Slot,
},
/// There was an error whilst processing the attestation. It is not known if it is valid or invalid.
///
/// ## Peer scoring
@@ -319,6 +325,7 @@ impl<T: BeaconChainTypes> VerifiedAggregatedAttestation<T> {
}?;
// Ensure the block being voted for (attestation.data.beacon_block_root) passes validation.
// Don't enforce the skip slot restriction for aggregates.
//
// This indirectly checks to see if the `attestation.data.beacon_block_root` is in our fork
// choice. Any known, non-finalized, processed block should be in fork choice, so this
@@ -327,7 +334,7 @@ impl<T: BeaconChainTypes> VerifiedAggregatedAttestation<T> {
//
// Attestations must be for a known block. If the block is unknown, we simply drop the
// attestation and do not delay consideration for later.
verify_head_block_is_known(chain, &attestation)?;
verify_head_block_is_known(chain, &attestation, None)?;
// Ensure that the attestation has participants.
if attestation.aggregation_bits.is_zero() {
@@ -433,7 +440,9 @@ impl<T: BeaconChainTypes> VerifiedUnaggregatedAttestation<T> {
// Attestations must be for a known block. If the block is unknown, we simply drop the
// attestation and do not delay consideration for later.
verify_head_block_is_known(chain, &attestation)?;
//
// Enforce a maximum skip distance for unaggregated attestations.
verify_head_block_is_known(chain, &attestation, chain.config.import_max_skip_slots)?;
let (indexed_attestation, committees_per_slot) =
obtain_indexed_attestation_and_committees_per_slot(chain, &attestation)?;
@@ -531,12 +540,22 @@ impl<T: BeaconChainTypes> VerifiedUnaggregatedAttestation<T> {
fn verify_head_block_is_known<T: BeaconChainTypes>(
chain: &BeaconChain<T>,
attestation: &Attestation<T::EthSpec>,
max_skip_slots: Option<u64>,
) -> Result<(), Error> {
if chain
if let Some(block) = chain
.fork_choice
.read()
.contains_block(&attestation.data.beacon_block_root)
.get_block(&attestation.data.beacon_block_root)
{
// Reject any block that exceeds our limit on skipped slots.
if let Some(max_skip_slots) = max_skip_slots {
if block.slot > attestation.data.slot + max_skip_slots {
return Err(Error::TooManySkippedSlots {
head_block_slot: block.slot,
attestation_slot: attestation.data.slot,
});
}
}
Ok(())
} else {
Err(Error::UnknownHeadBlock {