Skip to content

Commit eed935d

Browse files
authored
chore: make check_bundle_tx_list public (#219)
* make check_bundle_tx_list public * version bump to rc 17 * fix ProviderStateSource impl
1 parent a13e04e commit eed935d

File tree

8 files changed

+135
-96
lines changed

8 files changed

+135
-96
lines changed

Cargo.toml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ members = ["crates/*"]
33
resolver = "2"
44

55
[workspace.package]
6-
version = "0.16.0-rc.16"
6+
version = "0.16.0-rc.17"
77
edition = "2021"
88
rust-version = "1.87"
99
authors = ["init4"]
@@ -34,19 +34,19 @@ debug = false
3434
incremental = false
3535

3636
[workspace.dependencies]
37-
signet-bundle = { version = "0.16.0-rc.16", path = "crates/bundle" }
38-
signet-constants = { version = "0.16.0-rc.16", path = "crates/constants" }
39-
signet-evm = { version = "0.16.0-rc.16", path = "crates/evm" }
40-
signet-extract = { version = "0.16.0-rc.16", path = "crates/extract" }
41-
signet-journal = { version = "0.16.0-rc.16", path = "crates/journal" }
42-
signet-node = { version = "0.16.0-rc.16", path = "crates/node" }
43-
signet-orders = { version = "0.16.0-rc.16", path = "crates/orders" }
44-
signet-sim = { version = "0.16.0-rc.16", path = "crates/sim" }
45-
signet-types = { version = "0.16.0-rc.16", path = "crates/types" }
46-
signet-tx-cache = { version = "0.16.0-rc.16", path = "crates/tx-cache" }
47-
signet-zenith = { version = "0.16.0-rc.16", path = "crates/zenith" }
37+
signet-bundle = { version = "0.16.0-rc.17", path = "crates/bundle" }
38+
signet-constants = { version = "0.16.0-rc.17", path = "crates/constants" }
39+
signet-evm = { version = "0.16.0-rc.17", path = "crates/evm" }
40+
signet-extract = { version = "0.16.0-rc.17", path = "crates/extract" }
41+
signet-journal = { version = "0.16.0-rc.17", path = "crates/journal" }
42+
signet-node = { version = "0.16.0-rc.17", path = "crates/node" }
43+
signet-orders = { version = "0.16.0-rc.17", path = "crates/orders" }
44+
signet-sim = { version = "0.16.0-rc.17", path = "crates/sim" }
45+
signet-types = { version = "0.16.0-rc.17", path = "crates/types" }
46+
signet-tx-cache = { version = "0.16.0-rc.17", path = "crates/tx-cache" }
47+
signet-zenith = { version = "0.16.0-rc.17", path = "crates/zenith" }
4848

49-
signet-test-utils = { version = "0.16.0-rc.16", path = "crates/test-utils" }
49+
signet-test-utils = { version = "0.16.0-rc.17", path = "crates/test-utils" }
5050

5151
# trevm
5252
trevm = { version = "0.34.1", features = ["full_env_cfg", "asyncdb"] }

crates/bundle/src/send/driver.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ where
247247
Ok(htrevm.accept_state())
248248
})
249249
.map_err(|err| {
250-
error!(err = %err.error(), err_dbg = ?err.error(), "error while running host transaction");
250+
debug!(err = %err.error(), err_dbg = ?err.error(), "error while running host transaction");
251251
SignetEthBundleError::HostSimulation("host simulation error")
252252
}),
253253
trevm

crates/sim/src/cache/item.rs

Lines changed: 7 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
use crate::{cache::StateSource, CacheError, SimItemValidity};
1+
use crate::{
2+
cache::{check_bundle_tx_list, StateSource},
3+
CacheError, SimItemValidity,
4+
};
25
use alloy::{
36
consensus::{
47
transaction::{Recovered, SignerRecoverable},
58
Transaction, TxEnvelope,
69
},
7-
primitives::{Address, TxHash, U256},
10+
primitives::{TxHash, U256},
811
};
912
use signet_bundle::{RecoveredBundle, SignetEthBundle, TxRequirement};
1013
use std::{
1114
borrow::{Borrow, Cow},
12-
collections::BTreeMap,
1315
hash::Hash,
1416
sync::Arc,
1517
};
@@ -168,7 +170,7 @@ impl SimItem {
168170
where
169171
S: StateSource,
170172
{
171-
Self::check_bundle_tx_list(items, source).await
173+
check_bundle_tx_list(items, source).await
172174
}
173175

174176
#[instrument(level = "trace", skip_all)]
@@ -179,79 +181,7 @@ impl SimItem {
179181
where
180182
S: StateSource,
181183
{
182-
Self::check_bundle_tx_list(items, source).await
183-
}
184-
185-
async fn check_bundle_tx_list<S>(
186-
items: impl Iterator<Item = TxRequirement>,
187-
source: &S,
188-
) -> Result<SimItemValidity, S::Error>
189-
where
190-
S: StateSource,
191-
{
192-
// For bundles, we want to check the nonce of each transaction. To do
193-
// this, we build a small in memory cache so that if the same signer
194-
// appears, we can reuse the nonce info. We do not check balances after
195-
// the first tx, as they may have changed due to prior txs in the
196-
// bundle.
197-
198-
let mut nonce_cache: BTreeMap<Address, u64> = BTreeMap::new();
199-
let mut items = items.peekable();
200-
201-
// Peek to perform the balance check for the first tx
202-
if let Some(first) = items.peek() {
203-
let info = source.account_details(&first.signer).await?;
204-
205-
// check balance for the first tx is sufficient
206-
if first.balance > info.balance {
207-
trace!(
208-
required = %first.balance,
209-
available = %info.balance,
210-
signer = %first.signer,
211-
"insufficient balance",
212-
);
213-
return Ok(SimItemValidity::Future);
214-
}
215-
216-
// Cache the nonce. This will be used for the first tx.
217-
nonce_cache.insert(first.signer, info.nonce);
218-
}
219-
220-
for requirement in items {
221-
let state_nonce = match nonce_cache.get(&requirement.signer) {
222-
Some(cached_nonce) => *cached_nonce,
223-
None => {
224-
let nonce = source.nonce(&requirement.signer).await?;
225-
nonce_cache.insert(requirement.signer, nonce);
226-
nonce
227-
}
228-
};
229-
230-
let _guard = trace_span!(
231-
"check_bundle_tx",
232-
signer = %requirement.signer,
233-
item_nonce = requirement.nonce,
234-
expected_nonce = state_nonce,
235-
)
236-
.entered();
237-
238-
if requirement.nonce < state_nonce {
239-
trace!("nonce too low");
240-
return Ok(SimItemValidity::Never);
241-
}
242-
if requirement.nonce > state_nonce {
243-
trace!("nonce too high");
244-
return Ok(SimItemValidity::Future);
245-
}
246-
247-
// Increment the cached nonce for the next transaction from this
248-
// signer. Map _must_ have the entry as we just either loaded or
249-
// stored it above
250-
nonce_cache.entry(requirement.signer).and_modify(|n| *n += 1);
251-
}
252-
253-
// All transactions passed
254-
Ok(SimItemValidity::Now)
184+
check_bundle_tx_list(items, source).await
255185
}
256186

257187
async fn check_bundle<S, S2>(

crates/sim/src/cache/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ mod item;
55
pub use item::{SimIdentifier, SimItem};
66

77
mod state;
8-
pub use state::{AcctInfo, StateSource};
8+
pub use state::{AcctInfo, ProviderStateSource, StateSource};
99

1010
mod store;
1111
pub use store::SimCache;
1212

1313
mod validity;
14-
pub use validity::SimItemValidity;
14+
pub use validity::{check_bundle_tx_list, SimItemValidity};

crates/sim/src/cache/state.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use alloy::primitives::{Address, U256};
1+
use alloy::{
2+
primitives::{Address, U256},
3+
providers::Provider,
4+
transports::TransportError,
5+
};
26
use core::future::Future;
37
use trevm::revm::database_interface::async_db::DatabaseAsyncRef;
48

@@ -62,3 +66,25 @@ where
6266
Ok(AcctInfo { nonce: info.nonce, balance: info.balance, has_code })
6367
}
6468
}
69+
70+
/// A wrapper that implements [`StateSource`] for any alloy [`Provider`].
71+
///
72+
/// This allows using an alloy provider as a state source for bundle tx list
73+
/// validation via [`check_bundle_tx_list`].
74+
///
75+
/// [`check_bundle_tx_list`]: crate::check_bundle_tx_list
76+
#[derive(Debug, Clone)]
77+
pub struct ProviderStateSource<P>(pub P);
78+
79+
impl<P: Provider> StateSource for ProviderStateSource<P> {
80+
type Error = TransportError;
81+
82+
async fn account_details(&self, address: &Address) -> Result<AcctInfo, Self::Error> {
83+
let (nonce, balance, code) = tokio::join!(
84+
self.0.get_transaction_count(*address),
85+
self.0.get_balance(*address),
86+
self.0.get_code_at(*address),
87+
);
88+
Ok(AcctInfo { nonce: nonce?, balance: balance?, has_code: !code?.is_empty() })
89+
}
90+
}

crates/sim/src/cache/validity.rs

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1+
use crate::cache::StateSource;
2+
use alloy::primitives::Address;
13
use core::fmt::{self, Display, Formatter};
4+
use signet_bundle::TxRequirement;
5+
use std::collections::BTreeMap;
6+
use tracing::{trace, trace_span};
27

38
/// The validity status of a simulation item.
49
///
@@ -43,3 +48,80 @@ impl Display for SimItemValidity {
4348
}
4449
}
4550
}
51+
52+
/// Check a list of bundle transactions for validity against a state source.
53+
///
54+
/// Validates nonces sequentially, building a per-signer nonce cache so that
55+
/// multiple transactions from the same signer are checked with incrementing
56+
/// nonces. The first transaction's balance is also checked.
57+
pub async fn check_bundle_tx_list<S>(
58+
items: impl Iterator<Item = TxRequirement>,
59+
source: &S,
60+
) -> Result<SimItemValidity, S::Error>
61+
where
62+
S: StateSource,
63+
{
64+
// For bundles, we want to check the nonce of each transaction. To do
65+
// this, we build a small in memory cache so that if the same signer
66+
// appears, we can reuse the nonce info. We do not check balances after
67+
// the first tx, as they may have changed due to prior txs in the
68+
// bundle.
69+
70+
let mut nonce_cache: BTreeMap<Address, u64> = BTreeMap::new();
71+
let mut items = items.peekable();
72+
73+
// Peek to perform the balance check for the first tx
74+
if let Some(first) = items.peek() {
75+
let info = source.account_details(&first.signer).await?;
76+
77+
// check balance for the first tx is sufficient
78+
if first.balance > info.balance {
79+
trace!(
80+
required = %first.balance,
81+
available = %info.balance,
82+
signer = %first.signer,
83+
"insufficient balance",
84+
);
85+
return Ok(SimItemValidity::Future);
86+
}
87+
88+
// Cache the nonce. This will be used for the first tx.
89+
nonce_cache.insert(first.signer, info.nonce);
90+
}
91+
92+
for requirement in items {
93+
let state_nonce = match nonce_cache.get(&requirement.signer) {
94+
Some(cached_nonce) => *cached_nonce,
95+
None => {
96+
let nonce = source.nonce(&requirement.signer).await?;
97+
nonce_cache.insert(requirement.signer, nonce);
98+
nonce
99+
}
100+
};
101+
102+
let _guard = trace_span!(
103+
"check_bundle_tx",
104+
signer = %requirement.signer,
105+
item_nonce = requirement.nonce,
106+
expected_nonce = state_nonce,
107+
)
108+
.entered();
109+
110+
if requirement.nonce < state_nonce {
111+
trace!("nonce too low");
112+
return Ok(SimItemValidity::Never);
113+
}
114+
if requirement.nonce > state_nonce {
115+
trace!("nonce too high");
116+
return Ok(SimItemValidity::Future);
117+
}
118+
119+
// Increment the cached nonce for the next transaction from this
120+
// signer. Map _must_ have the entry as we just either loaded or
121+
// stored it above
122+
nonce_cache.entry(requirement.signer).and_modify(|n| *n += 1);
123+
}
124+
125+
// All transactions passed
126+
Ok(SimItemValidity::Now)
127+
}

crates/sim/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ pub use built::BuiltBlock;
2020

2121
mod cache;
2222
pub use cache::{
23-
AcctInfo, CacheError, SimCache, SimIdentifier, SimItem, SimItemValidity, StateSource,
23+
check_bundle_tx_list, AcctInfo, CacheError, ProviderStateSource, SimCache, SimIdentifier,
24+
SimItem, SimItemValidity, StateSource,
2425
};
2526

2627
mod env;

crates/tx-cache/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ signet-types.workspace = true
1818
alloy.workspace = true
1919
futures-util.workspace = true
2020

21-
reqwest.workspace = true
21+
reqwest = { workspace = true, features = ["json"] }
2222
serde = { workspace = true, features = ["derive"] }
2323
tracing.workspace = true
2424
uuid = { workspace = true, features = ["serde"] }

0 commit comments

Comments
 (0)