1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5#[cfg(not(feature = "std"))]
6extern crate alloc as std;
7
8mod account_info;
9pub mod bal;
10mod types;
11
12pub use bytecode;
13
14pub use account_info::AccountInfo;
15pub use bytecode::Bytecode;
16pub use primitives;
17pub use types::{EvmState, EvmStorage, TransientStorage};
18
19use bitflags::bitflags;
20use primitives::{hardfork::SpecId, HashMap, OnceLock, StorageKey, StorageValue, U256};
21use std::boxed::Box;
22
23#[derive(Debug, Clone, PartialEq, Eq, Default)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize))]
37pub struct Account {
38 pub info: AccountInfo,
40 pub original_info: Box<AccountInfo>,
42 pub transaction_id: usize,
44 pub storage: EvmStorage,
46 pub status: AccountStatus,
48}
49
50impl Account {
51 pub fn new_not_existing(transaction_id: usize) -> Self {
53 static DEFAULT: OnceLock<Account> = OnceLock::new();
54 DEFAULT
55 .get_or_init(|| Self {
56 info: AccountInfo::default(),
57 storage: HashMap::default(),
58 transaction_id,
59 status: AccountStatus::LoadedAsNotExisting,
60 original_info: Box::new(AccountInfo::default()),
61 })
62 .clone()
63 }
64
65 #[inline]
71 pub fn caller_initial_modification(&mut self, new_balance: U256, is_call: bool) -> U256 {
72 self.mark_touch();
74
75 if is_call {
76 self.info.nonce = self.info.nonce.saturating_add(1);
78 }
79
80 core::mem::replace(&mut self.info.balance, new_balance)
81 }
82
83 #[inline]
85 pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
86 if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
87 self.is_empty()
88 } else {
89 self.is_loaded_as_not_existing_not_touched()
90 }
91 }
92
93 #[inline]
95 pub fn mark_selfdestruct(&mut self) {
96 self.status |= AccountStatus::SelfDestructed;
97 }
98
99 #[inline]
101 pub fn unmark_selfdestruct(&mut self) {
102 self.status -= AccountStatus::SelfDestructed;
103 }
104
105 #[inline]
107 pub fn is_selfdestructed(&self) -> bool {
108 self.status.contains(AccountStatus::SelfDestructed)
109 }
110
111 #[inline]
113 pub fn mark_touch(&mut self) {
114 self.status |= AccountStatus::Touched;
115 }
116
117 #[inline]
119 pub fn unmark_touch(&mut self) {
120 self.status -= AccountStatus::Touched;
121 }
122
123 #[inline]
125 pub fn is_touched(&self) -> bool {
126 self.status.contains(AccountStatus::Touched)
127 }
128
129 #[inline]
131 pub fn mark_created(&mut self) {
132 self.status |= AccountStatus::Created;
133 }
134
135 #[inline]
137 pub fn unmark_created(&mut self) {
138 self.status -= AccountStatus::Created;
139 }
140
141 #[inline]
143 pub fn mark_cold(&mut self) {
144 self.status |= AccountStatus::Cold;
145 }
146
147 #[inline]
149 pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool {
150 self.transaction_id != transaction_id || self.status.contains(AccountStatus::Cold)
151 }
152
153 #[inline]
155 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool {
156 let is_cold = self.is_cold_transaction_id(transaction_id);
157 self.status -= AccountStatus::Cold;
158 self.transaction_id = transaction_id;
159 is_cold
160 }
161
162 #[inline]
164 pub fn is_created_locally(&self) -> bool {
165 self.status.contains(AccountStatus::CreatedLocal)
166 }
167
168 #[inline]
170 pub fn is_selfdestructed_locally(&self) -> bool {
171 self.status.contains(AccountStatus::SelfDestructedLocal)
172 }
173
174 #[inline]
176 pub fn selfdestruct(&mut self) {
177 self.storage.clear();
178 self.info = AccountInfo::default();
179 }
180
181 #[inline]
185 pub fn mark_created_locally(&mut self) -> bool {
186 self.mark_local_and_global(AccountStatus::CreatedLocal, AccountStatus::Created)
187 }
188
189 #[inline]
191 pub fn unmark_created_locally(&mut self) {
192 self.status -= AccountStatus::CreatedLocal;
193 }
194
195 #[inline]
197 pub fn mark_selfdestructed_locally(&mut self) -> bool {
198 self.mark_local_and_global(
199 AccountStatus::SelfDestructedLocal,
200 AccountStatus::SelfDestructed,
201 )
202 }
203
204 #[inline]
205 fn mark_local_and_global(
206 &mut self,
207 local_flag: AccountStatus,
208 global_flag: AccountStatus,
209 ) -> bool {
210 self.status |= local_flag;
211 let is_global_first_time = !self.status.contains(global_flag);
212 self.status |= global_flag;
213 is_global_first_time
214 }
215
216 #[inline]
218 pub fn unmark_selfdestructed_locally(&mut self) {
219 self.status -= AccountStatus::SelfDestructedLocal;
220 }
221
222 pub fn is_loaded_as_not_existing(&self) -> bool {
227 self.status.contains(AccountStatus::LoadedAsNotExisting)
228 }
229
230 pub fn is_loaded_as_not_existing_not_touched(&self) -> bool {
232 self.is_loaded_as_not_existing() && !self.is_touched()
233 }
234
235 pub fn is_created(&self) -> bool {
237 self.status.contains(AccountStatus::Created)
238 }
239
240 pub fn is_empty(&self) -> bool {
242 self.info.is_empty()
243 }
244
245 pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&StorageKey, &EvmStorageSlot)> {
249 self.storage.iter().filter(|(_, slot)| slot.is_changed())
250 }
251
252 pub fn with_info(mut self, info: AccountInfo) -> Self {
254 self.info = info;
255 self
256 }
257
258 pub fn with_storage<I>(mut self, storage_iter: I) -> Self
260 where
261 I: Iterator<Item = (StorageKey, EvmStorageSlot)>,
262 {
263 for (key, slot) in storage_iter {
264 self.storage.insert(key, slot);
265 }
266 self
267 }
268
269 pub fn with_selfdestruct_mark(mut self) -> Self {
271 self.mark_selfdestruct();
272 self
273 }
274
275 pub fn with_touched_mark(mut self) -> Self {
277 self.mark_touch();
278 self
279 }
280
281 pub fn with_created_mark(mut self) -> Self {
283 self.mark_created();
284 self
285 }
286
287 pub fn with_cold_mark(mut self) -> Self {
289 self.mark_cold();
290 self
291 }
292
293 pub fn with_warm_mark(mut self, transaction_id: usize) -> (Self, bool) {
296 let was_cold = self.mark_warm_with_transaction_id(transaction_id);
297 (self, was_cold)
298 }
299
300 pub fn with_warm(mut self, transaction_id: usize) -> Self {
302 self.mark_warm_with_transaction_id(transaction_id);
303 self
304 }
305}
306
307impl From<AccountInfo> for Account {
308 fn from(info: AccountInfo) -> Self {
309 let original_info = Box::new(info.clone());
310 Self {
311 info,
312 storage: HashMap::default(),
313 transaction_id: 0,
314 status: AccountStatus::empty(),
315 original_info,
316 }
317 }
318}
319
320#[cfg(feature = "serde")]
321mod serde_impl {
322 use super::*;
323 use serde::{Deserialize, Serialize};
324
325 #[derive(Serialize, Deserialize)]
326 struct AccountSerde {
327 info: AccountInfo,
328 original_info: Option<AccountInfo>,
329 storage: HashMap<StorageKey, EvmStorageSlot>,
330 transaction_id: usize,
331 status: AccountStatus,
332 }
333
334 impl<'de> Deserialize<'de> for super::Account {
335 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
336 where
337 D: serde::Deserializer<'de>,
338 {
339 let AccountSerde {
340 info,
341 original_info,
342 storage,
343 transaction_id,
344 status,
345 } = Deserialize::deserialize(deserializer)?;
346
347 let original_info = original_info.unwrap_or_else(|| info.clone());
349
350 Ok(Account {
351 info,
352 original_info: Box::new(original_info),
353 storage,
354 transaction_id,
355 status,
356 })
357 }
358 }
359}
360
361bitflags! {
363 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
395 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
396 #[cfg_attr(feature = "serde", serde(transparent))]
397 pub struct AccountStatus: u8 {
398 const Created = 0b00000001;
401 const CreatedLocal = 0b10000000;
403 const SelfDestructed = 0b00000010;
405 const SelfDestructedLocal = 0b01000000;
407 const Touched = 0b00000100;
411 const LoadedAsNotExisting = 0b00001000;
414 const Cold = 0b00010000;
417 }
418}
419
420impl AccountStatus {
421 #[inline]
423 pub fn is_touched(&self) -> bool {
424 self.contains(AccountStatus::Touched)
425 }
426}
427
428impl Default for AccountStatus {
429 fn default() -> Self {
430 AccountStatus::empty()
431 }
432}
433
434#[derive(Debug, Clone, Default, PartialEq, Eq)]
436#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
437pub struct EvmStorageSlot {
438 pub original_value: StorageValue,
440 pub present_value: StorageValue,
442 pub transaction_id: usize,
444 pub is_cold: bool,
446}
447
448impl EvmStorageSlot {
449 pub fn new(original: StorageValue, transaction_id: usize) -> Self {
451 Self {
452 original_value: original,
453 present_value: original,
454 transaction_id,
455 is_cold: false,
456 }
457 }
458
459 pub fn new_changed(
461 original_value: StorageValue,
462 present_value: StorageValue,
463 transaction_id: usize,
464 ) -> Self {
465 Self {
466 original_value,
467 present_value,
468 transaction_id,
469 is_cold: false,
470 }
471 }
472 pub fn is_changed(&self) -> bool {
474 self.original_value != self.present_value
475 }
476
477 #[inline]
479 pub fn original_value(&self) -> StorageValue {
480 self.original_value
481 }
482
483 #[inline]
485 pub fn present_value(&self) -> StorageValue {
486 self.present_value
487 }
488
489 #[inline]
491 pub fn mark_cold(&mut self) {
492 self.is_cold = true;
493 }
494
495 #[inline]
497 pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool {
498 self.transaction_id != transaction_id || self.is_cold
499 }
500
501 #[inline]
506 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool {
507 let is_cold = self.is_cold_transaction_id(transaction_id);
508 if is_cold {
509 self.original_value = self.present_value;
511 }
512 self.transaction_id = transaction_id;
513 self.is_cold = false;
514 is_cold
515 }
516}
517
518#[cfg(test)]
519mod tests {
520 use super::*;
521 use crate::EvmStorageSlot;
522 use primitives::{StorageKey, KECCAK_EMPTY, U256};
523
524 #[test]
525 fn account_is_empty_balance() {
526 let mut account = Account::default();
527 assert!(account.is_empty());
528
529 account.info.balance = U256::from(1);
530 assert!(!account.is_empty());
531
532 account.info.balance = U256::ZERO;
533 assert!(account.is_empty());
534 }
535
536 #[test]
537 fn account_is_empty_nonce() {
538 let mut account = Account::default();
539 assert!(account.is_empty());
540
541 account.info.nonce = 1;
542 assert!(!account.is_empty());
543
544 account.info.nonce = 0;
545 assert!(account.is_empty());
546 }
547
548 #[test]
549 fn account_is_empty_code_hash() {
550 let mut account = Account::default();
551 assert!(account.is_empty());
552
553 account.info.code_hash = [1; 32].into();
554 assert!(!account.is_empty());
555
556 account.info.code_hash = [0; 32].into();
557 assert!(account.is_empty());
558
559 account.info.code_hash = KECCAK_EMPTY;
560 assert!(account.is_empty());
561 }
562
563 #[test]
564 fn account_state() {
565 let mut account = Account::default();
566
567 assert!(!account.is_touched());
568 assert!(!account.is_selfdestructed());
569
570 account.mark_touch();
571 assert!(account.is_touched());
572 assert!(!account.is_selfdestructed());
573
574 account.mark_selfdestruct();
575 assert!(account.is_touched());
576 assert!(account.is_selfdestructed());
577
578 account.unmark_selfdestruct();
579 assert!(account.is_touched());
580 assert!(!account.is_selfdestructed());
581 }
582
583 #[test]
584 fn account_is_cold() {
585 let mut account = Account::default();
586
587 assert!(!account.status.contains(crate::AccountStatus::Cold));
589
590 assert!(!account.mark_warm_with_transaction_id(0));
592
593 account.mark_cold();
595
596 assert!(account.status.contains(crate::AccountStatus::Cold));
598
599 assert!(account.mark_warm_with_transaction_id(0));
601 }
602
603 #[test]
604 fn test_account_with_info() {
605 let info = AccountInfo::default();
606 let account = Account::default().with_info(info.clone());
607
608 assert_eq!(account.info, info);
609 assert_eq!(account.storage, HashMap::default());
610 assert_eq!(account.status, AccountStatus::empty());
611 }
612
613 #[test]
614 fn test_account_with_storage() {
615 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
616 let key1 = StorageKey::from(1);
617 let key2 = StorageKey::from(2);
618 let slot1 = EvmStorageSlot::new(StorageValue::from(10), 0);
619 let slot2 = EvmStorageSlot::new(StorageValue::from(20), 0);
620
621 storage.insert(key1, slot1.clone());
622 storage.insert(key2, slot2.clone());
623
624 let account = Account::default().with_storage(storage.clone().into_iter());
625
626 assert_eq!(account.storage.len(), 2);
627 assert_eq!(account.storage.get(&key1), Some(&slot1));
628 assert_eq!(account.storage.get(&key2), Some(&slot2));
629 }
630
631 #[test]
632 fn test_account_with_selfdestruct_mark() {
633 let account = Account::default().with_selfdestruct_mark();
634
635 assert!(account.is_selfdestructed());
636 assert!(!account.is_touched());
637 assert!(!account.is_created());
638 }
639
640 #[test]
641 #[cfg(feature = "serde")]
642 fn test_account_serialize_deserialize() {
643 let account = Account::default().with_selfdestruct_mark();
644 let serialized = serde_json::to_string(&account).unwrap();
645 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
646 assert_eq!(account, deserialized);
647 }
648
649 #[test]
650 #[cfg(feature = "serde")]
651 fn test_account_serialize_deserialize_without_original_info() {
652 let deserialize_without_original_info = r#"
653 {"info":{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","storage_id":null,"code":{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}},"transaction_id":0,"storage":{},"status":"SelfDestructed"}"#;
654
655 let account = Account::default().with_selfdestruct_mark();
656 let deserialized: Account =
657 serde_json::from_str(deserialize_without_original_info).unwrap();
658 assert_eq!(account, deserialized);
659 }
660
661 #[test]
662 fn test_account_with_touched_mark() {
663 let account = Account::default().with_touched_mark();
664
665 assert!(!account.is_selfdestructed());
666 assert!(account.is_touched());
667 assert!(!account.is_created());
668 }
669
670 #[test]
671 fn test_account_with_created_mark() {
672 let account = Account::default().with_created_mark();
673
674 assert!(!account.is_selfdestructed());
675 assert!(!account.is_touched());
676 assert!(account.is_created());
677 }
678
679 #[test]
680 fn test_account_with_cold_mark() {
681 let account = Account::default().with_cold_mark();
682
683 assert!(account.status.contains(AccountStatus::Cold));
684 }
685
686 #[test]
687 fn test_storage_mark_warm_with_transaction_id() {
688 let mut slot = EvmStorageSlot::new(U256::ZERO, 0);
689 slot.is_cold = true;
690 slot.transaction_id = 0;
691 assert!(slot.mark_warm_with_transaction_id(1));
692
693 slot.is_cold = false;
694 slot.transaction_id = 0;
695 assert!(slot.mark_warm_with_transaction_id(1));
696
697 slot.is_cold = true;
698 slot.transaction_id = 1;
699 assert!(slot.mark_warm_with_transaction_id(1));
700
701 slot.is_cold = false;
702 slot.transaction_id = 1;
703 assert!(!slot.mark_warm_with_transaction_id(1));
705 }
706
707 #[test]
708 fn test_account_with_warm_mark() {
709 let cold_account = Account::default().with_cold_mark();
711 assert!(cold_account.status.contains(AccountStatus::Cold));
712
713 let (warm_account, was_cold) = cold_account.with_warm_mark(0);
715
716 assert!(!warm_account.status.contains(AccountStatus::Cold));
718 assert!(was_cold);
719
720 let (still_warm_account, was_cold) = warm_account.with_warm_mark(0);
722 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
723 assert!(!was_cold);
724 }
725
726 #[test]
727 fn test_account_with_warm() {
728 let cold_account = Account::default().with_cold_mark();
730 assert!(cold_account.status.contains(AccountStatus::Cold));
731
732 let warm_account = cold_account.with_warm(0);
734
735 assert!(!warm_account.status.contains(AccountStatus::Cold));
737 }
738
739 #[test]
740 fn test_account_builder_chaining() {
741 let info = AccountInfo {
742 nonce: 5,
743 ..AccountInfo::default()
744 };
745
746 let slot_key = StorageKey::from(42);
747 let slot_value = EvmStorageSlot::new(StorageValue::from(123), 0);
748 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
749 storage.insert(slot_key, slot_value.clone());
750
751 let account = Account::default()
753 .with_info(info.clone())
754 .with_storage(storage.into_iter())
755 .with_created_mark()
756 .with_touched_mark()
757 .with_cold_mark()
758 .with_warm(0);
759
760 assert_eq!(account.info, info);
762 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
763 assert!(account.is_created());
764 assert!(account.is_touched());
765 assert!(!account.status.contains(AccountStatus::Cold));
766 }
767
768 #[test]
769 fn test_account_is_cold_transaction_id() {
770 let mut account = Account::default();
771 assert!(!account.is_cold_transaction_id(0));
773
774 assert!(account.is_cold_transaction_id(1));
776 account.mark_cold();
777 assert!(account.is_cold_transaction_id(0));
778 assert!(account.is_cold_transaction_id(1));
779 }
780}