diff --git a/crates/pevm/src/vm.rs b/crates/pevm/src/vm.rs index 19971252..009b7fdc 100644 --- a/crates/pevm/src/vm.rs +++ b/crates/pevm/src/vm.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + use alloy_primitives::TxKind; use alloy_rpc_types::Receipt; use hashbrown::HashMap; @@ -376,13 +378,42 @@ impl<'a, S: Storage, C: PevmChain> Database for VmDb<'a, S, C> { if location_hash == self.from_hash && self.tx.nonce.is_some_and(|nonce| nonce != account.nonce) { - if self.tx_idx > 0 { - // TODO: Better retry strategy -- immediately, to the - // closest sender tx, to the missing sender tx, etc. - return Err(ReadError::Blocking(self.tx_idx - 1)); - } else { - return Err(ReadError::InvalidNonce(self.tx_idx)); + let get_parent = |tx_idx: TxIdx| { + let parent_tx_idx = unsafe { *self.vm.parent_tx_idxs.get_unchecked(tx_idx) }; + if parent_tx_idx == tx_idx { + None + } else { + Some(parent_tx_idx) + } + }; + + // BEGIN: nonce check + let mut current_parent: Option = get_parent(self.tx_idx); + for read_origin in read_origins.iter() { + match read_origin { + ReadOrigin::MvMemory(tx_version) => { + if let Some(parent) = current_parent { + match parent.cmp(&tx_version.tx_idx) { + Ordering::Less => {} + Ordering::Equal => { + current_parent = get_parent(parent); + } + Ordering::Greater => { + return Err(ReadError::Blocking(parent)); + } + } + } + } + ReadOrigin::Storage => { + if let Some(parent) = current_parent { + return Err(ReadError::Blocking(parent)); + } + } + } } + // END: nonce check + + return Err(ReadError::InvalidNonce(self.tx_idx)); } // Fully evaluate the account and register it to read cache @@ -500,6 +531,7 @@ pub(crate) struct Vm<'a, S: Storage, C: PevmChain> { spec_id: SpecId, beneficiary_location_hash: MemoryLocationHash, reward_policy: RewardPolicy, + parent_tx_idxs: Vec, // parent_tx_idxs[i] = i if i does not have parent } impl<'a, S: Storage, C: PevmChain> Vm<'a, S, C> { @@ -511,6 +543,17 @@ impl<'a, S: Storage, C: PevmChain> Vm<'a, S, C> { txs: &'a [TxEnv], spec_id: SpecId, ) -> Self { + let mut parent_tx_idxs: Vec<_> = txs.iter().enumerate().map(|(index, _)| index).collect(); + let mut address_to_tx_idx = + HashMap::with_capacity_and_hasher(txs.len(), BuildSuffixHasher::default()); + + for (tx_idx, tx) in txs.iter().enumerate() { + // We use the fact that `insert` returns the previous value. + if let Some(parent_tx_idx) = address_to_tx_idx.insert(tx.caller, tx_idx) { + parent_tx_idxs[tx_idx] = parent_tx_idx; + } + } + Self { storage, mv_memory, @@ -522,6 +565,7 @@ impl<'a, S: Storage, C: PevmChain> Vm<'a, S, C> { block_env.coinbase, )), reward_policy: chain.get_reward_policy(), + parent_tx_idxs, } }