1#![cfg_attr(not(test), warn(unused_crate_dependencies))]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5mod account_info;
6mod types;
7pub use bytecode;
8
9pub use account_info::AccountInfo;
10pub use bytecode::Bytecode;
11pub use primitives;
12pub use types::{EvmState, EvmStorage, TransientStorage};
13
14use bitflags::bitflags;
15use primitives::hardfork::SpecId;
16use primitives::{HashMap, StorageKey, StorageValue, U256};
17
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21pub struct Account {
22 pub info: AccountInfo,
24 pub transaction_id: usize,
26 pub storage: EvmStorage,
28 pub status: AccountStatus,
30}
31
32impl Account {
33 pub fn new_not_existing(transaction_id: usize) -> Self {
35 Self {
36 info: AccountInfo::default(),
37 storage: HashMap::default(),
38 transaction_id,
39 status: AccountStatus::LoadedAsNotExisting,
40 }
41 }
42
43 #[inline]
49 pub fn caller_initial_modification(&mut self, new_balance: U256, is_call: bool) -> U256 {
50 self.mark_touch();
52
53 if is_call {
54 self.info.nonce = self.info.nonce.saturating_add(1);
56 }
57
58 core::mem::replace(&mut self.info.balance, new_balance)
59 }
60
61 #[inline]
63 pub fn state_clear_aware_is_empty(&self, spec: SpecId) -> bool {
64 if SpecId::is_enabled_in(spec, SpecId::SPURIOUS_DRAGON) {
65 self.is_empty()
66 } else {
67 self.is_loaded_as_not_existing_not_touched()
68 }
69 }
70
71 #[inline]
73 pub fn mark_selfdestruct(&mut self) {
74 self.status |= AccountStatus::SelfDestructed;
75 }
76
77 #[inline]
79 pub fn unmark_selfdestruct(&mut self) {
80 self.status -= AccountStatus::SelfDestructed;
81 }
82
83 #[inline]
85 pub fn is_selfdestructed(&self) -> bool {
86 self.status.contains(AccountStatus::SelfDestructed)
87 }
88
89 #[inline]
91 pub fn mark_touch(&mut self) {
92 self.status |= AccountStatus::Touched;
93 }
94
95 #[inline]
97 pub fn unmark_touch(&mut self) {
98 self.status -= AccountStatus::Touched;
99 }
100
101 #[inline]
103 pub fn is_touched(&self) -> bool {
104 self.status.contains(AccountStatus::Touched)
105 }
106
107 #[inline]
109 pub fn mark_created(&mut self) {
110 self.status |= AccountStatus::Created;
111 }
112
113 #[inline]
115 pub fn unmark_created(&mut self) {
116 self.status -= AccountStatus::Created;
117 }
118
119 #[inline]
121 pub fn mark_cold(&mut self) {
122 self.status |= AccountStatus::Cold;
123 }
124
125 #[inline]
127 pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool {
128 self.transaction_id != transaction_id || self.status.contains(AccountStatus::Cold)
129 }
130
131 #[inline]
133 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool {
134 let is_cold = self.is_cold_transaction_id(transaction_id);
135 self.status -= AccountStatus::Cold;
136 self.transaction_id = transaction_id;
137 is_cold
138 }
139
140 #[inline]
142 pub fn is_created_locally(&self) -> bool {
143 self.status.contains(AccountStatus::CreatedLocal)
144 }
145
146 #[inline]
148 pub fn is_selfdestructed_locally(&self) -> bool {
149 self.status.contains(AccountStatus::SelfDestructedLocal)
150 }
151
152 #[inline]
154 pub fn selfdestruct(&mut self) {
155 self.storage.clear();
156 self.info = AccountInfo::default();
157 }
158
159 #[inline]
163 pub fn mark_created_locally(&mut self) -> bool {
164 self.status |= AccountStatus::CreatedLocal;
165 let is_created_globaly = !self.status.contains(AccountStatus::Created);
166 self.status |= AccountStatus::Created;
167 is_created_globaly
168 }
169
170 #[inline]
172 pub fn unmark_created_locally(&mut self) {
173 self.status -= AccountStatus::CreatedLocal;
174 }
175
176 #[inline]
178 pub fn mark_selfdestructed_locally(&mut self) -> bool {
179 self.status |= AccountStatus::SelfDestructedLocal;
180 let is_global_selfdestructed = !self.status.contains(AccountStatus::SelfDestructed);
181 self.status |= AccountStatus::SelfDestructed;
182 is_global_selfdestructed
183 }
184
185 #[inline]
187 pub fn unmark_selfdestructed_locally(&mut self) {
188 self.status -= AccountStatus::SelfDestructedLocal;
189 }
190
191 pub fn is_loaded_as_not_existing(&self) -> bool {
196 self.status.contains(AccountStatus::LoadedAsNotExisting)
197 }
198
199 pub fn is_loaded_as_not_existing_not_touched(&self) -> bool {
201 self.is_loaded_as_not_existing() && !self.is_touched()
202 }
203
204 pub fn is_created(&self) -> bool {
206 self.status.contains(AccountStatus::Created)
207 }
208
209 pub fn is_empty(&self) -> bool {
211 self.info.is_empty()
212 }
213
214 pub fn changed_storage_slots(&self) -> impl Iterator<Item = (&StorageKey, &EvmStorageSlot)> {
218 self.storage.iter().filter(|(_, slot)| slot.is_changed())
219 }
220
221 pub fn with_info(mut self, info: AccountInfo) -> Self {
223 self.info = info;
224 self
225 }
226
227 pub fn with_storage<I>(mut self, storage_iter: I) -> Self
229 where
230 I: Iterator<Item = (StorageKey, EvmStorageSlot)>,
231 {
232 for (key, slot) in storage_iter {
233 self.storage.insert(key, slot);
234 }
235 self
236 }
237
238 pub fn with_selfdestruct_mark(mut self) -> Self {
240 self.mark_selfdestruct();
241 self
242 }
243
244 pub fn with_touched_mark(mut self) -> Self {
246 self.mark_touch();
247 self
248 }
249
250 pub fn with_created_mark(mut self) -> Self {
252 self.mark_created();
253 self
254 }
255
256 pub fn with_cold_mark(mut self) -> Self {
258 self.mark_cold();
259 self
260 }
261
262 pub fn with_warm_mark(mut self, transaction_id: usize) -> (Self, bool) {
265 let was_cold = self.mark_warm_with_transaction_id(transaction_id);
266 (self, was_cold)
267 }
268
269 pub fn with_warm(mut self, transaction_id: usize) -> Self {
271 self.mark_warm_with_transaction_id(transaction_id);
272 self
273 }
274}
275
276impl From<AccountInfo> for Account {
277 fn from(info: AccountInfo) -> Self {
278 Self {
279 info,
280 storage: HashMap::default(),
281 transaction_id: 0,
282 status: AccountStatus::empty(),
283 }
284 }
285}
286
287bitflags! {
289 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
321 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322 #[cfg_attr(feature = "serde", serde(transparent))]
323 pub struct AccountStatus: u8 {
324 const Created = 0b00000001;
327 const CreatedLocal = 0b10000000;
329 const SelfDestructed = 0b00000010;
331 const SelfDestructedLocal = 0b01000000;
333 const Touched = 0b00000100;
337 const LoadedAsNotExisting = 0b00001000;
340 const Cold = 0b00010000;
343 }
344}
345
346impl Default for AccountStatus {
347 fn default() -> Self {
348 AccountStatus::empty()
349 }
350}
351
352#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
354#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
355pub struct EvmStorageSlot {
356 pub original_value: StorageValue,
358 pub present_value: StorageValue,
360 pub transaction_id: usize,
362 pub is_cold: bool,
364}
365
366impl EvmStorageSlot {
367 pub fn new(original: StorageValue, transaction_id: usize) -> Self {
369 Self {
370 original_value: original,
371 present_value: original,
372 transaction_id,
373 is_cold: false,
374 }
375 }
376
377 pub fn new_changed(
379 original_value: StorageValue,
380 present_value: StorageValue,
381 transaction_id: usize,
382 ) -> Self {
383 Self {
384 original_value,
385 present_value,
386 transaction_id,
387 is_cold: false,
388 }
389 }
390 pub fn is_changed(&self) -> bool {
392 self.original_value != self.present_value
393 }
394
395 #[inline]
397 pub fn original_value(&self) -> StorageValue {
398 self.original_value
399 }
400
401 #[inline]
403 pub fn present_value(&self) -> StorageValue {
404 self.present_value
405 }
406
407 #[inline]
409 pub fn mark_cold(&mut self) {
410 self.is_cold = true;
411 }
412
413 #[inline]
415 pub fn is_cold_transaction_id(&self, transaction_id: usize) -> bool {
416 self.transaction_id != transaction_id || self.is_cold
417 }
418
419 #[inline]
424 pub fn mark_warm_with_transaction_id(&mut self, transaction_id: usize) -> bool {
425 let is_cold = self.is_cold_transaction_id(transaction_id);
426 self.transaction_id = transaction_id;
427 self.is_cold = false;
428 is_cold
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435 use crate::EvmStorageSlot;
436 use primitives::{StorageKey, KECCAK_EMPTY, U256};
437
438 #[test]
439 fn account_is_empty_balance() {
440 let mut account = Account::default();
441 assert!(account.is_empty());
442
443 account.info.balance = U256::from(1);
444 assert!(!account.is_empty());
445
446 account.info.balance = U256::ZERO;
447 assert!(account.is_empty());
448 }
449
450 #[test]
451 fn account_is_empty_nonce() {
452 let mut account = Account::default();
453 assert!(account.is_empty());
454
455 account.info.nonce = 1;
456 assert!(!account.is_empty());
457
458 account.info.nonce = 0;
459 assert!(account.is_empty());
460 }
461
462 #[test]
463 fn account_is_empty_code_hash() {
464 let mut account = Account::default();
465 assert!(account.is_empty());
466
467 account.info.code_hash = [1; 32].into();
468 assert!(!account.is_empty());
469
470 account.info.code_hash = [0; 32].into();
471 assert!(account.is_empty());
472
473 account.info.code_hash = KECCAK_EMPTY;
474 assert!(account.is_empty());
475 }
476
477 #[test]
478 fn account_state() {
479 let mut account = Account::default();
480
481 assert!(!account.is_touched());
482 assert!(!account.is_selfdestructed());
483
484 account.mark_touch();
485 assert!(account.is_touched());
486 assert!(!account.is_selfdestructed());
487
488 account.mark_selfdestruct();
489 assert!(account.is_touched());
490 assert!(account.is_selfdestructed());
491
492 account.unmark_selfdestruct();
493 assert!(account.is_touched());
494 assert!(!account.is_selfdestructed());
495 }
496
497 #[test]
498 fn account_is_cold() {
499 let mut account = Account::default();
500
501 assert!(!account.status.contains(crate::AccountStatus::Cold));
503
504 assert!(!account.mark_warm_with_transaction_id(0));
506
507 account.mark_cold();
509
510 assert!(account.status.contains(crate::AccountStatus::Cold));
512
513 assert!(account.mark_warm_with_transaction_id(0));
515 }
516
517 #[test]
518 fn test_account_with_info() {
519 let info = AccountInfo::default();
520 let account = Account::default().with_info(info.clone());
521
522 assert_eq!(account.info, info);
523 assert_eq!(account.storage, HashMap::default());
524 assert_eq!(account.status, AccountStatus::empty());
525 }
526
527 #[test]
528 fn test_account_with_storage() {
529 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
530 let key1 = StorageKey::from(1);
531 let key2 = StorageKey::from(2);
532 let slot1 = EvmStorageSlot::new(StorageValue::from(10), 0);
533 let slot2 = EvmStorageSlot::new(StorageValue::from(20), 0);
534
535 storage.insert(key1, slot1.clone());
536 storage.insert(key2, slot2.clone());
537
538 let account = Account::default().with_storage(storage.clone().into_iter());
539
540 assert_eq!(account.storage.len(), 2);
541 assert_eq!(account.storage.get(&key1), Some(&slot1));
542 assert_eq!(account.storage.get(&key2), Some(&slot2));
543 }
544
545 #[test]
546 fn test_account_with_selfdestruct_mark() {
547 let account = Account::default().with_selfdestruct_mark();
548
549 assert!(account.is_selfdestructed());
550 assert!(!account.is_touched());
551 assert!(!account.is_created());
552 }
553
554 #[test]
555 fn test_account_with_touched_mark() {
556 let account = Account::default().with_touched_mark();
557
558 assert!(!account.is_selfdestructed());
559 assert!(account.is_touched());
560 assert!(!account.is_created());
561 }
562
563 #[test]
564 fn test_account_with_created_mark() {
565 let account = Account::default().with_created_mark();
566
567 assert!(!account.is_selfdestructed());
568 assert!(!account.is_touched());
569 assert!(account.is_created());
570 }
571
572 #[test]
573 fn test_account_with_cold_mark() {
574 let account = Account::default().with_cold_mark();
575
576 assert!(account.status.contains(AccountStatus::Cold));
577 }
578
579 #[test]
580 fn test_storage_mark_warm_with_transaction_id() {
581 let mut slot = EvmStorageSlot::new(U256::ZERO, 0);
582 slot.is_cold = true;
583 slot.transaction_id = 0;
584 assert!(slot.mark_warm_with_transaction_id(1));
585
586 slot.is_cold = false;
587 slot.transaction_id = 0;
588 assert!(slot.mark_warm_with_transaction_id(1));
589
590 slot.is_cold = true;
591 slot.transaction_id = 1;
592 assert!(slot.mark_warm_with_transaction_id(1));
593
594 slot.is_cold = false;
595 slot.transaction_id = 1;
596 assert!(!slot.mark_warm_with_transaction_id(1));
598 }
599
600 #[test]
601 fn test_account_with_warm_mark() {
602 let cold_account = Account::default().with_cold_mark();
604 assert!(cold_account.status.contains(AccountStatus::Cold));
605
606 let (warm_account, was_cold) = cold_account.with_warm_mark(0);
608
609 assert!(!warm_account.status.contains(AccountStatus::Cold));
611 assert!(was_cold);
612
613 let (still_warm_account, was_cold) = warm_account.with_warm_mark(0);
615 assert!(!still_warm_account.status.contains(AccountStatus::Cold));
616 assert!(!was_cold);
617 }
618
619 #[test]
620 fn test_account_with_warm() {
621 let cold_account = Account::default().with_cold_mark();
623 assert!(cold_account.status.contains(AccountStatus::Cold));
624
625 let warm_account = cold_account.with_warm(0);
627
628 assert!(!warm_account.status.contains(AccountStatus::Cold));
630 }
631
632 #[test]
633 fn test_account_builder_chaining() {
634 let info = AccountInfo {
635 nonce: 5,
636 ..AccountInfo::default()
637 };
638
639 let slot_key = StorageKey::from(42);
640 let slot_value = EvmStorageSlot::new(StorageValue::from(123), 0);
641 let mut storage = HashMap::<StorageKey, EvmStorageSlot>::default();
642 storage.insert(slot_key, slot_value.clone());
643
644 let account = Account::default()
646 .with_info(info.clone())
647 .with_storage(storage.into_iter())
648 .with_created_mark()
649 .with_touched_mark()
650 .with_cold_mark()
651 .with_warm(0);
652
653 assert_eq!(account.info, info);
655 assert_eq!(account.storage.get(&slot_key), Some(&slot_value));
656 assert!(account.is_created());
657 assert!(account.is_touched());
658 assert!(!account.status.contains(AccountStatus::Cold));
659 }
660
661 #[test]
662 fn test_account_is_cold_transaction_id() {
663 let mut account = Account::default();
664 assert!(!account.is_cold_transaction_id(0));
666
667 assert!(account.is_cold_transaction_id(1));
669 account.mark_cold();
670 assert!(account.is_cold_transaction_id(0));
671 assert!(account.is_cold_transaction_id(1));
672 }
673}