revm_database/states/
transition_account.rs1use std::borrow::Cow;
2
3use super::{AccountRevert, AccountStatus, BundleAccount, StorageSlot, StorageWithOriginalValues};
4use bytecode::Bytecode;
5use either::Either;
6use primitives::{hash_map, B256, U256};
7use state::{AccountInfo, EvmStorage};
8
9#[derive(Clone, Debug, PartialEq, Eq, Default)]
15pub struct TransitionAccount<S = StorageWithOriginalValues> {
16 pub info: Option<AccountInfo>,
18 pub status: AccountStatus,
20 pub previous_info: Option<AccountInfo>,
24 pub previous_status: AccountStatus,
26 pub storage: S,
28 pub storage_was_destroyed: bool,
34}
35
36impl TransitionAccount {
37 pub fn new_empty_eip161(storage: StorageWithOriginalValues) -> Self {
39 Self {
40 info: Some(AccountInfo::default()),
41 status: AccountStatus::InMemoryChange,
42 previous_info: None,
43 previous_status: AccountStatus::LoadedNotExisting,
44 storage,
45 storage_was_destroyed: false,
46 }
47 }
48
49 pub fn has_new_contract(&self) -> Option<(B256, &Bytecode)> {
51 let present_new_codehash = self.info.as_ref().map(|info| &info.code_hash);
52 let previous_codehash = self.previous_info.as_ref().map(|info| &info.code_hash);
53 if present_new_codehash != previous_codehash {
54 return self
55 .info
56 .as_ref()
57 .and_then(|info| info.code.as_ref().map(|c| (info.code_hash, c)));
58 }
59 None
60 }
61
62 pub fn previous_balance(&self) -> U256 {
64 self.previous_info
65 .as_ref()
66 .map(|info| info.balance)
67 .unwrap_or_default()
68 }
69
70 pub fn current_balance(&self) -> U256 {
72 self.info
73 .as_ref()
74 .map(|info| info.balance)
75 .unwrap_or_default()
76 }
77
78 pub fn update(&mut self, other: TransitionAccount<Option<Cow<'_, EvmStorage>>>) {
81 self.info = other.info;
82 self.status = other.status;
83
84 let storage = other.storage.into_iter().flat_map(|storage| match storage {
85 Cow::Borrowed(storage) => Either::Left(
86 storage
87 .iter()
88 .map(|(k, v)| (Cow::Borrowed(k), Cow::Borrowed(v))),
89 ),
90 Cow::Owned(storage) => Either::Right(
91 storage
92 .into_iter()
93 .map(|(k, v)| (Cow::Owned(k), Cow::Owned(v))),
94 ),
95 });
96
97 if matches!(
100 other.status,
101 AccountStatus::Destroyed | AccountStatus::DestroyedAgain
102 ) {
103 self.storage = storage
104 .filter_map(|(key, slot)| {
105 slot.is_changed().then_some((
106 *key,
107 StorageSlot::new_changed(slot.original_value, slot.present_value),
108 ))
109 })
110 .collect();
111 self.storage_was_destroyed = true;
112 } else {
113 for (key, slot) in storage {
115 if !slot.is_changed() {
116 continue;
117 }
118 let slot = StorageSlot::new_changed(slot.original_value, slot.present_value);
119 match self.storage.entry(*key) {
120 hash_map::Entry::Vacant(entry) => {
121 entry.insert(slot);
122 }
123 hash_map::Entry::Occupied(mut entry) => {
124 let value = entry.get_mut();
125 if value.original_value() == slot.present_value() {
127 entry.remove();
128 } else {
129 value.present_value = slot.present_value;
131 }
132 }
133 }
134 }
135 }
136 }
137
138 pub fn create_revert(self) -> Option<AccountRevert> {
140 let mut previous_account = self.original_bundle_account();
141 previous_account.update_and_create_revert(self)
142 }
143
144 pub fn present_bundle_account(&self) -> BundleAccount {
146 BundleAccount {
147 info: self.info.clone(),
148 original_info: self.previous_info.clone(),
149 storage: self.storage.clone(),
150 status: self.status,
151 }
152 }
153
154 fn original_bundle_account(&self) -> BundleAccount {
156 BundleAccount {
157 info: self.previous_info.clone(),
158 original_info: self.previous_info.clone(),
159 storage: StorageWithOriginalValues::default(),
160 status: self.previous_status,
161 }
162 }
163}
164
165impl<S> TransitionAccount<S> {
166 pub fn map_storage<F, N>(self, f: F) -> TransitionAccount<N>
168 where
169 F: FnOnce(S) -> N,
170 {
171 let Self {
172 info,
173 status,
174 previous_info,
175 previous_status,
176 storage,
177 storage_was_destroyed,
178 } = self;
179 TransitionAccount {
180 info,
181 status,
182 previous_info,
183 previous_status,
184 storage: f(storage),
185 storage_was_destroyed,
186 }
187 }
188}