1use crate::{entry::SelfdestructionRevertStatus, warm_addresses::WarmAddresses};
3
4use super::JournalEntryTr;
5use bytecode::Bytecode;
6use context_interface::{
7 context::{SStoreResult, SelfDestructResult, StateLoad},
8 journaled_state::{AccountLoad, JournalCheckpoint, JournalLoadError, TransferError},
9};
10use core::mem;
11use database_interface::Database;
12use primitives::{
13 hardfork::SpecId::{self, *},
14 hash_map::Entry,
15 Address, HashMap, 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_addresses: WarmAddresses,
58}
59
60impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
67 pub fn new() -> JournalInner<ENTRY> {
72 Self {
73 state: HashMap::default(),
74 transient_storage: TransientStorage::default(),
75 logs: Vec::new(),
76 journal: Vec::default(),
77 transaction_id: 0,
78 depth: 0,
79 spec: SpecId::default(),
80 warm_addresses: WarmAddresses::new(),
81 }
82 }
83
84 #[inline]
86 pub fn take_logs(&mut self) -> Vec<Log> {
87 mem::take(&mut self.logs)
88 }
89
90 pub fn commit_tx(&mut self) {
98 let Self {
101 state,
102 transient_storage,
103 logs,
104 depth,
105 journal,
106 transaction_id,
107 spec,
108 warm_addresses,
109 } = self;
110 let _ = spec;
112 let _ = state;
113 transient_storage.clear();
114 *depth = 0;
115
116 journal.clear();
118
119 warm_addresses.clear_coinbase();
121 *transaction_id += 1;
123 logs.clear();
124 }
125
126 pub fn discard_tx(&mut self) {
128 let Self {
130 state,
131 transient_storage,
132 logs,
133 depth,
134 journal,
135 transaction_id,
136 spec,
137 warm_addresses,
138 } = self;
139 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
140 journal.drain(..).rev().for_each(|entry| {
142 entry.revert(state, None, is_spurious_dragon_enabled);
143 });
144 transient_storage.clear();
145 *depth = 0;
146 logs.clear();
147 *transaction_id += 1;
148
149 warm_addresses.clear_coinbase();
151 }
152
153 #[inline]
158 pub fn finalize(&mut self) -> EvmState {
159 let Self {
162 state,
163 transient_storage,
164 logs,
165 depth,
166 journal,
167 transaction_id,
168 spec,
169 warm_addresses,
170 } = self;
171 let _ = spec;
173 warm_addresses.clear_coinbase();
175
176 let state = mem::take(state);
177 logs.clear();
178 transient_storage.clear();
179
180 journal.clear();
182 *depth = 0;
183 *transaction_id = 0;
185
186 state
187 }
188
189 #[inline]
191 pub fn state(&mut self) -> &mut EvmState {
192 &mut self.state
193 }
194
195 #[inline]
197 pub fn set_spec_id(&mut self, spec: SpecId) {
198 self.spec = spec;
199 }
200
201 #[inline]
205 pub fn touch(&mut self, address: Address) {
206 if let Some(account) = self.state.get_mut(&address) {
207 Self::touch_account(&mut self.journal, address, account);
208 }
209 }
210
211 #[inline]
213 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
214 if !account.is_touched() {
215 journal.push(ENTRY::account_touched(address));
216 account.mark_touch();
217 }
218 }
219
220 #[inline]
228 pub fn account(&self, address: Address) -> &Account {
229 self.state
230 .get(&address)
231 .expect("Account expected to be loaded") }
233
234 #[inline]
238 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
239 let account = self.state.get_mut(&address).unwrap();
240 Self::touch_account(&mut self.journal, address, account);
241
242 self.journal.push(ENTRY::code_changed(address));
243
244 account.info.code_hash = hash;
245 account.info.code = Some(code);
246 }
247
248 #[inline]
254 pub fn set_code(&mut self, address: Address, code: Bytecode) {
255 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
256 if eip7702_bytecode.address().is_zero() {
257 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
258 return;
259 }
260 }
261
262 let hash = code.hash_slow();
263 self.set_code_with_hash(address, code, hash)
264 }
265
266 #[inline]
268 pub fn caller_accounting_journal_entry(
269 &mut self,
270 address: Address,
271 old_balance: U256,
272 bump_nonce: bool,
273 ) {
274 self.journal
276 .push(ENTRY::balance_changed(address, old_balance));
277 self.journal.push(ENTRY::account_touched(address));
279
280 if bump_nonce {
281 self.journal.push(ENTRY::nonce_changed(address));
283 }
284 }
285
286 #[inline]
290 pub fn balance_incr<DB: Database>(
291 &mut self,
292 db: &mut DB,
293 address: Address,
294 balance: U256,
295 ) -> Result<(), DB::Error> {
296 let account = self.load_account(db, address)?.data;
297 let old_balance = account.info.balance;
298 account.info.balance = account.info.balance.saturating_add(balance);
299
300 if !account.is_touched() {
302 account.mark_touch();
303 self.journal.push(ENTRY::account_touched(address));
304 }
305
306 self.journal
308 .push(ENTRY::balance_changed(address, old_balance));
309 Ok(())
310 }
311
312 #[inline]
314 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
315 self.journal.push(ENTRY::nonce_changed(address));
316 }
317
318 #[inline]
324 pub fn transfer_loaded(
325 &mut self,
326 from: Address,
327 to: Address,
328 balance: U256,
329 ) -> Option<TransferError> {
330 if from == to {
331 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
332 if balance > from_balance {
334 return Some(TransferError::OutOfFunds);
335 }
336 return None;
337 }
338
339 if balance.is_zero() {
340 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
341 return None;
342 }
343
344 let from_account = self.state.get_mut(&from).unwrap();
346 let from_balance = &mut from_account.info.balance;
349 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
350 return Some(TransferError::OutOfFunds);
351 };
352 *from_balance = from_balance_decr;
353
354 let to_account = self.state.get_mut(&to).unwrap();
356 Self::touch_account(&mut self.journal, to, to_account);
357 let to_balance = &mut to_account.info.balance;
358 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
359 return Some(TransferError::OverflowPayment);
361 };
362 *to_balance = to_balance_incr;
363
364 self.journal
366 .push(ENTRY::balance_transfer(from, to, balance));
367
368 None
369 }
370
371 #[inline]
373 pub fn transfer<DB: Database>(
374 &mut self,
375 db: &mut DB,
376 from: Address,
377 to: Address,
378 balance: U256,
379 ) -> Result<Option<TransferError>, DB::Error> {
380 if balance.is_zero() {
381 self.load_account(db, to)?;
382 let to_account = self.state.get_mut(&to).unwrap();
383 Self::touch_account(&mut self.journal, to, to_account);
384 return Ok(None);
385 }
386 self.load_account(db, from)?;
388 self.load_account(db, to)?;
389
390 let from_account = self.state.get_mut(&from).unwrap();
392 Self::touch_account(&mut self.journal, from, from_account);
393 let from_balance = &mut from_account.info.balance;
394
395 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
396 return Ok(Some(TransferError::OutOfFunds));
397 };
398 *from_balance = from_balance_decr;
399
400 let to_account = &mut self.state.get_mut(&to).unwrap();
402 Self::touch_account(&mut self.journal, to, to_account);
403 let to_balance = &mut to_account.info.balance;
404 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
405 return Ok(Some(TransferError::OverflowPayment));
407 };
408 *to_balance = to_balance_incr;
409
410 self.journal
411 .push(ENTRY::balance_transfer(from, to, balance));
412
413 Ok(None)
414 }
415
416 #[inline]
432 pub fn create_account_checkpoint(
433 &mut self,
434 caller: Address,
435 target_address: Address,
436 balance: U256,
437 spec_id: SpecId,
438 ) -> Result<JournalCheckpoint, TransferError> {
439 let checkpoint = self.checkpoint();
441
442 let caller_balance = self.state.get(&caller).unwrap().info.balance;
444 if caller_balance < balance {
446 self.checkpoint_revert(checkpoint);
447 return Err(TransferError::OutOfFunds);
448 }
449
450 let target_acc = self.state.get_mut(&target_address).unwrap();
452 let last_journal = &mut self.journal;
453
454 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
459 self.checkpoint_revert(checkpoint);
460 return Err(TransferError::CreateCollision);
461 }
462
463 let is_created_globally = target_acc.mark_created_locally();
465
466 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
468 target_acc.info.code = None;
469 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
471 target_acc.info.nonce = 1;
473 }
474
475 Self::touch_account(last_journal, target_address, target_acc);
478
479 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
481 self.checkpoint_revert(checkpoint);
482 return Err(TransferError::OverflowPayment);
483 };
484 target_acc.info.balance = new_balance;
485
486 self.state.get_mut(&caller).unwrap().info.balance -= balance;
488
489 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
491
492 Ok(checkpoint)
493 }
494
495 #[inline]
497 pub fn checkpoint(&mut self) -> JournalCheckpoint {
498 let checkpoint = JournalCheckpoint {
499 log_i: self.logs.len(),
500 journal_i: self.journal.len(),
501 };
502 self.depth += 1;
503 checkpoint
504 }
505
506 #[inline]
508 pub fn checkpoint_commit(&mut self) {
509 self.depth -= 1;
510 }
511
512 #[inline]
514 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
515 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
516 let state = &mut self.state;
517 let transient_storage = &mut self.transient_storage;
518 self.depth -= 1;
519 self.logs.truncate(checkpoint.log_i);
520
521 if checkpoint.journal_i < self.journal.len() {
523 self.journal
524 .drain(checkpoint.journal_i..)
525 .rev()
526 .for_each(|entry| {
527 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
528 });
529 }
530 }
531
532 #[inline]
544 pub fn selfdestruct<DB: Database>(
545 &mut self,
546 db: &mut DB,
547 address: Address,
548 target: Address,
549 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
550 let spec = self.spec;
551 let account_load = self.load_account(db, target)?;
552 let is_cold = account_load.is_cold;
553 let is_empty = account_load.state_clear_aware_is_empty(spec);
554
555 if address != target {
556 let acc_balance = self.state.get(&address).unwrap().info.balance;
559
560 let target_account = self.state.get_mut(&target).unwrap();
561 Self::touch_account(&mut self.journal, target, target_account);
562 target_account.info.balance += acc_balance;
563 }
564
565 let acc = self.state.get_mut(&address).unwrap();
566 let balance = acc.info.balance;
567
568 let destroyed_status = if !acc.is_selfdestructed() {
569 SelfdestructionRevertStatus::GloballySelfdestroyed
570 } else if !acc.is_selfdestructed_locally() {
571 SelfdestructionRevertStatus::LocallySelfdestroyed
572 } else {
573 SelfdestructionRevertStatus::RepeatedSelfdestruction
574 };
575
576 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
577
578 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
580 acc.mark_selfdestructed_locally();
581 acc.info.balance = U256::ZERO;
582 Some(ENTRY::account_destroyed(
583 address,
584 target,
585 destroyed_status,
586 balance,
587 ))
588 } else if address != target {
589 acc.info.balance = U256::ZERO;
590 Some(ENTRY::balance_transfer(address, target, balance))
591 } else {
592 None
597 };
598
599 if let Some(entry) = journal_entry {
600 self.journal.push(entry);
601 };
602
603 Ok(StateLoad {
604 data: SelfDestructResult {
605 had_value: !balance.is_zero(),
606 target_exists: !is_empty,
607 previously_destroyed: destroyed_status
608 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
609 },
610 is_cold,
611 })
612 }
613
614 #[inline]
616 pub fn load_account<DB: Database>(
617 &mut self,
618 db: &mut DB,
619 address: Address,
620 ) -> Result<StateLoad<&mut Account>, DB::Error> {
621 self.load_account_optional(db, address, false, [], false)
622 .map_err(JournalLoadError::unwrap_db_error)
623 }
624
625 #[inline]
633 pub fn load_account_delegated<DB: Database>(
634 &mut self,
635 db: &mut DB,
636 address: Address,
637 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
638 let spec = self.spec;
639 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
640 let account = self
641 .load_account_optional(db, address, is_eip7702_enabled, [], false)
642 .map_err(JournalLoadError::unwrap_db_error)?;
643 let is_empty = account.state_clear_aware_is_empty(spec);
644
645 let mut account_load = StateLoad::new(
646 AccountLoad {
647 is_delegate_account_cold: None,
648 is_empty,
649 },
650 account.is_cold,
651 );
652
653 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
655 let address = code.address();
656 let delegate_account = self
657 .load_account_optional(db, address, true, [], false)
658 .map_err(JournalLoadError::unwrap_db_error)?;
659 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
660 }
661
662 Ok(account_load)
663 }
664
665 #[inline]
672 pub fn load_code<DB: Database>(
673 &mut self,
674 db: &mut DB,
675 address: Address,
676 ) -> Result<StateLoad<&mut Account>, DB::Error> {
677 self.load_account_optional(db, address, true, [], false)
678 .map_err(JournalLoadError::unwrap_db_error)
679 }
680
681 #[inline(never)]
683 pub fn load_account_optional<DB: Database>(
684 &mut self,
685 db: &mut DB,
686 address: Address,
687 load_code: bool,
688 storage_keys: impl IntoIterator<Item = StorageKey>,
689 skip_cold_load: bool,
690 ) -> Result<StateLoad<&mut Account>, JournalLoadError<DB::Error>> {
691 let load = match self.state.entry(address) {
692 Entry::Occupied(entry) => {
693 let account = entry.into_mut();
694
695 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
697 if is_cold {
698 let should_be_cold = self.warm_addresses.is_cold(&address);
700
701 if should_be_cold && skip_cold_load {
703 return Err(JournalLoadError::ColdLoadSkipped);
704 }
705 is_cold = should_be_cold;
706
707 account.mark_warm_with_transaction_id(self.transaction_id);
709
710 if account.is_selfdestructed_locally() {
713 account.selfdestruct();
714 account.unmark_selfdestructed_locally();
715 }
716 account.unmark_created_locally();
718 }
719 StateLoad {
720 data: account,
721 is_cold,
722 }
723 }
724 Entry::Vacant(vac) => {
725 let is_cold = self.warm_addresses.is_cold(&address);
727
728 if is_cold && skip_cold_load {
730 return Err(JournalLoadError::ColdLoadSkipped);
731 }
732 let account = if let Some(account) = db.basic(address)? {
733 account.into()
734 } else {
735 Account::new_not_existing(self.transaction_id)
736 };
737
738 StateLoad {
739 data: vac.insert(account),
740 is_cold,
741 }
742 }
743 };
744
745 if load.is_cold {
747 self.journal.push(ENTRY::account_warmed(address));
748 }
749 if load_code {
750 let info = &mut load.data.info;
751 if info.code.is_none() {
752 let code = if info.code_hash == KECCAK_EMPTY {
753 Bytecode::default()
754 } else {
755 db.code_by_hash(info.code_hash)?
756 };
757 info.code = Some(code);
758 }
759 }
760
761 for storage_key in storage_keys.into_iter() {
762 sload_with_account(
763 load.data,
764 db,
765 &mut self.journal,
766 self.transaction_id,
767 address,
768 storage_key,
769 false,
770 )?;
771 }
772 Ok(load)
773 }
774
775 #[inline]
781 pub fn sload<DB: Database>(
782 &mut self,
783 db: &mut DB,
784 address: Address,
785 key: StorageKey,
786 skip_cold_load: bool,
787 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
788 let account = self.state.get_mut(&address).unwrap();
790 sload_with_account(
792 account,
793 db,
794 &mut self.journal,
795 self.transaction_id,
796 address,
797 key,
798 skip_cold_load,
799 )
800 }
801
802 #[inline]
808 pub fn sstore<DB: Database>(
809 &mut self,
810 db: &mut DB,
811 address: Address,
812 key: StorageKey,
813 new: StorageValue,
814 skip_cold_load: bool,
815 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
816 let present = self.sload(db, address, key, skip_cold_load)?;
818 let acc = self.state.get_mut(&address).unwrap();
819
820 let slot = acc.storage.get_mut(&key).unwrap();
822
823 if present.data == new {
825 return Ok(StateLoad::new(
826 SStoreResult {
827 original_value: slot.original_value(),
828 present_value: present.data,
829 new_value: new,
830 },
831 present.is_cold,
832 ));
833 }
834
835 self.journal
836 .push(ENTRY::storage_changed(address, key, present.data));
837 slot.present_value = new;
839 Ok(StateLoad::new(
840 SStoreResult {
841 original_value: slot.original_value(),
842 present_value: present.data,
843 new_value: new,
844 },
845 present.is_cold,
846 ))
847 }
848
849 #[inline]
853 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
854 self.transient_storage
855 .get(&(address, key))
856 .copied()
857 .unwrap_or_default()
858 }
859
860 #[inline]
867 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
868 let had_value = if new.is_zero() {
869 self.transient_storage.remove(&(address, key))
873 } else {
874 let previous_value = self
876 .transient_storage
877 .insert((address, key), new)
878 .unwrap_or_default();
879
880 if previous_value != new {
882 Some(previous_value)
884 } else {
885 None
886 }
887 };
888
889 if let Some(had_value) = had_value {
890 self.journal
892 .push(ENTRY::transient_storage_changed(address, key, had_value));
893 }
894 }
895
896 #[inline]
898 pub fn log(&mut self, log: Log) {
899 self.logs.push(log);
900 }
901}
902
903#[inline]
905pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
906 account: &mut Account,
907 db: &mut DB,
908 journal: &mut Vec<ENTRY>,
909 transaction_id: usize,
910 address: Address,
911 key: StorageKey,
912 skip_cold_load: bool,
913) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
914 let is_newly_created = account.is_created();
915 let (value, is_cold) = match account.storage.entry(key) {
916 Entry::Occupied(occ) => {
917 let slot = occ.into_mut();
918 let is_cold = slot.is_cold_transaction_id(transaction_id);
920 if skip_cold_load && is_cold {
921 return Err(JournalLoadError::ColdLoadSkipped);
922 }
923 slot.mark_warm_with_transaction_id(transaction_id);
924 (slot.present_value, is_cold)
925 }
926 Entry::Vacant(vac) => {
927 if skip_cold_load {
928 return Err(JournalLoadError::ColdLoadSkipped);
929 }
930 let value = if is_newly_created {
932 StorageValue::ZERO
933 } else {
934 db.storage(address, key)?
935 };
936 vac.insert(EvmStorageSlot::new(value, transaction_id));
937
938 (value, true)
939 }
940 };
941
942 if is_cold {
943 journal.push(ENTRY::storage_warmed(address, key));
945 }
946
947 Ok(StateLoad::new(value, is_cold))
948}