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, returning the previous code and hash.
218    ///
219    /// # Note
220    ///
221    /// It is on the caller's responsibility to ensure that the bytecode hash is correct.
222    pub const fn set_code_and_hash(
223        &mut self,
224        code: Bytecode,
225        code_hash: B256,
226    ) -> (B256, Option<Bytecode>) {
227        let previous_hash = core::mem::replace(&mut self.code_hash, code_hash);
228        let previous_code = self.code.replace(code);
229        (previous_hash, previous_code)
230    }
231    /// Returns a copy of this account with the [`Bytecode`] removed.
232    ///
233    /// This is useful when creating journals or snapshots of the state, where it is
234    /// desirable to store the code blobs elsewhere.
235    ///
236    /// ## Note
237    ///
238    /// This is distinct from [`without_code`][Self::without_code] in that it returns
239    /// a new [`AccountInfo`] instance with the code removed.
240    ///
241    /// [`without_code`][Self::without_code] will modify and return the same instance.
242    #[inline]
243    pub const fn copy_without_code(&self) -> Self {
244        Self {
245            balance: self.balance,
246            nonce: self.nonce,
247            code_hash: self.code_hash,
248            account_id: self.account_id,
249            code: None,
250        }
251    }
252
253    /// Strips the [`Bytecode`] from this account and drop it.
254    ///
255    /// This is useful when creating journals or snapshots of the state, where it is
256    /// desirable to store the code blobs elsewhere.
257    ///
258    /// ## Note
259    ///
260    /// This is distinct from [`copy_without_code`][Self::copy_without_code] in that it
261    /// modifies the account in place.
262    ///
263    /// [`copy_without_code`][Self::copy_without_code]
264    /// will copy the non-code fields and return a new [`AccountInfo`] instance.
265    #[inline]
266    pub fn without_code(mut self) -> Self {
267        self.take_bytecode();
268        self
269    }
270
271    /// Returns if an account is empty.
272    ///
273    /// An account is empty if the following conditions are met.
274    /// - code hash is zero or set to the Keccak256 hash of the empty string `""`
275    /// - balance is zero
276    /// - nonce is zero
277    #[inline]
278    pub fn is_empty(&self) -> bool {
279        self.is_code_hash_empty_or_zero() && self.balance.is_zero() && self.nonce == 0
280    }
281
282    /// Optimization hint.
283    #[inline]
284    pub(crate) fn is_default(&self) -> bool {
285        self.is_empty() && self.code.as_ref().is_some_and(Bytecode::is_default)
286    }
287
288    /// Returns `true` if the account is not empty.
289    #[inline]
290    pub fn exists(&self) -> bool {
291        !self.is_empty()
292    }
293
294    /// Returns `true` if account has no nonce and code.
295    #[inline]
296    pub fn has_no_code_and_nonce(&self) -> bool {
297        self.is_empty_code_hash() && self.nonce == 0
298    }
299
300    /// Returns bytecode hash associated with this account.
301    ///
302    /// If account does not have code, it returns `KECCAK_EMPTY` hash.
303    #[inline]
304    pub const fn code_hash(&self) -> B256 {
305        self.code_hash
306    }
307
308    /// Returns true if the code hash is the Keccak256 hash of the empty string `""`.
309    #[inline]
310    pub fn is_empty_code_hash(&self) -> bool {
311        self.code_hash == KECCAK_EMPTY
312    }
313
314    /// Returns true if the code hash is the Keccak256 hash of the empty string `""` or is zero.
315    #[inline]
316    pub fn is_code_hash_empty_or_zero(&self) -> bool {
317        self.is_empty_code_hash() || self.code_hash.is_zero()
318    }
319
320    /// Takes bytecode from account.
321    ///
322    /// Code will be set to [None].
323    #[inline]
324    pub const fn take_bytecode(&mut self) -> Option<Bytecode> {
325        self.code.take()
326    }
327
328    /// Initializes an [`AccountInfo`] with the given balance, setting all other fields to their
329    /// default values.
330    #[inline]
331    pub fn from_balance(balance: U256) -> Self {
332        AccountInfo {
333            balance,
334            ..Default::default()
335        }
336    }
337
338    /// Initializes an [`AccountInfo`] with the given bytecode, setting its balance to zero, its
339    /// nonce to `1`, and calculating the code hash from the given bytecode.
340    #[inline]
341    pub fn from_bytecode(bytecode: Bytecode) -> Self {
342        let hash = bytecode.hash_slow();
343
344        AccountInfo {
345            balance: U256::ZERO,
346            nonce: 1,
347            code: Some(bytecode),
348            code_hash: hash,
349            account_id: None,
350        }
351    }
352}
353
354#[cfg(test)]
355mod tests {
356    use super::*;
357    use std::collections::BTreeSet;
358
359    #[test]
360    fn test_account_info_trait_consistency() {
361        let bytecode = Bytecode::default();
362        let account1 = AccountInfo {
363            code: Some(bytecode),
364            ..AccountInfo::default()
365        };
366
367        let account2 = AccountInfo::default();
368
369        assert_eq!(account1, account2, "Accounts should be equal ignoring code");
370
371        assert_eq!(
372            account1.cmp(&account2),
373            Ordering::Equal,
374            "Ordering should be equal after ignoring code in Ord"
375        );
376
377        #[expect(clippy::mutable_key_type)] // Not observable
378        let mut set = BTreeSet::new();
379        assert!(set.insert(account1.clone()), "Inserted account1");
380        assert!(
381            !set.insert(account2.clone()),
382            "account2 not inserted (treated as duplicate)"
383        );
384
385        assert_eq!(set.len(), 1, "Set should have only one unique account");
386        assert!(set.contains(&account1), "Set contains account1");
387        assert!(
388            set.contains(&account2),
389            "Set contains account2 (since equal)"
390        );
391
392        let mut accounts = [account2, account1];
393        accounts.sort();
394        assert_eq!(accounts[0], accounts[1], "Sorted vec treats them as equal");
395    }
396
397    #[test]
398    fn is_default() {
399        assert!(AccountInfo::default().is_default())
400    }
401
402    #[test]
403    #[cfg(feature = "serde")]
404    fn is_default_after_serde() {
405        let info = AccountInfo::default();
406        let json = serde_json::to_string(&info).unwrap();
407        let deser: AccountInfo = serde_json::from_str(&json).unwrap();
408        assert!(deser.is_default());
409    }
410}