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 is_changed(&self) -> bool {
206 self.original_info.as_deref().map_or_else(
207 || !self.info.is_default(),
208 |original| self.info != *original,
209 )
210 }
211
212 #[inline]
214 pub fn mark_created(&mut self) {
215 self.status |= AccountStatus::Created;
216 }
217
218 #[inline]
220 pub fn unmark_created(&mut self) {
221 self.status -= AccountStatus::Created;
222 }
223
224 #[inline]
226 pub fn mark_cold(&mut self) {
227 self.status |= AccountStatus::Cold;
228 }
229
230 #[inline]
232 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
233 self.transaction_id.get() != transaction_id.get()
234 || self.status.contains(AccountStatus::Cold)
235 }
236
237 #[inline]
239 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
240 let is_cold = self.is_cold_transaction_id(transaction_id);
241 self.status -= AccountStatus::Cold;
242 self.transaction_id = transaction_id;
243 is_cold
244 }
245
246 #[inline]
248 pub const fn is_created_locally(&self) -> bool {
249 self.status.contains(AccountStatus::CreatedLocal)
250 }
251
252 #[inline]
254 pub const fn is_selfdestructed_locally(&self) -> bool {
255 self.status.contains(AccountStatus::SelfDestructedLocal)
256 }
257
258 #[inline]
260 pub fn selfdestruct(&mut self) {
261 self.storage.clear();
262 self.info = AccountInfo::default();
263 }
264
265 #[inline]
269 pub fn mark_created_locally(&mut self) -> bool {
270 self.mark_local_and_global(AccountStatus::CreatedLocal, AccountStatus::Created)
271 }
272
273 #[inline]
275 pub fn unmark_created_locally(&mut self) {
276 self.status -= AccountStatus::CreatedLocal;
277 }
278
279 #[inline]
281 pub fn mark_selfdestructed_locally(&mut self) -> bool {
282 self.mark_local_and_global(
283 AccountStatus::SelfDestructedLocal,
284 AccountStatus::SelfDestructed,
285 )
286 }
287
288 #[inline]
289 fn mark_local_and_global(
290 &mut self,
291 local_flag: AccountStatus,
292 global_flag: AccountStatus,
293 ) -> bool {
294 self.status |= local_flag;
295 let is_global_first_time = !self.status.contains(global_flag);
296 self.status |= global_flag;
297 is_global_first_time
298 }
299
300 #[inline]
302 pub fn unmark_selfdestructed_locally(&mut self) {
303 self.status -= AccountStatus::SelfDestructedLocal;
304 }
305
306 pub const fn is_loaded_as_not_existing(&self) -> bool {
311 self.status.contains(AccountStatus::LoadedAsNotExisting)
312 }
313
314 pub const fn is_loaded_as_not_existing_not_touched(&self) -> bool {
316 self.is_loaded_as_not_existing() && !self.is_touched()
317 }
318
319 pub const fn is_created(&self) -> bool {
321 self.status.contains(AccountStatus::Created)
322 }
323
324 pub fn is_empty(&self) -> bool {
326 self.info.is_empty()
327 }
328
329 pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&StorageKey, &EvmStorageSlot)> {
333 self.storage.iter().filter(|(_, slot)| slot.is_changed())
334 }
335
336 pub fn with_info(mut self, info: AccountInfo) -> Self {
338 self.info = info;
339 self
340 }
341
342 pub fn with_storage<I>(mut self, storage_iter: I) -> Self
344 where
345 I: Iterator<Item = (StorageKey, EvmStorageSlot)>,
346 {
347 for (key, slot) in storage_iter {
348 self.storage.insert(key, slot);
349 }
350 self
351 }
352
353 pub fn with_selfdestruct_mark(mut self) -> Self {
355 self.mark_selfdestruct();
356 self
357 }
358
359 pub fn with_touched_mark(mut self) -> Self {
361 self.mark_touch();
362 self
363 }
364
365 pub fn with_created_mark(mut self) -> Self {
367 self.mark_created();
368 self
369 }
370
371 pub fn with_cold_mark(mut self) -> Self {
373 self.mark_cold();
374 self
375 }
376
377 pub fn with_warm_mark(mut self, transaction_id: TransactionId) -> (Self, bool) {
380 let was_cold = self.mark_warm_with_transaction_id(transaction_id);
381 (self, was_cold)
382 }
383
384 pub fn with_warm(mut self, transaction_id: TransactionId) -> Self {
386 self.mark_warm_with_transaction_id(transaction_id);
387 self
388 }
389}
390
391impl From<AccountInfo> for Account {
392 fn from(info: AccountInfo) -> Self {
393 let original_info = if info.is_default() {
394 None
395 } else {
396 Some(Box::new(info.clone()))
397 };
398 Self {
399 info,
400 original_info,
401 transaction_id: TransactionId::ZERO,
402 storage: HashMap::default(),
403 status: AccountStatus::empty(),
404 }
405 }
406}
407
408#[cfg(feature = "serde")]
409mod serde_impl {
410 use super::*;
411 use serde::Deserialize;
412
413 #[derive(Default)]
415 enum MaybeOriginalInfo {
416 #[default]
418 Missing,
419 Present(Option<AccountInfo>),
421 }
422
423 impl<'de> Deserialize<'de> for MaybeOriginalInfo {
424 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
425 where
426 D: serde::Deserializer<'de>,
427 {
428 Option::<AccountInfo>::deserialize(deserializer).map(MaybeOriginalInfo::Present)
429 }
430 }
431
432 #[derive(Deserialize)]
433 struct AccountSerde {
434 info: AccountInfo,
435 #[serde(default)]
436 original_info: MaybeOriginalInfo,
437 storage: EvmStorage,
438 transaction_id: TransactionId,
439 status: AccountStatus,
440 }
441
442 impl<'de> Deserialize<'de> for super::Account {
443 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
444 where
445 D: serde::Deserializer<'de>,
446 {
447 let AccountSerde {
448 info,
449 original_info,
450 storage,
451 transaction_id,
452 status,
453 } = Deserialize::deserialize(deserializer)?;
454
455 let original_info = match original_info {
456 MaybeOriginalInfo::Missing => Some(Box::new(info.clone())),
458 MaybeOriginalInfo::Present(None) => None,
460 MaybeOriginalInfo::Present(Some(oi)) => Some(Box::new(oi)),
462 };
463
464 Ok(Account {
465 info,
466 original_info,
467 storage,
468 transaction_id,
469 status,
470 })
471 }
472 }
473}
474
475bitflags! {
477 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
509 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
510 #[cfg_attr(feature = "serde", serde(transparent))]
511 pub struct AccountStatus: u8 {
512 const Created = 0b00000001;
515 const CreatedLocal = 0b10000000;
517 const SelfDestructed = 0b00000010;
519 const SelfDestructedLocal = 0b01000000;
521 const Touched = 0b00000100;
525 const LoadedAsNotExisting = 0b00001000;
528 const Cold = 0b00010000;
531 }
532}
533
534impl AccountStatus {
535 #[inline]
537 pub const fn is_touched(&self) -> bool {
538 self.contains(AccountStatus::Touched)
539 }
540}
541
542impl Default for AccountStatus {
543 fn default() -> Self {
544 AccountStatus::empty()
545 }
546}
547
548#[derive(Debug, Clone, Default, PartialEq, Eq)]
550#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
551pub struct EvmStorageSlot {
552 pub original_value: StorageValue,
554 pub present_value: StorageValue,
556 pub transaction_id: TransactionId,
558 pub is_cold: bool,
560}
561
562impl EvmStorageSlot {
563 pub const fn new(original: StorageValue, transaction_id: TransactionId) -> Self {
565 Self {
566 original_value: original,
567 present_value: original,
568 transaction_id,
569 is_cold: false,
570 }
571 }
572
573 pub const fn new_changed(
575 original_value: StorageValue,
576 present_value: StorageValue,
577 transaction_id: TransactionId,
578 ) -> Self {
579 Self {
580 original_value,
581 present_value,
582 transaction_id,
583 is_cold: false,
584 }
585 }
586 pub fn is_changed(&self) -> bool {
588 self.original_value != self.present_value
589 }
590
591 #[inline]
593 pub const fn original_value(&self) -> StorageValue {
594 self.original_value
595 }
596
597 #[inline]
599 pub const fn present_value(&self) -> StorageValue {
600 self.present_value
601 }
602
603 #[inline]
605 pub const fn mark_cold(&mut self) {
606 self.is_cold = true;
607 }
608
609 #[inline]
611 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
612 self.transaction_id.get() != transaction_id.get() || self.is_cold
613 }
614
615 #[inline]
620 pub const fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
621 let is_cold = self.is_cold_transaction_id(transaction_id);
622 if is_cold {
623 self.original_value = self.present_value;
625 }
626 self.transaction_id = transaction_id;
627 self.is_cold = false;
628 is_cold
629 }
630}
631
632#[cfg(test)]
633mod tests {
634 use super::*;
635 use crate::EvmStorageSlot;
636 use primitives::{StorageKey, KECCAK_EMPTY, U256};
637
638 #[test]
639 fn account_is_empty_balance() {
640 let mut account = Account::default();
641 assert!(account.is_empty());
642
643 account.info.balance = U256::from(1);
644 assert!(!account.is_empty());
645
646 account.info.balance = U256::ZERO;
647 assert!(account.is_empty());
648 }
649
650 #[test]
651 fn account_is_empty_nonce() {
652 let mut account = Account::default();
653 assert!(account.is_empty());
654
655 account.info.nonce = 1;
656 assert!(!account.is_empty());
657
658 account.info.nonce = 0;
659 assert!(account.is_empty());
660 }
661
662 #[test]
663 fn account_is_empty_code_hash() {
664 let mut account = Account::default();
665 assert!(account.is_empty());
666
667 account.info.code_hash = [1; 32].into();
668 assert!(!account.is_empty());
669
670 account.info.code_hash = [0; 32].into();
671 assert!(account.is_empty());
672
673 account.info.code_hash = KECCAK_EMPTY;
674 assert!(account.is_empty());
675 }
676
677 #[test]
678 fn account_state() {
679 let mut account = Account::default();
680
681 assert!(!account.is_touched());
682 assert!(!account.is_selfdestructed());
683
684 account.mark_touch();
685 assert!(account.is_touched());
686 assert!(!account.is_selfdestructed());
687
688 account.mark_selfdestruct();
689 assert!(account.is_touched());
690 assert!(account.is_selfdestructed());
691
692 account.unmark_selfdestruct();
693 assert!(account.is_touched());
694 assert!(!account.is_selfdestructed());
695 }
696
697 #[test]
698 fn account_is_cold() {
699 let mut account = Account::default();
700
701 assert!(!account.status.contains(crate::AccountStatus::Cold));
703
704 assert!(!account.mark_warm_with_transaction_id(TransactionId::ZERO));
706
707 account.mark_cold();
709
710 assert!(account.status.contains(crate::AccountStatus::Cold));
712
713 assert!(account.mark_warm_with_transaction_id(TransactionId::ZERO));
715 }
716
717 #[test]
718 fn test_account_with_info() {
719 let info = AccountInfo::default();
720 let account = Account::default().with_info(info.clone());
721
722 assert_eq!(account.info, info);
723 assert_eq!(account.storage, HashMap::default());
724 assert_eq!(account.status, AccountStatus::empty());
725 }
726
727 #[test]
728 fn test_account_with_storage() {
729 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
730 let key1 = StorageKey::from(1);
731 let key2 = StorageKey::from(2);
732 let slot1 = EvmStorageSlot::new(StorageValue::from(10), TransactionId::ZERO);
733 let slot2 = EvmStorageSlot::new(StorageValue::from(20), TransactionId::ZERO);
734
735 storage.insert(key1, slot1.clone());
736 storage.insert(key2, slot2.clone());
737
738 let account = Account::default().with_storage(storage.clone().into_iter());
739
740 assert_eq!(account.storage.len(), 2);
741 assert_eq!(account.storage.get(&key1), Some(&slot1));
742 assert_eq!(account.storage.get(&key2), Some(&slot2));
743 }
744
745 #[test]
746 fn test_account_with_selfdestruct_mark() {
747 let account = Account::default().with_selfdestruct_mark();
748
749 assert!(account.is_selfdestructed());
750 assert!(!account.is_touched());
751 assert!(!account.is_created());
752 }
753
754 #[test]
755 #[cfg(feature = "serde")]
756 fn test_account_serialize_deserialize() {
757 let account = Account::default().with_selfdestruct_mark();
758 let serialized = serde_json::to_string(&account).unwrap();
759 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
760 assert_eq!(account, deserialized);
761 }
762
763 #[test]
764 #[cfg(feature = "serde")]
765 fn test_account_original_info_none_roundtrip() {
766 let account = Account::new_not_existing(TransactionId::new(2).unwrap());
767 assert!(account.original_info.is_none());
768 let serialized = serde_json::to_string(&account).unwrap();
769 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
770 assert!(deserialized.original_info.is_none());
771 assert_eq!(account, deserialized);
772 }
773
774 #[test]
775 #[cfg(feature = "serde")]
776 fn test_account_deserialize_original_info_missing_null_present() {
777 let code = r#"{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}"#;
778 let info = format!(
779 r#"{{"balance":"0x2386f26fc10000","nonce":1,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":{code}}}"#
780 );
781
782 let json =
784 format!(r#"{{"info":{info},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#);
785 let acct: Account = serde_json::from_str(&json).unwrap();
786 assert!(acct.original_info.is_some());
787 assert_eq!(acct.original_info(), acct.info);
788
789 let json = format!(
791 r#"{{"info":{info},"original_info":null,"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
792 );
793 let acct: Account = serde_json::from_str(&json).unwrap();
794 assert!(acct.original_info.is_none());
795
796 let original = r#"{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null}"#;
798 let json = format!(
799 r#"{{"info":{info},"original_info":{original},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
800 );
801 let acct: Account = serde_json::from_str(&json).unwrap();
802 assert!(acct.original_info.is_some());
803 assert_eq!(acct.original_info().nonce, 0);
804 assert_eq!(acct.original_info().balance, U256::ZERO);
805 }
806
807 #[test]
808 fn test_account_with_touched_mark() {
809 let account = Account::default().with_touched_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_created_mark() {
818 let account = Account::default().with_created_mark();
819
820 assert!(!account.is_selfdestructed());
821 assert!(!account.is_touched());
822 assert!(account.is_created());
823 }
824
825 #[test]
826 fn test_account_with_cold_mark() {
827 let account = Account::default().with_cold_mark();
828
829 assert!(account.status.contains(AccountStatus::Cold));
830 }
831
832 #[test]
833 fn test_storage_mark_warm_with_transaction_id() {
834 let tx_zero = TransactionId::ZERO;
835 let tx_one = TransactionId::new(1).unwrap();
836 let mut slot = EvmStorageSlot::new(U256::ZERO, tx_zero);
837 slot.is_cold = true;
838 slot.transaction_id = tx_zero;
839 assert!(slot.mark_warm_with_transaction_id(tx_one));
840
841 slot.is_cold = false;
842 slot.transaction_id = tx_zero;
843 assert!(slot.mark_warm_with_transaction_id(tx_one));
844
845 slot.is_cold = true;
846 slot.transaction_id = tx_one;
847 assert!(slot.mark_warm_with_transaction_id(tx_one));
848
849 slot.is_cold = false;
850 slot.transaction_id = tx_one;
851 assert!(!slot.mark_warm_with_transaction_id(tx_one));
853 }
854
855 #[test]
856 fn test_account_with_warm_mark() {
857 let cold_account = Account::default().with_cold_mark();
859 assert!(cold_account.status.contains(AccountStatus::Cold));
860
861 let (warm_account, was_cold) = cold_account.with_warm_mark(TransactionId::ZERO);
863
864 assert!(!warm_account.status.contains(AccountStatus::Cold));
866 assert!(was_cold);
867
868 let (still_warm_account, was_cold) = warm_account.with_warm_mark(TransactionId::ZERO);
870 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
871 assert!(!was_cold);
872 }
873
874 #[test]
875 fn test_account_with_warm() {
876 let cold_account = Account::default().with_cold_mark();
878 assert!(cold_account.status.contains(AccountStatus::Cold));
879
880 let warm_account = cold_account.with_warm(TransactionId::ZERO);
882
883 assert!(!warm_account.status.contains(AccountStatus::Cold));
885 }
886
887 #[test]
888 fn test_account_builder_chaining() {
889 let info = AccountInfo {
890 nonce: 5,
891 ..AccountInfo::default()
892 };
893
894 let slot_key = StorageKey::from(42);
895 let slot_value = EvmStorageSlot::new(StorageValue::from(123), TransactionId::ZERO);
896 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
897 storage.insert(slot_key, slot_value.clone());
898
899 let account = Account::default()
901 .with_info(info.clone())
902 .with_storage(storage.into_iter())
903 .with_created_mark()
904 .with_touched_mark()
905 .with_cold_mark()
906 .with_warm(TransactionId::ZERO);
907
908 assert_eq!(account.info, info);
910 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
911 assert!(account.is_created());
912 assert!(account.is_touched());
913 assert!(!account.status.contains(AccountStatus::Cold));
914 }
915
916 #[test]
917 fn test_account_is_cold_transaction_id() {
918 let tx_zero = TransactionId::ZERO;
919 let tx_one = TransactionId::new(1).unwrap();
920 let mut account = Account::default();
921 assert!(!account.is_cold_transaction_id(tx_zero));
923
924 assert!(account.is_cold_transaction_id(tx_one));
926 account.mark_cold();
927 assert!(account.is_cold_transaction_id(tx_zero));
928 assert!(account.is_cold_transaction_id(tx_one));
929 }
930}