-
Notifications
You must be signed in to change notification settings - Fork 3
feat: sim item filters by best valid #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
a3137f3
d396aa9
45527c7
b1c3117
990c908
8c90599
ad9a677
b9bde56
f7632e0
236cf11
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,26 @@ | ||
| use crate::CacheError; | ||
| use crate::{cache::StateSource, CacheError, SimItemValidity}; | ||
| use alloy::{ | ||
| consensus::{ | ||
| transaction::{Recovered, SignerRecoverable}, | ||
| Transaction, TxEnvelope, | ||
| }, | ||
| primitives::TxHash, | ||
| primitives::{Address, TxHash, U256}, | ||
| }; | ||
| use signet_bundle::{RecoveredBundle, SignetEthBundle}; | ||
| use signet_bundle::{RecoveredBundle, SignetEthBundle, TxRequirement}; | ||
| use std::{ | ||
| borrow::{Borrow, Cow}, | ||
| collections::BTreeMap, | ||
| hash::Hash, | ||
| sync::Arc, | ||
| }; | ||
|
|
||
| /// An item that can be simulated. | ||
| /// An item that can be simulated, wrapped in an Arc for cheap cloning. | ||
| #[derive(Debug, Clone, PartialEq, Eq)] | ||
| pub enum SimItem { | ||
| /// A bundle to be simulated. | ||
| Bundle(Box<RecoveredBundle>), | ||
| Bundle(Arc<RecoveredBundle>), | ||
| /// A transaction to be simulated. | ||
| Tx(Box<Recovered<TxEnvelope>>), | ||
| Tx(Arc<Recovered<TxEnvelope>>), | ||
| } | ||
|
|
||
| impl TryFrom<SignetEthBundle> for SimItem { | ||
|
|
@@ -57,15 +59,15 @@ impl TryFrom<TxEnvelope> for SimItem { | |
|
|
||
| impl SimItem { | ||
| /// Get the bundle if it is a bundle. | ||
| pub const fn as_bundle(&self) -> Option<&RecoveredBundle> { | ||
| pub fn as_bundle(&self) -> Option<&RecoveredBundle> { | ||
| match self { | ||
| Self::Bundle(bundle) => Some(bundle), | ||
| Self::Tx(_) => None, | ||
| } | ||
| } | ||
|
|
||
| /// Get the transaction if it is a transaction. | ||
| pub const fn as_tx(&self) -> Option<&Recovered<TxEnvelope>> { | ||
| pub fn as_tx(&self) -> Option<&Recovered<TxEnvelope>> { | ||
| match self { | ||
| Self::Bundle(_) => None, | ||
| Self::Tx(tx) => Some(tx), | ||
|
|
@@ -110,6 +112,128 @@ impl SimItem { | |
| Self::Tx(tx) => SimIdentifier::Tx(*tx.inner().hash()), | ||
| } | ||
| } | ||
|
|
||
| fn check_tx<S>(&self, source: &S) -> Result<SimItemValidity, Box<dyn std::error::Error>> | ||
| where | ||
| S: StateSource, | ||
| { | ||
| let item = self.as_tx().expect("SimItem is not a Tx"); | ||
|
|
||
| let total = U256::from(item.max_fee_per_gas() * item.gas_limit() as u128) + item.value(); | ||
|
|
||
| source | ||
| .map(&item.signer(), |info| { | ||
| // if the chain nonce is greater than the tx nonce, it is | ||
| // no longer valid | ||
| if info.nonce > item.nonce() { | ||
| return SimItemValidity::Never; | ||
| } | ||
| // if the chain nonce is less than the tx nonce, we need to wait | ||
| if info.nonce < item.nonce() { | ||
| return SimItemValidity::Future; | ||
| } | ||
| // if the balance is insufficient, we need to wait | ||
| if info.balance < total { | ||
| return SimItemValidity::Future; | ||
| } | ||
| // nonce is equal and balance is sufficient | ||
| SimItemValidity::Now | ||
| }) | ||
| .map_err(Into::into) | ||
| } | ||
|
|
||
| fn check_bundle_tx_list<S>( | ||
| items: impl Iterator<Item = TxRequirement>, | ||
| source: &S, | ||
| ) -> Result<SimItemValidity, S::Error> | ||
| where | ||
| S: StateSource, | ||
| { | ||
| // For bundles, we want to check the nonce of each transaction. To do | ||
| // this, we build a small in memory cache so that if the same signer | ||
| // appears, we can reuse the nonce info. We do not check balances after | ||
| // the first tx, as they may have changed due to prior txs in the | ||
| // bundle. | ||
|
|
||
| let mut nonce_cache: BTreeMap<Address, u64> = BTreeMap::new(); | ||
| let mut items = items.peekable(); | ||
|
|
||
| // Peek to perform the balance check for the first tx | ||
| if let Some(first) = items.peek() { | ||
|
Comment on lines
+161
to
+162
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this only checks the first transaction. if the bundle contains multiple senders, shouldn't we check the first tx balance for each?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. read the comment
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had already read it. I wanted stricter validation here, but I see how making it more strict can discard valid bundles
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a way we can rewrite this to be more clear? |
||
| let info = source.account_details(&first.signer)?; | ||
|
|
||
| // check balance for the first tx is sufficient | ||
| if first.balance > info.balance { | ||
| return Ok(SimItemValidity::Future); | ||
| } | ||
|
|
||
| // Cache the nonce. This will be used for the first tx. | ||
| nonce_cache.insert(first.signer, info.nonce); | ||
| } | ||
|
|
||
| for requirement in items { | ||
| let state_nonce = match nonce_cache.get(&requirement.signer) { | ||
| Some(cached_nonce) => *cached_nonce, | ||
| None => { | ||
| let nonce = source.nonce(&requirement.signer)?; | ||
| nonce_cache.insert(requirement.signer, nonce); | ||
| nonce | ||
| } | ||
| }; | ||
|
|
||
| if requirement.nonce < state_nonce { | ||
| return Ok(SimItemValidity::Never); | ||
| } | ||
| if requirement.nonce > state_nonce { | ||
| return Ok(SimItemValidity::Future); | ||
| } | ||
|
|
||
| // Increment the cached nonce for the next transaction from this | ||
| // signer. Map _must_ have the entry as we just either loaded or | ||
| // stored it above | ||
| nonce_cache.entry(requirement.signer).and_modify(|n| *n += 1); | ||
| } | ||
|
|
||
| // All transactions passed | ||
| Ok(SimItemValidity::Now) | ||
| } | ||
|
|
||
| fn check_bundle<S, S2>( | ||
| &self, | ||
| source: &S, | ||
| host_source: &S2, | ||
| ) -> Result<SimItemValidity, Box<dyn std::error::Error>> | ||
| where | ||
| S: StateSource, | ||
| S2: StateSource, | ||
| { | ||
| let item = self.as_bundle().expect("SimItem is not a Bundle"); | ||
|
|
||
| let ru_tx = Self::check_bundle_tx_list(item.tx_reqs(), source)?; | ||
| let host_tx = Self::check_bundle_tx_list(item.host_tx_reqs(), host_source)?; | ||
|
|
||
| // Check both the regular txs and the host txs. | ||
| Ok(ru_tx.min(host_tx)) | ||
| } | ||
|
|
||
| /// Check if the item is valid against the provided state sources. | ||
| /// | ||
| /// This will check that nonces and balances are sufficient for the item to | ||
| /// be included on the current state. | ||
| pub fn check<S, S2>( | ||
| &self, | ||
| source: &S, | ||
| host_source: &S2, | ||
| ) -> Result<SimItemValidity, Box<dyn std::error::Error>> | ||
| where | ||
| S: StateSource, | ||
| S2: StateSource, | ||
| { | ||
| match self { | ||
| SimItem::Bundle(_) => self.check_bundle(source, host_source), | ||
| SimItem::Tx(_) => self.check_tx(source), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A simulation cache item identifier. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| mod error; | ||
| pub use error::CacheError; | ||
|
|
||
| mod item; | ||
| pub use item::{SimIdentifier, SimItem}; | ||
|
|
||
| mod state; | ||
| pub use state::StateSource; | ||
|
|
||
| mod store; | ||
| pub use store::SimCache; | ||
|
|
||
| mod validity; | ||
| pub use validity::SimItemValidity; |
Uh oh!
There was an error while loading. Please reload this page.