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-7251 #9335

Merged
merged 1 commit into from
Jul 8, 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
11 changes: 9 additions & 2 deletions crates/ethereum/evm/src/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ use reth_evm::{
BatchExecutor, BlockExecutionError, BlockExecutionInput, BlockExecutionOutput,
BlockExecutorProvider, BlockValidationError, Executor, ProviderError,
},
system_calls::{apply_beacon_root_contract_call, apply_withdrawal_requests_contract_call},
system_calls::{
apply_beacon_root_contract_call, apply_consolidation_requests_contract_call,
apply_withdrawal_requests_contract_call,
},
ConfigureEvm,
};
use reth_execution_types::ExecutionOutcome;
Expand Down Expand Up @@ -223,7 +226,11 @@ where
let withdrawal_requests =
apply_withdrawal_requests_contract_call(&self.evm_config, &mut evm)?;

[deposit_requests, withdrawal_requests].concat()
// Collect all EIP-7251 requests
let consolidation_requests =
apply_consolidation_requests_contract_call(&self.evm_config, &mut evm)?;

[deposit_requests, withdrawal_requests, consolidation_requests].concat()
} else {
vec![]
};
Expand Down
8 changes: 8 additions & 0 deletions crates/evm/execution-errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ pub enum BlockValidationError {
/// The error message.
message: String,
},
/// EVM error during consolidation requests contract call [EIP-7251]
///
/// [EIP-7251]: https://eips.ethereum.org/EIPS/eip-7251
#[error("failed to apply consolidation requests contract call: {message}")]
ConsolidationRequestsContractCall {
/// The error message.
message: String,
},
/// Error when decoding deposit requests from receipts [EIP-6110]
///
/// [EIP-6110]: https://eips.ethereum.org/EIPS/eip-6110
Expand Down
136 changes: 136 additions & 0 deletions crates/evm/src/system_calls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::ConfigureEvm;
use alloy_eips::{
eip4788::BEACON_ROOTS_ADDRESS,
eip7002::{WithdrawalRequest, WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS},
eip7251::{ConsolidationRequest, CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS},
};
use reth_chainspec::{ChainSpec, EthereumHardforks};
use reth_execution_errors::{BlockExecutionError, BlockValidationError};
Expand Down Expand Up @@ -264,3 +265,138 @@ where

Ok(withdrawal_requests)
}

/// Apply the [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) 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_consolidation_requests_contract_call`] to ultimately calculate the
/// [requests](Request).
pub fn post_block_consolidation_requests_contract_call<EvmConfig, DB>(
Copy link
Member

Choose a reason for hiding this comment

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

we have essentially the same functionality copy pasted 3 times now, maybe perhaps time to abstract it

evm_config: &EvmConfig,
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-7251 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_consolidation_requests_contract_call::<EvmConfig, _, _>(evm_config, &mut evm_post_block)
}

/// Applies the post-block call to the EIP-7251 consolidation 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 consolidation requests are returned.
#[inline]
pub fn apply_consolidation_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,
{
// get previous env
let previous_env = Box::new(evm.context.env().clone());

// Fill transaction environment with the EIP-7251 consolidation requests contract message data.
//
// This requirement for the consolidation requests contract call defined by
// [EIP-7251](https://eips.ethereum.org/EIPS/eip-7251) 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 requests validations)
// clienst software MUST [..] call the contract as `SYSTEM_ADDRESS` and empty input data to
// trigger the system subroutine execute.
evm_config.fill_tx_env_system_contract_call(
&mut evm.context.evm.env,
alloy_eips::eip7002::SYSTEM_ADDRESS,
CONSOLIDATION_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::ConsolidationRequestsContractCall {
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::ConsolidationRequestsContractCall {
message: format!("execution reverted: {output}"),
})
}
ExecutionResult::Halt { reason, .. } => {
Err(BlockValidationError::ConsolidationRequestsContractCall {
message: format!("execution halted: {reason:?}"),
})
}
}?;

// Consolidations are encoded as a series of consolidation requests, each with the following
// format:
//
// +------+--------+---------------+
// | addr | pubkey | target pubkey |
// +------+--------+---------------+
// 20 48 48

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

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

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

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

consolidation_requests.push(Request::ConsolidationRequest(ConsolidationRequest {
source_address,
source_pubkey,
target_pubkey,
}));
}

Ok(consolidation_requests)
}
Loading