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

chore: move withdrawal_requests_contract_call to reth-evm #9272

Merged
merged 2 commits into from
Jul 4, 2024
Merged
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
1 change: 0 additions & 1 deletion Cargo.lock

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

10 changes: 4 additions & 6 deletions crates/ethereum/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
},
system_calls::apply_beacon_root_contract_call,
system_calls::{apply_beacon_root_contract_call, apply_withdrawal_requests_contract_call},
ConfigureEvm,
};
use reth_execution_types::ExecutionOutcome;
Expand All @@ -22,10 +22,7 @@ use reth_prune_types::PruneModes;
use reth_revm::{
batch::{BlockBatchRecord, BlockExecutorStats},
db::states::bundle_state::BundleRetention,
state_change::{
apply_blockhashes_update, apply_withdrawal_requests_contract_call,
post_block_balance_increments,
},
state_change::{apply_blockhashes_update, post_block_balance_increments},
Evm, State,
};
use revm_primitives::{
Expand Down Expand Up @@ -222,7 +219,8 @@ where
crate::eip6110::parse_deposits_from_receipts(&self.chain_spec, &receipts)?;

// Collect all EIP-7685 requests
let withdrawal_requests = apply_withdrawal_requests_contract_call(&mut evm)?;
let withdrawal_requests =
apply_withdrawal_requests_contract_call::<EvmConfig, _, _>(&mut evm)?;

[deposit_requests, withdrawal_requests].concat()
} else {
Expand Down
50 changes: 29 additions & 21 deletions crates/ethereum/payload/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,16 @@
#![allow(clippy::useless_let_if_seq)]

use reth_basic_payload_builder::{
commit_withdrawals, is_better_payload, post_block_withdrawal_requests_contract_call,
BuildArguments, BuildOutcome, PayloadBuilder, PayloadConfig, WithdrawalsOutcome,
commit_withdrawals, is_better_payload, BuildArguments, BuildOutcome, PayloadBuilder,
PayloadConfig, WithdrawalsOutcome,
};
use reth_errors::RethError;
use reth_evm::{system_calls::pre_block_beacon_root_contract_call, ConfigureEvm};
use reth_evm::{
system_calls::{
post_block_withdrawal_requests_contract_call, pre_block_beacon_root_contract_call,
},
ConfigureEvm,
};
use reth_evm_ethereum::{eip6110::parse_deposits_from_receipts, EthEvmConfig};
use reth_execution_types::ExecutionOutcome;
use reth_payload_builder::{
Expand Down Expand Up @@ -190,22 +195,24 @@ where
}

// Calculate the requests and the requests root.
let (requests, requests_root) =
if chain_spec.is_prague_active_at_timestamp(attributes.timestamp) {
// We do not calculate the EIP-6110 deposit requests because there are no
// transactions in an empty payload.
let withdrawal_requests = post_block_withdrawal_requests_contract_call(
&mut db,
&initialized_cfg,
&initialized_block_env,
)?;

let requests = withdrawal_requests;
let requests_root = calculate_requests_root(&requests);
(Some(requests.into()), Some(requests_root))
} else {
(None, None)
};
let (requests, requests_root) = if chain_spec
.is_prague_active_at_timestamp(attributes.timestamp)
{
// We do not calculate the EIP-6110 deposit requests because there are no
// transactions in an empty payload.
let withdrawal_requests = post_block_withdrawal_requests_contract_call::<EvmConfig, _>(
&mut db,
&initialized_cfg,
&initialized_block_env,
)
.map_err(|err| PayloadBuilderError::Internal(err.into()))?;

let requests = withdrawal_requests;
let requests_root = calculate_requests_root(&requests);
(Some(requests.into()), Some(requests_root))
} else {
(None, None)
};

let header = Header {
parent_hash: parent_block.hash(),
Expand Down Expand Up @@ -436,11 +443,12 @@ where
{
let deposit_requests = parse_deposits_from_receipts(&chain_spec, receipts.iter().flatten())
.map_err(|err| PayloadBuilderError::Internal(RethError::Execution(err.into())))?;
let withdrawal_requests = post_block_withdrawal_requests_contract_call(
let withdrawal_requests = post_block_withdrawal_requests_contract_call::<EvmConfig, _>(
&mut db,
&initialized_cfg,
&initialized_block_env,
)?;
)
.map_err(|err| PayloadBuilderError::Internal(err.into()))?;

let requests = [deposit_requests, withdrawal_requests].concat();
let requests_root = calculate_requests_root(&requests);
Expand Down
145 changes: 141 additions & 4 deletions crates/evm/src/system_calls.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
//! System contract call functions.

use alloy_eips::eip4788::BEACON_ROOTS_ADDRESS;
use crate::ConfigureEvm;
use alloy_eips::{
eip4788::BEACON_ROOTS_ADDRESS,
eip7002::{WithdrawalRequest, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS},
};
use reth_chainspec::{ChainSpec, EthereumHardforks};
use reth_execution_errors::{BlockExecutionError, BlockValidationError};
use reth_primitives::{Buf, Request};
use revm::{interpreter::Host, Database, DatabaseCommit, Evm};
use revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, B256};

use crate::ConfigureEvm;
use revm_primitives::{
Address, BlockEnv, Bytes, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, FixedBytes,
ResultAndState, B256,
};

/// Apply the [EIP-4788](https://eips.ethereum.org/EIPS/eip-4788) pre block contract call.
///
Expand Down Expand Up @@ -123,3 +129,134 @@ where

Ok(())
}

/// Apply the [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) post block contract call.
///
/// This constructs a new [Evm] with the given DB, and environment
/// ([`CfgEnvWithHandlerCfg`] and [`BlockEnv`]) to execute the post block contract call.
///
/// This uses [`apply_withdrawal_requests_contract_call`] to ultimately calculate the
/// [requests](Request).
pub fn post_block_withdrawal_requests_contract_call<EvmConfig, DB>(
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
) -> Result<Vec<Request>, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: std::fmt::Display,
EvmConfig: ConfigureEvm,
{
// apply post-block EIP-7002 contract call
let mut evm_post_block = Evm::builder()
.with_db(db)
.with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env(
initialized_cfg.clone(),
initialized_block_env.clone(),
Default::default(),
))
.build();

// initialize a block from the env, because the post block call needs the block itself
apply_withdrawal_requests_contract_call::<EvmConfig, _, _>(&mut evm_post_block)
}

/// Applies the post-block call to the EIP-7002 withdrawal requests contract.
///
/// If Prague is not active at the given timestamp, then this is a no-op, and an empty vector is
/// returned. Otherwise, the withdrawal requests are returned.
#[inline]
pub fn apply_withdrawal_requests_contract_call<EvmConfig, EXT, DB>(
evm: &mut Evm<'_, EXT, DB>,
) -> Result<Vec<Request>, BlockExecutionError>
where
DB: Database + DatabaseCommit,
DB::Error: core::fmt::Display,
EvmConfig: ConfigureEvm,
{
// get previous env
let previous_env = Box::new(evm.context.env().clone());

// Fill transaction environment with the EIP-7002 withdrawal requests contract message data.
//
// This requirement for the withdrawal requests contract call defined by
// [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) is:
//
// At the end of processing any execution block where `block.timestamp >= FORK_TIMESTAMP` (i.e.
// after processing all transactions and after performing the block body withdrawal requests
// validations), call the contract as `SYSTEM_ADDRESS`.
EvmConfig::fill_tx_env_system_contract_call(
&mut evm.context.evm.env,
alloy_eips::eip7002::SYSTEM_ADDRESS,
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
Bytes::new(),
);

let ResultAndState { result, mut state } = match evm.transact() {
Ok(res) => res,
Err(e) => {
evm.context.evm.env = previous_env;
return Err(BlockValidationError::WithdrawalRequestsContractCall {
message: format!("execution failed: {e}"),
}
.into())
}
};

// cleanup the state
state.remove(&alloy_eips::eip7002::SYSTEM_ADDRESS);
state.remove(&evm.block().coinbase);
evm.context.evm.db.commit(state);

// re-set the previous env
evm.context.evm.env = previous_env;

let mut data = match result {
ExecutionResult::Success { output, .. } => Ok(output.into_data()),
ExecutionResult::Revert { output, .. } => {
Err(BlockValidationError::WithdrawalRequestsContractCall {
message: format!("execution reverted: {output}"),
})
}
ExecutionResult::Halt { reason, .. } => {
Err(BlockValidationError::WithdrawalRequestsContractCall {
message: format!("execution halted: {reason:?}"),
})
}
}?;

// Withdrawals are encoded as a series of withdrawal requests, each with the following
// format:
//
// +------+--------+--------+
// | addr | pubkey | amount |
// +------+--------+--------+
// 20 48 8

const WITHDRAWAL_REQUEST_SIZE: usize = 20 + 48 + 8;
let mut withdrawal_requests = Vec::with_capacity(data.len() / WITHDRAWAL_REQUEST_SIZE);
while data.has_remaining() {
if data.remaining() < WITHDRAWAL_REQUEST_SIZE {
return Err(BlockValidationError::WithdrawalRequestsContractCall {
message: "invalid withdrawal request length".to_string(),
}
.into())
}

let mut source_address = Address::ZERO;
data.copy_to_slice(source_address.as_mut_slice());

let mut validator_pubkey = FixedBytes::<48>::ZERO;
data.copy_to_slice(validator_pubkey.as_mut_slice());

let amount = data.get_u64();

withdrawal_requests.push(Request::WithdrawalRequest(WithdrawalRequest {
source_address,
validator_pubkey,
amount,
}));
}

Ok(withdrawal_requests)
}
40 changes: 4 additions & 36 deletions crates/payload/basic/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,17 @@ use reth_payload_builder::{
use reth_payload_primitives::{BuiltPayload, PayloadBuilderAttributes};
use reth_primitives::{
constants::{EMPTY_WITHDRAWALS, RETH_CLIENT_VERSION, SLOT_DURATION},
proofs, BlockNumberOrTag, Bytes, Request, SealedBlock, Withdrawals, B256, U256,
proofs, BlockNumberOrTag, Bytes, SealedBlock, Withdrawals, B256, U256,
};
use reth_provider::{
BlockReaderIdExt, BlockSource, CanonStateNotification, ProviderError, StateProviderFactory,
};
use reth_revm::state_change::{
apply_withdrawal_requests_contract_call, post_block_withdrawals_balance_increments,
};
use reth_revm::state_change::post_block_withdrawals_balance_increments;
use reth_tasks::TaskSpawner;
use reth_transaction_pool::TransactionPool;
use revm::{
primitives::{BlockEnv, CfgEnvWithHandlerCfg, EnvWithHandlerCfg},
Database, DatabaseCommit, Evm, State,
primitives::{BlockEnv, CfgEnvWithHandlerCfg},
Database, State,
};
use std::{
fmt,
Expand Down Expand Up @@ -922,36 +920,6 @@ pub fn commit_withdrawals<DB: Database<Error = ProviderError>>(
})
}

/// Apply the [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) post block contract call.
///
/// This constructs a new [Evm] with the given DB, and environment
/// ([`CfgEnvWithHandlerCfg`] and [`BlockEnv`]) to execute the post block contract call.
///
/// This uses [`apply_withdrawal_requests_contract_call`] to ultimately calculate the
/// [requests](Request).
pub fn post_block_withdrawal_requests_contract_call<DB: Database + DatabaseCommit>(
db: &mut DB,
initialized_cfg: &CfgEnvWithHandlerCfg,
initialized_block_env: &BlockEnv,
) -> Result<Vec<Request>, PayloadBuilderError>
where
DB::Error: std::fmt::Display,
{
// apply post-block EIP-7002 contract call
let mut evm_post_block = Evm::builder()
.with_db(db)
.with_env_with_handler_cfg(EnvWithHandlerCfg::new_with_cfg_env(
initialized_cfg.clone(),
initialized_block_env.clone(),
Default::default(),
))
.build();

// initialize a block from the env, because the post block call needs the block itself
apply_withdrawal_requests_contract_call(&mut evm_post_block)
.map_err(|err| PayloadBuilderError::Internal(err.into()))
}

/// Checks if the new payload is better than the current best.
///
/// This compares the total fees of the blocks, higher is better.
Expand Down
2 changes: 0 additions & 2 deletions crates/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ pub mod genesis;
pub mod header;
pub mod proofs;
mod receipt;
/// Helpers for working with revm
pub mod revm;
pub use reth_static_file_types as static_file;
pub mod transaction;
#[cfg(any(test, feature = "arbitrary"))]
Expand Down
Loading
Loading