1use 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
49const MAX_REBASED_BALANCE: U256 = U256::from_limbs([u64::MAX, u64::MAX, 0, 0]);
51
52pub(crate) const POOL_BENEFICIARY: Address = address!("0x00000000000000000000000000000000000beef0");
56
57const MAX_POOL_RESULT_BYTES: usize = 64 << 20;
59
60const 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
73pub(crate) const fn cfg_reuses_prewarm_results(version: &Version) -> bool {
76 !version.feature(EvmFeatures::EIP8037)
77}
78
79#[derive_where(Debug)]
81pub(crate) struct PrewarmResults<T: EvmTypes> {
82 slots: Box<[Mutex<ResultSlot<T>>]>,
83 predicted: Arc<PredictedState>,
85}
86
87#[derive_where(Debug)]
89enum ResultSlot<T: EvmTypes> {
90 Empty,
92 Ready(Box<PrewarmedTx<T>>),
94 Unusable(ReadSet),
96 Executed,
99}
100
101impl<T: EvmTypes> PrewarmResults<T> {
102 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 pub(super) const fn predicted(&self) -> &Arc<PredictedState> {
112 &self.predicted
113 }
114
115 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 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 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 pub(super) fn reject(&self, index: usize, mut reads: ReadSet) {
170 for read in &mut reads.accounts {
172 read.balance = BalanceDependency::Exact;
173 }
174 self.set(index, |_| ResultSlot::Unusable(reads));
175 }
176
177 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 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 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 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 if rebased {
250 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub(crate) enum ReuseMiss {
261 Missing,
263 Stale,
265}
266
267#[derive_where(Debug)]
269struct PrewarmedTx<T: EvmTypes> {
270 result: TxResultWithState<T>,
271 reads: ReadSet,
272 credit: Option<BeneficiaryCredit>,
274}
275
276#[derive(Debug)]
278struct BeneficiaryCredit {
279 beneficiary: Address,
280 amount: U256,
281}
282
283impl<T: EvmTypes> PrewarmedTx<T> {
284 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 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 if current.is_empty() {
335 return Ok(None)
336 }
337 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
353enum BalanceDependency {
354 None,
356 AtLeast(U256),
358 Exact,
360}
361
362#[derive(Debug, Clone)]
364struct AccountRead {
365 address: Address,
366 info: Option<AccountInfo>,
368 balance: BalanceDependency,
369 predicted: bool,
371}
372
373impl AccountRead {
374 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 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
407enum AccountMatch {
408 Same,
410 Rebase { speculative: U256, current: U256 },
413 Differs,
415}
416
417#[derive(Debug, Clone)]
419struct StorageRead {
420 address: Address,
421 slot: U256,
422 value: U256,
423 predicted: bool,
425}
426
427#[derive(Debug, Default, Clone)]
429pub(crate) struct ReadSet {
430 accounts: Vec<AccountRead>,
431 storage: Vec<StorageRead>,
432 unknown_code: bool,
434 block_env: BlockEnvFields,
436}
437
438impl ReadSet {
439 fn finish(mut self, balances: &mut BalanceTracker, tx: SpeculativeTx) -> Result<Self, Self> {
445 let SpeculativeTx { caller, nonce, max_balance_spending } = tx;
446 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 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 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 let BalanceTracker { observed, senders, .. } = balances;
479 observed.sort_unstable();
480 senders.sort_unstable();
481 for read in &mut self.accounts {
482 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 fn position(&self, address: Address) -> Option<usize> {
503 self.accounts.binary_search_by_key(&address, |read| read.address).ok()
504 }
505
506 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#[derive(Debug)]
523pub(crate) struct SpeculativeTx {
524 pub(crate) caller: Address,
526 pub(crate) nonce: u64,
528 pub(crate) max_balance_spending: Option<U256>,
530}
531
532type Writes<T> = Vec<(usize, T)>;
534
535#[derive(Debug, Default)]
540pub(super) struct PredictedState {
541 accounts: DashMap<Address, Writes<Option<AccountInfo>>, DefaultHashBuilder>,
543 storage: DashMap<(Address, U256), Writes<U256>, DefaultHashBuilder>,
544 cleared: DashMap<Address, Writes<()>, DefaultHashBuilder>,
546 code: DashMap<B256, Bytecode, DefaultHashBuilder>,
548 generation: AtomicUsize,
550}
551
552impl PredictedState {
553 pub(super) fn generation(&self) -> usize {
556 self.generation.load(Ordering::Relaxed)
557 }
558
559 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 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 fn code(&self, code_hash: B256) -> Option<Bytecode> {
578 self.code.get(&code_hash).map(|code| code.clone())
579 }
580
581 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 fn publish_accounts(&self, index: usize, state: &PendingState) {
593 self.update(index, state.account_changes(), true);
594 }
595
596 fn retract(&self, index: usize, state: &PendingState) {
598 self.update(index, state.account_changes(), false);
599 }
600
601 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
639fn 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
651fn latest<T>(writes: &[(usize, T)], index: usize) -> Option<&(usize, T)> {
653 writes[..writes.partition_point(|(i, _)| *i < index)].last()
654}
655
656#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
659pub(crate) struct BlockEnvFields {
660 pub(crate) beneficiary: bool,
663 pub(crate) timestamp: bool,
665 pub(crate) prevrandao: bool,
667 pub(crate) gas_limit: bool,
669}
670
671impl BlockEnvFields {
672 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#[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#[derive_where(Debug)]
696struct StoredPoolTxs<T: EvmTypes> {
697 txs: B256Map<Arc<PoolTx<T>>>,
698 bytes: usize,
700}
701
702impl<T: EvmTypes> PoolResults<T> {
703 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 let Some(beneficiary) = result
724 .pending_state
725 .account_changes()
726 .find(|account| account.address == POOL_BENEFICIARY)
727 else {
728 return false
729 };
730 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 pub(crate) fn get(&self, hash: &B256) -> Option<Arc<PoolTx<T>>> {
755 self.stored.read().txs.get(hash).cloned()
756 }
757
758 pub(crate) fn is_empty(&self) -> bool {
760 self.stored.read().txs.is_empty()
761 }
762}
763
764fn 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#[derive_where(Debug)]
781pub(crate) struct PoolTx<T: EvmTypes> {
782 result: TxResultWithState<T>,
783 reads: ReadSet,
784 credit: U256,
786}
787
788impl<T: EvmTypes> PoolTx<T> {
789 pub(super) const fn state(&self) -> &PendingState {
791 &self.result.pending_state
792 }
793
794 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#[derive_where(Debug, Clone)]
812pub(crate) struct PoolSeeds<T: EvmTypes> {
813 results: Arc<PoolResults<T>>,
814 beneficiary: Address,
816 differs: BlockEnvFields,
818}
819
820impl<T: EvmTypes> PoolSeeds<T> {
821 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#[derive(Debug)]
834pub(crate) struct RecordingDb<DB: Database> {
835 inner: Db<DB>,
836 recording: RecordingHandle,
837}
838
839#[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 pub(super) fn start(&self, predicted: Option<(Arc<PredictedState>, usize)>) {
852 *self.0.borrow_mut() = RecordingState { reads: Some(ReadSet::default()), predicted };
853 }
854
855 pub(crate) fn start_recording_inner(&self) {
857 self.start(None);
858 }
859
860 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 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
944fn without_code(info: &AccountInfo) -> AccountInfo {
946 let mut info = info.clone();
947 info.code = None;
948 info
949}
950
951impl SpeculativeTx {
952 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#[derive(Debug, Default)]
969pub(crate) struct BalanceTracker {
970 recording: RecordingHandle,
971 read_failed: bool,
972 observed: Vec<Address>,
974 senders: Vec<(Address, U256)>,
976 block_env: BlockEnvFields,
978}
979
980impl BalanceTracker {
981 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 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 const NOOP: Address = Address::repeat_byte(0xc0);
1124 const COUNTER: Address = Address::repeat_byte(0xc1);
1126 const BENEFICIARY_BALANCE_READER: Address = Address::repeat_byte(0xc2);
1128 const SELF_BALANCE_READER: Address = Address::repeat_byte(0xc3);
1130 const FORWARDER: Address = Address::repeat_byte(0xc4);
1132 const RICH: Address = Address::repeat_byte(0xc5);
1134 const ZERO_BALANCE: Address = Address::repeat_byte(0xc6);
1136 const UNKNOWN_CODE: Address = Address::repeat_byte(0xc7);
1138 const TIMESTAMP_READER: Address = Address::repeat_byte(0xc8);
1140 const GAS_LIMIT_READER: Address = Address::repeat_byte(0xc9);
1142 const BENEFICIARY_READER: Address = Address::repeat_byte(0xca);
1144 const POOL_BENEFICIARY_CALLER: Address = Address::repeat_byte(0xcb);
1146 const BENEFICIARY_BALANCE_BY_ADDRESS: Address = Address::repeat_byte(0xcc);
1148 const PREVRANDAO_READER: Address = Address::repeat_byte(0xcd);
1150 const SATURATED: Address = Address::repeat_byte(0xce);
1152 const EXHAUSTED_SENDER: u8 = 5;
1154 const SELFDESTRUCTER: Address = Address::repeat_byte(0xcf);
1156 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 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 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 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 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 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 struct Execution {
1417 receipts: Vec<Receipt>,
1418 bundle: BundleState,
1419 changes: Vec<Vec<(Address, AccountInfo)>>,
1421 outcomes: Vec<Result<(), ReuseMiss>>,
1422 }
1423
1424 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 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 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 fn reuse(txs: &[Recovered<TransactionSigned>]) -> Vec<Result<(), ReuseMiss>> {
1465 reuse_in(txs, &(0..txs.len()).collect::<Vec<_>>(), true)
1466 }
1467
1468 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 assert_eq!(reusable(&[tx(1, 0, NOOP, 10u64.pow(19))]), Err(ReuseMiss::Stale));
1747 assert_eq!(reusable(&[tx(1, 1, NOOP, 0)]), Err(ReuseMiss::Stale));
1749 assert_eq!(reusable(&[tx(EXHAUSTED_SENDER, u64::MAX, NOOP, 0)]), Err(ReuseMiss::Stale));
1751 assert_eq!(
1753 reusable(&[tx_with_gas_limit(1, 0, NOOP, 0, 40_000_000)]),
1754 Err(ReuseMiss::Stale)
1755 );
1756 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 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 [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 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 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}