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 {
437 info: AccountInfo,
438 transaction_id: TransactionId,
439 storage: EvmStorage,
440 status: AccountStatus,
441 #[serde(default)]
442 original_info: MaybeOriginalInfo,
443 }
444
445 impl<'de> Deserialize<'de> for super::Account {
446 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
447 where
448 D: serde::Deserializer<'de>,
449 {
450 let AccountSerde {
451 info,
452 original_info,
453 storage,
454 transaction_id,
455 status,
456 } = Deserialize::deserialize(deserializer)?;
457
458 let original_info = match original_info {
459 MaybeOriginalInfo::Missing => Some(Box::new(info.clone())),
461 MaybeOriginalInfo::Present(None) => None,
463 MaybeOriginalInfo::Present(Some(oi)) => Some(Box::new(oi)),
465 };
466
467 Ok(Account {
468 info,
469 original_info,
470 storage,
471 transaction_id,
472 status,
473 })
474 }
475 }
476}
477
478bitflags! {
480 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
512 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
513 #[cfg_attr(feature = "serde", serde(transparent))]
514 pub struct AccountStatus: u8 {
515 const Created = 0b00000001;
518 const CreatedLocal = 0b10000000;
520 const SelfDestructed = 0b00000010;
522 const SelfDestructedLocal = 0b01000000;
524 const Touched = 0b00000100;
528 const LoadedAsNotExisting = 0b00001000;
531 const Cold = 0b00010000;
534 }
535}
536
537impl AccountStatus {
538 #[inline]
540 pub const fn is_touched(&self) -> bool {
541 self.contains(AccountStatus::Touched)
542 }
543}
544
545impl Default for AccountStatus {
546 fn default() -> Self {
547 AccountStatus::empty()
548 }
549}
550
551#[derive(Debug, Clone, Default, PartialEq, Eq)]
553#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
554pub struct EvmStorageSlot {
555 pub original_value: StorageValue,
557 pub present_value: StorageValue,
559 pub transaction_id: TransactionId,
561 pub is_cold: bool,
563}
564
565impl EvmStorageSlot {
566 pub const fn new(original: StorageValue, transaction_id: TransactionId) -> Self {
568 Self {
569 original_value: original,
570 present_value: original,
571 transaction_id,
572 is_cold: false,
573 }
574 }
575
576 pub const fn new_changed(
578 original_value: StorageValue,
579 present_value: StorageValue,
580 transaction_id: TransactionId,
581 ) -> Self {
582 Self {
583 original_value,
584 present_value,
585 transaction_id,
586 is_cold: false,
587 }
588 }
589 pub fn is_changed(&self) -> bool {
591 self.original_value != self.present_value
592 }
593
594 #[inline]
596 pub const fn original_value(&self) -> StorageValue {
597 self.original_value
598 }
599
600 #[inline]
602 pub const fn present_value(&self) -> StorageValue {
603 self.present_value
604 }
605
606 #[inline]
608 pub const fn mark_cold(&mut self) {
609 self.is_cold = true;
610 }
611
612 #[inline]
614 pub const fn is_cold_transaction_id(&self, transaction_id: TransactionId) -> bool {
615 self.transaction_id.get() != transaction_id.get() || self.is_cold
616 }
617
618 #[inline]
623 pub const fn mark_warm_with_transaction_id(&mut self, transaction_id: TransactionId) -> bool {
624 let is_cold = self.is_cold_transaction_id(transaction_id);
625 if self.transaction_id.get() != transaction_id.get() {
631 self.original_value = self.present_value;
632 }
633 self.transaction_id = transaction_id;
634 self.is_cold = false;
635 is_cold
636 }
637}
638
639#[cfg(test)]
640mod tests {
641 use super::*;
642 use crate::EvmStorageSlot;
643 use primitives::{StorageKey, KECCAK_EMPTY, U256};
644
645 #[test]
646 fn account_is_empty_balance() {
647 let mut account = Account::default();
648 assert!(account.is_empty());
649
650 account.info.balance = U256::from(1);
651 assert!(!account.is_empty());
652
653 account.info.balance = U256::ZERO;
654 assert!(account.is_empty());
655 }
656
657 #[test]
658 fn account_is_empty_nonce() {
659 let mut account = Account::default();
660 assert!(account.is_empty());
661
662 account.info.nonce = 1;
663 assert!(!account.is_empty());
664
665 account.info.nonce = 0;
666 assert!(account.is_empty());
667 }
668
669 #[test]
670 fn account_is_empty_code_hash() {
671 let mut account = Account::default();
672 assert!(account.is_empty());
673
674 account.info.code_hash = [1; 32].into();
675 assert!(!account.is_empty());
676
677 account.info.code_hash = [0; 32].into();
678 assert!(account.is_empty());
679
680 account.info.code_hash = KECCAK_EMPTY;
681 assert!(account.is_empty());
682 }
683
684 #[test]
685 fn account_state() {
686 let mut account = Account::default();
687
688 assert!(!account.is_touched());
689 assert!(!account.is_selfdestructed());
690
691 account.mark_touch();
692 assert!(account.is_touched());
693 assert!(!account.is_selfdestructed());
694
695 account.mark_selfdestruct();
696 assert!(account.is_touched());
697 assert!(account.is_selfdestructed());
698
699 account.unmark_selfdestruct();
700 assert!(account.is_touched());
701 assert!(!account.is_selfdestructed());
702 }
703
704 #[test]
705 fn account_is_cold() {
706 let mut account = Account::default();
707
708 assert!(!account.status.contains(crate::AccountStatus::Cold));
710
711 assert!(!account.mark_warm_with_transaction_id(TransactionId::ZERO));
713
714 account.mark_cold();
716
717 assert!(account.status.contains(crate::AccountStatus::Cold));
719
720 assert!(account.mark_warm_with_transaction_id(TransactionId::ZERO));
722 }
723
724 #[test]
725 fn test_account_with_info() {
726 let info = AccountInfo::default();
727 let account = Account::default().with_info(info.clone());
728
729 assert_eq!(account.info, info);
730 assert_eq!(account.storage, HashMap::default());
731 assert_eq!(account.status, AccountStatus::empty());
732 }
733
734 #[test]
735 fn test_account_with_storage() {
736 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
737 let key1 = StorageKey::from(1);
738 let key2 = StorageKey::from(2);
739 let slot1 = EvmStorageSlot::new(StorageValue::from(10), TransactionId::ZERO);
740 let slot2 = EvmStorageSlot::new(StorageValue::from(20), TransactionId::ZERO);
741
742 storage.insert(key1, slot1.clone());
743 storage.insert(key2, slot2.clone());
744
745 let account = Account::default().with_storage(storage.clone().into_iter());
746
747 assert_eq!(account.storage.len(), 2);
748 assert_eq!(account.storage.get(&key1), Some(&slot1));
749 assert_eq!(account.storage.get(&key2), Some(&slot2));
750 }
751
752 #[test]
753 fn test_account_with_selfdestruct_mark() {
754 let account = Account::default().with_selfdestruct_mark();
755
756 assert!(account.is_selfdestructed());
757 assert!(!account.is_touched());
758 assert!(!account.is_created());
759 }
760
761 #[test]
762 #[cfg(feature = "serde")]
763 fn test_account_serialize_deserialize() {
764 let account = Account::default().with_selfdestruct_mark();
765 let serialized = serde_json::to_string(&account).unwrap();
766 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
767 assert_eq!(account, deserialized);
768 }
769
770 #[test]
771 #[cfg(feature = "serde")]
772 fn test_account_postcard_round_trip() {
773 let mut account = Account::from(AccountInfo {
778 nonce: 5,
779 ..AccountInfo::default()
780 });
781 account.info.nonce = 7;
782 account.transaction_id = TransactionId::new(3).unwrap();
783 account.storage.insert(
784 StorageKey::from(42u64),
785 EvmStorageSlot {
786 original_value: U256::from(1u64),
787 present_value: U256::from(2u64),
788 transaction_id: account.transaction_id,
789 is_cold: false,
790 },
791 );
792 account.status = AccountStatus::Touched;
793
794 let bytes = postcard::to_allocvec(&account).unwrap();
795 let decoded: Account = postcard::from_bytes(&bytes).unwrap();
796
797 assert_eq!(account, decoded);
798 }
799
800 #[test]
801 #[cfg(feature = "serde")]
802 fn test_account_original_info_none_roundtrip() {
803 let account = Account::new_not_existing(TransactionId::new(2).unwrap());
804 assert!(account.original_info.is_none());
805 let serialized = serde_json::to_string(&account).unwrap();
806 let deserialized: Account = serde_json::from_str(&serialized).unwrap();
807 assert!(deserialized.original_info.is_none());
808 assert_eq!(account, deserialized);
809 }
810
811 #[test]
812 #[cfg(feature = "serde")]
813 fn test_account_deserialize_original_info_missing_null_present() {
814 let code = r#"{"LegacyAnalyzed":{"bytecode":"0x00","original_len":0,"jump_table":{"order":"bitvec::order::Lsb0","head":{"width":8,"index":0},"bits":0,"data":[]}}}"#;
815 let info = format!(
816 r#"{{"balance":"0x2386f26fc10000","nonce":1,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":{code}}}"#
817 );
818
819 let json =
821 format!(r#"{{"info":{info},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#);
822 let acct: Account = serde_json::from_str(&json).unwrap();
823 assert!(acct.original_info.is_some());
824 assert_eq!(acct.original_info(), acct.info);
825
826 let json = format!(
828 r#"{{"info":{info},"original_info":null,"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
829 );
830 let acct: Account = serde_json::from_str(&json).unwrap();
831 assert!(acct.original_info.is_none());
832
833 let original = r#"{"balance":"0x0","nonce":0,"code_hash":"0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470","code":null}"#;
835 let json = format!(
836 r#"{{"info":{info},"original_info":{original},"transaction_id":0,"storage":{{}},"status":"Touched"}}"#
837 );
838 let acct: Account = serde_json::from_str(&json).unwrap();
839 assert!(acct.original_info.is_some());
840 assert_eq!(acct.original_info().nonce, 0);
841 assert_eq!(acct.original_info().balance, U256::ZERO);
842 }
843
844 #[test]
845 fn test_account_with_touched_mark() {
846 let account = Account::default().with_touched_mark();
847
848 assert!(!account.is_selfdestructed());
849 assert!(account.is_touched());
850 assert!(!account.is_created());
851 }
852
853 #[test]
854 fn test_account_with_created_mark() {
855 let account = Account::default().with_created_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_cold_mark() {
864 let account = Account::default().with_cold_mark();
865
866 assert!(account.status.contains(AccountStatus::Cold));
867 }
868
869 #[test]
870 fn test_storage_mark_warm_with_transaction_id() {
871 let tx_zero = TransactionId::ZERO;
872 let tx_one = TransactionId::new(1).unwrap();
873 let mut slot = EvmStorageSlot::new(U256::ZERO, tx_zero);
874 slot.is_cold = true;
875 slot.transaction_id = tx_zero;
876 assert!(slot.mark_warm_with_transaction_id(tx_one));
877
878 slot.is_cold = false;
879 slot.transaction_id = tx_zero;
880 assert!(slot.mark_warm_with_transaction_id(tx_one));
881
882 slot.is_cold = true;
883 slot.transaction_id = tx_one;
884 assert!(slot.mark_warm_with_transaction_id(tx_one));
885
886 slot.is_cold = false;
887 slot.transaction_id = tx_one;
888 assert!(!slot.mark_warm_with_transaction_id(tx_one));
890 }
891
892 #[test]
893 fn test_account_with_warm_mark() {
894 let cold_account = Account::default().with_cold_mark();
896 assert!(cold_account.status.contains(AccountStatus::Cold));
897
898 let (warm_account, was_cold) = cold_account.with_warm_mark(TransactionId::ZERO);
900
901 assert!(!warm_account.status.contains(AccountStatus::Cold));
903 assert!(was_cold);
904
905 let (still_warm_account, was_cold) = warm_account.with_warm_mark(TransactionId::ZERO);
907 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
908 assert!(!was_cold);
909 }
910
911 #[test]
912 fn test_account_with_warm() {
913 let cold_account = Account::default().with_cold_mark();
915 assert!(cold_account.status.contains(AccountStatus::Cold));
916
917 let warm_account = cold_account.with_warm(TransactionId::ZERO);
919
920 assert!(!warm_account.status.contains(AccountStatus::Cold));
922 }
923
924 #[test]
925 fn test_account_builder_chaining() {
926 let info = AccountInfo {
927 nonce: 5,
928 ..AccountInfo::default()
929 };
930
931 let slot_key = StorageKey::from(42);
932 let slot_value = EvmStorageSlot::new(StorageValue::from(123), TransactionId::ZERO);
933 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
934 storage.insert(slot_key, slot_value.clone());
935
936 let account = Account::default()
938 .with_info(info.clone())
939 .with_storage(storage.into_iter())
940 .with_created_mark()
941 .with_touched_mark()
942 .with_cold_mark()
943 .with_warm(TransactionId::ZERO);
944
945 assert_eq!(account.info, info);
947 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
948 assert!(account.is_created());
949 assert!(account.is_touched());
950 assert!(!account.status.contains(AccountStatus::Cold));
951 }
952
953 #[test]
954 fn test_account_is_cold_transaction_id() {
955 let tx_zero = TransactionId::ZERO;
956 let tx_one = TransactionId::new(1).unwrap();
957 let mut account = Account::default();
958 assert!(!account.is_cold_transaction_id(tx_zero));
960
961 assert!(account.is_cold_transaction_id(tx_one));
963 account.mark_cold();
964 assert!(account.is_cold_transaction_id(tx_zero));
965 assert!(account.is_cold_transaction_id(tx_one));
966 }
967}