Skip to content

Commit 4c88ceb

Browse files
committed
move simumlation calls to builder tx
1 parent 902f09d commit 4c88ceb

File tree

5 files changed

+277
-205
lines changed

5 files changed

+277
-205
lines changed

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

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,42 @@
1-
use alloy_consensus::TxEip1559;
1+
use alloy_consensus::{Transaction, TxEip1559};
22
use alloy_eips::{Encodable2718, eip7623::TOTAL_COST_FLOOR_PER_TOKEN};
33
use alloy_evm::Database;
4+
use alloy_op_evm::OpEvm;
45
use alloy_primitives::{
5-
Address, B256, Log, TxKind, U256,
6-
map::foldhash::{HashSet, HashSetExt},
6+
Address, B256, Bytes, TxKind, U256,
7+
map::foldhash::{HashMap, HashSet, HashSetExt},
78
};
89
use core::fmt::Debug;
910
use op_alloy_consensus::OpTypedTransaction;
10-
use op_revm::OpTransactionError;
11-
use reth_evm::{ConfigureEvm, Evm, eth::receipt_builder::ReceiptBuilderCtx};
11+
use op_revm::{OpHaltReason, OpTransactionError};
12+
use reth_evm::{
13+
ConfigureEvm, Evm, EvmError, eth::receipt_builder::ReceiptBuilderCtx,
14+
precompiles::PrecompilesMap,
15+
};
1216
use reth_node_api::PayloadBuilderError;
1317
use reth_optimism_primitives::OpTransactionSigned;
1418
use reth_primitives::Recovered;
1519
use reth_provider::{ProviderError, StateProvider};
1620
use reth_revm::{State, database::StateProviderDatabase};
1721
use revm::{
1822
DatabaseCommit,
19-
context::result::{EVMError, ResultAndState},
23+
context::result::{EVMError, ExecutionResult, ResultAndState},
24+
inspector::NoOpInspector,
25+
state::Account,
2026
};
2127
use tracing::warn;
2228

2329
use crate::{
2430
builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo, tx_signer::Signer,
2531
};
2632

33+
#[derive(Debug, Default)]
34+
pub struct SimulationSuccessResult {
35+
pub gas_used: u64,
36+
pub output: Bytes,
37+
pub state_changes: HashMap<Address, Account>,
38+
}
39+
2740
#[derive(Debug, Clone)]
2841
pub struct BuilderTransactionCtx {
2942
pub gas_used: u64,
@@ -46,6 +59,14 @@ impl BuilderTransactionCtx {
4659
}
4760
}
4861

62+
#[derive(Debug, thiserror::Error)]
63+
pub enum InvalidContractDataError {
64+
#[error("did not find expected log {0:?} in emitted logs")]
65+
InvalidLogs(B256),
66+
#[error("could not decode output from contract call")]
67+
OutputAbiDecodeError,
68+
}
69+
4970
/// Possible error variants during construction of builder txs.
5071
#[derive(Debug, thiserror::Error)]
5172
pub enum BuilderTransactionError {
@@ -55,9 +76,15 @@ pub enum BuilderTransactionError {
5576
/// Signature signing fails
5677
#[error("failed to sign transaction: {0}")]
5778
SigningError(secp256k1::Error),
58-
/// Invalid contract data returned
59-
#[error("invalid contract data returned {0}")]
60-
InvalidContract(Address),
79+
/// Invalid contract errors indicating the contract is incorrect
80+
#[error("contract {0} may be incorrect, invalid contract data: {1}")]
81+
InvalidContract(Address, InvalidContractDataError),
82+
/// Transaction halted execution
83+
#[error("transaction halted {0:?}")]
84+
TransactionHalted(OpHaltReason),
85+
/// Transaction reverted
86+
#[error("transaction reverted {0}")]
87+
TransactionReverted(Box<dyn core::error::Error + Send + Sync>),
6188
/// Invalid tx errors during evm execution.
6289
#[error("invalid transaction error {0}")]
6390
InvalidTransactionError(Box<dyn core::error::Error + Send + Sync>),
@@ -93,12 +120,13 @@ impl From<BuilderTransactionError> for PayloadBuilderError {
93120
}
94121

95122
impl BuilderTransactionError {
96-
pub fn other<E>(error: E) -> Self
97-
where
98-
E: core::error::Error + Send + Sync + 'static,
99-
{
123+
pub fn other(error: impl core::error::Error + Send + Sync + 'static) -> Self {
100124
BuilderTransactionError::Other(Box::new(error))
101125
}
126+
127+
pub fn msg(msg: impl core::fmt::Display) -> Self {
128+
Self::Other(msg.to_string().into())
129+
}
102130
}
103131

104132
pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Default = ()> {
@@ -210,6 +238,84 @@ pub trait BuilderTransactions<ExtraCtx: Debug + Default = (), Extra: Debug + Def
210238

211239
Ok(simulation_state)
212240
}
241+
242+
fn sign_tx(
243+
&self,
244+
to: Address,
245+
from: Signer,
246+
gas_used: Option<u64>,
247+
calldata: Bytes,
248+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
249+
db: &mut State<impl Database>,
250+
) -> Result<Recovered<OpTransactionSigned>, BuilderTransactionError> {
251+
let nonce = get_nonce(db, from.address)?;
252+
// Create the EIP-1559 transaction
253+
let tx = OpTypedTransaction::Eip1559(TxEip1559 {
254+
chain_id: ctx.chain_id(),
255+
nonce,
256+
// Due to EIP-150, 63/64 of available gas is forwarded to external calls so need to add a buffer
257+
gas_limit: gas_used
258+
.map(|gas| gas * 64 / 63)
259+
.unwrap_or(ctx.block_gas_limit()),
260+
max_fee_per_gas: ctx.base_fee().into(),
261+
to: TxKind::Call(to),
262+
input: calldata,
263+
..Default::default()
264+
});
265+
Ok(from.sign_tx(tx)?)
266+
}
267+
268+
fn simulate_call(
269+
&self,
270+
signed_tx: Recovered<OpTransactionSigned>,
271+
expected_topic: Option<B256>,
272+
revert_handler: impl FnOnce(Bytes) -> BuilderTransactionError,
273+
evm: &mut OpEvm<
274+
&mut State<StateProviderDatabase<impl StateProvider>>,
275+
NoOpInspector,
276+
PrecompilesMap,
277+
>,
278+
) -> Result<SimulationSuccessResult, BuilderTransactionError> {
279+
let ResultAndState { result, state } = match evm.transact(&signed_tx) {
280+
Ok(res) => res,
281+
Err(err) => {
282+
if err.is_invalid_tx_err() {
283+
return Err(BuilderTransactionError::InvalidTransactionError(Box::new(
284+
err,
285+
)));
286+
} else {
287+
return Err(BuilderTransactionError::EvmExecutionError(Box::new(err)));
288+
}
289+
}
290+
};
291+
292+
match result {
293+
ExecutionResult::Success {
294+
logs,
295+
gas_used,
296+
output,
297+
..
298+
} => {
299+
if let Some(topic) = expected_topic
300+
&& !logs.iter().any(|log| log.topics().first() == Some(&topic))
301+
{
302+
return Err(BuilderTransactionError::InvalidContract(
303+
signed_tx.to().unwrap_or_default(),
304+
InvalidContractDataError::InvalidLogs(topic),
305+
));
306+
}
307+
Ok(SimulationSuccessResult {
308+
gas_used,
309+
output: output.into_data(),
310+
state_changes: state,
311+
})
312+
}
313+
ExecutionResult::Revert { output, .. } => Err(revert_handler(output)),
314+
ExecutionResult::Halt { reason, .. } => Err(BuilderTransactionError::other(
315+
BuilderTransactionError::TransactionHalted(reason),
316+
)),
317+
}
318+
}
213319
}
214320

215321
#[derive(Debug, Clone)]
@@ -322,7 +428,3 @@ pub fn get_balance(
322428
.map(|acc| acc.account_info().unwrap_or_default().balance)
323429
.map_err(|_| BuilderTransactionError::AccountLoadFailed(address))
324430
}
325-
326-
pub fn log_exists(logs: &[Log], topic: &B256) -> bool {
327-
logs.iter().any(|log| log.topics().first() == Some(topic))
328-
}

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

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use alloy_consensus::TxEip1559;
22
use alloy_eips::Encodable2718;
33
use alloy_evm::{Database, Evm};
44
use alloy_op_evm::OpEvm;
5-
use alloy_primitives::{Address, B256, TxKind};
5+
use alloy_primitives::{Address, TxKind};
66
use alloy_sol_types::{Error, SolCall, SolEvent, SolInterface, sol};
77
use core::fmt::Debug;
88
use op_alloy_consensus::OpTypedTransaction;
@@ -21,7 +21,7 @@ use tracing::warn;
2121
use crate::{
2222
builders::{
2323
BuilderTransactionCtx, BuilderTransactionError, BuilderTransactions,
24-
builder_tx::{BuilderTxBase, get_nonce, log_exists},
24+
builder_tx::{BuilderTxBase, get_nonce},
2525
context::OpPayloadBuilderCtx,
2626
flashblocks::payload::{FlashblocksExecutionInfo, FlashblocksExtraCtx},
2727
},
@@ -53,8 +53,6 @@ sol!(
5353
pub(super) enum FlashblockNumberError {
5454
#[error("flashblocks number contract tx reverted: {0:?}")]
5555
Revert(IFlashblockNumber::IFlashblockNumberErrors),
56-
#[error("contract may be invalid, mismatch in log emitted: expected {0:?}")]
57-
LogMismatch(B256),
5856
#[error("unknown revert: {0} err: {1}")]
5957
Unknown(String, Error),
6058
#[error("halt: {0:?}")]
@@ -184,14 +182,15 @@ impl FlashblocksNumberBuilderTx {
184182

185183
match result {
186184
ExecutionResult::Success { gas_used, logs, .. } => {
187-
if log_exists(
188-
&logs,
189-
&IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH,
190-
) {
185+
if logs.iter().any(|log| {
186+
log.topics().first()
187+
== Some(&IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH)
188+
}) {
191189
Ok(gas_used)
192190
} else {
193-
Err(BuilderTransactionError::other(
194-
FlashblockNumberError::LogMismatch(
191+
Err(BuilderTransactionError::InvalidContract(
192+
self.flashblock_number_address,
193+
crate::builders::InvalidContractDataError::InvalidLogs(
195194
IFlashblockNumber::FlashblockIncremented::SIGNATURE_HASH,
196195
),
197196
))
@@ -261,6 +260,7 @@ impl BuilderTransactions<FlashblocksExtraCtx, FlashblocksExecutionInfo>
261260
.evm_with_env(simulation_state, ctx.evm_env.clone());
262261
evm.modify_cfg(|cfg| {
263262
cfg.disable_balance_check = true;
263+
cfg.disable_block_gas_limit = true;
264264
});
265265

266266
let nonce = get_nonce(evm.db_mut(), signer.address)?;

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ mod generator;
2222
mod standard;
2323

2424
pub use builder_tx::{
25-
BuilderTransactionCtx, BuilderTransactionError, BuilderTransactions, get_balance, get_nonce,
26-
log_exists,
25+
BuilderTransactionCtx, BuilderTransactionError, BuilderTransactions, InvalidContractDataError,
26+
SimulationSuccessResult, get_balance, get_nonce,
2727
};
2828
pub use context::OpPayloadBuilderCtx;
2929
pub use flashblocks::FlashblocksBuilder;

0 commit comments

Comments
 (0)