1use super::{AccountRevert, BundleAccount, StorageWithOriginalValues};
2use crate::db::AccountStatus;
3use revm_interpreter::primitives::{hash_map, AccountInfo, Bytecode, B256, I256, U256};
45/// Account Created when EVM state is merged to cache state.
6/// And it is sent to Block state.
7///
8/// It is used when block state gets merged to bundle state to
9/// create needed Reverts.
10#[derive(Clone, Debug, PartialEq, Eq, Default)]
11pub struct TransitionAccount {
12pub info: Option<AccountInfo>,
13pub status: AccountStatus,
14/// Previous account info is needed for account that got initially loaded.
15 /// Initially loaded account are not present inside bundle and are needed
16 /// to generate Reverts.
17pub previous_info: Option<AccountInfo>,
18/// Mostly needed when previous status Loaded/LoadedEmpty.
19pub previous_status: AccountStatus,
20/// Storage contains both old and new account
21pub storage: StorageWithOriginalValues,
22/// If there is transition that clears the storage we should mark it here and
23 /// delete all storages in BundleState. This flag is needed if we have transition
24 /// between Destroyed states from DestroyedChanged-> DestroyedAgain-> DestroyedChanged
25 /// in the end transition that we would have would be `DestroyedChanged->DestroyedChanged`
26 /// and with only that info we couldn't decide what to do.
27pub storage_was_destroyed: bool,
28}
2930impl TransitionAccount {
31/// Create new LoadedEmpty account.
32pub fn new_empty_eip161(storage: StorageWithOriginalValues) -> Self {
33Self {
34 info: Some(AccountInfo::default()),
35 status: AccountStatus::InMemoryChange,
36 previous_info: None,
37 previous_status: AccountStatus::LoadedNotExisting,
38 storage,
39 storage_was_destroyed: false,
40 }
41 }
4243/// Return new contract bytecode if it is changed or newly created.
44pub fn has_new_contract(&self) -> Option<(B256, &Bytecode)> {
45let present_new_codehash = self.info.as_ref().map(|info| &info.code_hash);
46let previous_codehash = self.previous_info.as_ref().map(|info| &info.code_hash);
47if present_new_codehash != previous_codehash {
48return self
49.info
50 .as_ref()
51 .and_then(|info| info.code.as_ref().map(|c| (info.code_hash, c)));
52 }
53None
54}
5556/// Return the balance of account before transition.
57pub fn previous_balance(&self) -> U256 {
58self.previous_info
59 .as_ref()
60 .map(|info| info.balance)
61 .unwrap_or_default()
62 }
6364/// Return the balance of account after transition.
65pub fn current_balance(&self) -> U256 {
66self.info
67 .as_ref()
68 .map(|info| info.balance)
69 .unwrap_or_default()
70 }
7172/// Calculate the change in account's balance for this transition.
73 /// Returns `None` if delta does not fit in [I256].
74pub fn balance_delta(&self) -> Option<I256> {
75let previous_balance = self.previous_balance();
76let current_balance = self.current_balance();
77let delta = I256::try_from(previous_balance.abs_diff(current_balance)).ok()?;
78if current_balance >= previous_balance {
79Some(delta)
80 } else {
81 delta.checked_neg()
82 }
83 }
8485/// Update new values of transition. Don't override old values.
86 /// Both account info and old storages need to be left intact.
87pub fn update(&mut self, other: Self) {
88self.info.clone_from(&other.info);
89self.status = other.status;
9091// if transition is from some to destroyed drop the storage.
92 // This need to be done here as it is one increment of the state.
93if matches!(
94 other.status,
95 AccountStatus::Destroyed | AccountStatus::DestroyedAgain
96 ) {
97self.storage = other.storage;
98self.storage_was_destroyed = true;
99 } else {
100// update changed values to this transition.
101for (key, slot) in other.storage.into_iter() {
102match self.storage.entry(key) {
103 hash_map::Entry::Vacant(entry) => {
104 entry.insert(slot);
105 }
106 hash_map::Entry::Occupied(mut entry) => {
107let value = entry.get_mut();
108// if new value is same as original value. Remove storage entry.
109if value.original_value() == slot.present_value() {
110 entry.remove();
111 } else {
112// if value is different, update transition present value;
113value.present_value = slot.present_value;
114 }
115 }
116 }
117 }
118 }
119 }
120121/// Consume Self and create account revert from it.
122pub fn create_revert(self) -> Option<AccountRevert> {
123let mut previous_account = self.original_bundle_account();
124 previous_account.update_and_create_revert(self)
125 }
126127/// Present bundle account
128pub fn present_bundle_account(&self) -> BundleAccount {
129 BundleAccount {
130 info: self.info.clone(),
131 original_info: self.previous_info.clone(),
132 storage: self.storage.clone(),
133 status: self.status,
134 }
135 }
136137/// Original bundle account
138fn original_bundle_account(&self) -> BundleAccount {
139 BundleAccount {
140 info: self.previous_info.clone(),
141 original_info: self.previous_info.clone(),
142 storage: StorageWithOriginalValues::default(),
143 status: self.previous_status,
144 }
145 }
146}