1use super::JournalEntryTr;
3use bytecode::Bytecode;
4use context_interface::{
5 context::{SStoreResult, SelfDestructResult, StateLoad},
6 journaled_state::{AccountLoad, JournalCheckpoint, TransferError},
7};
8use core::mem;
9use database_interface::Database;
10use primitives::{
11 hardfork::SpecId::{self, *},
12 hash_map::Entry,
13 Address, HashMap, HashSet, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
14};
15use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
16use std::vec::Vec;
17#[derive(Debug, Clone, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22pub struct JournalInner<ENTRY> {
23 pub state: EvmState,
25 pub transient_storage: TransientStorage,
29 pub logs: Vec<Log>,
31 pub depth: usize,
33 pub journal: Vec<ENTRY>,
35 pub transaction_id: usize,
41 pub spec: SpecId,
54 pub warm_preloaded_addresses: HashSet<Address>,
61 pub precompiles: HashSet<Address>,
63}
64
65impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
72 pub fn new() -> JournalInner<ENTRY> {
77 Self {
78 state: HashMap::default(),
79 transient_storage: TransientStorage::default(),
80 logs: Vec::new(),
81 journal: Vec::default(),
82 transaction_id: 0,
83 depth: 0,
84 spec: SpecId::default(),
85 warm_preloaded_addresses: HashSet::default(),
86 precompiles: HashSet::default(),
87 }
88 }
89
90 #[inline]
92 pub fn take_logs(&mut self) -> Vec<Log> {
93 mem::take(&mut self.logs)
94 }
95
96 pub fn commit_tx(&mut self) {
104 let Self {
107 state,
108 transient_storage,
109 logs,
110 depth,
111 journal,
112 transaction_id,
113 spec,
114 warm_preloaded_addresses,
115 precompiles,
116 } = self;
117 let _ = spec;
119 let _ = precompiles;
120 let _ = state;
121 transient_storage.clear();
122 *depth = 0;
123
124 journal.clear();
126
127 warm_preloaded_addresses.clone_from(precompiles);
131 *transaction_id += 1;
133 logs.clear();
134 }
135
136 pub fn discard_tx(&mut self) {
138 let Self {
140 state,
141 transient_storage,
142 logs,
143 depth,
144 journal,
145 transaction_id,
146 spec,
147 warm_preloaded_addresses,
148 precompiles,
149 } = self;
150
151 let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
152 journal.drain(..).rev().for_each(|entry| {
154 entry.revert(state, None, is_spurious_dragon_enabled);
155 });
156 transient_storage.clear();
157 *depth = 0;
158 logs.clear();
159 *transaction_id += 1;
160 warm_preloaded_addresses.clone_from(precompiles);
161 }
162
163 #[inline]
168 pub fn finalize(&mut self) -> EvmState {
169 let Self {
172 state,
173 transient_storage,
174 logs,
175 depth,
176 journal,
177 transaction_id,
178 spec,
179 warm_preloaded_addresses,
180 precompiles,
181 } = self;
182 let _ = spec;
184 warm_preloaded_addresses.clone_from(precompiles);
186
187 let state = mem::take(state);
188 logs.clear();
189 transient_storage.clear();
190
191 journal.clear();
193 *depth = 0;
194 *transaction_id = 0;
196
197 state
198 }
199
200 #[inline]
202 pub fn state(&mut self) -> &mut EvmState {
203 &mut self.state
204 }
205
206 #[inline]
208 pub fn set_spec_id(&mut self, spec: SpecId) {
209 self.spec = spec;
210 }
211
212 #[inline]
216 pub fn touch(&mut self, address: Address) {
217 if let Some(account) = self.state.get_mut(&address) {
218 Self::touch_account(&mut self.journal, address, account);
219 }
220 }
221
222 #[inline]
224 fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
225 if !account.is_touched() {
226 journal.push(ENTRY::account_touched(address));
227 account.mark_touch();
228 }
229 }
230
231 #[inline]
239 pub fn account(&self, address: Address) -> &Account {
240 self.state
241 .get(&address)
242 .expect("Account expected to be loaded") }
244
245 #[inline]
249 pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
250 let account = self.state.get_mut(&address).unwrap();
251 Self::touch_account(&mut self.journal, address, account);
252
253 self.journal.push(ENTRY::code_changed(address));
254
255 account.info.code_hash = hash;
256 account.info.code = Some(code);
257 }
258
259 #[inline]
265 pub fn set_code(&mut self, address: Address, code: Bytecode) {
266 if let Bytecode::Eip7702(eip7702_bytecode) = &code {
267 if eip7702_bytecode.address().is_zero() {
268 self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
269 return;
270 }
271 }
272
273 let hash = code.hash_slow();
274 self.set_code_with_hash(address, code, hash)
275 }
276
277 #[inline]
279 pub fn caller_accounting_journal_entry(
280 &mut self,
281 address: Address,
282 old_balance: U256,
283 bump_nonce: bool,
284 ) {
285 self.journal
287 .push(ENTRY::balance_changed(address, old_balance));
288 self.journal.push(ENTRY::account_touched(address));
290
291 if bump_nonce {
292 self.journal.push(ENTRY::nonce_changed(address));
294 }
295 }
296
297 #[inline]
301 pub fn balance_incr<DB: Database>(
302 &mut self,
303 db: &mut DB,
304 address: Address,
305 balance: U256,
306 ) -> Result<(), DB::Error> {
307 let account = self.load_account(db, address)?.data;
308 let old_balance = account.info.balance;
309 account.info.balance = account.info.balance.saturating_add(balance);
310
311 if !account.is_touched() {
313 account.mark_touch();
314 self.journal.push(ENTRY::account_touched(address));
315 }
316
317 self.journal
319 .push(ENTRY::balance_changed(address, old_balance));
320 Ok(())
321 }
322
323 #[inline]
325 pub fn nonce_bump_journal_entry(&mut self, address: Address) {
326 self.journal.push(ENTRY::nonce_changed(address));
327 }
328
329 #[inline]
331 pub fn transfer<DB: Database>(
332 &mut self,
333 db: &mut DB,
334 from: Address,
335 to: Address,
336 balance: U256,
337 ) -> Result<Option<TransferError>, DB::Error> {
338 if balance.is_zero() {
339 self.load_account(db, to)?;
340 let to_account = self.state.get_mut(&to).unwrap();
341 Self::touch_account(&mut self.journal, to, to_account);
342 return Ok(None);
343 }
344 self.load_account(db, from)?;
346 self.load_account(db, to)?;
347
348 let from_account = self.state.get_mut(&from).unwrap();
350 Self::touch_account(&mut self.journal, from, from_account);
351 let from_balance = &mut from_account.info.balance;
352
353 let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
354 return Ok(Some(TransferError::OutOfFunds));
355 };
356 *from_balance = from_balance_decr;
357
358 let to_account = &mut self.state.get_mut(&to).unwrap();
360 Self::touch_account(&mut self.journal, to, to_account);
361 let to_balance = &mut to_account.info.balance;
362 let Some(to_balance_incr) = to_balance.checked_add(balance) else {
363 return Ok(Some(TransferError::OverflowPayment));
364 };
365 *to_balance = to_balance_incr;
366 self.journal
369 .push(ENTRY::balance_transfer(from, to, balance));
370
371 Ok(None)
372 }
373
374 #[inline]
390 pub fn create_account_checkpoint(
391 &mut self,
392 caller: Address,
393 target_address: Address,
394 balance: U256,
395 spec_id: SpecId,
396 ) -> Result<JournalCheckpoint, TransferError> {
397 let checkpoint = self.checkpoint();
399
400 let caller_balance = self.state.get(&caller).unwrap().info.balance;
402 if caller_balance < balance {
404 self.checkpoint_revert(checkpoint);
405 return Err(TransferError::OutOfFunds);
406 }
407
408 let target_acc = self.state.get_mut(&target_address).unwrap();
410 let last_journal = &mut self.journal;
411
412 if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
417 self.checkpoint_revert(checkpoint);
418 return Err(TransferError::CreateCollision);
419 }
420
421 target_acc.mark_created();
423
424 last_journal.push(ENTRY::account_created(target_address));
426 target_acc.info.code = None;
427 if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
429 target_acc.info.nonce = 1;
431 }
432
433 Self::touch_account(last_journal, target_address, target_acc);
436
437 let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
439 self.checkpoint_revert(checkpoint);
440 return Err(TransferError::OverflowPayment);
441 };
442 target_acc.info.balance = new_balance;
443
444 self.state.get_mut(&caller).unwrap().info.balance -= balance;
446
447 last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
449
450 Ok(checkpoint)
451 }
452
453 #[inline]
455 pub fn checkpoint(&mut self) -> JournalCheckpoint {
456 let checkpoint = JournalCheckpoint {
457 log_i: self.logs.len(),
458 journal_i: self.journal.len(),
459 };
460 self.depth += 1;
461 checkpoint
462 }
463
464 #[inline]
466 pub fn checkpoint_commit(&mut self) {
467 self.depth -= 1;
468 }
469
470 #[inline]
472 pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
473 let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
474 let state = &mut self.state;
475 let transient_storage = &mut self.transient_storage;
476 self.depth -= 1;
477 self.logs.truncate(checkpoint.log_i);
478
479 self.journal
481 .drain(checkpoint.journal_i..)
482 .rev()
483 .for_each(|entry| {
484 entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
485 });
486 }
487
488 #[inline]
500 pub fn selfdestruct<DB: Database>(
501 &mut self,
502 db: &mut DB,
503 address: Address,
504 target: Address,
505 ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
506 let spec = self.spec;
507 let account_load = self.load_account(db, target)?;
508 let is_cold = account_load.is_cold;
509 let is_empty = account_load.state_clear_aware_is_empty(spec);
510
511 if address != target {
512 let acc_balance = self.state.get(&address).unwrap().info.balance;
515
516 let target_account = self.state.get_mut(&target).unwrap();
517 Self::touch_account(&mut self.journal, target, target_account);
518 target_account.info.balance += acc_balance;
519 }
520
521 let acc = self.state.get_mut(&address).unwrap();
522 let balance = acc.info.balance;
523 let previously_destroyed = acc.is_selfdestructed();
524 let is_cancun_enabled = spec.is_enabled_in(CANCUN);
525
526 let journal_entry = if acc.is_created() || !is_cancun_enabled {
528 acc.mark_selfdestruct();
529 acc.info.balance = U256::ZERO;
530 Some(ENTRY::account_destroyed(
531 address,
532 target,
533 previously_destroyed,
534 balance,
535 ))
536 } else if address != target {
537 acc.info.balance = U256::ZERO;
538 Some(ENTRY::balance_transfer(address, target, balance))
539 } else {
540 None
545 };
546
547 if let Some(entry) = journal_entry {
548 self.journal.push(entry);
549 };
550
551 Ok(StateLoad {
552 data: SelfDestructResult {
553 had_value: !balance.is_zero(),
554 target_exists: !is_empty,
555 previously_destroyed,
556 },
557 is_cold,
558 })
559 }
560
561 #[inline]
563 pub fn load_account<DB: Database>(
564 &mut self,
565 db: &mut DB,
566 address: Address,
567 ) -> Result<StateLoad<&mut Account>, DB::Error> {
568 self.load_account_optional(db, address, false, [])
569 }
570
571 #[inline]
579 pub fn load_account_delegated<DB: Database>(
580 &mut self,
581 db: &mut DB,
582 address: Address,
583 ) -> Result<StateLoad<AccountLoad>, DB::Error> {
584 let spec = self.spec;
585 let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
586 let account = self.load_account_optional(db, address, is_eip7702_enabled, [])?;
587 let is_empty = account.state_clear_aware_is_empty(spec);
588
589 let mut account_load = StateLoad::new(
590 AccountLoad {
591 is_delegate_account_cold: None,
592 is_empty,
593 },
594 account.is_cold,
595 );
596
597 if let Some(Bytecode::Eip7702(code)) = &account.info.code {
599 let address = code.address();
600 let delegate_account = self.load_account(db, address)?;
601 account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
602 }
603
604 Ok(account_load)
605 }
606
607 #[inline]
614 pub fn load_code<DB: Database>(
615 &mut self,
616 db: &mut DB,
617 address: Address,
618 ) -> Result<StateLoad<&mut Account>, DB::Error> {
619 self.load_account_optional(db, address, true, [])
620 }
621
622 #[inline]
624 pub fn load_account_optional<DB: Database>(
625 &mut self,
626 db: &mut DB,
627 address: Address,
628 load_code: bool,
629 storage_keys: impl IntoIterator<Item = StorageKey>,
630 ) -> Result<StateLoad<&mut Account>, DB::Error> {
631 let load = match self.state.entry(address) {
632 Entry::Occupied(entry) => {
633 let account = entry.into_mut();
634 let is_cold = account.mark_warm();
635 StateLoad {
636 data: account,
637 is_cold,
638 }
639 }
640 Entry::Vacant(vac) => {
641 let account = if let Some(account) = db.basic(address)? {
642 account.into()
643 } else {
644 Account::new_not_existing(self.transaction_id)
645 };
646
647 let is_cold = !self.warm_preloaded_addresses.contains(&address);
649
650 StateLoad {
651 data: vac.insert(account),
652 is_cold,
653 }
654 }
655 };
656 if load.is_cold {
658 self.journal.push(ENTRY::account_warmed(address));
659 }
660 if load_code {
661 let info = &mut load.data.info;
662 if info.code.is_none() {
663 let code = if info.code_hash == KECCAK_EMPTY {
664 Bytecode::default()
665 } else {
666 db.code_by_hash(info.code_hash)?
667 };
668 info.code = Some(code);
669 }
670 }
671
672 for storage_key in storage_keys.into_iter() {
673 sload_with_account(
674 load.data,
675 db,
676 &mut self.journal,
677 self.transaction_id,
678 address,
679 storage_key,
680 )?;
681 }
682 Ok(load)
683 }
684
685 #[inline]
691 pub fn sload<DB: Database>(
692 &mut self,
693 db: &mut DB,
694 address: Address,
695 key: StorageKey,
696 ) -> Result<StateLoad<StorageValue>, DB::Error> {
697 let account = self.state.get_mut(&address).unwrap();
699 sload_with_account(
701 account,
702 db,
703 &mut self.journal,
704 self.transaction_id,
705 address,
706 key,
707 )
708 }
709
710 #[inline]
716 pub fn sstore<DB: Database>(
717 &mut self,
718 db: &mut DB,
719 address: Address,
720 key: StorageKey,
721 new: StorageValue,
722 ) -> Result<StateLoad<SStoreResult>, DB::Error> {
723 let present = self.sload(db, address, key)?;
725 let acc = self.state.get_mut(&address).unwrap();
726
727 let slot = acc.storage.get_mut(&key).unwrap();
729
730 if present.data == new {
732 return Ok(StateLoad::new(
733 SStoreResult {
734 original_value: slot.original_value(),
735 present_value: present.data,
736 new_value: new,
737 },
738 present.is_cold,
739 ));
740 }
741
742 self.journal
743 .push(ENTRY::storage_changed(address, key, present.data));
744 slot.present_value = new;
746 Ok(StateLoad::new(
747 SStoreResult {
748 original_value: slot.original_value(),
749 present_value: present.data,
750 new_value: new,
751 },
752 present.is_cold,
753 ))
754 }
755
756 #[inline]
760 pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
761 self.transient_storage
762 .get(&(address, key))
763 .copied()
764 .unwrap_or_default()
765 }
766
767 #[inline]
774 pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
775 let had_value = if new.is_zero() {
776 self.transient_storage.remove(&(address, key))
780 } else {
781 let previous_value = self
783 .transient_storage
784 .insert((address, key), new)
785 .unwrap_or_default();
786
787 if previous_value != new {
789 Some(previous_value)
791 } else {
792 None
793 }
794 };
795
796 if let Some(had_value) = had_value {
797 self.journal
799 .push(ENTRY::transient_storage_changed(address, key, had_value));
800 }
801 }
802
803 #[inline]
805 pub fn log(&mut self, log: Log) {
806 self.logs.push(log);
807 }
808}
809
810#[inline]
812pub fn sload_with_account<DB: Database, ENTRY: JournalEntryTr>(
813 account: &mut Account,
814 db: &mut DB,
815 journal: &mut Vec<ENTRY>,
816 transaction_id: usize,
817 address: Address,
818 key: StorageKey,
819) -> Result<StateLoad<StorageValue>, DB::Error> {
820 let is_newly_created = account.is_created();
821 let (value, is_cold) = match account.storage.entry(key) {
822 Entry::Occupied(occ) => {
823 let slot = occ.into_mut();
824 let is_cold = slot.mark_warm_with_transaction_id(transaction_id);
825 (slot.present_value, is_cold)
826 }
827 Entry::Vacant(vac) => {
828 let value = if is_newly_created {
830 StorageValue::ZERO
831 } else {
832 db.storage(address, key)?
833 };
834
835 vac.insert(EvmStorageSlot::new(value, transaction_id));
836
837 (value, true)
838 }
839 };
840
841 if is_cold {
842 journal.push(ENTRY::storage_warmed(address, key));
844 }
845
846 Ok(StateLoad::new(value, is_cold))
847}