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: add helpers to obtain the execution witness for a payload #12573

Merged
merged 2 commits into from
Nov 15, 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: 1 addition & 0 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion crates/optimism/payload/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ workspace = true
# reth
reth-chainspec.workspace = true
reth-primitives.workspace = true
reth-revm.workspace = true
reth-revm = { workspace = true, features = ["witness"] }
reth-transaction-pool.workspace = true
reth-provider.workspace = true
reth-rpc-types-compat.workspace = true
Expand All @@ -41,6 +41,7 @@ alloy-rlp.workspace = true
op-alloy-rpc-types-engine.workspace = true
op-alloy-consensus.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-rpc-types-debug.workspace = true
alloy-consensus.workspace = true

# misc
Expand Down
96 changes: 73 additions & 23 deletions crates/optimism/payload/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::{fmt::Display, sync::Arc};
use alloy_consensus::{Header, Transaction, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::merge::BEACON_NONCE;
use alloy_primitives::{Address, Bytes, U256};
use alloy_rpc_types_debug::ExecutionWitness;
use alloy_rpc_types_engine::PayloadId;
use reth_basic_payload_builder::*;
use reth_chain_state::ExecutedBlock;
Expand All @@ -20,7 +21,7 @@ use reth_primitives::{
revm_primitives::{BlockEnv, CfgEnvWithHandlerCfg},
Block, BlockBody, Receipt, SealedHeader, TransactionSigned, TxType,
};
use reth_provider::{ProviderError, StateProviderFactory, StateRootProvider};
use reth_provider::{ProviderError, StateProofProvider, StateProviderFactory, StateRootProvider};
use reth_revm::database::StateProviderDatabase;
use reth_transaction_pool::{
noop::NoopTransactionPool, BestTransactionsAttributes, PayloadTransactions, TransactionPool,
Expand All @@ -38,6 +39,7 @@ use crate::{
payload::{OpBuiltPayload, OpPayloadBuilderAttributes},
};
use op_alloy_consensus::DepositTransaction;
use reth_revm::witness::ExecutionWitnessRecord;
use reth_transaction_pool::pool::BestPayloadTransactions;

/// Optimism's payload builder
Expand Down Expand Up @@ -234,36 +236,33 @@ where
Pool: TransactionPool,
Txs: OpPayloadTransactions,
{
/// Builds the payload on top of the state.
pub fn build<EvmConfig, DB, P>(
/// Executes the payload and returns the outcome.
pub fn execute<EvmConfig, DB>(
self,
mut db: State<DB>,
ctx: OpPayloadBuilderCtx<EvmConfig>,
) -> Result<BuildOutcomeKind<OpBuiltPayload>, PayloadBuilderError>
state: &mut State<DB>,
ctx: &OpPayloadBuilderCtx<EvmConfig>,
) -> Result<BuildOutcomeKind<ExecutedPayload>, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header>,
DB: Database<Error = ProviderError> + AsRef<P>,
P: StateRootProvider,
DB: Database<Error = ProviderError>,
{
let Self { pool, best } = self;
debug!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload");

// 1. apply eip-4788 pre block contract call
ctx.apply_pre_beacon_root_contract_call(&mut db)?;
ctx.apply_pre_beacon_root_contract_call(state)?;

// 2. ensure create2deployer is force deployed
ctx.ensure_create2_deployer(&mut db)?;
ctx.ensure_create2_deployer(state)?;

// 3. execute sequencer transactions
let mut info = ctx.execute_sequencer_transactions(&mut db)?;
let mut info = ctx.execute_sequencer_transactions(state)?;

// 4. if mem pool transactions are requested we execute them
if !ctx.attributes().no_tx_pool {
let best_txs = best.best_transactions(pool, ctx.best_transaction_attributes());
if let Some(cancelled) =
ctx.execute_best_transactions::<_, Pool>(&mut info, &mut db, best_txs)?
{
return Ok(cancelled)
if ctx.execute_best_transactions::<_, Pool>(&mut info, state, best_txs)?.is_some() {
return Ok(BuildOutcomeKind::Cancelled)
}

// check if the new payload is even more valuable
Expand All @@ -273,16 +272,38 @@ where
}
}

let WithdrawalsOutcome { withdrawals_root, withdrawals } =
ctx.commit_withdrawals(&mut db)?;
let withdrawals_outcome = ctx.commit_withdrawals(state)?;

// merge all transitions into bundle state, this would apply the withdrawal balance changes
// and 4788 contract call
db.merge_transitions(BundleRetention::Reverts);
state.merge_transitions(BundleRetention::Reverts);

Ok(BuildOutcomeKind::Better { payload: ExecutedPayload { info, withdrawals_outcome } })
}

/// Builds the payload on top of the state.
pub fn build<EvmConfig, DB, P>(
self,
mut state: State<DB>,
ctx: OpPayloadBuilderCtx<EvmConfig>,
) -> Result<BuildOutcomeKind<OpBuiltPayload>, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header>,
DB: Database<Error = ProviderError> + AsRef<P>,
P: StateRootProvider,
{
let ExecutedPayload {
info,
withdrawals_outcome: WithdrawalsOutcome { withdrawals, withdrawals_root },
} = match self.execute(&mut state, &ctx)? {
BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload,
BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled),
BuildOutcomeKind::Aborted { fees } => return Ok(BuildOutcomeKind::Aborted { fees }),
};

let block_number = ctx.block_number();
let execution_outcome = ExecutionOutcome::new(
db.take_bundle(),
state.take_bundle(),
vec![info.receipts.clone()].into(),
block_number,
Vec::new(),
Expand All @@ -302,7 +323,7 @@ where
// // calculate the state root
let hashed_state = HashedPostState::from_bundle_state(&execution_outcome.state().state);
let (state_root, trie_output) = {
db.database.as_ref().state_root_with_updates(hashed_state.clone()).inspect_err(
state.database.as_ref().state_root_with_updates(hashed_state.clone()).inspect_err(
|err| {
warn!(target: "payload_builder",
parent_header=%ctx.parent().hash(),
Expand Down Expand Up @@ -388,6 +409,24 @@ where
Ok(BuildOutcomeKind::Better { payload })
}
}

/// Builds the payload and returns its [`ExecutionWitness`] based on the state after execution.
pub fn witness<EvmConfig, DB, P>(
self,
state: &mut State<DB>,
ctx: &OpPayloadBuilderCtx<EvmConfig>,
) -> Result<ExecutionWitness, PayloadBuilderError>
where
EvmConfig: ConfigureEvm<Header = Header>,
DB: Database<Error = ProviderError> + AsRef<P>,
P: StateProofProvider,
{
let _ = self.execute(state, ctx)?;
let ExecutionWitnessRecord { hashed_state, codes, keys } =
ExecutionWitnessRecord::from_executed_state(state);
let state = state.database.as_ref().witness(Default::default(), hashed_state)?;
Ok(ExecutionWitness { state: state.into_iter().collect(), codes, keys })
}
}

/// A type that returns a the [`PayloadTransactions`] that should be included in the pool.
Expand All @@ -411,6 +450,15 @@ impl OpPayloadTransactions for () {
}
}

/// Holds the state after execution
#[derive(Debug)]
pub struct ExecutedPayload {
/// Tracked execution info
pub info: ExecutionInfo,
/// Outcome after committing withdrawals.
pub withdrawals_outcome: WithdrawalsOutcome,
}

/// This acts as the container for executed transactions and its byproducts (receipts, gas used)
#[derive(Default, Debug)]
pub struct ExecutionInfo {
Expand Down Expand Up @@ -725,13 +773,15 @@ where
Ok(info)
}

/// Executes the given best transactions and updates the execution info
/// Executes the given best transactions and updates the execution info.
///
/// Returns `Ok(Some(())` if the job was cancelled.
pub fn execute_best_transactions<DB, Pool>(
&self,
info: &mut ExecutionInfo,
db: &mut State<DB>,
mut best_txs: impl PayloadTransactions,
) -> Result<Option<BuildOutcomeKind<OpBuiltPayload>>, PayloadBuilderError>
) -> Result<Option<()>, PayloadBuilderError>
where
DB: Database<Error = ProviderError>,
Pool: TransactionPool,
Expand Down Expand Up @@ -764,7 +814,7 @@ where

// check if the job was cancelled, if so we can exit early
if self.cancel.is_cancelled() {
return Ok(Some(BuildOutcomeKind::Cancelled))
return Ok(Some(()))
}

// Configure the environment for the tx.
Expand Down
Loading