1use super::warm_addresses::WarmAddresses;
3use bytecode::Bytecode;
4use context_interface::{
5 context::{SStoreResult, SelfDestructResult, StateLoad},
6 journaled_state::{
7 account::{JournaledAccount, JournaledAccountTr},
8 entry::{JournalEntryTr, SelfdestructionRevertStatus},
9 AccountLoad, JournalCheckpoint, JournalLoadError, TransferError,
10 },
11};
12use core::mem;
13use database_interface::Database;
14use primitives::{
15 hardfork::SpecId::{self, *},
16 hash_map::Entry,
17 hints_util::unlikely,
18 Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
19};
20use state::{Account, EvmState, TransientStorage};
21use std::vec::Vec;
22#[derive(Debug, Clone, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct JournalInner<ENTRY> {
28 pub state: EvmState,
30 pub transient_storage: TransientStorage,
34 pub logs: Vec<Log>,
36 pub depth: usize,
38 pub journal: Vec<ENTRY>,
40 pub transaction_id: usize,
46 pub spec: SpecId,
59 pub warm_addresses: WarmAddresses,
61}
62
63impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
64 fn default() -> Self {
65 Self::new()
66 }
67}
68
69impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
70 pub fn new() -> JournalInner<ENTRY> {
75 Self {
76 state: HashMap::default(),
77 transient_storage: TransientStorage::default(),
78 logs: Vec::new(),
79 journal: Vec::default(),
80 transaction_id: 0,
81 depth: 0,
82 spec: SpecId::default(),
83 warm_addresses: WarmAddresses::new(),
84 }
85 }
86
87 #[inline]
89 pub fn take_logs(&mut self) -> Vec<Log> {
90 mem::take(&mut self.logs)
91 }
92
93 pub fn commit_tx(&mut self) {
101 let Self {
104 state,
105 transient_storage,
106 logs,
107 depth,
108 journal,
109 transaction_id,
110 spec,
111 warm_addresses,
112 } = self;
113 let _ = spec;
115 let _ = state;
116 transient_storage.clear();
117 *depth = 0;
118
119 journal.clear();
121
122 warm_addresses.clear_coinbase_and_access_list();
124 *transaction_id += 1;
126
127 logs.clear();
128 }
129
130 pub fn discard_tx(&mut self) {
132 let Self {
134 state,
135 transient_storage,
136 logs,
137 depth,
138 journal,
139 transaction_id,
140 spec,
141 warm_addresses,
142 } = self;
143 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
144 journal.drain(..).rev().for_each(|entry| {
146 entry.revert(state, None, is_spurious_dragon_enabled);
147 });
148 transient_storage.clear();
149 *depth = 0;
150 logs.clear();
151 *transaction_id += 1;
152
153 warm_addresses.clear_coinbase_and_access_list();
155 }
156
157 #[inline]
162 pub fn finalize(&mut self) -> EvmState {
163 let Self {
166 state,
167 transient_storage,
168 logs,
169 depth,
170 journal,
171 transaction_id,
172 spec,
173 warm_addresses,
174 } = self;
175 let _ = spec;
177 warm_addresses.clear_coinbase_and_access_list();
179
180 let state = mem::take(state);
181 logs.clear();
182 transient_storage.clear();
183
184 journal.clear();
186 *depth = 0;
187 *transaction_id = 0;
189
190 state
191 }
192
193 #[inline]
195 pub fn state(&mut self) -> &mut EvmState {
196 &mut self.state
197 }
198
199 #[inline]
201 pub fn set_spec_id(&mut self, spec: SpecId) {
202 self.spec = spec;
203 }
204
205 #[inline]
209 pub fn touch(&mut self, address: Address) {
210 if let Some(account) = self.state.get_mut(&address) {
211 Self::touch_account(&mut self.journal, address, account);
212 }
213 }
214
215 #[inline]
217 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
218 if !account.is_touched() {
219 journal.push(ENTRY::account_touched(address));
220 account.mark_touch();
221 }
222 }
223
224 #[inline]
232 pub fn account(&self, address: Address) -> &Account {
233 self.state
234 .get(&address)
235 .expect("Account expected to be loaded") }
237
238 #[inline]
242 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
243 let account = self.state.get_mut(&address).unwrap();
244 Self::touch_account(&mut self.journal, address, account);
245
246 self.journal.push(ENTRY::code_changed(address));
247
248 account.info.code_hash = hash;
249 account.info.code = Some(code);
250 }
251
252 #[inline]
258 pub fn set_code(&mut self, address: Address, code: Bytecode) {
259 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
260 if eip7702_bytecode.address().is_zero() {
261 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
262 return;
263 }
264 }
265
266 let hash = code.hash_slow();
267 self.set_code_with_hash(address, code, hash)
268 }
269
270 #[inline]
272 pub fn caller_accounting_journal_entry(
273 &mut self,
274 address: Address,
275 old_balance: U256,
276 bump_nonce: bool,
277 ) {
278 self.journal
280 .push(ENTRY::balance_changed(address, old_balance));
281 self.journal.push(ENTRY::account_touched(address));
283
284 if bump_nonce {
285 self.journal.push(ENTRY::nonce_bumped(address));
287 }
288 }
289
290 #[inline]
294 pub fn balance_incr<DB: Database>(
295 &mut self,
296 db: &mut DB,
297 address: Address,
298 balance: U256,
299 ) -> Result<(), DB::Error> {
300 let mut account = self.load_account_mut(db, address)?.data;
301 account.incr_balance(balance);
302 Ok(())
303 }
304
305 #[inline]
307 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
308 self.journal.push(ENTRY::nonce_bumped(address));
309 }
310
311 #[inline]
317 pub fn transfer_loaded(
318 &mut self,
319 from: Address,
320 to: Address,
321 balance: U256,
322 ) -> Option<TransferError> {
323 if from == to {
324 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
325 if balance > from_balance {
327 return Some(TransferError::OutOfFunds);
328 }
329 return None;
330 }
331
332 if balance.is_zero() {
333 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
334 return None;
335 }
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 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
342 return Some(TransferError::OutOfFunds);
343 };
344 *from_balance = from_balance_decr;
345
346 let to_account = self.state.get_mut(&to).unwrap();
348 Self::touch_account(&mut self.journal, to, to_account);
349 let to_balance = &mut to_account.info.balance;
350 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
351 return Some(TransferError::OverflowPayment);
353 };
354 *to_balance = to_balance_incr;
355
356 self.journal
358 .push(ENTRY::balance_transfer(from, to, balance));
359
360 None
361 }
362
363 #[inline]
365 pub fn transfer<DB: Database>(
366 &mut self,
367 db: &mut DB,
368 from: Address,
369 to: Address,
370 balance: U256,
371 ) -> Result<Option<TransferError>, DB::Error> {
372 self.load_account(db, from)?;
373 self.load_account(db, to)?;
374 Ok(self.transfer_loaded(from, to, balance))
375 }
376
377 #[inline]
393 pub fn create_account_checkpoint(
394 &mut self,
395 caller: Address,
396 target_address: Address,
397 balance: U256,
398 spec_id: SpecId,
399 ) -> Result<JournalCheckpoint, TransferError> {
400 let checkpoint = self.checkpoint();
402
403 let target_acc = self.state.get_mut(&target_address).unwrap();
405 let last_journal = &mut self.journal;
406
407 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
412 self.checkpoint_revert(checkpoint);
413 return Err(TransferError::CreateCollision);
414 }
415
416 let is_created_globally = target_acc.mark_created_locally();
418
419 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
421 target_acc.info.code = None;
422 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
424 target_acc.info.nonce = 1;
426 }
427
428 Self::touch_account(last_journal, target_address, target_acc);
431
432 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
434 self.checkpoint_revert(checkpoint);
435 return Err(TransferError::OverflowPayment);
436 };
437 target_acc.info.balance = new_balance;
438
439 let caller_account = self.state.get_mut(&caller).unwrap();
441 caller_account.info.balance -= balance;
442
443 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
445
446 Ok(checkpoint)
447 }
448
449 #[inline]
451 pub fn checkpoint(&mut self) -> JournalCheckpoint {
452 let checkpoint = JournalCheckpoint {
453 log_i: self.logs.len(),
454 journal_i: self.journal.len(),
455 };
456 self.depth += 1;
457 checkpoint
458 }
459
460 #[inline]
462 pub fn checkpoint_commit(&mut self) {
463 self.depth = self.depth.saturating_sub(1);
464 }
465
466 #[inline]
468 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
469 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
470 let state = &mut self.state;
471 let transient_storage = &mut self.transient_storage;
472 self.depth = self.depth.saturating_sub(1);
473 self.logs.truncate(checkpoint.log_i);
474
475 if checkpoint.journal_i < self.journal.len() {
477 self.journal
478 .drain(checkpoint.journal_i..)
479 .rev()
480 .for_each(|entry| {
481 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
482 });
483 }
484 }
485
486 #[inline]
498 pub fn selfdestruct<DB: Database>(
499 &mut self,
500 db: &mut DB,
501 address: Address,
502 target: Address,
503 skip_cold_load: bool,
504 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<DB::Error>> {
505 let spec = self.spec;
506 let account_load = self.load_account_optional(db, target, false, skip_cold_load)?;
507 let is_cold = account_load.is_cold;
508 let is_empty = account_load.state_clear_aware_is_empty(spec);
509
510 if address != target {
511 let acc_balance = self.state.get(&address).unwrap().info.balance;
514
515 let target_account = self.state.get_mut(&target).unwrap();
516 Self::touch_account(&mut self.journal, target, target_account);
517 target_account.info.balance += acc_balance;
518 }
519
520 let acc = self.state.get_mut(&address).unwrap();
521 let balance = acc.info.balance;
522
523 let destroyed_status = if !acc.is_selfdestructed() {
524 SelfdestructionRevertStatus::GloballySelfdestroyed
525 } else if !acc.is_selfdestructed_locally() {
526 SelfdestructionRevertStatus::LocallySelfdestroyed
527 } else {
528 SelfdestructionRevertStatus::RepeatedSelfdestruction
529 };
530
531 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
532
533 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
535 acc.mark_selfdestructed_locally();
536 acc.info.balance = U256::ZERO;
537 Some(ENTRY::account_destroyed(
538 address,
539 target,
540 destroyed_status,
541 balance,
542 ))
543 } else if address != target {
544 acc.info.balance = U256::ZERO;
545 Some(ENTRY::balance_transfer(address, target, balance))
546 } else {
547 None
552 };
553
554 if let Some(entry) = journal_entry {
555 self.journal.push(entry);
556 };
557
558 Ok(StateLoad {
559 data: SelfDestructResult {
560 had_value: !balance.is_zero(),
561 target_exists: !is_empty,
562 previously_destroyed: destroyed_status
563 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
564 },
565 is_cold,
566 })
567 }
568
569 #[inline]
571 pub fn load_account<'a, 'db, DB: Database>(
572 &'a mut self,
573 db: &'db mut DB,
574 address: Address,
575 ) -> Result<StateLoad<&'a Account>, DB::Error>
576 where
577 'db: 'a,
578 {
579 self.load_account_optional(db, address, false, false)
580 .map_err(JournalLoadError::unwrap_db_error)
581 }
582
583 #[inline]
591 pub fn load_account_delegated<DB: Database>(
592 &mut self,
593 db: &mut DB,
594 address: Address,
595 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
596 let spec = self.spec;
597 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
598 let account = self
599 .load_account_optional(db, address, is_eip7702_enabled, false)
600 .map_err(JournalLoadError::unwrap_db_error)?;
601 let is_empty = account.state_clear_aware_is_empty(spec);
602
603 let mut account_load = StateLoad::new(
604 AccountLoad {
605 is_delegate_account_cold: None,
606 is_empty,
607 },
608 account.is_cold,
609 );
610
611 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
613 let address = code.address();
614 let delegate_account = self
615 .load_account_optional(db, address, true, false)
616 .map_err(JournalLoadError::unwrap_db_error)?;
617 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
618 }
619
620 Ok(account_load)
621 }
622
623 #[inline]
630 pub fn load_code<'a, 'db, DB: Database>(
631 &'a mut self,
632 db: &'db mut DB,
633 address: Address,
634 ) -> Result<StateLoad<&'a Account>, DB::Error>
635 where
636 'db: 'a,
637 {
638 self.load_account_optional(db, address, true, false)
639 .map_err(JournalLoadError::unwrap_db_error)
640 }
641
642 #[inline]
644 pub fn load_account_optional<'a, 'db, DB: Database>(
645 &'a mut self,
646 db: &'db mut DB,
647 address: Address,
648 load_code: bool,
649 skip_cold_load: bool,
650 ) -> Result<StateLoad<&'a Account>, JournalLoadError<DB::Error>>
651 where
652 'db: 'a,
653 {
654 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
655 if load_code {
656 load.data.load_code_preserve_error()?;
657 }
658 Ok(load.map(|i| i.into_account()))
659 }
660
661 #[inline]
663 pub fn load_account_mut<'a, 'db, DB: Database>(
664 &'a mut self,
665 db: &'db mut DB,
666 address: Address,
667 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, DB::Error>
668 where
669 'db: 'a,
670 {
671 self.load_account_mut_optional(db, address, false)
672 .map_err(JournalLoadError::unwrap_db_error)
673 }
674
675 #[inline]
677 pub fn load_account_mut_optional_code<'a, 'db, DB: Database>(
678 &'a mut self,
679 db: &'db mut DB,
680 address: Address,
681 load_code: bool,
682 skip_cold_load: bool,
683 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
684 where
685 'db: 'a,
686 {
687 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
688 if load_code {
689 load.data.load_code_preserve_error()?;
690 }
691 Ok(load)
692 }
693
694 #[inline]
704 pub fn get_account_mut<'a, 'db, DB: Database>(
705 &'a mut self,
706 db: &'db mut DB,
707 address: Address,
708 ) -> Option<JournaledAccount<'a, DB, ENTRY>>
709 where
710 'db: 'a,
711 {
712 let account = self.state.get_mut(&address)?;
713 Some(JournaledAccount::new(
714 address,
715 account,
716 &mut self.journal,
717 db,
718 self.warm_addresses.access_list(),
719 self.transaction_id,
720 ))
721 }
722
723 #[inline(never)]
725 pub fn load_account_mut_optional<'a, 'db, DB: Database>(
726 &'a mut self,
727 db: &'db mut DB,
728 address: Address,
729 skip_cold_load: bool,
730 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
731 where
732 'db: 'a,
733 {
734 let (account, is_cold) = match self.state.entry(address) {
735 Entry::Occupied(entry) => {
736 let account = entry.into_mut();
737
738 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
740
741 if unlikely(is_cold) {
742 is_cold = self
743 .warm_addresses
744 .check_is_cold(&address, skip_cold_load)?;
745
746 account.mark_warm_with_transaction_id(self.transaction_id);
748
749 if account.is_selfdestructed_locally() {
752 account.selfdestruct();
753 account.unmark_selfdestructed_locally();
754 }
755 *account.original_info = account.info.clone();
757
758 account.unmark_created_locally();
760
761 self.journal.push(ENTRY::account_warmed(address));
763 }
764 (account, is_cold)
765 }
766 Entry::Vacant(vac) => {
767 let is_cold = self
770 .warm_addresses
771 .check_is_cold(&address, skip_cold_load)?;
772
773 let account = if let Some(account) = db.basic(address)? {
774 let mut account: Account = account.into();
775 account.transaction_id = self.transaction_id;
776 account
777 } else {
778 Account::new_not_existing(self.transaction_id)
779 };
780
781 if is_cold {
783 self.journal.push(ENTRY::account_warmed(address));
784 }
785
786 (vac.insert(account), is_cold)
787 }
788 };
789
790 Ok(StateLoad::new(
791 JournaledAccount::new(
792 address,
793 account,
794 &mut self.journal,
795 db,
796 self.warm_addresses.access_list(),
797 self.transaction_id,
798 ),
799 is_cold,
800 ))
801 }
802
803 #[inline]
805 pub fn sload<DB: Database>(
806 &mut self,
807 db: &mut DB,
808 address: Address,
809 key: StorageKey,
810 skip_cold_load: bool,
811 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
812 self.load_account_mut(db, address)?
813 .sload_concrete_error(key, skip_cold_load)
814 .map(|s| s.map(|s| s.present_value))
815 }
816
817 #[inline]
821 pub fn sload_assume_account_present<DB: Database>(
822 &mut self,
823 db: &mut DB,
824 address: Address,
825 key: StorageKey,
826 skip_cold_load: bool,
827 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
828 let Some(mut account) = self.get_account_mut(db, address) else {
829 return Err(JournalLoadError::ColdLoadSkipped);
830 };
831
832 account
833 .sload_concrete_error(key, skip_cold_load)
834 .map(|s| s.map(|s| s.present_value))
835 }
836
837 #[inline]
841 pub fn sstore<DB: Database>(
842 &mut self,
843 db: &mut DB,
844 address: Address,
845 key: StorageKey,
846 new: StorageValue,
847 skip_cold_load: bool,
848 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
849 self.load_account_mut(db, address)?
850 .sstore_concrete_error(key, new, skip_cold_load)
851 }
852
853 #[inline]
859 pub fn sstore_assume_account_present<DB: Database>(
860 &mut self,
861 db: &mut DB,
862 address: Address,
863 key: StorageKey,
864 new: StorageValue,
865 skip_cold_load: bool,
866 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
867 let Some(mut account) = self.get_account_mut(db, address) else {
868 return Err(JournalLoadError::ColdLoadSkipped);
869 };
870
871 account.sstore_concrete_error(key, new, skip_cold_load)
872 }
873
874 #[inline]
878 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
879 self.transient_storage
880 .get(&(address, key))
881 .copied()
882 .unwrap_or_default()
883 }
884
885 #[inline]
892 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
893 let had_value = if new.is_zero() {
894 self.transient_storage.remove(&(address, key))
898 } else {
899 let previous_value = self
901 .transient_storage
902 .insert((address, key), new)
903 .unwrap_or_default();
904
905 if previous_value != new {
907 Some(previous_value)
909 } else {
910 None
911 }
912 };
913
914 if let Some(had_value) = had_value {
915 self.journal
917 .push(ENTRY::transient_storage_changed(address, key, had_value));
918 }
919 }
920
921 #[inline]
923 pub fn log(&mut self, log: Log) {
924 self.logs.push(log);
925 }
926}
927
928#[cfg(test)]
929mod tests {
930 use super::*;
931 use context_interface::journaled_state::entry::JournalEntry;
932 use database_interface::EmptyDB;
933 use primitives::{address, HashSet, U256};
934 use state::AccountInfo;
935
936 #[test]
937 fn test_sload_skip_cold_load() {
938 let mut journal = JournalInner::<JournalEntry>::new();
939 let test_address = address!("1000000000000000000000000000000000000000");
940 let test_key = U256::from(1);
941
942 let account_info = AccountInfo {
944 balance: U256::from(1000),
945 nonce: 1,
946 code_hash: KECCAK_EMPTY,
947 code: Some(Bytecode::default()),
948 account_id: None,
949 };
950 journal
951 .state
952 .insert(test_address, Account::from(account_info));
953
954 let mut access_list = HashMap::default();
956 let mut storage_keys = HashSet::default();
957 storage_keys.insert(test_key);
958 access_list.insert(test_address, storage_keys);
959 journal.warm_addresses.set_access_list(access_list);
960
961 let mut db = EmptyDB::new();
963 let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true);
964
965 assert!(result.is_ok());
967 let state_load = result.unwrap();
968 assert!(!state_load.is_cold); assert_eq!(state_load.data, U256::ZERO); }
971}