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:
Michael Sproul
2021-08-30 06:41:31 +00:00
parent 6b65b6f3bd
commit 10945e0619
16 changed files with 683 additions and 213 deletions

View File

@@ -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()

View File

@@ -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))