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-12-02 14:26:50 +11:00
committed by Paul Hauner
parent fe75a0a9a1
commit 5687c56d51
50 changed files with 1241 additions and 133 deletions
@@ -2838,6 +2838,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 {
@@ -2876,6 +2881,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: voluntary_exits.into(),
sync_aggregate,
execution_payload,
},
})
}
};
let block = SignedBeaconBlock::from_block(
@@ -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.
///
+29 -2
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()
+1 -1
View File
@@ -44,7 +44,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;
+56 -22
View File
@@ -19,7 +19,7 @@ use std::fmt;
use std::ops::Range;
use std::str::FromStr;
use std::time::Duration;
use types::Hash256;
use types::{Hash256, PowBlock, Uint256};
/// `keccak("DepositEvent(bytes,bytes,bytes,bytes,bytes)")`
pub const DEPOSIT_EVENT_TOPIC: &str =
@@ -49,6 +49,7 @@ pub enum Eth1Id {
#[derive(Clone, Copy)]
pub enum BlockQuery {
Number(u64),
Hash(Hash256),
Latest,
}
@@ -135,13 +136,6 @@ pub async fn get_chain_id(endpoint: &SensitiveUrl, timeout: Duration) -> Result<
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct Block {
pub hash: Hash256,
pub timestamp: u64,
pub number: u64,
}
/// Returns the current block number.
///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
@@ -156,40 +150,74 @@ pub async fn get_block_number(endpoint: &SensitiveUrl, timeout: Duration) -> Res
.map_err(|e| format!("Failed to get block number: {}", e))
}
/// Gets a block hash by block number.
/// Gets a block by hash or block number.
///
/// Uses HTTP JSON RPC at `endpoint`. E.g., `http://localhost:8545`.
pub async fn get_block(
endpoint: &SensitiveUrl,
query: BlockQuery,
timeout: Duration,
) -> Result<Block, String> {
) -> Result<PowBlock, String> {
let query_param = match query {
BlockQuery::Number(block_number) => format!("0x{:x}", block_number),
BlockQuery::Hash(hash) => format!("{:?}", hash), // debug formatting ensures output not truncated
BlockQuery::Latest => "latest".to_string(),
};
let rpc_method = match query {
BlockQuery::Number(_) | BlockQuery::Latest => "eth_getBlockByNumber",
BlockQuery::Hash(_) => "eth_getBlockByHash",
};
let params = json!([
query_param,
false // do not return full tx objects.
]);
let response_body = send_rpc_request(endpoint, "eth_getBlockByNumber", params, timeout).await?;
let response_body = send_rpc_request(endpoint, rpc_method, params, timeout).await?;
let response = response_result_or_error(&response_body)
.map_err(|e| format!("eth_getBlockByNumber failed: {}", e))?;
.map_err(|e| format!("{} failed: {}", rpc_method, e))?;
let hash: Vec<u8> = hex_to_bytes(
let block_hash: Vec<u8> = hex_to_bytes(
response
.get("hash")
.ok_or("No hash for block")?
.as_str()
.ok_or("Block hash was not string")?,
)?;
let hash: Hash256 = if hash.len() == 32 {
Hash256::from_slice(&hash)
let block_hash: Hash256 = if block_hash.len() == 32 {
Hash256::from_slice(&block_hash)
} else {
return Err(format!("Block has was not 32 bytes: {:?}", hash));
return Err(format!("Block hash was not 32 bytes: {:?}", block_hash));
};
let parent_hash: Vec<u8> = hex_to_bytes(
response
.get("parentHash")
.ok_or("No parent hash for block")?
.as_str()
.ok_or("Parent hash was not string")?,
)?;
let parent_hash: Hash256 = if parent_hash.len() == 32 {
Hash256::from_slice(&parent_hash)
} else {
return Err(format!("parent hash was not 32 bytes: {:?}", parent_hash));
};
let total_difficulty_str = response
.get("totalDifficulty")
.ok_or("No total difficulty for block")?
.as_str()
.ok_or("Total difficulty was not a string")?;
let total_difficulty = Uint256::from_str(total_difficulty_str)
.map_err(|e| format!("total_difficulty from_str {:?}", e))?;
let difficulty_str = response
.get("difficulty")
.ok_or("No difficulty for block")?
.as_str()
.ok_or("Difficulty was not a string")?;
let difficulty =
Uint256::from_str(difficulty_str).map_err(|e| format!("difficulty from_str {:?}", e))?;
let timestamp = hex_to_u64_be(
response
.get("timestamp")
@@ -198,7 +226,7 @@ pub async fn get_block(
.ok_or("Block timestamp was not string")?,
)?;
let number = hex_to_u64_be(
let block_number = hex_to_u64_be(
response
.get("number")
.ok_or("No number for block")?
@@ -206,14 +234,20 @@ pub async fn get_block(
.ok_or("Block number was not string")?,
)?;
if number <= usize::max_value() as u64 {
Ok(Block {
hash,
if block_number <= usize::max_value() as u64 {
Ok(PowBlock {
block_hash,
parent_hash,
total_difficulty,
difficulty,
timestamp,
number,
block_number,
})
} else {
Err(format!("Block number {} is larger than a usize", number))
Err(format!(
"Block number {} is larger than a usize",
block_number
))
}
.map_err(|e| format!("Failed to get block number: {}", e))
}
+22 -5
View File
@@ -21,7 +21,7 @@ use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use tokio::sync::RwLock as TRwLock;
use tokio::time::{interval_at, Duration, Instant};
use types::{ChainSpec, EthSpec, Unsigned};
use types::{ChainSpec, EthSpec, ExecutionPayload, Unsigned};
/// Indicates the default eth1 network id we use for the deposit contract.
pub const DEFAULT_NETWORK_ID: Eth1Id = Eth1Id::Goerli;
@@ -331,6 +331,8 @@ pub enum SingleEndpointError {
GetDepositCountFailed(String),
/// Failed to read the deposit contract root from the eth1 node.
GetDepositLogsFailed(String),
/// Failed to run engine_ExecutePayload
EngineExecutePayloadFailed,
}
#[derive(Debug, PartialEq)]
@@ -669,6 +671,21 @@ impl Service {
}
}
/// This is were we call out to engine_executePayload to determine if payload is valid
pub async fn on_payload<T: EthSpec>(
&self,
_execution_payload: ExecutionPayload<T>,
) -> Result<bool, Error> {
let endpoints = self.init_endpoints();
// TODO: call engine_executePayload and figure out how backup endpoint works..
endpoints
.first_success(|_e| async move { Ok(true) })
.await
.map(|(res, _)| res)
.map_err(Error::FallbackError)
}
/// Update the deposit and block cache, returning an error if either fail.
///
/// ## Returns
@@ -1242,7 +1259,7 @@ async fn download_eth1_block(
});
// Performs a `get_blockByNumber` call to an eth1 node.
let http_block = get_block(
let pow_block = get_block(
endpoint,
block_number_opt
.map(BlockQuery::Number)
@@ -1253,9 +1270,9 @@ async fn download_eth1_block(
.await?;
Ok(Eth1Block {
hash: http_block.hash,
number: http_block.number,
timestamp: http_block.timestamp,
hash: pow_block.block_hash,
number: pow_block.block_number,
timestamp: pow_block.timestamp,
deposit_root,
deposit_count,
})
+5 -4
View File
@@ -1,6 +1,6 @@
#![cfg(test)]
use environment::{Environment, EnvironmentBuilder};
use eth1::http::{get_deposit_count, get_deposit_logs_in_range, get_deposit_root, Block, Log};
use eth1::http::{get_deposit_count, get_deposit_logs_in_range, get_deposit_root, Log};
use eth1::{Config, Service};
use eth1::{DepositCache, DEFAULT_CHAIN_ID, DEFAULT_NETWORK_ID};
use eth1_test_rig::GanacheEth1Instance;
@@ -571,8 +571,9 @@ mod deposit_tree {
mod http {
use super::*;
use eth1::http::BlockQuery;
use types::PowBlock;
async fn get_block(eth1: &GanacheEth1Instance, block_number: u64) -> Block {
async fn get_block(eth1: &GanacheEth1Instance, block_number: u64) -> PowBlock {
eth1::http::get_block(
&SensitiveUrl::parse(eth1.endpoint().as_str()).unwrap(),
BlockQuery::Number(block_number),
@@ -639,7 +640,7 @@ mod http {
// Check the block hash.
let new_block = get_block(&eth1, block_number).await;
assert_ne!(
new_block.hash, old_block.hash,
new_block.block_hash, old_block.block_hash,
"block hash should change with each deposit"
);
@@ -661,7 +662,7 @@ mod http {
// Check to ensure the block root is changing
assert_ne!(
new_root,
Some(new_block.hash),
Some(new_block.block_hash),
"the deposit root should be different to the block hash"
);
}
+3 -1
View File
@@ -209,7 +209,9 @@ pub fn gossipsub_config(fork_context: Arc<ForkContext>) -> GossipsubConfig {
) -> Vec<u8> {
let topic_bytes = message.topic.as_str().as_bytes();
match fork_context.current_fork() {
ForkName::Altair => {
// according to: https://github.com/ethereum/consensus-specs/blob/dev/specs/merge/p2p-interface.md#the-gossip-domain-gossipsub
// the derivation of the message-id remains the same in the merge
ForkName::Altair | ForkName::Merge => {
let topic_len_bytes = topic_bytes.len().to_le_bytes();
let mut vec = Vec::with_capacity(
prefix.len() + topic_len_bytes.len() + topic_bytes.len() + message.data.len(),
@@ -17,7 +17,7 @@ use std::sync::Arc;
use tokio_util::codec::{Decoder, Encoder};
use types::{
EthSpec, ForkContext, ForkName, SignedBeaconBlock, SignedBeaconBlockAltair,
SignedBeaconBlockBase,
SignedBeaconBlockBase, SignedBeaconBlockMerge,
};
use unsigned_varint::codec::Uvi;
@@ -375,7 +375,7 @@ fn handle_error<T>(
}
/// Returns `Some(context_bytes)` for encoding RPC responses that require context bytes.
/// Returns `None` when context bytes are not required.
/// Returns `None` when context bytes are not required.
fn context_bytes<T: EthSpec>(
protocol: &ProtocolId,
fork_context: &ForkContext,
@@ -383,23 +383,25 @@ fn context_bytes<T: EthSpec>(
) -> Option<[u8; CONTEXT_BYTES_LEN]> {
// Add the context bytes if required
if protocol.has_context_bytes() {
if let RPCCodedResponse::Success(RPCResponse::BlocksByRange(res)) = resp {
if let SignedBeaconBlock::Altair { .. } = **res {
// Altair context being `None` implies that "altair never happened".
// This code should be unreachable if altair is disabled since only Version::V1 would be valid in that case.
return fork_context.to_context_bytes(ForkName::Altair);
} else if let SignedBeaconBlock::Base { .. } = **res {
return Some(fork_context.genesis_context_bytes());
}
}
if let RPCCodedResponse::Success(RPCResponse::BlocksByRoot(res)) = resp {
if let SignedBeaconBlock::Altair { .. } = **res {
// Altair context being `None` implies that "altair never happened".
// This code should be unreachable if altair is disabled since only Version::V1 would be valid in that case.
return fork_context.to_context_bytes(ForkName::Altair);
} else if let SignedBeaconBlock::Base { .. } = **res {
return Some(fork_context.genesis_context_bytes());
if let RPCCodedResponse::Success(rpc_variant) = resp {
if let RPCResponse::BlocksByRange(ref_box_block)
| RPCResponse::BlocksByRoot(ref_box_block) = rpc_variant
{
return match **ref_box_block {
// NOTE: If you are adding another fork type here, be sure to modify the
// `fork_context.to_context_bytes()` function to support it as well!
SignedBeaconBlock::Merge { .. } => {
// TODO: check this
// Merge context being `None` implies that "merge never happened".
fork_context.to_context_bytes(ForkName::Merge)
}
SignedBeaconBlock::Altair { .. } => {
// Altair context being `None` implies that "altair never happened".
// This code should be unreachable if altair is disabled since only Version::V1 would be valid in that case.
fork_context.to_context_bytes(ForkName::Altair)
}
SignedBeaconBlock::Base { .. } => Some(fork_context.genesis_context_bytes()),
};
}
}
}
@@ -559,6 +561,12 @@ fn handle_v2_response<T: EthSpec>(
ForkName::Base => Ok(Some(RPCResponse::BlocksByRange(Box::new(
SignedBeaconBlock::Base(SignedBeaconBlockBase::from_ssz_bytes(decoded_buffer)?),
)))),
// TODO: check this (though it seems okay)
ForkName::Merge => Ok(Some(RPCResponse::BlocksByRange(Box::new(
SignedBeaconBlock::Merge(SignedBeaconBlockMerge::from_ssz_bytes(
decoded_buffer,
)?),
)))),
},
Protocol::BlocksByRoot => match fork_name {
ForkName::Altair => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
@@ -569,6 +577,12 @@ fn handle_v2_response<T: EthSpec>(
ForkName::Base => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
SignedBeaconBlock::Base(SignedBeaconBlockBase::from_ssz_bytes(decoded_buffer)?),
)))),
// TODO: check this (though it seems right)
ForkName::Merge => Ok(Some(RPCResponse::BlocksByRoot(Box::new(
SignedBeaconBlock::Merge(SignedBeaconBlockMerge::from_ssz_bytes(
decoded_buffer,
)?),
)))),
},
_ => Err(RPCError::ErrorResponse(
RPCResponseErrorCode::InvalidRequest,
@@ -21,8 +21,8 @@ use tokio_util::{
compat::{Compat, FuturesAsyncReadCompatExt},
};
use types::{
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, EthSpec, ForkContext, Hash256, MainnetEthSpec,
Signature, SignedBeaconBlock,
BeaconBlock, BeaconBlockAltair, BeaconBlockBase, BeaconBlockMerge, EthSpec, ForkContext,
Hash256, MainnetEthSpec, Signature, SignedBeaconBlock,
};
lazy_static! {
@@ -53,6 +53,20 @@ lazy_static! {
)
.as_ssz_bytes()
.len();
pub static ref SIGNED_BEACON_BLOCK_MERGE_MIN: usize = SignedBeaconBlock::<MainnetEthSpec>::from_block(
BeaconBlock::Merge(BeaconBlockMerge::<MainnetEthSpec>::empty(&MainnetEthSpec::default_spec())),
Signature::empty(),
)
.as_ssz_bytes()
.len();
pub static ref SIGNED_BEACON_BLOCK_MERGE_MAX: usize = SignedBeaconBlock::<MainnetEthSpec>::from_block(
BeaconBlock::Merge(BeaconBlockMerge::full(&MainnetEthSpec::default_spec())),
Signature::empty(),
)
.as_ssz_bytes()
.len();
pub static ref BLOCKS_BY_ROOT_REQUEST_MIN: usize =
VariableList::<Hash256, MaxRequestBlocks>::from(Vec::<Hash256>::new())
.as_ssz_bytes()
@@ -253,12 +267,18 @@ impl ProtocolId {
Protocol::Goodbye => RpcLimits::new(0, 0), // Goodbye request has no response
Protocol::BlocksByRange => RpcLimits::new(
std::cmp::min(
*SIGNED_BEACON_BLOCK_ALTAIR_MIN,
*SIGNED_BEACON_BLOCK_BASE_MIN,
std::cmp::min(
*SIGNED_BEACON_BLOCK_ALTAIR_MIN,
*SIGNED_BEACON_BLOCK_BASE_MIN,
),
*SIGNED_BEACON_BLOCK_MERGE_MIN,
),
std::cmp::max(
*SIGNED_BEACON_BLOCK_ALTAIR_MAX,
*SIGNED_BEACON_BLOCK_BASE_MAX,
std::cmp::max(
*SIGNED_BEACON_BLOCK_ALTAIR_MAX,
*SIGNED_BEACON_BLOCK_BASE_MAX,
),
*SIGNED_BEACON_BLOCK_MERGE_MAX,
),
),
Protocol::BlocksByRoot => RpcLimits::new(
@@ -10,7 +10,8 @@ use std::io::{Error, ErrorKind};
use types::{
Attestation, AttesterSlashing, EthSpec, ForkContext, ForkName, ProposerSlashing,
SignedAggregateAndProof, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase,
SignedContributionAndProof, SignedVoluntaryExit, SubnetId, SyncCommitteeMessage, SyncSubnetId,
SignedBeaconBlockMerge, SignedContributionAndProof, SignedVoluntaryExit, SubnetId,
SyncCommitteeMessage, SyncSubnetId,
};
#[derive(Debug, Clone, PartialEq)]
@@ -161,6 +162,10 @@ impl<T: EthSpec> PubsubMessage<T> {
SignedBeaconBlockAltair::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?,
),
Some(ForkName::Merge) => SignedBeaconBlock::<T>::Merge(
SignedBeaconBlockMerge::from_ssz_bytes(data)
.map_err(|e| format!("{:?}", e))?,
),
None => {
return Err(format!(
"Unknown gossipsub fork digest: {:?}",
@@ -5,7 +5,8 @@ use beacon_chain::{
observed_operations::ObservationOutcome,
sync_committee_verification::Error as SyncCommitteeError,
validator_monitor::get_block_delay_ms,
BeaconChainError, BeaconChainTypes, BlockError, ForkChoiceError, GossipVerifiedBlock,
BeaconChainError, BeaconChainTypes, BlockError, ExecutionPayloadError, ForkChoiceError,
GossipVerifiedBlock,
};
use lighthouse_network::{Client, MessageAcceptance, MessageId, PeerAction, PeerId, ReportSource};
use slog::{crit, debug, error, info, trace, warn};
@@ -746,6 +747,16 @@ impl<T: BeaconChainTypes> Worker<T> {
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
return None;
}
// TODO: check that this is what we're supposed to do when we don't want to
// penalize a peer for our configuration issue
// in the verification process BUT is this the proper way to handle it?
Err(e @BlockError::ExecutionPayloadError(ExecutionPayloadError::Eth1VerificationError(_)))
| Err(e @BlockError::ExecutionPayloadError(ExecutionPayloadError::NoEth1Connection)) => {
debug!(self.log, "Could not verify block for gossip, ignoring the block";
"error" => %e);
self.propagate_validation_result(message_id, peer_id, MessageAcceptance::Ignore);
return;
}
Err(e @ BlockError::StateRootMismatch { .. })
| Err(e @ BlockError::IncorrectBlockProposer { .. })
| Err(e @ BlockError::BlockSlotLimitReached)
@@ -759,6 +770,8 @@ impl<T: BeaconChainTypes> Worker<T> {
| Err(e @ BlockError::TooManySkippedSlots { .. })
| Err(e @ BlockError::WeakSubjectivityConflict)
| Err(e @ BlockError::InconsistentFork(_))
// TODO: is this what we should be doing when block verification fails?
| Err(e @BlockError::ExecutionPayloadError(_))
| Err(e @ BlockError::GenesisBlock) => {
warn!(self.log, "Could not verify block for gossip, rejecting the block";
"error" => %e);
+4
View File
@@ -883,6 +883,10 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
&mut block.message.state_root,
&mut block.message.parent_root,
),
SignedBeaconBlock::Merge(block) => (
&mut block.message.state_root,
&mut block.message.parent_root,
),
};
*state_root = Hash256::zero();
+38 -7
View File
@@ -14,8 +14,8 @@ use types::*;
///
/// Utilises lazy-loading from separate storage for its vector fields.
#[superstruct(
variants(Base, Altair),
variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode),)
variants(Base, Altair, Merge),
variant_attributes(derive(Debug, PartialEq, Clone, Encode, Decode))
)]
#[derive(Debug, PartialEq, Clone, Encode)]
#[ssz(enum_behaviour = "transparent")]
@@ -66,9 +66,9 @@ where
pub current_epoch_attestations: VariableList<PendingAttestation<T>, T::MaxPendingAttestations>,
// Participation (Altair and later)
#[superstruct(only(Altair))]
#[superstruct(only(Altair, Merge))]
pub previous_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
#[superstruct(only(Altair))]
#[superstruct(only(Altair, Merge))]
pub current_epoch_participation: VariableList<ParticipationFlags, T::ValidatorRegistryLimit>,
// Finality
@@ -78,14 +78,18 @@ where
pub finalized_checkpoint: Checkpoint,
// Inactivity
#[superstruct(only(Altair))]
#[superstruct(only(Altair, Merge))]
pub inactivity_scores: VariableList<u64, T::ValidatorRegistryLimit>,
// Light-client sync committees
#[superstruct(only(Altair))]
#[superstruct(only(Altair, Merge))]
pub current_sync_committee: Arc<SyncCommittee<T>>,
#[superstruct(only(Altair))]
#[superstruct(only(Altair, Merge))]
pub next_sync_committee: Arc<SyncCommittee<T>>,
// Execution
#[superstruct(only(Merge))]
pub latest_execution_payload_header: ExecutionPayloadHeader<T>,
}
/// Implement the conversion function from BeaconState -> PartialBeaconState.
@@ -160,6 +164,20 @@ impl<T: EthSpec> PartialBeaconState<T> {
inactivity_scores
]
),
BeaconState::Merge(s) => impl_from_state_forgetful!(
s,
outer,
Merge,
PartialBeaconStateMerge,
[
previous_epoch_participation,
current_epoch_participation,
current_sync_committee,
next_sync_committee,
inactivity_scores,
latest_execution_payload_header
]
),
}
}
@@ -334,6 +352,19 @@ impl<E: EthSpec> TryInto<BeaconState<E>> for PartialBeaconState<E> {
inactivity_scores
]
),
PartialBeaconState::Merge(inner) => impl_try_into_beacon_state!(
inner,
Merge,
BeaconStateMerge,
[
previous_epoch_participation,
current_epoch_participation,
current_sync_committee,
next_sync_committee,
inactivity_scores,
latest_execution_payload_header
]
),
};
Ok(state)
}