Skip to content

Commit 6b1752f

Browse files
avaloncheSozinM
andauthored
Refactor payload builder to accept generic builder tx (#217)
* Refactor payload builder to accept generic builder tx * Update crates/op-rbuilder/src/builders/builder_tx.rs Co-authored-by: Solar Mithril <[email protected]> * Update crates/op-rbuilder/src/builders/builder_tx.rs Co-authored-by: Solar Mithril <[email protected]> * Update crates/op-rbuilder/src/builders/flashblocks/service.rs Co-authored-by: Solar Mithril <[email protected]> * Update crates/op-rbuilder/src/builders/builder_tx.rs Co-authored-by: Solar Mithril <[email protected]> * fix lint * fix tests --------- Co-authored-by: Solar Mithril <[email protected]>
1 parent 192d8b4 commit 6b1752f

File tree

17 files changed

+767
-413
lines changed

17 files changed

+767
-413
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/op-rbuilder/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ alloy-primitives.workspace = true
6262
alloy-consensus.workspace = true
6363
alloy-contract.workspace = true
6464
alloy-eips.workspace = true
65+
alloy-evm.workspace = true
6566
alloy-rpc-types-beacon.workspace = true
6667
alloy-rpc-types-engine.workspace = true
6768
alloy-transport-http.workspace = true

crates/op-rbuilder/build.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,10 @@ fn main() -> Result<(), Box<dyn Error>> {
5959

6060
let author_name = env::var("VERGEN_GIT_COMMIT_AUTHOR_NAME")?;
6161
let author_email = env::var("VERGEN_GIT_COMMIT_AUTHOR_EMAIL")?;
62-
let author_full = format!("{} <{}>", author_name, author_email);
62+
let author_full = format!("{author_name} <{author_email}>");
6363

6464
// Set author full name
65-
println!("cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR={}", author_full);
65+
println!("cargo:rustc-env=VERGEN_GIT_COMMIT_AUTHOR={author_full}");
6666

6767
let is_dirty = env::var("VERGEN_GIT_DIRTY")? == "true";
6868
// > git describe --always --tags
Lines changed: 155 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,169 @@
1+
use alloy_evm::Database;
2+
use alloy_primitives::{
3+
Address,
4+
map::foldhash::{HashSet, HashSetExt},
5+
};
6+
use core::fmt::Debug;
7+
use op_revm::OpTransactionError;
8+
use reth_evm::{ConfigureEvm, Evm, eth::receipt_builder::ReceiptBuilderCtx};
9+
use reth_node_api::PayloadBuilderError;
110
use reth_optimism_primitives::OpTransactionSigned;
211
use reth_primitives::Recovered;
12+
use reth_provider::{ProviderError, StateProvider};
13+
use reth_revm::{
14+
State, database::StateProviderDatabase, db::states::bundle_state::BundleRetention,
15+
};
16+
use revm::{
17+
DatabaseCommit,
18+
context::result::{EVMError, ResultAndState},
19+
};
20+
use tracing::warn;
321

4-
use crate::tx_signer::Signer;
22+
use crate::{builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo};
523

6-
pub trait BuilderTx {
7-
fn estimated_builder_tx_gas(&self) -> u64;
8-
fn estimated_builder_tx_da_size(&self) -> Option<u64>;
9-
fn signed_builder_tx(&self) -> Result<Recovered<OpTransactionSigned>, secp256k1::Error>;
24+
#[derive(Debug, Clone)]
25+
pub struct BuilderTransactionCtx {
26+
pub gas_used: u64,
27+
pub da_size: u64,
28+
pub signed_tx: Recovered<OpTransactionSigned>,
1029
}
1130

12-
// Scaffolding for how to construct the end of block builder transaction
13-
// This will be the regular end of block transaction without the TEE key
14-
#[derive(Clone)]
15-
pub(super) struct StandardBuilderTx {
16-
#[allow(dead_code)]
17-
pub signer: Option<Signer>,
31+
/// Possible error variants during construction of builder txs.
32+
#[derive(Debug, thiserror::Error)]
33+
pub enum BuilderTransactionError {
34+
/// Builder account load fails to get builder nonce
35+
#[error("failed to load account {0}")]
36+
AccountLoadFailed(Address),
37+
/// Signature signing fails
38+
#[error("failed to sign transaction: {0}")]
39+
SigningError(secp256k1::Error),
40+
/// Unrecoverable error during evm execution.
41+
#[error("evm execution error {0}")]
42+
EvmExecutionError(Box<dyn core::error::Error + Send + Sync>),
43+
/// Any other builder transaction errors.
44+
#[error(transparent)]
45+
Other(Box<dyn core::error::Error + Send + Sync>),
1846
}
1947

20-
impl BuilderTx for StandardBuilderTx {
21-
fn estimated_builder_tx_gas(&self) -> u64 {
22-
todo!()
48+
impl From<secp256k1::Error> for BuilderTransactionError {
49+
fn from(error: secp256k1::Error) -> Self {
50+
BuilderTransactionError::SigningError(error)
2351
}
52+
}
53+
54+
impl From<EVMError<ProviderError, OpTransactionError>> for BuilderTransactionError {
55+
fn from(error: EVMError<ProviderError, OpTransactionError>) -> Self {
56+
BuilderTransactionError::EvmExecutionError(Box::new(error))
57+
}
58+
}
2459

25-
fn estimated_builder_tx_da_size(&self) -> Option<u64> {
26-
todo!()
60+
impl From<BuilderTransactionError> for PayloadBuilderError {
61+
fn from(error: BuilderTransactionError) -> Self {
62+
match error {
63+
BuilderTransactionError::EvmExecutionError(e) => {
64+
PayloadBuilderError::EvmExecutionError(e)
65+
}
66+
_ => PayloadBuilderError::Other(Box::new(error)),
67+
}
2768
}
69+
}
70+
71+
pub trait BuilderTransactions<ExtraCtx: Debug + Default = ()>: Debug {
72+
fn simulate_builder_txs<Extra: Debug + Default>(
73+
&self,
74+
state_provider: impl StateProvider + Clone,
75+
info: &mut ExecutionInfo<Extra>,
76+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
77+
db: &mut State<impl Database>,
78+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>;
79+
80+
fn add_builder_txs<Extra: Debug + Default>(
81+
&self,
82+
state_provider: impl StateProvider + Clone,
83+
info: &mut ExecutionInfo<Extra>,
84+
builder_ctx: &OpPayloadBuilderCtx<ExtraCtx>,
85+
db: &mut State<impl Database>,
86+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> {
87+
{
88+
let mut evm = builder_ctx
89+
.evm_config
90+
.evm_with_env(&mut *db, builder_ctx.evm_env.clone());
91+
92+
let mut invalid: HashSet<Address> = HashSet::new();
93+
94+
let builder_txs =
95+
self.simulate_builder_txs(state_provider, info, builder_ctx, evm.db_mut())?;
96+
for builder_tx in builder_txs.iter() {
97+
if invalid.contains(&builder_tx.signed_tx.signer()) {
98+
warn!(target: "payload_builder", tx_hash = ?builder_tx.signed_tx.tx_hash(), "builder signer invalid as previous builder tx reverted");
99+
continue;
100+
}
101+
102+
let ResultAndState { result, state } = evm
103+
.transact(&builder_tx.signed_tx)
104+
.map_err(|err| BuilderTransactionError::EvmExecutionError(Box::new(err)))?;
105+
106+
if !result.is_success() {
107+
warn!(target: "payload_builder", tx_hash = ?builder_tx.signed_tx.tx_hash(), "builder tx reverted");
108+
invalid.insert(builder_tx.signed_tx.signer());
109+
continue;
110+
}
111+
112+
// Add gas used by the transaction to cumulative gas used, before creating the receipt
113+
let gas_used = result.gas_used();
114+
info.cumulative_gas_used += gas_used;
115+
116+
let ctx = ReceiptBuilderCtx {
117+
tx: builder_tx.signed_tx.inner(),
118+
evm: &evm,
119+
result,
120+
state: &state,
121+
cumulative_gas_used: info.cumulative_gas_used,
122+
};
123+
info.receipts.push(builder_ctx.build_receipt(ctx, None));
124+
125+
// Commit changes
126+
evm.db_mut().commit(state);
127+
128+
// Append sender and transaction to the respective lists
129+
info.executed_senders.push(builder_tx.signed_tx.signer());
130+
info.executed_transactions
131+
.push(builder_tx.signed_tx.clone().into_inner());
132+
}
133+
134+
// Release the db reference by dropping evm
135+
drop(evm);
136+
137+
Ok(builder_txs)
138+
}
139+
}
140+
141+
fn simulate_builder_txs_state<Extra: Debug + Default>(
142+
&self,
143+
state_provider: impl StateProvider + Clone,
144+
builder_txs: Vec<&BuilderTransactionCtx>,
145+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
146+
db: &mut State<impl Database>,
147+
) -> Result<State<StateProviderDatabase<impl StateProvider>>, BuilderTransactionError> {
148+
let state = StateProviderDatabase::new(state_provider.clone());
149+
let mut simulation_state = State::builder()
150+
.with_database(state)
151+
.with_bundle_prestate(db.bundle_state.clone())
152+
.with_bundle_update()
153+
.build();
154+
let mut evm = ctx
155+
.evm_config
156+
.evm_with_env(&mut simulation_state, ctx.evm_env.clone());
157+
158+
for builder_tx in builder_txs {
159+
let ResultAndState { state, .. } = evm
160+
.transact(&builder_tx.signed_tx)
161+
.map_err(|err| BuilderTransactionError::EvmExecutionError(Box::new(err)))?;
162+
163+
evm.db_mut().commit(state);
164+
evm.db_mut().merge_transitions(BundleRetention::Reverts);
165+
}
28166

29-
fn signed_builder_tx(&self) -> Result<Recovered<OpTransactionSigned>, secp256k1::Error> {
30-
todo!()
167+
Ok(simulation_state)
31168
}
32169
}

0 commit comments

Comments
 (0)