Skip to content

Commit 470bf36

Browse files
committed
add flashblocks tx
1 parent f942c37 commit 470bf36

File tree

11 files changed

+399
-215
lines changed

11 files changed

+399
-215
lines changed
Lines changed: 33 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,27 @@
1-
use alloy_consensus::TxEip1559;
2-
use alloy_eips::{eip7623::TOTAL_COST_FLOOR_PER_TOKEN, Encodable2718};
31
use alloy_evm::Database;
42
use alloy_primitives::{
53
map::foldhash::{HashSet, HashSetExt},
6-
Address, TxKind,
4+
Address,
75
};
86
use core::fmt::Debug;
9-
use op_alloy_consensus::OpTypedTransaction;
107
use op_revm::OpTransactionError;
118
use reth_evm::{eth::receipt_builder::ReceiptBuilderCtx, ConfigureEvm, Evm};
129
use reth_node_api::PayloadBuilderError;
1310
use reth_optimism_primitives::OpTransactionSigned;
1411
use reth_primitives::Recovered;
1512
use reth_provider::{ProviderError, StateProvider};
16-
use reth_revm::State;
13+
use reth_revm::{
14+
database::StateProviderDatabase, db::states::bundle_state::BundleRetention, State,
15+
};
1716
use revm::{
1817
context::result::{EVMError, ResultAndState},
1918
DatabaseCommit,
2019
};
2120
use tracing::{debug, warn};
2221

23-
use crate::{
24-
builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo, tx_signer::Signer,
25-
};
22+
use crate::{builders::context::OpPayloadBuilderCtx, primitives::reth::ExecutionInfo};
2623

24+
#[derive(Debug, Clone)]
2725
pub struct BuilderTransactionCtx {
2826
pub gas_used: u64,
2927
pub da_size: u64,
@@ -70,20 +68,20 @@ impl From<BuilderTransactionError> for PayloadBuilderError {
7068
}
7169
}
7270

73-
pub trait BuilderTransactions: Debug {
71+
pub trait BuilderTransactions<ExtraCtx: Debug + Default = ()>: Debug {
7472
fn simulate_builder_txs<Extra: Debug + Default>(
7573
&self,
7674
state_provider: impl StateProvider + Clone,
7775
info: &mut ExecutionInfo<Extra>,
78-
ctx: &OpPayloadBuilderCtx,
76+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
7977
db: &mut State<impl Database>,
8078
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError>;
8179

8280
fn add_builder_txs<Extra: Debug + Default>(
8381
&self,
8482
state_provider: impl StateProvider + Clone,
8583
info: &mut ExecutionInfo<Extra>,
86-
builder_ctx: &OpPayloadBuilderCtx,
84+
builder_ctx: &OpPayloadBuilderCtx<ExtraCtx>,
8785
db: &mut State<impl Database>,
8886
) -> Result<(), BuilderTransactionError> {
8987
{
@@ -92,7 +90,7 @@ pub trait BuilderTransactions: Debug {
9290
.evm_with_env(&mut *db, builder_ctx.evm_env.clone());
9391

9492
let mut invalid: HashSet<Address> = HashSet::new();
95-
// simulate builder txs on the top of block state
93+
9694
let builder_txs =
9795
self.simulate_builder_txs(state_provider, info, builder_ctx, evm.db_mut())?;
9896
for builder_tx in builder_txs {
@@ -139,108 +137,33 @@ pub trait BuilderTransactions: Debug {
139137
Ok(())
140138
}
141139
}
142-
}
143-
144-
// Scaffolding for how to construct the end of block builder transaction
145-
// This will be the regular end of block transaction without the TEE key
146-
#[derive(Debug, Clone)]
147-
pub struct StandardBuilderTx {
148-
#[allow(dead_code)]
149-
pub signer: Option<Signer>,
150-
}
151-
152-
impl StandardBuilderTx {
153-
pub fn new(signer: Option<Signer>) -> Self {
154-
Self { signer }
155-
}
156140

157-
pub fn simulate_builder_tx(
141+
fn simulate_builder_txs_state<Extra: Debug + Default>(
158142
&self,
159-
ctx: &OpPayloadBuilderCtx,
143+
state_provider: impl StateProvider + Clone,
144+
builder_txs: Vec<&BuilderTransactionCtx>,
145+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
160146
db: &mut State<impl Database>,
161-
) -> Result<Option<BuilderTransactionCtx>, BuilderTransactionError> {
162-
match self.signer {
163-
Some(signer) => {
164-
let message: Vec<u8> = format!("Block Number: {}", ctx.block_number()).into_bytes();
165-
let gas_used = self.estimate_builder_tx_gas(&message);
166-
let signed_tx = self.signed_builder_tx(ctx, db, signer, gas_used, message)?;
167-
let da_size = op_alloy_flz::tx_estimated_size_fjord_bytes(
168-
signed_tx.encoded_2718().as_slice(),
169-
);
170-
Ok(Some(BuilderTransactionCtx {
171-
gas_used,
172-
da_size,
173-
signed_tx,
174-
}))
175-
}
176-
None => Ok(None),
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);
177165
}
178-
}
179166

180-
fn estimate_builder_tx_gas(&self, input: &[u8]) -> u64 {
181-
// Count zero and non-zero bytes
182-
let (zero_bytes, nonzero_bytes) = input.iter().fold((0, 0), |(zeros, nonzeros), &byte| {
183-
if byte == 0 {
184-
(zeros + 1, nonzeros)
185-
} else {
186-
(zeros, nonzeros + 1)
187-
}
188-
});
189-
190-
// Calculate gas cost (4 gas per zero byte, 16 gas per non-zero byte)
191-
let zero_cost = zero_bytes * 4;
192-
let nonzero_cost = nonzero_bytes * 16;
193-
194-
// Tx gas should be not less than floor gas https://eips.ethereum.org/EIPS/eip-7623
195-
let tokens_in_calldata = zero_bytes + nonzero_bytes * 4;
196-
let floor_gas = 21_000 + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN;
197-
198-
std::cmp::max(zero_cost + nonzero_cost + 21_000, floor_gas)
199-
}
200-
201-
fn signed_builder_tx(
202-
&self,
203-
ctx: &OpPayloadBuilderCtx,
204-
db: &mut State<impl Database>,
205-
signer: Signer,
206-
gas_used: u64,
207-
message: Vec<u8>,
208-
) -> Result<Recovered<OpTransactionSigned>, BuilderTransactionError> {
209-
let nonce = db
210-
.load_cache_account(signer.address)
211-
.map(|acc| acc.account_info().unwrap_or_default().nonce)
212-
.map_err(|_| BuilderTransactionError::AccountLoadFailed(signer.address))?;
213-
214-
// Create the EIP-1559 transaction
215-
let tx = OpTypedTransaction::Eip1559(TxEip1559 {
216-
chain_id: ctx.chain_id(),
217-
nonce,
218-
gas_limit: gas_used,
219-
max_fee_per_gas: ctx.base_fee().into(),
220-
max_priority_fee_per_gas: 0,
221-
to: TxKind::Call(Address::ZERO),
222-
// Include the message as part of the transaction data
223-
input: message.into(),
224-
..Default::default()
225-
});
226-
// Sign the transaction
227-
let builder_tx = signer
228-
.sign_tx(tx)
229-
.map_err(BuilderTransactionError::SigningError)?;
230-
231-
Ok(builder_tx)
232-
}
233-
}
234-
235-
impl BuilderTransactions for StandardBuilderTx {
236-
fn simulate_builder_txs<Extra: Debug + Default>(
237-
&self,
238-
_state_provider: impl StateProvider + Clone,
239-
_info: &mut ExecutionInfo<Extra>,
240-
ctx: &OpPayloadBuilderCtx,
241-
db: &mut State<impl Database>,
242-
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> {
243-
let builder_tx = self.simulate_builder_tx(ctx, db)?;
244-
Ok(builder_tx.into_iter().collect())
167+
Ok(simulation_state)
245168
}
246169
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
use alloy_consensus::TxEip1559;
2+
use alloy_eips::{eip7623::TOTAL_COST_FLOOR_PER_TOKEN, Encodable2718};
3+
use alloy_evm::Database;
4+
use alloy_primitives::{Address, TxKind};
5+
use core::fmt::Debug;
6+
use op_alloy_consensus::OpTypedTransaction;
7+
use reth_optimism_primitives::OpTransactionSigned;
8+
use reth_primitives::Recovered;
9+
use reth_provider::StateProvider;
10+
use reth_revm::State;
11+
12+
use crate::{
13+
builders::{
14+
context::OpPayloadBuilderCtx, flashblocks::payload::FlashblocksExtraCtx,
15+
BuilderTransactionCtx, BuilderTransactionError, BuilderTransactions,
16+
},
17+
flashtestations::builder_tx::FlashtestationsBuilderTx,
18+
primitives::reth::ExecutionInfo,
19+
tx_signer::Signer,
20+
};
21+
22+
// This will be the end of block transaction of a regular block
23+
#[derive(Debug, Clone)]
24+
pub struct FlashblocksBuilderTx {
25+
pub signer: Option<Signer>,
26+
pub flashtestations_builder_tx: Option<FlashtestationsBuilderTx>,
27+
}
28+
29+
impl FlashblocksBuilderTx {
30+
pub fn new(
31+
signer: Option<Signer>,
32+
flashtestations_builder_tx: Option<FlashtestationsBuilderTx>,
33+
) -> Self {
34+
Self {
35+
signer,
36+
flashtestations_builder_tx,
37+
}
38+
}
39+
40+
pub fn simulate_builder_tx<ExtraCtx: Debug + Default>(
41+
&self,
42+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
43+
db: &mut State<impl Database>,
44+
) -> Result<Option<BuilderTransactionCtx>, BuilderTransactionError> {
45+
match self.signer {
46+
Some(signer) => {
47+
let message: Vec<u8> = format!("Block Number: {}", ctx.block_number()).into_bytes();
48+
let gas_used = self.estimate_builder_tx_gas(&message);
49+
let signed_tx = self.signed_builder_tx(ctx, db, signer, gas_used, message)?;
50+
let da_size = op_alloy_flz::tx_estimated_size_fjord_bytes(
51+
signed_tx.encoded_2718().as_slice(),
52+
);
53+
Ok(Some(BuilderTransactionCtx {
54+
gas_used,
55+
da_size,
56+
signed_tx,
57+
}))
58+
}
59+
None => Ok(None),
60+
}
61+
}
62+
63+
fn estimate_builder_tx_gas(&self, input: &[u8]) -> u64 {
64+
// Count zero and non-zero bytes
65+
let (zero_bytes, nonzero_bytes) = input.iter().fold((0, 0), |(zeros, nonzeros), &byte| {
66+
if byte == 0 {
67+
(zeros + 1, nonzeros)
68+
} else {
69+
(zeros, nonzeros + 1)
70+
}
71+
});
72+
73+
// Calculate gas cost (4 gas per zero byte, 16 gas per non-zero byte)
74+
let zero_cost = zero_bytes * 4;
75+
let nonzero_cost = nonzero_bytes * 16;
76+
77+
// Tx gas should be not less than floor gas https://eips.ethereum.org/EIPS/eip-7623
78+
let tokens_in_calldata = zero_bytes + nonzero_bytes * 4;
79+
let floor_gas = 21_000 + tokens_in_calldata * TOTAL_COST_FLOOR_PER_TOKEN;
80+
81+
std::cmp::max(zero_cost + nonzero_cost + 21_000, floor_gas)
82+
}
83+
84+
fn signed_builder_tx<ExtraCtx: Debug + Default>(
85+
&self,
86+
ctx: &OpPayloadBuilderCtx<ExtraCtx>,
87+
db: &mut State<impl Database>,
88+
signer: Signer,
89+
gas_used: u64,
90+
message: Vec<u8>,
91+
) -> Result<Recovered<OpTransactionSigned>, BuilderTransactionError> {
92+
let nonce = db
93+
.load_cache_account(signer.address)
94+
.map(|acc| acc.account_info().unwrap_or_default().nonce)
95+
.map_err(|_| BuilderTransactionError::AccountLoadFailed(signer.address))?;
96+
97+
// Create the EIP-1559 transaction
98+
let tx = OpTypedTransaction::Eip1559(TxEip1559 {
99+
chain_id: ctx.chain_id(),
100+
nonce,
101+
gas_limit: gas_used,
102+
max_fee_per_gas: ctx.base_fee().into(),
103+
max_priority_fee_per_gas: 0,
104+
to: TxKind::Call(Address::ZERO),
105+
// Include the message as part of the transaction data
106+
input: message.into(),
107+
..Default::default()
108+
});
109+
// Sign the transaction
110+
let builder_tx = signer
111+
.sign_tx(tx)
112+
.map_err(BuilderTransactionError::SigningError)?;
113+
114+
Ok(builder_tx)
115+
}
116+
}
117+
118+
impl BuilderTransactions<FlashblocksExtraCtx> for FlashblocksBuilderTx {
119+
fn simulate_builder_txs<Extra: Debug + Default>(
120+
&self,
121+
state_provider: impl StateProvider + Clone,
122+
info: &mut ExecutionInfo<Extra>,
123+
ctx: &OpPayloadBuilderCtx<FlashblocksExtraCtx>,
124+
db: &mut State<impl Database>,
125+
) -> Result<Vec<BuilderTransactionCtx>, BuilderTransactionError> {
126+
let mut builder_txs = Vec::<BuilderTransactionCtx>::new();
127+
let flashblocks_builder_tx = self.simulate_builder_tx(ctx, db)?;
128+
builder_txs.extend(flashblocks_builder_tx.clone());
129+
if let Some(flashtestations_builder_tx) = &self.flashtestations_builder_tx {
130+
// We only include flashtestations txs in the last flashblock
131+
if ctx.flashblock_index() == ctx.target_flashblock_count() - 1 {
132+
let mut simulation_state = self.simulate_builder_txs_state::<FlashblocksExtraCtx>(
133+
state_provider.clone(),
134+
flashblocks_builder_tx.iter().collect(),
135+
ctx,
136+
db,
137+
)?;
138+
let flashtestations_builder_txs = flashtestations_builder_tx.simulate_builder_txs(
139+
state_provider,
140+
info,
141+
ctx,
142+
&mut simulation_state,
143+
)?;
144+
builder_txs.extend(flashtestations_builder_txs);
145+
}
146+
}
147+
Ok(builder_txs)
148+
}
149+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use crate::traits::{NodeBounds, PoolBounds};
33
use config::FlashblocksConfig;
44
use service::FlashblocksServiceBuilder;
55

6+
mod builder_tx;
67
mod config;
7-
//mod context;
88
mod payload;
99
mod service;
1010
mod wspub;

0 commit comments

Comments
 (0)