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
8#[cfg(feature = "account-ext")]
9mod account_extension;
10mod account_info;
11#[cfg(feature = "account-ext")]
12pub use account_extension::AccountExtension;
13pub mod bal;
14mod types;
15
16pub use bytecode;
17
18pub use account_info::{AccountId, AccountInfo};
19pub use bytecode::Bytecode;
20pub use primitives;
21pub use types::{EvmState, EvmStorage, TransientStorage};
22
23use bitflags::bitflags;
24use nonmax::NonMaxU32;
25use primitives::{hardfork::SpecId, HashMap, StorageKey, StorageValue, U256};
26use std::boxed::Box;
27
28#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
33#[cfg_attr(feature = "serde", serde(transparent))]
34pub struct TransactionId(NonMaxU32);
35
36impl TransactionId {
37 pub const ZERO: Self = Self(NonMaxU32::ZERO);
39
40 #[inline]
44 pub fn new(id: usize) -> Option<Self> {
45 let id = u32::try_from(id).ok()?;
46 NonMaxU32::new(id).map(Self)
47 }
48
49 #[inline]
51 pub const fn get(self) -> usize {
52 self.0.get() as usize
53 }
54
55 #[inline]
61 pub const fn increment(&mut self) {
62 self.0 = match NonMaxU32::new(self.0.get() + 1) {
63 Some(id) => id,
64 None => panic!("transaction id overflow"),
65 };
66 }
67}
68
69#[derive(Debug, Clone, Eq, Default)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize))]
83pub struct Account {
84 pub info: AccountInfo,
86 pub transaction_id: TransactionId,
88 pub storage: EvmStorage,
90 pub status: AccountStatus,
92
93 original_info: Option<Box<AccountInfo>>,
96}
97
98impl PartialEq for Account {
99 #[inline]
100 fn eq(&self, other: &Self) -> bool {
101 self.info == other.info
102 && self.transaction_id == other.transaction_id
103 && self.storage == other.storage
104 && self.status == other.status
105 && self.original_info() == other.original_info()
106 }
107}
108
109impl Account {
110 #[inline]
112 pub fn new_not_existing(transaction_id: TransactionId) -> Self {
113 Self {
114 transaction_id,
115 status: AccountStatus::LoadedAsNotExisting,
116 ..Default::default()
117 }
118 }
119
120 #[inline]
126 pub fn caller_initial_modification(&mut self, new_balance: U256, is_call: bool) -> U256 {
127 self.mark_touch();
129
130 if is_call {
131 self.info.nonce = self.info.nonce.saturating_add(1);
133 }
134
135 core::mem::replace(&mut self.info.balance, new_balance)
136 }
137
138 #[inline]
140 pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
141 if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
142 self.is_empty()
143 } else {
144 self.is_loaded_as_not_existing_not_touched()
145 }
146 }
147
148 #[inline]
150 pub fn original_info(&self) -> AccountInfo {
151 self.original_info.as_deref().cloned().unwrap_or_default()
152 }
153
154 #[inline]
156 pub fn original_info_mut(&mut self) -> &mut AccountInfo {
157 self.original_info.get_or_insert_default()
158 }
159
160 pub fn set_current_info_as_original(&mut self) {
162 if self.original_info.is_none() && self.info.is_default() {
163 return;
164 }
165 self.original_info
166 .get_or_insert_default()
167 .as_mut()
168 .clone_from(&self.info);
169 }
170
171 #[inline]
173 pub fn mark_selfdestruct(&mut self) {
174 self.status |= AccountStatus::SelfDestructed;
175 }
176
177 #[inline]
179 pub fn unmark_selfdestruct(&mut self) {
180 self.status -= AccountStatus::SelfDestructed;
181 }
182
183 #[inline]
185 pub const fn is_selfdestructed(&self) -> bool {
186 self.status.contains(AccountStatus::SelfDestructed)
187 }
188
189 #[inline]
191 pub fn mark_touch(&mut self) {
192 self.status |= AccountStatus::Touched;
193 }
194
195 #[inline]
197 pub fn unmark_touch(&mut self) {
198 self.status -= AccountStatus::Touched;
199 }
200
201 #[inline]
203 pub const fn is_touched(&self) -> bool {
204 self.status.contains(AccountStatus::Touched)
205 }
206
207 #[inline]
209 pub fn is_changed(&self) -> bool {
210 self.original_info.as_deref().map_or_else(
211 || !self.info.is_default(),
212 |original| self.info != *original,
213 )
214 }
215
216 #[inline]
218 pub fn mark_created(&mut self) {
219 self.status |= AccountStatus::Created;
220 }
221
222 #[inline]
224 pub fn unmark_created(&mut self) {
225 self.status -= AccountStatus::Created;
226 }
227
228 #[inline]
230 pub fn mark_cold(&mut self) {
231 self.status |= AccountStatus::Cold;
232 }
233
234 #[inline]
236 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
237 self.transaction_id.get() != transaction_id.get()
238 || self.status.contains(AccountStatus::Cold)
239 }
240
241 #[inline]
243 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
244 let is_cold = self.is_cold_transaction_id(transaction_id);
245 self.status -= AccountStatus::Cold;
246 self.transaction_id = transaction_id;
247 is_cold
248 }
249
250 #[inline]
252 pub const fn is_created_locally(&self) -> bool {
253 self.status.contains(AccountStatus::CreatedLocal)
254 }
255
256 #[inline]
258 pub const fn is_selfdestructed_locally(&self) -> bool {
259 self.status.contains(AccountStatus::SelfDestructedLocal)
260 }
261
262 #[inline]
264 pub fn selfdestruct(&mut self) {
265 self.storage.clear();
266 self.info = AccountInfo::default();
267 }
268
269 #[inline]
273 pub fn mark_created_locally(&mut self) -> bool {
274 self.mark_local_and_global(AccountStatus::CreatedLocal, AccountStatus::Created)
275 }
276
277 #[inline]
279 pub fn unmark_created_locally(&mut self) {
280 self.status -= AccountStatus::CreatedLocal;
281 }
282
283 #[inline]
285 pub fn mark_selfdestructed_locally(&mut self) -> bool {
286 self.mark_local_and_global(
287 AccountStatus::SelfDestructedLocal,
288 AccountStatus::SelfDestructed,
289 )
290 }
291
292 #[inline]
293 fn mark_local_and_global(
294 &mut self,
295 local_flag: AccountStatus,
296 global_flag: AccountStatus,
297 ) -> bool {
298 self.status |= local_flag;
299 let is_global_first_time = !self.status.contains(global_flag);
300 self.status |= global_flag;
301 is_global_first_time
302 }
303
304 #[inline]
306 pub fn unmark_selfdestructed_locally(&mut self) {
307 self.status -= AccountStatus::SelfDestructedLocal;
308 }
309
310 pub const fn is_loaded_as_not_existing(&self) -> bool {
315 self.status.contains(AccountStatus::LoadedAsNotExisting)
316 }
317
318 pub const fn is_loaded_as_not_existing_not_touched(&self) -> bool {
320 self.is_loaded_as_not_existing() && !self.is_touched()
321 }
322
323 pub const fn is_created(&self) -> bool {
325 self.status.contains(AccountStatus::Created)
326 }
327
328 pub fn is_empty(&self) -> bool {
330 self.info.is_empty()
331 }
332
333 pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&StorageKey, &EvmStorageSlot)> {
337 self.storage.iter().filter(|(_, slot)| slot.is_changed())
338 }
339
340 pub fn with_info(mut self, info: AccountInfo) -> Self {
342 self.info = info;
343 self
344 }
345
346 pub fn with_storage<I>(mut self, storage_iter: I) -> Self
348 where
349 I: Iterator<Item = (StorageKey, EvmStorageSlot)>,
350 {
351 for (key, slot) in storage_iter {
352 self.storage.insert(key, slot);
353 }
354 self
355 }
356
357 pub fn with_selfdestruct_mark(mut self) -> Self {
359 self.mark_selfdestruct();
360 self
361 }
362
363 pub fn with_touched_mark(mut self) -> Self {
365 self.mark_touch();
366 self
367 }
368
369 pub fn with_created_mark(mut self) -> Self {
371 self.mark_created();
372 self
373 }
374
375 pub fn with_cold_mark(mut self) -> Self {
377 self.mark_cold();
378 self
379 }
380
381 pub fn with_warm_mark(mut self, transaction_id: TransactionId) -> (Self, bool) {
384 let was_cold = self.mark_warm_with_transaction_id(transaction_id);
385 (self, was_cold)
386 }
387
388 pub fn with_warm(mut self, transaction_id: TransactionId) -> Self {
390 self.mark_warm_with_transaction_id(transaction_id);
391 self
392 }
393}
394
395impl From<AccountInfo> for Account {
396 fn from(info: AccountInfo) -> Self {
397 let original_info = if info.is_default() {
398 None
399 } else {
400 Some(Box::new(info.clone()))
401 };
402 Self {
403 info,
404 original_info,
405 transaction_id: TransactionId::ZERO,
406 storage: HashMap::default(),
407 status: AccountStatus::empty(),
408 }
409 }
410}
411
412#[cfg(feature = "serde")]
413mod serde_impl {
414 use super::*;
415 use serde::Deserialize;
416
417 #[derive(Default)]
419 enum MaybeOriginalInfo {
420 #[default]
422 Missing,
423 Present(Option<AccountInfo>),
425 }
426
427 impl<'de> Deserialize<'de> for MaybeOriginalInfo {
428 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
429 where
430 D: serde::Deserializer<'de>,
431 {
432 Option::<AccountInfo>::deserialize(deserializer).map(MaybeOriginalInfo::Present)
433 }
434 }
435
436 #[derive(Deserialize)]
437 struct AccountSerde {
441 info: AccountInfo,
442 transaction_id: TransactionId,
443 storage: EvmStorage,
444 status: AccountStatus,
445 #[serde(default)]
446 original_info: MaybeOriginalInfo,
447 }
448
449 impl<'de> Deserialize<'de> for super::Account {
450 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
451 where
452 D: serde::Deserializer<'de>,
453 {
454 let AccountSerde {
455 info,
456 original_info,
457 storage,
458 transaction_id,
459 status,
460 } = Deserialize::deserialize(deserializer)?;
461
462 let original_info = match original_info {
463 MaybeOriginalInfo::Missing => Some(Box::new(info.clone())),
465 MaybeOriginalInfo::Present(None) => None,
467 MaybeOriginalInfo::Present(Some(oi)) => Some(Box::new(oi)),
469 };
470
471 Ok(Account {
472 info,
473 original_info,
474 storage,
475 transaction_id,
476 status,
477 })
478 }
479 }
480}
481
482bitflags! {
484 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
516 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
517 #[cfg_attr(feature = "serde", serde(transparent))]
518 pub struct AccountStatus: u8 {
519 const Created = 0b00000001;
522 const CreatedLocal = 0b10000000;
524 const SelfDestructed = 0b00000010;
526 const SelfDestructedLocal = 0b01000000;
528 const Touched = 0b00000100;
532 const LoadedAsNotExisting = 0b00001000;
535 const Cold = 0b00010000;
538 }
539}
540
541impl AccountStatus {
542 #[inline]
544 pub const fn is_touched(&self) -> bool {
545 self.contains(AccountStatus::Touched)
546 }
547}
548
549impl Default for AccountStatus {
550 fn default() -> Self {
551 AccountStatus::empty()
552 }
553}
554
555#[derive(Debug, Clone, Default, PartialEq, Eq)]
557#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
558pub struct EvmStorageSlot {
559 pub original_value: StorageValue,
561 pub present_value: StorageValue,
563 pub transaction_id: TransactionId,
565 pub is_cold: bool,
567}
568
569impl EvmStorageSlot {
570 pub const fn new(original: StorageValue, transaction_id: TransactionId) -> Self {
572 Self {
573 original_value: original,
574 present_value: original,
575 transaction_id,
576 is_cold: false,
577 }
578 }
579
580 pub const fn new_changed(
582 original_value: StorageValue,
583 present_value: StorageValue,
584 transaction_id: TransactionId,
585 ) -> Self {
586 Self {
587 original_value,
588 present_value,
589 transaction_id,
590 is_cold: false,
591 }
592 }
593 pub fn is_changed(&self) -> bool {
595 self.original_value != self.present_value
596 }
597
598 #[inline]
600 pub const fn original_value(&self) -> StorageValue {
601 self.original_value
602 }
603
604 #[inline]
606 pub const fn present_value(&self) -> StorageValue {
607 self.present_value
608 }
609
610 #[inline]
612 pub const fn mark_cold(&mut self) {
613 self.is_cold = true;
614 }
615
616 #[inline]
618 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
619 self.transaction_id.get() != transaction_id.get() || self.is_cold
620 }
621
622 #[inline]
627 pub const fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
628 let is_cold = self.is_cold_transaction_id(transaction_id);
629 if self.transaction_id.get() != transaction_id.get() {
635 self.original_value = self.present_value;
636 }
637 self.transaction_id = transaction_id;
638 self.is_cold = false;
639 is_cold
640 }
641}
642
643#[cfg(test)]
644mod tests {
645 use super::*;
646 use crate::EvmStorageSlot;
647 use primitives::{StorageKey, KECCAK_EMPTY, U256};
648
649 #[test]
650 fn account_is_empty_balance() {
651 let mut account = Account::default();
652 assert!(account.is_empty());
653
654 account.info.balance = U256::from(1);
655 assert!(!account.is_empty());
656
657 account.info.balance = U256::ZERO;
658 assert!(account.is_empty());
659 }
660
661 #[test]
662 fn account_is_empty_nonce() {
663 let mut account = Account::default();
664 assert!(account.is_empty());
665
666 account.info.nonce = 1;
667 assert!(!account.is_empty());
668
669 account.info.nonce = 0;
670 assert!(account.is_empty());
671 }
672
673 #[test]
674 fn account_is_empty_code_hash() {
675 let mut account = Account::default();
676 assert!(account.is_empty());
677
678 account.info.code_hash = [1; 32].into();
679 assert!(!account.is_empty());
680
681 account.info.code_hash = [0; 32].into();
682 assert!(account.is_empty());
683
684 account.info.code_hash = KECCAK_EMPTY;
685 assert!(account.is_empty());
686 }
687
688 #[test]
689 fn account_state() {
690 let mut account = Account::default();
691
692 assert!(!account.is_touched());
693 assert!(!account.is_selfdestructed());
694
695 account.mark_touch();
696 assert!(account.is_touched());
697 assert!(!account.is_selfdestructed());
698
699 account.mark_selfdestruct();
700 assert!(account.is_touched());
701 assert!(account.is_selfdestructed());
702
703 account.unmark_selfdestruct();
704 assert!(account.is_touched());
705 assert!(!account.is_selfdestructed());
706 }
707
708 #[test]
709 fn account_is_cold() {
710 let mut account = Account::default();
711
712 assert!(!account.status.contains(crate::AccountStatus::Cold));
714
715 assert!(!account.mark_warm_with_transaction_id(TransactionId::ZERO));
717
718 account.mark_cold();
720
721 assert!(account.status.contains(crate::AccountStatus::Cold));
723
724 assert!(account.mark_warm_with_transaction_id(TransactionId::ZERO));
726 }
727
728 #[test]
729 fn test_account_with_info() {
730 let info = AccountInfo::default();
731 let account = Account::default().with_info(info.clone());
732
733 assert_eq!(account.info, info);
734 assert_eq!(account.storage, HashMap::default());
735 assert_eq!(account.status, AccountStatus::empty());
736 }
737
738 #[test]
739 fn test_account_with_storage() {
740 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
741 let key1 = StorageKey::from(1);
742 let key2 = StorageKey::from(2);
743 let slot1 = EvmStorageSlot::new(StorageValue::from(10), TransactionId::ZERO);
744 let slot2 = EvmStorageSlot::new(StorageValue::from(20), TransactionId::ZERO);
745
746 storage.insert(key1, slot1.clone());
747 storage.insert(key2, slot2.clone());
748
749 let account = Account::default().with_storage(storage.clone().into_iter());
750
751 assert_eq!(account.storage.len(), 2);
752 assert_eq!(account.storage.get(&key1), Some(&slot1));
753 assert_eq!(account.storage.get(&key2), Some(&slot2));
754 }
755
756 #[test]
757 fn test_account_with_selfdestruct_mark() {
758 let account = Account::default().with_selfdestruct_mark();
759
760 assert!(account.is_selfdestructed());
761 assert!(!account.is_touched());
762 assert!(!account.is_created());
763 }
764
765 #[test]
766 #[cfg(feature = "serde")]
767 fn test_account_serialize_deserialize() {
768 let account = Account::default().with_selfdestruct_mark();
769 let serialized = serde_json::to_string(&account).unwrap();
770 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
771 assert_eq!(account, deserialized);
772 }
773
774 #[test]
775 #[cfg(feature = "serde")]
776 fn test_account_binary_round_trip() {
777 let mut account = Account::from(AccountInfo {
782 nonce: 5,
783 ..AccountInfo::default()
784 });
785 account.info.nonce = 7;
786 account.transaction_id = TransactionId::new(3).unwrap();
787 account.storage.insert(
788 StorageKey::from(42u64),
789 EvmStorageSlot {
790 original_value: U256::from(1u64),
791 present_value: U256::from(2u64),
792 transaction_id: account.transaction_id,
793 is_cold: false,
794 },
795 );
796 account.status = AccountStatus::Touched;
797
798 #[cfg(feature = "account-ext")]
800 let decoded: Account =
801 rmp_serde::from_slice(&rmp_serde::to_vec(&account).unwrap()).unwrap();
802 #[cfg(not(feature = "account-ext"))]
803 let decoded: Account =
804 postcard::from_bytes(&postcard::to_allocvec(&account).unwrap()).unwrap();
805
806 assert_eq!(account, decoded);
807 }
808
809 #[test]
810 #[cfg(feature = "serde")]
811 fn test_account_original_info_none_roundtrip() {
812 let account = Account::new_not_existing(TransactionId::new(2).unwrap());
813 assert!(account.original_info.is_none());
814 let serialized = serde_json::to_string(&account).unwrap();
815 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
816 assert!(deserialized.original_info.is_none());
817 assert_eq!(account, deserialized);
818 }
819
820 #[test]
821 #[cfg(feature = "serde")]
822 fn test_account_deserialize_original_info_missing_null_present() {
823 let code = r#"{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}"#;
824 let info = format!(
825 r#"{{"balance":"0x2386f26fc10000","nonce":1,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":{code}}}"#
826 );
827
828 let json =
830 format!(r#"{{"info":{info},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#);
831 let acct: Account = serde_json::from_str(&json).unwrap();
832 assert!(acct.original_info.is_some());
833 assert_eq!(acct.original_info(), acct.info);
834
835 let json = format!(
837 r#"{{"info":{info},"original_info":null,"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
838 );
839 let acct: Account = serde_json::from_str(&json).unwrap();
840 assert!(acct.original_info.is_none());
841
842 let original = r#"{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null}"#;
844 let json = format!(
845 r#"{{"info":{info},"original_info":{original},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
846 );
847 let acct: Account = serde_json::from_str(&json).unwrap();
848 assert!(acct.original_info.is_some());
849 assert_eq!(acct.original_info().nonce, 0);
850 assert_eq!(acct.original_info().balance, U256::ZERO);
851 }
852
853 #[test]
854 fn test_account_with_touched_mark() {
855 let account = Account::default().with_touched_mark();
856
857 assert!(!account.is_selfdestructed());
858 assert!(account.is_touched());
859 assert!(!account.is_created());
860 }
861
862 #[test]
863 fn test_account_with_created_mark() {
864 let account = Account::default().with_created_mark();
865
866 assert!(!account.is_selfdestructed());
867 assert!(!account.is_touched());
868 assert!(account.is_created());
869 }
870
871 #[test]
872 fn test_account_with_cold_mark() {
873 let account = Account::default().with_cold_mark();
874
875 assert!(account.status.contains(AccountStatus::Cold));
876 }
877
878 #[test]
879 fn test_storage_mark_warm_with_transaction_id() {
880 let tx_zero = TransactionId::ZERO;
881 let tx_one = TransactionId::new(1).unwrap();
882 let mut slot = EvmStorageSlot::new(U256::ZERO, tx_zero);
883 slot.is_cold = true;
884 slot.transaction_id = tx_zero;
885 assert!(slot.mark_warm_with_transaction_id(tx_one));
886
887 slot.is_cold = false;
888 slot.transaction_id = tx_zero;
889 assert!(slot.mark_warm_with_transaction_id(tx_one));
890
891 slot.is_cold = true;
892 slot.transaction_id = tx_one;
893 assert!(slot.mark_warm_with_transaction_id(tx_one));
894
895 slot.is_cold = false;
896 slot.transaction_id = tx_one;
897 assert!(!slot.mark_warm_with_transaction_id(tx_one));
899 }
900
901 #[test]
902 fn test_account_with_warm_mark() {
903 let cold_account = Account::default().with_cold_mark();
905 assert!(cold_account.status.contains(AccountStatus::Cold));
906
907 let (warm_account, was_cold) = cold_account.with_warm_mark(TransactionId::ZERO);
909
910 assert!(!warm_account.status.contains(AccountStatus::Cold));
912 assert!(was_cold);
913
914 let (still_warm_account, was_cold) = warm_account.with_warm_mark(TransactionId::ZERO);
916 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
917 assert!(!was_cold);
918 }
919
920 #[test]
921 fn test_account_with_warm() {
922 let cold_account = Account::default().with_cold_mark();
924 assert!(cold_account.status.contains(AccountStatus::Cold));
925
926 let warm_account = cold_account.with_warm(TransactionId::ZERO);
928
929 assert!(!warm_account.status.contains(AccountStatus::Cold));
931 }
932
933 #[test]
934 fn test_account_builder_chaining() {
935 let info = AccountInfo {
936 nonce: 5,
937 ..AccountInfo::default()
938 };
939
940 let slot_key = StorageKey::from(42);
941 let slot_value = EvmStorageSlot::new(StorageValue::from(123), TransactionId::ZERO);
942 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
943 storage.insert(slot_key, slot_value.clone());
944
945 let account = Account::default()
947 .with_info(info.clone())
948 .with_storage(storage.into_iter())
949 .with_created_mark()
950 .with_touched_mark()
951 .with_cold_mark()
952 .with_warm(TransactionId::ZERO);
953
954 assert_eq!(account.info, info);
956 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
957 assert!(account.is_created());
958 assert!(account.is_touched());
959 assert!(!account.status.contains(AccountStatus::Cold));
960 }
961
962 #[test]
963 fn test_account_is_cold_transaction_id() {
964 let tx_zero = TransactionId::ZERO;
965 let tx_one = TransactionId::new(1).unwrap();
966 let mut account = Account::default();
967 assert!(!account.is_cold_transaction_id(tx_zero));
969
970 assert!(account.is_cold_transaction_id(tx_one));
972 account.mark_cold();
973 assert!(account.is_cold_transaction_id(tx_zero));
974 assert!(account.is_cold_transaction_id(tx_one));
975 }
976}