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::{AccountId, AccountInfo};
15pub use bytecode::Bytecode;
16pub use primitives;
17pub use types::{EvmState, EvmStorage, TransientStorage};
18
19use bitflags::bitflags;
20use nonmax::NonMaxU32;
21use primitives::{hardfork::SpecId, HashMap, StorageKey, StorageValue, U256};
22use std::boxed::Box;
23
24#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[cfg_attr(feature = "serde", serde(transparent))]
30pub struct TransactionId(NonMaxU32);
31
32impl TransactionId {
33 pub const ZERO: Self = Self(NonMaxU32::ZERO);
35
36 #[inline]
40 pub fn new(id: usize) -> Option<Self> {
41 let id = u32::try_from(id).ok()?;
42 NonMaxU32::new(id).map(Self)
43 }
44
45 #[inline]
47 pub const fn get(self) -> usize {
48 self.0.get() as usize
49 }
50
51 #[inline]
57 pub const fn increment(&mut self) {
58 self.0 = match NonMaxU32::new(self.0.get() + 1) {
59 Some(id) => id,
60 None => panic!("transaction id overflow"),
61 };
62 }
63}
64
65#[derive(Debug, Clone, Eq, Default)]
78#[cfg_attr(feature = "serde", derive(serde::Serialize))]
79pub struct Account {
80 pub info: AccountInfo,
82 pub transaction_id: TransactionId,
84 pub storage: EvmStorage,
86 pub status: AccountStatus,
88
89 original_info: Option<Box<AccountInfo>>,
92}
93
94impl PartialEq for Account {
95 #[inline]
96 fn eq(&self, other: &Self) -> bool {
97 self.info == other.info
98 && self.transaction_id == other.transaction_id
99 && self.storage == other.storage
100 && self.status == other.status
101 && self.original_info() == other.original_info()
102 }
103}
104
105impl Account {
106 #[inline]
108 pub fn new_not_existing(transaction_id: TransactionId) -> Self {
109 Self {
110 transaction_id,
111 status: AccountStatus::LoadedAsNotExisting,
112 ..Default::default()
113 }
114 }
115
116 #[inline]
122 pub fn caller_initial_modification(&mut self, new_balance: U256, is_call: bool) -> U256 {
123 self.mark_touch();
125
126 if is_call {
127 self.info.nonce = self.info.nonce.saturating_add(1);
129 }
130
131 core::mem::replace(&mut self.info.balance, new_balance)
132 }
133
134 #[inline]
136 pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
137 if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
138 self.is_empty()
139 } else {
140 self.is_loaded_as_not_existing_not_touched()
141 }
142 }
143
144 #[inline]
146 pub fn original_info(&self) -> AccountInfo {
147 self.original_info.as_deref().cloned().unwrap_or_default()
148 }
149
150 #[inline]
152 pub fn original_info_mut(&mut self) -> &mut AccountInfo {
153 self.original_info.get_or_insert_default()
154 }
155
156 pub fn set_current_info_as_original(&mut self) {
158 if self.original_info.is_none() && self.info.is_default() {
159 return;
160 }
161 self.original_info
162 .get_or_insert_default()
163 .as_mut()
164 .clone_from(&self.info);
165 }
166
167 #[inline]
169 pub fn mark_selfdestruct(&mut self) {
170 self.status |= AccountStatus::SelfDestructed;
171 }
172
173 #[inline]
175 pub fn unmark_selfdestruct(&mut self) {
176 self.status -= AccountStatus::SelfDestructed;
177 }
178
179 #[inline]
181 pub const fn is_selfdestructed(&self) -> bool {
182 self.status.contains(AccountStatus::SelfDestructed)
183 }
184
185 #[inline]
187 pub fn mark_touch(&mut self) {
188 self.status |= AccountStatus::Touched;
189 }
190
191 #[inline]
193 pub fn unmark_touch(&mut self) {
194 self.status -= AccountStatus::Touched;
195 }
196
197 #[inline]
199 pub const fn is_touched(&self) -> bool {
200 self.status.contains(AccountStatus::Touched)
201 }
202
203 #[inline]
205 pub fn mark_created(&mut self) {
206 self.status |= AccountStatus::Created;
207 }
208
209 #[inline]
211 pub fn unmark_created(&mut self) {
212 self.status -= AccountStatus::Created;
213 }
214
215 #[inline]
217 pub fn mark_cold(&mut self) {
218 self.status |= AccountStatus::Cold;
219 }
220
221 #[inline]
223 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
224 self.transaction_id.get() != transaction_id.get()
225 || self.status.contains(AccountStatus::Cold)
226 }
227
228 #[inline]
230 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
231 let is_cold = self.is_cold_transaction_id(transaction_id);
232 self.status -= AccountStatus::Cold;
233 self.transaction_id = transaction_id;
234 is_cold
235 }
236
237 #[inline]
239 pub const fn is_created_locally(&self) -> bool {
240 self.status.contains(AccountStatus::CreatedLocal)
241 }
242
243 #[inline]
245 pub const fn is_selfdestructed_locally(&self) -> bool {
246 self.status.contains(AccountStatus::SelfDestructedLocal)
247 }
248
249 #[inline]
251 pub fn selfdestruct(&mut self) {
252 self.storage.clear();
253 self.info = AccountInfo::default();
254 }
255
256 #[inline]
260 pub fn mark_created_locally(&mut self) -> bool {
261 self.mark_local_and_global(AccountStatus::CreatedLocal, AccountStatus::Created)
262 }
263
264 #[inline]
266 pub fn unmark_created_locally(&mut self) {
267 self.status -= AccountStatus::CreatedLocal;
268 }
269
270 #[inline]
272 pub fn mark_selfdestructed_locally(&mut self) -> bool {
273 self.mark_local_and_global(
274 AccountStatus::SelfDestructedLocal,
275 AccountStatus::SelfDestructed,
276 )
277 }
278
279 #[inline]
280 fn mark_local_and_global(
281 &mut self,
282 local_flag: AccountStatus,
283 global_flag: AccountStatus,
284 ) -> bool {
285 self.status |= local_flag;
286 let is_global_first_time = !self.status.contains(global_flag);
287 self.status |= global_flag;
288 is_global_first_time
289 }
290
291 #[inline]
293 pub fn unmark_selfdestructed_locally(&mut self) {
294 self.status -= AccountStatus::SelfDestructedLocal;
295 }
296
297 pub const fn is_loaded_as_not_existing(&self) -> bool {
302 self.status.contains(AccountStatus::LoadedAsNotExisting)
303 }
304
305 pub const fn is_loaded_as_not_existing_not_touched(&self) -> bool {
307 self.is_loaded_as_not_existing() && !self.is_touched()
308 }
309
310 pub const fn is_created(&self) -> bool {
312 self.status.contains(AccountStatus::Created)
313 }
314
315 pub fn is_empty(&self) -> bool {
317 self.info.is_empty()
318 }
319
320 pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&StorageKey, &EvmStorageSlot)> {
324 self.storage.iter().filter(|(_, slot)| slot.is_changed())
325 }
326
327 pub fn with_info(mut self, info: AccountInfo) -> Self {
329 self.info = info;
330 self
331 }
332
333 pub fn with_storage<I>(mut self, storage_iter: I) -> Self
335 where
336 I: Iterator<Item = (StorageKey, EvmStorageSlot)>,
337 {
338 for (key, slot) in storage_iter {
339 self.storage.insert(key, slot);
340 }
341 self
342 }
343
344 pub fn with_selfdestruct_mark(mut self) -> Self {
346 self.mark_selfdestruct();
347 self
348 }
349
350 pub fn with_touched_mark(mut self) -> Self {
352 self.mark_touch();
353 self
354 }
355
356 pub fn with_created_mark(mut self) -> Self {
358 self.mark_created();
359 self
360 }
361
362 pub fn with_cold_mark(mut self) -> Self {
364 self.mark_cold();
365 self
366 }
367
368 pub fn with_warm_mark(mut self, transaction_id: TransactionId) -> (Self, bool) {
371 let was_cold = self.mark_warm_with_transaction_id(transaction_id);
372 (self, was_cold)
373 }
374
375 pub fn with_warm(mut self, transaction_id: TransactionId) -> Self {
377 self.mark_warm_with_transaction_id(transaction_id);
378 self
379 }
380}
381
382impl From<AccountInfo> for Account {
383 fn from(info: AccountInfo) -> Self {
384 let original_info = if info.is_default() {
385 None
386 } else {
387 Some(Box::new(info.clone()))
388 };
389 Self {
390 info,
391 original_info,
392 transaction_id: TransactionId::ZERO,
393 storage: HashMap::default(),
394 status: AccountStatus::empty(),
395 }
396 }
397}
398
399#[cfg(feature = "serde")]
400mod serde_impl {
401 use super::*;
402 use serde::Deserialize;
403
404 #[derive(Default)]
406 enum MaybeOriginalInfo {
407 #[default]
409 Missing,
410 Present(Option<AccountInfo>),
412 }
413
414 impl<'de> Deserialize<'de> for MaybeOriginalInfo {
415 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
416 where
417 D: serde::Deserializer<'de>,
418 {
419 Option::<AccountInfo>::deserialize(deserializer).map(MaybeOriginalInfo::Present)
420 }
421 }
422
423 #[derive(Deserialize)]
424 struct AccountSerde {
425 info: AccountInfo,
426 #[serde(default)]
427 original_info: MaybeOriginalInfo,
428 storage: EvmStorage,
429 transaction_id: TransactionId,
430 status: AccountStatus,
431 }
432
433 impl<'de> Deserialize<'de> for super::Account {
434 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
435 where
436 D: serde::Deserializer<'de>,
437 {
438 let AccountSerde {
439 info,
440 original_info,
441 storage,
442 transaction_id,
443 status,
444 } = Deserialize::deserialize(deserializer)?;
445
446 let original_info = match original_info {
447 MaybeOriginalInfo::Missing => Some(Box::new(info.clone())),
449 MaybeOriginalInfo::Present(None) => None,
451 MaybeOriginalInfo::Present(Some(oi)) => Some(Box::new(oi)),
453 };
454
455 Ok(Account {
456 info,
457 original_info,
458 storage,
459 transaction_id,
460 status,
461 })
462 }
463 }
464}
465
466bitflags! {
468 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
500 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
501 #[cfg_attr(feature = "serde", serde(transparent))]
502 pub struct AccountStatus: u8 {
503 const Created = 0b00000001;
506 const CreatedLocal = 0b10000000;
508 const SelfDestructed = 0b00000010;
510 const SelfDestructedLocal = 0b01000000;
512 const Touched = 0b00000100;
516 const LoadedAsNotExisting = 0b00001000;
519 const Cold = 0b00010000;
522 }
523}
524
525impl AccountStatus {
526 #[inline]
528 pub const fn is_touched(&self) -> bool {
529 self.contains(AccountStatus::Touched)
530 }
531}
532
533impl Default for AccountStatus {
534 fn default() -> Self {
535 AccountStatus::empty()
536 }
537}
538
539#[derive(Debug, Clone, Default, PartialEq, Eq)]
541#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
542pub struct EvmStorageSlot {
543 pub original_value: StorageValue,
545 pub present_value: StorageValue,
547 pub transaction_id: TransactionId,
549 pub is_cold: bool,
551}
552
553impl EvmStorageSlot {
554 pub const fn new(original: StorageValue, transaction_id: TransactionId) -> Self {
556 Self {
557 original_value: original,
558 present_value: original,
559 transaction_id,
560 is_cold: false,
561 }
562 }
563
564 pub const fn new_changed(
566 original_value: StorageValue,
567 present_value: StorageValue,
568 transaction_id: TransactionId,
569 ) -> Self {
570 Self {
571 original_value,
572 present_value,
573 transaction_id,
574 is_cold: false,
575 }
576 }
577 pub fn is_changed(&self) -> bool {
579 self.original_value != self.present_value
580 }
581
582 #[inline]
584 pub const fn original_value(&self) -> StorageValue {
585 self.original_value
586 }
587
588 #[inline]
590 pub const fn present_value(&self) -> StorageValue {
591 self.present_value
592 }
593
594 #[inline]
596 pub const fn mark_cold(&mut self) {
597 self.is_cold = true;
598 }
599
600 #[inline]
602 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
603 self.transaction_id.get() != transaction_id.get() || self.is_cold
604 }
605
606 #[inline]
611 pub const fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
612 let is_cold = self.is_cold_transaction_id(transaction_id);
613 if is_cold {
614 self.original_value = self.present_value;
616 }
617 self.transaction_id = transaction_id;
618 self.is_cold = false;
619 is_cold
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626 use crate::EvmStorageSlot;
627 use primitives::{StorageKey, KECCAK_EMPTY, U256};
628
629 #[test]
630 fn account_is_empty_balance() {
631 let mut account = Account::default();
632 assert!(account.is_empty());
633
634 account.info.balance = U256::from(1);
635 assert!(!account.is_empty());
636
637 account.info.balance = U256::ZERO;
638 assert!(account.is_empty());
639 }
640
641 #[test]
642 fn account_is_empty_nonce() {
643 let mut account = Account::default();
644 assert!(account.is_empty());
645
646 account.info.nonce = 1;
647 assert!(!account.is_empty());
648
649 account.info.nonce = 0;
650 assert!(account.is_empty());
651 }
652
653 #[test]
654 fn account_is_empty_code_hash() {
655 let mut account = Account::default();
656 assert!(account.is_empty());
657
658 account.info.code_hash = [1; 32].into();
659 assert!(!account.is_empty());
660
661 account.info.code_hash = [0; 32].into();
662 assert!(account.is_empty());
663
664 account.info.code_hash = KECCAK_EMPTY;
665 assert!(account.is_empty());
666 }
667
668 #[test]
669 fn account_state() {
670 let mut account = Account::default();
671
672 assert!(!account.is_touched());
673 assert!(!account.is_selfdestructed());
674
675 account.mark_touch();
676 assert!(account.is_touched());
677 assert!(!account.is_selfdestructed());
678
679 account.mark_selfdestruct();
680 assert!(account.is_touched());
681 assert!(account.is_selfdestructed());
682
683 account.unmark_selfdestruct();
684 assert!(account.is_touched());
685 assert!(!account.is_selfdestructed());
686 }
687
688 #[test]
689 fn account_is_cold() {
690 let mut account = Account::default();
691
692 assert!(!account.status.contains(crate::AccountStatus::Cold));
694
695 assert!(!account.mark_warm_with_transaction_id(TransactionId::ZERO));
697
698 account.mark_cold();
700
701 assert!(account.status.contains(crate::AccountStatus::Cold));
703
704 assert!(account.mark_warm_with_transaction_id(TransactionId::ZERO));
706 }
707
708 #[test]
709 fn test_account_with_info() {
710 let info = AccountInfo::default();
711 let account = Account::default().with_info(info.clone());
712
713 assert_eq!(account.info, info);
714 assert_eq!(account.storage, HashMap::default());
715 assert_eq!(account.status, AccountStatus::empty());
716 }
717
718 #[test]
719 fn test_account_with_storage() {
720 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
721 let key1 = StorageKey::from(1);
722 let key2 = StorageKey::from(2);
723 let slot1 = EvmStorageSlot::new(StorageValue::from(10), TransactionId::ZERO);
724 let slot2 = EvmStorageSlot::new(StorageValue::from(20), TransactionId::ZERO);
725
726 storage.insert(key1, slot1.clone());
727 storage.insert(key2, slot2.clone());
728
729 let account = Account::default().with_storage(storage.clone().into_iter());
730
731 assert_eq!(account.storage.len(), 2);
732 assert_eq!(account.storage.get(&key1), Some(&slot1));
733 assert_eq!(account.storage.get(&key2), Some(&slot2));
734 }
735
736 #[test]
737 fn test_account_with_selfdestruct_mark() {
738 let account = Account::default().with_selfdestruct_mark();
739
740 assert!(account.is_selfdestructed());
741 assert!(!account.is_touched());
742 assert!(!account.is_created());
743 }
744
745 #[test]
746 #[cfg(feature = "serde")]
747 fn test_account_serialize_deserialize() {
748 let account = Account::default().with_selfdestruct_mark();
749 let serialized = serde_json::to_string(&account).unwrap();
750 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
751 assert_eq!(account, deserialized);
752 }
753
754 #[test]
755 #[cfg(feature = "serde")]
756 fn test_account_original_info_none_roundtrip() {
757 let account = Account::new_not_existing(TransactionId::new(2).unwrap());
758 assert!(account.original_info.is_none());
759 let serialized = serde_json::to_string(&account).unwrap();
760 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
761 assert!(deserialized.original_info.is_none());
762 assert_eq!(account, deserialized);
763 }
764
765 #[test]
766 #[cfg(feature = "serde")]
767 fn test_account_deserialize_original_info_missing_null_present() {
768 let code = r#"{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}"#;
769 let info = format!(
770 r#"{{"balance":"0x2386f26fc10000","nonce":1,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":{code}}}"#
771 );
772
773 let json =
775 format!(r#"{{"info":{info},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#);
776 let acct: Account = serde_json::from_str(&json).unwrap();
777 assert!(acct.original_info.is_some());
778 assert_eq!(acct.original_info(), acct.info);
779
780 let json = format!(
782 r#"{{"info":{info},"original_info":null,"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
783 );
784 let acct: Account = serde_json::from_str(&json).unwrap();
785 assert!(acct.original_info.is_none());
786
787 let original = r#"{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null}"#;
789 let json = format!(
790 r#"{{"info":{info},"original_info":{original},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
791 );
792 let acct: Account = serde_json::from_str(&json).unwrap();
793 assert!(acct.original_info.is_some());
794 assert_eq!(acct.original_info().nonce, 0);
795 assert_eq!(acct.original_info().balance, U256::ZERO);
796 }
797
798 #[test]
799 fn test_account_with_touched_mark() {
800 let account = Account::default().with_touched_mark();
801
802 assert!(!account.is_selfdestructed());
803 assert!(account.is_touched());
804 assert!(!account.is_created());
805 }
806
807 #[test]
808 fn test_account_with_created_mark() {
809 let account = Account::default().with_created_mark();
810
811 assert!(!account.is_selfdestructed());
812 assert!(!account.is_touched());
813 assert!(account.is_created());
814 }
815
816 #[test]
817 fn test_account_with_cold_mark() {
818 let account = Account::default().with_cold_mark();
819
820 assert!(account.status.contains(AccountStatus::Cold));
821 }
822
823 #[test]
824 fn test_storage_mark_warm_with_transaction_id() {
825 let tx_zero = TransactionId::ZERO;
826 let tx_one = TransactionId::new(1).unwrap();
827 let mut slot = EvmStorageSlot::new(U256::ZERO, tx_zero);
828 slot.is_cold = true;
829 slot.transaction_id = tx_zero;
830 assert!(slot.mark_warm_with_transaction_id(tx_one));
831
832 slot.is_cold = false;
833 slot.transaction_id = tx_zero;
834 assert!(slot.mark_warm_with_transaction_id(tx_one));
835
836 slot.is_cold = true;
837 slot.transaction_id = tx_one;
838 assert!(slot.mark_warm_with_transaction_id(tx_one));
839
840 slot.is_cold = false;
841 slot.transaction_id = tx_one;
842 assert!(!slot.mark_warm_with_transaction_id(tx_one));
844 }
845
846 #[test]
847 fn test_account_with_warm_mark() {
848 let cold_account = Account::default().with_cold_mark();
850 assert!(cold_account.status.contains(AccountStatus::Cold));
851
852 let (warm_account, was_cold) = cold_account.with_warm_mark(TransactionId::ZERO);
854
855 assert!(!warm_account.status.contains(AccountStatus::Cold));
857 assert!(was_cold);
858
859 let (still_warm_account, was_cold) = warm_account.with_warm_mark(TransactionId::ZERO);
861 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
862 assert!(!was_cold);
863 }
864
865 #[test]
866 fn test_account_with_warm() {
867 let cold_account = Account::default().with_cold_mark();
869 assert!(cold_account.status.contains(AccountStatus::Cold));
870
871 let warm_account = cold_account.with_warm(TransactionId::ZERO);
873
874 assert!(!warm_account.status.contains(AccountStatus::Cold));
876 }
877
878 #[test]
879 fn test_account_builder_chaining() {
880 let info = AccountInfo {
881 nonce: 5,
882 ..AccountInfo::default()
883 };
884
885 let slot_key = StorageKey::from(42);
886 let slot_value = EvmStorageSlot::new(StorageValue::from(123), TransactionId::ZERO);
887 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
888 storage.insert(slot_key, slot_value.clone());
889
890 let account = Account::default()
892 .with_info(info.clone())
893 .with_storage(storage.into_iter())
894 .with_created_mark()
895 .with_touched_mark()
896 .with_cold_mark()
897 .with_warm(TransactionId::ZERO);
898
899 assert_eq!(account.info, info);
901 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
902 assert!(account.is_created());
903 assert!(account.is_touched());
904 assert!(!account.status.contains(AccountStatus::Cold));
905 }
906
907 #[test]
908 fn test_account_is_cold_transaction_id() {
909 let tx_zero = TransactionId::ZERO;
910 let tx_one = TransactionId::new(1).unwrap();
911 let mut account = Account::default();
912 assert!(!account.is_cold_transaction_id(tx_zero));
914
915 assert!(account.is_cold_transaction_id(tx_one));
917 account.mark_cold();
918 assert!(account.is_cold_transaction_id(tx_zero));
919 assert!(account.is_cold_transaction_id(tx_one));
920 }
921}