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 if checkpoint.journal_i < self.journal.len() {
470 self.journal
471 .drain(checkpoint.journal_i..)
472 .rev()
473 .for_each(|entry| {
474 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
475 });
476 }
477 }
478
479 #[inline]
491 pub fn selfdestruct<DB: Database>(
492 &mut self,
493 db: &mut DB,
494 address: Address,
495 target: Address,
496 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
497 let spec = self.spec;
498 let account_load = self.load_account(db, target)?;
499 let is_cold = account_load.is_cold;
500 let is_empty = account_load.state_clear_aware_is_empty(spec);
501
502 if address != target {
503 let acc_balance = self.state.get(&address).unwrap().info.balance;
506
507 let target_account = self.state.get_mut(&target).unwrap();
508 Self::touch_account(&mut self.journal, target, target_account);
509 target_account.info.balance += acc_balance;
510 }
511
512 let acc = self.state.get_mut(&address).unwrap();
513 let balance = acc.info.balance;
514
515 let destroyed_status = if !acc.is_selfdestructed() {
516 SelfdestructionRevertStatus::GloballySelfdestroyed
517 } else if !acc.is_selfdestructed_locally() {
518 SelfdestructionRevertStatus::LocallySelfdestroyed
519 } else {
520 SelfdestructionRevertStatus::RepeatedSelfdestruction
521 };
522
523 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
524
525 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
527 acc.mark_selfdestructed_locally();
528 acc.info.balance = U256::ZERO;
529 Some(ENTRY::account_destroyed(
530 address,
531 target,
532 destroyed_status,
533 balance,
534 ))
535 } else if address != target {
536 acc.info.balance = U256::ZERO;
537 Some(ENTRY::balance_transfer(address, target, balance))
538 } else {
539 None
544 };
545
546 if let Some(entry) = journal_entry {
547 self.journal.push(entry);
548 };
549
550 Ok(StateLoad {
551 data: SelfDestructResult {
552 had_value: !balance.is_zero(),
553 target_exists: !is_empty,
554 previously_destroyed: destroyed_status
555 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
556 },
557 is_cold,
558 })
559 }
560
561 #[inline]
563 pub fn load_account<DB: Database>(
564 &mut self,
565 db: &mut DB,
566 address: Address,
567 ) -> Result<StateLoad<&mut Account>, DB::Error> {
568 self.load_account_optional(db, address, false, [])
569 }
570
571 #[inline]
579 pub fn load_account_delegated<DB: Database>(
580 &mut self,
581 db: &mut DB,
582 address: Address,
583 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
584 let spec = self.spec;
585 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
586 let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
587 let is_empty = account.state_clear_aware_is_empty(spec);
588
589 let mut account_load = StateLoad::new(
590 AccountLoad {
591 is_delegate_account_cold: None,
592 is_empty,
593 },
594 account.is_cold,
595 );
596
597 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
599 let address = code.address();
600 let delegate_account = self.load_account(db, address)?;
601 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
602 }
603
604 Ok(account_load)
605 }
606
607 #[inline]
614 pub fn load_code<DB: Database>(
615 &mut self,
616 db: &mut DB,
617 address: Address,
618 ) -> Result<StateLoad<&mut Account>, DB::Error> {
619 self.load_account_optional(db, address, true, [])
620 }
621
622 #[inline]
624 pub fn load_account_optional<DB: Database>(
625 &mut self,
626 db: &mut DB,
627 address: Address,
628 load_code: bool,
629 storage_keys: impl IntoIterator<Item = StorageKey>,
630 ) -> Result<StateLoad<&mut Account>, DB::Error> {
631 let load = match self.state.entry(address) {
632 Entry::Occupied(entry) => {
633 let account = entry.into_mut();
634 let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
635 if is_cold {
637 if account.is_selfdestructed_locally() {
640 account.selfdestruct();
641 account.unmark_selfdestructed_locally();
642 }
643 account.unmark_created_locally();
645 }
646 StateLoad {
647 data: account,
648 is_cold,
649 }
650 }
651 Entry::Vacant(vac) => {
652 let account = if let Some(account) = db.basic(address)? {
653 account.into()
654 } else {
655 Account::new_not_existing(self.transaction_id)
656 };
657
658 let is_cold = self.warm_addresses.is_cold(&address);
660
661 StateLoad {
662 data: vac.insert(account),
663 is_cold,
664 }
665 }
666 };
667
668 if load.is_cold {
670 self.journal.push(ENTRY::account_warmed(address));
671 }
672 if load_code {
673 let info = &mut load.data.info;
674 if info.code.is_none() {
675 let code = if info.code_hash == KECCAK_EMPTY {
676 Bytecode::default()
677 } else {
678 db.code_by_hash(info.code_hash)?
679 };
680 info.code = Some(code);
681 }
682 }
683
684 for storage_key in storage_keys.into_iter() {
685 sload_with_account(
686 load.data,
687 db,
688 &mut self.journal,
689 self.transaction_id,
690 address,
691 storage_key,
692 )?;
693 }
694 Ok(load)
695 }
696
697 #[inline]
703 pub fn sload<DB: Database>(
704 &mut self,
705 db: &mut DB,
706 address: Address,
707 key: StorageKey,
708 ) -> Result<StateLoad<StorageValue>, DB::Error> {
709 let account = self.state.get_mut(&address).unwrap();
711 sload_with_account(
713 account,
714 db,
715 &mut self.journal,
716 self.transaction_id,
717 address,
718 key,
719 )
720 }
721
722 #[inline]
728 pub fn sstore<DB: Database>(
729 &mut self,
730 db: &mut DB,
731 address: Address,
732 key: StorageKey,
733 new: StorageValue,
734 ) -> Result<StateLoad<SStoreResult>, DB::Error> {
735 let present = self.sload(db, address, key)?;
737 let acc = self.state.get_mut(&address).unwrap();
738
739 let slot = acc.storage.get_mut(&key).unwrap();
741
742 if present.data == new {
744 return Ok(StateLoad::new(
745 SStoreResult {
746 original_value: slot.original_value(),
747 present_value: present.data,
748 new_value: new,
749 },
750 present.is_cold,
751 ));
752 }
753
754 self.journal
755 .push(ENTRY::storage_changed(address, key, present.data));
756 slot.present_value = new;
758 Ok(StateLoad::new(
759 SStoreResult {
760 original_value: slot.original_value(),
761 present_value: present.data,
762 new_value: new,
763 },
764 present.is_cold,
765 ))
766 }
767
768 #[inline]
772 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
773 self.transient_storage
774 .get(&(address, key))
775 .copied()
776 .unwrap_or_default()
777 }
778
779 #[inline]
786 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
787 let had_value = if new.is_zero() {
788 self.transient_storage.remove(&(address, key))
792 } else {
793 let previous_value = self
795 .transient_storage
796 .insert((address, key), new)
797 .unwrap_or_default();
798
799 if previous_value != new {
801 Some(previous_value)
803 } else {
804 None
805 }
806 };
807
808 if let Some(had_value) = had_value {
809 self.journal
811 .push(ENTRY::transient_storage_changed(address, key, had_value));
812 }
813 }
814
815 #[inline]
817 pub fn log(&mut self, log: Log) {
818 self.logs.push(log);
819 }
820}
821
822#[inline]
824pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
825 account: &mut Account,
826 db: &mut DB,
827 journal: &mut Vec<ENTRY>,
828 transaction_id: usize,
829 address: Address,
830 key: StorageKey,
831) -> Result<StateLoad<StorageValue>, DB::Error> {
832 let is_newly_created = account.is_created();
833 let (value, is_cold) = match account.storage.entry(key) {
834 Entry::Occupied(occ) => {
835 let slot = occ.into_mut();
836 let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
837 (slot.present_value, is_cold)
838 }
839 Entry::Vacant(vac) => {
840 let value = if is_newly_created {
842 StorageValue::ZERO
843 } else {
844 db.storage(address, key)?
845 };
846
847 vac.insert(EvmStorageSlot::new(value, transaction_id));
848
849 (value, true)
850 }
851 };
852
853 if is_cold {
854 journal.push(ENTRY::storage_warmed(address, key));
856 }
857
858 Ok(StateLoad::new(value, is_cold))
859}