Skip to main content

reth_engine_tree/tree/payload_processor/
reuse.rs

1//! Reuse of speculative prewarm execution results.
2//!
3//! Prewarm workers execute block transactions in parallel against the parent state and record
4//! every account and storage value a transaction reads from the database. When sequential
5//! execution reaches a transaction whose recorded reads still match the current state, it commits
6//! the prewarm result instead of executing the transaction again. Missing or stale results fall
7//! back to normal execution, which never waits for prewarming.
8//!
9//! Most conflicts between transactions are balance changes, such as fee credits to the block
10//! beneficiary or value transfers to popular contracts. A transaction that never observes an
11//! account's balance produces the same result for any balance, apart from the balance itself, so
12//! such balances may differ and the transaction's balance change is applied to the current balance
13//! instead. Balances are observed by `BALANCE`, `SELFBALANCE`, `SELFDESTRUCT` and failed value
14//! transfers. Successful value transfers and gas payments only require enough starting balance
15//! to cover the largest cumulative debit recorded during speculation.
16//!
17//! Prewarming also seeds results of pool transactions that the txpool prewarm worker executed on
18//! top of the parent before the block arrived, see [`PoolSeeds`]. Their block environment is
19//! predicted, so they must not depend on fields that turn out to differ, and their fee credit to
20//! the beneficiary is applied to the actual beneficiary when the result is reused.
21
22use alloy_consensus::{InMemorySize, Transaction};
23use alloy_primitives::{
24    address,
25    map::{B256Map, DefaultHashBuilder, Entry},
26    Address, B256, KECCAK256_EMPTY, U256,
27};
28use dashmap::DashMap;
29use derive_where::derive_where;
30use evm2::{
31    bytecode::Bytecode,
32    evm::{
33        AccountChanges, AccountInfo, Database, Db, DbResult, DynDatabase, PendingState, StorageSlot,
34    },
35    interpreter::{op, InstrStop, Interpreter, Message, MessageKind, MessageResult},
36    utils::WordExt,
37    EvmFeatures, EvmTypes, Inspector, InspectorConfig, OpcodeSet, TxResultWithState, Version,
38};
39use parking_lot::{Mutex, RwLock};
40use std::{
41    cell::RefCell,
42    rc::Rc,
43    sync::{
44        atomic::{AtomicUsize, Ordering},
45        Arc,
46    },
47};
48
49/// Balances above this are compared exactly, so that applying balance changes cannot overflow.
50const MAX_REBASED_BALANCE: U256 = U256::from_limbs([u64::MAX, u64::MAX, 0, 0]);
51
52/// Beneficiary of speculative pool transaction executions.
53///
54/// Results of transactions that access it are discarded, so its value only affects how many are.
55pub(crate) const POOL_BENEFICIARY: Address = address!("0x00000000000000000000000000000000000beef0");
56
57/// Maximum estimated number of bytes held by [`PoolResults`].
58const MAX_POOL_RESULT_BYTES: usize = 64 << 20;
59
60/// Opcodes [`BalanceTracker::step`] is called for. Must contain every opcode it matches on.
61const BALANCE_TRACKER_STEPS: OpcodeSet = {
62    let mut steps = OpcodeSet::EMPTY;
63    steps.insert(op::BALANCE);
64    steps.insert(op::SELFBALANCE);
65    steps.insert(op::SELFDESTRUCT);
66    steps.insert(op::COINBASE);
67    steps.insert(op::TIMESTAMP);
68    steps.insert(op::DIFFICULTY);
69    steps.insert(op::GASLIMIT);
70    steps
71};
72
73/// Returns whether blocks executed with `cfg_env` may reuse prewarm results, which excludes
74/// EIP-8037 multidimensional block gas.
75pub(crate) const fn cfg_reuses_prewarm_results(version: &Version) -> bool {
76    !version.feature(EvmFeatures::EIP8037)
77}
78
79/// Speculative prewarm results of a block's transactions, indexed by their position in the block.
80#[derive_where(Debug)]
81pub(crate) struct PrewarmResults<T: EvmTypes> {
82    slots: Box<[Mutex<ResultSlot<T>>]>,
83    /// Writes predicted by the stored results.
84    predicted: Arc<PredictedState>,
85}
86
87/// Prewarm state of a transaction in [`PrewarmResults`].
88#[derive_where(Debug)]
89enum ResultSlot<T: EvmTypes> {
90    /// Prewarming has not executed the transaction.
91    Empty,
92    /// A result that may be reused.
93    Ready(Box<PrewarmedTx<T>>),
94    /// A result that cannot be reused, with the reads it depends on.
95    Unusable(ReadSet),
96    /// Block execution executed the transaction or reused its result, which are published as
97    /// its actual writes.
98    Executed,
99}
100
101impl<T: EvmTypes> PrewarmResults<T> {
102    /// Creates empty result slots for `transaction_count` transactions.
103    pub(crate) fn new(transaction_count: usize) -> Self {
104        Self {
105            slots: (0..transaction_count).map(|_| Mutex::new(ResultSlot::Empty)).collect(),
106            predicted: Arc::default(),
107        }
108    }
109
110    /// Returns the writes predicted by the stored results.
111    pub(super) const fn predicted(&self) -> &Arc<PredictedState> {
112        &self.predicted
113    }
114
115    /// Stores the prewarm result of the transaction at `index` and publishes its writes if
116    /// [`ReadSet::finish`] accepts its reads, otherwise marks it as unusable.
117    pub(super) fn store(
118        &self,
119        index: usize,
120        result: TxResultWithState<T>,
121        reads: ReadSet,
122        balances: &mut BalanceTracker,
123        tx: SpeculativeTx,
124    ) {
125        match reads.finish(balances, tx) {
126            Ok(reads) => self.set(index, |predicted| {
127                predicted.publish(index, &result.pending_state);
128                ResultSlot::Ready(Box::new(PrewarmedTx { result, reads, credit: None }))
129            }),
130            Err(reads) => self.reject(index, reads),
131        }
132    }
133
134    /// Stores the result of the pool transaction with `hash` as the prewarm result of the
135    /// transaction at `index` and publishes its writes, unless prewarming already executed it or
136    /// the result depends on the block environment fields that `seeds` differ in.
137    ///
138    /// Returns the pool transaction if it was stored.
139    pub(super) fn seed(
140        &self,
141        index: usize,
142        hash: &B256,
143        seeds: &PoolSeeds<T>,
144    ) -> Option<Arc<PoolTx<T>>>
145    where
146        T::TxResultExt: Clone,
147    {
148        let tx = seeds.results.get(hash)?;
149        // The speculative execution treated the actual beneficiary as a cold account.
150        if tx.reads.block_env.intersects(seeds.differs) ||
151            tx.reads.position(seeds.beneficiary).is_some()
152        {
153            return None
154        }
155        let mut slot = self.slots.get(index)?.lock();
156        if !matches!(*slot, ResultSlot::Empty) {
157            return None
158        }
159        self.predicted.publish(index, &tx.result.pending_state);
160        *slot = ResultSlot::Ready(Box::new(PrewarmedTx {
161            result: tx.result.clone(),
162            reads: tx.reads.clone(),
163            credit: Some(BeneficiaryCredit { beneficiary: seeds.beneficiary, amount: tx.credit }),
164        }));
165        Some(tx)
166    }
167
168    /// Marks the result of the transaction at `index` as unusable.
169    pub(super) fn reject(&self, index: usize, mut reads: ReadSet) {
170        // Rejection may depend on any balance, such as the caller's.
171        for read in &mut reads.accounts {
172            read.balance = BalanceDependency::Exact;
173        }
174        self.set(index, |_| ResultSlot::Unusable(reads));
175    }
176
177    /// Replaces the predicted writes of the transaction at `index` with the writes of its actual
178    /// execution by block execution.
179    pub(crate) fn publish_executed(&self, index: usize, state: &PendingState) {
180        let Some(slot) = self.slots.get(index) else { return };
181        let mut slot = slot.lock();
182        if let ResultSlot::Ready(previous) = core::mem::replace(&mut *slot, ResultSlot::Executed) {
183            self.predicted.retract(index, &previous.result.pending_state);
184        }
185        self.predicted.publish(index, state);
186        self.predicted.generation.fetch_add(1, Ordering::Relaxed);
187    }
188
189    /// Replaces the slot of the transaction at `index`, retracting the writes of its previous
190    /// result, unless block execution already published its actual writes.
191    fn set(&self, index: usize, f: impl FnOnce(&PredictedState) -> ResultSlot<T>) {
192        let Some(slot) = self.slots.get(index) else { return };
193        let mut slot = slot.lock();
194        match &*slot {
195            ResultSlot::Ready(previous) => {
196                self.predicted.retract(index, &previous.result.pending_state)
197            }
198            ResultSlot::Executed => return,
199            ResultSlot::Empty | ResultSlot::Unusable(_) => {}
200        }
201        *slot = f(&self.predicted);
202    }
203
204    /// Returns whether the transaction at `index` should be executed again, because prewarming
205    /// skipped it or its reads no longer match the predicted state.
206    pub(super) fn needs_refresh(&self, index: usize) -> bool {
207        let Some(slot) = self.slots.get(index) else { return false };
208        let slot = slot.lock();
209        let reads = match &*slot {
210            ResultSlot::Empty => return true,
211            ResultSlot::Ready(tx) => &tx.reads,
212            ResultSlot::Unusable(reads) => reads,
213            ResultSlot::Executed => return false,
214        };
215        !reads.matches(&self.predicted, index)
216    }
217
218    /// Takes the prewarm result of the transaction at `index` if it is ready and its reads match
219    /// `db`.
220    ///
221    /// The returned result has its balance changes applied to the current balances. Stale results,
222    /// and results whose reads fail, stay in their slot unchanged and are freed with `self`.
223    pub(crate) fn take_valid<DB: DynDatabase + ?Sized>(
224        &self,
225        index: usize,
226        db: &mut DB,
227    ) -> Result<Result<TxResultWithState<T>, ReuseMiss>, evm2::DatabaseError> {
228        let Some(slot) = self.slots.get(index) else { return Ok(Err(ReuseMiss::Missing)) };
229        let mut slot = slot.lock();
230        let mut tx = match core::mem::replace(&mut *slot, ResultSlot::Executed) {
231            ResultSlot::Ready(tx) => tx,
232            ResultSlot::Unusable(reads) => {
233                *slot = ResultSlot::Unusable(reads);
234                return Ok(Err(ReuseMiss::Stale))
235            }
236            previous @ (ResultSlot::Empty | ResultSlot::Executed) => {
237                *slot = previous;
238                return Ok(Err(ReuseMiss::Missing))
239            }
240        };
241        let rebased = match tx.validate(db) {
242            Ok(Some(rebased)) => rebased,
243            result => {
244                *slot = ResultSlot::Ready(tx);
245                return result.map(|_| Err(ReuseMiss::Stale))
246            }
247        };
248        // The result stays published as the prediction of this transaction's writes.
249        if rebased {
250            // Later transactions read the rebased balances.
251            self.predicted.publish_accounts(index, &tx.result.pending_state);
252            self.predicted.generation.fetch_add(1, Ordering::Relaxed);
253        }
254        Ok(Ok(tx.result))
255    }
256}
257
258/// Reason a prewarm result could not be reused.
259#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub(crate) enum ReuseMiss {
261    /// Prewarming had not finished the transaction.
262    Missing,
263    /// The speculative result was unusable or failed a reuse eligibility check.
264    Stale,
265}
266
267/// A speculative transaction result and the database reads it depends on.
268#[derive_where(Debug)]
269struct PrewarmedTx<T: EvmTypes> {
270    result: TxResultWithState<T>,
271    reads: ReadSet,
272    /// The fee credit to apply to the block beneficiary, for results of pool transactions.
273    credit: Option<BeneficiaryCredit>,
274}
275
276/// A fee credit to the block beneficiary that a speculative result does not contain.
277#[derive(Debug)]
278struct BeneficiaryCredit {
279    beneficiary: Address,
280    amount: U256,
281}
282
283impl<T: EvmTypes> PrewarmedTx<T> {
284    /// Validates recorded reads, then applies balance changes to the current state.
285    fn validate<DB: DynDatabase + ?Sized>(&mut self, db: &mut DB) -> DbResult<Option<bool>> {
286        for read in &self.reads.storage {
287            if db.get_storage(&read.address, &read.slot)? != read.value {
288                return Ok(None)
289            }
290        }
291        let mut rebased = Vec::new();
292        for read in &self.reads.accounts {
293            let current = db.get_account(&read.address)?;
294            match read.compare(current.as_ref()) {
295                AccountMatch::Same => {}
296                AccountMatch::Rebase { speculative, current } => {
297                    if let Some(account) = self
298                        .result
299                        .pending_state
300                        .account_changes()
301                        .find(|a| a.address == read.address)
302                    {
303                        // Replacing metadata must not erase creation, destruction or storage-wipe
304                        // flags.
305                        if account.created || account.selfdestructed || account.storage.wiped {
306                            return Ok(None)
307                        }
308                        let Some(mut info) = account.current.cloned() else { return Ok(None) };
309                        let Some(balance) = info
310                            .balance
311                            .checked_add(current)
312                            .and_then(|b| b.checked_sub(speculative))
313                        else {
314                            return Ok(None)
315                        };
316                        info.balance = balance;
317                        let mut original = account.original.cloned();
318                        if let Some(original) = &mut original {
319                            original.balance = current;
320                        }
321                        rebased.push((read.address, original, Some(info)));
322                    }
323                }
324                AccountMatch::Differs => return Ok(None),
325            }
326        }
327        if let Some(credit) = &self.credit {
328            let Some(original) = db.get_account(&credit.beneficiary)? else { return Ok(None) };
329            let mut current = original.clone();
330            let Some(balance) = current.balance.checked_add(credit.amount) else { return Ok(None) };
331            current.balance = balance;
332            // Execution touches the beneficiary even for a zero credit, which deletes it if it is
333            // empty (EIP-161). Detached state cannot express that, so execute normally.
334            if current.is_empty() {
335                return Ok(None)
336            }
337            // The synthetic beneficiary was not observed. Preserve its current canonical cache
338            // value when committing the detached result's loaded-but-unchanged entries.
339            let synthetic = db.get_account(&POOL_BENEFICIARY)?;
340            rebased.push((POOL_BENEFICIARY, synthetic.clone(), synthetic));
341            rebased.push((credit.beneficiary, Some(original), Some(current)));
342        }
343        let changed = !rebased.is_empty();
344        for (address, original, current) in rebased {
345            self.result.pending_state.insert_account(address, original, current);
346        }
347        Ok(Some(changed))
348    }
349}
350
351/// How a speculative execution depends on an account's balance.
352#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353enum BalanceDependency {
354    /// The balance was only credited or debited, so it may differ.
355    None,
356    /// The account paid for gas or sent value, so the balance must be at least this large.
357    AtLeast(U256),
358    /// The balance was observed, so it must match.
359    Exact,
360}
361
362/// An account a speculative execution read.
363#[derive(Debug, Clone)]
364struct AccountRead {
365    address: Address,
366    /// The account without its bytecode, `None` if it did not exist.
367    info: Option<AccountInfo>,
368    balance: BalanceDependency,
369    /// Whether the account was read from the predicted state.
370    predicted: bool,
371}
372
373impl AccountRead {
374    /// Compares the read account with `current`.
375    fn compare(&self, current: Option<&AccountInfo>) -> AccountMatch {
376        let (speculative, current) = match (&self.info, current) {
377            (Some(speculative), Some(current)) => (speculative, current),
378            (None, None) => return AccountMatch::Same,
379            _ => return AccountMatch::Differs,
380        };
381        if speculative.balance == current.balance {
382            return if speculative == current { AccountMatch::Same } else { AccountMatch::Differs }
383        }
384
385        let allowed = match self.balance {
386            BalanceDependency::None => true,
387            BalanceDependency::AtLeast(min) => current.balance >= min,
388            BalanceDependency::Exact => false,
389        };
390        // A zero balance can make the account empty, which changes gas costs and EIP-161 state
391        // clearing.
392        if !allowed ||
393            speculative.balance.is_zero() ||
394            current.balance.is_zero() ||
395            current.balance > MAX_REBASED_BALANCE ||
396            (AccountInfo { balance: speculative.balance, ..without_code(current) }) !=
397                *speculative
398        {
399            return AccountMatch::Differs
400        }
401        AccountMatch::Rebase { speculative: speculative.balance, current: current.balance }
402    }
403}
404
405/// How a read account compares with its current state.
406#[derive(Debug, Clone, Copy, PartialEq, Eq)]
407enum AccountMatch {
408    /// The account is unchanged.
409    Same,
410    /// Only the balance changed, so the result's balance change must be applied to the current
411    /// balance.
412    Rebase { speculative: U256, current: U256 },
413    /// The account differs in a way the execution may depend on.
414    Differs,
415}
416
417/// A storage slot a speculative execution read.
418#[derive(Debug, Clone)]
419struct StorageRead {
420    address: Address,
421    slot: U256,
422    value: U256,
423    /// Whether the slot was read from the predicted state.
424    predicted: bool,
425}
426
427/// Database reads of a speculative transaction execution.
428#[derive(Debug, Default, Clone)]
429pub(crate) struct ReadSet {
430    accounts: Vec<AccountRead>,
431    storage: Vec<StorageRead>,
432    /// Whether execution loaded bytecode that is unknown to the database.
433    unknown_code: bool,
434    /// Block environment fields the execution observed.
435    block_env: BlockEnvFields,
436}
437
438impl ReadSet {
439    /// Classifies how the execution depends on the balance of each account it read.
440    ///
441    /// Speculation disables nonce and balance checks, so this fails unless `caller` had the
442    /// transaction's `nonce` and could pay `max_balance_spending`, i.e. unless execution with
443    /// these checks enabled would have accepted the transaction.
444    fn finish(mut self, balances: &mut BalanceTracker, tx: SpeculativeTx) -> Result<Self, Self> {
445        let SpeculativeTx { caller, nonce, max_balance_spending } = tx;
446        // Balance changes are applied once per account.
447        self.accounts.sort_unstable_by_key(|read| read.address);
448        self.accounts.dedup_by_key(|read| read.address);
449        self.block_env = balances.block_env;
450
451        let Ok(caller_index) = self.accounts.binary_search_by_key(&caller, |read| read.address)
452        else {
453            return Err(self)
454        };
455        let caller_info = &self.accounts[caller_index].info;
456        let (caller_nonce, caller_balance) =
457            caller_info.as_ref().map_or((0, U256::ZERO), |info| (info.nonce, info.balance));
458        let Some(max_balance_spending) = max_balance_spending else { return Err(self) };
459        // Transactions at the nonce limit cannot be reused, even with validation disabled.
460        if self.unknown_code ||
461            balances.read_failed ||
462            nonce == u64::MAX ||
463            caller_nonce != nonce ||
464            caller_balance < max_balance_spending
465        {
466            return Err(self)
467        }
468
469        // Rebasing only bounded balances keeps every balance during execution far from overflow,
470        // so no value transfer can overflow in one execution but not the other.
471        let bounded = self
472            .accounts
473            .iter()
474            .all(|read| read.info.as_ref().is_none_or(|info| info.balance <= MAX_REBASED_BALANCE));
475        let paid = caller_balance - max_balance_spending;
476        // Sorted, so that each account is looked up in logarithmic time and the smallest balance
477        // a sender had left comes first.
478        let BalanceTracker { observed, senders, .. } = balances;
479        observed.sort_unstable();
480        senders.sort_unstable();
481        for read in &mut self.accounts {
482            // A sender needs enough balance to leave the same smallest balance, which is the
483            // balance after paying for gas for the caller.
484            let sent = senders
485                .get(senders.partition_point(|(address, _)| *address < read.address))
486                .filter(|(address, _)| *address == read.address)
487                .map(|(_, left)| *left);
488            let left = sent.into_iter().chain((read.address == caller).then_some(paid)).min();
489            read.balance = if !bounded || observed.binary_search(&read.address).is_ok() {
490                BalanceDependency::Exact
491            } else if let Some(left) = left {
492                let balance = read.info.as_ref().map_or(U256::ZERO, |info| info.balance);
493                BalanceDependency::AtLeast(balance.saturating_sub(left))
494            } else {
495                BalanceDependency::None
496            };
497        }
498        Ok(self)
499    }
500
501    /// Returns the position of the read of `address` in the sorted account reads.
502    fn position(&self, address: Address) -> Option<usize> {
503        self.accounts.binary_search_by_key(&address, |read| read.address).ok()
504    }
505
506    /// Returns whether executing the transaction at `index` against `predicted` would read the
507    /// same values, as far as the execution depends on them.
508    fn matches(&self, predicted: &PredictedState, index: usize) -> bool {
509        self.accounts.iter().all(|read| match predicted.account(read.address, index) {
510            Some(account) => read.compare(account.as_ref()) != AccountMatch::Differs,
511            None => !read.predicted,
512        }) && self.storage.iter().all(|read| {
513            match predicted.storage(read.address, read.slot, index) {
514                Some(value) => value == read.value,
515                None => !read.predicted,
516            }
517        })
518    }
519}
520
521/// The fields of a speculatively executed transaction that its validity checks depend on.
522#[derive(Debug)]
523pub(crate) struct SpeculativeTx {
524    /// The transaction's sender.
525    pub(crate) caller: Address,
526    /// The transaction's nonce.
527    pub(crate) nonce: u64,
528    /// The most the caller may spend, or an error if it overflows.
529    pub(crate) max_balance_spending: Option<U256>,
530}
531
532/// Writes of a key, sorted by the index of the transaction that made them.
533type Writes<T> = Vec<(usize, T)>;
534
535/// State writes predicted by speculative results, by transaction index.
536///
537/// Speculative executions read the latest write predicted before their transaction, so that
538/// transactions depending on earlier ones in the block can still be reused.
539#[derive(Debug, Default)]
540pub(super) struct PredictedState {
541    /// Accounts, `None` if destroyed.
542    accounts: DashMap<Address, Writes<Option<AccountInfo>>, DefaultHashBuilder>,
543    storage: DashMap<(Address, U256), Writes<U256>, DefaultHashBuilder>,
544    /// Transactions that cleared an account's storage by creating or destroying it.
545    cleared: DashMap<Address, Writes<()>, DefaultHashBuilder>,
546    /// Bytecode deployed by predicted writes.
547    code: DashMap<B256, Bytecode, DefaultHashBuilder>,
548    /// Number of actual execution or rebased account updates published by block execution.
549    generation: AtomicUsize,
550}
551
552impl PredictedState {
553    /// Returns the number of actual execution or rebased account updates published by block
554    /// execution.
555    pub(super) fn generation(&self) -> usize {
556        self.generation.load(Ordering::Relaxed)
557    }
558
559    /// Returns the latest account write predicted before the transaction at `index`.
560    fn account(&self, address: Address, index: usize) -> Option<Option<AccountInfo>> {
561        let writes = self.accounts.get(&address)?;
562        latest(&writes, index).map(|(_, account)| account.clone())
563    }
564
565    /// Returns the latest storage value predicted before the transaction at `index`.
566    fn storage(&self, address: Address, slot: U256, index: usize) -> Option<U256> {
567        let cleared = self.cleared.get(&address).and_then(|w| latest(&w, index).map(|(i, _)| *i));
568        let written = self.storage.get(&(address, slot)).and_then(|w| latest(&w, index).copied());
569        match (written, cleared) {
570            (Some((written, value)), Some(cleared)) if written >= cleared => Some(value),
571            (_, Some(_)) => Some(U256::ZERO),
572            (written, None) => written.map(|(_, value)| value),
573        }
574    }
575
576    /// Returns predicted bytecode by its hash.
577    fn code(&self, code_hash: B256) -> Option<Bytecode> {
578        self.code.get(&code_hash).map(|code| code.clone())
579    }
580
581    /// Publishes the transaction's writes as predictions for later transactions.
582    fn publish(&self, index: usize, state: &PendingState) {
583        for account in state.account_changes() {
584            if let Some((hash, code)) = account.code {
585                self.code.insert(hash, code.clone());
586            }
587        }
588        self.update(index, state.account_changes(), true);
589    }
590
591    /// Republishes rebased account balances.
592    fn publish_accounts(&self, index: usize, state: &PendingState) {
593        self.update(index, state.account_changes(), true);
594    }
595
596    /// Retracts the transaction's predicted writes.
597    fn retract(&self, index: usize, state: &PendingState) {
598        self.update(index, state.account_changes(), false);
599    }
600
601    /// Publishes or retracts each changed account and slot.
602    fn update<'a>(
603        &self,
604        index: usize,
605        state: impl IntoIterator<Item = AccountChanges<'a>>,
606        publish: bool,
607    ) {
608        for account in state {
609            if !account.is_changed() {
610                continue
611            }
612            let address = account.address;
613            if account.storage.wiped || account.created || account.current.is_none() {
614                write(&mut self.cleared.entry(address).or_default(), index, publish.then_some(()));
615            }
616            write(
617                &mut self.accounts.entry(address).or_default(),
618                index,
619                publish.then(|| account.current.map(without_code)),
620            );
621            if account.current.is_none() {
622                continue
623            }
624            for (&slot, value) in &account.storage.slots {
625                if (account.storage.wiped && !value.value.current.is_zero()) ||
626                    (!account.storage.wiped && value.value.original != value.value.current)
627                {
628                    write(
629                        &mut self.storage.entry((address, slot)).or_default(),
630                        index,
631                        publish.then_some(value.value.current),
632                    );
633                }
634            }
635        }
636    }
637}
638
639/// Sets the write of the transaction at `index`, or removes it if `value` is `None`.
640fn write<T>(writes: &mut Writes<T>, index: usize, value: Option<T>) {
641    match (writes.binary_search_by_key(&index, |(i, _)| *i), value) {
642        (Ok(i), Some(value)) => writes[i].1 = value,
643        (Err(i), Some(value)) => writes.insert(i, (index, value)),
644        (Ok(i), None) => {
645            writes.remove(i);
646        }
647        (Err(_), None) => {}
648    }
649}
650
651/// Returns the latest write before the transaction at `index`.
652fn latest<T>(writes: &[(usize, T)], index: usize) -> Option<&(usize, T)> {
653    writes[..writes.partition_point(|(i, _)| *i < index)].last()
654}
655
656/// Block environment fields that speculative executions may observe and that may differ from the
657/// actual block environment.
658#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
659pub(crate) struct BlockEnvFields {
660    /// The beneficiary, observed by `COINBASE`, by accessing its account, or possibly by running
661    /// out of gas.
662    pub(crate) beneficiary: bool,
663    /// The timestamp, observed by `TIMESTAMP`.
664    pub(crate) timestamp: bool,
665    /// The prevrandao, observed by `PREVRANDAO`.
666    pub(crate) prevrandao: bool,
667    /// The gas limit, observed by `GASLIMIT`.
668    pub(crate) gas_limit: bool,
669}
670
671impl BlockEnvFields {
672    /// Returns whether any field is set in both `self` and `other`.
673    const fn intersects(self, other: Self) -> bool {
674        (self.beneficiary && other.beneficiary) ||
675            (self.timestamp && other.timestamp) ||
676            (self.prevrandao && other.prevrandao) ||
677            (self.gas_limit && other.gas_limit)
678    }
679}
680
681/// Results of pool transactions executed on top of a block's parent in a predicted block
682/// environment with [`POOL_BENEFICIARY`] as the beneficiary, by transaction hash.
683#[derive_where(Debug)]
684pub(crate) struct PoolResults<T: EvmTypes> {
685    stored: RwLock<StoredPoolTxs<T>>,
686}
687
688impl<T: EvmTypes> Default for PoolResults<T> {
689    fn default() -> Self {
690        Self { stored: RwLock::new(StoredPoolTxs { txs: B256Map::default(), bytes: 0 }) }
691    }
692}
693
694/// Results of pool transactions stored in [`PoolResults`].
695#[derive_where(Debug)]
696struct StoredPoolTxs<T: EvmTypes> {
697    txs: B256Map<Arc<PoolTx<T>>>,
698    /// Estimated number of bytes held by `txs`.
699    bytes: usize,
700}
701
702impl<T: EvmTypes> PoolResults<T> {
703    /// Stores the result of the pool transaction with `hash` if [`ReadSet::finish`] accepts its
704    /// reads, unless it may have accessed the beneficiary, a result is already stored for `hash`,
705    /// or the stored results would hold more than [`MAX_POOL_RESULT_BYTES`].
706    ///
707    /// Returns whether the result was stored.
708    pub(crate) fn store(
709        &self,
710        hash: B256,
711        mut result: TxResultWithState<T>,
712        reads: ReadSet,
713        balances: &mut BalanceTracker,
714        tx: SpeculativeTx,
715    ) -> bool {
716        if balances.block_env.beneficiary {
717            return false
718        }
719        let Ok(mut reads) = reads.finish(balances, tx) else { return false };
720
721        // The beneficiary was only loaded to credit fees, which are credited to the actual
722        // beneficiary instead. Crediting fees always loads and touches it, even for zero fees.
723        let Some(beneficiary) = result
724            .pending_state
725            .account_changes()
726            .find(|account| account.address == POOL_BENEFICIARY)
727        else {
728            return false
729        };
730        // A zero fee can leave an empty beneficiary that EIP-161 clears during finalization.
731        let balance = beneficiary.current.map_or(U256::ZERO, |info| info.balance);
732        let Some(index) = reads.position(POOL_BENEFICIARY) else { return false };
733        let original = reads.accounts.remove(index).info;
734        let speculative = original.as_ref().map_or(U256::ZERO, |info| info.balance);
735        if speculative > MAX_REBASED_BALANCE {
736            return false
737        }
738        let Some(credit) = balance.checked_sub(speculative) else { return false };
739        result.pending_state.insert_account(POOL_BENEFICIARY, original.clone(), original);
740
741        let bytes = retained_bytes(&result, &reads);
742        let mut stored = self.stored.write();
743        let total = stored.bytes + bytes;
744        if total > MAX_POOL_RESULT_BYTES {
745            return false
746        }
747        let Entry::Vacant(entry) = stored.txs.entry(hash) else { return false };
748        entry.insert(Arc::new(PoolTx { result, reads, credit }));
749        stored.bytes = total;
750        true
751    }
752
753    /// Returns the result of the pool transaction with `hash`.
754    pub(crate) fn get(&self, hash: &B256) -> Option<Arc<PoolTx<T>>> {
755        self.stored.read().txs.get(hash).cloned()
756    }
757
758    /// Returns whether no result is stored.
759    pub(crate) fn is_empty(&self) -> bool {
760        self.stored.read().txs.is_empty()
761    }
762}
763
764/// Returns the estimated number of bytes that storing `result` and `reads` holds.
765fn retained_bytes<T: EvmTypes>(result: &TxResultWithState<T>, reads: &ReadSet) -> usize {
766    let accounts = result.pending_state.account_changes().map(|account| {
767        size_of::<AccountInfo>() * 2 +
768            size_of::<Address>() +
769            account.storage.slots.capacity() * size_of::<(U256, StorageSlot)>() +
770            account.code.map_or(0, |(_, code)| code.len())
771    });
772    reads.accounts.capacity() * size_of::<AccountRead>() +
773        reads.storage.capacity() * size_of::<StorageRead>() +
774        result.result.output.len() +
775        result.result.logs.iter().map(InMemorySize::size).sum::<usize>() +
776        accounts.sum::<usize>()
777}
778
779/// A speculative result of a pool transaction without its fee credit to the beneficiary.
780#[derive_where(Debug)]
781pub(crate) struct PoolTx<T: EvmTypes> {
782    result: TxResultWithState<T>,
783    reads: ReadSet,
784    /// The fee credit to the beneficiary.
785    credit: U256,
786}
787
788impl<T: EvmTypes> PoolTx<T> {
789    /// Returns the state changes of the transaction.
790    pub(super) const fn state(&self) -> &PendingState {
791        &self.result.pending_state
792    }
793
794    /// Loads every read of the transaction from `db`.
795    pub(super) fn load_reads<DB: DynDatabase + ?Sized>(
796        &self,
797        db: &mut DB,
798    ) -> Result<(), evm2::DatabaseError> {
799        for read in &self.reads.accounts {
800            db.get_account(&read.address)?;
801        }
802        for read in &self.reads.storage {
803            db.get_storage(&read.address, &read.slot)?;
804        }
805        Ok(())
806    }
807}
808
809/// Results of pool transactions that may seed the [`PrewarmResults`] of a block on top of the
810/// parent they were executed on.
811#[derive_where(Debug, Clone)]
812pub(crate) struct PoolSeeds<T: EvmTypes> {
813    results: Arc<PoolResults<T>>,
814    /// The beneficiary of the block.
815    beneficiary: Address,
816    /// Block environment fields that differ from the predicted environment.
817    differs: BlockEnvFields,
818}
819
820impl<T: EvmTypes> PoolSeeds<T> {
821    /// Creates seeds for a block with `beneficiary` whose environment differs from the predicted
822    /// environment of `results` in `differs`.
823    pub(crate) const fn new(
824        results: Arc<PoolResults<T>>,
825        beneficiary: Address,
826        differs: BlockEnvFields,
827    ) -> Self {
828        Self { results, beneficiary, differs }
829    }
830}
831
832/// Database wrapper that records reads for speculative execution.
833#[derive(Debug)]
834pub(crate) struct RecordingDb<DB: Database> {
835    inner: Db<DB>,
836    recording: RecordingHandle,
837}
838
839/// Per-worker recording state shared with its EVM and inspector.
840#[derive(Debug, Default, Clone)]
841pub(crate) struct RecordingHandle(Rc<RefCell<RecordingState>>);
842
843#[derive(Debug, Default)]
844struct RecordingState {
845    reads: Option<ReadSet>,
846    predicted: Option<(Arc<PredictedState>, usize)>,
847}
848
849impl RecordingHandle {
850    /// Begins recording against earlier predicted writes, or just the parent state.
851    pub(super) fn start(&self, predicted: Option<(Arc<PredictedState>, usize)>) {
852        *self.0.borrow_mut() = RecordingState { reads: Some(ReadSet::default()), predicted };
853    }
854
855    /// Begins recording a pool transaction against its parent state.
856    pub(crate) fn start_recording_inner(&self) {
857        self.start(None);
858    }
859
860    /// Stops recording and returns the transaction's reads.
861    pub(crate) fn take_reads(&self) -> Option<ReadSet> {
862        let mut state = self.0.borrow_mut();
863        state.predicted = None;
864        state.reads.take()
865    }
866}
867
868impl<DB: Database> RecordingDb<DB> {
869    /// Wraps a database with the worker's recording state.
870    pub(crate) const fn new(inner: DB, recording: RecordingHandle) -> Self {
871        Self { inner: Db::new(inner), recording }
872    }
873}
874
875impl<DB: Database> Database for RecordingDb<DB> {
876    type Error = evm2::DatabaseError;
877
878    fn is_fatal(error: &Self::Error) -> bool {
879        error.is_fatal()
880    }
881
882    fn get_account(&mut self, address: &Address) -> DbResult<Option<AccountInfo>> {
883        let mut state = self.recording.0.borrow_mut();
884        let predicted =
885            state.predicted.as_ref().and_then(|(state, index)| state.account(*address, *index));
886        let is_predicted = predicted.is_some();
887        let account = match predicted {
888            Some(account) => account,
889            None => self.inner.get_account(address)?,
890        };
891        if let Some(reads) = &mut state.reads {
892            reads.accounts.push(AccountRead {
893                address: *address,
894                info: account.as_ref().map(without_code),
895                balance: BalanceDependency::None,
896                predicted: is_predicted,
897            });
898        }
899        Ok(account)
900    }
901
902    fn get_code_by_hash(&mut self, hash: &B256) -> DbResult<Bytecode> {
903        let mut state = self.recording.0.borrow_mut();
904        if let Some(code) = state.predicted.as_ref().and_then(|(state, _)| state.code(*hash)) {
905            return Ok(code)
906        }
907        let code = self.inner.get_code_by_hash(hash)?;
908        if code.is_empty() &&
909            *hash != KECCAK256_EMPTY &&
910            let Some(reads) = &mut state.reads
911        {
912            reads.unknown_code = true;
913        }
914        Ok(code)
915    }
916
917    fn get_storage(&mut self, address: &Address, key: &U256) -> DbResult<U256> {
918        let mut state = self.recording.0.borrow_mut();
919        let predicted = state
920            .predicted
921            .as_ref()
922            .and_then(|(state, index)| state.storage(*address, *key, *index));
923        let is_predicted = predicted.is_some();
924        let value = match predicted {
925            Some(value) => value,
926            None => self.inner.get_storage(address, key)?,
927        };
928        if let Some(reads) = &mut state.reads {
929            reads.storage.push(StorageRead {
930                address: *address,
931                slot: *key,
932                value,
933                predicted: is_predicted,
934            });
935        }
936        Ok(value)
937    }
938
939    fn get_block_hash(&mut self, number: &U256) -> DbResult<B256> {
940        self.inner.get_block_hash(number)
941    }
942}
943
944/// Copies account metadata without retaining the bytecode allocation.
945fn without_code(info: &AccountInfo) -> AccountInfo {
946    let mut info = info.clone();
947    info.code = None;
948    info
949}
950
951impl SpeculativeTx {
952    /// Captures the nonce and maximum fee and value debit checked by canonical execution.
953    pub(crate) fn new(caller: Address, tx: &impl Transaction) -> Self {
954        let max_balance_spending = U256::from(tx.gas_limit())
955            .checked_mul(U256::from(tx.max_fee_per_gas()))
956            .and_then(|gas| gas.checked_add(tx.value()))
957            .and_then(|gas| {
958                U256::from(tx.blob_gas_used().unwrap_or_default())
959                    .checked_mul(U256::from(tx.max_fee_per_blob_gas().unwrap_or_default()))
960                    .and_then(|blob| gas.checked_add(blob))
961            });
962        Self { caller, nonce: tx.nonce(), max_balance_spending }
963    }
964}
965
966/// Inspector that tracks which balances and block environment fields a transaction observes and
967/// which accounts send value.
968#[derive(Debug, Default)]
969pub(crate) struct BalanceTracker {
970    recording: RecordingHandle,
971    read_failed: bool,
972    /// Accounts whose balance was observed.
973    observed: Vec<Address>,
974    /// Accounts that sent value, with the balance they had left after each send.
975    senders: Vec<(Address, U256)>,
976    /// Block environment fields that were observed.
977    block_env: BlockEnvFields,
978}
979
980impl BalanceTracker {
981    /// Creates an inspector sharing the worker's recorded database reads.
982    pub(crate) fn new(recording: RecordingHandle) -> Self {
983        Self { recording, ..Self::default() }
984    }
985
986    fn frame_end<T: EvmTypes>(
987        &mut self,
988        interp: &mut Interpreter<'_, '_, T>,
989        message: &Message<T>,
990        result: &MessageResult<T>,
991    ) {
992        let beneficiary = interp.host().block().beneficiary;
993        self.block_env.beneficiary |=
994            result.stop.is_out_of_gas() ||
995                (message.depth == 0 &&
996                    self.recording.0.borrow_mut().reads.as_ref().is_some_and(|r| {
997                        r.accounts.iter().any(|a| a.address == beneficiary)
998                    }));
999    }
1000
1001    fn send<T: EvmTypes>(
1002        &mut self,
1003        interp: &mut Interpreter<'_, '_, T>,
1004        sender: Address,
1005        value: U256,
1006    ) {
1007        let left = match interp.host().state_mut().account_info_untracked(&sender) {
1008            Ok(account) => account.and_then(|account| account.balance.checked_sub(value)),
1009            Err(_) => {
1010                // A failed observer read cannot justify reusing the speculative result.
1011                self.read_failed = true;
1012                None
1013            }
1014        };
1015        match left {
1016            Some(left) => self.senders.push((sender, left)),
1017            None => self.observed.push(sender),
1018        }
1019    }
1020}
1021
1022impl<T: EvmTypes> Inspector<T> for BalanceTracker {
1023    fn inspector_config(&self) -> InspectorConfig {
1024        InspectorConfig::new().with_steps(BALANCE_TRACKER_STEPS)
1025    }
1026
1027    fn step(&mut self, interp: &mut Interpreter<'_, '_, T>) {
1028        let address = match interp.opcode() {
1029            op::BALANCE => {
1030                let Some(word) = interp.stack().peek(0) else { return };
1031                word.to_address()
1032            }
1033            op::SELFBALANCE | op::SELFDESTRUCT => interp.message().destination,
1034            op::COINBASE => {
1035                self.block_env.beneficiary = true;
1036                return
1037            }
1038            op::TIMESTAMP => {
1039                self.block_env.timestamp = true;
1040                return
1041            }
1042            op::DIFFICULTY => {
1043                self.block_env.prevrandao = true;
1044                return
1045            }
1046            op::GASLIMIT => {
1047                self.block_env.gas_limit = true;
1048                return
1049            }
1050            _ => return,
1051        };
1052        if self.observed.last() != Some(&address) {
1053            self.observed.push(address);
1054        }
1055    }
1056
1057    fn call(
1058        &mut self,
1059        interp: &mut Interpreter<'_, '_, T>,
1060        message: &mut Message<T>,
1061    ) -> Option<MessageResult<T>> {
1062        if matches!(message.kind, MessageKind::Call | MessageKind::CallCode) &&
1063            !message.value.is_zero()
1064        {
1065            self.send(interp, message.caller, message.value);
1066        }
1067        None
1068    }
1069
1070    fn call_end(
1071        &mut self,
1072        interp: &mut Interpreter<'_, '_, T>,
1073        message: &Message<T>,
1074        result: &mut MessageResult<T>,
1075    ) {
1076        if result.stop == InstrStop::OutOfFunds {
1077            self.observed.push(message.caller);
1078        }
1079        self.frame_end(interp, message, result);
1080    }
1081
1082    fn create(
1083        &mut self,
1084        interp: &mut Interpreter<'_, '_, T>,
1085        message: &mut Message<T>,
1086    ) -> Option<MessageResult<T>> {
1087        if !message.value.is_zero() {
1088            self.send(interp, message.caller, message.value);
1089        }
1090        None
1091    }
1092
1093    fn create_end(
1094        &mut self,
1095        interp: &mut Interpreter<'_, '_, T>,
1096        message: &Message<T>,
1097        result: &mut MessageResult<T>,
1098    ) {
1099        if result.stop == InstrStop::OutOfFunds {
1100            self.observed.push(message.caller);
1101        }
1102        self.frame_end(interp, message, result);
1103    }
1104}
1105
1106#[cfg(test)]
1107mod tests {
1108    use super::*;
1109    use alloy_consensus::{transaction::Recovered, TxEip1559, TxLegacy};
1110    use alloy_primitives::{bytes, Bytes, Signature, TxKind};
1111    use evm2::{
1112        evm::{CacheDB, EmptyDB},
1113        BaseEvmTypes, SpecId,
1114    };
1115    use reth_errors::ProviderError;
1116    use reth_ethereum_primitives::{Receipt, Transaction, TransactionSigned};
1117    use reth_evm::{BlockExecutor, BlockExecutorFactory, ConfigureEvm, Evm, EvmFor};
1118    use reth_evm_ethereum::{EthBlockExecutionCtx, EthEvmConfig, EthEvmEnv};
1119    use reth_trie_common::bundle_state::BundleState;
1120
1121    const BENEFICIARY: Address = Address::repeat_byte(0xbe);
1122    /// Does nothing.
1123    const NOOP: Address = Address::repeat_byte(0xc0);
1124    /// Increments slot 0.
1125    const COUNTER: Address = Address::repeat_byte(0xc1);
1126    /// Stores the beneficiary balance in slot 0.
1127    const BENEFICIARY_BALANCE_READER: Address = Address::repeat_byte(0xc2);
1128    /// Logs its own balance.
1129    const SELF_BALANCE_READER: Address = Address::repeat_byte(0xc3);
1130    /// Sends 5 wei to [`NOOP`].
1131    const FORWARDER: Address = Address::repeat_byte(0xc4);
1132    /// Has a balance above [`MAX_REBASED_BALANCE`].
1133    const RICH: Address = Address::repeat_byte(0xc5);
1134    /// Exists with a zero balance.
1135    const ZERO_BALANCE: Address = Address::repeat_byte(0xc6);
1136    /// Has a code hash whose bytecode the database does not know.
1137    const UNKNOWN_CODE: Address = Address::repeat_byte(0xc7);
1138    /// Stores the timestamp in slot 0.
1139    const TIMESTAMP_READER: Address = Address::repeat_byte(0xc8);
1140    /// Stores the gas limit in slot 0.
1141    const GAS_LIMIT_READER: Address = Address::repeat_byte(0xc9);
1142    /// Stores the beneficiary in slot 0.
1143    const BENEFICIARY_READER: Address = Address::repeat_byte(0xca);
1144    /// Calls [`POOL_BENEFICIARY`].
1145    const POOL_BENEFICIARY_CALLER: Address = Address::repeat_byte(0xcb);
1146    /// Reads the balance of [`BENEFICIARY`] by its address.
1147    const BENEFICIARY_BALANCE_BY_ADDRESS: Address = Address::repeat_byte(0xcc);
1148    /// Stores the prevrandao in slot 0.
1149    const PREVRANDAO_READER: Address = Address::repeat_byte(0xcd);
1150    /// Has the largest balance.
1151    const SATURATED: Address = Address::repeat_byte(0xce);
1152    /// Index of a sender with the largest nonce.
1153    const EXHAUSTED_SENDER: u8 = 5;
1154    /// Selfdestructs to the noop contract.
1155    const SELFDESTRUCTER: Address = Address::repeat_byte(0xcf);
1156    /// Exists and is empty.
1157    const EMPTY: Address = Address::repeat_byte(0xd0);
1158    const GAS_PRICE: u128 = 2;
1159
1160    type Results = PrewarmResults<BaseEvmTypes>;
1161
1162    fn evm_env(prewarm: bool) -> EthEvmEnv {
1163        let mut env = EthEvmEnv::new(
1164            SpecId::CANCUN,
1165            evm2::env::BlockEnv::<BaseEvmTypes> {
1166                number: U256::from(1),
1167                beneficiary: BENEFICIARY,
1168                basefee: U256::ONE,
1169                gas_limit: U256::from(30_000_000),
1170                ..Default::default()
1171            },
1172            1,
1173        );
1174        if prewarm {
1175            env.version.features.remove(EvmFeatures::NONCE_CHECK | EvmFeatures::BALANCE_CHECK);
1176        }
1177        env
1178    }
1179
1180    fn sender(i: u8) -> Address {
1181        Address::repeat_byte(i)
1182    }
1183
1184    fn tx(from: u8, nonce: u64, to: Address, value: u64) -> Recovered<TransactionSigned> {
1185        tx_with_gas_limit(from, nonce, to, value, 100_000)
1186    }
1187
1188    fn tx_with_gas_limit(
1189        from: u8,
1190        nonce: u64,
1191        to: Address,
1192        value: u64,
1193        gas_limit: u64,
1194    ) -> Recovered<TransactionSigned> {
1195        tx_with_gas_price(from, nonce, to, value, gas_limit, GAS_PRICE)
1196    }
1197
1198    fn tx_with_gas_price(
1199        from: u8,
1200        nonce: u64,
1201        to: Address,
1202        value: u64,
1203        gas_limit: u64,
1204        gas_price: u128,
1205    ) -> Recovered<TransactionSigned> {
1206        let tx = Transaction::Legacy(TxLegacy {
1207            chain_id: Some(1),
1208            nonce,
1209            gas_price,
1210            gas_limit,
1211            to: TxKind::Call(to),
1212            value: U256::from(value),
1213            input: Bytes::new(),
1214        });
1215        Recovered::new_unchecked(
1216            TransactionSigned::new_unhashed(tx, Signature::test_signature()),
1217            sender(from),
1218        )
1219    }
1220
1221    fn parent_state() -> CacheDB<EmptyDB> {
1222        let mut db = CacheDB::new(EmptyDB::default());
1223        // The exhausted sender is funded separately.
1224        for i in [1, 2, 3, 4, 6, 7] {
1225            db.insert_account_info(
1226                &sender(i),
1227                AccountInfo { balance: U256::from(10u64.pow(18)), ..Default::default() },
1228            );
1229        }
1230        db.insert_account_info(
1231            &sender(EXHAUSTED_SENDER),
1232            AccountInfo {
1233                balance: U256::from(10u64.pow(18)),
1234                nonce: u64::MAX,
1235                ..Default::default()
1236            },
1237        );
1238        db.insert_account_info(
1239            &BENEFICIARY,
1240            AccountInfo { balance: U256::from(1), ..Default::default() },
1241        );
1242        db.insert_account_info(
1243            &RICH,
1244            AccountInfo { balance: U256::MAX >> 1, ..Default::default() },
1245        );
1246        db.insert_account_info(
1247            &SATURATED,
1248            AccountInfo { balance: U256::MAX, ..Default::default() },
1249        );
1250        db.insert_account_info(
1251            &sender(1).create(0),
1252            AccountInfo { balance: U256::from(12), ..Default::default() },
1253        );
1254        db.insert_account_info(&ZERO_BALANCE, AccountInfo { nonce: 1, ..Default::default() });
1255        db.insert_account_info(&EMPTY, AccountInfo::default());
1256        db.insert_account_info(
1257            &UNKNOWN_CODE,
1258            AccountInfo { code_hash: B256::repeat_byte(0xab), code: None, ..Default::default() },
1259        );
1260        for (address, code) in [
1261            (NOOP, bytes!("00")),
1262            (SELFDESTRUCTER, bytes!("73c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0ff")),
1263            (COUNTER, bytes!("5f546001015f5500")),
1264            (BENEFICIARY_BALANCE_READER, bytes!("41315f5500")),
1265            (SELF_BALANCE_READER, bytes!("475f5260205fa000")),
1266            (FORWARDER, bytes!("5f5f5f5f600573c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c05af100")),
1267            (TIMESTAMP_READER, bytes!("425f5500")),
1268            (GAS_LIMIT_READER, bytes!("455f5500")),
1269            (BENEFICIARY_READER, bytes!("415f5500")),
1270            (
1271                POOL_BENEFICIARY_CALLER,
1272                bytes!("5f5f5f5f5f7300000000000000000000000000000000000beef05af100"),
1273            ),
1274            (
1275                BENEFICIARY_BALANCE_BY_ADDRESS,
1276                bytes!("73bebebebebebebebebebebebebebebebebebebebe3100"),
1277            ),
1278            (PREVRANDAO_READER, bytes!("445f5500")),
1279        ] {
1280            let code = Bytecode::new_raw(code);
1281            db.insert_account_info(
1282                &address,
1283                AccountInfo {
1284                    balance: U256::from(12),
1285                    code_hash: code.hash_slow(),
1286                    code: Some(code),
1287                    ..Default::default()
1288                },
1289            );
1290        }
1291        db
1292    }
1293
1294    /// Speculative execution of `txs`, as by prewarm workers.
1295    struct Prewarm<'a> {
1296        txs: &'a [Recovered<TransactionSigned>],
1297        results: Results,
1298        evm: EvmFor<'static, EthEvmConfig>,
1299        recording: RecordingHandle,
1300    }
1301
1302    impl<'a> Prewarm<'a> {
1303        /// Executes the transactions at the indices in `order`, then refreshes stale results
1304        /// among them in block order.
1305        fn new(txs: &'a [Recovered<TransactionSigned>], order: &[usize]) -> Self {
1306            let recording = RecordingHandle::default();
1307            let evm = EthEvmConfig::mainnet()
1308                .evm_with_env(RecordingDb::new(parent_state(), recording.clone()), evm_env(true));
1309            let mut prewarm = Self { txs, results: Results::new(txs.len()), evm, recording };
1310            for &index in order {
1311                prewarm.run(index);
1312            }
1313            let mut refresh = order.to_vec();
1314            refresh.sort_unstable();
1315            for index in refresh {
1316                if prewarm.results.needs_refresh(index) {
1317                    prewarm.run(index);
1318                }
1319            }
1320            prewarm
1321        }
1322
1323        fn run(&mut self, index: usize) {
1324            let tx = &self.txs[index];
1325            let tx_env = EthEvmConfig::mainnet().tx_env(tx.clone());
1326            let speculative = SpeculativeTx::new(tx.signer(), tx.inner());
1327            drop(self.evm.take_state_cache());
1328            self.recording.start(Some((Arc::clone(self.results.predicted()), index)));
1329            let result = Evm::transact_with_inspector(
1330                &mut self.evm,
1331                &tx_env,
1332                BalanceTracker::new(self.recording.clone()),
1333            );
1334            let reads = self.recording.take_reads().unwrap();
1335            match result {
1336                Ok((mut balances, result)) => {
1337                    self.results.store(index, result, reads, &mut balances, speculative)
1338                }
1339                Err(_) => self.results.reject(index, reads),
1340            }
1341        }
1342
1343        /// Refreshes stale or missing results of transactions after `index`.
1344        fn refresh_after(&mut self, index: usize) {
1345            for index in index + 1..self.txs.len() {
1346                if self.results.needs_refresh(index) {
1347                    self.run(index);
1348                }
1349            }
1350        }
1351    }
1352
1353    /// Executes `txs` sequentially, committing valid prewarm results instead of executing.
1354    ///
1355    /// If `publish` is set, prewarm results are refreshed against the writes of executed
1356    /// transactions.
1357    fn execute(
1358        txs: &[Recovered<TransactionSigned>],
1359        mut prewarm: Option<&mut Prewarm<'_>>,
1360        publish: bool,
1361        beneficiary: Address,
1362    ) -> Execution {
1363        let config = EthEvmConfig::mainnet();
1364        let mut env = evm_env(false);
1365        env.block.beneficiary = beneficiary;
1366        let evm = config.evm_with_env(parent_state(), env);
1367        let ctx = EthBlockExecutionCtx {
1368            parent_hash: B256::ZERO,
1369            parent_beacon_block_root: None,
1370            ommers: &[],
1371            withdrawals: None,
1372            extra_data: Bytes::new(),
1373            tx_count_hint: None,
1374            slot_number: None,
1375        };
1376        let mut executor = config.block_executor_factory().create_executor(evm, ctx);
1377        let mut outcomes = Vec::new();
1378        let mut changes = Vec::new();
1379        for (index, tx) in txs.iter().enumerate() {
1380            let outcome = if let Some(prewarm) = prewarm.as_deref_mut() {
1381                prewarm.results.take_valid(index, executor.evm_mut().state_db_mut()).unwrap()
1382            } else {
1383                Err(ReuseMiss::Missing)
1384            };
1385            match outcome {
1386                Ok(result) => {
1387                    changes.push(changed_accounts(&result.pending_state));
1388                    executor
1389                        .commit_transaction(config.prewarm_tx_result(tx.inner(), result).unwrap())
1390                        .unwrap();
1391                    outcomes.push(Ok(()));
1392                }
1393                Err(miss) => {
1394                    let result = executor.execute_transaction_without_commit(tx.clone()).unwrap();
1395                    changes.push(changed_accounts(&result.result().pending_state));
1396                    if let Some(prewarm) = prewarm.as_deref_mut() &&
1397                        publish
1398                    {
1399                        prewarm.results.publish_executed(index, &result.result().pending_state);
1400                    }
1401                    executor.commit_transaction(result).unwrap();
1402                    if let Some(prewarm) = prewarm.as_deref_mut() &&
1403                        publish
1404                    {
1405                        prewarm.refresh_after(index);
1406                    }
1407                    outcomes.push(Err(miss));
1408                }
1409            }
1410        }
1411        let output = executor.finish().unwrap();
1412        Execution { receipts: output.result.receipts, bundle: output.state, changes, outcomes }
1413    }
1414
1415    /// Output of [`execute`].
1416    struct Execution {
1417        receipts: Vec<Receipt>,
1418        bundle: BundleState,
1419        /// Accounts each transaction changed, as the state root task sees them.
1420        changes: Vec<Vec<(Address, AccountInfo)>>,
1421        outcomes: Vec<Result<(), ReuseMiss>>,
1422    }
1423
1424    /// Returns the accounts in `state` whose info differs from their original info, which the
1425    /// state root task streams as updates.
1426    fn changed_accounts(state: &PendingState) -> Vec<(Address, AccountInfo)> {
1427        let mut changed = state
1428            .account_changes()
1429            .filter(|account| account.is_info_changed())
1430            .filter_map(|account| account.current.map(|info| (account.address, without_code(info))))
1431            .collect::<Vec<_>>();
1432        changed.sort_unstable_by_key(|(address, _)| *address);
1433        changed
1434    }
1435
1436    /// Database that fails every read.
1437    struct FailingDb;
1438
1439    impl DynDatabase for FailingDb {
1440        fn get_account(&mut self, _: &Address) -> DbResult<Option<AccountInfo>> {
1441            Err(evm2::DatabaseError::new(ProviderError::BestBlockNotFound, true))
1442        }
1443        fn get_code_by_hash(&mut self, _: &B256) -> DbResult<Bytecode> {
1444            Err(evm2::DatabaseError::new(ProviderError::BestBlockNotFound, true))
1445        }
1446        fn get_storage(&mut self, _: &Address, _: &U256) -> DbResult<U256> {
1447            Err(evm2::DatabaseError::new(ProviderError::BestBlockNotFound, true))
1448        }
1449        fn get_block_hash(&mut self, _: &U256) -> DbResult<B256> {
1450            Err(evm2::DatabaseError::new(ProviderError::BestBlockNotFound, true))
1451        }
1452    }
1453
1454    /// Returns whether the prewarm result of the first transaction can be reused, for
1455    /// transactions that block execution would reject.
1456    fn reusable(txs: &[Recovered<TransactionSigned>]) -> Result<(), ReuseMiss> {
1457        let prewarm = Prewarm::new(txs, &[0]);
1458        let mut state = parent_state();
1459        prewarm.results.take_valid(0, &mut state).unwrap().map(drop)
1460    }
1461
1462    /// Asserts that reusing prewarm results produces the same output as executing every
1463    /// transaction, and returns the reuse outcomes.
1464    fn reuse(txs: &[Recovered<TransactionSigned>]) -> Vec<Result<(), ReuseMiss>> {
1465        reuse_in(txs, &(0..txs.len()).collect::<Vec<_>>(), true)
1466    }
1467
1468    /// Like [`reuse`], prewarming only the transactions at the indices in `order`, and refreshing
1469    /// results against the writes of executed transactions if `publish` is set.
1470    fn reuse_in(
1471        txs: &[Recovered<TransactionSigned>],
1472        order: &[usize],
1473        publish: bool,
1474    ) -> Vec<Result<(), ReuseMiss>> {
1475        let reused = execute(txs, Some(&mut Prewarm::new(txs, order)), publish, BENEFICIARY);
1476        let expected = execute(txs, None, false, BENEFICIARY);
1477        assert_eq!(reused.receipts, expected.receipts);
1478        assert_eq!(reused.bundle, expected.bundle);
1479        assert_eq!(reused.changes, expected.changes);
1480        reused.outcomes
1481    }
1482
1483    /// Executes `txs` as pool transactions on top of the parent state, as by the txpool prewarm
1484    /// worker, in a block environment that differs from the actual one in the timestamp and
1485    /// prevrandao.
1486    fn pool_results(txs: &[Recovered<TransactionSigned>]) -> PoolResults<BaseEvmTypes> {
1487        let mut env = evm_env(true);
1488        env.block.beneficiary = POOL_BENEFICIARY;
1489        env.block.timestamp = U256::from(12);
1490        env.block.prevrandao = U256::from_be_bytes([1; 32]);
1491        let recording = RecordingHandle::default();
1492        let mut evm = EthEvmConfig::mainnet()
1493            .evm_with_env(RecordingDb::new(parent_state(), recording.clone()), env);
1494        let results = PoolResults::default();
1495        for tx in txs {
1496            let tx_env = EthEvmConfig::mainnet().tx_env(tx.clone());
1497            let speculative = SpeculativeTx::new(tx.signer(), tx.inner());
1498            drop(evm.take_state_cache());
1499            recording.start_recording_inner();
1500            let (mut balances, result) = Evm::transact_with_inspector(
1501                &mut evm,
1502                &tx_env,
1503                BalanceTracker::new(recording.clone()),
1504            )
1505            .unwrap();
1506            let reads = recording.take_reads().unwrap();
1507            results.store(*tx.tx_hash(), result, reads, &mut balances, speculative);
1508        }
1509        results
1510    }
1511
1512    /// Asserts that reusing results of the `pool` transactions produces the same output as
1513    /// executing every transaction in a block with `beneficiary`, and returns the reuse outcomes.
1514    fn reuse_pool(
1515        txs: &[Recovered<TransactionSigned>],
1516        pool: &[Recovered<TransactionSigned>],
1517        beneficiary: Address,
1518    ) -> Vec<Result<(), ReuseMiss>> {
1519        let differs = BlockEnvFields { timestamp: true, prevrandao: true, ..Default::default() };
1520        reuse_pool_with(txs, pool, beneficiary, differs)
1521    }
1522
1523    /// Like [`reuse_pool`], for a block environment that differs from the predicted one in
1524    /// `differs`.
1525    fn reuse_pool_with(
1526        txs: &[Recovered<TransactionSigned>],
1527        pool: &[Recovered<TransactionSigned>],
1528        beneficiary: Address,
1529        differs: BlockEnvFields,
1530    ) -> Vec<Result<(), ReuseMiss>> {
1531        let seeds = PoolSeeds::new(Arc::new(pool_results(pool)), beneficiary, differs);
1532        let mut prewarm = Prewarm::new(txs, &[]);
1533        for (index, tx) in txs.iter().enumerate() {
1534            prewarm.results.seed(index, tx.tx_hash(), &seeds);
1535        }
1536        let reused = execute(txs, Some(&mut prewarm), false, beneficiary);
1537        let expected = execute(txs, None, false, beneficiary);
1538        assert_eq!(reused.receipts, expected.receipts);
1539        assert_eq!(reused.bundle, expected.bundle);
1540        assert_eq!(reused.changes, expected.changes);
1541        reused.outcomes
1542    }
1543
1544    #[test]
1545    fn reuses_independent_transactions() {
1546        let txs = [tx(1, 0, sender(2), 1), tx(3, 0, Address::repeat_byte(0xaa), 1)];
1547        assert_eq!(reuse(&txs), [Ok(()), Ok(())]);
1548    }
1549
1550    #[test]
1551    fn rebases_unobserved_balances() {
1552        // The last transaction's caller and recipient were credited by earlier ones.
1553        let txs = [tx(1, 0, NOOP, 1), tx(1, 1, sender(2), 1), tx(2, 0, NOOP, 1)];
1554        assert_eq!(reuse(&txs), [Ok(()), Ok(()), Ok(())]);
1555    }
1556
1557    #[test]
1558    fn reuses_predicted_writes() {
1559        let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1), tx(1, 1, COUNTER, 1)];
1560        assert_eq!(reuse(&txs), [Ok(()), Ok(()), Ok(())]);
1561        // Results executed before the transactions they depend on are executed again.
1562        assert_eq!(reuse_in(&txs, &[2, 1, 0], true), [Ok(()), Ok(()), Ok(())]);
1563    }
1564
1565    #[test]
1566    fn refreshes_against_executed_writes() {
1567        let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1), tx(1, 1, sender(3), 1)];
1568        assert_eq!(reuse_in(&txs, &[1, 2], true), [Err(ReuseMiss::Missing), Ok(()), Ok(())]);
1569    }
1570
1571    #[test]
1572    fn rebases_original_balances() {
1573        // The first transaction leaves the forwarder 5 wei more, and the second forwards 5 wei, so
1574        // its rebased balance equals its speculative balance before the second transaction.
1575        let txs = [tx(1, 0, FORWARDER, 10), tx(2, 0, FORWARDER, 0)];
1576        assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Ok(())]);
1577    }
1578
1579    #[test]
1580    fn rejects_stale_storage() {
1581        let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1)];
1582        assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]);
1583    }
1584
1585    #[test]
1586    fn rejects_stale_callers() {
1587        let txs = [tx(1, 0, sender(2), 1), tx(1, 1, sender(3), 1)];
1588        assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]);
1589    }
1590
1591    #[test]
1592    fn rejects_observed_balances() {
1593        let txs = [
1594            tx(1, 0, SELF_BALANCE_READER, 1),
1595            tx(2, 0, SELF_BALANCE_READER, 1),
1596            tx(3, 0, BENEFICIARY_BALANCE_READER, 1),
1597        ];
1598        assert_eq!(
1599            reuse_in(&txs, &[1, 2], false),
1600            [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale), Err(ReuseMiss::Stale)]
1601        );
1602    }
1603
1604    #[test]
1605    fn rejects_lower_sender_balances() {
1606        let txs = [tx(1, 0, FORWARDER, 0), tx(2, 0, FORWARDER, 0), tx(3, 0, FORWARDER, 0)];
1607        // The forwarder can send twice from its speculative balance, but not three times.
1608        assert_eq!(
1609            reuse_in(&txs, &[1, 2], false),
1610            [Err(ReuseMiss::Missing), Ok(()), Err(ReuseMiss::Stale)]
1611        );
1612    }
1613
1614    #[test]
1615    fn reuses_pool_results() {
1616        // The last transaction's caller was credited by the first one.
1617        let txs = [tx(1, 0, sender(2), 1), tx(3, 0, COUNTER, 1), tx(2, 0, GAS_LIMIT_READER, 1)];
1618        assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Ok(()), Ok(()), Ok(())]);
1619        // Transactions missing from the pool are executed.
1620        assert_eq!(
1621            reuse_pool(&txs, &txs[1..], BENEFICIARY),
1622            [Err(ReuseMiss::Missing), Ok(()), Ok(())]
1623        );
1624    }
1625
1626    #[test]
1627    fn rejects_stale_pool_results() {
1628        // Test transactions are hashed without their sender.
1629        let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 2)];
1630        assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Ok(()), Err(ReuseMiss::Stale)]);
1631        // The beneficiary must exist to be credited.
1632        let missing = Address::repeat_byte(0xbf);
1633        assert_eq!(reuse_pool(&txs, &txs, missing), [Err(ReuseMiss::Stale), Err(ReuseMiss::Stale)]);
1634    }
1635
1636    #[test]
1637    fn rejects_zero_credit_to_empty_beneficiary() {
1638        // Executing the credit deletes the empty beneficiary, which detached state cannot express.
1639        let txs = [tx_with_gas_price(1, 0, COUNTER, 1, 100_000, 1)];
1640        assert_eq!(reuse_pool(&txs, &txs, EMPTY), [Err(ReuseMiss::Stale)]);
1641    }
1642
1643    #[test]
1644    fn rejects_pool_results_observing_block_env() {
1645        let txs = [
1646            tx(1, 0, TIMESTAMP_READER, 1),
1647            tx(2, 0, BENEFICIARY_READER, 1),
1648            tx(3, 0, POOL_BENEFICIARY, 1),
1649            tx(4, 0, POOL_BENEFICIARY_CALLER, 1),
1650            tx(6, 0, BENEFICIARY, 1),
1651            tx(7, 0, PREVRANDAO_READER, 1),
1652        ];
1653        assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Err(ReuseMiss::Missing); 6]);
1654
1655        let txs = [tx(1, 0, GAS_LIMIT_READER, 1)];
1656        let differs = BlockEnvFields { gas_limit: true, ..Default::default() };
1657        assert_eq!(reuse_pool_with(&txs, &txs, BENEFICIARY, differs), [Err(ReuseMiss::Missing)]);
1658    }
1659
1660    #[test]
1661    fn seeds_only_unexecuted_transactions() {
1662        let txs = [tx(1, 0, COUNTER, 1)];
1663        let differs = BlockEnvFields::default();
1664        let seeds = PoolSeeds::new(Arc::new(pool_results(&txs)), BENEFICIARY, differs);
1665        assert!(Prewarm::new(&txs, &[]).results.seed(0, txs[0].tx_hash(), &seeds).is_some());
1666        assert!(Prewarm::new(&txs, &[0]).results.seed(0, txs[0].tx_hash(), &seeds).is_none());
1667    }
1668
1669    #[test]
1670    fn rejects_pool_results_overflowing_the_beneficiary() {
1671        let txs = [tx(1, 0, COUNTER, 1)];
1672        assert_eq!(reuse_pool(&txs, &txs, SATURATED), [Err(ReuseMiss::Stale)]);
1673    }
1674
1675    /// Stores the recorded result of `tx` in `results` again under `hash`, after `modify` changed
1676    /// it and the read of the pool beneficiary.
1677    fn store_again(
1678        results: &PoolResults<BaseEvmTypes>,
1679        tx: &Recovered<TransactionSigned>,
1680        hash: B256,
1681        modify: impl FnOnce(&mut TxResultWithState<BaseEvmTypes>, &mut AccountRead),
1682    ) -> bool {
1683        let recorded = results.get(tx.tx_hash()).unwrap();
1684        let mut result = recorded.result.clone();
1685        result.pending_state.insert_account(POOL_BENEFICIARY, None, Some(AccountInfo::default()));
1686        let mut beneficiary = AccountRead {
1687            address: POOL_BENEFICIARY,
1688            info: None,
1689            balance: BalanceDependency::None,
1690            predicted: false,
1691        };
1692        modify(&mut result, &mut beneficiary);
1693        let mut reads = recorded.reads.clone();
1694        reads.accounts.push(beneficiary);
1695        let speculative = SpeculativeTx::new(tx.signer(), tx.inner());
1696        results.store(hash, result, reads, &mut BalanceTracker::default(), speculative)
1697    }
1698
1699    #[test]
1700    fn bounds_pool_results() {
1701        let txs = [tx(1, 0, COUNTER, 1)];
1702        let results = pool_results(&txs);
1703        assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1704        // Deployed code counts towards the bound.
1705        assert!(!store_again(&results, &txs[0], B256::with_last_byte(1), |result, _| {
1706            let original = result.pending_state.account_info(&COUNTER).unwrap().clone();
1707            let current = original
1708                .clone()
1709                .with_code(Bytecode::new_raw(vec![0; MAX_POOL_RESULT_BYTES].into()));
1710            result.pending_state.insert_account(COUNTER, Some(original), Some(current));
1711        }));
1712    }
1713
1714    #[test]
1715    fn rejects_recorded_pool_hashes() {
1716        let txs = [tx(1, 0, COUNTER, 1)];
1717        let results = pool_results(&txs);
1718        assert!(!store_again(&results, &txs[0], *txs[0].tx_hash(), |_, _| {}));
1719        assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1720    }
1721
1722    #[test]
1723    fn rejects_saturated_pool_beneficiaries() {
1724        let txs = [tx(1, 0, COUNTER, 1)];
1725        let results = pool_results(&txs);
1726        // Crediting a saturated balance leaves it unchanged.
1727        assert!(!store_again(&results, &txs[0], B256::ZERO, |result, beneficiary| {
1728            beneficiary.info = Some(AccountInfo { balance: U256::MAX, ..Default::default() });
1729            result.pending_state.insert_account(POOL_BENEFICIARY, None, beneficiary.info.clone());
1730        }));
1731        assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1732    }
1733
1734    #[test]
1735    fn rejects_pool_results_running_out_of_gas() {
1736        // Enough gas to read the balance of the warm beneficiary in the block, but not of the cold
1737        // one during speculation.
1738        let txs = [tx_with_gas_limit(1, 0, BENEFICIARY_BALANCE_BY_ADDRESS, 0, 21_500)];
1739        assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Err(ReuseMiss::Missing)]);
1740    }
1741
1742    #[test]
1743    fn rejects_invalid_transactions() {
1744        assert_eq!(reusable(&[tx(1, 0, NOOP, 0)]), Ok(()));
1745        // The caller cannot pay for the value.
1746        assert_eq!(reusable(&[tx(1, 0, NOOP, 10u64.pow(19))]), Err(ReuseMiss::Stale));
1747        // The caller's nonce differs.
1748        assert_eq!(reusable(&[tx(1, 1, NOOP, 0)]), Err(ReuseMiss::Stale));
1749        // The caller's nonce cannot be incremented.
1750        assert_eq!(reusable(&[tx(EXHAUSTED_SENDER, u64::MAX, NOOP, 0)]), Err(ReuseMiss::Stale));
1751        // Speculative execution fails.
1752        assert_eq!(
1753            reusable(&[tx_with_gas_limit(1, 0, NOOP, 0, 40_000_000)]),
1754            Err(ReuseMiss::Stale)
1755        );
1756        // The most the caller may spend overflows, though the effective fee does not.
1757        let overflowing = Transaction::Eip1559(TxEip1559 {
1758            chain_id: 1,
1759            max_fee_per_gas: u128::MAX,
1760            gas_limit: 100_000,
1761            to: TxKind::Call(NOOP),
1762            ..Default::default()
1763        });
1764        let overflowing = Recovered::new_unchecked(
1765            TransactionSigned::new_unhashed(overflowing, Signature::test_signature()),
1766            sender(1),
1767        );
1768        assert_eq!(reusable(&[overflowing]), Err(ReuseMiss::Stale));
1769    }
1770
1771    #[test]
1772    fn keeps_stale_results_unchanged() {
1773        let txs = [tx(1, 0, NOOP, 0)];
1774        let prewarm = Prewarm::new(&txs, &[0]);
1775        // The caller's balance can be rebased, but the callee changed.
1776        let mut db = parent_state();
1777        db.insert_account_info(
1778            &sender(1),
1779            AccountInfo { balance: U256::from(2 * 10u64.pow(18)), ..Default::default() },
1780        );
1781        let mut noop = DynDatabase::get_account(&mut db, &NOOP).unwrap().unwrap();
1782        noop.nonce = 1;
1783        db.insert_account_info(&NOOP, noop);
1784        let mut stale = db;
1785        assert_eq!(
1786            prewarm.results.take_valid(0, &mut stale).unwrap().map(drop),
1787            Err(ReuseMiss::Stale)
1788        );
1789
1790        let mut state = parent_state();
1791        let result = prewarm.results.take_valid(0, &mut state).unwrap().unwrap();
1792        let fee = U256::from(21_000 * GAS_PRICE);
1793        assert_eq!(
1794            result.pending_state.account_info(&sender(1)).unwrap().balance,
1795            U256::from(10u64.pow(18)) - fee
1796        );
1797    }
1798
1799    #[test]
1800    fn keeps_results_on_database_errors() {
1801        let txs = [tx(1, 0, NOOP, 0)];
1802        let prewarm = Prewarm::new(&txs, &[0]);
1803        assert!(matches!(
1804            prewarm.results.take_valid(0, &mut FailingDb),
1805            Err(err) if err.to_string() == ProviderError::BestBlockNotFound.to_string()
1806        ));
1807        let mut state = parent_state();
1808        assert_eq!(prewarm.results.take_valid(0, &mut state).unwrap().map(drop), Ok(()));
1809    }
1810
1811    #[test]
1812    fn rejects_unknown_code() {
1813        assert_eq!(reuse_in(&[tx(1, 0, UNKNOWN_CODE, 0)], &[0], false), [Err(ReuseMiss::Stale)]);
1814    }
1815
1816    #[test]
1817    fn rejects_rebasing_unbounded_or_zero_balances() {
1818        let rebased = [tx(1, 0, sender(4), 1), tx(2, 0, sender(4), 1)];
1819        assert_eq!(reuse_in(&rebased, &[1], false), [Err(ReuseMiss::Missing), Ok(())]);
1820        for txs in [
1821            [tx(1, 0, RICH, 1), tx(2, 0, RICH, 1)],
1822            [tx(1, 0, ZERO_BALANCE, 1), tx(2, 0, ZERO_BALANCE, 1)],
1823            // Any unbounded balance read makes every balance exact.
1824            [tx(1, 0, sender(4), 1), tx(4, 0, RICH, 1)],
1825        ] {
1826            assert_eq!(
1827                reuse_in(&txs, &[1], false),
1828                [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]
1829            );
1830        }
1831    }
1832
1833    #[test]
1834    fn preserves_account_lifecycle_and_rejects_rebasing_it() {
1835        let creation = Recovered::new_unchecked(
1836            TransactionSigned::new_unhashed(
1837                Transaction::Legacy(TxLegacy {
1838                    chain_id: Some(1),
1839                    gas_price: GAS_PRICE,
1840                    gas_limit: 100_000,
1841                    to: TxKind::Create,
1842                    input: bytes!("60015f5500"),
1843                    ..Default::default()
1844                }),
1845                Signature::test_signature(),
1846            ),
1847            sender(1),
1848        );
1849        for (transaction, address) in
1850            [(creation, sender(1).create(0)), (tx(1, 0, SELFDESTRUCTER, 0), SELFDESTRUCTER)]
1851        {
1852            let txs = [transaction];
1853            assert_eq!(reuse(&txs), [Ok(())]);
1854            let prewarm = Prewarm::new(&txs, &[0]);
1855            // An observed SELFDESTRUCT balance is exact; creation also cannot have its
1856            // lifecycle flags reset by metadata replacement during rebasing.
1857            let mut db = parent_state();
1858            let mut info = DynDatabase::get_account(&mut db, &address).unwrap().unwrap();
1859            info.balance += U256::ONE;
1860            db.insert_account_info(&address, info);
1861            assert_eq!(
1862                prewarm.results.take_valid(0, &mut db).unwrap().map(drop),
1863                Err(ReuseMiss::Stale)
1864            );
1865            let result = prewarm.results.take_valid(0, &mut parent_state()).unwrap().unwrap();
1866            let account =
1867                result.pending_state.account_changes().find(|a| a.address == address).unwrap();
1868            if address == sender(1).create(0) {
1869                assert!(account.created);
1870                assert_eq!(account.storage.slots[&U256::ZERO].value.current, U256::ONE);
1871            } else {
1872                // EIP-6780 retains pre-existing contracts after SELFDESTRUCT.
1873                assert_eq!(account.current.unwrap().balance, U256::ZERO);
1874            }
1875        }
1876    }
1877
1878    #[test]
1879    fn rejects_results_after_failed_observer_reads() {
1880        let reads = ReadSet {
1881            accounts: vec![AccountRead {
1882                address: sender(1),
1883                info: Some(AccountInfo {
1884                    balance: U256::from(10u64.pow(18)),
1885                    ..Default::default()
1886                }),
1887                balance: BalanceDependency::None,
1888                predicted: false,
1889            }],
1890            ..Default::default()
1891        };
1892        let mut balances = BalanceTracker { read_failed: true, ..Default::default() };
1893        let tx = tx(1, 0, NOOP, 0);
1894        assert!(reads.finish(&mut balances, SpeculativeTx::new(tx.signer(), tx.inner())).is_err());
1895    }
1896}