Skip to main content

revm_state/
account_info.rs

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