-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
146 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
//! [EIP-7002](https://eips.ethereum.org/EIPS/eip-7002) system call implementation. | ||
use alloc::{boxed::Box, format, string::ToString, vec::Vec}; | ||
use core::fmt::Display; | ||
|
||
use crate::ConfigureEvm; | ||
use alloy_eips::eip7002::{WithdrawalRequest, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS}; | ||
use reth_execution_errors::{BlockExecutionError, BlockValidationError}; | ||
use reth_primitives::{Buf, Header, Request}; | ||
use revm::{interpreter::Host, Database, DatabaseCommit, Evm}; | ||
use revm_primitives::{ | ||
Address, BlockEnv, Bytes, CfgEnvWithHandlerCfg, EnvWithHandlerCfg, ExecutionResult, FixedBytes, | ||
ResultAndState, | ||
}; | ||
|
||
/// 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>( | ||
evm_config: &EvmConfig, | ||
db: &mut DB, | ||
initialized_cfg: &CfgEnvWithHandlerCfg, | ||
initialized_block_env: &BlockEnv, | ||
) -> Result<Vec<Request>, BlockExecutionError> | ||
where | ||
DB: Database + DatabaseCommit, | ||
DB::Error: Display, | ||
EvmConfig: ConfigureEvm<Header = Header>, | ||
{ | ||
// 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(evm_config, &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_config: &EvmConfig, | ||
evm: &mut Evm<'_, EXT, DB>, | ||
) -> Result<Vec<Request>, BlockExecutionError> | ||
where | ||
DB: Database + DatabaseCommit, | ||
DB::Error: core::fmt::Display, | ||
EvmConfig: ConfigureEvm<Header = Header>, | ||
{ | ||
// 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`. | ||
evm_config.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(WithdrawalRequest { source_address, validator_pubkey, amount }.into()); | ||
} | ||
|
||
Ok(withdrawal_requests) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters