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, 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]
320 pub fn transfer<DB: Database>(
321 &mut self,
322 db: &mut DB,
323 from: Address,
324 to: Address,
325 balance: U256,
326 ) -> Result<Option<TransferError>, DB::Error> {
327 if balance.is_zero() {
328 self.load_account(db, to)?;
329 let to_account = self.state.get_mut(&to).unwrap();
330 Self::touch_account(&mut self.journal, to, to_account);
331 return Ok(None);
332 }
333 self.load_account(db, from)?;
335 self.load_account(db, to)?;
336
337 let from_account = self.state.get_mut(&from).unwrap();
339 Self::touch_account(&mut self.journal, from, from_account);
340 let from_balance = &mut from_account.info.balance;
341
342 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
343 return Ok(Some(TransferError::OutOfFunds));
344 };
345 *from_balance = from_balance_decr;
346
347 let to_account = &mut self.state.get_mut(&to).unwrap();
349 Self::touch_account(&mut self.journal, to, to_account);
350 let to_balance = &mut to_account.info.balance;
351 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
352 return Ok(Some(TransferError::OverflowPayment));
353 };
354 *to_balance = to_balance_incr;
355 self.journal
358 .push(ENTRY::balance_transfer(from, to, balance));
359
360 Ok(None)
361 }
362
363 #[inline]
379 pub fn create_account_checkpoint(
380 &mut self,
381 caller: Address,
382 target_address: Address,
383 balance: U256,
384 spec_id: SpecId,
385 ) -> Result<JournalCheckpoint, TransferError> {
386 let checkpoint = self.checkpoint();
388
389 let caller_balance = self.state.get(&caller).unwrap().info.balance;
391 if caller_balance < balance {
393 self.checkpoint_revert(checkpoint);
394 return Err(TransferError::OutOfFunds);
395 }
396
397 let target_acc = self.state.get_mut(&target_address).unwrap();
399 let last_journal = &mut self.journal;
400
401 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
406 self.checkpoint_revert(checkpoint);
407 return Err(TransferError::CreateCollision);
408 }
409
410 let is_created_globally = target_acc.mark_created_locally();
412
413 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
415 target_acc.info.code = None;
416 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
418 target_acc.info.nonce = 1;
420 }
421
422 Self::touch_account(last_journal, target_address, target_acc);
425
426 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
428 self.checkpoint_revert(checkpoint);
429 return Err(TransferError::OverflowPayment);
430 };
431 target_acc.info.balance = new_balance;
432
433 self.state.get_mut(&caller).unwrap().info.balance -= balance;
435
436 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
438
439 Ok(checkpoint)
440 }
441
442 #[inline]
444 pub fn checkpoint(&mut self) -> JournalCheckpoint {
445 let checkpoint = JournalCheckpoint {
446 log_i: self.logs.len(),
447 journal_i: self.journal.len(),
448 };
449 self.depth += 1;
450 checkpoint
451 }
452
453 #[inline]
455 pub fn checkpoint_commit(&mut self) {
456 self.depth -= 1;
457 }
458
459 #[inline]
461 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
462 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
463 let state = &mut self.state;
464 let transient_storage = &mut self.transient_storage;
465 self.depth -= 1;
466 self.logs.truncate(checkpoint.log_i);
467
468 self.journal
470 .drain(checkpoint.journal_i..)
471 .rev()
472 .for_each(|entry| {
473 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
474 });
475 }
476
477 #[inline]
489 pub fn selfdestruct<DB: Database>(
490 &mut self,
491 db: &mut DB,
492 address: Address,
493 target: Address,
494 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
495 let spec = self.spec;
496 let account_load = self.load_account(db, target)?;
497 let is_cold = account_load.is_cold;
498 let is_empty = account_load.state_clear_aware_is_empty(spec);
499
500 if address != target {
501 let acc_balance = self.state.get(&address).unwrap().info.balance;
504
505 let target_account = self.state.get_mut(&target).unwrap();
506 Self::touch_account(&mut self.journal, target, target_account);
507 target_account.info.balance += acc_balance;
508 }
509
510 let acc = self.state.get_mut(&address).unwrap();
511 let balance = acc.info.balance;
512
513 let destroyed_status = if !acc.is_selfdestructed() {
514 SelfdestructionRevertStatus::GloballySelfdestroyed
515 } else if !acc.is_selfdestructed_locally() {
516 SelfdestructionRevertStatus::LocallySelfdestroyed
517 } else {
518 SelfdestructionRevertStatus::RepeatedSelfdestruction
519 };
520
521 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
522
523 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
525 acc.mark_selfdestructed_locally();
526 acc.info.balance = U256::ZERO;
527 Some(ENTRY::account_destroyed(
528 address,
529 target,
530 destroyed_status,
531 balance,
532 ))
533 } else if address != target {
534 acc.info.balance = U256::ZERO;
535 Some(ENTRY::balance_transfer(address, target, balance))
536 } else {
537 None
542 };
543
544 if let Some(entry) = journal_entry {
545 self.journal.push(entry);
546 };
547
548 Ok(StateLoad {
549 data: SelfDestructResult {
550 had_value: !balance.is_zero(),
551 target_exists: !is_empty,
552 previously_destroyed: destroyed_status
553 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
554 },
555 is_cold,
556 })
557 }
558
559 #[inline]
561 pub fn load_account<DB: Database>(
562 &mut self,
563 db: &mut DB,
564 address: Address,
565 ) -> Result<StateLoad<&mut Account>, DB::Error> {
566 self.load_account_optional(db, address, false, [])
567 }
568
569 #[inline]
577 pub fn load_account_delegated<DB: Database>(
578 &mut self,
579 db: &mut DB,
580 address: Address,
581 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
582 let spec = self.spec;
583 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
584 let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
585 let is_empty = account.state_clear_aware_is_empty(spec);
586
587 let mut account_load = StateLoad::new(
588 AccountLoad {
589 is_delegate_account_cold: None,
590 is_empty,
591 },
592 account.is_cold,
593 );
594
595 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
597 let address = code.address();
598 let delegate_account = self.load_account(db, address)?;
599 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
600 }
601
602 Ok(account_load)
603 }
604
605 #[inline]
612 pub fn load_code<DB: Database>(
613 &mut self,
614 db: &mut DB,
615 address: Address,
616 ) -> Result<StateLoad<&mut Account>, DB::Error> {
617 self.load_account_optional(db, address, true, [])
618 }
619
620 #[inline]
622 pub fn load_account_optional<DB: Database>(
623 &mut self,
624 db: &mut DB,
625 address: Address,
626 load_code: bool,
627 storage_keys: impl IntoIterator<Item = StorageKey>,
628 ) -> Result<StateLoad<&mut Account>, DB::Error> {
629 let load = match self.state.entry(address) {
630 Entry::Occupied(entry) => {
631 let account = entry.into_mut();
632 let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
633 if is_cold {
635 if account.is_selfdestructed_locally() {
638 account.selfdestruct();
639 account.unmark_selfdestructed_locally();
640 }
641 account.unmark_created_locally();
643 }
644 StateLoad {
645 data: account,
646 is_cold,
647 }
648 }
649 Entry::Vacant(vac) => {
650 let account = if let Some(account) = db.basic(address)? {
651 account.into()
652 } else {
653 Account::new_not_existing(self.transaction_id)
654 };
655
656 let is_cold = self.warm_addresses.is_cold(&address);
658
659 StateLoad {
660 data: vac.insert(account),
661 is_cold,
662 }
663 }
664 };
665
666 if load.is_cold {
668 self.journal.push(ENTRY::account_warmed(address));
669 }
670 if load_code {
671 let info = &mut load.data.info;
672 if info.code.is_none() {
673 let code = if info.code_hash == KECCAK_EMPTY {
674 Bytecode::default()
675 } else {
676 db.code_by_hash(info.code_hash)?
677 };
678 info.code = Some(code);
679 }
680 }
681
682 for storage_key in storage_keys.into_iter() {
683 sload_with_account(
684 load.data,
685 db,
686 &mut self.journal,
687 self.transaction_id,
688 address,
689 storage_key,
690 )?;
691 }
692 Ok(load)
693 }
694
695 #[inline]
701 pub fn sload<DB: Database>(
702 &mut self,
703 db: &mut DB,
704 address: Address,
705 key: StorageKey,
706 ) -> Result<StateLoad<StorageValue>, DB::Error> {
707 let account = self.state.get_mut(&address).unwrap();
709 sload_with_account(
711 account,
712 db,
713 &mut self.journal,
714 self.transaction_id,
715 address,
716 key,
717 )
718 }
719
720 #[inline]
726 pub fn sstore<DB: Database>(
727 &mut self,
728 db: &mut DB,
729 address: Address,
730 key: StorageKey,
731 new: StorageValue,
732 ) -> Result<StateLoad<SStoreResult>, DB::Error> {
733 let present = self.sload(db, address, key)?;
735 let acc = self.state.get_mut(&address).unwrap();
736
737 let slot = acc.storage.get_mut(&key).unwrap();
739
740 if present.data == new {
742 return Ok(StateLoad::new(
743 SStoreResult {
744 original_value: slot.original_value(),
745 present_value: present.data,
746 new_value: new,
747 },
748 present.is_cold,
749 ));
750 }
751
752 self.journal
753 .push(ENTRY::storage_changed(address, key, present.data));
754 slot.present_value = new;
756 Ok(StateLoad::new(
757 SStoreResult {
758 original_value: slot.original_value(),
759 present_value: present.data,
760 new_value: new,
761 },
762 present.is_cold,
763 ))
764 }
765
766 #[inline]
770 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
771 self.transient_storage
772 .get(&(address, key))
773 .copied()
774 .unwrap_or_default()
775 }
776
777 #[inline]
784 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
785 let had_value = if new.is_zero() {
786 self.transient_storage.remove(&(address, key))
790 } else {
791 let previous_value = self
793 .transient_storage
794 .insert((address, key), new)
795 .unwrap_or_default();
796
797 if previous_value != new {
799 Some(previous_value)
801 } else {
802 None
803 }
804 };
805
806 if let Some(had_value) = had_value {
807 self.journal
809 .push(ENTRY::transient_storage_changed(address, key, had_value));
810 }
811 }
812
813 #[inline]
815 pub fn log(&mut self, log: Log) {
816 self.logs.push(log);
817 }
818}
819
820#[inline]
822pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
823 account: &mut Account,
824 db: &mut DB,
825 journal: &mut Vec<ENTRY>,
826 transaction_id: usize,
827 address: Address,
828 key: StorageKey,
829) -> Result<StateLoad<StorageValue>, DB::Error> {
830 let is_newly_created = account.is_created();
831 let (value, is_cold) = match account.storage.entry(key) {
832 Entry::Occupied(occ) => {
833 let slot = occ.into_mut();
834 let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
835 (slot.present_value, is_cold)
836 }
837 Entry::Vacant(vac) => {
838 let value = if is_newly_created {
840 StorageValue::ZERO
841 } else {
842 db.storage(address, key)?
843 };
844
845 vac.insert(EvmStorageSlot::new(value, transaction_id));
846
847 (value, true)
848 }
849 };
850
851 if is_cold {
852 journal.push(ENTRY::storage_warmed(address, key));
854 }
855
856 Ok(StateLoad::new(value, is_cold))
857}