Skip to content

Commit

Permalink
Merge branch 'main' into klkvr/add-tx-check-to-static-writer
Browse files Browse the repository at this point in the history
  • Loading branch information
mattsse committed Nov 15, 2024
2 parents af81abe + 6e00e58 commit dbab888
Show file tree
Hide file tree
Showing 17 changed files with 359 additions and 100 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,11 @@ jobs:
partition: 2
total_partitions: 2
- type: optimism
args: --features "asm-keccak optimism" --locked
args: --features "asm-keccak optimism" --locked --exclude reth --exclude reth-bench --exclude "example-*"
partition: 1
total_partitions: 2
- type: optimism
args: --features "asm-keccak optimism" --locked
args: --features "asm-keccak optimism" --locked --exclude reth --exclude reth-bench --exclude "example-*"
partition: 2
total_partitions: 2
- type: book
Expand Down
5 changes: 5 additions & 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
173 changes: 134 additions & 39 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,8 @@ use crate::{
payload::{OpBuiltPayload, OpPayloadBuilderAttributes},
};
use op_alloy_consensus::DepositTransaction;
use op_alloy_rpc_types_engine::OpPayloadAttributes;
use reth_revm::witness::ExecutionWitnessRecord;
use reth_transaction_pool::pool::BestPayloadTransactions;

/// Optimism's payload builder
Expand Down Expand Up @@ -92,21 +95,6 @@ where
EvmConfig: ConfigureEvm<Header = Header>,
Txs: OpPayloadTransactions,
{
/// Returns the configured [`CfgEnvWithHandlerCfg`] and [`BlockEnv`] for the targeted payload
/// (that has the `parent` as its parent).
pub fn cfg_and_block_env(
&self,
config: &PayloadConfig<OpPayloadBuilderAttributes>,
parent: &Header,
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), EvmConfig::Error> {
let next_attributes = NextBlockEnvAttributes {
timestamp: config.attributes.timestamp(),
suggested_fee_recipient: config.attributes.suggested_fee_recipient(),
prev_randao: config.attributes.prev_randao(),
};
self.evm_config.next_cfg_and_block_env(parent, next_attributes)
}

/// Constructs an Optimism payload from the transactions sent via the
/// Payload attributes by the sequencer. If the `no_tx_pool` argument is passed in
/// the payload attributes, the transaction pool will be ignored and the only transactions
Expand All @@ -124,7 +112,7 @@ where
Pool: TransactionPool,
{
let (initialized_cfg, initialized_block_env) = self
.cfg_and_block_env(&args.config, &args.config.parent_header)
.cfg_and_block_env(&args.config.attributes, &args.config.parent_header)
.map_err(PayloadBuilderError::other)?;

let BuildArguments { client, pool, mut cached_reads, config, cancel, best_payload } = args;
Expand Down Expand Up @@ -159,6 +147,65 @@ where
}
}

impl<EvmConfig, Txs> OpPayloadBuilder<EvmConfig, Txs>
where
EvmConfig: ConfigureEvm<Header = Header>,
{
/// Returns the configured [`CfgEnvWithHandlerCfg`] and [`BlockEnv`] for the targeted payload
/// (that has the `parent` as its parent).
pub fn cfg_and_block_env(
&self,
attributes: &OpPayloadBuilderAttributes,
parent: &Header,
) -> Result<(CfgEnvWithHandlerCfg, BlockEnv), EvmConfig::Error> {
let next_attributes = NextBlockEnvAttributes {
timestamp: attributes.timestamp(),
suggested_fee_recipient: attributes.suggested_fee_recipient(),
prev_randao: attributes.prev_randao(),
};
self.evm_config.next_cfg_and_block_env(parent, next_attributes)
}

/// Computes the witness for the payload.
pub fn payload_witness<Client>(
&self,
client: &Client,
parent: SealedHeader,
attributes: OpPayloadAttributes,
) -> Result<ExecutionWitness, PayloadBuilderError>
where
Client: StateProviderFactory + ChainSpecProvider<ChainSpec = OpChainSpec>,
{
let attributes = OpPayloadBuilderAttributes::try_new(parent.hash(), attributes, 3)
.map_err(PayloadBuilderError::other)?;

let (initialized_cfg, initialized_block_env) =
self.cfg_and_block_env(&attributes, &parent).map_err(PayloadBuilderError::other)?;

let config = PayloadConfig {
parent_header: Arc::new(parent),
attributes,
extra_data: Default::default(),
};
let ctx = OpPayloadBuilderCtx {
evm_config: self.evm_config.clone(),
chain_spec: client.chain_spec(),
config,
initialized_cfg,
initialized_block_env,
cancel: Default::default(),
best_payload: Default::default(),
};

let state_provider = client.state_by_block_hash(ctx.parent().hash())?;
let state = StateProviderDatabase::new(state_provider);
let mut state = State::builder().with_database(state).with_bundle_update().build();

let builder = OpBuilder { pool: NoopTransactionPool::default(), best: () };
builder.witness(&mut state, &ctx)
}
}

/// Implementation of the [`PayloadBuilder`] trait for [`OpPayloadBuilder`].
impl<Pool, Client, EvmConfig, Txs> PayloadBuilder<Pool, Client> for OpPayloadBuilder<EvmConfig, Txs>
where
Expand Down Expand Up @@ -234,36 +281,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 +317,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 +368,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 +454,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 +495,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 +818,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 +859,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
11 changes: 11 additions & 0 deletions crates/optimism/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,14 @@ alloy-eips.workspace = true
alloy-rlp.workspace = true
derive_more.workspace = true
bytes.workspace = true
reth-primitives-traits.workspace = true
reth-codecs = { workspace = true, optional = true }
reth-primitives = { workspace = true, features = ["reth-codec"], optional = true }

[features]
default = ["reth-codec"]
reth-codec = ["dep:reth-codecs", "dep:reth-primitives"]

[dev-dependencies]
reth-codecs = { workspace = true, features = ["test-utils"] }
rstest.workspace = true
2 changes: 1 addition & 1 deletion crates/optimism/primitives/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]

pub mod bedrock;
pub mod op_tx_type;
pub mod tx_type;
Loading

0 comments on commit dbab888

Please sign in to comment.