1use alloy_consensus::InMemorySize;
23use alloy_primitives::{
24 address,
25 map::{B256Map, DefaultHashBuilder, Entry},
26 Address, B256, U256,
27};
28use dashmap::DashMap;
29use parking_lot::{Mutex, RwLock};
30use revm::{
31 bytecode::opcode,
32 context::{
33 result::{HaltReason, InvalidTransaction, ResultAndState},
34 Block, CfgEnv, ContextTr, JournalTr,
35 },
36 inspector::JournalExt,
37 interpreter::{
38 instructions::utility::IntoAddress, interpreter_types::Jumps, CallInputs, CallOutcome,
39 CreateInputs, CreateOutcome, InstructionResult, Interpreter, SuccessOrHalt,
40 },
41 primitives::{StorageKey, StorageValue, KECCAK_EMPTY},
42 state::{Account, AccountInfo, Bytecode, EvmState, EvmStorageSlot},
43 Database, Inspector,
44};
45use std::sync::{
46 atomic::{AtomicUsize, Ordering},
47 Arc,
48};
49
50const MAX_REBASED_BALANCE: U256 = U256::from_limbs([u64::MAX, u64::MAX, 0, 0]);
52
53pub(crate) const POOL_BENEFICIARY: Address = address!("0x00000000000000000000000000000000000beef0");
57
58const MAX_POOL_RESULT_BYTES: usize = 64 << 20;
60
61pub(crate) const fn cfg_reuses_prewarm_results<Spec>(cfg_env: &CfgEnv<Spec>) -> bool {
64 !cfg_env.enable_amsterdam_eip8037
65}
66
67#[derive(Debug)]
69pub(crate) struct PrewarmResults<H> {
70 slots: Box<[Mutex<ResultSlot<H>>]>,
71 predicted: Arc<PredictedState>,
73}
74
75#[derive(Debug)]
77enum ResultSlot<H> {
78 Empty,
80 Ready(Box<PrewarmedTx<H>>),
82 Unusable(ReadSet),
84 Executed,
87}
88
89impl<H> PrewarmResults<H> {
90 pub(crate) fn new(transaction_count: usize) -> Self {
92 Self {
93 slots: (0..transaction_count).map(|_| Mutex::new(ResultSlot::Empty)).collect(),
94 predicted: Arc::default(),
95 }
96 }
97
98 pub(super) const fn predicted(&self) -> &Arc<PredictedState> {
100 &self.predicted
101 }
102
103 pub(super) fn store(
106 &self,
107 index: usize,
108 result: ResultAndState<H>,
109 reads: ReadSet,
110 balances: &mut BalanceTracker,
111 tx: SpeculativeTx,
112 ) {
113 match reads.finish(balances, tx) {
114 Ok(reads) => self.set(index, |predicted| {
115 predicted.publish(index, &result.state);
116 ResultSlot::Ready(Box::new(PrewarmedTx { result, reads, credit: None }))
117 }),
118 Err(reads) => self.reject(index, reads),
119 }
120 }
121
122 pub(super) fn seed(
128 &self,
129 index: usize,
130 hash: &B256,
131 seeds: &PoolSeeds<H>,
132 ) -> Option<Arc<PoolTx<H>>>
133 where
134 H: Clone,
135 {
136 let tx = seeds.results.get(hash)?;
137 if tx.reads.block_env.intersects(seeds.differs) ||
139 tx.reads.position(seeds.beneficiary).is_some()
140 {
141 return None
142 }
143 let mut slot = self.slots.get(index)?.lock();
144 if !matches!(*slot, ResultSlot::Empty) {
145 return None
146 }
147 self.predicted.publish(index, &tx.result.state);
148 *slot = ResultSlot::Ready(Box::new(PrewarmedTx {
149 result: tx.result.clone(),
150 reads: tx.reads.clone(),
151 credit: Some(BeneficiaryCredit { beneficiary: seeds.beneficiary, amount: tx.credit }),
152 }));
153 Some(tx)
154 }
155
156 pub(super) fn reject(&self, index: usize, mut reads: ReadSet) {
158 for read in &mut reads.accounts {
160 read.balance = BalanceDependency::Exact;
161 }
162 self.set(index, |_| ResultSlot::Unusable(reads));
163 }
164
165 pub(crate) fn publish_executed(&self, index: usize, state: &EvmState) {
168 let Some(slot) = self.slots.get(index) else { return };
169 let mut slot = slot.lock();
170 self.predicted.publish(index, state);
172 if let ResultSlot::Ready(previous) = core::mem::replace(&mut *slot, ResultSlot::Executed) {
173 self.predicted.retract_unwritten(index, &previous.result.state, state);
174 }
175 self.predicted.generation.fetch_add(1, Ordering::Relaxed);
176 }
177
178 fn set(&self, index: usize, f: impl FnOnce(&PredictedState) -> ResultSlot<H>) {
181 let Some(slot) = self.slots.get(index) else { return };
182 let mut slot = slot.lock();
183 match &*slot {
184 ResultSlot::Ready(previous) => self.predicted.retract(index, &previous.result.state),
185 ResultSlot::Executed => return,
186 ResultSlot::Empty | ResultSlot::Unusable(_) => {}
187 }
188 *slot = f(&self.predicted);
189 }
190
191 pub(super) fn needs_refresh(&self, index: usize) -> bool {
194 let Some(slot) = self.slots.get(index) else { return false };
195 let slot = slot.lock();
196 let reads = match &*slot {
197 ResultSlot::Empty => return true,
198 ResultSlot::Ready(tx) => &tx.reads,
199 ResultSlot::Unusable(reads) => reads,
200 ResultSlot::Executed => return false,
201 };
202 !reads.matches(&self.predicted, index)
203 }
204
205 pub(crate) fn take_valid<DB: Database>(
211 &self,
212 index: usize,
213 db: &mut DB,
214 ) -> Result<Result<ResultAndState<H>, ReuseMiss>, DB::Error> {
215 let Some(slot) = self.slots.get(index) else { return Ok(Err(ReuseMiss::Missing)) };
216 let mut slot = slot.lock();
217 let mut tx = match core::mem::replace(&mut *slot, ResultSlot::Executed) {
218 ResultSlot::Ready(tx) => tx,
219 ResultSlot::Unusable(reads) => {
220 *slot = ResultSlot::Unusable(reads);
221 return Ok(Err(ReuseMiss::Stale))
222 }
223 previous @ (ResultSlot::Empty | ResultSlot::Executed) => {
224 *slot = previous;
225 return Ok(Err(ReuseMiss::Missing))
226 }
227 };
228 let rebased = match tx.validate(db) {
229 Ok(Some(rebased)) => rebased,
230 result => {
231 *slot = ResultSlot::Ready(tx);
232 return result.map(|_| Err(ReuseMiss::Stale))
233 }
234 };
235 if rebased {
237 self.predicted.publish_accounts(index, &tx.result.state);
239 self.predicted.generation.fetch_add(1, Ordering::Relaxed);
240 }
241 Ok(Ok(tx.result))
242 }
243}
244
245#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub(crate) enum ReuseMiss {
248 Missing,
250 Stale,
252}
253
254#[derive(Debug)]
256struct PrewarmedTx<H> {
257 result: ResultAndState<H>,
258 reads: ReadSet,
259 credit: Option<BeneficiaryCredit>,
261}
262
263#[derive(Debug)]
265struct BeneficiaryCredit {
266 beneficiary: Address,
267 amount: U256,
268}
269
270impl<H> PrewarmedTx<H> {
271 fn validate<DB: Database>(&mut self, db: &mut DB) -> Result<Option<bool>, DB::Error> {
277 for read in &self.reads.storage {
278 if db.storage(read.address, read.slot)? != read.value {
279 return Ok(None)
280 }
281 }
282 let mut rebased = Vec::new();
283 for read in &self.reads.accounts {
284 let current = db.basic(read.address)?;
285 match read.compare(current.as_ref()) {
286 AccountMatch::Same => {}
287 AccountMatch::Rebase { speculative, current } => {
288 if let Some(account) = self.result.state.get(&read.address) {
289 let Some(balance) = account
292 .info
293 .balance
294 .checked_add(current)
295 .and_then(|balance| balance.checked_sub(speculative))
296 else {
297 return Ok(None)
298 };
299 rebased.push(Rebase { address: read.address, balance, original: current });
300 }
301 }
302 AccountMatch::Differs => return Ok(None),
303 }
304 }
305 let credited = match &self.credit {
306 Some(credit) => {
307 let Some(info) = db.basic(credit.beneficiary)? else { return Ok(None) };
311 let mut account = Account::from(info);
312 let Some(balance) = account.info.balance.checked_add(credit.amount) else {
314 return Ok(None)
315 };
316 account.info.balance = balance;
317 account.mark_touch();
318 Some((credit.beneficiary, account))
319 }
320 None => None,
321 };
322 for rebase in &rebased {
323 if let Some(account) = self.result.state.get_mut(&rebase.address) {
324 account.info.balance = rebase.balance;
325 account.original_info_mut().balance = rebase.original;
327 }
328 }
329 let changed = !rebased.is_empty() || credited.is_some();
330 self.result.state.extend(credited);
331 Ok(Some(changed))
332 }
333}
334
335#[derive(Debug)]
337struct Rebase {
338 address: Address,
339 balance: U256,
341 original: U256,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq)]
347enum BalanceDependency {
348 None,
350 AtLeast(U256),
352 Exact,
354}
355
356#[derive(Debug, Clone)]
358struct AccountRead {
359 address: Address,
360 info: Option<AccountInfo>,
362 balance: BalanceDependency,
363 predicted: bool,
365}
366
367impl AccountRead {
368 fn compare(&self, current: Option<&AccountInfo>) -> AccountMatch {
370 let (speculative, current) = match (&self.info, current) {
371 (Some(speculative), Some(current)) => (speculative, current),
372 (None, None) => return AccountMatch::Same,
373 _ => return AccountMatch::Differs,
374 };
375 if speculative.balance == current.balance {
376 return if speculative == current { AccountMatch::Same } else { AccountMatch::Differs }
377 }
378
379 let allowed = match self.balance {
380 BalanceDependency::None => true,
381 BalanceDependency::AtLeast(min) => current.balance >= min,
382 BalanceDependency::Exact => false,
383 };
384 if !allowed ||
387 speculative.balance.is_zero() ||
388 current.balance.is_zero() ||
389 current.balance > MAX_REBASED_BALANCE ||
390 (AccountInfo { balance: speculative.balance, ..current.copy_without_code() }) !=
391 *speculative
392 {
393 return AccountMatch::Differs
394 }
395 AccountMatch::Rebase { speculative: speculative.balance, current: current.balance }
396 }
397}
398
399#[derive(Debug, Clone, Copy, PartialEq, Eq)]
401enum AccountMatch {
402 Same,
404 Rebase { speculative: U256, current: U256 },
407 Differs,
409}
410
411#[derive(Debug, Clone)]
413struct StorageRead {
414 address: Address,
415 slot: StorageKey,
416 value: StorageValue,
417 predicted: bool,
419}
420
421#[derive(Debug, Default, Clone)]
423pub(crate) struct ReadSet {
424 accounts: Vec<AccountRead>,
425 storage: Vec<StorageRead>,
426 unknown_code: bool,
428 block_env: BlockEnvFields,
430}
431
432impl ReadSet {
433 fn finish(mut self, balances: &mut BalanceTracker, tx: SpeculativeTx) -> Result<Self, Self> {
439 let SpeculativeTx { caller, nonce, max_balance_spending } = tx;
440 self.accounts.sort_unstable_by_key(|read| read.address);
442 self.accounts.dedup_by_key(|read| read.address);
443 self.block_env = balances.block_env;
444
445 let Ok(caller_index) = self.accounts.binary_search_by_key(&caller, |read| read.address)
446 else {
447 return Err(self)
448 };
449 let caller_info = &self.accounts[caller_index].info;
450 let (caller_nonce, caller_balance) =
451 caller_info.as_ref().map_or((0, U256::ZERO), |info| (info.nonce, info.balance));
452 let Ok(max_balance_spending) = max_balance_spending else { return Err(self) };
453 if self.unknown_code || caller_nonce != nonce || caller_balance < max_balance_spending {
455 return Err(self)
456 }
457
458 let bounded = self
461 .accounts
462 .iter()
463 .all(|read| read.info.as_ref().is_none_or(|info| info.balance <= MAX_REBASED_BALANCE));
464 let paid = caller_balance - max_balance_spending;
465 let BalanceTracker { observed, senders, .. } = balances;
468 observed.sort_unstable();
469 senders.sort_unstable();
470 for read in &mut self.accounts {
471 let sent = senders
474 .get(senders.partition_point(|(address, _)| *address < read.address))
475 .filter(|(address, _)| *address == read.address)
476 .map(|(_, left)| *left);
477 let left = sent.into_iter().chain((read.address == caller).then_some(paid)).min();
478 read.balance = if !bounded || observed.binary_search(&read.address).is_ok() {
479 BalanceDependency::Exact
480 } else if let Some(left) = left {
481 let balance = read.info.as_ref().map_or(U256::ZERO, |info| info.balance);
482 BalanceDependency::AtLeast(balance.saturating_sub(left))
483 } else {
484 BalanceDependency::None
485 };
486 }
487 Ok(self)
488 }
489
490 fn position(&self, address: Address) -> Option<usize> {
492 self.accounts.binary_search_by_key(&address, |read| read.address).ok()
493 }
494
495 fn matches(&self, predicted: &PredictedState, index: usize) -> bool {
498 self.accounts.iter().all(|read| match predicted.account(read.address, index) {
499 Some(account) => read.compare(account.as_ref()) != AccountMatch::Differs,
500 None => !read.predicted,
501 }) && self.storage.iter().all(|read| {
502 match predicted.storage(read.address, read.slot, index) {
503 Some(value) => value == read.value,
504 None => !read.predicted,
505 }
506 })
507 }
508}
509
510#[derive(Debug)]
512pub(crate) struct SpeculativeTx {
513 pub(crate) caller: Address,
515 pub(crate) nonce: u64,
517 pub(crate) max_balance_spending: Result<U256, InvalidTransaction>,
519}
520
521type Writes<T> = Vec<(usize, T)>;
523
524#[derive(Debug, Default)]
529pub(super) struct PredictedState {
530 accounts: DashMap<Address, Writes<Option<AccountInfo>>, DefaultHashBuilder>,
532 storage: DashMap<(Address, StorageKey), Writes<StorageValue>, DefaultHashBuilder>,
533 cleared: DashMap<Address, Writes<()>, DefaultHashBuilder>,
535 code: DashMap<B256, Bytecode, DefaultHashBuilder>,
537 generation: AtomicUsize,
539}
540
541impl PredictedState {
542 pub(super) fn generation(&self) -> usize {
545 self.generation.load(Ordering::Relaxed)
546 }
547
548 fn account(&self, address: Address, index: usize) -> Option<Option<AccountInfo>> {
550 let writes = self.accounts.get(&address)?;
551 latest(&writes, index).map(|(_, account)| account.clone())
552 }
553
554 fn storage(&self, address: Address, slot: StorageKey, index: usize) -> Option<StorageValue> {
556 let cleared = self.cleared.get(&address).and_then(|w| latest(&w, index).map(|(i, _)| *i));
557 let written = self.storage.get(&(address, slot)).and_then(|w| latest(&w, index).copied());
558 match (written, cleared) {
559 (Some((written, value)), Some(cleared)) if written >= cleared => Some(value),
560 (_, Some(_)) => Some(StorageValue::ZERO),
561 (written, None) => written.map(|(_, value)| value),
562 }
563 }
564
565 fn code(&self, code_hash: B256) -> Option<Bytecode> {
567 self.code.get(&code_hash).map(|code| code.clone())
568 }
569
570 fn publish(&self, index: usize, state: &EvmState) {
572 self.update(index, state, true);
573 for account in state.values() {
574 if account.is_touched() &&
575 account.is_created() &&
576 let Some(code) = &account.info.code
577 {
578 self.code.insert(account.info.code_hash, code.clone());
579 }
580 }
581 }
582
583 fn publish_accounts(&self, index: usize, state: &EvmState) {
585 for (address, account) in state {
586 if account.is_touched() && !account.is_selfdestructed() && !account.info.is_empty() {
587 write(
588 &mut self.accounts.entry(*address).or_default(),
589 index,
590 Some(Some(account.info.copy_without_code())),
591 );
592 }
593 }
594 }
595
596 fn retract(&self, index: usize, state: &EvmState) {
598 self.update(index, state, false);
599 }
600
601 fn retract_unwritten(&self, index: usize, previous: &EvmState, state: &EvmState) {
604 for (address, account) in previous {
605 if !account.is_touched() {
606 continue
607 }
608 let Some(written) = state.get(address).filter(|account| account.is_touched()) else {
609 self.update(index, [(address, account)], false);
610 continue
611 };
612 let clears = |account: &Account| {
613 account.is_selfdestructed() || account.info.is_empty() || account.is_created()
614 };
615 if clears(account) && !clears(written) {
616 write::<()>(&mut self.cleared.entry(*address).or_default(), index, None);
617 }
618 let destroyed = written.is_selfdestructed() || written.info.is_empty();
620 for (slot, _) in account.changed_storage_slots() {
621 if destroyed || !written.storage.get(slot).is_some_and(|value| value.is_changed()) {
622 write::<StorageValue>(
623 &mut self.storage.entry((*address, *slot)).or_default(),
624 index,
625 None,
626 );
627 }
628 }
629 }
630 }
631
632 fn update<'a>(
635 &self,
636 index: usize,
637 state: impl IntoIterator<Item = (&'a Address, &'a Account)>,
638 publish: bool,
639 ) {
640 for (address, account) in state {
641 if !account.is_touched() {
642 continue
643 }
644 let destroyed = account.is_selfdestructed() || account.info.is_empty();
646 if destroyed || account.is_created() {
647 write(&mut self.cleared.entry(*address).or_default(), index, publish.then_some(()));
648 }
649 let info = publish.then(|| (!destroyed).then(|| account.info.copy_without_code()));
651 write(&mut self.accounts.entry(*address).or_default(), index, info);
652 if destroyed {
653 continue
654 }
655 for (slot, value) in account.changed_storage_slots() {
656 write(
657 &mut self.storage.entry((*address, *slot)).or_default(),
658 index,
659 publish.then_some(value.present_value),
660 );
661 }
662 }
663 }
664}
665
666fn write<T>(writes: &mut Writes<T>, index: usize, value: Option<T>) {
668 match (writes.binary_search_by_key(&index, |(i, _)| *i), value) {
669 (Ok(i), Some(value)) => writes[i].1 = value,
670 (Err(i), Some(value)) => writes.insert(i, (index, value)),
671 (Ok(i), None) => {
672 writes.remove(i);
673 }
674 (Err(_), None) => {}
675 }
676}
677
678fn latest<T>(writes: &[(usize, T)], index: usize) -> Option<&(usize, T)> {
680 writes[..writes.partition_point(|(i, _)| *i < index)].last()
681}
682
683#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
686pub(crate) struct BlockEnvFields {
687 pub(crate) beneficiary: bool,
690 pub(crate) timestamp: bool,
692 pub(crate) prevrandao: bool,
694 pub(crate) gas_limit: bool,
696}
697
698impl BlockEnvFields {
699 const fn intersects(self, other: Self) -> bool {
701 (self.beneficiary && other.beneficiary) ||
702 (self.timestamp && other.timestamp) ||
703 (self.prevrandao && other.prevrandao) ||
704 (self.gas_limit && other.gas_limit)
705 }
706}
707
708#[derive(Debug)]
711pub(crate) struct PoolResults<H> {
712 stored: RwLock<StoredPoolTxs<H>>,
713}
714
715impl<H> Default for PoolResults<H> {
716 fn default() -> Self {
717 Self { stored: RwLock::new(StoredPoolTxs { txs: B256Map::default(), bytes: 0 }) }
718 }
719}
720
721#[derive(Debug)]
723struct StoredPoolTxs<H> {
724 txs: B256Map<Arc<PoolTx<H>>>,
725 bytes: usize,
727}
728
729impl<H> PoolResults<H> {
730 pub(crate) fn store(
736 &self,
737 hash: B256,
738 mut result: ResultAndState<H>,
739 reads: ReadSet,
740 balances: &mut BalanceTracker,
741 tx: SpeculativeTx,
742 ) -> bool {
743 if balances.block_env.beneficiary {
744 return false
745 }
746 let Ok(mut reads) = reads.finish(balances, tx) else { return false };
747
748 let Some(beneficiary) = result.state.remove(&POOL_BENEFICIARY) else { return false };
751 let Some(index) = reads.position(POOL_BENEFICIARY) else { return false };
752 let speculative = reads.accounts.remove(index).info.map_or(U256::ZERO, |info| info.balance);
753 if speculative > MAX_REBASED_BALANCE {
755 return false
756 }
757 let Some(credit) = beneficiary.info.balance.checked_sub(speculative) else { return false };
758
759 let bytes = retained_bytes(&result, &reads);
760 let mut stored = self.stored.write();
761 let total = stored.bytes + bytes;
762 if total > MAX_POOL_RESULT_BYTES {
763 return false
764 }
765 let Entry::Vacant(entry) = stored.txs.entry(hash) else { return false };
766 entry.insert(Arc::new(PoolTx { result, reads, credit }));
767 stored.bytes = total;
768 true
769 }
770
771 pub(crate) fn get(&self, hash: &B256) -> Option<Arc<PoolTx<H>>> {
773 self.stored.read().txs.get(hash).cloned()
774 }
775
776 pub(crate) fn is_empty(&self) -> bool {
778 self.stored.read().txs.is_empty()
779 }
780}
781
782fn retained_bytes<H>(result: &ResultAndState<H>, reads: &ReadSet) -> usize {
784 let accounts = result.state.values().map(|account| {
785 let code = account.info.code.as_ref().filter(|_| account.is_created());
787 account.storage.capacity() * size_of::<(StorageKey, EvmStorageSlot)>() +
788 code.map_or(0, Bytecode::len)
789 });
790 result.state.capacity() * size_of::<(Address, Account)>() +
791 reads.accounts.capacity() * size_of::<AccountRead>() +
792 reads.storage.capacity() * size_of::<StorageRead>() +
793 result.result.output().map_or(0, |output| output.len()) +
794 result.result.logs().iter().map(InMemorySize::size).sum::<usize>() +
795 accounts.sum::<usize>()
796}
797
798#[derive(Debug)]
800pub(crate) struct PoolTx<H> {
801 result: ResultAndState<H>,
802 reads: ReadSet,
803 credit: U256,
805}
806
807impl<H> PoolTx<H> {
808 pub(super) const fn state(&self) -> &EvmState {
810 &self.result.state
811 }
812
813 pub(super) fn load_reads<DB: Database>(&self, db: &mut DB) -> Result<(), DB::Error> {
815 for read in &self.reads.accounts {
816 db.basic(read.address)?;
817 }
818 for read in &self.reads.storage {
819 db.storage(read.address, read.slot)?;
820 }
821 Ok(())
822 }
823}
824
825#[derive(Debug, Clone)]
828pub(crate) struct PoolSeeds<H> {
829 results: Arc<PoolResults<H>>,
830 beneficiary: Address,
832 differs: BlockEnvFields,
834}
835
836impl<H> PoolSeeds<H> {
837 pub(crate) const fn new(
840 results: Arc<PoolResults<H>>,
841 beneficiary: Address,
842 differs: BlockEnvFields,
843 ) -> Self {
844 Self { results, beneficiary, differs }
845 }
846}
847
848#[derive(Debug)]
850pub(crate) struct RecordingDb<DB> {
851 inner: DB,
852 reads: Option<ReadSet>,
854 predicted: Option<(Arc<PredictedState>, usize)>,
856}
857
858impl<DB> RecordingDb<DB> {
859 pub(crate) const fn new(inner: DB) -> Self {
861 Self { inner, reads: None, predicted: None }
862 }
863
864 pub(super) fn start_recording(&mut self, predicted: Arc<PredictedState>, index: usize) {
867 self.reads = Some(ReadSet::default());
868 self.predicted = Some((predicted, index));
869 }
870
871 pub(crate) fn start_recording_inner(&mut self) {
873 self.reads = Some(ReadSet::default());
874 self.predicted = None;
875 }
876
877 pub(crate) fn take_reads(&mut self) -> Option<ReadSet> {
879 self.predicted = None;
880 self.reads.take()
881 }
882}
883
884impl<DB: Database> Database for RecordingDb<DB> {
885 type Error = DB::Error;
886
887 fn basic(&mut self, address: Address) -> Result<Option<AccountInfo>, Self::Error> {
888 let predicted =
889 self.predicted.as_ref().and_then(|(state, index)| state.account(address, *index));
890 let is_predicted = predicted.is_some();
891 let account = match predicted {
892 Some(account) => account,
893 None => self.inner.basic(address)?,
894 };
895
896 if let Some(reads) = &mut self.reads {
897 reads.accounts.push(AccountRead {
898 address,
899 info: account.as_ref().map(AccountInfo::copy_without_code),
900 balance: BalanceDependency::None,
901 predicted: is_predicted,
902 });
903 }
904 Ok(account)
905 }
906
907 fn code_by_hash(&mut self, code_hash: B256) -> Result<Bytecode, Self::Error> {
908 if let Some(code) = self.predicted.as_ref().and_then(|(state, _)| state.code(code_hash)) {
909 return Ok(code)
910 }
911 let code = self.inner.code_by_hash(code_hash)?;
912 if code.is_empty() &&
914 code_hash != KECCAK_EMPTY &&
915 let Some(reads) = &mut self.reads
916 {
917 reads.unknown_code = true;
918 }
919 Ok(code)
920 }
921
922 fn storage(
923 &mut self,
924 address: Address,
925 index: StorageKey,
926 ) -> Result<StorageValue, Self::Error> {
927 let predicted = self
928 .predicted
929 .as_ref()
930 .and_then(|(state, tx_index)| state.storage(address, index, *tx_index));
931 let is_predicted = predicted.is_some();
932 let value = match predicted {
933 Some(value) => value,
934 None => self.inner.storage(address, index)?,
935 };
936 if let Some(reads) = &mut self.reads {
937 reads.storage.push(StorageRead {
938 address,
939 slot: index,
940 value,
941 predicted: is_predicted,
942 });
943 }
944 Ok(value)
945 }
946
947 fn block_hash(&mut self, number: u64) -> Result<B256, Self::Error> {
948 self.inner.block_hash(number)
949 }
950}
951
952#[derive(Debug, Default)]
955pub(crate) struct BalanceTracker {
956 observed: Vec<Address>,
958 senders: Vec<(Address, U256)>,
960 block_env: BlockEnvFields,
962}
963
964impl BalanceTracker {
965 pub(crate) fn clear(&mut self) {
967 self.observed.clear();
968 self.senders.clear();
969 self.block_env = BlockEnvFields::default();
970 }
971
972 fn frame_end<CTX: ContextTr<Journal: JournalExt>>(
979 &mut self,
980 context: &CTX,
981 result: InstructionResult,
982 ) {
983 let out_of_gas = matches!(
984 SuccessOrHalt::<HaltReason>::from(result),
985 SuccessOrHalt::Halt(HaltReason::OutOfGas(_))
986 );
987 let journal = context.journal_ref();
988 self.block_env.beneficiary |= out_of_gas ||
989 (journal.depth() == 0 &&
990 journal.evm_state().contains_key(&context.block().beneficiary()));
991 }
992
993 fn send<CTX: ContextTr<Journal: JournalTr<State = EvmState>>>(
995 &mut self,
996 context: &CTX,
997 sender: Address,
998 value: U256,
999 ) {
1000 let left = context
1002 .journal_ref()
1003 .evm_state()
1004 .get(&sender)
1005 .and_then(|account| account.info.balance.checked_sub(value));
1006 match left {
1007 Some(left) => self.senders.push((sender, left)),
1008 None => self.observed.push(sender),
1009 }
1010 }
1011}
1012
1013impl<CTX: ContextTr<Journal: JournalExt> + ContextTr<Journal: JournalTr<State = EvmState>>>
1014 Inspector<CTX> for BalanceTracker
1015{
1016 fn step(&mut self, interp: &mut Interpreter, _context: &mut CTX) {
1017 let address = match interp.bytecode.opcode() {
1018 opcode::BALANCE => {
1019 let Ok(word) = interp.stack.peek(0) else { return };
1020 word.into_address()
1021 }
1022 opcode::SELFBALANCE | opcode::SELFDESTRUCT => interp.input.target_address,
1023 opcode::COINBASE => {
1024 self.block_env.beneficiary = true;
1025 return
1026 }
1027 opcode::TIMESTAMP => {
1028 self.block_env.timestamp = true;
1029 return
1030 }
1031 opcode::DIFFICULTY => {
1032 self.block_env.prevrandao = true;
1033 return
1034 }
1035 opcode::GASLIMIT => {
1036 self.block_env.gas_limit = true;
1037 return
1038 }
1039 _ => return,
1040 };
1041 if self.observed.last() != Some(&address) {
1043 self.observed.push(address);
1044 }
1045 }
1046
1047 fn call(&mut self, context: &mut CTX, inputs: &mut CallInputs) -> Option<CallOutcome> {
1048 if let Some(value) = inputs.transfer_value().filter(|value| !value.is_zero()) {
1049 self.send(context, inputs.transfer_from(), value);
1050 }
1051 None
1052 }
1053
1054 fn call_end(&mut self, context: &mut CTX, inputs: &CallInputs, outcome: &mut CallOutcome) {
1055 if *outcome.instruction_result() == InstructionResult::OutOfFunds {
1056 self.observed.push(inputs.transfer_from());
1057 }
1058 self.frame_end(context, *outcome.instruction_result());
1059 }
1060
1061 fn create(&mut self, context: &mut CTX, inputs: &mut CreateInputs) -> Option<CreateOutcome> {
1062 if !inputs.value().is_zero() {
1063 self.send(context, inputs.caller(), inputs.value());
1064 }
1065 None
1066 }
1067
1068 fn create_end(
1069 &mut self,
1070 context: &mut CTX,
1071 inputs: &CreateInputs,
1072 outcome: &mut CreateOutcome,
1073 ) {
1074 if *outcome.instruction_result() == InstructionResult::OutOfFunds {
1075 self.observed.push(inputs.caller());
1076 }
1077 self.frame_end(context, *outcome.instruction_result());
1078 }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use super::*;
1084 use alloy_consensus::{transaction::Recovered, TxEip1559, TxLegacy};
1085 use alloy_evm::{
1086 block::{BlockExecutor, TxResult},
1087 eth::EthBlockExecutionCtx,
1088 Evm, EvmEnv,
1089 };
1090 use alloy_primitives::{bytes, Bytes, Signature, TxKind};
1091 use reth_errors::ProviderError;
1092 use reth_ethereum_primitives::{Receipt, Transaction, TransactionSigned};
1093 use reth_evm::{ConfigureEvm, EvmFor, HaltReasonFor};
1094 use reth_evm_ethereum::EthEvmConfig;
1095 use revm::{
1096 context::{BlockEnv, CfgEnv, Transaction as _},
1097 database::{states::bundle_state::BundleRetention, BundleState, CacheDB, EmptyDB, State},
1098 primitives::hardfork::SpecId,
1099 };
1100
1101 const BENEFICIARY: Address = Address::repeat_byte(0xbe);
1102 const NOOP: Address = Address::repeat_byte(0xc0);
1104 const COUNTER: Address = Address::repeat_byte(0xc1);
1106 const BENEFICIARY_BALANCE_READER: Address = Address::repeat_byte(0xc2);
1108 const SELF_BALANCE_READER: Address = Address::repeat_byte(0xc3);
1110 const FORWARDER: Address = Address::repeat_byte(0xc4);
1112 const RICH: Address = Address::repeat_byte(0xc5);
1114 const ZERO_BALANCE: Address = Address::repeat_byte(0xc6);
1116 const UNKNOWN_CODE: Address = Address::repeat_byte(0xc7);
1118 const TIMESTAMP_READER: Address = Address::repeat_byte(0xc8);
1120 const GAS_LIMIT_READER: Address = Address::repeat_byte(0xc9);
1122 const BENEFICIARY_READER: Address = Address::repeat_byte(0xca);
1124 const POOL_BENEFICIARY_CALLER: Address = Address::repeat_byte(0xcb);
1126 const BENEFICIARY_BALANCE_BY_ADDRESS: Address = Address::repeat_byte(0xcc);
1128 const PREVRANDAO_READER: Address = Address::repeat_byte(0xcd);
1130 const SATURATED: Address = Address::repeat_byte(0xce);
1132 const EXHAUSTED_SENDER: u8 = 5;
1134 const GAS_PRICE: u128 = 2;
1135
1136 type Results = PrewarmResults<HaltReasonFor<EthEvmConfig>>;
1137
1138 fn evm_env(prewarm: bool) -> EvmEnv {
1139 let mut cfg_env = CfgEnv::new_with_spec(SpecId::CANCUN).with_chain_id(1);
1140 cfg_env.disable_nonce_check = prewarm;
1141 cfg_env.disable_balance_check = prewarm;
1142 let block_env = BlockEnv {
1143 number: U256::from(1),
1144 beneficiary: BENEFICIARY,
1145 basefee: 1,
1146 gas_limit: 30_000_000,
1147 ..Default::default()
1148 };
1149 EvmEnv { cfg_env, block_env }
1150 }
1151
1152 fn sender(i: u8) -> Address {
1153 Address::repeat_byte(i)
1154 }
1155
1156 fn tx(from: u8, nonce: u64, to: Address, value: u64) -> Recovered<TransactionSigned> {
1157 tx_with_gas_limit(from, nonce, to, value, 100_000)
1158 }
1159
1160 fn tx_with_gas_limit(
1161 from: u8,
1162 nonce: u64,
1163 to: Address,
1164 value: u64,
1165 gas_limit: u64,
1166 ) -> Recovered<TransactionSigned> {
1167 let tx = Transaction::Legacy(TxLegacy {
1168 chain_id: Some(1),
1169 nonce,
1170 gas_price: GAS_PRICE,
1171 gas_limit,
1172 to: TxKind::Call(to),
1173 value: U256::from(value),
1174 input: Bytes::new(),
1175 });
1176 Recovered::new_unchecked(
1177 TransactionSigned::new_unhashed(tx, Signature::test_signature()),
1178 sender(from),
1179 )
1180 }
1181
1182 fn parent_state() -> CacheDB<EmptyDB> {
1183 let mut db = CacheDB::new(EmptyDB::default());
1184 for i in [1, 2, 3, 4, 6, 7] {
1186 db.insert_account_info(
1187 sender(i),
1188 AccountInfo { balance: U256::from(10u64.pow(18)), ..Default::default() },
1189 );
1190 }
1191 db.insert_account_info(
1192 sender(EXHAUSTED_SENDER),
1193 AccountInfo {
1194 balance: U256::from(10u64.pow(18)),
1195 nonce: u64::MAX,
1196 ..Default::default()
1197 },
1198 );
1199 db.insert_account_info(
1200 BENEFICIARY,
1201 AccountInfo { balance: U256::from(1), ..Default::default() },
1202 );
1203 db.insert_account_info(RICH, AccountInfo { balance: U256::MAX >> 1, ..Default::default() });
1204 db.insert_account_info(SATURATED, AccountInfo { balance: U256::MAX, ..Default::default() });
1205 db.insert_account_info(ZERO_BALANCE, AccountInfo { nonce: 1, ..Default::default() });
1206 db.insert_account_info(
1207 UNKNOWN_CODE,
1208 AccountInfo { code_hash: B256::repeat_byte(0xab), code: None, ..Default::default() },
1209 );
1210 for (address, code) in [
1211 (NOOP, bytes!("00")),
1212 (COUNTER, bytes!("5f546001015f5500")),
1213 (BENEFICIARY_BALANCE_READER, bytes!("41315f5500")),
1214 (SELF_BALANCE_READER, bytes!("475f5500")),
1215 (FORWARDER, bytes!("5f5f5f5f600573c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c05af100")),
1216 (TIMESTAMP_READER, bytes!("425f5500")),
1217 (GAS_LIMIT_READER, bytes!("455f5500")),
1218 (BENEFICIARY_READER, bytes!("415f5500")),
1219 (
1220 POOL_BENEFICIARY_CALLER,
1221 bytes!("5f5f5f5f5f7300000000000000000000000000000000000beef05af100"),
1222 ),
1223 (
1224 BENEFICIARY_BALANCE_BY_ADDRESS,
1225 bytes!("73bebebebebebebebebebebebebebebebebebebebe3100"),
1226 ),
1227 (PREVRANDAO_READER, bytes!("445f5500")),
1228 ] {
1229 let code = Bytecode::new_raw(code);
1230 db.insert_account_info(
1231 address,
1232 AccountInfo {
1233 balance: U256::from(12),
1234 code_hash: code.hash_slow(),
1235 code: Some(code),
1236 ..Default::default()
1237 },
1238 );
1239 }
1240 db
1241 }
1242
1243 struct Prewarm<'a> {
1245 txs: &'a [Recovered<TransactionSigned>],
1246 results: Results,
1247 evm: EvmFor<EthEvmConfig, RecordingDb<CacheDB<EmptyDB>>, BalanceTracker>,
1248 }
1249
1250 impl<'a> Prewarm<'a> {
1251 fn new(txs: &'a [Recovered<TransactionSigned>], order: &[usize]) -> Self {
1254 let evm = EthEvmConfig::mainnet().evm_with_env_and_inspector(
1255 RecordingDb::new(parent_state()),
1256 evm_env(true),
1257 BalanceTracker::default(),
1258 );
1259 let mut prewarm = Self { txs, results: Results::new(txs.len()), evm };
1260 for &index in order {
1261 prewarm.run(index);
1262 }
1263 let mut refresh = order.to_vec();
1264 refresh.sort_unstable();
1265 for index in refresh {
1266 if prewarm.results.needs_refresh(index) {
1267 prewarm.run(index);
1268 }
1269 }
1270 prewarm
1271 }
1272
1273 fn run(&mut self, index: usize) {
1274 let tx = &self.txs[index];
1275 let tx_env = EthEvmConfig::mainnet().tx_env(tx);
1276 let speculative = SpeculativeTx {
1277 caller: tx.signer(),
1278 nonce: tx_env.nonce,
1279 max_balance_spending: tx_env.max_balance_spending(),
1280 };
1281 self.evm.db_mut().start_recording(Arc::clone(self.results.predicted()), index);
1282 self.evm.inspector_mut().clear();
1283 let result = self.evm.transact(tx_env);
1284 let reads = self.evm.db_mut().take_reads().unwrap();
1285 match result {
1286 Ok(result) => {
1287 self.results.store(index, result, reads, self.evm.inspector_mut(), speculative)
1288 }
1289 Err(_) => self.results.reject(index, reads),
1290 }
1291 }
1292
1293 fn refresh_after(&mut self, index: usize) {
1295 for index in index + 1..self.txs.len() {
1296 if self.results.needs_refresh(index) {
1297 self.run(index);
1298 }
1299 }
1300 }
1301 }
1302
1303 fn execute(
1308 txs: &[Recovered<TransactionSigned>],
1309 mut prewarm: Option<&mut Prewarm<'_>>,
1310 publish: bool,
1311 beneficiary: Address,
1312 ) -> Execution {
1313 let config = EthEvmConfig::mainnet();
1314 let mut state = State::builder().with_database(parent_state()).with_bundle_update().build();
1315 let mut evm_env = evm_env(false);
1316 evm_env.block_env.beneficiary = beneficiary;
1317 let evm = config.evm_with_env(&mut state, evm_env);
1318 let ctx = EthBlockExecutionCtx {
1319 parent_hash: B256::ZERO,
1320 parent_beacon_block_root: None,
1321 ommers: &[],
1322 withdrawals: None,
1323 extra_data: Bytes::new(),
1324 tx_count_hint: None,
1325 slot_number: None,
1326 };
1327 let mut executor = config.create_executor(evm, ctx);
1328 let mut outcomes = Vec::new();
1329 let mut changes = Vec::new();
1330 for (index, tx) in txs.iter().enumerate() {
1331 let Some(prewarm) = prewarm.as_deref_mut() else {
1332 executor
1333 .execute_transaction_with_result_closure(tx, |result| {
1334 changes.push(changed_accounts(&result.result().state))
1335 })
1336 .unwrap();
1337 outcomes.push(Err(ReuseMiss::Missing));
1338 continue
1339 };
1340 let outcome =
1341 match prewarm.results.take_valid(index, executor.evm_mut().db_mut()).unwrap() {
1342 Ok(result) => {
1343 changes.push(changed_accounts(&result.state));
1344 executor.commit_transaction(
1345 config.prewarm_tx_result(tx.inner(), result).unwrap(),
1346 );
1347 Ok(())
1348 }
1349 Err(miss) => {
1350 executor
1351 .execute_transaction_with_result_closure(tx, |result| {
1352 changes.push(changed_accounts(&result.result().state));
1353 if publish {
1354 prewarm.results.publish_executed(index, &result.result().state)
1355 }
1356 })
1357 .unwrap();
1358 if publish {
1359 prewarm.refresh_after(index);
1360 }
1361 Err(miss)
1362 }
1363 };
1364 outcomes.push(outcome);
1365 }
1366 let receipts = executor.receipts().to_vec();
1367 drop(executor);
1368 state.merge_transitions(BundleRetention::Reverts);
1369 Execution { receipts, bundle: state.take_bundle(), changes, outcomes }
1370 }
1371
1372 struct Execution {
1374 receipts: Vec<Receipt>,
1375 bundle: BundleState,
1376 changes: Vec<Vec<(Address, AccountInfo)>>,
1378 outcomes: Vec<Result<(), ReuseMiss>>,
1379 }
1380
1381 fn changed_accounts(state: &EvmState) -> Vec<(Address, AccountInfo)> {
1384 let mut changed = state
1385 .iter()
1386 .filter(|(_, account)| account.is_touched() && account.info != account.original_info())
1387 .map(|(address, account)| (*address, account.info.copy_without_code()))
1388 .collect::<Vec<_>>();
1389 changed.sort_unstable_by_key(|(address, _)| *address);
1390 changed
1391 }
1392
1393 struct FailingDb;
1395
1396 impl Database for FailingDb {
1397 type Error = ProviderError;
1398
1399 fn basic(&mut self, _address: Address) -> Result<Option<AccountInfo>, Self::Error> {
1400 Err(ProviderError::BestBlockNotFound)
1401 }
1402
1403 fn code_by_hash(&mut self, _code_hash: B256) -> Result<Bytecode, Self::Error> {
1404 Err(ProviderError::BestBlockNotFound)
1405 }
1406
1407 fn storage(
1408 &mut self,
1409 _address: Address,
1410 _index: StorageKey,
1411 ) -> Result<StorageValue, Self::Error> {
1412 Err(ProviderError::BestBlockNotFound)
1413 }
1414
1415 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
1416 Err(ProviderError::BestBlockNotFound)
1417 }
1418 }
1419
1420 fn reusable(txs: &[Recovered<TransactionSigned>]) -> Result<(), ReuseMiss> {
1423 let prewarm = Prewarm::new(txs, &[0]);
1424 let mut state = State::builder().with_database(parent_state()).build();
1425 prewarm.results.take_valid(0, &mut state).unwrap().map(drop)
1426 }
1427
1428 fn reuse(txs: &[Recovered<TransactionSigned>]) -> Vec<Result<(), ReuseMiss>> {
1431 reuse_in(txs, &(0..txs.len()).collect::<Vec<_>>(), true)
1432 }
1433
1434 fn reuse_in(
1437 txs: &[Recovered<TransactionSigned>],
1438 order: &[usize],
1439 publish: bool,
1440 ) -> Vec<Result<(), ReuseMiss>> {
1441 let reused = execute(txs, Some(&mut Prewarm::new(txs, order)), publish, BENEFICIARY);
1442 let expected = execute(txs, None, false, BENEFICIARY);
1443 assert_eq!(reused.receipts, expected.receipts);
1444 assert_eq!(reused.bundle, expected.bundle);
1445 assert_eq!(reused.changes, expected.changes);
1446 reused.outcomes
1447 }
1448
1449 fn pool_results(
1453 txs: &[Recovered<TransactionSigned>],
1454 ) -> PoolResults<HaltReasonFor<EthEvmConfig>> {
1455 let mut evm_env = evm_env(true);
1456 evm_env.block_env.beneficiary = POOL_BENEFICIARY;
1457 evm_env.block_env.timestamp = U256::from(12);
1458 evm_env.block_env.prevrandao = Some(B256::repeat_byte(1));
1459 let mut evm = EthEvmConfig::mainnet().evm_with_env_and_inspector(
1460 RecordingDb::new(parent_state()),
1461 evm_env,
1462 BalanceTracker::default(),
1463 );
1464 let results = PoolResults::default();
1465 for tx in txs {
1466 let tx_env = EthEvmConfig::mainnet().tx_env(tx);
1467 let speculative = SpeculativeTx {
1468 caller: tx.signer(),
1469 nonce: tx_env.nonce,
1470 max_balance_spending: tx_env.max_balance_spending(),
1471 };
1472 evm.db_mut().start_recording_inner();
1473 evm.inspector_mut().clear();
1474 let result = evm.transact(tx_env).unwrap();
1475 let reads = evm.db_mut().take_reads().unwrap();
1476 results.store(*tx.tx_hash(), result, reads, evm.inspector_mut(), speculative);
1477 }
1478 results
1479 }
1480
1481 fn reuse_pool(
1484 txs: &[Recovered<TransactionSigned>],
1485 pool: &[Recovered<TransactionSigned>],
1486 beneficiary: Address,
1487 ) -> Vec<Result<(), ReuseMiss>> {
1488 let differs = BlockEnvFields { timestamp: true, prevrandao: true, ..Default::default() };
1489 reuse_pool_with(txs, pool, beneficiary, differs)
1490 }
1491
1492 fn reuse_pool_with(
1495 txs: &[Recovered<TransactionSigned>],
1496 pool: &[Recovered<TransactionSigned>],
1497 beneficiary: Address,
1498 differs: BlockEnvFields,
1499 ) -> Vec<Result<(), ReuseMiss>> {
1500 let seeds = PoolSeeds::new(Arc::new(pool_results(pool)), beneficiary, differs);
1501 let mut prewarm = Prewarm::new(txs, &[]);
1502 for (index, tx) in txs.iter().enumerate() {
1503 prewarm.results.seed(index, tx.tx_hash(), &seeds);
1504 }
1505 let reused = execute(txs, Some(&mut prewarm), false, beneficiary);
1506 let expected = execute(txs, None, false, beneficiary);
1507 assert_eq!(reused.receipts, expected.receipts);
1508 assert_eq!(reused.bundle, expected.bundle);
1509 assert_eq!(reused.changes, expected.changes);
1510 reused.outcomes
1511 }
1512
1513 #[test]
1514 fn reuses_independent_transactions() {
1515 let txs = [tx(1, 0, sender(2), 1), tx(3, 0, Address::repeat_byte(0xaa), 1)];
1516 assert_eq!(reuse(&txs), [Ok(()), Ok(())]);
1517 }
1518
1519 #[test]
1520 fn rebases_unobserved_balances() {
1521 let txs = [tx(1, 0, NOOP, 1), tx(1, 1, sender(2), 1), tx(2, 0, NOOP, 1)];
1523 assert_eq!(reuse(&txs), [Ok(()), Ok(()), Ok(())]);
1524 }
1525
1526 #[test]
1527 fn reuses_predicted_writes() {
1528 let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1), tx(1, 1, COUNTER, 1)];
1529 assert_eq!(reuse(&txs), [Ok(()), Ok(()), Ok(())]);
1530 assert_eq!(reuse_in(&txs, &[2, 1, 0], true), [Ok(()), Ok(()), Ok(())]);
1532 }
1533
1534 #[test]
1535 fn refreshes_against_executed_writes() {
1536 let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1), tx(1, 1, sender(3), 1)];
1537 assert_eq!(reuse_in(&txs, &[1, 2], true), [Err(ReuseMiss::Missing), Ok(()), Ok(())]);
1538 }
1539
1540 #[test]
1541 fn rebases_original_balances() {
1542 let txs = [tx(1, 0, FORWARDER, 10), tx(2, 0, FORWARDER, 0)];
1545 assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Ok(())]);
1546 }
1547
1548 #[test]
1549 fn rejects_stale_storage() {
1550 let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 1)];
1551 assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]);
1552 }
1553
1554 #[test]
1555 fn rejects_stale_callers() {
1556 let txs = [tx(1, 0, sender(2), 1), tx(1, 1, sender(3), 1)];
1557 assert_eq!(reuse_in(&txs, &[1], false), [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]);
1558 }
1559
1560 #[test]
1561 fn rejects_observed_balances() {
1562 let txs = [
1563 tx(1, 0, SELF_BALANCE_READER, 1),
1564 tx(2, 0, SELF_BALANCE_READER, 1),
1565 tx(3, 0, BENEFICIARY_BALANCE_READER, 1),
1566 ];
1567 assert_eq!(
1568 reuse_in(&txs, &[1, 2], false),
1569 [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale), Err(ReuseMiss::Stale)]
1570 );
1571 }
1572
1573 #[test]
1574 fn rejects_lower_sender_balances() {
1575 let txs = [tx(1, 0, FORWARDER, 0), tx(2, 0, FORWARDER, 0), tx(3, 0, FORWARDER, 0)];
1576 assert_eq!(
1578 reuse_in(&txs, &[1, 2], false),
1579 [Err(ReuseMiss::Missing), Ok(()), Err(ReuseMiss::Stale)]
1580 );
1581 }
1582
1583 #[test]
1584 fn reuses_pool_results() {
1585 let txs = [tx(1, 0, sender(2), 1), tx(3, 0, COUNTER, 1), tx(2, 0, GAS_LIMIT_READER, 1)];
1587 assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Ok(()), Ok(()), Ok(())]);
1588 assert_eq!(
1590 reuse_pool(&txs, &txs[1..], BENEFICIARY),
1591 [Err(ReuseMiss::Missing), Ok(()), Ok(())]
1592 );
1593 }
1594
1595 #[test]
1596 fn rejects_stale_pool_results() {
1597 let txs = [tx(1, 0, COUNTER, 1), tx(2, 0, COUNTER, 2)];
1599 assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Ok(()), Err(ReuseMiss::Stale)]);
1600 let missing = Address::repeat_byte(0xbf);
1602 assert_eq!(reuse_pool(&txs, &txs, missing), [Err(ReuseMiss::Stale), Err(ReuseMiss::Stale)]);
1603 }
1604
1605 #[test]
1606 fn rejects_pool_results_observing_block_env() {
1607 let txs = [
1608 tx(1, 0, TIMESTAMP_READER, 1),
1609 tx(2, 0, BENEFICIARY_READER, 1),
1610 tx(3, 0, POOL_BENEFICIARY, 1),
1611 tx(4, 0, POOL_BENEFICIARY_CALLER, 1),
1612 tx(6, 0, BENEFICIARY, 1),
1613 tx(7, 0, PREVRANDAO_READER, 1),
1614 ];
1615 assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Err(ReuseMiss::Missing); 6]);
1616
1617 let txs = [tx(1, 0, GAS_LIMIT_READER, 1)];
1618 let differs = BlockEnvFields { gas_limit: true, ..Default::default() };
1619 assert_eq!(reuse_pool_with(&txs, &txs, BENEFICIARY, differs), [Err(ReuseMiss::Missing)]);
1620 }
1621
1622 #[test]
1623 fn seeds_only_unexecuted_transactions() {
1624 let txs = [tx(1, 0, COUNTER, 1)];
1625 let differs = BlockEnvFields::default();
1626 let seeds = PoolSeeds::new(Arc::new(pool_results(&txs)), BENEFICIARY, differs);
1627 assert!(Prewarm::new(&txs, &[]).results.seed(0, txs[0].tx_hash(), &seeds).is_some());
1628 assert!(Prewarm::new(&txs, &[0]).results.seed(0, txs[0].tx_hash(), &seeds).is_none());
1629 }
1630
1631 #[test]
1632 fn rejects_pool_results_overflowing_the_beneficiary() {
1633 let txs = [tx(1, 0, COUNTER, 1)];
1634 assert_eq!(reuse_pool(&txs, &txs, SATURATED), [Err(ReuseMiss::Stale)]);
1635 }
1636
1637 fn store_again(
1640 results: &PoolResults<HaltReasonFor<EthEvmConfig>>,
1641 tx: &Recovered<TransactionSigned>,
1642 hash: B256,
1643 modify: impl FnOnce(&mut ResultAndState<HaltReasonFor<EthEvmConfig>>, &mut AccountRead),
1644 ) -> bool {
1645 let recorded = results.get(tx.tx_hash()).unwrap();
1646 let mut result = recorded.result.clone();
1647 result.state.insert(POOL_BENEFICIARY, Account::default());
1648 let mut beneficiary = AccountRead {
1649 address: POOL_BENEFICIARY,
1650 info: None,
1651 balance: BalanceDependency::None,
1652 predicted: false,
1653 };
1654 modify(&mut result, &mut beneficiary);
1655 let mut reads = recorded.reads.clone();
1656 reads.accounts.push(beneficiary);
1657 let tx_env = EthEvmConfig::mainnet().tx_env(tx);
1658 let speculative = SpeculativeTx {
1659 caller: tx.signer(),
1660 nonce: tx_env.nonce,
1661 max_balance_spending: tx_env.max_balance_spending(),
1662 };
1663 results.store(hash, result, reads, &mut BalanceTracker::default(), speculative)
1664 }
1665
1666 #[test]
1667 fn bounds_pool_results() {
1668 let txs = [tx(1, 0, COUNTER, 1)];
1669 let results = pool_results(&txs);
1670 assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1671 assert!(!store_again(&results, &txs[0], B256::with_last_byte(1), |result, _| {
1673 let account = result.state.get_mut(&COUNTER).unwrap();
1674 account.mark_created();
1675 account.info.code = Some(Bytecode::new_raw(vec![0; MAX_POOL_RESULT_BYTES].into()));
1676 }));
1677 }
1678
1679 #[test]
1680 fn rejects_recorded_pool_hashes() {
1681 let txs = [tx(1, 0, COUNTER, 1)];
1682 let results = pool_results(&txs);
1683 assert!(!store_again(&results, &txs[0], *txs[0].tx_hash(), |_, _| {}));
1684 assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1685 }
1686
1687 #[test]
1688 fn rejects_saturated_pool_beneficiaries() {
1689 let txs = [tx(1, 0, COUNTER, 1)];
1690 let results = pool_results(&txs);
1691 assert!(!store_again(&results, &txs[0], B256::ZERO, |result, beneficiary| {
1693 beneficiary.info = Some(AccountInfo { balance: U256::MAX, ..Default::default() });
1694 result.state.get_mut(&POOL_BENEFICIARY).unwrap().info.balance = U256::MAX;
1695 }));
1696 assert!(store_again(&results, &txs[0], B256::ZERO, |_, _| {}));
1697 }
1698
1699 #[test]
1700 fn rejects_pool_results_running_out_of_gas() {
1701 let txs = [tx_with_gas_limit(1, 0, BENEFICIARY_BALANCE_BY_ADDRESS, 0, 21_500)];
1704 assert_eq!(reuse_pool(&txs, &txs, BENEFICIARY), [Err(ReuseMiss::Missing)]);
1705 }
1706
1707 #[test]
1708 fn rejects_invalid_transactions() {
1709 assert_eq!(reusable(&[tx(1, 0, NOOP, 0)]), Ok(()));
1710 assert_eq!(reusable(&[tx(1, 0, NOOP, 10u64.pow(19))]), Err(ReuseMiss::Stale));
1712 assert_eq!(reusable(&[tx(1, 1, NOOP, 0)]), Err(ReuseMiss::Stale));
1714 assert_eq!(reusable(&[tx(EXHAUSTED_SENDER, u64::MAX, NOOP, 0)]), Err(ReuseMiss::Stale));
1716 assert_eq!(
1718 reusable(&[tx_with_gas_limit(1, 0, NOOP, 0, 40_000_000)]),
1719 Err(ReuseMiss::Stale)
1720 );
1721 let overflowing = Transaction::Eip1559(TxEip1559 {
1723 chain_id: 1,
1724 max_fee_per_gas: u128::MAX,
1725 gas_limit: 100_000,
1726 to: TxKind::Call(NOOP),
1727 ..Default::default()
1728 });
1729 let overflowing = Recovered::new_unchecked(
1730 TransactionSigned::new_unhashed(overflowing, Signature::test_signature()),
1731 sender(1),
1732 );
1733 assert_eq!(reusable(&[overflowing]), Err(ReuseMiss::Stale));
1734 }
1735
1736 #[test]
1737 fn keeps_stale_results_unchanged() {
1738 let txs = [tx(1, 0, NOOP, 0)];
1739 let prewarm = Prewarm::new(&txs, &[0]);
1740 let mut db = parent_state();
1742 db.insert_account_info(
1743 sender(1),
1744 AccountInfo { balance: U256::from(2 * 10u64.pow(18)), ..Default::default() },
1745 );
1746 let mut noop = db.basic(NOOP).unwrap().unwrap();
1747 noop.nonce = 1;
1748 db.insert_account_info(NOOP, noop);
1749 let mut stale = State::builder().with_database(db).build();
1750 assert_eq!(
1751 prewarm.results.take_valid(0, &mut stale).unwrap().map(drop),
1752 Err(ReuseMiss::Stale)
1753 );
1754
1755 let mut state = State::builder().with_database(parent_state()).build();
1756 let result = prewarm.results.take_valid(0, &mut state).unwrap().unwrap();
1757 let fee = U256::from(21_000 * GAS_PRICE);
1758 assert_eq!(result.state[&sender(1)].info.balance, U256::from(10u64.pow(18)) - fee);
1759 }
1760
1761 #[test]
1762 fn keeps_results_on_database_errors() {
1763 let txs = [tx(1, 0, NOOP, 0)];
1764 let prewarm = Prewarm::new(&txs, &[0]);
1765 assert!(matches!(
1766 prewarm.results.take_valid(0, &mut FailingDb),
1767 Err(ProviderError::BestBlockNotFound)
1768 ));
1769 let mut state = State::builder().with_database(parent_state()).build();
1770 assert_eq!(prewarm.results.take_valid(0, &mut state).unwrap().map(drop), Ok(()));
1771 }
1772
1773 #[test]
1774 fn rejects_unknown_code() {
1775 assert_eq!(reuse_in(&[tx(1, 0, UNKNOWN_CODE, 0)], &[0], false), [Err(ReuseMiss::Stale)]);
1776 }
1777
1778 #[test]
1779 fn rejects_rebasing_unbounded_or_zero_balances() {
1780 let rebased = [tx(1, 0, sender(4), 1), tx(2, 0, sender(4), 1)];
1781 assert_eq!(reuse_in(&rebased, &[1], false), [Err(ReuseMiss::Missing), Ok(())]);
1782 for txs in [
1783 [tx(1, 0, RICH, 1), tx(2, 0, RICH, 1)],
1784 [tx(1, 0, ZERO_BALANCE, 1), tx(2, 0, ZERO_BALANCE, 1)],
1785 [tx(1, 0, sender(4), 1), tx(4, 0, RICH, 1)],
1787 ] {
1788 assert_eq!(
1789 reuse_in(&txs, &[1], false),
1790 [Err(ReuseMiss::Missing), Err(ReuseMiss::Stale)]
1791 );
1792 }
1793 }
1794}