1use super::warm_addresses::WarmAddresses;
3use bytecode::Bytecode;
4use context_interface::{
5    context::{SStoreResult, SelfDestructResult, StateLoad},
6    journaled_state::{
7        account::JournaledAccount,
8        entry::{JournalEntryTr, SelfdestructionRevertStatus},
9    },
10    journaled_state::{AccountLoad, JournalCheckpoint, JournalLoadError, TransferError},
11};
12use core::mem;
13use database_interface::Database;
14use primitives::{
15    hardfork::SpecId::{self, *},
16    hash_map::Entry,
17    Address, HashMap, Log, StorageKey, StorageValue, B256, KECCAK_EMPTY, U256,
18};
19use state::{Account, EvmState, EvmStorageSlot, TransientStorage};
20use std::vec::Vec;
21#[derive(Debug, Clone, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
26pub struct JournalInner<ENTRY> {
27    pub state: EvmState,
29    pub transient_storage: TransientStorage,
33    pub logs: Vec<Log>,
35    pub depth: usize,
37    pub journal: Vec<ENTRY>,
39    pub transaction_id: usize,
45    pub spec: SpecId,
58    pub warm_addresses: WarmAddresses,
60}
61
62impl<ENTRY: JournalEntryTr> Default for JournalInner<ENTRY> {
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68impl<ENTRY: JournalEntryTr> JournalInner<ENTRY> {
69    pub fn new() -> JournalInner<ENTRY> {
74        Self {
75            state: HashMap::default(),
76            transient_storage: TransientStorage::default(),
77            logs: Vec::new(),
78            journal: Vec::default(),
79            transaction_id: 0,
80            depth: 0,
81            spec: SpecId::default(),
82            warm_addresses: WarmAddresses::new(),
83        }
84    }
85
86    #[inline]
88    pub fn take_logs(&mut self) -> Vec<Log> {
89        mem::take(&mut self.logs)
90    }
91
92    pub fn commit_tx(&mut self) {
100        let Self {
103            state,
104            transient_storage,
105            logs,
106            depth,
107            journal,
108            transaction_id,
109            spec,
110            warm_addresses,
111        } = self;
112        let _ = spec;
114        let _ = state;
115        transient_storage.clear();
116        *depth = 0;
117
118        journal.clear();
120
121        warm_addresses.clear_coinbase_and_access_list();
123        *transaction_id += 1;
125        logs.clear();
126    }
127
128    pub fn discard_tx(&mut self) {
130        let Self {
132            state,
133            transient_storage,
134            logs,
135            depth,
136            journal,
137            transaction_id,
138            spec,
139            warm_addresses,
140        } = self;
141        let is_spurious_dragon_enabled = spec.is_enabled_in(SPURIOUS_DRAGON);
142        journal.drain(..).rev().for_each(|entry| {
144            entry.revert(state, None, is_spurious_dragon_enabled);
145        });
146        transient_storage.clear();
147        *depth = 0;
148        logs.clear();
149        *transaction_id += 1;
150
151        warm_addresses.clear_coinbase_and_access_list();
153    }
154
155    #[inline]
160    pub fn finalize(&mut self) -> EvmState {
161        let Self {
164            state,
165            transient_storage,
166            logs,
167            depth,
168            journal,
169            transaction_id,
170            spec,
171            warm_addresses,
172        } = self;
173        let _ = spec;
175        warm_addresses.clear_coinbase_and_access_list();
177
178        let state = mem::take(state);
179        logs.clear();
180        transient_storage.clear();
181
182        journal.clear();
184        *depth = 0;
185        *transaction_id = 0;
187
188        state
189    }
190
191    #[inline]
193    pub fn state(&mut self) -> &mut EvmState {
194        &mut self.state
195    }
196
197    #[inline]
199    pub fn set_spec_id(&mut self, spec: SpecId) {
200        self.spec = spec;
201    }
202
203    #[inline]
207    pub fn touch(&mut self, address: Address) {
208        if let Some(account) = self.state.get_mut(&address) {
209            Self::touch_account(&mut self.journal, address, account);
210        }
211    }
212
213    #[inline]
215    fn touch_account(journal: &mut Vec<ENTRY>, address: Address, account: &mut Account) {
216        if !account.is_touched() {
217            journal.push(ENTRY::account_touched(address));
218            account.mark_touch();
219        }
220    }
221
222    #[inline]
230    pub fn account(&self, address: Address) -> &Account {
231        self.state
232            .get(&address)
233            .expect("Account expected to be loaded") }
235
236    #[inline]
240    pub fn set_code_with_hash(&mut self, address: Address, code: Bytecode, hash: B256) {
241        let account = self.state.get_mut(&address).unwrap();
242        Self::touch_account(&mut self.journal, address, account);
243
244        self.journal.push(ENTRY::code_changed(address));
245
246        account.info.code_hash = hash;
247        account.info.code = Some(code);
248    }
249
250    #[inline]
256    pub fn set_code(&mut self, address: Address, code: Bytecode) {
257        if let Bytecode::Eip7702(eip7702_bytecode) = &code {
258            if eip7702_bytecode.address().is_zero() {
259                self.set_code_with_hash(address, Bytecode::default(), KECCAK_EMPTY);
260                return;
261            }
262        }
263
264        let hash = code.hash_slow();
265        self.set_code_with_hash(address, code, hash)
266    }
267
268    #[inline]
270    pub fn caller_accounting_journal_entry(
271        &mut self,
272        address: Address,
273        old_balance: U256,
274        bump_nonce: bool,
275    ) {
276        self.journal
278            .push(ENTRY::balance_changed(address, old_balance));
279        self.journal.push(ENTRY::account_touched(address));
281
282        if bump_nonce {
283            self.journal.push(ENTRY::nonce_changed(address));
285        }
286    }
287
288    #[inline]
292    pub fn balance_incr<DB: Database>(
293        &mut self,
294        db: &mut DB,
295        address: Address,
296        balance: U256,
297    ) -> Result<(), DB::Error> {
298        let mut account = self.load_account_mut(db, address)?.data;
299        account.incr_balance(balance);
300        Ok(())
301    }
302
303    #[inline]
305    pub fn nonce_bump_journal_entry(&mut self, address: Address) {
306        self.journal.push(ENTRY::nonce_changed(address));
307    }
308
309    #[inline]
315    pub fn transfer_loaded(
316        &mut self,
317        from: Address,
318        to: Address,
319        balance: U256,
320    ) -> Option<TransferError> {
321        if from == to {
322            let from_balance = self.state.get_mut(&to).unwrap().info.balance;
323            if balance > from_balance {
325                return Some(TransferError::OutOfFunds);
326            }
327            return None;
328        }
329
330        if balance.is_zero() {
331            Self::touch_account(&mut self.journal, to, self.state.get_mut(&to).unwrap());
332            return None;
333        }
334
335        let from_account = self.state.get_mut(&from).unwrap();
337        Self::touch_account(&mut self.journal, from, from_account);
338        let from_balance = &mut from_account.info.balance;
339        let Some(from_balance_decr) = from_balance.checked_sub(balance) else {
340            return Some(TransferError::OutOfFunds);
341        };
342        *from_balance = from_balance_decr;
343
344        let to_account = self.state.get_mut(&to).unwrap();
346        Self::touch_account(&mut self.journal, to, to_account);
347        let to_balance = &mut to_account.info.balance;
348        let Some(to_balance_incr) = to_balance.checked_add(balance) else {
349            return Some(TransferError::OverflowPayment);
351        };
352        *to_balance = to_balance_incr;
353
354        self.journal
356            .push(ENTRY::balance_transfer(from, to, balance));
357
358        None
359    }
360
361    #[inline]
363    pub fn transfer<DB: Database>(
364        &mut self,
365        db: &mut DB,
366        from: Address,
367        to: Address,
368        balance: U256,
369    ) -> Result<Option<TransferError>, DB::Error> {
370        self.load_account(db, from)?;
371        self.load_account(db, to)?;
372        Ok(self.transfer_loaded(from, to, balance))
373    }
374
375    #[inline]
391    pub fn create_account_checkpoint(
392        &mut self,
393        caller: Address,
394        target_address: Address,
395        balance: U256,
396        spec_id: SpecId,
397    ) -> Result<JournalCheckpoint, TransferError> {
398        let checkpoint = self.checkpoint();
400
401        let target_acc = self.state.get_mut(&target_address).unwrap();
403        let last_journal = &mut self.journal;
404
405        if target_acc.info.code_hash != KECCAK_EMPTY || target_acc.info.nonce != 0 {
410            self.checkpoint_revert(checkpoint);
411            return Err(TransferError::CreateCollision);
412        }
413
414        let is_created_globally = target_acc.mark_created_locally();
416
417        last_journal.push(ENTRY::account_created(target_address, is_created_globally));
419        target_acc.info.code = None;
420        if spec_id.is_enabled_in(SPURIOUS_DRAGON) {
422            target_acc.info.nonce = 1;
424        }
425
426        Self::touch_account(last_journal, target_address, target_acc);
429
430        let Some(new_balance) = target_acc.info.balance.checked_add(balance) else {
432            self.checkpoint_revert(checkpoint);
433            return Err(TransferError::OverflowPayment);
434        };
435        target_acc.info.balance = new_balance;
436
437        self.state.get_mut(&caller).unwrap().info.balance -= balance;
439
440        last_journal.push(ENTRY::balance_transfer(caller, target_address, balance));
442
443        Ok(checkpoint)
444    }
445
446    #[inline]
448    pub fn checkpoint(&mut self) -> JournalCheckpoint {
449        let checkpoint = JournalCheckpoint {
450            log_i: self.logs.len(),
451            journal_i: self.journal.len(),
452        };
453        self.depth += 1;
454        checkpoint
455    }
456
457    #[inline]
459    pub fn checkpoint_commit(&mut self) {
460        self.depth = self.depth.saturating_sub(1);
461    }
462
463    #[inline]
465    pub fn checkpoint_revert(&mut self, checkpoint: JournalCheckpoint) {
466        let is_spurious_dragon_enabled = self.spec.is_enabled_in(SPURIOUS_DRAGON);
467        let state = &mut self.state;
468        let transient_storage = &mut self.transient_storage;
469        self.depth = self.depth.saturating_sub(1);
470        self.logs.truncate(checkpoint.log_i);
471
472        if checkpoint.journal_i < self.journal.len() {
474            self.journal
475                .drain(checkpoint.journal_i..)
476                .rev()
477                .for_each(|entry| {
478                    entry.revert(state, Some(transient_storage), is_spurious_dragon_enabled);
479                });
480        }
481    }
482
483    #[inline]
495    pub fn selfdestruct<DB: Database>(
496        &mut self,
497        db: &mut DB,
498        address: Address,
499        target: Address,
500    ) -> Result<StateLoad<SelfDestructResult>, DB::Error> {
501        let spec = self.spec;
502        let account_load = self.load_account(db, target)?;
503        let is_cold = account_load.is_cold;
504        let is_empty = account_load.state_clear_aware_is_empty(spec);
505
506        if address != target {
507            let acc_balance = self.state.get(&address).unwrap().info.balance;
510
511            let target_account = self.state.get_mut(&target).unwrap();
512            Self::touch_account(&mut self.journal, target, target_account);
513            target_account.info.balance += acc_balance;
514        }
515
516        let acc = self.state.get_mut(&address).unwrap();
517        let balance = acc.info.balance;
518
519        let destroyed_status = if !acc.is_selfdestructed() {
520            SelfdestructionRevertStatus::GloballySelfdestroyed
521        } else if !acc.is_selfdestructed_locally() {
522            SelfdestructionRevertStatus::LocallySelfdestroyed
523        } else {
524            SelfdestructionRevertStatus::RepeatedSelfdestruction
525        };
526
527        let is_cancun_enabled = spec.is_enabled_in(CANCUN);
528
529        let journal_entry = if acc.is_created_locally() || !is_cancun_enabled {
531            acc.mark_selfdestructed_locally();
532            acc.info.balance = U256::ZERO;
533            Some(ENTRY::account_destroyed(
534                address,
535                target,
536                destroyed_status,
537                balance,
538            ))
539        } else if address != target {
540            acc.info.balance = U256::ZERO;
541            Some(ENTRY::balance_transfer(address, target, balance))
542        } else {
543            None
548        };
549
550        if let Some(entry) = journal_entry {
551            self.journal.push(entry);
552        };
553
554        Ok(StateLoad {
555            data: SelfDestructResult {
556                had_value: !balance.is_zero(),
557                target_exists: !is_empty,
558                previously_destroyed: destroyed_status
559                    == SelfdestructionRevertStatus::RepeatedSelfdestruction,
560            },
561            is_cold,
562        })
563    }
564
565    #[inline]
567    pub fn load_account<DB: Database>(
568        &mut self,
569        db: &mut DB,
570        address: Address,
571    ) -> Result<StateLoad<&Account>, DB::Error> {
572        self.load_account_optional(db, address, false, false)
573            .map_err(JournalLoadError::unwrap_db_error)
574    }
575
576    #[inline]
584    pub fn load_account_delegated<DB: Database>(
585        &mut self,
586        db: &mut DB,
587        address: Address,
588    ) -> Result<StateLoad<AccountLoad>, DB::Error> {
589        let spec = self.spec;
590        let is_eip7702_enabled = spec.is_enabled_in(SpecId::PRAGUE);
591        let account = self
592            .load_account_optional(db, address, is_eip7702_enabled, false)
593            .map_err(JournalLoadError::unwrap_db_error)?;
594        let is_empty = account.state_clear_aware_is_empty(spec);
595
596        let mut account_load = StateLoad::new(
597            AccountLoad {
598                is_delegate_account_cold: None,
599                is_empty,
600            },
601            account.is_cold,
602        );
603
604        if let Some(Bytecode::Eip7702(code)) = &account.info.code {
606            let address = code.address();
607            let delegate_account = self
608                .load_account_optional(db, address, true, false)
609                .map_err(JournalLoadError::unwrap_db_error)?;
610            account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
611        }
612
613        Ok(account_load)
614    }
615
616    #[inline]
623    pub fn load_code<DB: Database>(
624        &mut self,
625        db: &mut DB,
626        address: Address,
627    ) -> Result<StateLoad<&Account>, DB::Error> {
628        self.load_account_optional(db, address, true, false)
629            .map_err(JournalLoadError::unwrap_db_error)
630    }
631
632    #[inline]
634    pub fn load_account_optional<DB: Database>(
635        &mut self,
636        db: &mut DB,
637        address: Address,
638        load_code: bool,
639        skip_cold_load: bool,
640    ) -> Result<StateLoad<&Account>, JournalLoadError<DB::Error>> {
641        let load = self.load_account_mut_optional_code(db, address, load_code, skip_cold_load)?;
642        Ok(load.map(|i| i.into_account_ref()))
643    }
644
645    #[inline]
647    pub fn load_account_mut<DB: Database>(
648        &mut self,
649        db: &mut DB,
650        address: Address,
651    ) -> Result<StateLoad<JournaledAccount<'_, ENTRY>>, DB::Error> {
652        self.load_account_mut_optional_code(db, address, false, false)
653            .map_err(JournalLoadError::unwrap_db_error)
654    }
655
656    #[inline(never)]
658    pub fn load_account_mut_optional_code<DB: Database>(
659        &mut self,
660        db: &mut DB,
661        address: Address,
662        load_code: bool,
663        skip_cold_load: bool,
664    ) -> Result<StateLoad<JournaledAccount<'_, ENTRY>>, JournalLoadError<DB::Error>> {
665        let load = match self.state.entry(address) {
666            Entry::Occupied(entry) => {
667                let account = entry.into_mut();
668
669                let mut is_cold = account.is_cold_transaction_id(self.transaction_id);
671                if is_cold {
672                    let should_be_cold = self.warm_addresses.is_cold(&address);
674
675                    if should_be_cold && skip_cold_load {
677                        return Err(JournalLoadError::ColdLoadSkipped);
678                    }
679                    is_cold = should_be_cold;
680
681                    account.mark_warm_with_transaction_id(self.transaction_id);
683
684                    if account.is_selfdestructed_locally() {
687                        account.selfdestruct();
688                        account.unmark_selfdestructed_locally();
689                    }
690                    account.unmark_created_locally();
692                }
693                StateLoad {
694                    data: account,
695                    is_cold,
696                }
697            }
698            Entry::Vacant(vac) => {
699                let is_cold = self.warm_addresses.is_cold(&address);
701
702                if is_cold && skip_cold_load {
704                    return Err(JournalLoadError::ColdLoadSkipped);
705                }
706                let account = if let Some(account) = db.basic(address)? {
707                    account.into()
708                } else {
709                    Account::new_not_existing(self.transaction_id)
710                };
711
712                StateLoad {
713                    data: vac.insert(account),
714                    is_cold,
715                }
716            }
717        };
718
719        if load.is_cold {
721            self.journal.push(ENTRY::account_warmed(address));
722        }
723
724        if load_code && load.data.info.code.is_none() {
725            let info = &mut load.data.info;
726            let code = if info.code_hash == KECCAK_EMPTY {
727                Bytecode::default()
728            } else {
729                db.code_by_hash(info.code_hash)?
730            };
731            info.code = Some(code);
732        }
733
734        Ok(load.map(|i| JournaledAccount::new(address, i, &mut self.journal)))
735    }
736
737    #[inline]
743    pub fn sload<DB: Database>(
744        &mut self,
745        db: &mut DB,
746        address: Address,
747        key: StorageKey,
748        skip_cold_load: bool,
749    ) -> Result<StateLoad<StorageValue>, JournalLoadError<DB::Error>> {
750        let account = self.state.get_mut(&address).unwrap();
752
753        let is_newly_created = account.is_created();
754        let (value, is_cold) = match account.storage.entry(key) {
755            Entry::Occupied(occ) => {
756                let slot = occ.into_mut();
757                let is_cold = slot.is_cold_transaction_id(self.transaction_id);
759                if skip_cold_load && is_cold {
760                    return Err(JournalLoadError::ColdLoadSkipped);
761                }
762                slot.mark_warm_with_transaction_id(self.transaction_id);
763                (slot.present_value, is_cold)
764            }
765            Entry::Vacant(vac) => {
766                if skip_cold_load {
767                    return Err(JournalLoadError::ColdLoadSkipped);
768                }
769                let value = if is_newly_created {
771                    StorageValue::ZERO
772                } else {
773                    db.storage(address, key)?
774                };
775                vac.insert(EvmStorageSlot::new(value, self.transaction_id));
776
777                let is_cold = !self.warm_addresses.is_storage_warm(&address, &key);
779
780                (value, is_cold)
781            }
782        };
783
784        if is_cold {
785            self.journal.push(ENTRY::storage_warmed(address, key));
787        }
788
789        Ok(StateLoad::new(value, is_cold))
790    }
791
792    #[inline]
798    pub fn sstore<DB: Database>(
799        &mut self,
800        db: &mut DB,
801        address: Address,
802        key: StorageKey,
803        new: StorageValue,
804        skip_cold_load: bool,
805    ) -> Result<StateLoad<SStoreResult>, JournalLoadError<DB::Error>> {
806        let present = self.sload(db, address, key, skip_cold_load)?;
808        let acc = self.state.get_mut(&address).unwrap();
809
810        let slot = acc.storage.get_mut(&key).unwrap();
812
813        if present.data == new {
815            return Ok(StateLoad::new(
816                SStoreResult {
817                    original_value: slot.original_value(),
818                    present_value: present.data,
819                    new_value: new,
820                },
821                present.is_cold,
822            ));
823        }
824
825        self.journal
826            .push(ENTRY::storage_changed(address, key, present.data));
827        slot.present_value = new;
829        Ok(StateLoad::new(
830            SStoreResult {
831                original_value: slot.original_value(),
832                present_value: present.data,
833                new_value: new,
834            },
835            present.is_cold,
836        ))
837    }
838
839    #[inline]
843    pub fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue {
844        self.transient_storage
845            .get(&(address, key))
846            .copied()
847            .unwrap_or_default()
848    }
849
850    #[inline]
857    pub fn tstore(&mut self, address: Address, key: StorageKey, new: StorageValue) {
858        let had_value = if new.is_zero() {
859            self.transient_storage.remove(&(address, key))
863        } else {
864            let previous_value = self
866                .transient_storage
867                .insert((address, key), new)
868                .unwrap_or_default();
869
870            if previous_value != new {
872                Some(previous_value)
874            } else {
875                None
876            }
877        };
878
879        if let Some(had_value) = had_value {
880            self.journal
882                .push(ENTRY::transient_storage_changed(address, key, had_value));
883        }
884    }
885
886    #[inline]
888    pub fn log(&mut self, log: Log) {
889        self.logs.push(log);
890    }
891}