1use super::warm_addresses::WarmAddresses;
3use bytecode::Bytecode;
4use context_interface::{
5 context::{SStoreResult, SelfDestructResult, StateLoad},
6 journaled_state::{
7 account::JournaledAccount,
8 entry::{JournalEntryTr, SelfdestructionRevertStatus},
9 },
10 journaled_state::{AccountLoad, JournalCheckpoint, JournalLoadError, TransferError},
11};
12use core::mem;
13use database_interface::Database;
14use primitives::{
15 hardfork::SpecId::{self, *},
16 hash_map::Entry,
17 Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
18};
19use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
20use std::vec::Vec;
21#[derive(Debug, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct JournalInner<ENTRY> {
27 pub state: EvmState,
29 pub transient_storage: TransientStorage,
33 pub logs: Vec<Log>,
35 pub depth: usize,
37 pub journal: Vec<ENTRY>,
39 pub transaction_id: usize,
45 pub spec: SpecId,
58 pub warm_addresses: WarmAddresses,
60}
61
62impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
69 pub fn new() -> JournalInner<ENTRY> {
74 Self {
75 state: HashMap::default(),
76 transient_storage: TransientStorage::default(),
77 logs: Vec::new(),
78 journal: Vec::default(),
79 transaction_id: 0,
80 depth: 0,
81 spec: SpecId::default(),
82 warm_addresses: WarmAddresses::new(),
83 }
84 }
85
86 #[inline]
88 pub fn take_logs(&mut self) -> Vec<Log> {
89 mem::take(&mut self.logs)
90 }
91
92 pub fn commit_tx(&mut self) {
100 let Self {
103 state,
104 transient_storage,
105 logs,
106 depth,
107 journal,
108 transaction_id,
109 spec,
110 warm_addresses,
111 } = self;
112 let _ = spec;
114 let _ = state;
115 transient_storage.clear();
116 *depth = 0;
117
118 journal.clear();
120
121 warm_addresses.clear_coinbase_and_access_list();
123 *transaction_id += 1;
125 logs.clear();
126 }
127
128 pub fn discard_tx(&mut self) {
130 let Self {
132 state,
133 transient_storage,
134 logs,
135 depth,
136 journal,
137 transaction_id,
138 spec,
139 warm_addresses,
140 } = self;
141 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
142 journal.drain(..).rev().for_each(|entry| {
144 entry.revert(state, None, is_spurious_dragon_enabled);
145 });
146 transient_storage.clear();
147 *depth = 0;
148 logs.clear();
149 *transaction_id += 1;
150
151 warm_addresses.clear_coinbase_and_access_list();
153 }
154
155 #[inline]
160 pub fn finalize(&mut self) -> EvmState {
161 let Self {
164 state,
165 transient_storage,
166 logs,
167 depth,
168 journal,
169 transaction_id,
170 spec,
171 warm_addresses,
172 } = self;
173 let _ = spec;
175 warm_addresses.clear_coinbase_and_access_list();
177
178 let state = mem::take(state);
179 logs.clear();
180 transient_storage.clear();
181
182 journal.clear();
184 *depth = 0;
185 *transaction_id = 0;
187
188 state
189 }
190
191 #[inline]
193 pub fn state(&mut self) -> &mut EvmState {
194 &mut self.state
195 }
196
197 #[inline]
199 pub fn set_spec_id(&mut self, spec: SpecId) {
200 self.spec = spec;
201 }
202
203 #[inline]
207 pub fn touch(&mut self, address: Address) {
208 if let Some(account) = self.state.get_mut(&address) {
209 Self::touch_account(&mut self.journal, address, account);
210 }
211 }
212
213 #[inline]
215 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
216 if !account.is_touched() {
217 journal.push(ENTRY::account_touched(address));
218 account.mark_touch();
219 }
220 }
221
222 #[inline]
230 pub fn account(&self, address: Address) -> &Account {
231 self.state
232 .get(&address)
233 .expect("Account expected to be loaded") }
235
236 #[inline]
240 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
241 let account = self.state.get_mut(&address).unwrap();
242 Self::touch_account(&mut self.journal, address, account);
243
244 self.journal.push(ENTRY::code_changed(address));
245
246 account.info.code_hash = hash;
247 account.info.code = Some(code);
248 }
249
250 #[inline]
256 pub fn set_code(&mut self, address: Address, code: Bytecode) {
257 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
258 if eip7702_bytecode.address().is_zero() {
259 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
260 return;
261 }
262 }
263
264 let hash = code.hash_slow();
265 self.set_code_with_hash(address, code, hash)
266 }
267
268 #[inline]
270 pub fn caller_accounting_journal_entry(
271 &mut self,
272 address: Address,
273 old_balance: U256,
274 bump_nonce: bool,
275 ) {
276 self.journal
278 .push(ENTRY::balance_changed(address, old_balance));
279 self.journal.push(ENTRY::account_touched(address));
281
282 if bump_nonce {
283 self.journal.push(ENTRY::nonce_changed(address));
285 }
286 }
287
288 #[inline]
292 pub fn balance_incr<DB: Database>(
293 &mut self,
294 db: &mut DB,
295 address: Address,
296 balance: U256,
297 ) -> Result<(), DB::Error> {
298 let mut account = self.load_account_mut(db, address)?.data;
299 account.incr_balance(balance);
300 Ok(())
301 }
302
303 #[inline]
305 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
306 self.journal.push(ENTRY::nonce_changed(address));
307 }
308
309 #[inline]
315 pub fn transfer_loaded(
316 &mut self,
317 from: Address,
318 to: Address,
319 balance: U256,
320 ) -> Option<TransferError> {
321 if from == to {
322 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
323 if balance > from_balance {
325 return Some(TransferError::OutOfFunds);
326 }
327 return None;
328 }
329
330 if balance.is_zero() {
331 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
332 return None;
333 }
334
335 let from_account = self.state.get_mut(&from).unwrap();
337 Self::touch_account(&mut self.journal, from, from_account);
338 let from_balance = &mut from_account.info.balance;
339 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
340 return Some(TransferError::OutOfFunds);
341 };
342 *from_balance = from_balance_decr;
343
344 let to_account = self.state.get_mut(&to).unwrap();
346 Self::touch_account(&mut self.journal, to, to_account);
347 let to_balance = &mut to_account.info.balance;
348 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
349 return Some(TransferError::OverflowPayment);
351 };
352 *to_balance = to_balance_incr;
353
354 self.journal
356 .push(ENTRY::balance_transfer(from, to, balance));
357
358 None
359 }
360
361 #[inline]
363 pub fn transfer<DB: Database>(
364 &mut self,
365 db: &mut DB,
366 from: Address,
367 to: Address,
368 balance: U256,
369 ) -> Result<Option<TransferError>, DB::Error> {
370 self.load_account(db, from)?;
371 self.load_account(db, to)?;
372 Ok(self.transfer_loaded(from, to, balance))
373 }
374
375 #[inline]
391 pub fn create_account_checkpoint(
392 &mut self,
393 caller: Address,
394 target_address: Address,
395 balance: U256,
396 spec_id: SpecId,
397 ) -> Result<JournalCheckpoint, TransferError> {
398 let checkpoint = self.checkpoint();
400
401 let target_acc = self.state.get_mut(&target_address).unwrap();
403 let last_journal = &mut self.journal;
404
405 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
410 self.checkpoint_revert(checkpoint);
411 return Err(TransferError::CreateCollision);
412 }
413
414 let is_created_globally = target_acc.mark_created_locally();
416
417 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
419 target_acc.info.code = None;
420 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
422 target_acc.info.nonce = 1;
424 }
425
426 Self::touch_account(last_journal, target_address, target_acc);
429
430 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
432 self.checkpoint_revert(checkpoint);
433 return Err(TransferError::OverflowPayment);
434 };
435 target_acc.info.balance = new_balance;
436
437 self.state.get_mut(&caller).unwrap().info.balance -= balance;
439
440 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
442
443 Ok(checkpoint)
444 }
445
446 #[inline]
448 pub fn checkpoint(&mut self) -> JournalCheckpoint {
449 let checkpoint = JournalCheckpoint {
450 log_i: self.logs.len(),
451 journal_i: self.journal.len(),
452 };
453 self.depth += 1;
454 checkpoint
455 }
456
457 #[inline]
459 pub fn checkpoint_commit(&mut self) {
460 self.depth = self.depth.saturating_sub(1);
461 }
462
463 #[inline]
465 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
466 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
467 let state = &mut self.state;
468 let transient_storage = &mut self.transient_storage;
469 self.depth = self.depth.saturating_sub(1);
470 self.logs.truncate(checkpoint.log_i);
471
472 if checkpoint.journal_i < self.journal.len() {
474 self.journal
475 .drain(checkpoint.journal_i..)
476 .rev()
477 .for_each(|entry| {
478 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
479 });
480 }
481 }
482
483 #[inline]
495 pub fn selfdestruct<DB: Database>(
496 &mut self,
497 db: &mut DB,
498 address: Address,
499 target: Address,
500 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
501 let spec = self.spec;
502 let account_load = self.load_account(db, target)?;
503 let is_cold = account_load.is_cold;
504 let is_empty = account_load.state_clear_aware_is_empty(spec);
505
506 if address != target {
507 let acc_balance = self.state.get(&address).unwrap().info.balance;
510
511 let target_account = self.state.get_mut(&target).unwrap();
512 Self::touch_account(&mut self.journal, target, target_account);
513 target_account.info.balance += acc_balance;
514 }
515
516 let acc = self.state.get_mut(&address).unwrap();
517 let balance = acc.info.balance;
518
519 let destroyed_status = if !acc.is_selfdestructed() {
520 SelfdestructionRevertStatus::GloballySelfdestroyed
521 } else if !acc.is_selfdestructed_locally() {
522 SelfdestructionRevertStatus::LocallySelfdestroyed
523 } else {
524 SelfdestructionRevertStatus::RepeatedSelfdestruction
525 };
526
527 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
528
529 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
531 acc.mark_selfdestructed_locally();
532 acc.info.balance = U256::ZERO;
533 Some(ENTRY::account_destroyed(
534 address,
535 target,
536 destroyed_status,
537 balance,
538 ))
539 } else if address != target {
540 acc.info.balance = U256::ZERO;
541 Some(ENTRY::balance_transfer(address, target, balance))
542 } else {
543 None
548 };
549
550 if let Some(entry) = journal_entry {
551 self.journal.push(entry);
552 };
553
554 Ok(StateLoad {
555 data: SelfDestructResult {
556 had_value: !balance.is_zero(),
557 target_exists: !is_empty,
558 previously_destroyed: destroyed_status
559 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
560 },
561 is_cold,
562 })
563 }
564
565 #[inline]
567 pub fn load_account<DB: Database>(
568 &mut self,
569 db: &mut DB,
570 address: Address,
571 ) -> Result<StateLoad<&Account>, DB::Error> {
572 self.load_account_optional(db, address, false, false)
573 .map_err(JournalLoadError::unwrap_db_error)
574 }
575
576 #[inline]
584 pub fn load_account_delegated<DB: Database>(
585 &mut self,
586 db: &mut DB,
587 address: Address,
588 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
589 let spec = self.spec;
590 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
591 let account = self
592 .load_account_optional(db, address, is_eip7702_enabled, false)
593 .map_err(JournalLoadError::unwrap_db_error)?;
594 let is_empty = account.state_clear_aware_is_empty(spec);
595
596 let mut account_load = StateLoad::new(
597 AccountLoad {
598 is_delegate_account_cold: None,
599 is_empty,
600 },
601 account.is_cold,
602 );
603
604 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
606 let address = code.address();
607 let delegate_account = self
608 .load_account_optional(db, address, true, false)
609 .map_err(JournalLoadError::unwrap_db_error)?;
610 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
611 }
612
613 Ok(account_load)
614 }
615
616 #[inline]
623 pub fn load_code<DB: Database>(
624 &mut self,
625 db: &mut DB,
626 address: Address,
627 ) -> Result<StateLoad<&Account>, DB::Error> {
628 self.load_account_optional(db, address, true, false)
629 .map_err(JournalLoadError::unwrap_db_error)
630 }
631
632 #[inline]
634 pub fn load_account_optional<DB: Database>(
635 &mut self,
636 db: &mut DB,
637 address: Address,
638 load_code: bool,
639 skip_cold_load: bool,
640 ) -> Result<StateLoad<&Account>, JournalLoadError<DB::Error>> {
641 let load = self.load_account_mut_optional_code(db, address, load_code, skip_cold_load)?;
642 Ok(load.map(|i| i.into_account_ref()))
643 }
644
645 #[inline]
647 pub fn load_account_mut<DB: Database>(
648 &mut self,
649 db: &mut DB,
650 address: Address,
651 ) -> Result<StateLoad<JournaledAccount<'_, ENTRY>>, DB::Error> {
652 self.load_account_mut_optional_code(db, address, false, false)
653 .map_err(JournalLoadError::unwrap_db_error)
654 }
655
656 #[inline(never)]
658 pub fn load_account_mut_optional_code<DB: Database>(
659 &mut self,
660 db: &mut DB,
661 address: Address,
662 load_code: bool,
663 skip_cold_load: bool,
664 ) -> Result<StateLoad<JournaledAccount<'_, ENTRY>>, JournalLoadError<DB::Error>> {
665 let load = match self.state.entry(address) {
666 Entry::Occupied(entry) => {
667 let account = entry.into_mut();
668
669 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
671 if is_cold {
672 let should_be_cold = self.warm_addresses.is_cold(&address);
674
675 if should_be_cold && skip_cold_load {
677 return Err(JournalLoadError::ColdLoadSkipped);
678 }
679 is_cold = should_be_cold;
680
681 account.mark_warm_with_transaction_id(self.transaction_id);
683
684 if account.is_selfdestructed_locally() {
687 account.selfdestruct();
688 account.unmark_selfdestructed_locally();
689 }
690 account.unmark_created_locally();
692 }
693 StateLoad {
694 data: account,
695 is_cold,
696 }
697 }
698 Entry::Vacant(vac) => {
699 let is_cold = self.warm_addresses.is_cold(&address);
701
702 if is_cold && skip_cold_load {
704 return Err(JournalLoadError::ColdLoadSkipped);
705 }
706 let account = if let Some(account) = db.basic(address)? {
707 account.into()
708 } else {
709 Account::new_not_existing(self.transaction_id)
710 };
711
712 StateLoad {
713 data: vac.insert(account),
714 is_cold,
715 }
716 }
717 };
718
719 if load.is_cold {
721 self.journal.push(ENTRY::account_warmed(address));
722 }
723
724 if load_code && load.data.info.code.is_none() {
725 let info = &mut load.data.info;
726 let code = if info.code_hash == KECCAK_EMPTY {
727 Bytecode::default()
728 } else {
729 db.code_by_hash(info.code_hash)?
730 };
731 info.code = Some(code);
732 }
733
734 Ok(load.map(|i| JournaledAccount::new(address, i, &mut self.journal)))
735 }
736
737 #[inline]
743 pub fn sload<DB: Database>(
744 &mut self,
745 db: &mut DB,
746 address: Address,
747 key: StorageKey,
748 skip_cold_load: bool,
749 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
750 let account = self.state.get_mut(&address).unwrap();
752
753 let is_newly_created = account.is_created();
754 let (value, is_cold) = match account.storage.entry(key) {
755 Entry::Occupied(occ) => {
756 let slot = occ.into_mut();
757 let is_cold = slot.is_cold_transaction_id(self.transaction_id);
759 if skip_cold_load && is_cold {
760 return Err(JournalLoadError::ColdLoadSkipped);
761 }
762 slot.mark_warm_with_transaction_id(self.transaction_id);
763 (slot.present_value, is_cold)
764 }
765 Entry::Vacant(vac) => {
766 if skip_cold_load {
767 return Err(JournalLoadError::ColdLoadSkipped);
768 }
769 let value = if is_newly_created {
771 StorageValue::ZERO
772 } else {
773 db.storage(address, key)?
774 };
775 vac.insert(EvmStorageSlot::new(value, self.transaction_id));
776
777 let is_cold = !self.warm_addresses.is_storage_warm(&address, &key);
779
780 (value, is_cold)
781 }
782 };
783
784 if is_cold {
785 self.journal.push(ENTRY::storage_warmed(address, key));
787 }
788
789 Ok(StateLoad::new(value, is_cold))
790 }
791
792 #[inline]
798 pub fn sstore<DB: Database>(
799 &mut self,
800 db: &mut DB,
801 address: Address,
802 key: StorageKey,
803 new: StorageValue,
804 skip_cold_load: bool,
805 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
806 let present = self.sload(db, address, key, skip_cold_load)?;
808 let acc = self.state.get_mut(&address).unwrap();
809
810 let slot = acc.storage.get_mut(&key).unwrap();
812
813 if present.data == new {
815 return Ok(StateLoad::new(
816 SStoreResult {
817 original_value: slot.original_value(),
818 present_value: present.data,
819 new_value: new,
820 },
821 present.is_cold,
822 ));
823 }
824
825 self.journal
826 .push(ENTRY::storage_changed(address, key, present.data));
827 slot.present_value = new;
829 Ok(StateLoad::new(
830 SStoreResult {
831 original_value: slot.original_value(),
832 present_value: present.data,
833 new_value: new,
834 },
835 present.is_cold,
836 ))
837 }
838
839 #[inline]
843 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
844 self.transient_storage
845 .get(&(address, key))
846 .copied()
847 .unwrap_or_default()
848 }
849
850 #[inline]
857 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
858 let had_value = if new.is_zero() {
859 self.transient_storage.remove(&(address, key))
863 } else {
864 let previous_value = self
866 .transient_storage
867 .insert((address, key), new)
868 .unwrap_or_default();
869
870 if previous_value != new {
872 Some(previous_value)
874 } else {
875 None
876 }
877 };
878
879 if let Some(had_value) = had_value {
880 self.journal
882 .push(ENTRY::transient_storage_changed(address, key, had_value));
883 }
884 }
885
886 #[inline]
888 pub fn log(&mut self, log: Log) {
889 self.logs.push(log);
890 }
891}