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::{BURN_LOG_TOPIC, ETH_TRANSFER_LOG_ADDRESS, ETH_TRANSFER_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_burn_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_burn_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_burn_log(address, balance);
296 }
297 }
298
299 #[inline]
301 pub const fn state(&mut self) -> &mut EvmState {
302 &mut self.state
303 }
304
305 #[inline]
307 pub const fn set_spec_id(&mut self, spec: SpecId) {
308 self.cfg.spec = spec;
309 }
310
311 #[inline]
313 pub const 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 const 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 const 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_burn_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.set_current_info_as_original();
910
911 account.unmark_created_locally();
913
914 self.journal.push(ENTRY::account_warmed(address));
916 }
917 (account, is_cold)
918 }
919 Entry::Vacant(vac) => {
920 let is_cold = self
923 .warm_addresses
924 .check_is_cold(&address, skip_cold_load)?;
925
926 let account = if let Some(account) = db.basic(address)? {
927 let mut account: Account = account.into();
928 account.transaction_id = self.transaction_id;
929 account
930 } else {
931 Account::new_not_existing(self.transaction_id)
932 };
933
934 if is_cold {
936 self.journal.push(ENTRY::account_warmed(address));
937 }
938
939 (vac.insert(account), is_cold)
940 }
941 };
942
943 Ok(StateLoad::new(
944 JournaledAccount::new(
945 address,
946 account,
947 &mut self.journal,
948 db,
949 self.warm_addresses.access_list(),
950 self.transaction_id,
951 ),
952 is_cold,
953 ))
954 }
955
956 #[inline]
958 pub fn sload<DB: Database>(
959 &mut self,
960 db: &mut DB,
961 address: Address,
962 key: StorageKey,
963 skip_cold_load: bool,
964 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
965 self.load_account_mut(db, address)?
966 .sload_concrete_error(key, skip_cold_load)
967 .map(|s| s.map(|s| s.present_value))
968 }
969
970 #[inline]
974 pub fn sload_assume_account_present<DB: Database>(
975 &mut self,
976 db: &mut DB,
977 address: Address,
978 key: StorageKey,
979 skip_cold_load: bool,
980 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
981 let Some(mut account) = self.get_account_mut(db, address) else {
982 return Err(JournalLoadError::ColdLoadSkipped);
983 };
984
985 account
986 .sload_concrete_error(key, skip_cold_load)
987 .map(|s| s.map(|s| s.present_value))
988 }
989
990 #[inline]
994 pub fn sstore<DB: Database>(
995 &mut self,
996 db: &mut DB,
997 address: Address,
998 key: StorageKey,
999 new: StorageValue,
1000 skip_cold_load: bool,
1001 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
1002 self.load_account_mut(db, address)?
1003 .sstore_concrete_error(key, new, skip_cold_load)
1004 }
1005
1006 #[inline]
1012 pub fn sstore_assume_account_present<DB: Database>(
1013 &mut self,
1014 db: &mut DB,
1015 address: Address,
1016 key: StorageKey,
1017 new: StorageValue,
1018 skip_cold_load: bool,
1019 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
1020 let Some(mut account) = self.get_account_mut(db, address) else {
1021 return Err(JournalLoadError::ColdLoadSkipped);
1022 };
1023
1024 account.sstore_concrete_error(key, new, skip_cold_load)
1025 }
1026
1027 #[inline]
1031 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
1032 self.transient_storage
1033 .get(&(address, key))
1034 .copied()
1035 .unwrap_or_default()
1036 }
1037
1038 #[inline]
1045 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
1046 let had_value = if new.is_zero() {
1047 self.transient_storage.remove(&(address, key))
1051 } else {
1052 let previous_value = self
1054 .transient_storage
1055 .insert((address, key), new)
1056 .unwrap_or_default();
1057
1058 if previous_value != new {
1060 Some(previous_value)
1062 } else {
1063 None
1064 }
1065 };
1066
1067 if let Some(had_value) = had_value {
1068 self.journal
1070 .push(ENTRY::transient_storage_changed(address, key, had_value));
1071 }
1072 }
1073
1074 #[inline]
1076 pub fn log(&mut self, log: Log) {
1077 self.logs.push(log);
1078 }
1079
1080 #[inline]
1087 pub fn eip7708_transfer_log(&mut self, from: Address, to: Address, balance: U256) {
1088 if !self.cfg.spec.is_enabled_in(AMSTERDAM) || self.cfg.eip7708_disabled || balance.is_zero()
1090 {
1091 return;
1092 }
1093
1094 let topics = std::vec![
1100 ETH_TRANSFER_LOG_TOPIC,
1101 B256::left_padding_from(from.as_slice()),
1102 B256::left_padding_from(to.as_slice()),
1103 ];
1104 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1105
1106 self.logs.push(Log {
1107 address: ETH_TRANSFER_LOG_ADDRESS,
1108 data: LogData::new(topics, data).expect("3 topics is valid"),
1109 });
1110 }
1111
1112 #[inline]
1120 pub fn eip7708_burn_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![BURN_LOG_TOPIC, B256::left_padding_from(address.as_slice()),];
1132 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1133
1134 self.logs.push(Log {
1135 address: ETH_TRANSFER_LOG_ADDRESS,
1136 data: LogData::new(topics, data).expect("2 topics is valid"),
1137 });
1138 }
1139}
1140
1141#[cfg(test)]
1142mod tests {
1143 use super::*;
1144 use context_interface::journaled_state::entry::JournalEntry;
1145 use database_interface::EmptyDB;
1146 use primitives::{address, HashSet, U256};
1147 use state::AccountInfo;
1148
1149 #[test]
1150 fn test_sload_skip_cold_load() {
1151 let mut journal = JournalInner::<JournalEntry>::new();
1152 let test_address = address!("1000000000000000000000000000000000000000");
1153 let test_key = U256::from(1);
1154
1155 let account_info = AccountInfo {
1157 balance: U256::from(1000),
1158 nonce: 1,
1159 code_hash: KECCAK_EMPTY,
1160 code: Some(Bytecode::default()),
1161 account_id: None,
1162 };
1163 journal
1164 .state
1165 .insert(test_address, Account::from(account_info));
1166
1167 let mut access_list = HashMap::default();
1169 let mut storage_keys = HashSet::default();
1170 storage_keys.insert(test_key);
1171 access_list.insert(test_address, storage_keys);
1172 journal.warm_addresses.set_access_list(access_list);
1173
1174 let mut db = EmptyDB::new();
1176 let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true);
1177
1178 assert!(result.is_ok());
1180 let state_load = result.unwrap();
1181 assert!(!state_load.is_cold); assert_eq!(state_load.data, U256::ZERO); }
1184}