signet_sim/cache/
validity.rs1use crate::cache::StateSource;
2use alloy::primitives::Address;
3use core::fmt::{self, Display, Formatter};
4use signet_bundle::TxRequirement;
5use std::collections::BTreeMap;
6use tracing::{trace, trace_span};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
14pub enum SimItemValidity {
15 Never,
17 Future,
21 Now,
23}
24
25impl SimItemValidity {
26 pub const fn is_valid_now(&self) -> bool {
28 matches!(self, SimItemValidity::Now)
29 }
30
31 pub const fn is_never_valid(&self) -> bool {
33 matches!(self, SimItemValidity::Never)
34 }
35
36 pub const fn is_future_valid(&self) -> bool {
38 matches!(self, SimItemValidity::Future)
39 }
40}
41
42impl Display for SimItemValidity {
43 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
44 match self {
45 Self::Never => f.write_str("never"),
46 Self::Future => f.write_str("future"),
47 Self::Now => f.write_str("now"),
48 }
49 }
50}
51
52pub async fn check_bundle_tx_list<S>(
58 items: impl Iterator<Item = TxRequirement>,
59 source: &S,
60) -> Result<SimItemValidity, S::Error>
61where
62 S: StateSource,
63{
64 let mut nonce_cache: BTreeMap<Address, u64> = BTreeMap::new();
71 let mut items = items.peekable();
72
73 if let Some(first) = items.peek() {
75 let info = source.account_details(&first.signer).await?;
76
77 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 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 nonce_cache.entry(requirement.signer).and_modify(|n| *n += 1);
123 }
124
125 Ok(SimItemValidity::Now)
127}