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_TO_SELF_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#[derive(Debug, Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct JournalInner<ENTRY> {
29 pub state: EvmState,
31 pub transient_storage: TransientStorage,
35 pub logs: Vec<Log>,
37 pub depth: usize,
39 pub journal: Vec<ENTRY>,
41 pub transaction_id: usize,
47 pub spec: SpecId,
60 pub warm_addresses: WarmAddresses,
62}
63
64impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
71 pub fn new() -> JournalInner<ENTRY> {
76 Self {
77 state: HashMap::default(),
78 transient_storage: TransientStorage::default(),
79 logs: Vec::new(),
80 journal: Vec::default(),
81 transaction_id: 0,
82 depth: 0,
83 spec: SpecId::default(),
84 warm_addresses: WarmAddresses::new(),
85 }
86 }
87
88 #[inline]
90 pub fn take_logs(&mut self) -> Vec<Log> {
91 mem::take(&mut self.logs)
92 }
93
94 pub fn commit_tx(&mut self) {
102 let Self {
105 state,
106 transient_storage,
107 logs,
108 depth,
109 journal,
110 transaction_id,
111 spec,
112 warm_addresses,
113 } = self;
114 let _ = spec;
116 let _ = state;
117 transient_storage.clear();
118 *depth = 0;
119
120 journal.clear();
122
123 warm_addresses.clear_coinbase_and_access_list();
125 *transaction_id += 1;
127
128 logs.clear();
129 }
130
131 pub fn discard_tx(&mut self) {
133 let Self {
135 state,
136 transient_storage,
137 logs,
138 depth,
139 journal,
140 transaction_id,
141 spec,
142 warm_addresses,
143 } = self;
144 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
145 journal.drain(..).rev().for_each(|entry| {
147 entry.revert(state, None, is_spurious_dragon_enabled);
148 });
149 transient_storage.clear();
150 *depth = 0;
151 logs.clear();
152 *transaction_id += 1;
153
154 warm_addresses.clear_coinbase_and_access_list();
156 }
157
158 #[inline]
163 pub fn finalize(&mut self) -> EvmState {
164 let Self {
167 state,
168 transient_storage,
169 logs,
170 depth,
171 journal,
172 transaction_id,
173 spec,
174 warm_addresses,
175 } = self;
176 let _ = spec;
178 warm_addresses.clear_coinbase_and_access_list();
180
181 let state = mem::take(state);
182 logs.clear();
183 transient_storage.clear();
184
185 journal.clear();
187 *depth = 0;
188 *transaction_id = 0;
190
191 state
192 }
193
194 #[inline]
196 pub fn state(&mut self) -> &mut EvmState {
197 &mut self.state
198 }
199
200 #[inline]
202 pub fn set_spec_id(&mut self, spec: SpecId) {
203 self.spec = spec;
204 }
205
206 #[inline]
210 pub fn touch(&mut self, address: Address) {
211 if let Some(account) = self.state.get_mut(&address) {
212 Self::touch_account(&mut self.journal, address, account);
213 }
214 }
215
216 #[inline]
218 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
219 if !account.is_touched() {
220 journal.push(ENTRY::account_touched(address));
221 account.mark_touch();
222 }
223 }
224
225 #[inline]
233 pub fn account(&self, address: Address) -> &Account {
234 self.state
235 .get(&address)
236 .expect("Account expected to be loaded") }
238
239 #[inline]
243 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
244 let account = self.state.get_mut(&address).unwrap();
245 Self::touch_account(&mut self.journal, address, account);
246
247 self.journal.push(ENTRY::code_changed(address));
248
249 account.info.code_hash = hash;
250 account.info.code = Some(code);
251 }
252
253 #[inline]
259 pub fn set_code(&mut self, address: Address, code: Bytecode) {
260 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
261 if eip7702_bytecode.address().is_zero() {
262 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
263 return;
264 }
265 }
266
267 let hash = code.hash_slow();
268 self.set_code_with_hash(address, code, hash)
269 }
270
271 #[inline]
273 #[deprecated]
274 pub fn caller_accounting_journal_entry(
275 &mut self,
276 address: Address,
277 old_balance: U256,
278 bump_nonce: bool,
279 ) {
280 self.journal
282 .push(ENTRY::balance_changed(address, old_balance));
283 self.journal.push(ENTRY::account_touched(address));
285
286 if bump_nonce {
287 self.journal.push(ENTRY::nonce_bumped(address));
289 }
290 }
291
292 #[inline]
296 pub fn balance_incr<DB: Database>(
297 &mut self,
298 db: &mut DB,
299 address: Address,
300 balance: U256,
301 ) -> Result<(), DB::Error> {
302 let mut account = self.load_account_mut(db, address)?.data;
303 account.incr_balance(balance);
304 Ok(())
305 }
306
307 #[inline]
309 #[deprecated]
310 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
311 self.journal.push(ENTRY::nonce_bumped(address));
312 }
313
314 #[inline]
320 pub fn transfer_loaded(
321 &mut self,
322 from: Address,
323 to: Address,
324 balance: U256,
325 ) -> Option<TransferError> {
326 if from == to {
327 let from_balance = self.state.get_mut(&to).unwrap().info.balance;
328 if balance > from_balance {
330 return Some(TransferError::OutOfFunds);
331 }
332 return None;
333 }
334
335 if balance.is_zero() {
336 Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
337 return None;
338 }
339
340 let from_account = self.state.get_mut(&from).unwrap();
342 Self::touch_account(&mut self.journal, from, from_account);
343 let from_balance = &mut from_account.info.balance;
344 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
345 return Some(TransferError::OutOfFunds);
346 };
347 *from_balance = from_balance_decr;
348
349 let to_account = self.state.get_mut(&to).unwrap();
351 Self::touch_account(&mut self.journal, to, to_account);
352 let to_balance = &mut to_account.info.balance;
353 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
354 return Some(TransferError::OverflowPayment);
356 };
357 *to_balance = to_balance_incr;
358
359 self.journal
361 .push(ENTRY::balance_transfer(from, to, balance));
362
363 self.eip7708_transfer_log(from, to, balance);
365
366 None
367 }
368
369 #[inline]
371 pub fn transfer<DB: Database>(
372 &mut self,
373 db: &mut DB,
374 from: Address,
375 to: Address,
376 balance: U256,
377 ) -> Result<Option<TransferError>, DB::Error> {
378 self.load_account(db, from)?;
379 self.load_account(db, to)?;
380 Ok(self.transfer_loaded(from, to, balance))
381 }
382
383 #[inline]
399 pub fn create_account_checkpoint(
400 &mut self,
401 caller: Address,
402 target_address: Address,
403 balance: U256,
404 spec_id: SpecId,
405 ) -> Result<JournalCheckpoint, TransferError> {
406 let checkpoint = self.checkpoint();
408
409 let target_acc = self.state.get_mut(&target_address).unwrap();
411 let last_journal = &mut self.journal;
412
413 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
418 self.checkpoint_revert(checkpoint);
419 return Err(TransferError::CreateCollision);
420 }
421
422 let is_created_globally = target_acc.mark_created_locally();
424
425 last_journal.push(ENTRY::account_created(target_address, is_created_globally));
427 target_acc.info.code = None;
428 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
430 target_acc.info.nonce = 1;
432 }
433
434 Self::touch_account(last_journal, target_address, target_acc);
437
438 if balance.is_zero() {
440 return Ok(checkpoint);
441 }
442
443 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
445 self.checkpoint_revert(checkpoint);
446 return Err(TransferError::OverflowPayment);
447 };
448 target_acc.info.balance = new_balance;
449
450 let caller_account = self.state.get_mut(&caller).unwrap();
452 caller_account.info.balance -= balance;
453
454 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
456
457 self.eip7708_transfer_log(caller, target_address, balance);
459
460 Ok(checkpoint)
461 }
462
463 #[inline]
465 pub fn checkpoint(&mut self) -> JournalCheckpoint {
466 let checkpoint = JournalCheckpoint {
467 log_i: self.logs.len(),
468 journal_i: self.journal.len(),
469 };
470 self.depth += 1;
471 checkpoint
472 }
473
474 #[inline]
476 pub fn checkpoint_commit(&mut self) {
477 self.depth = self.depth.saturating_sub(1);
478 }
479
480 #[inline]
482 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
483 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
484 let state = &mut self.state;
485 let transient_storage = &mut self.transient_storage;
486 self.depth = self.depth.saturating_sub(1);
487 self.logs.truncate(checkpoint.log_i);
488
489 if checkpoint.journal_i < self.journal.len() {
491 self.journal
492 .drain(checkpoint.journal_i..)
493 .rev()
494 .for_each(|entry| {
495 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
496 });
497 }
498 }
499
500 #[inline]
512 pub fn selfdestruct<DB: Database>(
513 &mut self,
514 db: &mut DB,
515 address: Address,
516 target: Address,
517 skip_cold_load: bool,
518 ) -> Result<StateLoad<SelfDestructResult>, JournalLoadError<DB::Error>> {
519 let spec = self.spec;
520 let account_load = self.load_account_optional(db, target, false, skip_cold_load)?;
521 let is_cold = account_load.is_cold;
522 let is_empty = account_load.state_clear_aware_is_empty(spec);
523
524 if address != target {
525 let acc_balance = self.state.get(&address).unwrap().info.balance;
528
529 let target_account = self.state.get_mut(&target).unwrap();
530 Self::touch_account(&mut self.journal, target, target_account);
531 target_account.info.balance += acc_balance;
532 }
533
534 let acc = self.state.get_mut(&address).unwrap();
535 let balance = acc.info.balance;
536
537 let destroyed_status = if !acc.is_selfdestructed() {
538 SelfdestructionRevertStatus::GloballySelfdestroyed
539 } else if !acc.is_selfdestructed_locally() {
540 SelfdestructionRevertStatus::LocallySelfdestroyed
541 } else {
542 SelfdestructionRevertStatus::RepeatedSelfdestruction
543 };
544
545 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
546
547 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
549 acc.mark_selfdestructed_locally();
550 acc.info.balance = U256::ZERO;
551
552 if target != address {
554 self.eip7708_transfer_log(address, target, balance);
556 } else {
557 self.eip7708_selfdestruct_to_self_log(address, balance);
559 }
560 Some(ENTRY::account_destroyed(
561 address,
562 target,
563 destroyed_status,
564 balance,
565 ))
566 } else if address != target {
567 acc.info.balance = U256::ZERO;
568 self.eip7708_transfer_log(address, target, balance);
571 Some(ENTRY::balance_transfer(address, target, balance))
572 } else {
573 None
578 };
579
580 if let Some(entry) = journal_entry {
581 self.journal.push(entry);
582 };
583
584 Ok(StateLoad {
585 data: SelfDestructResult {
586 had_value: !balance.is_zero(),
587 target_exists: !is_empty,
588 previously_destroyed: destroyed_status
589 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
590 },
591 is_cold,
592 })
593 }
594
595 #[inline]
597 pub fn load_account<'a, 'db, DB: Database>(
598 &'a mut self,
599 db: &'db mut DB,
600 address: Address,
601 ) -> Result<StateLoad<&'a Account>, DB::Error>
602 where
603 'db: 'a,
604 {
605 self.load_account_optional(db, address, false, false)
606 .map_err(JournalLoadError::unwrap_db_error)
607 }
608
609 #[inline]
617 pub fn load_account_delegated<DB: Database>(
618 &mut self,
619 db: &mut DB,
620 address: Address,
621 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
622 let spec = self.spec;
623 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
624 let account = self
625 .load_account_optional(db, address, is_eip7702_enabled, false)
626 .map_err(JournalLoadError::unwrap_db_error)?;
627 let is_empty = account.state_clear_aware_is_empty(spec);
628
629 let mut account_load = StateLoad::new(
630 AccountLoad {
631 is_delegate_account_cold: None,
632 is_empty,
633 },
634 account.is_cold,
635 );
636
637 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
639 let address = code.address();
640 let delegate_account = self
641 .load_account_optional(db, address, true, false)
642 .map_err(JournalLoadError::unwrap_db_error)?;
643 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
644 }
645
646 Ok(account_load)
647 }
648
649 #[inline]
656 pub fn load_code<'a, 'db, DB: Database>(
657 &'a mut self,
658 db: &'db mut DB,
659 address: Address,
660 ) -> Result<StateLoad<&'a Account>, DB::Error>
661 where
662 'db: 'a,
663 {
664 self.load_account_optional(db, address, true, false)
665 .map_err(JournalLoadError::unwrap_db_error)
666 }
667
668 #[inline]
670 pub fn load_account_optional<'a, 'db, DB: Database>(
671 &'a mut self,
672 db: &'db mut DB,
673 address: Address,
674 load_code: bool,
675 skip_cold_load: bool,
676 ) -> Result<StateLoad<&'a Account>, JournalLoadError<DB::Error>>
677 where
678 'db: 'a,
679 {
680 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
681 if load_code {
682 load.data.load_code_preserve_error()?;
683 }
684 Ok(load.map(|i| i.into_account()))
685 }
686
687 #[inline]
689 pub fn load_account_mut<'a, 'db, DB: Database>(
690 &'a mut self,
691 db: &'db mut DB,
692 address: Address,
693 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, DB::Error>
694 where
695 'db: 'a,
696 {
697 self.load_account_mut_optional(db, address, false)
698 .map_err(JournalLoadError::unwrap_db_error)
699 }
700
701 #[inline]
703 pub fn load_account_mut_optional_code<'a, 'db, DB: Database>(
704 &'a mut self,
705 db: &'db mut DB,
706 address: Address,
707 load_code: bool,
708 skip_cold_load: bool,
709 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
710 where
711 'db: 'a,
712 {
713 let mut load = self.load_account_mut_optional(db, address, skip_cold_load)?;
714 if load_code {
715 load.data.load_code_preserve_error()?;
716 }
717 Ok(load)
718 }
719
720 #[inline]
730 pub fn get_account_mut<'a, 'db, DB: Database>(
731 &'a mut self,
732 db: &'db mut DB,
733 address: Address,
734 ) -> Option<JournaledAccount<'a, DB, ENTRY>>
735 where
736 'db: 'a,
737 {
738 let account = self.state.get_mut(&address)?;
739 Some(JournaledAccount::new(
740 address,
741 account,
742 &mut self.journal,
743 db,
744 self.warm_addresses.access_list(),
745 self.transaction_id,
746 ))
747 }
748
749 #[inline(never)]
751 pub fn load_account_mut_optional<'a, 'db, DB: Database>(
752 &'a mut self,
753 db: &'db mut DB,
754 address: Address,
755 skip_cold_load: bool,
756 ) -> Result<StateLoad<JournaledAccount<'a, DB, ENTRY>>, JournalLoadError<DB::Error>>
757 where
758 'db: 'a,
759 {
760 let (account, is_cold) = match self.state.entry(address) {
761 Entry::Occupied(entry) => {
762 let account = entry.into_mut();
763
764 let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
766
767 if unlikely(is_cold) {
768 is_cold = self
769 .warm_addresses
770 .check_is_cold(&address, skip_cold_load)?;
771
772 account.mark_warm_with_transaction_id(self.transaction_id);
774
775 if account.is_selfdestructed_locally() {
778 account.selfdestruct();
779 account.unmark_selfdestructed_locally();
780 }
781 *account.original_info = account.info.clone();
783
784 account.unmark_created_locally();
786
787 self.journal.push(ENTRY::account_warmed(address));
789 }
790 (account, is_cold)
791 }
792 Entry::Vacant(vac) => {
793 let is_cold = self
796 .warm_addresses
797 .check_is_cold(&address, skip_cold_load)?;
798
799 let account = if let Some(account) = db.basic(address)? {
800 let mut account: Account = account.into();
801 account.transaction_id = self.transaction_id;
802 account
803 } else {
804 Account::new_not_existing(self.transaction_id)
805 };
806
807 if is_cold {
809 self.journal.push(ENTRY::account_warmed(address));
810 }
811
812 (vac.insert(account), is_cold)
813 }
814 };
815
816 Ok(StateLoad::new(
817 JournaledAccount::new(
818 address,
819 account,
820 &mut self.journal,
821 db,
822 self.warm_addresses.access_list(),
823 self.transaction_id,
824 ),
825 is_cold,
826 ))
827 }
828
829 #[inline]
831 pub fn sload<DB: Database>(
832 &mut self,
833 db: &mut DB,
834 address: Address,
835 key: StorageKey,
836 skip_cold_load: bool,
837 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
838 self.load_account_mut(db, address)?
839 .sload_concrete_error(key, skip_cold_load)
840 .map(|s| s.map(|s| s.present_value))
841 }
842
843 #[inline]
847 pub fn sload_assume_account_present<DB: Database>(
848 &mut self,
849 db: &mut DB,
850 address: Address,
851 key: StorageKey,
852 skip_cold_load: bool,
853 ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
854 let Some(mut account) = self.get_account_mut(db, address) else {
855 return Err(JournalLoadError::ColdLoadSkipped);
856 };
857
858 account
859 .sload_concrete_error(key, skip_cold_load)
860 .map(|s| s.map(|s| s.present_value))
861 }
862
863 #[inline]
867 pub fn sstore<DB: Database>(
868 &mut self,
869 db: &mut DB,
870 address: Address,
871 key: StorageKey,
872 new: StorageValue,
873 skip_cold_load: bool,
874 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
875 self.load_account_mut(db, address)?
876 .sstore_concrete_error(key, new, skip_cold_load)
877 }
878
879 #[inline]
885 pub fn sstore_assume_account_present<DB: Database>(
886 &mut self,
887 db: &mut DB,
888 address: Address,
889 key: StorageKey,
890 new: StorageValue,
891 skip_cold_load: bool,
892 ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
893 let Some(mut account) = self.get_account_mut(db, address) else {
894 return Err(JournalLoadError::ColdLoadSkipped);
895 };
896
897 account.sstore_concrete_error(key, new, skip_cold_load)
898 }
899
900 #[inline]
904 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
905 self.transient_storage
906 .get(&(address, key))
907 .copied()
908 .unwrap_or_default()
909 }
910
911 #[inline]
918 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
919 let had_value = if new.is_zero() {
920 self.transient_storage.remove(&(address, key))
924 } else {
925 let previous_value = self
927 .transient_storage
928 .insert((address, key), new)
929 .unwrap_or_default();
930
931 if previous_value != new {
933 Some(previous_value)
935 } else {
936 None
937 }
938 };
939
940 if let Some(had_value) = had_value {
941 self.journal
943 .push(ENTRY::transient_storage_changed(address, key, had_value));
944 }
945 }
946
947 #[inline]
949 pub fn log(&mut self, log: Log) {
950 self.logs.push(log);
951 }
952
953 #[inline]
960 pub fn eip7708_transfer_log(&mut self, from: Address, to: Address, balance: U256) {
961 if !self.spec.is_enabled_in(AMSTERDAM) || balance.is_zero() {
963 return;
964 }
965
966 let topics = std::vec![
972 ETH_TRANSFER_LOG_TOPIC,
973 B256::left_padding_from(from.as_slice()),
974 B256::left_padding_from(to.as_slice()),
975 ];
976 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
977
978 self.logs.push(Log {
979 address: ETH_TRANSFER_LOG_ADDRESS,
980 data: LogData::new(topics, data).expect("3 topics is valid"),
981 });
982 }
983
984 #[inline]
991 pub fn eip7708_selfdestruct_to_self_log(&mut self, address: Address, balance: U256) {
992 if !self.spec.is_enabled_in(AMSTERDAM) || balance.is_zero() {
994 return;
995 }
996
997 let topics = std::vec![
1002 SELFDESTRUCT_TO_SELF_LOG_TOPIC,
1003 B256::left_padding_from(address.as_slice()),
1004 ];
1005 let data = Bytes::copy_from_slice(&balance.to_be_bytes::<32>());
1006
1007 self.logs.push(Log {
1008 address: ETH_TRANSFER_LOG_ADDRESS,
1009 data: LogData::new(topics, data).expect("2 topics is valid"),
1010 });
1011 }
1012}
1013
1014#[cfg(test)]
1015mod tests {
1016 use super::*;
1017 use context_interface::journaled_state::entry::JournalEntry;
1018 use database_interface::EmptyDB;
1019 use primitives::{address, HashSet, U256};
1020 use state::AccountInfo;
1021
1022 #[test]
1023 fn test_sload_skip_cold_load() {
1024 let mut journal = JournalInner::<JournalEntry>::new();
1025 let test_address = address!("1000000000000000000000000000000000000000");
1026 let test_key = U256::from(1);
1027
1028 let account_info = AccountInfo {
1030 balance: U256::from(1000),
1031 nonce: 1,
1032 code_hash: KECCAK_EMPTY,
1033 code: Some(Bytecode::default()),
1034 account_id: None,
1035 };
1036 journal
1037 .state
1038 .insert(test_address, Account::from(account_info));
1039
1040 let mut access_list = HashMap::default();
1042 let mut storage_keys = HashSet::default();
1043 storage_keys.insert(test_key);
1044 access_list.insert(test_address, storage_keys);
1045 journal.warm_addresses.set_access_list(access_list);
1046
1047 let mut db = EmptyDB::new();
1049 let result = journal.sload_assume_account_present(&mut db, test_address, test_key, true);
1050
1051 assert!(result.is_ok());
1053 let state_load = result.unwrap();
1054 assert!(!state_load.is_cold); assert_eq!(state_load.data, U256::ZERO); }
1057}