revm_state/
account_info.rs

1use bytecode::Bytecode;
2use core::{
3    cmp::Ordering,
4    hash::{Hash, Hasher},
5};
6use primitives::{OnceLock, B256, KECCAK_EMPTY, U256};
7
8/// Account information that contains balance, nonce, code hash and code
9///
10/// Code is set as optional.
11#[derive(Clone, Debug, Eq)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct AccountInfo {
14    /// Account balance.
15    pub balance: U256,
16    /// Account nonce.
17    pub nonce: u64,
18    /// Hash of the raw bytes in `code`, or [`KECCAK_EMPTY`].
19    pub code_hash: B256,
20    /// Used as a hint to optimize the access to the storage of account.
21    ///
22    /// It is set when account is loaded from the database, and if it is `Some` it will called
23    /// by journal to ask database the storage with this account_id (It will still send the address to the database).
24    #[cfg_attr(feature = "serde", serde(skip))]
25    pub account_id: Option<usize>,
26    /// [`Bytecode`] data associated with this account.
27    ///
28    /// If [`None`], `code_hash` will be used to fetch it from the database, if code needs to be
29    /// loaded from inside `revm`.
30    ///
31    /// By default, this is `Some(Bytecode::default())`.
32    pub code: Option<Bytecode>,
33}
34
35impl Default for AccountInfo {
36    fn default() -> Self {
37        static DEFAULT: OnceLock<AccountInfo> = OnceLock::new();
38        DEFAULT
39            .get_or_init(|| Self {
40                balance: U256::ZERO,
41                code_hash: KECCAK_EMPTY,
42                account_id: None,
43                nonce: 0,
44                code: Some(Bytecode::default()),
45            })
46            .clone()
47    }
48}
49
50impl PartialEq for AccountInfo {
51    fn eq(&self, other: &Self) -> bool {
52        self.balance == other.balance
53            && self.nonce == other.nonce
54            && self.code_hash == other.code_hash
55    }
56}
57
58impl Hash for AccountInfo {
59    fn hash<H: Hasher>(&self, state: &mut H) {
60        self.balance.hash(state);
61        self.nonce.hash(state);
62        self.code_hash.hash(state);
63    }
64}
65
66impl PartialOrd for AccountInfo {
67    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
68        Some(self.cmp(other))
69    }
70}
71
72impl Ord for AccountInfo {
73    fn cmp(&self, other: &Self) -> Ordering {
74        self.balance
75            .cmp(&other.balance)
76            .then_with(|| self.nonce.cmp(&other.nonce))
77            .then_with(|| self.code_hash.cmp(&other.code_hash))
78    }
79}
80
81impl AccountInfo {
82    /// Creates a new [`AccountInfo`] with the given fields.
83    #[inline]
84    pub fn new(balance: U256, nonce: u64, code_hash: B256, code: Bytecode) -> Self {
85        Self {
86            balance,
87            nonce,
88            code: Some(code),
89            code_hash,
90            account_id: None,
91        }
92    }
93
94    /// Creates a new [`AccountInfo`] with the given code.
95    ///
96    /// # Note
97    ///
98    /// As code hash is calculated with [`Bytecode::hash_slow`] there will be performance penalty if used frequently.
99    pub fn with_code(self, code: Bytecode) -> Self {
100        Self {
101            code_hash: code.hash_slow(),
102            code: Some(code),
103            ..self
104        }
105    }
106
107    /// Creates a new [`AccountInfo`] with the given code hash.
108    ///
109    /// # Note
110    ///
111    /// Resets code to `None`. Not guaranteed to maintain invariant `code` and `code_hash`. See
112    /// also [Self::with_code_and_hash].
113    pub fn with_code_hash(self, code_hash: B256) -> Self {
114        Self {
115            code_hash,
116            code: None,
117            ..self
118        }
119    }
120
121    /// Creates a new [`AccountInfo`] with the given code and code hash.
122    ///
123    /// # Note
124    ///
125    /// In debug mode panics if [`Bytecode::hash_slow`] called on `code` is not equivalent to
126    /// `code_hash`. See also [`Self::with_code`].
127    pub fn with_code_and_hash(self, code: Bytecode, code_hash: B256) -> Self {
128        debug_assert_eq!(code.hash_slow(), code_hash);
129        Self {
130            code_hash,
131            code: Some(code),
132            ..self
133        }
134    }
135
136    /// Creates a new [`AccountInfo`] with the given balance.
137    pub fn with_balance(mut self, balance: U256) -> Self {
138        self.balance = balance;
139        self
140    }
141
142    /// Creates a new [`AccountInfo`] with the given nonce.
143    pub fn with_nonce(mut self, nonce: u64) -> Self {
144        self.nonce = nonce;
145        self
146    }
147
148    /// Sets the [`AccountInfo`] `balance`.
149    #[inline]
150    pub fn set_balance(&mut self, balance: U256) -> &mut Self {
151        self.balance = balance;
152        self
153    }
154
155    /// Sets the [`AccountInfo`] `nonce`.
156    #[inline]
157    pub fn set_nonce(&mut self, nonce: u64) -> &mut Self {
158        self.nonce = nonce;
159        self
160    }
161
162    /// Sets the [`AccountInfo`] `code_hash` and clears any cached bytecode.
163    ///
164    /// # Note
165    ///
166    /// Calling this after `set_code(...)` will remove the bytecode you just set.
167    /// If you intend to mutate the code, use only `set_code`.
168    #[inline]
169    pub fn set_code_hash(&mut self, code_hash: B256) -> &mut Self {
170        self.code = None;
171        self.code_hash = code_hash;
172        self
173    }
174
175    /// Replaces the [`AccountInfo`] bytecode and recalculates `code_hash`.
176    ///
177    /// # Note
178    ///
179    /// As code hash is calculated with [`Bytecode::hash_slow`] there will be performance penalty if used frequently.
180    #[inline]
181    pub fn set_code(&mut self, code: Bytecode) -> &mut Self {
182        self.code_hash = code.hash_slow();
183        self.code = Some(code);
184        self
185    }
186    /// Sets the bytecode and its hash.
187    ///
188    /// # Note
189    ///
190    /// It is on the caller's responsibility to ensure that the bytecode hash is correct.
191    pub fn set_code_and_hash(&mut self, code: Bytecode, code_hash: B256) {
192        self.code_hash = code_hash;
193        self.code = Some(code);
194    }
195    /// Returns a copy of this account with the [`Bytecode`] removed.
196    ///
197    /// This is useful when creating journals or snapshots of the state, where it is
198    /// desirable to store the code blobs elsewhere.
199    ///
200    /// ## Note
201    ///
202    /// This is distinct from [`without_code`][Self::without_code] in that it returns
203    /// a new [`AccountInfo`] instance with the code removed.
204    ///
205    /// [`without_code`][Self::without_code] will modify and return the same instance.
206    #[inline]
207    pub fn copy_without_code(&self) -> Self {
208        Self {
209            balance: self.balance,
210            nonce: self.nonce,
211            code_hash: self.code_hash,
212            account_id: self.account_id,
213            code: None,
214        }
215    }
216
217    /// Strips the [`Bytecode`] from this account and drop it.
218    ///
219    /// This is useful when creating journals or snapshots of the state, where it is
220    /// desirable to store the code blobs elsewhere.
221    ///
222    /// ## Note
223    ///
224    /// This is distinct from [`copy_without_code`][Self::copy_without_code] in that it
225    /// modifies the account in place.
226    ///
227    /// [`copy_without_code`][Self::copy_without_code]
228    /// will copy the non-code fields and return a new [`AccountInfo`] instance.
229    pub fn without_code(mut self) -> Self {
230        self.take_bytecode();
231        self
232    }
233
234    /// Returns if an account is empty.
235    ///
236    /// An account is empty if the following conditions are met.
237    /// - code hash is zero or set to the Keccak256 hash of the empty string `""`
238    /// - balance is zero
239    /// - nonce is zero
240    #[inline]
241    pub fn is_empty(&self) -> bool {
242        let code_empty = self.is_empty_code_hash() || self.code_hash.is_zero();
243        code_empty && self.balance.is_zero() && self.nonce == 0
244    }
245
246    /// Returns `true` if the account is not empty.
247    #[inline]
248    pub fn exists(&self) -> bool {
249        !self.is_empty()
250    }
251
252    /// Returns `true` if account has no nonce and code.
253    #[inline]
254    pub fn has_no_code_and_nonce(&self) -> bool {
255        self.is_empty_code_hash() && self.nonce == 0
256    }
257
258    /// Returns bytecode hash associated with this account.
259    ///
260    /// If account does not have code, it returns `KECCAK_EMPTY` hash.
261    #[inline]
262    pub fn code_hash(&self) -> B256 {
263        self.code_hash
264    }
265
266    /// Returns true if the code hash is the Keccak256 hash of the empty string `""`.
267    #[inline]
268    pub fn is_empty_code_hash(&self) -> bool {
269        self.code_hash == KECCAK_EMPTY
270    }
271
272    /// Takes bytecode from account.
273    ///
274    /// Code will be set to [None].
275    #[inline]
276    pub fn take_bytecode(&mut self) -> Option<Bytecode> {
277        self.code.take()
278    }
279
280    /// Initializes an [`AccountInfo`] with the given balance, setting all other fields to their
281    /// default values.
282    #[inline]
283    pub fn from_balance(balance: U256) -> Self {
284        AccountInfo {
285            balance,
286            ..Default::default()
287        }
288    }
289
290    /// Initializes an [`AccountInfo`] with the given bytecode, setting its balance to zero, its
291    /// nonce to `1`, and calculating the code hash from the given bytecode.
292    #[inline]
293    pub fn from_bytecode(bytecode: Bytecode) -> Self {
294        let hash = bytecode.hash_slow();
295
296        AccountInfo {
297            balance: U256::ZERO,
298            nonce: 1,
299            code: Some(bytecode),
300            code_hash: hash,
301            account_id: None,
302        }
303    }
304}
305
306#[cfg(test)]
307mod tests {
308    use crate::AccountInfo;
309    use bytecode::Bytecode;
310    use core::cmp::Ordering;
311    use std::collections::BTreeSet;
312
313    #[test]
314    fn test_account_info_trait_consistency() {
315        let bytecode = Bytecode::default();
316        let account1 = AccountInfo {
317            code: Some(bytecode),
318            ..AccountInfo::default()
319        };
320
321        let account2 = AccountInfo::default();
322
323        assert_eq!(account1, account2, "Accounts should be equal ignoring code");
324
325        assert_eq!(
326            account1.cmp(&account2),
327            Ordering::Equal,
328            "Ordering should be equal after ignoring code in Ord"
329        );
330
331        let mut set = BTreeSet::new();
332        assert!(set.insert(account1.clone()), "Inserted account1");
333        assert!(
334            !set.insert(account2.clone()),
335            "account2 not inserted (treated as duplicate)"
336        );
337
338        assert_eq!(set.len(), 1, "Set should have only one unique account");
339        assert!(set.contains(&account1), "Set contains account1");
340        assert!(
341            set.contains(&account2),
342            "Set contains account2 (since equal)"
343        );
344
345        let mut accounts = [account2, account1];
346        accounts.sort();
347        assert_eq!(accounts[0], accounts[1], "Sorted vec treats them as equal");
348    }
349}