Initial merge changes

Added Execution Payload from Rayonism Fork

Updated new Containers to match Merge Spec

Updated BeaconBlockBody for Merge Spec

Completed updating BeaconState and BeaconBlockBody

Modified ExecutionPayload<T> to use Transaction<T>

Mostly Finished Changes for beacon-chain.md

Added some things for fork-choice.md

Update to match new fork-choice.md/fork.md changes

ran cargo fmt

Added Missing Pieces in eth2_libp2p for Merge

fix ef test

Various Changes to Conform Closer to Merge Spec
This commit is contained in:
Mark Mackey
2021-09-08 13:45:22 -05:00
committed by Paul Hauner
parent c0122e1a52
commit 3718c36c51
53 changed files with 1293 additions and 155 deletions

View File

@@ -2385,7 +2385,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
let _fork_choice_block_timer =
metrics::start_timer(&metrics::FORK_CHOICE_PROCESS_BLOCK_TIMES);
fork_choice
.on_block(current_slot, &block, block_root, &state)
.on_block(current_slot, &block, block_root, &state, &self.spec)
.map_err(|e| BlockError::BeaconChainError(e.into()))?;
}
@@ -2786,6 +2786,11 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
SyncAggregate::new()
}))
};
// Closure to fetch a sync aggregate in cases where it is required.
let get_execution_payload = || -> Result<ExecutionPayload<_>, BlockProductionError> {
// TODO: actually get the payload from eth1 node..
Ok(ExecutionPayload::default())
};
let inner_block = match state {
BeaconState::Base(_) => BeaconBlock::Base(BeaconBlockBase {
@@ -2824,6 +2829,28 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
},
})
}
BeaconState::Merge(_) => {
let sync_aggregate = get_sync_aggregate()?;
let execution_payload = get_execution_payload()?;
BeaconBlock::Merge(BeaconBlockMerge {
slot,
proposer_index,
parent_root,
state_root: Hash256::zero(),
body: BeaconBlockBodyMerge {
randao_reveal,
eth1_data,
graffiti,
proposer_slashings: proposer_slashings.into(),
attester_slashings: attester_slashings.into(),
attestations,
deposits,
voluntary_exits,
sync_aggregate,
execution_payload,
},
})
}
};
let block = SignedBeaconBlock::from_block(

View File

@@ -48,7 +48,7 @@ use crate::{
BLOCK_PROCESSING_CACHE_LOCK_TIMEOUT, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
VALIDATOR_PUBKEY_CACHE_LOCK_TIMEOUT,
},
metrics, BeaconChain, BeaconChainError, BeaconChainTypes,
eth1_chain, metrics, BeaconChain, BeaconChainError, BeaconChainTypes,
};
use fork_choice::{ForkChoice, ForkChoiceStore};
use parking_lot::RwLockReadGuard;
@@ -56,6 +56,7 @@ use proto_array::Block as ProtoBlock;
use slog::{debug, error, Logger};
use slot_clock::SlotClock;
use ssz::Encode;
use state_processing::per_block_processing::{is_execution_enabled, is_merge_complete};
use state_processing::{
block_signature_verifier::{BlockSignatureVerifier, Error as BlockSignatureVerifierError},
per_block_processing, per_slot_processing,
@@ -68,9 +69,9 @@ use std::io::Write;
use store::{Error as DBError, HotColdDB, HotStateSummary, KeyValueStore, StoreOp};
use tree_hash::TreeHash;
use types::{
BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, CloneConfig, Epoch, EthSpec, Hash256,
InconsistentFork, PublicKey, PublicKeyBytes, RelativeEpoch, SignedBeaconBlock,
SignedBeaconBlockHeader, Slot,
BeaconBlockRef, BeaconState, BeaconStateError, ChainSpec, CloneConfig, Epoch, EthSpec,
ExecutionPayload, Hash256, InconsistentFork, PublicKey, PublicKeyBytes, RelativeEpoch,
SignedBeaconBlock, SignedBeaconBlockHeader, Slot,
};
/// Maximum block slot number. Block with slots bigger than this constant will NOT be processed.
@@ -223,6 +224,66 @@ pub enum BlockError<T: EthSpec> {
///
/// The block is invalid and the peer is faulty.
InconsistentFork(InconsistentFork),
/// There was an error while validating the ExecutionPayload
///
/// ## Peer scoring
///
/// See `ExecutionPayloadError` for scoring information
ExecutionPayloadError(ExecutionPayloadError),
}
/// Returned when block validation failed due to some issue verifying
/// the execution payload.
#[derive(Debug)]
pub enum ExecutionPayloadError {
/// There's no eth1 connection (mandatory after merge)
///
/// ## Peer scoring
///
/// As this is our fault, do not penalize the peer
NoEth1Connection,
/// Error occurred during engine_executePayload
///
/// ## Peer scoring
///
/// Some issue with our configuration, do not penalize peer
Eth1VerificationError(eth1_chain::Error),
/// The execution engine returned INVALID for the payload
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
RejectedByExecutionEngine,
/// The execution payload is empty when is shouldn't be
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
PayloadEmpty,
/// The execution payload timestamp does not match the slot
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
InvalidPayloadTimestamp,
/// The gas used in the block exceeds the gas limit
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
GasUsedExceedsLimit,
/// The payload block hash equals the parent hash
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
BlockHashEqualsParentHash,
/// The execution payload transaction list data exceeds size limits
///
/// ## Peer scoring
///
/// The block is invalid and the peer is faulty
TransactionDataExceedsSizeLimit,
}
impl<T: EthSpec> std::fmt::Display for BlockError<T> {
@@ -668,6 +729,18 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
});
}
// TODO: avoid this by adding field to fork-choice to determine if merge-block has been imported
let (parent, block) = if let Some(snapshot) = parent {
(Some(snapshot), block)
} else {
let (snapshot, block) = load_parent(block, chain)?;
(Some(snapshot), block)
};
let state = &parent.as_ref().unwrap().pre_state;
// validate the block's execution_payload
validate_execution_payload(block.message(), state)?;
Ok(Self {
block,
block_root,
@@ -989,6 +1062,34 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
}
}
// This is the soonest we can run these checks as they must be called AFTER per_slot_processing
if is_execution_enabled(&state, block.message().body()) {
let eth1_chain = chain
.eth1_chain
.as_ref()
.ok_or(BlockError::ExecutionPayloadError(
ExecutionPayloadError::NoEth1Connection,
))?;
if !eth1_chain
.on_payload(block.message().body().execution_payload().ok_or(
BlockError::InconsistentFork(InconsistentFork {
fork_at_slot: eth2::types::ForkName::Merge,
object_fork: block.message().body().fork_name(),
}),
)?)
.map_err(|e| {
BlockError::ExecutionPayloadError(ExecutionPayloadError::Eth1VerificationError(
e,
))
})?
{
return Err(BlockError::ExecutionPayloadError(
ExecutionPayloadError::RejectedByExecutionEngine,
));
}
}
// If the block is sufficiently recent, notify the validator monitor.
if let Some(slot) = chain.slot_clock.now() {
let epoch = slot.epoch(T::EthSpec::slots_per_epoch());
@@ -1097,6 +1198,38 @@ impl<'a, T: BeaconChainTypes> FullyVerifiedBlock<'a, T> {
}
}
/// Validate the gossip block's execution_payload according to the checks described here:
/// https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/p2p-interface.md#beacon_block
fn validate_execution_payload<E: EthSpec>(
block: BeaconBlockRef<'_, E>,
state: &BeaconState<E>,
) -> Result<(), BlockError<E>> {
if !is_execution_enabled(state, block.body()) {
return Ok(());
}
let execution_payload = block
.body()
.execution_payload()
// TODO: this really should never error so maybe
// we should make this simpler..
.ok_or(BlockError::InconsistentFork(InconsistentFork {
fork_at_slot: eth2::types::ForkName::Merge,
object_fork: block.body().fork_name(),
}))?;
if is_merge_complete(state) {
if *execution_payload == <ExecutionPayload<E>>::default() {
return Err(BlockError::ExecutionPayloadError(
ExecutionPayloadError::PayloadEmpty,
));
}
}
// TODO: finish these
Ok(())
}
/// Check that the count of skip slots between the block and its parent does not exceed our maximum
/// value.
///

View File

@@ -15,8 +15,8 @@ use std::time::{SystemTime, UNIX_EPOCH};
use store::{DBColumn, Error as StoreError, StoreItem};
use task_executor::TaskExecutor;
use types::{
BeaconState, BeaconStateError, ChainSpec, Deposit, Eth1Data, EthSpec, Hash256, Slot, Unsigned,
DEPOSIT_TREE_DEPTH,
BeaconState, BeaconStateError, ChainSpec, Deposit, Eth1Data, EthSpec, ExecutionPayload,
Hash256, Slot, Unsigned, DEPOSIT_TREE_DEPTH,
};
type BlockNumber = u64;
@@ -53,6 +53,8 @@ pub enum Error {
UnknownPreviousEth1BlockHash,
/// An arithmetic error occurred.
ArithError(safe_arith::ArithError),
/// Unable to execute payload
UnableToExecutePayload(String),
}
impl From<safe_arith::ArithError> for Error {
@@ -274,6 +276,15 @@ where
)
}
pub fn on_payload(&self, execution_payload: &ExecutionPayload<E>) -> Result<bool, Error> {
if self.use_dummy_backend {
let dummy_backend: DummyEth1ChainBackend<E> = DummyEth1ChainBackend::default();
dummy_backend.on_payload(execution_payload)
} else {
self.backend.on_payload(execution_payload)
}
}
/// Instantiate `Eth1Chain` from a persisted `SszEth1`.
///
/// The `Eth1Chain` will have the same caches as the persisted `SszEth1`.
@@ -334,6 +345,9 @@ pub trait Eth1ChainBackend<T: EthSpec>: Sized + Send + Sync {
/// an idea of how up-to-date the remote eth1 node is.
fn head_block(&self) -> Option<Eth1Block>;
/// Verifies the execution payload
fn on_payload(&self, execution_payload: &ExecutionPayload<T>) -> Result<bool, Error>;
/// Encode the `Eth1ChainBackend` instance to bytes.
fn as_bytes(&self) -> Vec<u8>;
@@ -388,6 +402,10 @@ impl<T: EthSpec> Eth1ChainBackend<T> for DummyEth1ChainBackend<T> {
None
}
fn on_payload(&self, _execution_payload: &ExecutionPayload<T>) -> Result<bool, Error> {
Ok(true)
}
/// Return empty Vec<u8> for dummy backend.
fn as_bytes(&self) -> Vec<u8> {
Vec::new()
@@ -556,6 +574,15 @@ impl<T: EthSpec> Eth1ChainBackend<T> for CachingEth1Backend<T> {
self.core.head_block()
}
fn on_payload(&self, execution_payload: &ExecutionPayload<T>) -> Result<bool, Error> {
futures::executor::block_on(async move {
self.core
.on_payload(execution_payload.clone())
.await
.map_err(|e| Error::UnableToExecutePayload(format!("{:?}", e)))
})
}
/// Return encoded byte representation of the block and deposit caches.
fn as_bytes(&self) -> Vec<u8> {
self.core.as_bytes()

View File

@@ -166,7 +166,7 @@ pub fn reset_fork_choice_to_finalization<E: EthSpec, Hot: ItemStore<E>, Cold: It
let (block, _) = block.deconstruct();
fork_choice
.on_block(block.slot(), &block, block.canonical_root(), &state)
.on_block(block.slot(), &block, block.canonical_root(), &state, spec)
.map_err(|e| format!("Error applying replayed block to fork choice: {:?}", e))?;
}

View File

@@ -43,7 +43,7 @@ pub use self::errors::{BeaconChainError, BlockProductionError};
pub use self::historical_blocks::HistoricalBlockError;
pub use attestation_verification::Error as AttestationError;
pub use beacon_fork_choice_store::{BeaconForkChoiceStore, Error as ForkChoiceStoreError};
pub use block_verification::{BlockError, GossipVerifiedBlock};
pub use block_verification::{BlockError, ExecutionPayloadError, GossipVerifiedBlock};
pub use eth1_chain::{Eth1Chain, Eth1ChainBackend};
pub use events::ServerSentEventHandler;
pub use metrics::scrape_for_metrics;