1use crate::entry::SelfdestructionRevertStatus;
3
4use super::JournalEntryTr;
5use bytecode::Bytecode;
6use context_interface::{
7 context::{SStoreResult, SelfDestructResult, StateLoad},
8 journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
9};
10use core::mem;
11use database_interface::Database;
12use primitives::{
13 hardfork::SpecId::{self, *},
14 hash_map::Entry,
15 Address, HashMap, HashSet, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
16};
17use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
18use std::vec::Vec;
19#[derive(Debug, Clone, PartialEq, Eq)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24pub struct JournalInner<ENTRY> {
25 pub state: EvmState,
27 pub transient_storage: TransientStorage,
31 pub logs: Vec<Log>,
33 pub depth: usize,
35 pub journal: Vec<ENTRY>,
37 pub transaction_id: usize,
43 pub spec: SpecId,
56 pub warm_preloaded_addresses: HashSet<Address>,
63 pub precompiles: HashSet<Address>,
65}
66
67impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
68 fn default() -> Self {
69 Self::new()
70 }
71}
72
73impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
74 pub fn new() -> JournalInner<ENTRY> {
79 Self {
80 state: HashMap::default(),
81 transient_storage: TransientStorage::default(),
82 logs: Vec::new(),
83 journal: Vec::default(),
84 transaction_id: 0,
85 depth: 0,
86 spec: SpecId::default(),
87 warm_preloaded_addresses: HashSet::default(),
88 precompiles: HashSet::default(),
89 }
90 }
91
92 #[inline]
94 pub fn take_logs(&mut self) -> Vec<Log> {
95 mem::take(&mut self.logs)
96 }
97
98 pub fn commit_tx(&mut self) {
106 let Self {
109 state,
110 transient_storage,
111 logs,
112 depth,
113 journal,
114 transaction_id,
115 spec,
116 warm_preloaded_addresses,
117 precompiles,
118 } = self;
119 let _ = spec;
121 let _ = precompiles;
122 let _ = state;
123 transient_storage.clear();
124 *depth = 0;
125
126 journal.clear();
128
129 warm_preloaded_addresses.clone_from(precompiles);
133 *transaction_id += 1;
135 logs.clear();
136 }
137
138 pub fn discard_tx(&mut self) {
140 let Self {
142 state,
143 transient_storage,
144 logs,
145 depth,
146 journal,
147 transaction_id,
148 spec,
149 warm_preloaded_addresses,
150 precompiles,
151 } = self;
152
153 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
154 journal.drain(..).rev().for_each(|entry| {
156 entry.revert(state, None, is_spurious_dragon_enabled);
157 });
158 transient_storage.clear();
159 *depth = 0;
160 logs.clear();
161 *transaction_id += 1;
162 warm_preloaded_addresses.clone_from(precompiles);
163 }
164
165 #[inline]
170 pub fn finalize(&mut self) -> EvmState {
171 let Self {
174 state,
175 transient_storage,
176 logs,
177 depth,
178 journal,
179 transaction_id,
180 spec,
181 warm_preloaded_addresses,
182 precompiles,
183 } = self;
184 let _ = spec;
186 warm_preloaded_addresses.clone_from(precompiles);
188
189 let state = mem::take(state);
190 logs.clear();
191 transient_storage.clear();
192
193 journal.clear();
195 *depth = 0;
196 *transaction_id = 0;
198
199 state
200 }
201
202 #[inline]
204 pub fn state(&mut self) -> &mut EvmState {
205 &mut self.state
206 }
207
208 #[inline]
210 pub fn set_spec_id(&mut self, spec: SpecId) {
211 self.spec = spec;
212 }
213
214 #[inline]
218 pub fn touch(&mut self, address: Address) {
219 if let Some(account) = self.state.get_mut(&address) {
220 Self::touch_account(&mut self.journal, address, account);
221 }
222 }
223
224 #[inline]
226 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
227 if !account.is_touched() {
228 journal.push(ENTRY::account_touched(address));
229 account.mark_touch();
230 }
231 }
232
233 #[inline]
241 pub fn account(&self, address: Address) -> &Account {
242 self.state
243 .get(&address)
244 .expect("Account expected to be loaded") }
246
247 #[inline]
251 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
252 let account = self.state.get_mut(&address).unwrap();
253 Self::touch_account(&mut self.journal, address, account);
254
255 self.journal.push(ENTRY::code_changed(address));
256
257 account.info.code_hash = hash;
258 account.info.code = Some(code);
259 }
260
261 #[inline]
267 pub fn set_code(&mut self, address: Address, code: Bytecode) {
268 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
269 if eip7702_bytecode.address().is_zero() {
270 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
271 return;
272 }
273 }
274
275 let hash = code.hash_slow();
276 self.set_code_with_hash(address, code, hash)
277 }
278
279 #[inline]
281 pub fn caller_accounting_journal_entry(
282 &mut self,
283 address: Address,
284 old_balance: U256,
285 bump_nonce: bool,
286 ) {
287 self.journal
289 .push(ENTRY::balance_changed(address, old_balance));
290 self.journal.push(ENTRY::account_touched(address));
292
293 if bump_nonce {
294 self.journal.push(ENTRY::nonce_changed(address));
296 }
297 }
298
299 #[inline]
303 pub fn balance_incr<DB: Database>(
304 &mut self,
305 db: &mut DB,
306 address: Address,
307 balance: U256,
308 ) -> Result<(), DB::Error> {
309 let account = self.load_account(db, address)?.data;
310 let old_balance = account.info.balance;
311 account.info.balance = account.info.balance.saturating_add(balance);
312
313 if !account.is_touched() {
315 account.mark_touch();
316 self.journal.push(ENTRY::account_touched(address));
317 }
318
319 self.journal
321 .push(ENTRY::balance_changed(address, old_balance));
322 Ok(())
323 }
324
325 #[inline]
327 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
328 self.journal.push(ENTRY::nonce_changed(address));
329 }
330
331 #[inline]
333 pub fn transfer<DB: Database>(
334 &mut self,
335 db: &mut DB,
336 from: Address,
337 to: Address,
338 balance: U256,
339 ) -> Result<Option<TransferError>, DB::Error> {
340 if balance.is_zero() {
341 self.load_account(db, to)?;
342 let to_account = self.state.get_mut(&to).unwrap();
343 Self::touch_account(&mut self.journal, to, to_account);
344 return Ok(None);
345 }
346 self.load_account(db, from)?;
348 self.load_account(db, to)?;
349
350 let from_account = self.state.get_mut(&from).unwrap();
352 Self::touch_account(&mut self.journal, from, from_account);
353 let from_balance = &mut from_account.info.balance;
354
355 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
356 return Ok(Some(TransferError::OutOfFunds));
357 };
358 *from_balance = from_balance_decr;
359
360 let to_account = &mut self.state.get_mut(&to).unwrap();
362 Self::touch_account(&mut self.journal, to, to_account);
363 let to_balance = &mut to_account.info.balance;
364 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
365 return Ok(Some(TransferError::OverflowPayment));
366 };
367 *to_balance = to_balance_incr;
368 self.journal
371 .push(ENTRY::balance_transfer(from, to, balance));
372
373 Ok(None)
374 }
375
376 #[inline]
392 pub fn create_account_checkpoint(
393 &mut self,
394 caller: Address,
395 target_address: Address,
396 balance: U256,
397 spec_id: SpecId,
398 ) -> Result<JournalCheckpoint, TransferError> {
399 let checkpoint = self.checkpoint();
401
402 let caller_balance = self.state.get(&caller).unwrap().info.balance;
404 if caller_balance < balance {
406 self.checkpoint_revert(checkpoint);
407 return Err(TransferError::OutOfFunds);
408 }
409
410 let target_acc = self.state.get_mut(&target_address).unwrap();
412 let last_journal = &mut self.journal;
413
414 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
419 self.checkpoint_revert(checkpoint);
420 return Err(TransferError::CreateCollision);
421 }
422
423 let is_created_globaly = target_acc.mark_created_locally();
425
426 last_journal.push(ENTRY::account_created(target_address, is_created_globaly));
428 target_acc.info.code = None;
429 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
431 target_acc.info.nonce = 1;
433 }
434
435 Self::touch_account(last_journal, target_address, target_acc);
438
439 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
441 self.checkpoint_revert(checkpoint);
442 return Err(TransferError::OverflowPayment);
443 };
444 target_acc.info.balance = new_balance;
445
446 self.state.get_mut(&caller).unwrap().info.balance -= balance;
448
449 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
451
452 Ok(checkpoint)
453 }
454
455 #[inline]
457 pub fn checkpoint(&mut self) -> JournalCheckpoint {
458 let checkpoint = JournalCheckpoint {
459 log_i: self.logs.len(),
460 journal_i: self.journal.len(),
461 };
462 self.depth += 1;
463 checkpoint
464 }
465
466 #[inline]
468 pub fn checkpoint_commit(&mut self) {
469 self.depth -= 1;
470 }
471
472 #[inline]
474 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
475 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
476 let state = &mut self.state;
477 let transient_storage = &mut self.transient_storage;
478 self.depth -= 1;
479 self.logs.truncate(checkpoint.log_i);
480
481 self.journal
483 .drain(checkpoint.journal_i..)
484 .rev()
485 .for_each(|entry| {
486 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
487 });
488 }
489
490 #[inline]
502 pub fn selfdestruct<DB: Database>(
503 &mut self,
504 db: &mut DB,
505 address: Address,
506 target: Address,
507 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
508 let spec = self.spec;
509 let account_load = self.load_account(db, target)?;
510 let is_cold = account_load.is_cold;
511 let is_empty = account_load.state_clear_aware_is_empty(spec);
512
513 if address != target {
514 let acc_balance = self.state.get(&address).unwrap().info.balance;
517
518 let target_account = self.state.get_mut(&target).unwrap();
519 Self::touch_account(&mut self.journal, target, target_account);
520 target_account.info.balance += acc_balance;
521 }
522
523 let acc = self.state.get_mut(&address).unwrap();
524 let balance = acc.info.balance;
525
526 let destroyed_status = if !acc.is_selfdestructed() {
527 SelfdestructionRevertStatus::GloballySelfdestroyed
528 } else if !acc.is_selfdestructed_locally() {
529 SelfdestructionRevertStatus::LocallySelfdestroyed
530 } else {
531 SelfdestructionRevertStatus::RepeatedSelfdestruction
532 };
533
534 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
535
536 let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
538 acc.mark_selfdestructed_locally();
539 acc.info.balance = U256::ZERO;
540 Some(ENTRY::account_destroyed(
541 address,
542 target,
543 destroyed_status,
544 balance,
545 ))
546 } else if address != target {
547 acc.info.balance = U256::ZERO;
548 Some(ENTRY::balance_transfer(address, target, balance))
549 } else {
550 None
555 };
556
557 if let Some(entry) = journal_entry {
558 self.journal.push(entry);
559 };
560
561 Ok(StateLoad {
562 data: SelfDestructResult {
563 had_value: !balance.is_zero(),
564 target_exists: !is_empty,
565 previously_destroyed: destroyed_status
566 == SelfdestructionRevertStatus::RepeatedSelfdestruction,
567 },
568 is_cold,
569 })
570 }
571
572 #[inline]
574 pub fn load_account<DB: Database>(
575 &mut self,
576 db: &mut DB,
577 address: Address,
578 ) -> Result<StateLoad<&mut Account>, DB::Error> {
579 self.load_account_optional(db, address, false, [])
580 }
581
582 #[inline]
590 pub fn load_account_delegated<DB: Database>(
591 &mut self,
592 db: &mut DB,
593 address: Address,
594 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
595 let spec = self.spec;
596 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
597 let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
598 let is_empty = account.state_clear_aware_is_empty(spec);
599
600 let mut account_load = StateLoad::new(
601 AccountLoad {
602 is_delegate_account_cold: None,
603 is_empty,
604 },
605 account.is_cold,
606 );
607
608 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
610 let address = code.address();
611 let delegate_account = self.load_account(db, address)?;
612 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
613 }
614
615 Ok(account_load)
616 }
617
618 #[inline]
625 pub fn load_code<DB: Database>(
626 &mut self,
627 db: &mut DB,
628 address: Address,
629 ) -> Result<StateLoad<&mut Account>, DB::Error> {
630 self.load_account_optional(db, address, true, [])
631 }
632
633 #[inline]
635 pub fn load_account_optional<DB: Database>(
636 &mut self,
637 db: &mut DB,
638 address: Address,
639 load_code: bool,
640 storage_keys: impl IntoIterator<Item = StorageKey>,
641 ) -> Result<StateLoad<&mut Account>, DB::Error> {
642 let load = match self.state.entry(address) {
643 Entry::Occupied(entry) => {
644 let account = entry.into_mut();
645 let is_cold = account.mark_warm_with_transaction_id(self.transaction_id);
646 if is_cold {
648 if account.is_selfdestructed_locally() {
651 account.selfdestruct();
652 account.unmark_selfdestructed_locally();
653 }
654 account.unmark_created_locally();
656 }
657 StateLoad {
658 data: account,
659 is_cold,
660 }
661 }
662 Entry::Vacant(vac) => {
663 let account = if let Some(account) = db.basic(address)? {
664 account.into()
665 } else {
666 Account::new_not_existing(self.transaction_id)
667 };
668
669 let is_cold = !self.warm_preloaded_addresses.contains(&address);
671
672 StateLoad {
673 data: vac.insert(account),
674 is_cold,
675 }
676 }
677 };
678
679 if load.is_cold {
681 self.journal.push(ENTRY::account_warmed(address));
682 }
683 if load_code {
684 let info = &mut load.data.info;
685 if info.code.is_none() {
686 let code = if info.code_hash == KECCAK_EMPTY {
687 Bytecode::default()
688 } else {
689 db.code_by_hash(info.code_hash)?
690 };
691 info.code = Some(code);
692 }
693 }
694
695 for storage_key in storage_keys.into_iter() {
696 sload_with_account(
697 load.data,
698 db,
699 &mut self.journal,
700 self.transaction_id,
701 address,
702 storage_key,
703 )?;
704 }
705 Ok(load)
706 }
707
708 #[inline]
714 pub fn sload<DB: Database>(
715 &mut self,
716 db: &mut DB,
717 address: Address,
718 key: StorageKey,
719 ) -> Result<StateLoad<StorageValue>, DB::Error> {
720 let account = self.state.get_mut(&address).unwrap();
722 sload_with_account(
724 account,
725 db,
726 &mut self.journal,
727 self.transaction_id,
728 address,
729 key,
730 )
731 }
732
733 #[inline]
739 pub fn sstore<DB: Database>(
740 &mut self,
741 db: &mut DB,
742 address: Address,
743 key: StorageKey,
744 new: StorageValue,
745 ) -> Result<StateLoad<SStoreResult>, DB::Error> {
746 let present = self.sload(db, address, key)?;
748 let acc = self.state.get_mut(&address).unwrap();
749
750 let slot = acc.storage.get_mut(&key).unwrap();
752
753 if present.data == new {
755 return Ok(StateLoad::new(
756 SStoreResult {
757 original_value: slot.original_value(),
758 present_value: present.data,
759 new_value: new,
760 },
761 present.is_cold,
762 ));
763 }
764
765 self.journal
766 .push(ENTRY::storage_changed(address, key, present.data));
767 slot.present_value = new;
769 Ok(StateLoad::new(
770 SStoreResult {
771 original_value: slot.original_value(),
772 present_value: present.data,
773 new_value: new,
774 },
775 present.is_cold,
776 ))
777 }
778
779 #[inline]
783 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
784 self.transient_storage
785 .get(&(address, key))
786 .copied()
787 .unwrap_or_default()
788 }
789
790 #[inline]
797 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
798 let had_value = if new.is_zero() {
799 self.transient_storage.remove(&(address, key))
803 } else {
804 let previous_value = self
806 .transient_storage
807 .insert((address, key), new)
808 .unwrap_or_default();
809
810 if previous_value != new {
812 Some(previous_value)
814 } else {
815 None
816 }
817 };
818
819 if let Some(had_value) = had_value {
820 self.journal
822 .push(ENTRY::transient_storage_changed(address, key, had_value));
823 }
824 }
825
826 #[inline]
828 pub fn log(&mut self, log: Log) {
829 self.logs.push(log);
830 }
831}
832
833#[inline]
835pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
836 account: &mut Account,
837 db: &mut DB,
838 journal: &mut Vec<ENTRY>,
839 transaction_id: usize,
840 address: Address,
841 key: StorageKey,
842) -> Result<StateLoad<StorageValue>, DB::Error> {
843 let is_newly_created = account.is_created();
844 let (value, is_cold) = match account.storage.entry(key) {
845 Entry::Occupied(occ) => {
846 let slot = occ.into_mut();
847 let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
848 (slot.present_value, is_cold)
849 }
850 Entry::Vacant(vac) => {
851 let value = if is_newly_created {
853 StorageValue::ZERO
854 } else {
855 db.storage(address, key)?
856 };
857
858 vac.insert(EvmStorageSlot::new(value, transaction_id));
859
860 (value, true)
861 }
862 };
863
864 if is_cold {
865 journal.push(ENTRY::storage_warmed(address, key));
867 }
868
869 Ok(StateLoad::new(value, is_cold))
870}