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}