1use crate::entry::SelfdestructionRevertStatus;
3
4use super::JournalEntryTr;
5use bytecode::Bytecode;
6use context_interface::{
7 context::{SStoreResult, SelfDestructResult, StateLoad},
8 journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
9};
10use core::mem;
11use database_interface::Database;
12use primitives::{
13 hardfork::SpecId::{self, *},
14 hash_map::Entry,
15 Address, HashMap, HashSet, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
16};
17use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
18use std::vec::Vec;
19#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct JournalInner<ENTRY> {
25 pub state: EvmState,
27 pub transient_storage: TransientStorage,
31 pub logs: Vec<Log>,
33 pub depth: usize,
35 pub journal: Vec<ENTRY>,
37 pub transaction_id: usize,
43 pub spec: SpecId,
56 pub warm_preloaded_addresses: HashSet<Address>,
63 pub warm_coinbase_address: Option<Address>,
65 pub precompiles: HashSet<Address>,
67}
68
69impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
76 pub fn new() -> JournalInner<ENTRY> {
81 Self {
82 state: HashMap::default(),
83 transient_storage: TransientStorage::default(),
84 logs: Vec::new(),
85 journal: Vec::default(),
86 transaction_id: 0,
87 depth: 0,
88 spec: SpecId::default(),
89 warm_preloaded_addresses: HashSet::default(),
90 precompiles: HashSet::default(),
91 warm_coinbase_address: None,
92 }
93 }
94
95 #[inline]
97 pub fn take_logs(&mut self) -> Vec<Log> {
98 mem::take(&mut self.logs)
99 }
100
101 pub fn commit_tx(&mut self) {
109 let Self {
112 state,
113 transient_storage,
114 logs,
115 depth,
116 journal,
117 transaction_id,
118 spec,
119 warm_preloaded_addresses,
120 precompiles,
121 warm_coinbase_address,
122 } = self;
123 let _ = spec;
125 let _ = precompiles;
126 let _ = state;
127 transient_storage.clear();
128 *depth = 0;
129
130 journal.clear();
132
133 *warm_coinbase_address = None;
135 reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
139 *transaction_id += 1;
141 logs.clear();
142 }
143
144 pub fn discard_tx(&mut self) {
146 let Self {
148 state,
149 transient_storage,
150 logs,
151 depth,
152 journal,
153 transaction_id,
154 spec,
155 warm_preloaded_addresses,
156 warm_coinbase_address,
157 precompiles,
158 } = self;
159
160 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
161 journal.drain(..).rev().for_each(|entry| {
163 entry.revert(state, None, is_spurious_dragon_enabled);
164 });
165 transient_storage.clear();
166 *depth = 0;
167 logs.clear();
168 *transaction_id += 1;
169 *warm_coinbase_address = None;
171 reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
172 }
173
174 #[inline]
179 pub fn finalize(&mut self) -> EvmState {
180 let Self {
183 state,
184 transient_storage,
185 logs,
186 depth,
187 journal,
188 transaction_id,
189 spec,
190 warm_preloaded_addresses,
191 warm_coinbase_address,
192 precompiles,
193 } = self;
194 let _ = spec;
196 *warm_coinbase_address = None;
198 reset_preloaded_addresses(warm_preloaded_addresses, precompiles);
200
201 let state = mem::take(state);
202 logs.clear();
203 transient_storage.clear();
204
205 journal.clear();
207 *depth = 0;
208 *transaction_id = 0;
210
211 state
212 }
213
214 #[inline]
216 pub fn state(&mut self) -> &mut EvmState {
217 &mut self.state
218 }
219
220 #[inline]
222 pub fn set_spec_id(&mut self, spec: SpecId) {
223 self.spec = spec;
224 }
225
226 #[inline]
230 pub fn touch(&mut self, address: Address) {
231 if let Some(account) = self.state.get_mut(&address) {
232 Self::touch_account(&mut self.journal, address, account);
233 }
234 }
235
236 #[inline]
238 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
239 if !account.is_touched() {
240 journal.push(ENTRY::account_touched(address));
241 account.mark_touch();
242 }
243 }
244
245 #[inline]
253 pub fn account(&self, address: Address) -> &Account {
254 self.state
255 .get(&address)
256 .expect("Account expected to be loaded") }
258
259 #[inline]
263 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
264 let account = self.state.get_mut(&address).unwrap();
265 Self::touch_account(&mut self.journal, address, account);
266
267 self.journal.push(ENTRY::code_changed(address));
268
269 account.info.code_hash = hash;
270 account.info.code = Some(code);
271 }
272
273 #[inline]
279 pub fn set_code(&mut self, address: Address, code: Bytecode) {
280 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
281 if eip7702_bytecode.address().is_zero() {
282 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
283 return;
284 }
285 }
286
287 let hash = code.hash_slow();
288 self.set_code_with_hash(address, code, hash)
289 }
290
291 #[inline]
293 pub fn caller_accounting_journal_entry(
294 &mut self,
295 address: Address,
296 old_balance: U256,
297 bump_nonce: bool,
298 ) {
299 self.journal
301 .push(ENTRY::balance_changed(address, old_balance));
302 self.journal.push(ENTRY::account_touched(address));
304
305 if bump_nonce {
306 self.journal.push(ENTRY::nonce_changed(address));
308 }
309 }
310
311 #[inline]
315 pub fn balance_incr<DB: Database>(
316 &mut self,
317 db: &mut DB,
318 address: Address,
319 balance: U256,
320 ) -> Result<(), DB::Error> {
321 let account = self.load_account(db, address)?.data;
322 let old_balance = account.info.balance;
323 account.info.balance = account.info.balance.saturating_add(balance);
324
325 if !account.is_touched() {
327 account.mark_touch();
328 self.journal.push(ENTRY::account_touched(address));
329 }
330
331 self.journal
333 .push(ENTRY::balance_changed(address, old_balance));
334 Ok(())
335 }
336
337 #[inline]
339 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
340 self.journal.push(ENTRY::nonce_changed(address));
341 }
342
343 #[inline]
345 pub fn transfer<DB: Database>(
346 &mut self,
347 db: &mut DB,
348 from: Address,
349 to: Address,
350 balance: U256,
351 ) -> Result<Option<TransferError>, DB::Error> {
352 if balance.is_zero() {
353 self.load_account(db, to)?;
354 let to_account = self.state.get_mut(&to).unwrap();
355 Self::touch_account(&mut self.journal, to, to_account);
356 return Ok(None);
357 }
358 self.load_account(db, from)?;
360 self.load_account(db, to)?;
361
362 let from_account = self.state.get_mut(&from).unwrap();
364 Self::touch_account(&mut self.journal, from, from_account);
365 let from_balance = &mut from_account.info.balance;
366
367 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
368 return Ok(Some(TransferError::OutOfFunds));
369 };
370 *from_balance = from_balance_decr;
371
372 let to_account = &mut self.state.get_mut(&to).unwrap();
374 Self::touch_account(&mut self.journal, to, to_account);
375 let to_balance = &mut to_account.info.balance;
376 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
377 return Ok(Some(TransferError::OverflowPayment));
378 };
379 *to_balance = to_balance_incr;
380 self.journal
383 .push(ENTRY::balance_transfer(from, to, balance));
384
385 Ok(None)
386 }
387
388 #[inline]
404 pub fn create_account_checkpoint(
405 &mut self,
406 caller: Address,
407 target_address: Address,
408 balance: U256,
409 spec_id: SpecId,
410 ) -> Result<JournalCheckpoint, TransferError> {
411 let checkpoint = self.checkpoint();
413
414 let caller_balance = self.state.get(&caller).unwrap().info.balance;
416 if caller_balance < balance {
418 self.checkpoint_revert(checkpoint);
419 return Err(TransferError::OutOfFunds);
420 }
421
422 let target_acc = self.state.get_mut(&target_address).unwrap();
424 let last_journal = &mut self.journal;
425
426 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
431 self.checkpoint_revert(checkpoint);
432 return Err(TransferError::CreateCollision);
433 }
434
435 let is_created_globally = target_acc.mark_created_locally();
437
438 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
440 target_acc.info.code = None;
441 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
443 target_acc.info.nonce = 1;
445 }
446
447 Self::touch_account(last_journal, target_address, target_acc);
450
451 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
453 self.checkpoint_revert(checkpoint);
454 return Err(TransferError::OverflowPayment);
455 };
456 target_acc.info.balance = new_balance;
457
458 self.state.get_mut(&caller).unwrap().info.balance -= balance;
460
461 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
463
464 Ok(checkpoint)
465 }
466
467 #[inline]
469 pub fn checkpoint(&mut self) -> JournalCheckpoint {
470 let checkpoint = JournalCheckpoint {
471 log_i: self.logs.len(),
472 journal_i: self.journal.len(),
473 };
474 self.depth += 1;
475 checkpoint
476 }
477
478 #[inline]
480 pub fn checkpoint_commit(&mut self) {
481 self.depth -= 1;
482 }
483
484 #[inline]
486 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
487 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
488 let state = &mut self.state;
489 let transient_storage = &mut self.transient_storage;
490 self.depth -= 1;
491 self.logs.truncate(checkpoint.log_i);
492
493 self.journal
495 .drain(checkpoint.journal_i..)
496 .rev()
497 .for_each(|entry| {
498 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
499 });
500 }
501
502 #[inline]
514 pub fn selfdestruct<DB: Database>(
515 &mut self,
516 db: &mut DB,
517 address: Address,
518 target: Address,
519 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
520 let spec = self.spec;
521 let account_load = self.load_account(db, target)?;
522 let is_cold = account_load.is_cold;
523 let is_empty = account_load.state_clear_aware_is_empty(spec);
524
525 if address != target {
526 let acc_balance = self.state.get(&address).unwrap().info.balance;
529
530 let target_account = self.state.get_mut(&target).unwrap();
531 Self::touch_account(&mut self.journal, target, target_account);
532 target_account.info.balance += acc_balance;
533 }
534
535 let acc = self.state.get_mut(&address).unwrap();
536 let balance = acc.info.balance;
537
538 let destroyed_status = if !acc.is_selfdestructed() {
539 SelfdestructionRevertStatus::GloballySelfdestroyed
540 } else if !acc.is_selfdestructed_locally() {
541 SelfdestructionRevertStatus::LocallySelfdestroyed
542 } else {
543 SelfdestructionRevertStatus::RepeatedSelfdestruction
544 };
545
546 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
547
548 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
550 acc.mark_selfdestructed_locally();
551 acc.info.balance = U256::ZERO;
552 Some(ENTRY::account_destroyed(
553 address,
554 target,
555 destroyed_status,
556 balance,
557 ))
558 } else if address != target {
559 acc.info.balance = U256::ZERO;
560 Some(ENTRY::balance_transfer(address, target, balance))
561 } else {
562 None
567 };
568
569 if let Some(entry) = journal_entry {
570 self.journal.push(entry);
571 };
572
573 Ok(StateLoad {
574 data: SelfDestructResult {
575 had_value: !balance.is_zero(),
576 target_exists: !is_empty,
577 previously_destroyed: destroyed_status
578 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
579 },
580 is_cold,
581 })
582 }
583
584 #[inline]
586 pub fn load_account<DB: Database>(
587 &mut self,
588 db: &mut DB,
589 address: Address,
590 ) -> Result<StateLoad<&mut Account>, DB::Error> {
591 self.load_account_optional(db, address, false, [])
592 }
593
594 #[inline]
602 pub fn load_account_delegated<DB: Database>(
603 &mut self,
604 db: &mut DB,
605 address: Address,
606 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
607 let spec = self.spec;
608 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
609 let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
610 let is_empty = account.state_clear_aware_is_empty(spec);
611
612 let mut account_load = StateLoad::new(
613 AccountLoad {
614 is_delegate_account_cold: None,
615 is_empty,
616 },
617 account.is_cold,
618 );
619
620 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
622 let address = code.address();
623 let delegate_account = self.load_account(db, address)?;
624 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
625 }
626
627 Ok(account_load)
628 }
629
630 #[inline]
637 pub fn load_code<DB: Database>(
638 &mut self,
639 db: &mut DB,
640 address: Address,
641 ) -> Result<StateLoad<&mut Account>, DB::Error> {
642 self.load_account_optional(db, address, true, [])
643 }
644
645 #[inline]
647 pub fn load_account_optional<DB: Database>(
648 &mut self,
649 db: &mut DB,
650 address: Address,
651 load_code: bool,
652 storage_keys: impl IntoIterator<Item = StorageKey>,
653 ) -> Result<StateLoad<&mut Account>, DB::Error> {
654 let load = match self.state.entry(address) {
655 Entry::Occupied(entry) => {
656 let account = entry.into_mut();
657 let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
658 if is_cold {
660 if account.is_selfdestructed_locally() {
663 account.selfdestruct();
664 account.unmark_selfdestructed_locally();
665 }
666 account.unmark_created_locally();
668 }
669 StateLoad {
670 data: account,
671 is_cold,
672 }
673 }
674 Entry::Vacant(vac) => {
675 let account = if let Some(account) = db.basic(address)? {
676 account.into()
677 } else {
678 Account::new_not_existing(self.transaction_id)
679 };
680
681 let is_cold = !self.warm_preloaded_addresses.contains(&address)
683 && self.warm_coinbase_address.as_ref() != Some(&address);
684
685 StateLoad {
686 data: vac.insert(account),
687 is_cold,
688 }
689 }
690 };
691
692 if load.is_cold {
694 self.journal.push(ENTRY::account_warmed(address));
695 }
696 if load_code {
697 let info = &mut load.data.info;
698 if info.code.is_none() {
699 let code = if info.code_hash == KECCAK_EMPTY {
700 Bytecode::default()
701 } else {
702 db.code_by_hash(info.code_hash)?
703 };
704 info.code = Some(code);
705 }
706 }
707
708 for storage_key in storage_keys.into_iter() {
709 sload_with_account(
710 load.data,
711 db,
712 &mut self.journal,
713 self.transaction_id,
714 address,
715 storage_key,
716 )?;
717 }
718 Ok(load)
719 }
720
721 #[inline]
727 pub fn sload<DB: Database>(
728 &mut self,
729 db: &mut DB,
730 address: Address,
731 key: StorageKey,
732 ) -> Result<StateLoad<StorageValue>, DB::Error> {
733 let account = self.state.get_mut(&address).unwrap();
735 sload_with_account(
737 account,
738 db,
739 &mut self.journal,
740 self.transaction_id,
741 address,
742 key,
743 )
744 }
745
746 #[inline]
752 pub fn sstore<DB: Database>(
753 &mut self,
754 db: &mut DB,
755 address: Address,
756 key: StorageKey,
757 new: StorageValue,
758 ) -> Result<StateLoad<SStoreResult>, DB::Error> {
759 let present = self.sload(db, address, key)?;
761 let acc = self.state.get_mut(&address).unwrap();
762
763 let slot = acc.storage.get_mut(&key).unwrap();
765
766 if present.data == new {
768 return Ok(StateLoad::new(
769 SStoreResult {
770 original_value: slot.original_value(),
771 present_value: present.data,
772 new_value: new,
773 },
774 present.is_cold,
775 ));
776 }
777
778 self.journal
779 .push(ENTRY::storage_changed(address, key, present.data));
780 slot.present_value = new;
782 Ok(StateLoad::new(
783 SStoreResult {
784 original_value: slot.original_value(),
785 present_value: present.data,
786 new_value: new,
787 },
788 present.is_cold,
789 ))
790 }
791
792 #[inline]
796 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
797 self.transient_storage
798 .get(&(address, key))
799 .copied()
800 .unwrap_or_default()
801 }
802
803 #[inline]
810 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
811 let had_value = if new.is_zero() {
812 self.transient_storage.remove(&(address, key))
816 } else {
817 let previous_value = self
819 .transient_storage
820 .insert((address, key), new)
821 .unwrap_or_default();
822
823 if previous_value != new {
825 Some(previous_value)
827 } else {
828 None
829 }
830 };
831
832 if let Some(had_value) = had_value {
833 self.journal
835 .push(ENTRY::transient_storage_changed(address, key, had_value));
836 }
837 }
838
839 #[inline]
841 pub fn log(&mut self, log: Log) {
842 self.logs.push(log);
843 }
844}
845
846#[inline]
848pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
849 account: &mut Account,
850 db: &mut DB,
851 journal: &mut Vec<ENTRY>,
852 transaction_id: usize,
853 address: Address,
854 key: StorageKey,
855) -> Result<StateLoad<StorageValue>, DB::Error> {
856 let is_newly_created = account.is_created();
857 let (value, is_cold) = match account.storage.entry(key) {
858 Entry::Occupied(occ) => {
859 let slot = occ.into_mut();
860 let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
861 (slot.present_value, is_cold)
862 }
863 Entry::Vacant(vac) => {
864 let value = if is_newly_created {
866 StorageValue::ZERO
867 } else {
868 db.storage(address, key)?
869 };
870
871 vac.insert(EvmStorageSlot::new(value, transaction_id));
872
873 (value, true)
874 }
875 };
876
877 if is_cold {
878 journal.push(ENTRY::storage_warmed(address, key));
880 }
881
882 Ok(StateLoad::new(value, is_cold))
883}
884
885fn reset_preloaded_addresses(
886 warm_preloaded_addresses: &mut HashSet<Address>,
887 precompiles: &HashSet<Address>,
888) {
889 if warm_preloaded_addresses.len() == precompiles.len() {
892 debug_assert_eq!(warm_preloaded_addresses, precompiles);
893 return;
894 }
895 warm_preloaded_addresses.clone_from(precompiles);
896}