Skip to content

Commit 107c3cd

Browse files
committed
add tx simulation
1 parent aa6b67e commit 107c3cd

File tree

9 files changed

+519
-222
lines changed

9 files changed

+519
-222
lines changed

crates/op-rbuilder/src/builders/builder_tx.rs

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ use alloy_primitives::{
66
};
77
use core::fmt::Debug;
88
use op_alloy_consensus::OpTypedTransaction;
9+
use op_revm::OpTransactionError;
910
use reth_evm::{eth::receipt_builder::ReceiptBuilderCtx, ConfigureEvm, Evm};
1011
use reth_node_api::PayloadBuilderError;
1112
use reth_optimism_primitives::OpTransactionSigned;
1213
use reth_primitives::Recovered;
13-
use reth_provider::ProviderError;
14+
use reth_provider::{ProviderError, StateProvider};
1415
use reth_revm::State;
15-
use revm::{context::result::ResultAndState, Database, DatabaseCommit};
16+
use revm::{
17+
context::result::{EVMError, ResultAndState},
18+
Database, DatabaseCommit,
19+
};
1620
use tracing::{debug, warn};
1721

1822
use crate::{
@@ -48,6 +52,12 @@ impl From<secp256k1::Error> for BuilderTransactionError {
4852
}
4953
}
5054

55+
impl From<EVMError<ProviderError, OpTransactionError>> for BuilderTransactionError {
56+
fn from(error: EVMError<ProviderError, OpTransactionError>) -> Self {
57+
BuilderTransactionError::EvmExecutionError(Box::new(error))
58+
}
59+
}
60+
5161
impl From<BuilderTransactionError> for PayloadBuilderError {
5262
fn from(error: BuilderTransactionError) -> Self {
5363
match error {
@@ -60,30 +70,30 @@ impl From<BuilderTransactionError> for PayloadBuilderError {
6070
}
6171

6272
pub trait BuilderTransactions: Debug {
63-
fn simulate_builder_txs<DB, Extra: Debug + Default>(
73+
fn simulate_builder_txs<Extra: Debug + Default>(
6474
&self,
75+
state_provider: impl StateProvider,
6576
info: &mut ExecutionInfo<Extra>,
6677
ctx: &OpPayloadBuilderCtx,
67-
db: &mut State<DB>,
68-
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>
69-
where
70-
DB: Database<Error = ProviderError>;
78+
db: &mut State<impl Database<Error = ProviderError>>,
79+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>;
7180

72-
fn add_builder_txs<DB, Extra: Debug + Default>(
81+
fn add_builder_txs<Extra: Debug + Default>(
7382
&self,
83+
state_provider: impl StateProvider,
7484
info: &mut ExecutionInfo<Extra>,
7585
builder_ctx: &OpPayloadBuilderCtx,
76-
db: &mut State<DB>,
77-
) -> Result<(), BuilderTransactionError>
78-
where
79-
DB: Database<Error = ProviderError>,
80-
{
86+
db: &mut State<impl Database<Error = ProviderError>>,
87+
) -> Result<(), BuilderTransactionError> {
8188
{
8289
let mut evm = builder_ctx
8390
.evm_config
8491
.evm_with_env(&mut *db, builder_ctx.evm_env.clone());
92+
8593
let mut invalid: HashSet<Address> = HashSet::new();
86-
let builder_txs = self.simulate_builder_txs(info, builder_ctx, evm.db_mut())?;
94+
// simulate builder txs on the top of block state
95+
let builder_txs =
96+
self.simulate_builder_txs(state_provider, info, builder_ctx, evm.db_mut())?;
8797
for builder_tx in builder_txs {
8898
if invalid.contains(&builder_tx.signed_tx.signer()) {
8999
debug!(target: "payload_builder", tx_hash = ?builder_tx.signed_tx.tx_hash(), "builder signer invalid as previous builder tx reverted");
@@ -143,6 +153,29 @@ impl StandardBuilderTx {
143153
Self { signer }
144154
}
145155

156+
pub fn simulate_builder_txs(
157+
&self,
158+
ctx: &OpPayloadBuilderCtx,
159+
db: &mut State<impl Database<Error = ProviderError>>,
160+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> {
161+
match self.signer {
162+
Some(signer) => {
163+
let message: Vec<u8> = format!("Block Number: {}", ctx.block_number()).into_bytes();
164+
let gas_used = self.estimate_builder_tx_gas(&message);
165+
let signed_tx = self.signed_builder_tx(ctx, db, signer, gas_used, message)?;
166+
let da_size = op_alloy_flz::tx_estimated_size_fjord_bytes(
167+
signed_tx.encoded_2718().as_slice(),
168+
);
169+
Ok(vec![BuilderTransactionCtx {
170+
gas_used,
171+
da_size,
172+
signed_tx,
173+
}])
174+
}
175+
None => Ok(vec![]),
176+
}
177+
}
178+
146179
fn estimate_builder_tx_gas(&self, input: &[u8]) -> u64 {
147180
// Count zero and non-zero bytes
148181
let (zero_bytes, nonzero_bytes) = input.iter().fold((0, 0), |(zeros, nonzeros), &byte| {
@@ -202,30 +235,13 @@ impl StandardBuilderTx {
202235
}
203236

204237
impl BuilderTransactions for StandardBuilderTx {
205-
fn simulate_builder_txs<DB, Extra: Debug + Default>(
238+
fn simulate_builder_txs<Extra: Debug + Default>(
206239
&self,
240+
_state_provider: impl StateProvider,
207241
_info: &mut ExecutionInfo<Extra>,
208242
ctx: &OpPayloadBuilderCtx,
209-
db: &mut State<DB>,
210-
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>
211-
where
212-
DB: Database<Error = ProviderError>,
213-
{
214-
match self.signer {
215-
Some(signer) => {
216-
let message: Vec<u8> = format!("Block Number: {}", ctx.block_number()).into_bytes();
217-
let gas_used = self.estimate_builder_tx_gas(&message);
218-
let signed_tx = self.signed_builder_tx(ctx, db, signer, gas_used, message)?;
219-
let da_size = op_alloy_flz::tx_estimated_size_fjord_bytes(
220-
signed_tx.encoded_2718().as_slice(),
221-
);
222-
Ok(vec![BuilderTransactionCtx {
223-
gas_used,
224-
da_size,
225-
signed_tx,
226-
}])
227-
}
228-
None => Ok(vec![]),
229-
}
243+
db: &mut State<impl Database<Error = ProviderError>>,
244+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> {
245+
self.simulate_builder_txs(ctx, db)
230246
}
231247
}

crates/op-rbuilder/src/builders/flashblocks/payload.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -249,13 +249,14 @@ where
249249

250250
// If we have payload with txpool we add first builder tx right after deposits
251251
if !ctx.attributes().no_tx_pool {
252-
self.builder_tx.add_builder_txs(&mut info, &ctx, &mut db)?;
252+
self.builder_tx
253+
.add_builder_txs(&state_provider, &mut info, &ctx, &mut db)?;
253254
}
254255

255256
// We subtract gas limit and da limit for builder transaction from the whole limit
256257
let builder_txs = self
257258
.builder_tx
258-
.simulate_builder_txs(&mut info, &ctx, &mut db)
259+
.simulate_builder_txs(&state_provider, &mut info, &ctx, &mut db)
259260
.map_err(|err| PayloadBuilderError::Other(Box::new(err)))?;
260261
let builder_tx_gas = builder_txs.iter().fold(0, |acc, tx| acc + tx.gas_used);
261262
let builder_tx_da_size: u64 = builder_txs.iter().fold(0, |acc, tx| acc + tx.da_size);
@@ -454,7 +455,7 @@ where
454455
let mut db = State::builder()
455456
.with_database(state)
456457
.with_bundle_update()
457-
.with_bundle_prestate(bundle_state)
458+
.with_bundle_prestate(bundle_state.clone())
458459
.build();
459460

460461
let best_txs_start_time = Instant::now();
@@ -495,7 +496,7 @@ where
495496
// If it is the last flashblocks, add the builder txn to the block if enabled
496497
invoke_on_last_flashblock(flashblock_count, last_flashblock, || {
497498
self.builder_tx
498-
.add_builder_txs(&mut info, &ctx, &mut db)
499+
.add_builder_txs(&state_provider, &mut info, &ctx, &mut db)
499500
.map_err(|err| PayloadBuilderError::Other(Box::new(err)))
500501
})?;
501502

crates/op-rbuilder/src/builders/flashblocks/service.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,7 @@ where
9191
}
9292
};
9393

94-
if self.0.flashtestations_config.enable_block_proofs {
95-
return self.spawn_payload_builder_service(ctx, pool, flashtestations_builder_tx);
96-
}
94+
return self.spawn_payload_builder_service(ctx, pool, flashtestations_builder_tx);
9795
}
9896
self.spawn_payload_builder_service(ctx, pool, StandardBuilderTx { signer })
9997
}

crates/op-rbuilder/src/builders/standard/payload.rs

Lines changed: 37 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::super::context::OpPayloadBuilderCtx;
12
use crate::{
23
builders::{generator::BuildArguments, BuilderConfig, BuilderTransactions},
34
metrics::OpRBuilderMetrics,
@@ -21,10 +22,7 @@ use reth_optimism_node::{OpBuiltPayload, OpPayloadBuilderAttributes};
2122
use reth_optimism_primitives::{OpPrimitives, OpTransactionSigned};
2223
use reth_payload_util::{BestPayloadTransactions, NoopPayloadTransactions, PayloadTransactions};
2324
use reth_primitives::RecoveredBlock;
24-
use reth_provider::{
25-
ExecutionOutcome, HashedPostStateProvider, ProviderError, StateRootProvider,
26-
StorageRootProvider,
27-
};
25+
use reth_provider::{ExecutionOutcome, ProviderError, StateProvider};
2826
use reth_revm::{
2927
database::StateProviderDatabase, db::states::bundle_state::BundleRetention, State,
3028
};
@@ -34,8 +32,6 @@ use std::{sync::Arc, time::Instant};
3432
use tokio_util::sync::CancellationToken;
3533
use tracing::{error, info, warn};
3634

37-
use super::super::context::OpPayloadBuilderCtx;
38-
3935
/// Optimism's payload builder
4036
#[derive(Debug, Clone)]
4137
pub struct StandardOpPayloadBuilder<Pool, Client, BuilderTx, Txs = ()> {
@@ -233,22 +229,18 @@ where
233229
let builder = OpBuilder::new(best);
234230

235231
let state_provider = self.client.state_by_block_hash(ctx.parent().hash())?;
236-
let state = StateProviderDatabase::new(state_provider);
232+
let state = StateProviderDatabase::new(&state_provider);
237233
let metrics = ctx.metrics.clone();
238-
239234
if ctx.attributes().no_tx_pool {
240-
let db = State::builder()
241-
.with_database(state)
242-
.with_bundle_update()
243-
.build();
244-
builder.build(db, ctx, self.builder_tx.clone())
235+
builder.build(state, &state_provider, ctx, self.builder_tx.clone())
245236
} else {
246237
// sequencer mode we can reuse cachedreads from previous runs
247-
let db = State::builder()
248-
.with_database(cached_reads.as_db_mut(state))
249-
.with_bundle_update()
250-
.build();
251-
builder.build(db, ctx, self.builder_tx.clone())
238+
builder.build(
239+
cached_reads.as_db_mut(state),
240+
&state_provider,
241+
ctx,
242+
self.builder_tx.clone(),
243+
)
252244
}
253245
.map(|out| {
254246
metrics
@@ -298,30 +290,29 @@ pub struct ExecutedPayload {
298290

299291
impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
300292
/// Executes the payload and returns the outcome.
301-
pub fn execute<DB, P, BuilderTx>(
293+
pub fn execute<BuilderTx>(
302294
self,
303-
state: &mut State<DB>,
295+
state_provider: impl StateProvider,
296+
db: &mut State<impl Database<Error = ProviderError>>,
304297
ctx: &OpPayloadBuilderCtx,
305298
builder_tx: BuilderTx,
306299
) -> Result<BuildOutcomeKind<ExecutedPayload>, PayloadBuilderError>
307300
where
308-
DB: Database<Error = ProviderError> + AsRef<P>,
309-
P: StorageRootProvider,
310301
BuilderTx: BuilderTransactions,
311302
{
312303
let Self { best } = self;
313304
info!(target: "payload_builder", id=%ctx.payload_id(), parent_header = ?ctx.parent().hash(), parent_number = ctx.parent().number, "building new payload");
314305

315306
// 1. apply pre-execution changes
316307
ctx.evm_config
317-
.builder_for_next_block(state, ctx.parent(), ctx.block_env_attributes.clone())
308+
.builder_for_next_block(db, ctx.parent(), ctx.block_env_attributes.clone())
318309
.map_err(PayloadBuilderError::other)?
319310
.apply_pre_execution_changes()?;
320311

321312
let sequencer_tx_start_time = Instant::now();
322313

323314
// 3. execute sequencer transactions
324-
let mut info = ctx.execute_sequencer_transactions(state)?;
315+
let mut info = ctx.execute_sequencer_transactions(db)?;
325316

326317
ctx.metrics
327318
.sequencer_tx_duration
@@ -330,7 +321,7 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
330321
// 4. if mem pool transactions are requested we execute them
331322

332323
// gas reserved for builder tx
333-
let builder_txs = builder_tx.simulate_builder_txs(&mut info, ctx, state)?;
324+
let builder_txs = builder_tx.simulate_builder_txs(&state_provider, &mut info, ctx, db)?;
334325
let builder_tx_gas = builder_txs.iter().fold(0, |acc, tx| acc + tx.gas_used);
335326
let block_gas_limit = ctx.block_gas_limit().saturating_sub(builder_tx_gas);
336327
if block_gas_limit == 0 {
@@ -358,7 +349,7 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
358349
if ctx
359350
.execute_best_transactions(
360351
&mut info,
361-
state,
352+
db,
362353
best_txs,
363354
block_gas_limit,
364355
block_da_limit,
@@ -370,13 +361,13 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
370361
}
371362

372363
// Add builder tx to the block
373-
builder_tx.add_builder_txs(&mut info, ctx, state)?;
364+
builder_tx.add_builder_txs(&state_provider, &mut info, ctx, db)?;
374365

375366
let state_merge_start_time = Instant::now();
376367

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

381372
ctx.metrics
382373
.state_transition_merge_duration
@@ -392,26 +383,32 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
392383
}
393384

394385
/// Builds the payload on top of the state.
395-
pub fn build<DB, P, BuilderTx>(
386+
pub fn build<BuilderTx>(
396387
self,
397-
mut state: State<DB>,
388+
state: impl Database<Error = ProviderError>,
389+
state_provider: impl StateProvider,
398390
ctx: OpPayloadBuilderCtx,
399391
builder_tx: BuilderTx,
400392
) -> Result<BuildOutcomeKind<OpBuiltPayload>, PayloadBuilderError>
401393
where
402-
DB: Database<Error = ProviderError> + AsRef<P>,
403-
P: StateRootProvider + HashedPostStateProvider + StorageRootProvider,
404394
BuilderTx: BuilderTransactions,
405395
{
406-
let ExecutedPayload { info } = match self.execute(&mut state, &ctx, builder_tx)? {
407-
BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload,
408-
BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled),
409-
BuildOutcomeKind::Aborted { fees } => return Ok(BuildOutcomeKind::Aborted { fees }),
410-
};
396+
let mut db = State::builder()
397+
.with_database(state)
398+
.with_bundle_update()
399+
.build();
400+
let ExecutedPayload { info } =
401+
match self.execute(&state_provider, &mut db, &ctx, builder_tx)? {
402+
BuildOutcomeKind::Better { payload } | BuildOutcomeKind::Freeze(payload) => payload,
403+
BuildOutcomeKind::Cancelled => return Ok(BuildOutcomeKind::Cancelled),
404+
BuildOutcomeKind::Aborted { fees } => {
405+
return Ok(BuildOutcomeKind::Aborted { fees })
406+
}
407+
};
411408

412409
let block_number = ctx.block_number();
413410
let execution_outcome = ExecutionOutcome::new(
414-
state.take_bundle(),
411+
db.take_bundle(),
415412
vec![info.receipts],
416413
block_number,
417414
Vec::new(),
@@ -432,12 +429,9 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
432429
// calculate the state root
433430
let state_root_start_time = Instant::now();
434431

435-
let state_provider = state.database.as_ref();
436432
let hashed_state = state_provider.hashed_post_state(execution_outcome.state());
437433
let (state_root, trie_output) = {
438-
state
439-
.database
440-
.as_ref()
434+
state_provider
441435
.state_root_with_updates(hashed_state.clone())
442436
.inspect_err(|err| {
443437
warn!(target: "payload_builder",
@@ -457,7 +451,7 @@ impl<Txs: PayloadTxsBounds> OpBuilder<'_, Txs> {
457451
// `l2tol1-message-passer`
458452
(
459453
Some(
460-
isthmus::withdrawals_root(execution_outcome.state(), state.database.as_ref())
454+
isthmus::withdrawals_root(execution_outcome.state(), state_provider)
461455
.map_err(PayloadBuilderError::other)?,
462456
),
463457
Some(EMPTY_REQUESTS_HASH),

0 commit comments

Comments
 (0)