revm_context_interface/journaled_state/
account.rs

1//! This module contains [`JournaledAccount`] struct a wrapper around account and journal entries that
2//! allow updates to the account and journal entries.
3//!
4//! Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it.
5
6use super::entry::JournalEntryTr;
7use core::ops::Deref;
8use primitives::{Address, B256, KECCAK_EMPTY, U256};
9use state::{Account, Bytecode};
10use std::vec::Vec;
11
12/// Journaled account contains both mutable account and journal entries.
13///
14/// Useful to encapsulate account and journal entries together. So when account gets changed, we can add a journal entry for it.
15#[derive(Debug, PartialEq, Eq)]
16pub struct JournaledAccount<'a, ENTRY: JournalEntryTr> {
17    /// Address of the account.
18    address: Address,
19    /// Mutable account.
20    account: &'a mut Account,
21    /// Journal entries.
22    journal_entries: &'a mut Vec<ENTRY>,
23}
24
25impl<'a, ENTRY: JournalEntryTr> JournaledAccount<'a, ENTRY> {
26    /// Consumes the journaled account and returns the mutable account.
27    #[inline]
28    pub fn into_account_ref(self) -> &'a Account {
29        self.account
30    }
31
32    /// Creates a new journaled account.
33    #[inline]
34    pub fn new(
35        address: Address,
36        account: &'a mut Account,
37        journal_entries: &'a mut Vec<ENTRY>,
38    ) -> Self {
39        Self {
40            address,
41            account,
42            journal_entries,
43        }
44    }
45
46    /// Returns the balance of the account.
47    #[inline]
48    pub fn balance(&self) -> &U256 {
49        &self.account.info.balance
50    }
51
52    /// Returns the nonce of the account.
53    #[inline]
54    pub fn nonce(&self) -> u64 {
55        self.account.info.nonce
56    }
57
58    /// Returns the code hash of the account.
59    #[inline]
60    pub fn code_hash(&self) -> &B256 {
61        &self.account.info.code_hash
62    }
63
64    /// Returns the code of the account.
65    #[inline]
66    pub fn code(&self) -> Option<&Bytecode> {
67        self.account.info.code.as_ref()
68    }
69
70    /// Touches the account.
71    #[inline]
72    pub fn touch(&mut self) {
73        if !self.account.status.is_touched() {
74            self.account.mark_touch();
75            self.journal_entries
76                .push(ENTRY::account_touched(self.address));
77        }
78    }
79
80    /// Marks the account as cold without making a journal entry.
81    ///
82    /// Changing account without journal entry can be a footgun as reverting of the state change
83    /// would not happen without entry. It is the reason why this function has an `unsafe` prefix.
84    ///
85    /// If account is in access list, it would still be marked as warm if account get accessed again.
86    #[inline]
87    pub fn unsafe_mark_cold(&mut self) {
88        self.account.mark_cold();
89    }
90
91    /// Sets the balance of the account.
92    ///
93    /// If balance is the same, we don't add a journal entry.
94    ///
95    /// Touches the account in all cases.
96    #[inline]
97    pub fn set_balance(&mut self, balance: U256) {
98        self.touch();
99        if self.account.info.balance != balance {
100            self.journal_entries.push(ENTRY::balance_changed(
101                self.address,
102                self.account.info.balance,
103            ));
104            self.account.info.set_balance(balance);
105        }
106    }
107
108    /// Increments the balance of the account.
109    ///
110    /// Touches the account in all cases.
111    #[inline]
112    pub fn incr_balance(&mut self, balance: U256) -> bool {
113        self.touch();
114        let Some(balance) = self.account.info.balance.checked_add(balance) else {
115            return false;
116        };
117        self.set_balance(balance);
118        true
119    }
120
121    /// Decrements the balance of the account.
122    ///
123    /// Touches the account in all cases.
124    #[inline]
125    pub fn decr_balance(&mut self, balance: U256) -> bool {
126        self.touch();
127        let Some(balance) = self.account.info.balance.checked_sub(balance) else {
128            return false;
129        };
130        self.set_balance(balance);
131        true
132    }
133
134    /// Bumps the nonce of the account.
135    ///
136    /// Touches the account in all cases.
137    ///
138    /// Returns true if nonce was bumped, false if nonce is at the max value.
139    #[inline]
140    pub fn bump_nonce(&mut self) -> bool {
141        self.touch();
142        let Some(nonce) = self.account.info.nonce.checked_add(1) else {
143            return false;
144        };
145        self.account.info.set_nonce(nonce);
146        self.journal_entries.push(ENTRY::nonce_bumped(self.address));
147        true
148    }
149
150    /// Set the nonce of the account and create a journal entry.
151    ///
152    /// Touches the account in all cases.
153    #[inline]
154    pub fn set_nonce(&mut self, nonce: u64) {
155        self.touch();
156        let previous_nonce = self.account.info.nonce;
157        self.account.info.set_nonce(nonce);
158        self.journal_entries
159            .push(ENTRY::nonce_changed(self.address, previous_nonce));
160    }
161
162    /// Set the nonce of the account without creating a journal entry.
163    ///
164    /// Changing account without journal entry can be a footgun as reverting of the state change
165    /// would not happen without entry. It is the reason why this function has an `unsafe` prefix.
166    #[inline]
167    pub fn unsafe_set_nonce(&mut self, nonce: u64) {
168        self.account.info.set_nonce(nonce);
169    }
170
171    /// Sets the code of the account.
172    ///
173    /// Touches the account in all cases.
174    #[inline]
175    pub fn set_code(&mut self, code_hash: B256, code: Bytecode) {
176        self.touch();
177        self.account.info.set_code_hash(code_hash);
178        self.account.info.set_code(code);
179        self.journal_entries.push(ENTRY::code_changed(self.address));
180    }
181
182    /// Sets the code of the account. Calculates hash of the code.
183    ///
184    /// Touches the account in all cases.
185    #[inline]
186    pub fn set_code_and_hash_slow(&mut self, code: Bytecode) {
187        let code_hash = code.hash_slow();
188        self.set_code(code_hash, code);
189    }
190
191    /// Delegates the account to another address (EIP-7702).
192    ///
193    /// This touches the account, sets the code to the delegation designation,
194    /// and bumps the nonce.
195    #[inline]
196    pub fn delegate(&mut self, address: Address) {
197        let (bytecode, hash) = if address.is_zero() {
198            (Bytecode::default(), KECCAK_EMPTY)
199        } else {
200            let bytecode = Bytecode::new_eip7702(address);
201            let hash = bytecode.hash_slow();
202            (bytecode, hash)
203        };
204        self.touch();
205        self.set_code(hash, bytecode);
206        self.bump_nonce();
207    }
208}
209
210impl<'a, ENTRY: JournalEntryTr> Deref for JournaledAccount<'a, ENTRY> {
211    type Target = Account;
212
213    fn deref(&self) -> &Self::Target {
214        self.account
215    }
216}