Skip to content

Commit 057ae57

Browse files
committed
adds bundle simulation
1 parent de81c94 commit 057ae57

File tree

3 files changed

+82
-12
lines changed

3 files changed

+82
-12
lines changed

bin/builder.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async fn main() -> eyre::Result<()> {
2929
let builder = BlockBuilder::new(&config, authenticator.clone(), ru_provider);
3030
let submit = SubmitTask {
3131
authenticator: authenticator.clone(),
32-
host_provider,
32+
host_provider: host_provider.clone(),
3333
zenith,
3434
client: reqwest::Client::new(),
3535
sequencer_signer,
@@ -38,7 +38,7 @@ async fn main() -> eyre::Result<()> {
3838

3939
let authenticator_jh = authenticator.spawn();
4040
let (submit_channel, submit_jh) = submit.spawn();
41-
let build_jh = builder.spawn(submit_channel);
41+
let build_jh = builder.spawn(submit_channel, host_provider.clone());
4242

4343
let port = config.builder_port;
4444
let server = serve_builder_with_span(([0, 0, 0, 0], port), span);

src/tasks/block.rs

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
use super::bundler::{Bundle, BundlePoller};
22
use super::oauth::Authenticator;
33
use super::tx_poller::TxPoller;
4-
use crate::config::{BuilderConfig, WalletlessProvider};
5-
use alloy::primitives::{keccak256, Bytes, B256};
6-
use alloy::providers::Provider;
4+
5+
use crate::config::{BuilderConfig, Provider, WalletlessProvider};
6+
7+
use alloy::primitives::{keccak256, Bytes, FixedBytes, B256};
8+
use alloy::providers::Provider as _;
9+
use alloy::rpc::types::TransactionRequest;
710
use alloy::{
811
consensus::{SidecarBuilder, SidecarCoder, TxEnvelope},
912
eips::eip2718::Decodable2718,
1013
};
1114
use alloy_rlp::Buf;
15+
use eyre::{bail, eyre};
1216
use std::time::{SystemTime, UNIX_EPOCH};
1317
use std::{sync::OnceLock, time::Duration};
1418
use tokio::{sync::mpsc, task::JoinHandle};
1519
use tracing::{debug, error, Instrument};
16-
use zenith_types::{encode_txns, Alloy2718Coder};
20+
use zenith_types::{encode_txns, Alloy2718Coder, ZenithEthBundle};
1721

1822
/// Ethereum's slot time in seconds.
1923
pub const ETHEREUM_SLOT_TIME: u64 = 12;
@@ -153,14 +157,17 @@ impl BlockBuilder {
153157
}
154158
}
155159

156-
async fn get_bundles(&mut self, in_progress: &mut InProgressBlock) {
160+
async fn get_bundles(&mut self, host_provider: &Provider, in_progress: &mut InProgressBlock) {
157161
tracing::trace!("query bundles from cache");
158162
let bundles = self.bundle_poller.check_bundle_cache().await;
159163
// OPTIMIZE: Sort bundles received from cache
160164
match bundles {
161165
Ok(bundles) => {
162166
for bundle in bundles {
163-
in_progress.ingest_bundle(bundle);
167+
let result = self.simulate_bundle(&bundle.bundle, host_provider).await;
168+
if result.is_ok() {
169+
in_progress.ingest_bundle(bundle.clone());
170+
}
164171
}
165172
}
166173
Err(e) => {
@@ -170,6 +177,54 @@ impl BlockBuilder {
170177
self.bundle_poller.evict();
171178
}
172179

180+
/// Simulates a Flashbots-style ZenithEthBundle, simualating each transaction in it's bundle
181+
/// by calling it against the host provider at the current height against default storage (no state overrides)
182+
/// and failing the whole bundle if any transaction not listed in the reverts list fails that call.
183+
async fn simulate_bundle(
184+
&mut self,
185+
bundle: &ZenithEthBundle,
186+
host_provider: &Provider,
187+
) -> eyre::Result<()> {
188+
tracing::info!("simulating bundle");
189+
190+
let reverts = &bundle.bundle.reverting_tx_hashes;
191+
tracing::debug!(reverts = ?reverts, "processing bundle with reverts");
192+
193+
for tx in &bundle.bundle.txs {
194+
let (tx_env, hash) = self.parse_from_bundle(tx)?;
195+
196+
// Simulate and check for reversion allowance
197+
match self.simulate_transaction(host_provider, tx_env).await {
198+
Ok(_) => {
199+
// Passed, log trace and continue
200+
tracing::debug!(tx = %hash, "tx passed simulation");
201+
continue;
202+
}
203+
Err(sim_err) => {
204+
// Failed, only continfue if tx is marked in revert list
205+
tracing::debug!("tx failed simulation: {}", sim_err);
206+
if reverts.contains(&hash) {
207+
continue;
208+
} else {
209+
bail!("tx {hash} failed simulation but was not marked as allowed to revert")
210+
}
211+
}
212+
}
213+
}
214+
Ok(())
215+
}
216+
217+
/// Simulates a transaction by calling it on the host provider at the current height with the current state.
218+
async fn simulate_transaction(
219+
&self,
220+
host_provider: &Provider,
221+
tx_env: TxEnvelope,
222+
) -> eyre::Result<()> {
223+
let tx = TransactionRequest::from_transaction(tx_env);
224+
host_provider.call(&tx).await?;
225+
Ok(())
226+
}
227+
173228
async fn filter_transactions(&self, in_progress: &mut InProgressBlock) {
174229
// query the rollup node to see which transaction(s) have been included
175230
let mut confirmed_transactions = Vec::new();
@@ -203,9 +258,25 @@ impl BlockBuilder {
203258
self.secs_to_next_slot() + self.config.target_slot_time
204259
}
205260

261+
/// Parses bytes into a transaction envelope that is compatible with Flashbots-style bundles
262+
fn parse_from_bundle(&self, tx: &Bytes) -> Result<(TxEnvelope, FixedBytes<32>), eyre::Error> {
263+
let tx_env = TxEnvelope::decode_2718(&mut tx.chunk())?;
264+
let hash = tx_env.tx_hash().to_owned();
265+
tracing::debug!(hash = %hash, "decoded bundle tx");
266+
if tx_env.is_eip4844() {
267+
tracing::error!("eip-4844 disallowed");
268+
return Err(eyre!("EIP-4844 transactions are not allowed in bundles"));
269+
}
270+
Ok((tx_env, hash))
271+
}
272+
206273
/// Spawn the block builder task, returning the inbound channel to it, and
207274
/// a handle to the running task.
208-
pub fn spawn(mut self, outbound: mpsc::UnboundedSender<InProgressBlock>) -> JoinHandle<()> {
275+
pub fn spawn(
276+
mut self,
277+
outbound: mpsc::UnboundedSender<InProgressBlock>,
278+
host_provider: Provider,
279+
) -> JoinHandle<()> {
209280
tokio::spawn(
210281
async move {
211282
loop {
@@ -216,7 +287,7 @@ impl BlockBuilder {
216287
// Build a block
217288
let mut in_progress = InProgressBlock::default();
218289
self.get_transactions(&mut in_progress).await;
219-
self.get_bundles(&mut in_progress).await;
290+
self.get_bundles(&host_provider, &mut in_progress).await;
220291

221292
// Filter confirmed transactions from the block
222293
self.filter_transactions(&mut in_progress).await;

src/tasks/submit.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ use crate::{
66
use alloy::{
77
consensus::{constants::GWEI_TO_WEI, SimpleCoder},
88
eips::BlockNumberOrTag,
9-
network::TransactionBuilder,
10-
network::TransactionBuilder4844,
9+
network::{TransactionBuilder, TransactionBuilder4844},
1110
primitives::{FixedBytes, U256},
1211
providers::{Provider as _, SendableTx, WalletProvider},
1312
rpc::types::eth::TransactionRequest,

0 commit comments

Comments
 (0)