1use crate::send::SignetEthBundle;
2use alloy::{hex, primitives::U256};
3use signet_evm::{DriveBundleResult, EvmErrored, EvmNeedsTx, SignetInspector, SignetLayered};
4use signet_types::{AggregateFills, AggregateOrders, MarketError, SignedPermitError};
5use std::borrow::Cow;
6use tracing::{debug, error};
7use trevm::{
8 helpers::Ctx,
9 inspectors::{Layered, TimeLimit},
10 revm::{
11 context::result::EVMError, inspector::InspectorEvmTr, Database, DatabaseCommit, Inspector,
12 },
13 trevm_bail, trevm_ensure, trevm_try, BundleDriver, BundleError,
14};
15
16pub type SignetEthBundleInsp<I> = Layered<TimeLimit, I>;
19
20#[derive(Debug)]
22pub struct DriverOutput<Db, Insp>
23where
24 Db: Database,
25 Insp: Inspector<Ctx<Db>>,
26{
27 pub host_evm: Option<signet_evm::EvmNeedsTx<Db, Insp>>,
29
30 pub total_gas_used: u64,
32
33 pub total_host_gas_used: u64,
35
36 pub beneficiary_balance_increase: U256,
38
39 pub bundle_fills: AggregateFills,
41
42 pub bundle_orders: AggregateOrders,
44}
45
46impl<Db, Insp> DriverOutput<Db, Insp>
47where
48 Db: Database,
49 Insp: Inspector<Ctx<Db>>,
50{
51 pub const fn use_gas(&mut self, gas: u64) {
53 self.total_gas_used = self.total_gas_used.saturating_add(gas);
54 }
55
56 pub const fn use_host_gas(&mut self, gas: u64) {
58 self.total_host_gas_used = self.total_host_gas_used.saturating_add(gas);
59 }
60
61 pub fn absorb(&mut self, fills: &AggregateFills, orders: &AggregateOrders) {
63 self.bundle_fills.absorb(fills);
64 self.bundle_orders.absorb(orders);
65 }
66
67 pub const fn record_beneficiary_increase(&mut self, increase: U256) {
69 self.beneficiary_balance_increase =
70 self.beneficiary_balance_increase.saturating_add(increase);
71 }
72}
73
74#[derive(thiserror::Error)]
76pub enum SignetEthBundleError<Db: Database> {
77 #[error(transparent)]
79 Bundle(#[from] BundleError<Db>),
80
81 #[error(transparent)]
83 SignetPermit(#[from] SignedPermitError),
84
85 #[error(transparent)]
87 Contract(#[from] alloy::contract::Error),
88
89 #[error(transparent)]
91 Market(#[from] MarketError),
92
93 #[error("{0}")]
95 HostSimulation(&'static str),
96}
97
98impl<Db: Database> core::fmt::Debug for SignetEthBundleError<Db> {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 match self {
101 SignetEthBundleError::Bundle(bundle_error) => {
102 f.debug_tuple("BundleError").field(bundle_error).finish()
103 }
104 SignetEthBundleError::SignetPermit(signed_order_error) => {
105 f.debug_tuple("SignedPermitError").field(signed_order_error).finish()
106 }
107 SignetEthBundleError::Contract(contract_error) => {
108 f.debug_tuple("ContractError").field(contract_error).finish()
109 }
110 SignetEthBundleError::Market(market_error) => {
111 f.debug_tuple("MarketError").field(market_error).finish()
112 }
113 SignetEthBundleError::HostSimulation(msg) => {
114 f.debug_tuple("HostSimulationError").field(msg).finish()
115 }
116 }
117 }
118}
119
120impl<Db: Database> From<EVMError<Db::Error>> for SignetEthBundleError<Db> {
121 fn from(err: EVMError<Db::Error>) -> Self {
122 Self::Bundle(BundleError::from(err))
123 }
124}
125
126#[derive(Debug)]
128pub struct SignetEthBundleDriver<'a, 'b, Db, Insp>
129where
130 Db: Database,
131 Insp: Inspector<Ctx<Db>>,
132{
133 bundle: &'a SignetEthBundle,
135
136 pub fill_state: Cow<'b, AggregateFills>,
138
139 deadline: std::time::Instant,
142
143 output: DriverOutput<Db, Insp>,
145}
146
147impl<'a, 'b, Db, Insp> SignetEthBundleDriver<'a, 'b, Db, Insp>
148where
149 Db: Database,
150 Insp: Inspector<Ctx<Db>>,
151{
152 pub fn new(
155 bundle: &'a SignetEthBundle,
156 host_evm: signet_evm::EvmNeedsTx<Db, Insp>,
157 deadline: std::time::Instant,
158 ) -> Self {
159 Self::new_with_fill_state(bundle, host_evm, deadline, Default::default())
160 }
161
162 pub fn new_with_fill_state(
167 bundle: &'a SignetEthBundle,
168 host_evm: signet_evm::EvmNeedsTx<Db, Insp>,
169 deadline: std::time::Instant,
170 fill_state: Cow<'b, AggregateFills>,
171 ) -> Self {
172 Self {
173 bundle,
174 fill_state,
175 deadline,
176 output: DriverOutput {
177 host_evm: Some(host_evm),
178 total_gas_used: 0,
179 total_host_gas_used: 0,
180 beneficiary_balance_increase: U256::ZERO,
181 bundle_fills: AggregateFills::default(),
182 bundle_orders: AggregateOrders::default(),
183 },
184 }
185 }
186
187 pub const fn bundle(&self) -> &SignetEthBundle {
189 self.bundle
190 }
191
192 pub const fn deadline(&self) -> std::time::Instant {
194 self.deadline
195 }
196
197 pub const fn total_gas_used(&self) -> u64 {
199 self.output.total_gas_used
200 }
201
202 pub const fn beneficiary_balance_increase(&self) -> U256 {
204 self.output.beneficiary_balance_increase
205 }
206
207 pub fn into_outputs(self) -> DriverOutput<Db, Insp> {
209 self.output
210 }
211}
212
213impl<RuDb, HostDb, RuInsp, HostInsp> BundleDriver<RuDb, SignetLayered<Layered<TimeLimit, RuInsp>>>
214 for SignetEthBundleDriver<'_, '_, HostDb, HostInsp>
215where
216 RuDb: Database + DatabaseCommit,
217 RuInsp: Inspector<Ctx<RuDb>>,
218 HostDb: Database + DatabaseCommit,
219 HostInsp: Inspector<Ctx<HostDb>>,
220{
221 type Error = SignetEthBundleError<RuDb>;
222
223 fn run_bundle(
224 &mut self,
225 mut trevm: EvmNeedsTx<RuDb, SignetEthBundleInsp<RuInsp>>,
226 ) -> DriveBundleResult<Self, RuDb, SignetEthBundleInsp<RuInsp>> {
227 let bundle = &self.bundle.bundle;
228 trevm_ensure!(!bundle.txs.is_empty(), trevm, BundleError::BundleEmpty.into());
232
233 trevm_ensure!(
235 trevm.block_number().to::<u64>() == bundle.block_number,
236 trevm,
237 BundleError::BlockNumberMismatch.into()
238 );
239
240 let timestamp = trevm.block_timestamp();
242 trevm_ensure!(
243 self.bundle.is_valid_at_timestamp(timestamp.to()),
244 trevm,
245 BundleError::TimestampOutOfRange.into()
246 );
247
248 let host_txs = trevm_try!(self.bundle.decode_and_validate_host_txs(), trevm);
250 let txs = trevm_try!(self.bundle.decode_and_validate_txs(), trevm);
251
252 let beneficiary = trevm.beneficiary();
256 let inital_beneficiary_balance =
257 trevm_try!(trevm.try_read_balance(beneficiary).map_err(EVMError::Database), trevm);
258
259 for tx in host_txs.into_iter() {
265 self.output.host_evm = Some(trevm_try!(
266 self.output
267 .host_evm
268 .take()
269 .expect("host_evm missing")
270 .run_tx(&tx)
271 .and_then(|mut htrevm| {
272 let result = htrevm.result();
273 if let Some(output) = result.output() {
274 if !result.is_success() {
275 debug!(output = hex::encode(output), "host transaction reverted");
276 }
277 }
278
279 trevm_ensure!(
280 result.is_success(),
281 htrevm,
282 EVMError::Custom("host transaction reverted".to_string())
283 );
284
285 self.output.use_host_gas(result.gas_used());
287
288 let host_fills = htrevm
290 .inner_mut_unchecked()
291 .inspector
292 .as_mut_detector()
293 .take_aggregates()
294 .0;
295 self.output.bundle_fills.absorb(&host_fills);
296
297 Ok(htrevm.accept_state())
298 })
299 .map_err(|err| {
300 error!(err = %err.error(), err_dbg = ?err.error(), "error while running host transaction");
301 SignetEthBundleError::HostSimulation("host simulation error")
302 }),
303 trevm
304 ));
305 }
306
307 for tx in txs.into_iter() {
309 let _span = tracing::debug_span!("bundle_tx_loop", tx_hash = %tx.hash()).entered();
310
311 let limit = trevm.inner_mut_unchecked().ctx_inspector().1.outer_mut().outer_mut();
313 *limit = TimeLimit::new(self.deadline - std::time::Instant::now());
314
315 let tx_hash = tx.hash();
316
317 let mut t = trevm.run_tx(&tx).map_err(EvmErrored::err_into).inspect_err(
321 |err| error!(err = %err.error(), "error while running rollup transaction"),
322 )?;
323
324 let result = t.result();
326 let gas_used = result.gas_used();
327
328 if result.is_success() {
333 let (tx_fills, tx_orders) =
334 t.inner_mut_unchecked().inspector.as_mut_detector().take_aggregates();
335
336 let mut candidate_fills = self.output.bundle_fills.clone();
339 let mut candidate_orders = self.output.bundle_orders.clone();
340
341 candidate_fills.absorb(&tx_fills);
343 candidate_orders.absorb(&tx_orders);
344
345 if self.fill_state.check_ru_tx_events(&candidate_fills, &candidate_orders).is_err()
348 {
349 if self.bundle.reverting_tx_hashes().contains(tx_hash) {
350 debug!("transaction marked as revertible, reverting");
351 trevm = t.reject();
352 continue;
353 } else {
354 debug!("transaction dropped due to insufficient fills, not marked as revertible");
355 return Err(t.errored(BundleError::BundleReverted.into()));
356 }
357 }
358
359 self.output.bundle_fills = candidate_fills;
361 self.output.bundle_orders = candidate_orders;
362 } else {
363 if !self.bundle.reverting_tx_hashes().contains(tx_hash) {
368 debug!("transaction reverted, not marked as revertible");
369 return Err(t.errored(BundleError::BundleReverted.into()));
370 }
371 }
372
373 self.output.use_gas(gas_used);
376 trevm = t.accept_state()
377 }
378
379 let beneficiary_balance =
382 trevm_try!(trevm.try_read_balance(beneficiary).map_err(EVMError::Database), trevm);
383
384 self.output.record_beneficiary_increase(
385 beneficiary_balance.saturating_sub(inital_beneficiary_balance),
386 );
387
388 Ok(trevm)
389 }
390
391 fn post_bundle(
392 &mut self,
393 _trevm: &EvmNeedsTx<RuDb, SignetEthBundleInsp<RuInsp>>,
394 ) -> Result<(), Self::Error> {
395 Ok(())
396 }
397}