mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 19:09:54 +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_interfaces::executor::{BlockExecutionError, BlockValidationError};
|
||||
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 revm::{primitives::ResultAndState, DatabaseCommit, EVM};
|
||||
@ -109,8 +109,8 @@ where
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a map of addresses to their balance increments if shanghai is active at the given
|
||||
/// timestamp.
|
||||
/// Returns a map of addresses to their balance increments if the Shanghai hardfork is active at the
|
||||
/// given timestamp.
|
||||
#[inline]
|
||||
pub fn post_block_withdrawals_balance_increments(
|
||||
chain_spec: &ChainSpec,
|
||||
@ -137,7 +137,7 @@ pub fn insert_post_block_withdrawals_balance_increments(
|
||||
balance_increments: &mut HashMap<Address, u128>,
|
||||
) {
|
||||
// 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 {
|
||||
for withdrawal in withdrawals {
|
||||
*balance_increments.entry(withdrawal.address).or_default() +=
|
||||
|
||||
@ -1,16 +1,24 @@
|
||||
//! 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::{
|
||||
constants::{BEACON_NONCE, EMPTY_WITHDRAWALS},
|
||||
proofs, Block, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader,
|
||||
constants::{eip4844::MAX_DATA_GAS_PER_BLOCK, BEACON_NONCE},
|
||||
proofs, Block, ChainSpec, Header, IntoRecoveredTransaction, Receipt, SealedBlock, SealedHeader,
|
||||
EMPTY_OMMER_ROOT, H256, U256,
|
||||
};
|
||||
use reth_provider::{BundleStateWithReceipts, StateProviderFactory};
|
||||
use reth_revm::{database::StateProviderDatabase, env::tx_env_with_recovered, into_reth_log};
|
||||
use reth_provider::{BundleStateWithReceipts, ChainSpecProvider, StateProviderFactory};
|
||||
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 revm::{db::states::bundle_state::BundleRetention, DatabaseCommit, State};
|
||||
use revm_primitives::{BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState};
|
||||
use revm::{db::states::bundle_state::BundleRetention, Database, DatabaseCommit, State};
|
||||
use revm_primitives::{
|
||||
BlockEnv, CfgEnv, EVMError, Env, InvalidTransaction, ResultAndState, SpecId,
|
||||
};
|
||||
use std::time::Instant;
|
||||
|
||||
/// Configured [BlockEnv] and [CfgEnv] for a pending block
|
||||
@ -25,14 +33,19 @@ pub(crate) struct 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>(
|
||||
self,
|
||||
client: &Client,
|
||||
pool: &Pool,
|
||||
) -> EthResult<SealedBlock>
|
||||
where
|
||||
Client: StateProviderFactory,
|
||||
Client: StateProviderFactory + ChainSpecProvider,
|
||||
Pool: TransactionPool,
|
||||
{
|
||||
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 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 block_number = block_env.number.to::<u64>();
|
||||
|
||||
let mut executed_txs = Vec::new();
|
||||
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();
|
||||
|
||||
while let Some(pool_tx) = best_txs.next() {
|
||||
@ -65,6 +104,28 @@ impl PendingBlockEnv {
|
||||
// convert tx to a signed 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.
|
||||
let env =
|
||||
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();
|
||||
// commit changes
|
||||
db.commit(state);
|
||||
|
||||
// add gas used by the transaction to cumulative gas used, before creating the receipt
|
||||
cumulative_gas_used += gas_used;
|
||||
@ -112,6 +173,17 @@ impl PendingBlockEnv {
|
||||
// append transaction to the list of executed transactions
|
||||
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.
|
||||
db.merge_transitions(BundleRetention::PlainState);
|
||||
|
||||
@ -126,6 +198,10 @@ impl PendingBlockEnv {
|
||||
// create the block header
|
||||
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 {
|
||||
parent_hash,
|
||||
ommers_hash: EMPTY_OMMER_ROOT,
|
||||
@ -133,7 +209,7 @@ impl PendingBlockEnv {
|
||||
state_root,
|
||||
transactions_root,
|
||||
receipts_root,
|
||||
withdrawals_root: Some(EMPTY_WITHDRAWALS),
|
||||
withdrawals_root,
|
||||
logs_bloom,
|
||||
timestamp: block_env.timestamp.to::<u64>(),
|
||||
mix_hash: block_env.prevrandao.unwrap_or_default(),
|
||||
@ -143,20 +219,61 @@ impl PendingBlockEnv {
|
||||
gas_limit: block_gas_limit,
|
||||
difficulty: U256::ZERO,
|
||||
gas_used: cumulative_gas_used,
|
||||
blob_gas_used: None,
|
||||
excess_blob_gas: None,
|
||||
blob_gas_used,
|
||||
excess_blob_gas: block_env.get_blob_excess_gas(),
|
||||
extra_data: Default::default(),
|
||||
parent_beacon_block_root: None,
|
||||
parent_beacon_block_root,
|
||||
};
|
||||
|
||||
// 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();
|
||||
|
||||
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]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum PendingBlockEnvOrigin {
|
||||
|
||||
Reference in New Issue
Block a user