mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
support EIP-4844 transaction when building pending block (#4688)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
This commit is contained in:
@ -1,7 +1,7 @@
|
|||||||
use reth_consensus_common::calc;
|
use reth_consensus_common::calc;
|
||||||
use reth_interfaces::executor::{BlockExecutionError, BlockValidationError};
|
use reth_interfaces::executor::{BlockExecutionError, BlockValidationError};
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
constants::SYSTEM_ADDRESS, Address, ChainSpec, Hardfork, Header, Withdrawal, H256, U256,
|
constants::SYSTEM_ADDRESS, Address, ChainSpec, Header, Withdrawal, H256, U256,
|
||||||
};
|
};
|
||||||
use reth_revm_primitives::{env::fill_tx_env_with_beacon_root_contract_call, Database};
|
use reth_revm_primitives::{env::fill_tx_env_with_beacon_root_contract_call, Database};
|
||||||
use revm::{primitives::ResultAndState, DatabaseCommit, EVM};
|
use revm::{primitives::ResultAndState, DatabaseCommit, EVM};
|
||||||
@ -109,8 +109,8 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a map of addresses to their balance increments if shanghai is active at the given
|
/// Returns a map of addresses to their balance increments if the Shanghai hardfork is active at the
|
||||||
/// timestamp.
|
/// given timestamp.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn post_block_withdrawals_balance_increments(
|
pub fn post_block_withdrawals_balance_increments(
|
||||||
chain_spec: &ChainSpec,
|
chain_spec: &ChainSpec,
|
||||||
@ -137,7 +137,7 @@ pub fn insert_post_block_withdrawals_balance_increments(
|
|||||||
balance_increments: &mut HashMap<Address, u128>,
|
balance_increments: &mut HashMap<Address, u128>,
|
||||||
) {
|
) {
|
||||||
// Process withdrawals
|
// Process withdrawals
|
||||||
if chain_spec.fork(Hardfork::Shanghai).active_at_timestamp(block_timestamp) {
|
if chain_spec.is_shanghai_activated_at_timestamp(block_timestamp) {
|
||||||
if let Some(withdrawals) = withdrawals {
|
if let Some(withdrawals) = withdrawals {
|
||||||
for withdrawal in withdrawals {
|
for withdrawal in withdrawals {
|
||||||
*balance_increments.entry(withdrawal.address).or_default() +=
|
*balance_increments.entry(withdrawal.address).or_default() +=
|
||||||
|
|||||||
@ -1,16 +1,24 @@
|
|||||||
//! Support for building a pending block via local txpool.
|
//! Support for building a pending block via local txpool.
|
||||||
|
|
||||||
use crate::eth::error::EthResult;
|
use crate::eth::error::{EthApiError, EthResult};
|
||||||
|
use core::fmt::Debug;
|
||||||
use reth_primitives::{
|
use reth_primitives::{
|
||||||
constants::{BEACON_NONCE, EMPTY_WITHDRAWALS},
|
constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE},
|
||||||
proofs, Block, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader,
|
proofs, Block, ChainSpec, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader,
|
||||||
EMPTY_OMMER_ROOT, H256, U256,
|
EMPTY_OMMER_ROOT, H256, U256,
|
||||||
};
|
};
|
||||||
use reth_provider::{BundleStateWithReceipts, StateProviderFactory};
|
use reth_provider::{BundleStateWithReceipts, ChainSpecProvider, StateProviderFactory};
|
||||||
use reth_revm::{database::StateProviderDatabase, env::tx_env_with_recovered, into_reth_log};
|
use reth_revm::{
|
||||||
|
database::StateProviderDatabase,
|
||||||
|
env::tx_env_with_recovered,
|
||||||
|
into_reth_log,
|
||||||
|
state_change::{apply_beacon_root_contract_call, post_block_withdrawals_balance_increments},
|
||||||
|
};
|
||||||
use reth_transaction_pool::TransactionPool;
|
use reth_transaction_pool::TransactionPool;
|
||||||
use revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State};
|
use revm::{db::states::bundle_state::BundleRetention, Database, DatabaseCommit, State};
|
||||||
use revm_primitives::{BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState};
|
use revm_primitives::{
|
||||||
|
BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState, SpecId,
|
||||||
|
};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
/// Configured [BlockEnv] and [CfgEnv] for a pending block
|
/// Configured [BlockEnv] and [CfgEnv] for a pending block
|
||||||
@ -25,14 +33,19 @@ pub(crate) struct PendingBlockEnv {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PendingBlockEnv {
|
impl PendingBlockEnv {
|
||||||
/// Builds a pending block from the given client and pool.
|
/// Builds a pending block using the given client and pool.
|
||||||
|
///
|
||||||
|
/// If the origin is the actual pending block, the block is built with withdrawals.
|
||||||
|
///
|
||||||
|
/// After Cancun, if the origin is the actual pending block, the block includes the EIP-4788 pre
|
||||||
|
/// block contract call using the parent beacon block root received from the CL.
|
||||||
pub(crate) fn build_block<Client, Pool>(
|
pub(crate) fn build_block<Client, Pool>(
|
||||||
self,
|
self,
|
||||||
client: &Client,
|
client: &Client,
|
||||||
pool: &Pool,
|
pool: &Pool,
|
||||||
) -> EthResult<SealedBlock>
|
) -> EthResult<SealedBlock>
|
||||||
where
|
where
|
||||||
Client: StateProviderFactory,
|
Client: StateProviderFactory + ChainSpecProvider,
|
||||||
Pool: TransactionPool,
|
Pool: TransactionPool,
|
||||||
{
|
{
|
||||||
let Self { cfg, block_env, origin } = self;
|
let Self { cfg, block_env, origin } = self;
|
||||||
@ -43,13 +56,39 @@ impl PendingBlockEnv {
|
|||||||
let mut db = State::builder().with_database(Box::new(state)).with_bundle_update().build();
|
let mut db = State::builder().with_database(Box::new(state)).with_bundle_update().build();
|
||||||
|
|
||||||
let mut cumulative_gas_used = 0;
|
let mut cumulative_gas_used = 0;
|
||||||
let block_gas_limit: u64 = block_env.gas_limit.try_into().unwrap_or(u64::MAX);
|
let mut sum_blob_gas_used = 0;
|
||||||
|
let block_gas_limit: u64 = block_env.gas_limit.to::<u64>();
|
||||||
let base_fee = block_env.basefee.to::<u64>();
|
let base_fee = block_env.basefee.to::<u64>();
|
||||||
let block_number = block_env.number.to::<u64>();
|
let block_number = block_env.number.to::<u64>();
|
||||||
|
|
||||||
let mut executed_txs = Vec::new();
|
let mut executed_txs = Vec::new();
|
||||||
let mut best_txs = pool.best_transactions_with_base_fee(base_fee);
|
let mut best_txs = pool.best_transactions_with_base_fee(base_fee);
|
||||||
|
|
||||||
|
let (withdrawals, withdrawals_root) = match origin {
|
||||||
|
PendingBlockEnvOrigin::ActualPending(ref block) => {
|
||||||
|
(block.withdrawals.clone(), block.withdrawals_root)
|
||||||
|
}
|
||||||
|
PendingBlockEnvOrigin::DerivedFromLatest(_) => (None, None),
|
||||||
|
};
|
||||||
|
|
||||||
|
let chain_spec = client.chain_spec();
|
||||||
|
|
||||||
|
let parent_beacon_block_root = if origin.is_actual_pending() {
|
||||||
|
// apply eip-4788 pre block contract call if we got the block from the CL with the real
|
||||||
|
// parent beacon block root
|
||||||
|
pre_block_beacon_root_contract_call(
|
||||||
|
&mut db,
|
||||||
|
chain_spec.as_ref(),
|
||||||
|
block_number,
|
||||||
|
&cfg,
|
||||||
|
&block_env,
|
||||||
|
origin.header().parent_beacon_block_root,
|
||||||
|
)?;
|
||||||
|
origin.header().parent_beacon_block_root
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut receipts = Vec::new();
|
let mut receipts = Vec::new();
|
||||||
|
|
||||||
while let Some(pool_tx) = best_txs.next() {
|
while let Some(pool_tx) = best_txs.next() {
|
||||||
@ -65,6 +104,28 @@ impl PendingBlockEnv {
|
|||||||
// convert tx to a signed transaction
|
// convert tx to a signed transaction
|
||||||
let tx = pool_tx.to_recovered_transaction();
|
let tx = pool_tx.to_recovered_transaction();
|
||||||
|
|
||||||
|
// There's only limited amount of blob space available per block, so we need to check if
|
||||||
|
// the EIP-4844 can still fit in the block
|
||||||
|
if let Some(blob_tx) = tx.transaction.as_eip4844() {
|
||||||
|
let tx_blob_gas = blob_tx.blob_gas();
|
||||||
|
if sum_blob_gas_used + tx_blob_gas > MAX_DATA_GAS_PER_BLOCK {
|
||||||
|
// we can't fit this _blob_ transaction into the block, so we mark it as
|
||||||
|
// invalid, which removes its dependent transactions from
|
||||||
|
// the iterator. This is similar to the gas limit condition
|
||||||
|
// for regular transactions above.
|
||||||
|
best_txs.mark_invalid(&pool_tx);
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
// add to the data gas if we're going to execute the transaction
|
||||||
|
sum_blob_gas_used += tx_blob_gas;
|
||||||
|
|
||||||
|
// if we've reached the max data gas per block, we can skip blob txs entirely
|
||||||
|
if sum_blob_gas_used == MAX_DATA_GAS_PER_BLOCK {
|
||||||
|
best_txs.skip_blobs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Configure the environment for the block.
|
// Configure the environment for the block.
|
||||||
let env =
|
let env =
|
||||||
Env { cfg: cfg.clone(), block: block_env.clone(), tx: tx_env_with_recovered(&tx) };
|
Env { cfg: cfg.clone(), block: block_env.clone(), tx: tx_env_with_recovered(&tx) };
|
||||||
@ -93,10 +154,10 @@ impl PendingBlockEnv {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// commit changes
|
|
||||||
db.commit(state);
|
|
||||||
|
|
||||||
let gas_used = result.gas_used();
|
let gas_used = result.gas_used();
|
||||||
|
// commit changes
|
||||||
|
db.commit(state);
|
||||||
|
|
||||||
// add gas used by the transaction to cumulative gas used, before creating the receipt
|
// add gas used by the transaction to cumulative gas used, before creating the receipt
|
||||||
cumulative_gas_used += gas_used;
|
cumulative_gas_used += gas_used;
|
||||||
@ -112,6 +173,17 @@ impl PendingBlockEnv {
|
|||||||
// append transaction to the list of executed transactions
|
// append transaction to the list of executed transactions
|
||||||
executed_txs.push(tx.into_signed());
|
executed_txs.push(tx.into_signed());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// executes the withdrawals and commits them to the Database and BundleState.
|
||||||
|
let balance_increments = post_block_withdrawals_balance_increments(
|
||||||
|
&chain_spec,
|
||||||
|
block_env.timestamp.try_into().unwrap_or(u64::MAX),
|
||||||
|
withdrawals.clone().unwrap_or_default().as_ref(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// increment account balances for withdrawals
|
||||||
|
db.increment_balances(balance_increments)?;
|
||||||
|
|
||||||
// merge all transitions into bundle state.
|
// merge all transitions into bundle state.
|
||||||
db.merge_transitions(BundleRetention::PlainState);
|
db.merge_transitions(BundleRetention::PlainState);
|
||||||
|
|
||||||
@ -126,6 +198,10 @@ impl PendingBlockEnv {
|
|||||||
// create the block header
|
// create the block header
|
||||||
let transactions_root = proofs::calculate_transaction_root(&executed_txs);
|
let transactions_root = proofs::calculate_transaction_root(&executed_txs);
|
||||||
|
|
||||||
|
// check if cancun is activated to set eip4844 header fields correctly
|
||||||
|
let blob_gas_used =
|
||||||
|
if cfg.spec_id >= SpecId::CANCUN { Some(sum_blob_gas_used) } else { None };
|
||||||
|
|
||||||
let header = Header {
|
let header = Header {
|
||||||
parent_hash,
|
parent_hash,
|
||||||
ommers_hash: EMPTY_OMMER_ROOT,
|
ommers_hash: EMPTY_OMMER_ROOT,
|
||||||
@ -133,7 +209,7 @@ impl PendingBlockEnv {
|
|||||||
state_root,
|
state_root,
|
||||||
transactions_root,
|
transactions_root,
|
||||||
receipts_root,
|
receipts_root,
|
||||||
withdrawals_root: Some(EMPTY_WITHDRAWALS),
|
withdrawals_root,
|
||||||
logs_bloom,
|
logs_bloom,
|
||||||
timestamp: block_env.timestamp.to::<u64>(),
|
timestamp: block_env.timestamp.to::<u64>(),
|
||||||
mix_hash: block_env.prevrandao.unwrap_or_default(),
|
mix_hash: block_env.prevrandao.unwrap_or_default(),
|
||||||
@ -143,20 +219,61 @@ impl PendingBlockEnv {
|
|||||||
gas_limit: block_gas_limit,
|
gas_limit: block_gas_limit,
|
||||||
difficulty: U256::ZERO,
|
difficulty: U256::ZERO,
|
||||||
gas_used: cumulative_gas_used,
|
gas_used: cumulative_gas_used,
|
||||||
blob_gas_used: None,
|
blob_gas_used,
|
||||||
excess_blob_gas: None,
|
excess_blob_gas: block_env.get_blob_excess_gas(),
|
||||||
extra_data: Default::default(),
|
extra_data: Default::default(),
|
||||||
parent_beacon_block_root: None,
|
parent_beacon_block_root,
|
||||||
};
|
};
|
||||||
|
|
||||||
// seal the block
|
// seal the block
|
||||||
let block = Block { header, body: executed_txs, ommers: vec![], withdrawals: Some(vec![]) };
|
let block = Block { header, body: executed_txs, ommers: vec![], withdrawals };
|
||||||
let sealed_block = block.seal_slow();
|
let sealed_block = block.seal_slow();
|
||||||
|
|
||||||
Ok(sealed_block)
|
Ok(sealed_block)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call.
|
||||||
|
///
|
||||||
|
/// This constructs a new [EVM](revm::EVM) with the given DB, and environment ([CfgEnv] and
|
||||||
|
/// [BlockEnv]) to execute the pre block contract call.
|
||||||
|
///
|
||||||
|
/// This uses [apply_beacon_root_contract_call] to ultimately apply the beacon root contract state
|
||||||
|
/// change.
|
||||||
|
fn pre_block_beacon_root_contract_call<DB>(
|
||||||
|
db: &mut DB,
|
||||||
|
chain_spec: &ChainSpec,
|
||||||
|
block_number: u64,
|
||||||
|
initialized_cfg: &CfgEnv,
|
||||||
|
initialized_block_env: &BlockEnv,
|
||||||
|
parent_beacon_block_root: Option<H256>,
|
||||||
|
) -> EthResult<()>
|
||||||
|
where
|
||||||
|
DB: Database + DatabaseCommit,
|
||||||
|
<DB as Database>::Error: Debug,
|
||||||
|
{
|
||||||
|
// Configure the environment for the block.
|
||||||
|
let env = Env {
|
||||||
|
cfg: initialized_cfg.clone(),
|
||||||
|
block: initialized_block_env.clone(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// apply pre-block EIP-4788 contract call
|
||||||
|
let mut evm_pre_block = revm::EVM::with_env(env);
|
||||||
|
evm_pre_block.database(db);
|
||||||
|
|
||||||
|
// initialize a block from the env, because the pre block call needs the block itself
|
||||||
|
apply_beacon_root_contract_call(
|
||||||
|
chain_spec,
|
||||||
|
initialized_block_env.timestamp.to::<u64>(),
|
||||||
|
block_number,
|
||||||
|
parent_beacon_block_root,
|
||||||
|
&mut evm_pre_block,
|
||||||
|
)
|
||||||
|
.map_err(|err| EthApiError::Internal(err.into()))
|
||||||
|
}
|
||||||
|
|
||||||
/// The origin for a configured [PendingBlockEnv]
|
/// The origin for a configured [PendingBlockEnv]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) enum PendingBlockEnvOrigin {
|
pub(crate) enum PendingBlockEnvOrigin {
|
||||||
|
|||||||
Reference in New Issue
Block a user