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        self.is_code_hash_empty_or_zero() && self.balance.is_zero() && self.nonce == 0
275    }
276
277    /// Optimization hint.
278    #[inline]
279    pub(crate) fn is_default(&self) -> bool {
280        self.is_empty() && self.code.as_ref().is_some_and(Bytecode::is_default)
281    }
282
283    /// Returns `true` if the account is not empty.
284    #[inline]
285    pub fn exists(&self) -> bool {
286        !self.is_empty()
287    }
288
289    /// Returns `true` if account has no nonce and code.
290    #[inline]
291    pub fn has_no_code_and_nonce(&self) -> bool {
292        self.is_empty_code_hash() && self.nonce == 0
293    }
294
295    /// Returns bytecode hash associated with this account.
296    ///
297    /// If account does not have code, it returns `KECCAK_EMPTY` hash.
298    #[inline]
299    pub const fn code_hash(&self) -> B256 {
300        self.code_hash
301    }
302
303    /// Returns true if the code hash is the Keccak256 hash of the empty string `""`.
304    #[inline]
305    pub fn is_empty_code_hash(&self) -> bool {
306        self.code_hash == KECCAK_EMPTY
307    }
308
309    /// Returns true if the code hash is the Keccak256 hash of the empty string `""` or is zero.
310    #[inline]
311    pub fn is_code_hash_empty_or_zero(&self) -> bool {
312        self.is_empty_code_hash() || self.code_hash.is_zero()
313    }
314
315    /// Takes bytecode from account.
316    ///
317    /// Code will be set to [None].
318    #[inline]
319    pub const fn take_bytecode(&mut self) -> Option<Bytecode> {
320        self.code.take()
321    }
322
323    /// Initializes an [`AccountInfo`] with the given balance, setting all other fields to their
324    /// default values.
325    #[inline]
326    pub fn from_balance(balance: U256) -> Self {
327        AccountInfo {
328            balance,
329            ..Default::default()
330        }
331    }
332
333    /// Initializes an [`AccountInfo`] with the given bytecode, setting its balance to zero, its
334    /// nonce to `1`, and calculating the code hash from the given bytecode.
335    #[inline]
336    pub fn from_bytecode(bytecode: Bytecode) -> Self {
337        let hash = bytecode.hash_slow();
338
339        AccountInfo {
340            balance: U256::ZERO,
341            nonce: 1,
342            code: Some(bytecode),
343            code_hash: hash,
344            account_id: None,
345        }
346    }
347}
348
349#[cfg(test)]
350mod tests {
351    use super::*;
352    use std::collections::BTreeSet;
353
354    #[test]
355    fn test_account_info_trait_consistency() {
356        let bytecode = Bytecode::default();
357        let account1 = AccountInfo {
358            code: Some(bytecode),
359            ..AccountInfo::default()
360        };
361
362        let account2 = AccountInfo::default();
363
364        assert_eq!(account1, account2, "Accounts should be equal ignoring code");
365
366        assert_eq!(
367            account1.cmp(&account2),
368            Ordering::Equal,
369            "Ordering should be equal after ignoring code in Ord"
370        );
371
372        #[expect(clippy::mutable_key_type)] // Not observable
373        let mut set = BTreeSet::new();
374        assert!(set.insert(account1.clone()), "Inserted account1");
375        assert!(
376            !set.insert(account2.clone()),
377            "account2 not inserted (treated as duplicate)"
378        );
379
380        assert_eq!(set.len(), 1, "Set should have only one unique account");
381        assert!(set.contains(&account1), "Set contains account1");
382        assert!(
383            set.contains(&account2),
384            "Set contains account2 (since equal)"
385        );
386
387        let mut accounts = [account2, account1];
388        accounts.sort();
389        assert_eq!(accounts[0], accounts[1], "Sorted vec treats them as equal");
390    }
391
392    #[test]
393    fn is_default() {
394        assert!(AccountInfo::default().is_default())
395    }
396
397    #[test]
398    #[cfg(feature = "serde")]
399    fn is_default_after_serde() {
400        let info = AccountInfo::default();
401        let json = serde_json::to_string(&info).unwrap();
402        let deser: AccountInfo = serde_json::from_str(&json).unwrap();
403        assert!(deser.is_default());
404    }
405}