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 eip7708::{ETH_TRANSFER_LOG_ADDRESS, ETH_TRANSFER_LOG_TOPIC, SELFDESTRUCT_LOG_TOPIC},
16 hardfork::SpecId::{self, *},
17 hash_map::Entry,
18 hints_util::unlikely,
19 Address, Bytes, HashMap, Log, LogData, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
20};
21use state::{Account, EvmState, TransientStorage};
22use std::vec::Vec;
23
24#[derive(Debug, Clone, Default, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub struct JournalCfg {
30 pub spec: SpecId,
43 pub eip7708_disabled: bool,
45 pub eip7708_delayed_burn_disabled: bool,
53}
54#[derive(Debug, Clone, PartialEq, Eq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
59pub struct JournalInner<ENTRY> {
60 pub state: EvmState,
62 pub transient_storage: TransientStorage,
66 pub logs: Vec<Log>,
68 pub depth: usize,
70 pub journal: Vec<ENTRY>,
72 pub transaction_id: usize,
78 pub cfg: JournalCfg,
80 pub warm_addresses: WarmAddresses,
82 pub selfdestructed_addresses: Vec<Address>,
92}
93
94impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
101 pub fn new() -> JournalInner<ENTRY> {
106 Self {
107 state: HashMap::default(),
108 transient_storage: TransientStorage::default(),
109 logs: Vec::new(),
110 journal: Vec::default(),
111 transaction_id: 0,
112 depth: 0,
113 cfg: JournalCfg::default(),
114 warm_addresses: WarmAddresses::new(),
115 selfdestructed_addresses: Vec::new(),
116 }
117 }
118
119 #[inline]
124 pub fn take_logs(&mut self) -> Vec<Log> {
125 self.eip7708_emit_selfdestruct_remaining_balance_logs();
127 mem::take(&mut self.logs)
128 }
129
130 pub fn commit_tx(&mut self) {
138 let Self {
141 state,
142 transient_storage,
143 logs,
144 depth,
145 journal,
146 transaction_id,
147 cfg,
148 warm_addresses,
149 selfdestructed_addresses,
150 } = self;
151 let _ = cfg;
153 let _ = state;
154 transient_storage.clear();
155 *depth = 0;
156
157 journal.clear();
159
160 warm_addresses.clear_coinbase_and_access_list();
162 *transaction_id += 1;
164
165 logs.clear();
166 selfdestructed_addresses.clear();
167 }
168
169 pub fn discard_tx(&mut self) {
171 let Self {
173 state,
174 transient_storage,
175 logs,
176 depth,
177 journal,
178 transaction_id,
179 cfg,
180 warm_addresses,
181 selfdestructed_addresses,
182 } = self;
183 let is_spurious_dragon_enabled = cfg.spec.is_enabled_in(SPURIOUS_DRAGON);
184 journal.drain(..).rev().for_each(|entry| {
186 entry.revert(state, None, is_spurious_dragon_enabled);
187 });
188 transient_storage.clear();
189 *depth = 0;
190 logs.clear();
191 selfdestructed_addresses.clear();
192 *transaction_id += 1;
193
194 warm_addresses.clear_coinbase_and_access_list();
196 }
197
198 #[inline]
203 pub fn finalize(&mut self) -> EvmState {
204 let Self {
207 state,
208 transient_storage,
209 logs,
210 depth,
211 journal,
212 transaction_id,
213 cfg,
214 warm_addresses,
215 selfdestructed_addresses,
216 } = self;
217 warm_addresses.clear_coinbase_and_access_list();
219 selfdestructed_addresses.clear();
220
221 let mut state = mem::take(state);
222
223 if !cfg.spec.is_enabled_in(SPURIOUS_DRAGON) {
228 for acc in state.values_mut() {
229 if acc.is_touched()
230 && acc.is_empty()
231 && !acc.is_selfdestructed()
232 && !acc.is_created()
233 {
234 if acc.is_loaded_as_not_existing() {
235 acc.mark_created();
237 } else {
238 acc.unmark_touch();
240 }
241 }
242 }
243 }
244
245 logs.clear();
246 transient_storage.clear();
247
248 journal.clear();
250 *depth = 0;
251 *transaction_id = 0;
253
254 state
255 }
256
257 #[inline]
270 pub fn eip7708_emit_selfdestruct_remaining_balance_logs(&mut self) {
271 if !self.cfg.spec.is_enabled_in(AMSTERDAM)
272 || self.cfg.eip7708_disabled
273 || self.cfg.eip7708_delayed_burn_disabled
274 {
275 return;
276 }
277
278 let mut addresses_with_balance: Vec<(Address, U256)> = self
280 .selfdestructed_addresses
281 .iter()
282 .filter_map(|address| {
283 self.state
284 .get(address)
285 .filter(|account| !account.info.balance.is_zero())
286 .map(|account| (*address, account.info.balance))
287 })
288 .collect();
289
290 addresses_with_balance.sort_by_key(|(addr, _)| *addr);
292
293 for (address, balance) in addresses_with_balance {
295 self.eip7708_selfdestruct_to_self_log(address, balance);
296 }
297 }
298
299 #[inline]
301 pub fn state(&mut self) -> &mut EvmState {
302 &mut self.state
303 }
304
305 #[inline]
307 pub fn set_spec_id(&mut self, spec: SpecId) {
308 self.cfg.spec = spec;
309 }
310
311 #[inline]
313 pub fn set_eip7708_config(&mut self, disabled: bool, delayed_burn_disabled: bool) {
314 self.cfg.eip7708_disabled = disabled;
315 self.cfg.eip7708_delayed_burn_disabled = delayed_burn_disabled;
316 }
317
318 #[inline]
322 pub fn touch(&mut self, address: Address) {
323 if let Some(account) = self.state.get_mut(&address) {
324 Self::touch_account(&mut self.journal, address, account);
325 }
326 }
327
328 #[inline]
330 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
331 if !account.is_touched() {
332 journal.push(ENTRY::account_touched(address));
333 account.mark_touch();
334 }
335 }
336
337 #[inline]
345 pub fn account(&self, address: Address) -> &Account {
346 self.state
347 .get(&address)
348 .expect("Account expected to be loaded") }
350
351 #[inline]
355 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
356 let account = self.state.get_mut(&address).unwrap();
357 Self::touch_account(&mut self.journal, address, account);
358
359 self.journal.push(ENTRY::code_changed(address));
360
361 account.info.code_hash = hash;
362 account.info.code = Some(code);
363 }
364
365 #[inline]
371 pub fn set_code(&mut self, address: Address, code: Bytecode) {
372 if let Some(eip7702_address) = code.eip7702_address() {
373 if eip7702_address.is_zero() {
374 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
375 return;
376 }
377 }
378
379 let hash = code.hash_slow();
380 self.set_code_with_hash(address, code, hash)
381 }
382
383 #[inline]
385 #[deprecated]
386 pub fn caller_accounting_journal_entry(
387 &mut self,
388 address: Address,
389 old_balance: U256,
390 bump_nonce: bool,
391 ) {
392 self.journal
394 .push(ENTRY::balance_changed(address, old_balance));
395 self.journal.push(ENTRY::account_touched(address));
397
398 if bump_nonce {
399 self.journal.push(ENTRY::nonce_bumped(address));
401 }
402 }
403
404 #[inline]
408 pub fn balance_incr<DB: Database>(
409 &mut self,
410 db: &mut DB,
411 address: Address,
412 balance: U256,
413 ) -> Result<(), DB::Error> {
414 let mut account = self.load_account_mut(db, address)?.data;
415 account.incr_balance(balance);
416 Ok(())
417 }
418
419 #[inline]
421 #[deprecated]
422 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
423 self.journal.push(ENTRY::nonce_bumped(address));
424 }
425
426 #[inline]
432 pub fn transfer_loaded(
433 &mut self,
434 from: Address,
435 to: Address,
436 balance: U256,
437 ) -> Option<TransferError> {
438 if from == to {
439 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
440 if balance > from_balance {
442 return Some(TransferError::OutOfFunds);
443 }
444 return None;
445 }
446
447 if balance.is_zero() {
448 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
449 return None;
450 }
451
452 let from_account = self.state.get_mut(&from).unwrap();
454 Self::touch_account(&mut self.journal, from, from_account);
455 let from_balance = &mut from_account.info.balance;
456 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
457 return Some(TransferError::OutOfFunds);
458 };
459 *from_balance = from_balance_decr;
460
461 let to_account = self.state.get_mut(&to).unwrap();
463 Self::touch_account(&mut self.journal, to, to_account);
464 let to_balance = &mut to_account.info.balance;
465 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
466 return Some(TransferError::OverflowPayment);
468 };
469 *to_balance = to_balance_incr;
470
471 self.journal
473 .push(ENTRY::balance_transfer(from, to, balance));
474
475 self.eip7708_transfer_log(from, to, balance);
477
478 None
479 }
480
481 #[inline]
483 pub fn transfer<DB: Database>(
484 &mut self,
485 db: &mut DB,
486 from: Address,
487 to: Address,
488 balance: U256,
489 ) -> Result<Option<TransferError>, DB::Error> {
490 self.load_account(db, from)?;
491 self.load_account(db, to)?;
492 Ok(self.transfer_loaded(from, to, balance))
493 }
494
495 #[inline]
511 pub fn create_account_checkpoint(
512 &mut self,
513 caller: Address,
514 target_address: Address,
515 balance: U256,
516 spec_id: SpecId,
517 ) -> Result<JournalCheckpoint, TransferError> {
518 let checkpoint = self.checkpoint();
520
521 let target_acc = self.state.get_mut(&target_address).unwrap();
523 let last_journal = &mut self.journal;
524
525 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
530 self.checkpoint_revert(checkpoint);
531 return Err(TransferError::CreateCollision);
532 }
533
534 let is_created_globally = target_acc.mark_created_locally();
536
537 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
539 target_acc.info.code = None;
540 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
542 target_acc.info.nonce = 1;
544 }
545
546 Self::touch_account(last_journal, target_address, target_acc);
549
550 if balance.is_zero() {
552 return Ok(checkpoint);
553 }
554
555 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
557 self.checkpoint_revert(checkpoint);
558 return Err(TransferError::OverflowPayment);
559 };
560 target_acc.info.balance = new_balance;
561
562 let caller_account = self.state.get_mut(&caller).unwrap();
564 caller_account.info.balance -= balance;
565
566 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
568
569 self.eip7708_transfer_log(caller, target_address, balance);
571
572 Ok(checkpoint)
573 }
574
575 #[inline]
577 pub fn checkpoint(&mut self) -> JournalCheckpoint {
578 let checkpoint = JournalCheckpoint {
579 log_i: self.logs.len(),
580 journal_i: self.journal.len(),
581 selfdestructed_i: self.selfdestructed_addresses.len(),
582 };
583 self.depth += 1;
584 checkpoint
585 }
586
587 #[inline]
589 pub fn checkpoint_commit(&mut self) {
590 self.depth = self.depth.saturating_sub(1);
591 }
592
593 #[inline]
595 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
596 let is_spurious_dragon_enabled = self.cfg.spec.is_enabled_in(SPURIOUS_DRAGON);
597 let state = &mut self.state;
598 let transient_storage = &mut self.transient_storage;
599 self.depth = self.depth.saturating_sub(1);
600 self.logs.truncate(checkpoint.log_i);
601 self.selfdestructed_addresses
603 .truncate(checkpoint.selfdestructed_i);
604
605 if checkpoint.journal_i < self.journal.len() {
607 self.journal
608 .drain(checkpoint.journal_i..)
609 .rev()
610 .for_each(|entry| {
611 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
612 });
613 }
614 }
615
616 #[inline]
628 pub fn selfdestruct<DB: Database>(
629 &mut self,
630 db: &mut DB,
631 address: Address,
632 target: Address,
633 skip_cold_load: bool,
634 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<DB::Error>> {
635 let spec = self.cfg.spec;
636 let account_load = self.load_account_optional(db, target, false, skip_cold_load)?;
637 let is_cold = account_load.is_cold;
638 let is_empty = account_load.state_clear_aware_is_empty(spec);
639
640 if address != target {
641 let acc_balance = self.state.get(&address).unwrap().info.balance;
644
645 let target_account = self.state.get_mut(&target).unwrap();
646 Self::touch_account(&mut self.journal, target, target_account);
647 target_account.info.balance += acc_balance;
648 }
649
650 let acc = self.state.get_mut(&address).unwrap();
651 let balance = acc.info.balance;
652
653 let destroyed_status = if !acc.is_selfdestructed() {
654 SelfdestructionRevertStatus::GloballySelfdestroyed
655 } else if !acc.is_selfdestructed_locally() {
656 SelfdestructionRevertStatus::LocallySelfdestroyed
657 } else {
658 SelfdestructionRevertStatus::RepeatedSelfdestruction
659 };
660
661 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
662
663 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
665 if destroyed_status == SelfdestructionRevertStatus::GloballySelfdestroyed
668 && !self.cfg.eip7708_delayed_burn_disabled
669 {
670 self.selfdestructed_addresses.push(address);
671 }
672
673 acc.mark_selfdestructed_locally();
674 acc.info.balance = U256::ZERO;
675
676 if target != address {
678 self.eip7708_transfer_log(address, target, balance);
680 } else {
681 self.eip7708_selfdestruct_to_self_log(address, balance);
683 }
684 Some(ENTRY::account_destroyed(
685 address,
686 target,
687 destroyed_status,
688 balance,
689 ))
690 } else if address != target {
691 acc.info.balance = U256::ZERO;
692 self.eip7708_transfer_log(address, target, balance);
695 Some(ENTRY::balance_transfer(address, target, balance))
696 } else {
697 None
702 };
703
704 if let Some(entry) = journal_entry {
705 self.journal.push(entry);
706 };
707
708 Ok(StateLoad {
709 data: SelfDestructResult {
710 had_value: !balance.is_zero(),
711 target_exists: !is_empty,
712 previously_destroyed: destroyed_status
713 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
714 },
715 is_cold,
716 })
717 }
718
719 #[inline]
721 pub fn load_account<'a, 'db, DB: Database>(
722 &'a mut self,
723 db: &'db mut DB,
724 address: Address,
725 ) -> Result<StateLoad<&'a Account>, DB::Error>
726 where
727 'db: 'a,
728 {
729 self.load_account_optional(db, address, false, false)
730 .map_err(JournalLoadError::unwrap_db_error)
731 }
732
733 #[inline]
741 pub fn load_account_delegated<DB: Database>(
742 &mut self,
743 db: &mut DB,
744 address: Address,
745 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
746 let spec = self.cfg.spec;
747 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
748 let account = self
749 .load_account_optional(db, address, is_eip7702_enabled, false)
750 .map_err(JournalLoadError::unwrap_db_error)?;
751 let is_empty = account.state_clear_aware_is_empty(spec);
752
753 let mut account_load = StateLoad::new(
754 AccountLoad {
755 is_delegate_account_cold: None,
756 is_empty,
757 },
758 account.is_cold,
759 );
760
761 if let Some(address) = account
763 .info
764 .code
765 .as_ref()
766 .and_then(Bytecode::eip7702_address)
767 {
768 let delegate_account = self
769 .load_account_optional(db, address, true, false)
770 .map_err(JournalLoadError::unwrap_db_error)?;
771 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
772 }
773
774 Ok(account_load)
775 }
776
777 #[inline]
784 pub fn load_code<'a, 'db, DB: Database>(
785 &'a mut self,
786 db: &'db mut DB,
787 address: Address,
788 ) -> Result<StateLoad<&'a Account>, DB::Error>
789 where
790 'db: 'a,
791 {
792 self.load_account_optional(db, address, true, false)
793 .map_err(JournalLoadError::unwrap_db_error)
794 }
795
796 #[inline]
798 pub fn load_account_optional<'a, 'db, DB: Database>(
799 &'a mut self,
800 db: &'db mut DB,
801 address: Address,
802 load_code: bool,
803 skip_cold_load: bool,
804 ) -> Result<StateLoad<&'a Account>, JournalLoadError<DB::Error>>
805 where
806 'db: 'a,
807 {
808 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
809 if load_code {
810 load.data.load_code_preserve_error()?;
811 }
812 Ok(load.map(|i| i.into_account()))
813 }
814
815 #[inline]
817 pub fn load_account_mut<'a, 'db, DB: Database>(
818 &'a mut self,
819 db: &'db mut DB,
820 address: Address,
821 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, DB::Error>
822 where
823 'db: 'a,
824 {
825 self.load_account_mut_optional(db, address, false)
826 .map_err(JournalLoadError::unwrap_db_error)
827 }
828
829 #[inline]
831 pub fn load_account_mut_optional_code<'a, 'db, DB: Database>(
832 &'a mut self,
833 db: &'db mut DB,
834 address: Address,
835 load_code: bool,
836 skip_cold_load: bool,
837 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
838 where
839 'db: 'a,
840 {
841 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
842 if load_code {
843 load.data.load_code_preserve_error()?;
844 }
845 Ok(load)
846 }
847
848 #[inline]
858 pub fn get_account_mut<'a, 'db, DB: Database>(
859 &'a mut self,
860 db: &'db mut DB,
861 address: Address,
862 ) -> Option<JournaledAccount<'a, DB, ENTRY>>
863 where
864 'db: 'a,
865 {
866 let account = self.state.get_mut(&address)?;
867 Some(JournaledAccount::new(
868 address,
869 account,
870 &mut self.journal,
871 db,
872 self.warm_addresses.access_list(),
873 self.transaction_id,
874 ))
875 }
876
877 #[inline(never)]
879 pub fn load_account_mut_optional<'a, 'db, DB: Database>(
880 &'a mut self,
881 db: &'db mut DB,
882 address: Address,
883 skip_cold_load: bool,
884 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
885 where
886 'db: 'a,
887 {
888 let (account, is_cold) = match self.state.entry(address) {
889 Entry::Occupied(entry) => {
890 let account = entry.into_mut();
891
892 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
894
895 if unlikely(is_cold) {
896 is_cold = self
897 .warm_addresses
898 .check_is_cold(&address, skip_cold_load)?;
899
900 account.mark_warm_with_transaction_id(self.transaction_id);
902
903 if account.is_selfdestructed_locally() {
906 account.selfdestruct();
907 account.unmark_selfdestructed_locally();
908 }
909 *account.original_info = account.info.clone();
911
912 account.unmark_created_locally();
914
915 self.journal.push(ENTRY::account_warmed(address));
917 }
918 (account, is_cold)
919 }
920 Entry::Vacant(vac) => {
921 let is_cold = self
924 .warm_addresses
925 .check_is_cold(&address, skip_cold_load)?;
926
927 let account = if let Some(account) = db.basic(address)? {
928 let mut account: Account = account.into();
929 account.transaction_id = self.transaction_id;
930 account
931 } else {
932 Account::new_not_existing(self.transaction_id)
933 };
934
935 if is_cold {
937 self.journal.push(ENTRY::account_warmed(address));
938 }
939
940 (vac.insert(account), is_cold)
941 }
942 };
943
944 Ok(StateLoad::new(
945 JournaledAccount::new(
946 address,
947 account,
948 &mut self.journal,
949 db,
950 self.warm_addresses.access_list(),
951 self.transaction_id,
952 ),
953 is_cold,
954 ))
955 }
956
957 #[inline]
959 pub fn sload<DB: Database>(
960 &mut self,
961 db: &mut DB,
962 address: Address,
963 key: StorageKey,
964 skip_cold_load: bool,
965 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
966 self.load_account_mut(db, address)?
967 .sload_concrete_error(key, skip_cold_load)
968 .map(|s| s.map(|s| s.present_value))
969 }
970
971 #[inline]
975 pub fn sload_assume_account_present<DB: Database>(
976 &mut self,
977 db: &mut DB,
978 address: Address,
979 key: StorageKey,
980 skip_cold_load: bool,
981 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
982 let Some(mut account) = self.get_account_mut(db, address) else {
983 return Err(JournalLoadError::ColdLoadSkipped);
984 };
985
986 account
987 .sload_concrete_error(key, skip_cold_load)
988 .map(|s| s.map(|s| s.present_value))
989 }
990
991 #[inline]
995 pub fn sstore<DB: Database>(
996 &mut self,
997 db: &mut DB,
998 address: Address,
999 key: StorageKey,
1000 new: StorageValue,
1001 skip_cold_load: bool,
1002 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
1003 self.load_account_mut(db, address)?
1004 .sstore_concrete_error(key, new, skip_cold_load)
1005 }
1006
1007 #[inline]
1013 pub fn sstore_assume_account_present<DB: Database>(
1014 &mut self,
1015 db: &mut DB,
1016 address: Address,
1017 key: StorageKey,
1018 new: StorageValue,
1019 skip_cold_load: bool,
1020 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
1021 let Some(mut account) = self.get_account_mut(db, address) else {
1022 return Err(JournalLoadError::ColdLoadSkipped);
1023 };
1024
1025 account.sstore_concrete_error(key, new, skip_cold_load)
1026 }
1027
1028 #[inline]
1032 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
1033 self.transient_storage
1034 .get(&(address, key))
1035 .copied()
1036 .unwrap_or_default()
1037 }
1038
1039 #[inline]
1046 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
1047 let had_value = if new.is_zero() {
1048 self.transient_storage.remove(&(address, key))
1052 } else {
1053 let previous_value = self
1055 .transient_storage
1056 .insert((address, key), new)
1057 .unwrap_or_default();
1058
1059 if previous_value != new {
1061 Some(previous_value)
1063 } else {
1064 None
1065 }
1066 };
1067
1068 if let Some(had_value) = had_value {
1069 self.journal
1071 .push(ENTRY::transient_storage_changed(address, key, had_value));
1072 }
1073 }
1074
1075 #[inline]
1077 pub fn log(&mut self, log: Log) {
1078 self.logs.push(log);
1079 }
1080
1081 #[inline]
1088 pub fn eip7708_transfer_log(&mut self, from: Address, to: Address, balance: U256) {
1089 if !self.cfg.spec.is_enabled_in(AMSTERDAM) || self.cfg.eip7708_disabled || balance.is_zero()
1091 {
1092 return;
1093 }
1094
1095 let topics = std::vec![
1101 ETH_TRANSFER_LOG_TOPIC,
1102 B256::left_padding_from(from.as_slice()),
1103 B256::left_padding_from(to.as_slice()),
1104 ];
1105 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1106
1107 self.logs.push(Log {
1108 address: ETH_TRANSFER_LOG_ADDRESS,
1109 data: LogData::new(topics, data).expect("3 topics is valid"),
1110 });
1111 }
1112
1113 #[inline]
1120 pub fn eip7708_selfdestruct_to_self_log(&mut self, address: Address, balance: U256) {
1121 if !self.cfg.spec.is_enabled_in(AMSTERDAM) || self.cfg.eip7708_disabled || balance.is_zero()
1123 {
1124 return;
1125 }
1126
1127 let topics = std::vec![
1132 SELFDESTRUCT_LOG_TOPIC,
1133 B256::left_padding_from(address.as_slice()),
1134 ];
1135 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1136
1137 self.logs.push(Log {
1138 address: ETH_TRANSFER_LOG_ADDRESS,
1139 data: LogData::new(topics, data).expect("2 topics is valid"),
1140 });
1141 }
1142}
1143
1144#[cfg(test)]
1145mod tests {
1146 use super::*;
1147 use context_interface::journaled_state::entry::JournalEntry;
1148 use database_interface::EmptyDB;
1149 use primitives::{address, HashSet, U256};
1150 use state::AccountInfo;
1151
1152 #[test]
1153 fn test_sload_skip_cold_load() {
1154 let mut journal = JournalInner::<JournalEntry>::new();
1155 let test_address = address!("1000000000000000000000000000000000000000");
1156 let test_key = U256::from(1);
1157
1158 let account_info = AccountInfo {
1160 balance: U256::from(1000),
1161 nonce: 1,
1162 code_hash: KECCAK_EMPTY,
1163 code: Some(Bytecode::default()),
1164 account_id: None,
1165 };
1166 journal
1167 .state
1168 .insert(test_address, Account::from(account_info));
1169
1170 let mut access_list = HashMap::default();
1172 let mut storage_keys = HashSet::default();
1173 storage_keys.insert(test_key);
1174 access_list.insert(test_address, storage_keys);
1175 journal.warm_addresses.set_access_list(access_list);
1176
1177 let mut db = EmptyDB::new();
1179 let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true);
1180
1181 assert!(result.is_ok());
1183 let state_load = result.unwrap();
1184 assert!(!state_load.is_cold); assert_eq!(state_load.data, U256::ZERO); }
1187}