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 let _ = cfg;
219 warm_addresses.clear_coinbase_and_access_list();
221 selfdestructed_addresses.clear();
222
223 let state = mem::take(state);
224 logs.clear();
225 transient_storage.clear();
226
227 journal.clear();
229 *depth = 0;
230 *transaction_id = 0;
232
233 state
234 }
235
236 #[inline]
249 pub fn eip7708_emit_selfdestruct_remaining_balance_logs(&mut self) {
250 if !self.cfg.spec.is_enabled_in(AMSTERDAM)
251 || self.cfg.eip7708_disabled
252 || self.cfg.eip7708_delayed_burn_disabled
253 {
254 return;
255 }
256
257 let mut addresses_with_balance: Vec<(Address, U256)> = self
259 .selfdestructed_addresses
260 .iter()
261 .filter_map(|address| {
262 self.state
263 .get(address)
264 .filter(|account| !account.info.balance.is_zero())
265 .map(|account| (*address, account.info.balance))
266 })
267 .collect();
268
269 addresses_with_balance.sort_by_key(|(addr, _)| *addr);
271
272 for (address, balance) in addresses_with_balance {
274 self.eip7708_selfdestruct_to_self_log(address, balance);
275 }
276 }
277
278 #[inline]
280 pub fn state(&mut self) -> &mut EvmState {
281 &mut self.state
282 }
283
284 #[inline]
286 pub fn set_spec_id(&mut self, spec: SpecId) {
287 self.cfg.spec = spec;
288 }
289
290 #[inline]
292 pub fn set_eip7708_config(&mut self, disabled: bool, delayed_burn_disabled: bool) {
293 self.cfg.eip7708_disabled = disabled;
294 self.cfg.eip7708_delayed_burn_disabled = delayed_burn_disabled;
295 }
296
297 #[inline]
301 pub fn touch(&mut self, address: Address) {
302 if let Some(account) = self.state.get_mut(&address) {
303 Self::touch_account(&mut self.journal, address, account);
304 }
305 }
306
307 #[inline]
309 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
310 if !account.is_touched() {
311 journal.push(ENTRY::account_touched(address));
312 account.mark_touch();
313 }
314 }
315
316 #[inline]
324 pub fn account(&self, address: Address) -> &Account {
325 self.state
326 .get(&address)
327 .expect("Account expected to be loaded") }
329
330 #[inline]
334 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
335 let account = self.state.get_mut(&address).unwrap();
336 Self::touch_account(&mut self.journal, address, account);
337
338 self.journal.push(ENTRY::code_changed(address));
339
340 account.info.code_hash = hash;
341 account.info.code = Some(code);
342 }
343
344 #[inline]
350 pub fn set_code(&mut self, address: Address, code: Bytecode) {
351 if let Some(eip7702_address) = code.eip7702_address() {
352 if eip7702_address.is_zero() {
353 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
354 return;
355 }
356 }
357
358 let hash = code.hash_slow();
359 self.set_code_with_hash(address, code, hash)
360 }
361
362 #[inline]
364 #[deprecated]
365 pub fn caller_accounting_journal_entry(
366 &mut self,
367 address: Address,
368 old_balance: U256,
369 bump_nonce: bool,
370 ) {
371 self.journal
373 .push(ENTRY::balance_changed(address, old_balance));
374 self.journal.push(ENTRY::account_touched(address));
376
377 if bump_nonce {
378 self.journal.push(ENTRY::nonce_bumped(address));
380 }
381 }
382
383 #[inline]
387 pub fn balance_incr<DB: Database>(
388 &mut self,
389 db: &mut DB,
390 address: Address,
391 balance: U256,
392 ) -> Result<(), DB::Error> {
393 let mut account = self.load_account_mut(db, address)?.data;
394 account.incr_balance(balance);
395 Ok(())
396 }
397
398 #[inline]
400 #[deprecated]
401 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
402 self.journal.push(ENTRY::nonce_bumped(address));
403 }
404
405 #[inline]
411 pub fn transfer_loaded(
412 &mut self,
413 from: Address,
414 to: Address,
415 balance: U256,
416 ) -> Option<TransferError> {
417 if from == to {
418 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
419 if balance > from_balance {
421 return Some(TransferError::OutOfFunds);
422 }
423 return None;
424 }
425
426 if balance.is_zero() {
427 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
428 return None;
429 }
430
431 let from_account = self.state.get_mut(&from).unwrap();
433 Self::touch_account(&mut self.journal, from, from_account);
434 let from_balance = &mut from_account.info.balance;
435 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
436 return Some(TransferError::OutOfFunds);
437 };
438 *from_balance = from_balance_decr;
439
440 let to_account = self.state.get_mut(&to).unwrap();
442 Self::touch_account(&mut self.journal, to, to_account);
443 let to_balance = &mut to_account.info.balance;
444 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
445 return Some(TransferError::OverflowPayment);
447 };
448 *to_balance = to_balance_incr;
449
450 self.journal
452 .push(ENTRY::balance_transfer(from, to, balance));
453
454 self.eip7708_transfer_log(from, to, balance);
456
457 None
458 }
459
460 #[inline]
462 pub fn transfer<DB: Database>(
463 &mut self,
464 db: &mut DB,
465 from: Address,
466 to: Address,
467 balance: U256,
468 ) -> Result<Option<TransferError>, DB::Error> {
469 self.load_account(db, from)?;
470 self.load_account(db, to)?;
471 Ok(self.transfer_loaded(from, to, balance))
472 }
473
474 #[inline]
490 pub fn create_account_checkpoint(
491 &mut self,
492 caller: Address,
493 target_address: Address,
494 balance: U256,
495 spec_id: SpecId,
496 ) -> Result<JournalCheckpoint, TransferError> {
497 let checkpoint = self.checkpoint();
499
500 let target_acc = self.state.get_mut(&target_address).unwrap();
502 let last_journal = &mut self.journal;
503
504 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
509 self.checkpoint_revert(checkpoint);
510 return Err(TransferError::CreateCollision);
511 }
512
513 let is_created_globally = target_acc.mark_created_locally();
515
516 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
518 target_acc.info.code = None;
519 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
521 target_acc.info.nonce = 1;
523 }
524
525 Self::touch_account(last_journal, target_address, target_acc);
528
529 if balance.is_zero() {
531 return Ok(checkpoint);
532 }
533
534 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
536 self.checkpoint_revert(checkpoint);
537 return Err(TransferError::OverflowPayment);
538 };
539 target_acc.info.balance = new_balance;
540
541 let caller_account = self.state.get_mut(&caller).unwrap();
543 caller_account.info.balance -= balance;
544
545 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
547
548 self.eip7708_transfer_log(caller, target_address, balance);
550
551 Ok(checkpoint)
552 }
553
554 #[inline]
556 pub fn checkpoint(&mut self) -> JournalCheckpoint {
557 let checkpoint = JournalCheckpoint {
558 log_i: self.logs.len(),
559 journal_i: self.journal.len(),
560 selfdestructed_i: self.selfdestructed_addresses.len(),
561 };
562 self.depth += 1;
563 checkpoint
564 }
565
566 #[inline]
568 pub fn checkpoint_commit(&mut self) {
569 self.depth = self.depth.saturating_sub(1);
570 }
571
572 #[inline]
574 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
575 let is_spurious_dragon_enabled = self.cfg.spec.is_enabled_in(SPURIOUS_DRAGON);
576 let state = &mut self.state;
577 let transient_storage = &mut self.transient_storage;
578 self.depth = self.depth.saturating_sub(1);
579 self.logs.truncate(checkpoint.log_i);
580 self.selfdestructed_addresses
582 .truncate(checkpoint.selfdestructed_i);
583
584 if checkpoint.journal_i < self.journal.len() {
586 self.journal
587 .drain(checkpoint.journal_i..)
588 .rev()
589 .for_each(|entry| {
590 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
591 });
592 }
593 }
594
595 #[inline]
607 pub fn selfdestruct<DB: Database>(
608 &mut self,
609 db: &mut DB,
610 address: Address,
611 target: Address,
612 skip_cold_load: bool,
613 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<DB::Error>> {
614 let spec = self.cfg.spec;
615 let account_load = self.load_account_optional(db, target, false, skip_cold_load)?;
616 let is_cold = account_load.is_cold;
617 let is_empty = account_load.state_clear_aware_is_empty(spec);
618
619 if address != target {
620 let acc_balance = self.state.get(&address).unwrap().info.balance;
623
624 let target_account = self.state.get_mut(&target).unwrap();
625 Self::touch_account(&mut self.journal, target, target_account);
626 target_account.info.balance += acc_balance;
627 }
628
629 let acc = self.state.get_mut(&address).unwrap();
630 let balance = acc.info.balance;
631
632 let destroyed_status = if !acc.is_selfdestructed() {
633 SelfdestructionRevertStatus::GloballySelfdestroyed
634 } else if !acc.is_selfdestructed_locally() {
635 SelfdestructionRevertStatus::LocallySelfdestroyed
636 } else {
637 SelfdestructionRevertStatus::RepeatedSelfdestruction
638 };
639
640 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
641
642 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
644 if destroyed_status == SelfdestructionRevertStatus::GloballySelfdestroyed
647 && !self.cfg.eip7708_delayed_burn_disabled
648 {
649 self.selfdestructed_addresses.push(address);
650 }
651
652 acc.mark_selfdestructed_locally();
653 acc.info.balance = U256::ZERO;
654
655 if target != address {
657 self.eip7708_transfer_log(address, target, balance);
659 } else {
660 self.eip7708_selfdestruct_to_self_log(address, balance);
662 }
663 Some(ENTRY::account_destroyed(
664 address,
665 target,
666 destroyed_status,
667 balance,
668 ))
669 } else if address != target {
670 acc.info.balance = U256::ZERO;
671 self.eip7708_transfer_log(address, target, balance);
674 Some(ENTRY::balance_transfer(address, target, balance))
675 } else {
676 None
681 };
682
683 if let Some(entry) = journal_entry {
684 self.journal.push(entry);
685 };
686
687 Ok(StateLoad {
688 data: SelfDestructResult {
689 had_value: !balance.is_zero(),
690 target_exists: !is_empty,
691 previously_destroyed: destroyed_status
692 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
693 },
694 is_cold,
695 })
696 }
697
698 #[inline]
700 pub fn load_account<'a, 'db, DB: Database>(
701 &'a mut self,
702 db: &'db mut DB,
703 address: Address,
704 ) -> Result<StateLoad<&'a Account>, DB::Error>
705 where
706 'db: 'a,
707 {
708 self.load_account_optional(db, address, false, false)
709 .map_err(JournalLoadError::unwrap_db_error)
710 }
711
712 #[inline]
720 pub fn load_account_delegated<DB: Database>(
721 &mut self,
722 db: &mut DB,
723 address: Address,
724 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
725 let spec = self.cfg.spec;
726 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
727 let account = self
728 .load_account_optional(db, address, is_eip7702_enabled, false)
729 .map_err(JournalLoadError::unwrap_db_error)?;
730 let is_empty = account.state_clear_aware_is_empty(spec);
731
732 let mut account_load = StateLoad::new(
733 AccountLoad {
734 is_delegate_account_cold: None,
735 is_empty,
736 },
737 account.is_cold,
738 );
739
740 if let Some(address) = account
742 .info
743 .code
744 .as_ref()
745 .and_then(Bytecode::eip7702_address)
746 {
747 let delegate_account = self
748 .load_account_optional(db, address, true, false)
749 .map_err(JournalLoadError::unwrap_db_error)?;
750 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
751 }
752
753 Ok(account_load)
754 }
755
756 #[inline]
763 pub fn load_code<'a, 'db, DB: Database>(
764 &'a mut self,
765 db: &'db mut DB,
766 address: Address,
767 ) -> Result<StateLoad<&'a Account>, DB::Error>
768 where
769 'db: 'a,
770 {
771 self.load_account_optional(db, address, true, false)
772 .map_err(JournalLoadError::unwrap_db_error)
773 }
774
775 #[inline]
777 pub fn load_account_optional<'a, 'db, DB: Database>(
778 &'a mut self,
779 db: &'db mut DB,
780 address: Address,
781 load_code: bool,
782 skip_cold_load: bool,
783 ) -> Result<StateLoad<&'a Account>, JournalLoadError<DB::Error>>
784 where
785 'db: 'a,
786 {
787 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
788 if load_code {
789 load.data.load_code_preserve_error()?;
790 }
791 Ok(load.map(|i| i.into_account()))
792 }
793
794 #[inline]
796 pub fn load_account_mut<'a, 'db, DB: Database>(
797 &'a mut self,
798 db: &'db mut DB,
799 address: Address,
800 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, DB::Error>
801 where
802 'db: 'a,
803 {
804 self.load_account_mut_optional(db, address, false)
805 .map_err(JournalLoadError::unwrap_db_error)
806 }
807
808 #[inline]
810 pub fn load_account_mut_optional_code<'a, 'db, DB: Database>(
811 &'a mut self,
812 db: &'db mut DB,
813 address: Address,
814 load_code: bool,
815 skip_cold_load: bool,
816 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
817 where
818 'db: 'a,
819 {
820 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
821 if load_code {
822 load.data.load_code_preserve_error()?;
823 }
824 Ok(load)
825 }
826
827 #[inline]
837 pub fn get_account_mut<'a, 'db, DB: Database>(
838 &'a mut self,
839 db: &'db mut DB,
840 address: Address,
841 ) -> Option<JournaledAccount<'a, DB, ENTRY>>
842 where
843 'db: 'a,
844 {
845 let account = self.state.get_mut(&address)?;
846 Some(JournaledAccount::new(
847 address,
848 account,
849 &mut self.journal,
850 db,
851 self.warm_addresses.access_list(),
852 self.transaction_id,
853 ))
854 }
855
856 #[inline(never)]
858 pub fn load_account_mut_optional<'a, 'db, DB: Database>(
859 &'a mut self,
860 db: &'db mut DB,
861 address: Address,
862 skip_cold_load: bool,
863 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
864 where
865 'db: 'a,
866 {
867 let (account, is_cold) = match self.state.entry(address) {
868 Entry::Occupied(entry) => {
869 let account = entry.into_mut();
870
871 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
873
874 if unlikely(is_cold) {
875 is_cold = self
876 .warm_addresses
877 .check_is_cold(&address, skip_cold_load)?;
878
879 account.mark_warm_with_transaction_id(self.transaction_id);
881
882 if account.is_selfdestructed_locally() {
885 account.selfdestruct();
886 account.unmark_selfdestructed_locally();
887 }
888 *account.original_info = account.info.clone();
890
891 account.unmark_created_locally();
893
894 self.journal.push(ENTRY::account_warmed(address));
896 }
897 (account, is_cold)
898 }
899 Entry::Vacant(vac) => {
900 let is_cold = self
903 .warm_addresses
904 .check_is_cold(&address, skip_cold_load)?;
905
906 let account = if let Some(account) = db.basic(address)? {
907 let mut account: Account = account.into();
908 account.transaction_id = self.transaction_id;
909 account
910 } else {
911 Account::new_not_existing(self.transaction_id)
912 };
913
914 if is_cold {
916 self.journal.push(ENTRY::account_warmed(address));
917 }
918
919 (vac.insert(account), is_cold)
920 }
921 };
922
923 Ok(StateLoad::new(
924 JournaledAccount::new(
925 address,
926 account,
927 &mut self.journal,
928 db,
929 self.warm_addresses.access_list(),
930 self.transaction_id,
931 ),
932 is_cold,
933 ))
934 }
935
936 #[inline]
938 pub fn sload<DB: Database>(
939 &mut self,
940 db: &mut DB,
941 address: Address,
942 key: StorageKey,
943 skip_cold_load: bool,
944 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
945 self.load_account_mut(db, address)?
946 .sload_concrete_error(key, skip_cold_load)
947 .map(|s| s.map(|s| s.present_value))
948 }
949
950 #[inline]
954 pub fn sload_assume_account_present<DB: Database>(
955 &mut self,
956 db: &mut DB,
957 address: Address,
958 key: StorageKey,
959 skip_cold_load: bool,
960 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
961 let Some(mut account) = self.get_account_mut(db, address) else {
962 return Err(JournalLoadError::ColdLoadSkipped);
963 };
964
965 account
966 .sload_concrete_error(key, skip_cold_load)
967 .map(|s| s.map(|s| s.present_value))
968 }
969
970 #[inline]
974 pub fn sstore<DB: Database>(
975 &mut self,
976 db: &mut DB,
977 address: Address,
978 key: StorageKey,
979 new: StorageValue,
980 skip_cold_load: bool,
981 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
982 self.load_account_mut(db, address)?
983 .sstore_concrete_error(key, new, skip_cold_load)
984 }
985
986 #[inline]
992 pub fn sstore_assume_account_present<DB: Database>(
993 &mut self,
994 db: &mut DB,
995 address: Address,
996 key: StorageKey,
997 new: StorageValue,
998 skip_cold_load: bool,
999 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
1000 let Some(mut account) = self.get_account_mut(db, address) else {
1001 return Err(JournalLoadError::ColdLoadSkipped);
1002 };
1003
1004 account.sstore_concrete_error(key, new, skip_cold_load)
1005 }
1006
1007 #[inline]
1011 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
1012 self.transient_storage
1013 .get(&(address, key))
1014 .copied()
1015 .unwrap_or_default()
1016 }
1017
1018 #[inline]
1025 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
1026 let had_value = if new.is_zero() {
1027 self.transient_storage.remove(&(address, key))
1031 } else {
1032 let previous_value = self
1034 .transient_storage
1035 .insert((address, key), new)
1036 .unwrap_or_default();
1037
1038 if previous_value != new {
1040 Some(previous_value)
1042 } else {
1043 None
1044 }
1045 };
1046
1047 if let Some(had_value) = had_value {
1048 self.journal
1050 .push(ENTRY::transient_storage_changed(address, key, had_value));
1051 }
1052 }
1053
1054 #[inline]
1056 pub fn log(&mut self, log: Log) {
1057 self.logs.push(log);
1058 }
1059
1060 #[inline]
1067 pub fn eip7708_transfer_log(&mut self, from: Address, to: Address, balance: U256) {
1068 if !self.cfg.spec.is_enabled_in(AMSTERDAM) || self.cfg.eip7708_disabled || balance.is_zero()
1070 {
1071 return;
1072 }
1073
1074 let topics = std::vec![
1080 ETH_TRANSFER_LOG_TOPIC,
1081 B256::left_padding_from(from.as_slice()),
1082 B256::left_padding_from(to.as_slice()),
1083 ];
1084 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1085
1086 self.logs.push(Log {
1087 address: ETH_TRANSFER_LOG_ADDRESS,
1088 data: LogData::new(topics, data).expect("3 topics is valid"),
1089 });
1090 }
1091
1092 #[inline]
1099 pub fn eip7708_selfdestruct_to_self_log(&mut self, address: Address, balance: U256) {
1100 if !self.cfg.spec.is_enabled_in(AMSTERDAM) || self.cfg.eip7708_disabled || balance.is_zero()
1102 {
1103 return;
1104 }
1105
1106 let topics = std::vec![
1111 SELFDESTRUCT_LOG_TOPIC,
1112 B256::left_padding_from(address.as_slice()),
1113 ];
1114 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1115
1116 self.logs.push(Log {
1117 address: ETH_TRANSFER_LOG_ADDRESS,
1118 data: LogData::new(topics, data).expect("2 topics is valid"),
1119 });
1120 }
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125 use super::*;
1126 use context_interface::journaled_state::entry::JournalEntry;
1127 use database_interface::EmptyDB;
1128 use primitives::{address, HashSet, U256};
1129 use state::AccountInfo;
1130
1131 #[test]
1132 fn test_sload_skip_cold_load() {
1133 let mut journal = JournalInner::<JournalEntry>::new();
1134 let test_address = address!("1000000000000000000000000000000000000000");
1135 let test_key = U256::from(1);
1136
1137 let account_info = AccountInfo {
1139 balance: U256::from(1000),
1140 nonce: 1,
1141 code_hash: KECCAK_EMPTY,
1142 code: Some(Bytecode::default()),
1143 account_id: None,
1144 };
1145 journal
1146 .state
1147 .insert(test_address, Account::from(account_info));
1148
1149 let mut access_list = HashMap::default();
1151 let mut storage_keys = HashSet::default();
1152 storage_keys.insert(test_key);
1153 access_list.insert(test_address, storage_keys);
1154 journal.warm_addresses.set_access_list(access_list);
1155
1156 let mut db = EmptyDB::new();
1158 let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true);
1159
1160 assert!(result.is_ok());
1162 let state_load = result.unwrap();
1163 assert!(!state_load.is_cold); assert_eq!(state_load.data, U256::ZERO); }
1166}