1use std::collections::{hash_map::Entry::Vacant, HashMap};
2
3use alloy::primitives::{Address, U256};
4use revm::state::AccountInfo;
5use tracing::{debug, warn};
6
7#[derive(Clone, Default, Debug)]
16pub struct Account {
17 pub info: AccountInfo,
18 pub permanent_storage: HashMap<U256, U256>,
19 pub temp_storage: HashMap<U256, U256>,
20 pub mocked: bool,
21}
22
23#[derive(Default, Clone, PartialEq, Eq, Debug)]
24pub struct StateUpdate {
25 pub storage: Option<HashMap<U256, U256>>,
26 pub balance: Option<U256>,
27}
28#[derive(Clone, Default, Debug)]
29pub struct AccountStorage {
31 accounts: HashMap<Address, Account>,
32}
33
34impl AccountStorage {
35 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn clear(&mut self) {
41 self.accounts.clear();
42 }
43
44 pub fn init_account(
59 &mut self,
60 address: Address,
61 info: AccountInfo,
62 permanent_storage: Option<HashMap<U256, U256>>,
63 mocked: bool,
64 ) {
65 if let Vacant(e) = self.accounts.entry(address) {
66 e.insert(Account {
67 info,
68 permanent_storage: permanent_storage.unwrap_or_default(),
69 temp_storage: HashMap::new(),
70 mocked,
71 });
72 debug!(
73 "Inserted a {} account {:x?}",
74 if mocked { "mocked" } else { "non-mocked" },
75 address
76 );
77 }
78 }
79
80 pub fn update_account(&mut self, address: &Address, update: &StateUpdate) {
98 if let Some(account) = self.accounts.get_mut(address) {
99 if let Some(new_balance) = update.balance {
100 account.info.balance = new_balance;
101 }
102 if let Some(new_storage) = &update.storage {
103 for (index, value) in new_storage {
104 account
105 .permanent_storage
106 .insert(*index, *value);
107 }
108 }
109 } else {
110 warn!(?address, "Tried to update account {:x?} that was not initialized", address);
111 }
112 }
113
114 pub fn get_account_info(&self, address: &Address) -> Option<&AccountInfo> {
128 self.accounts
129 .get(address)
130 .map(|acc| &acc.info)
131 }
132
133 pub fn account_present(&self, address: &Address) -> bool {
144 self.accounts.contains_key(address)
145 }
146
147 pub fn set_temp_storage(&mut self, address: Address, index: U256, value: U256) {
159 if let Some(acc) = self.accounts.get_mut(&address) {
160 acc.temp_storage.insert(index, value);
161 } else {
162 warn!("Trying to set storage on unitialized account {:x?}.", address);
163 }
164 }
165
166 pub fn get_storage(&self, address: &Address, index: &U256) -> Option<U256> {
182 if let Some(acc) = self.accounts.get(address) {
183 if let Some(s) = acc.temp_storage.get(index) {
184 Some(*s)
185 } else {
186 acc.permanent_storage
187 .get(index)
188 .copied()
189 }
190 } else {
191 None
192 }
193 }
194
195 pub fn get_permanent_storage(&self, address: &Address, index: &U256) -> Option<U256> {
205 if let Some(acc) = self.accounts.get(address) {
206 acc.permanent_storage
207 .get(index)
208 .copied()
209 } else {
210 None
211 }
212 }
213
214 pub fn clear_temp_storage(&mut self) {
218 self.accounts
219 .values_mut()
220 .for_each(|acc| acc.temp_storage.clear());
221 }
222
223 pub fn is_mocked_account(&self, address: &Address) -> Option<bool> {
229 self.accounts
230 .get(address)
231 .map(|acc| acc.mocked)
232 }
233}
234
235#[cfg(test)]
236mod tests {
237 use std::{error::Error, str::FromStr};
238
239 use revm::primitives::KECCAK_EMPTY;
240
241 use super::*;
242 use crate::evm::account_storage::{Account, AccountStorage};
243
244 #[test]
245 fn test_insert_account() -> Result<(), Box<dyn Error>> {
246 let mut account_storage = AccountStorage::default();
247 let expected_nonce = 100;
248 let expected_balance = U256::from(500);
249 let acc_address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
250 let info: AccountInfo = AccountInfo {
251 nonce: expected_nonce,
252 balance: expected_balance,
253 code: None,
254 code_hash: KECCAK_EMPTY,
255 };
256 let mut storage_new = HashMap::new();
257 let expected_storage_value = U256::from_str("5").unwrap();
258 storage_new.insert(U256::from_str("1").unwrap(), expected_storage_value);
259
260 account_storage.init_account(acc_address, info, Some(storage_new), false);
261
262 let acc = account_storage
263 .get_account_info(&acc_address)
264 .unwrap();
265 let storage_value = account_storage
266 .get_storage(&acc_address, &U256::from_str("1").unwrap())
267 .unwrap();
268 assert_eq!(acc.nonce, expected_nonce, "Nonce should match expected value");
269 assert_eq!(acc.balance, expected_balance, "Balance should match expected value");
270 assert_eq!(acc.code_hash, KECCAK_EMPTY, "Code hash should match expected value");
271 assert_eq!(
272 storage_value, expected_storage_value,
273 "Storage value should match expected value"
274 );
275 Ok(())
276 }
277
278 #[test]
279 fn test_update_account_info() -> Result<(), Box<dyn Error>> {
280 let mut account_storage = AccountStorage::default();
281 let acc_address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc")?;
282 let info: AccountInfo = AccountInfo {
283 nonce: 100,
284 balance: U256::from(500),
285 code: None,
286 code_hash: KECCAK_EMPTY,
287 };
288 let mut original_storage = HashMap::new();
289 let storage_index = U256::from_str("1").unwrap();
290 original_storage.insert(storage_index, U256::from_str("5").unwrap());
291 account_storage.accounts.insert(
292 acc_address,
293 Account {
294 info,
295 permanent_storage: original_storage,
296 temp_storage: HashMap::new(),
297 mocked: false,
298 },
299 );
300 let updated_balance = U256::from(100);
301 let updated_storage_value = U256::from_str("999").unwrap();
302 let mut updated_storage = HashMap::new();
303 updated_storage.insert(storage_index, updated_storage_value);
304 let state_update =
305 StateUpdate { balance: Some(updated_balance), storage: Some(updated_storage) };
306
307 account_storage.update_account(&acc_address, &state_update);
308
309 assert_eq!(
310 account_storage
311 .get_account_info(&acc_address)
312 .unwrap()
313 .balance,
314 updated_balance,
315 "Account balance should be updated"
316 );
317 assert_eq!(
318 account_storage
319 .get_storage(&acc_address, &storage_index)
320 .unwrap(),
321 updated_storage_value,
322 "Storage value should be updated"
323 );
324 Ok(())
325 }
326
327 #[test]
328 fn test_get_account_info() {
329 let mut account_storage = AccountStorage::default();
330 let address_1 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
331 let address_2 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
332 let account_info_1 = AccountInfo::default();
333 let account_info_2 = AccountInfo { nonce: 500, ..Default::default() };
334 account_storage.init_account(address_1, account_info_1, None, false);
335 account_storage.init_account(address_2, account_info_2, None, false);
336
337 let existing_account = account_storage.get_account_info(&address_1);
338 let address_3 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9de").unwrap();
339 let non_existing_account = account_storage.get_account_info(&address_3);
340
341 assert_eq!(
342 existing_account.unwrap().nonce,
343 AccountInfo::default().nonce,
344 "Existing account's nonce should match the expected value"
345 );
346 assert_eq!(non_existing_account, None, "Non-existing account should return None");
347 }
348
349 #[test]
350 fn test_account_present() {
351 let mut account_storage = AccountStorage::default();
352 let existing_account =
353 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
354 let address_2 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
355 let non_existing_account =
356 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9de").unwrap();
357 account_storage
358 .accounts
359 .insert(existing_account, Account::default());
360 account_storage
361 .accounts
362 .insert(address_2, Account::default());
363
364 assert!(
365 account_storage.account_present(&existing_account),
366 "Existing account should be present in the AccountStorage"
367 );
368 assert!(
369 !account_storage.account_present(&non_existing_account),
370 "Non-existing account should not be present in the AccountStorage"
371 );
372 }
373
374 #[test]
375 fn test_set_get_storage() {
376 let mut account_storage = AccountStorage::default();
378 let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
380 let non_existing_address =
381 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
382 let account = Account::default();
383 account_storage
384 .accounts
385 .insert(address, account);
386 let index = U256::from_str("1").unwrap();
387 let value = U256::from_str("1").unwrap();
388 let non_existing_index = U256::from_str("2").unwrap();
389 let non_existing_value = U256::from_str("2").unwrap();
390 account_storage.set_temp_storage(
391 non_existing_address,
392 non_existing_index,
393 non_existing_value,
394 );
395 account_storage.set_temp_storage(address, index, value);
396
397 let storage = account_storage.get_storage(&address, &index);
398 let empty_storage = account_storage.get_storage(&non_existing_address, &non_existing_index);
399
400 assert_eq!(storage, Some(value), "Storage value should match the value that was set");
401 assert_eq!(empty_storage, None, "Storage value should be None for a non-existing account");
402 }
403
404 #[test]
405 fn test_get_storage() {
406 let mut account_storage = AccountStorage::default();
407 let existing_address =
408 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
409 let non_existent_address =
410 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
411 let index = U256::from(42);
412 let value = U256::from(100);
413 let non_existent_index = U256::from(999);
414 let mut account = Account::default();
415 account
416 .temp_storage
417 .insert(index, value);
418 account_storage
419 .accounts
420 .insert(existing_address, account);
421
422 assert_eq!(
423 account_storage.get_storage(&existing_address, &index),
424 Some(value), "If the storage features the address and index the value at that position should be retunred."
425 );
426
427 assert_eq!(
429 account_storage.get_storage(&non_existent_address, &index),
430 None,
431 "If the storage does not feature the address None should be returned."
432 );
433
434 assert_eq!(
436 account_storage.get_storage(&existing_address, &non_existent_index),
437 None,
438 "If the storage does not feature the index None should be returned."
439 );
440 }
441
442 #[test]
443 fn test_get_storage_priority() {
444 let mut account_storage = AccountStorage::default();
445 let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
446 let index = U256::from(69);
447 let temp_value = U256::from(100);
448 let permanent_value = U256::from(200);
449 let mut account = Account::default();
450 account
451 .temp_storage
452 .insert(index, temp_value);
453 account
454 .permanent_storage
455 .insert(index, permanent_value);
456 account_storage
457 .accounts
458 .insert(address, account);
459
460 assert_eq!(
461 account_storage.get_storage(&address, &index),
462 Some(temp_value),
463 "Temp storage value should take priority over permanent storage value"
464 );
465 }
466
467 #[test]
468 fn test_is_mocked_account() {
469 let mut account_storage = AccountStorage::default();
470 let mocked_account_address =
471 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
472 let not_mocked_account_address =
473 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
474 let unknown_address =
475 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9de").unwrap();
476 let mocked_account = Account { mocked: true, ..Default::default() };
477 let not_mocked_account = Account { mocked: false, ..Default::default() };
478 account_storage
479 .accounts
480 .insert(mocked_account_address, mocked_account);
481 account_storage
482 .accounts
483 .insert(not_mocked_account_address, not_mocked_account);
484
485 assert_eq!(account_storage.is_mocked_account(&mocked_account_address), Some(true));
486 assert_eq!(account_storage.is_mocked_account(¬_mocked_account_address), Some(false));
487 assert_eq!(account_storage.is_mocked_account(&unknown_address), None);
488 }
489
490 #[test]
491 fn test_clear_temp_storage() {
492 let mut account_storage = AccountStorage::default();
493 let address_1 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
494 let address_2 = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
495 let mut account_1 = Account::default();
496 account_1
497 .temp_storage
498 .insert(U256::from(1), U256::from(10));
499 let mut account_2 = Account::default();
500 account_2
501 .temp_storage
502 .insert(U256::from(2), U256::from(20));
503 account_storage
504 .accounts
505 .insert(address_1, account_1);
506 account_storage
507 .accounts
508 .insert(address_2, account_2);
509
510 account_storage.clear_temp_storage();
511
512 let account_1_temp_storage = account_storage.accounts[&address_1]
513 .temp_storage
514 .len();
515 let account_2_temp_storage = account_storage.accounts[&address_2]
516 .temp_storage
517 .len();
518 assert_eq!(account_1_temp_storage, 0, "Temporary storage of account 1 should be cleared");
519 assert_eq!(account_2_temp_storage, 0, "Temporary storage of account 2 should be cleared");
520 }
521
522 #[test]
523 fn test_get_permanent_storage() {
524 let mut account_storage = AccountStorage::default();
525 let address = Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc").unwrap();
526 let non_existing_address =
527 Address::from_str("0xb4e16d0168e52d35cacd2c6185b44281ec28c9dd").unwrap();
528 let index = U256::from_str("123").unwrap();
529 let value = U256::from_str("456").unwrap();
530 let mut account = Account::default();
531 account
532 .permanent_storage
533 .insert(index, value);
534 account_storage
535 .accounts
536 .insert(address, account);
537
538 let result = account_storage.get_permanent_storage(&address, &index);
539 let not_existing_result =
540 account_storage.get_permanent_storage(&non_existing_address, &index);
541 let empty_index = U256::from_str("789").unwrap();
542 let no_storage = account_storage.get_permanent_storage(&address, &empty_index);
543
544 assert_eq!(
545 result,
546 Some(value),
547 "Expected value for existing account with permanent storage"
548 );
549 assert_eq!(not_existing_result, None, "Expected None for non-existing account");
550 assert_eq!(
551 no_storage, None,
552 "Expected None for existing account without permanent storage"
553 );
554 }
555}