mirror of
https://github.com/sigp/lighthouse.git
synced 2026-07-03 21:04:28 +00:00
Revert bad blocks on missed fork (#2529)
## Issue Addressed Closes #2526 ## Proposed Changes If the head block fails to decode on start up, do two things: 1. Revert all blocks between the head and the most recent hard fork (to `fork_slot - 1`). 2. Reset fork choice so that it contains the new head, and all blocks back to the new head's finalized checkpoint. ## Additional Info I tweaked some of the beacon chain test harness stuff in order to make it generic enough to test with a non-zero slot clock on start-up. In the process I consolidated all the various `new_` methods into a single generic one which will hopefully serve all future uses 🤞
This commit is contained in:
@@ -4,7 +4,7 @@ use proto_array::{Block as ProtoBlock, ProtoArrayForkChoice};
|
||||
use ssz_derive::{Decode, Encode};
|
||||
use types::{
|
||||
AttestationShufflingId, BeaconBlock, BeaconState, BeaconStateError, Checkpoint, Epoch, EthSpec,
|
||||
Hash256, IndexedAttestation, RelativeEpoch, Slot,
|
||||
Hash256, IndexedAttestation, RelativeEpoch, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
use crate::ForkChoiceStore;
|
||||
@@ -38,6 +38,10 @@ pub enum Error<T> {
|
||||
ForkChoiceStoreError(T),
|
||||
UnableToSetJustifiedCheckpoint(T),
|
||||
AfterBlockFailed(T),
|
||||
InvalidAnchor {
|
||||
block_slot: Slot,
|
||||
state_slot: Slot,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> From<InvalidAttestation> for Error<T> {
|
||||
@@ -237,20 +241,28 @@ where
|
||||
T: ForkChoiceStore<E>,
|
||||
E: EthSpec,
|
||||
{
|
||||
/// Instantiates `Self` from the genesis parameters.
|
||||
pub fn from_genesis(
|
||||
/// Instantiates `Self` from an anchor (genesis or another finalized checkpoint).
|
||||
pub fn from_anchor(
|
||||
fc_store: T,
|
||||
genesis_block_root: Hash256,
|
||||
genesis_block: &BeaconBlock<E>,
|
||||
genesis_state: &BeaconState<E>,
|
||||
anchor_block_root: Hash256,
|
||||
anchor_block: &SignedBeaconBlock<E>,
|
||||
anchor_state: &BeaconState<E>,
|
||||
) -> Result<Self, Error<T::Error>> {
|
||||
let finalized_block_slot = genesis_block.slot();
|
||||
let finalized_block_state_root = genesis_block.state_root();
|
||||
// Sanity check: the anchor must lie on an epoch boundary.
|
||||
if anchor_block.slot() % E::slots_per_epoch() != 0 {
|
||||
return Err(Error::InvalidAnchor {
|
||||
block_slot: anchor_block.slot(),
|
||||
state_slot: anchor_state.slot(),
|
||||
});
|
||||
}
|
||||
|
||||
let finalized_block_slot = anchor_block.slot();
|
||||
let finalized_block_state_root = anchor_block.state_root();
|
||||
let current_epoch_shuffling_id =
|
||||
AttestationShufflingId::new(genesis_block_root, genesis_state, RelativeEpoch::Current)
|
||||
AttestationShufflingId::new(anchor_block_root, anchor_state, RelativeEpoch::Current)
|
||||
.map_err(Error::BeaconStateError)?;
|
||||
let next_epoch_shuffling_id =
|
||||
AttestationShufflingId::new(genesis_block_root, genesis_state, RelativeEpoch::Next)
|
||||
AttestationShufflingId::new(anchor_block_root, anchor_state, RelativeEpoch::Next)
|
||||
.map_err(Error::BeaconStateError)?;
|
||||
|
||||
let proto_array = ProtoArrayForkChoice::new(
|
||||
|
||||
@@ -48,12 +48,10 @@ impl fmt::Debug for ForkChoiceTest {
|
||||
impl ForkChoiceTest {
|
||||
/// Creates a new tester.
|
||||
pub fn new() -> Self {
|
||||
let harness = BeaconChainHarness::new_with_target_aggregators(
|
||||
let harness = BeaconChainHarness::new_with_store_config(
|
||||
MainnetEthSpec,
|
||||
None,
|
||||
generate_deterministic_keypairs(VALIDATOR_COUNT),
|
||||
// Ensure we always have an aggregator for each slot.
|
||||
u64::max_value(),
|
||||
StoreConfig::default(),
|
||||
);
|
||||
|
||||
@@ -66,8 +64,6 @@ impl ForkChoiceTest {
|
||||
MainnetEthSpec,
|
||||
None,
|
||||
generate_deterministic_keypairs(VALIDATOR_COUNT),
|
||||
// Ensure we always have an aggregator for each slot.
|
||||
u64::max_value(),
|
||||
StoreConfig::default(),
|
||||
chain_config,
|
||||
);
|
||||
|
||||
@@ -88,6 +88,17 @@ impl<T: EthSpec> BeaconBlock<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Try decoding each beacon block variant in sequence.
|
||||
///
|
||||
/// This is *not* recommended unless you really have no idea what variant the block should be.
|
||||
/// Usually it's better to prefer `from_ssz_bytes` which will decode the correct variant based
|
||||
/// on the fork slot.
|
||||
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
||||
BeaconBlockAltair::from_ssz_bytes(bytes)
|
||||
.map(BeaconBlock::Altair)
|
||||
.or_else(|_| BeaconBlockBase::from_ssz_bytes(bytes).map(BeaconBlock::Base))
|
||||
}
|
||||
|
||||
/// Convenience accessor for the `body` as a `BeaconBlockBodyRef`.
|
||||
pub fn body(&self) -> BeaconBlockBodyRef<'_, T> {
|
||||
self.to_ref().body()
|
||||
|
||||
@@ -74,10 +74,23 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
self.message().fork_name(spec)
|
||||
}
|
||||
|
||||
/// SSZ decode.
|
||||
/// SSZ decode with fork variant determined by slot.
|
||||
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
|
||||
// We need to use the slot-switching `from_ssz_bytes` of `BeaconBlock`, which doesn't
|
||||
// compose with the other SSZ utils, so we duplicate some parts of `ssz_derive` here.
|
||||
Self::from_ssz_bytes_with(bytes, |bytes| BeaconBlock::from_ssz_bytes(bytes, spec))
|
||||
}
|
||||
|
||||
/// SSZ decode which attempts to decode all variants (slow).
|
||||
pub fn any_from_ssz_bytes(bytes: &[u8]) -> Result<Self, ssz::DecodeError> {
|
||||
Self::from_ssz_bytes_with(bytes, BeaconBlock::any_from_ssz_bytes)
|
||||
}
|
||||
|
||||
/// SSZ decode with custom decode function.
|
||||
pub fn from_ssz_bytes_with(
|
||||
bytes: &[u8],
|
||||
block_decoder: impl FnOnce(&[u8]) -> Result<BeaconBlock<E>, ssz::DecodeError>,
|
||||
) -> Result<Self, ssz::DecodeError> {
|
||||
// We need the customer decoder for `BeaconBlock`, which doesn't compose with the other
|
||||
// SSZ utils, so we duplicate some parts of `ssz_derive` here.
|
||||
let mut builder = ssz::SszDecoderBuilder::new(bytes);
|
||||
|
||||
builder.register_anonymous_variable_length_item()?;
|
||||
@@ -86,7 +99,7 @@ impl<E: EthSpec> SignedBeaconBlock<E> {
|
||||
let mut decoder = builder.build()?;
|
||||
|
||||
// Read the first item as a `BeaconBlock`.
|
||||
let message = decoder.decode_next_with(|bytes| BeaconBlock::from_ssz_bytes(bytes, spec))?;
|
||||
let message = decoder.decode_next_with(block_decoder)?;
|
||||
let signature = decoder.decode_next()?;
|
||||
|
||||
Ok(Self::from_block(message, signature))
|
||||
|
||||
Reference in New Issue
Block a user