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:
@@ -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.
|
||||
///
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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(ð1, 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"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user