Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: EIP-7742 #12957

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion crates/chainspec/src/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use alloc::{boxed::Box, sync::Arc, vec::Vec};
use alloy_chains::{Chain, NamedChain};
use alloy_consensus::constants::EMPTY_WITHDRAWALS;
use alloy_eips::{
eip1559::INITIAL_BASE_FEE, eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
eip1559::INITIAL_BASE_FEE, eip4844, eip6110::MAINNET_DEPOSIT_CONTRACT_ADDRESS,
eip7685::EMPTY_REQUESTS_HASH,
};
use alloy_genesis::Genesis;
Expand Down Expand Up @@ -290,6 +290,9 @@ impl ChainSpec {
let requests_hash = self
.is_prague_active_at_timestamp(self.genesis.timestamp)
.then_some(EMPTY_REQUESTS_HASH);
let target_blobs_per_block = self
.is_prague_active_at_timestamp(self.genesis.timestamp)
.then_some(eip4844::TARGET_BLOBS_PER_BLOCK);

Header {
gas_limit: self.genesis.gas_limit,
Expand All @@ -306,6 +309,7 @@ impl ChainSpec {
blob_gas_used: blob_gas_used.map(Into::into),
excess_blob_gas: excess_blob_gas.map(Into::into),
requests_hash,
target_blobs_per_block,
..Default::default()
}
}
Expand Down
5 changes: 2 additions & 3 deletions crates/consensus/common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,12 @@ workspace = true
[dependencies]
# reth
reth-chainspec.workspace = true
reth-consensus.workspace = true
reth-primitives.workspace = true
reth-primitives-traits.workspace = true
reth-consensus.workspace = true

# ethereum
alloy-primitives.workspace = true
revm-primitives.workspace = true
reth-primitives-traits.workspace = true
alloy-consensus.workspace = true
alloy-eips.workspace = true

Expand Down
25 changes: 20 additions & 5 deletions crates/consensus/common/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

use alloy_consensus::{constants::MAXIMUM_EXTRA_DATA_SIZE, BlockHeader, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{
calc_next_block_base_fee,
calc_next_block_base_fee, eip4844,
eip4844::{DATA_GAS_PER_BLOB, MAX_DATA_GAS_PER_BLOCK},
eip7742,
};
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
use reth_consensus::ConsensusError;
use reth_primitives::SealedBlock;
use reth_primitives_traits::{BlockBody, GotExpected, SealedHeader};
use revm_primitives::calc_excess_blob_gas;

/// Gas used needs to be less than gas limit. Gas used is going to be checked after execution.
#[inline]
Expand Down Expand Up @@ -310,10 +310,15 @@ pub fn validate_against_parent_timestamp<H: BlockHeader>(
/// ensures that the `blob_gas_used` and `excess_blob_gas` fields exist in the child header, and
/// that the `excess_blob_gas` field matches the expected `excess_blob_gas` calculated from the
/// parent header fields.
pub fn validate_against_parent_4844<H: BlockHeader>(
pub fn validate_against_parent_blob_fields<H: BlockHeader>(
header: &H,
parent: &H,
chain_spec: impl EthereumHardforks,
) -> Result<(), ConsensusError> {
if !chain_spec.is_cancun_active_at_timestamp(header.timestamp()) {
return Ok(())
}

// From [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844#header-extension):
//
// > For the first post-fork block, both parent.blob_gas_used and parent.excess_blob_gas
Expand All @@ -328,8 +333,18 @@ pub fn validate_against_parent_4844<H: BlockHeader>(
}
let excess_blob_gas = header.excess_blob_gas().ok_or(ConsensusError::ExcessBlobGasMissing)?;

let expected_excess_blob_gas =
calc_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used);
let expected_excess_blob_gas = if chain_spec.is_prague_active_at_timestamp(parent.timestamp()) {
let parent_target_blobs_per_block =
parent.target_blobs_per_block().ok_or(ConsensusError::TargetBlobsPerBlockMissing)?;
eip7742::calc_excess_blob_gas(
parent_excess_blob_gas,
parent_blob_gas_used,
parent_target_blobs_per_block,
)
} else {
eip4844::calc_excess_blob_gas(parent_excess_blob_gas, parent_blob_gas_used)
};
Comment on lines +336 to +346
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks correct


if expected_excess_blob_gas != excess_blob_gas {
return Err(ConsensusError::ExcessBlobGasDiff {
diff: GotExpected { got: excess_blob_gas, expected: expected_excess_blob_gas },
Expand Down
8 changes: 8 additions & 0 deletions crates/consensus/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,10 @@ pub enum ConsensusError {
#[display("missing requests hash")]
RequestsHashMissing,

/// Error when target blobs per block is missing.
#[display("missing target blobs per block")]
TargetBlobsPerBlockMissing,

/// Error when an unexpected withdrawals root is encountered.
#[display("unexpected withdrawals root")]
WithdrawalsRootUnexpected,
Expand All @@ -323,6 +327,10 @@ pub enum ConsensusError {
#[display("unexpected requests hash")]
RequestsHashUnexpected,

/// Error when an unexpected target blobs per block is encountered.
#[display("unexpected target blobs per block")]
TargetBlobsPerBlockUnexpected,

/// Error when withdrawals are missing.
#[display("missing withdrawals")]
BodyWithdrawalsMissing,
Expand Down
11 changes: 10 additions & 1 deletion crates/e2e-test-utils/src/engine_api.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::traits::PayloadEnvelopeExt;
use alloy_primitives::B256;
use alloy_primitives::{B256, U64};
use alloy_rpc_types_engine::{ForkchoiceState, PayloadStatusEnum};
use jsonrpsee::{
core::client::ClientT,
Expand Down Expand Up @@ -41,6 +41,14 @@ impl<E: EngineTypes, ChainSpec: EthereumHardforks> EngineApiTestContext<E, Chain
Ok(self.engine_api_client.request("engine_getPayloadV3", (payload_id,)).await?)
}

/// Retrieves a v3 payload from the engine api as serde value
pub async fn get_payload_v4_value(
&self,
payload_id: PayloadId,
) -> eyre::Result<serde_json::Value> {
Ok(self.engine_api_client.request("engine_getPayloadV4", (payload_id,)).await?)
}

/// Submits a payload to the engine api
pub async fn submit_payload(
&self,
Expand All @@ -67,6 +75,7 @@ impl<E: EngineTypes, ChainSpec: EthereumHardforks> EngineApiTestContext<E, Chain
versioned_hashes,
payload_builder_attributes.parent_beacon_block_root().unwrap(),
requests,
U64::from(payload_builder_attributes.target_blobs_per_block().unwrap()),
)
.await?
} else {
Expand Down
4 changes: 2 additions & 2 deletions crates/e2e-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ pub async fn setup<N>(
num_nodes: usize,
chain_spec: Arc<N::ChainSpec>,
is_dev: bool,
attributes_generator: impl Fn(u64) -> <<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static,
attributes_generator: impl Fn(Arc<N::ChainSpec>, u64) -> <<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static,
) -> eyre::Result<(Vec<NodeHelperType<N>>, TaskManager, Wallet)>
where
N: Default + Node<TmpNodeAdapter<N>> + NodeTypesForTree + NodeTypesWithEngine,
Expand Down Expand Up @@ -113,7 +113,7 @@ pub async fn setup_engine<N>(
num_nodes: usize,
chain_spec: Arc<N::ChainSpec>,
is_dev: bool,
attributes_generator: impl Fn(u64) -> <<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static,
attributes_generator: impl Fn(Arc<N::ChainSpec>, u64) -> <<N as NodeTypesWithEngine>::Engine as PayloadTypes>::PayloadBuilderAttributes + Copy + 'static,
) -> eyre::Result<(
Vec<NodeHelperType<N, BlockchainProvider2<NodeTypesWithDBAdapter<N, TmpDB>>>>,
TaskManager,
Expand Down
14 changes: 11 additions & 3 deletions crates/e2e-test-utils/src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use reth_provider::{
};
use reth_rpc_eth_api::helpers::{EthApiSpec, EthTransactions, TraceExt};
use reth_stages_types::StageId;
use std::{marker::PhantomData, pin::Pin};
use std::{marker::PhantomData, pin::Pin, sync::Arc};
use tokio_stream::StreamExt;
use url::Url;

Expand Down Expand Up @@ -61,10 +61,14 @@ where
/// Creates a new test node
pub async fn new(
node: FullNode<Node, AddOns>,
attributes_generator: impl Fn(u64) -> Engine::PayloadBuilderAttributes + 'static,
attributes_generator: impl Fn(Arc<<Node::Types as NodeTypes>::ChainSpec>, u64) -> Engine::PayloadBuilderAttributes
+ 'static,
) -> eyre::Result<Self> {
let builder = node.payload_builder.clone();
let chain_spec = node.chain_spec();

let attributes_generator =
move |timestamp| attributes_generator(chain_spec.clone(), timestamp);
Ok(Self {
inner: node.clone(),
payload: PayloadTestContext::new(builder, attributes_generator).await?,
Expand Down Expand Up @@ -132,7 +136,11 @@ where
// wait for the payload builder to have finished building
self.payload.wait_for_built_payload(eth_attr.payload_id()).await;
// trigger resolve payload via engine api
self.engine_api.get_payload_v3_value(eth_attr.payload_id()).await?;
if self.inner.chain_spec().is_prague_active_at_timestamp(eth_attr.timestamp()) {
self.engine_api.get_payload_v4_value(eth_attr.payload_id()).await?;
} else {
self.engine_api.get_payload_v3_value(eth_attr.payload_id()).await?;
}
// ensure we're also receiving the built payload as event
Ok((self.payload.expect_built_payload().await?, eth_attr))
}
Expand Down
1 change: 1 addition & 0 deletions crates/engine/local/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ reth-transaction-pool.workspace = true
reth-stages-api.workspace = true

# alloy
alloy-eips.workspace = true
alloy-consensus.workspace = true
alloy-primitives.workspace = true
alloy-rpc-types-engine.workspace = true
Expand Down
11 changes: 9 additions & 2 deletions crates/engine/local/src/payload.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! The implementation of the [`PayloadAttributesBuilder`] for the
//! [`LocalEngineService`](super::service::LocalEngineService).

use alloy_eips::eip4844;
use alloy_primitives::{Address, B256};
use reth_chainspec::EthereumHardforks;
use reth_ethereum_engine_primitives::EthPayloadAttributes;
Expand Down Expand Up @@ -39,8 +40,14 @@ where
.chain_spec
.is_cancun_active_at_timestamp(timestamp)
.then(B256::random),
target_blobs_per_block: None,
max_blobs_per_block: None,
target_blobs_per_block: self
.chain_spec
.is_prague_active_at_timestamp(timestamp)
.then_some(eip4844::TARGET_BLOBS_PER_BLOCK),
max_blobs_per_block: self
.chain_spec
.is_prague_active_at_timestamp(timestamp)
.then_some(eip4844::MAX_BLOBS_PER_BLOCK as u64),
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions crates/engine/util/src/reorg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use reth_revm::{
DatabaseCommit,
};
use reth_rpc_types_compat::engine::payload::block_to_payload;
use revm_primitives::{calc_excess_blob_gas, EVMError, EnvWithHandlerCfg};
use revm_primitives::{EVMError, EnvWithHandlerCfg};
use std::{
collections::VecDeque,
future::Future,
Expand Down Expand Up @@ -387,10 +387,7 @@ where
if chain_spec.is_cancun_active_at_timestamp(reorg_target.timestamp) {
(
Some(sum_blob_gas_used),
Some(calc_excess_blob_gas(
reorg_target_parent.excess_blob_gas.unwrap_or_default(),
reorg_target_parent.blob_gas_used.unwrap_or_default(),
)),
Some(reorg_target_parent.next_block_excess_blob_gas().unwrap_or_default()),
)
} else {
(None, None)
Expand Down Expand Up @@ -421,8 +418,8 @@ where
blob_gas_used: blob_gas_used.map(Into::into),
excess_blob_gas: excess_blob_gas.map(Into::into),
state_root: state_provider.state_root(hashed_state)?,
requests_hash: None, // TODO(prague)
target_blobs_per_block: None, // TODO(prague)
requests_hash: None, // TODO(prague)
target_blobs_per_block: reorg_target.header.target_blobs_per_block,
},
body: BlockBody {
transactions,
Expand Down
18 changes: 12 additions & 6 deletions crates/ethereum/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use reth_consensus::{
Consensus, ConsensusError, FullConsensus, HeaderValidator, PostExecutionInput,
};
use reth_consensus_common::validation::{
validate_4844_header_standalone, validate_against_parent_4844,
validate_4844_header_standalone, validate_against_parent_blob_fields,
validate_against_parent_eip1559_base_fee, validate_against_parent_hash_number,
validate_against_parent_timestamp, validate_block_pre_execution, validate_body_against_header,
validate_header_base_fee, validate_header_extradata, validate_header_gas,
Expand Down Expand Up @@ -167,8 +167,16 @@ where
if header.requests_hash().is_none() {
return Err(ConsensusError::RequestsHashMissing)
}
} else if header.requests_hash().is_some() {
return Err(ConsensusError::RequestsHashUnexpected)
if header.target_blobs_per_block().is_none() {
return Err(ConsensusError::TargetBlobsPerBlockMissing)
}
} else {
if header.requests_hash().is_some() {
return Err(ConsensusError::RequestsHashUnexpected)
}
if header.target_blobs_per_block().is_some() {
return Err(ConsensusError::TargetBlobsPerBlockUnexpected)
}
}

Ok(())
Expand All @@ -194,9 +202,7 @@ where
)?;

// ensure that the blob gas fields for this block
if self.chain_spec.is_cancun_active_at_timestamp(header.timestamp()) {
validate_against_parent_4844(header.header(), parent.header())?;
}
validate_against_parent_blob_fields(header.header(), parent.header(), &self.chain_spec)?;

Ok(())
}
Expand Down
10 changes: 10 additions & 0 deletions crates/ethereum/engine-primitives/src/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,10 @@ pub struct EthPayloadBuilderAttributes {
pub withdrawals: Withdrawals,
/// Root of the parent beacon block
pub parent_beacon_block_root: Option<B256>,
/// Target blobs per block for the generated payload
pub target_blobs_per_block: Option<u64>,
/// Max blobs per block for the generated payload
pub max_blobs_per_block: Option<u64>,
}

// === impl EthPayloadBuilderAttributes ===
Expand All @@ -229,6 +233,8 @@ impl EthPayloadBuilderAttributes {
prev_randao: attributes.prev_randao,
withdrawals: attributes.withdrawals.unwrap_or_default().into(),
parent_beacon_block_root: attributes.parent_beacon_block_root,
target_blobs_per_block: attributes.target_blobs_per_block,
max_blobs_per_block: attributes.max_blobs_per_block,
}
}
}
Expand Down Expand Up @@ -275,6 +281,10 @@ impl PayloadBuilderAttributes for EthPayloadBuilderAttributes {
fn withdrawals(&self) -> &Withdrawals {
&self.withdrawals
}

fn target_blobs_per_block(&self) -> Option<u64> {
self.target_blobs_per_block
}
}

/// Generates the payload id for the configured payload from the [`PayloadAttributes`].
Expand Down
16 changes: 12 additions & 4 deletions crates/ethereum/node/tests/e2e/utils.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use alloy_eips::{BlockId, BlockNumberOrTag};
use alloy_eips::{eip4844, BlockId, BlockNumberOrTag};
use alloy_primitives::{bytes, Address, B256};
use alloy_provider::{
network::{
Expand All @@ -10,6 +10,7 @@ use alloy_rpc_types_engine::PayloadAttributes;
use alloy_rpc_types_eth::TransactionRequest;
use alloy_signer::SignerSync;
use rand::{seq::SliceRandom, Rng};
use reth_chainspec::EthereumHardforks;
use reth_e2e_test_utils::{wallet::Wallet, NodeHelperType, TmpDB};
use reth_node_api::NodeTypesWithDBAdapter;
use reth_node_ethereum::EthereumNode;
Expand All @@ -19,15 +20,22 @@ use reth_provider::FullProvider;
use revm::primitives::{AccessListItem, Authorization};

/// Helper function to create a new eth payload attributes
pub(crate) fn eth_payload_attributes(timestamp: u64) -> EthPayloadBuilderAttributes {
pub(crate) fn eth_payload_attributes(
chain_spec: impl EthereumHardforks,
timestamp: u64,
) -> EthPayloadBuilderAttributes {
let attributes = PayloadAttributes {
timestamp,
prev_randao: B256::ZERO,
suggested_fee_recipient: Address::ZERO,
withdrawals: Some(vec![]),
parent_beacon_block_root: Some(B256::ZERO),
target_blobs_per_block: None,
max_blobs_per_block: None,
target_blobs_per_block: chain_spec
.is_prague_active_at_timestamp(timestamp)
.then_some(eip4844::TARGET_BLOBS_PER_BLOCK),
max_blobs_per_block: chain_spec
.is_prague_active_at_timestamp(timestamp)
.then_some(eip4844::MAX_BLOBS_PER_BLOCK as u64),
};
EthPayloadBuilderAttributes::new(B256::ZERO, attributes)
}
Expand Down
Loading
Loading