revm_database/states/bundle_account.rs
1use super::{
2 reverts::AccountInfoRevert, AccountRevert, AccountStatus, RevertToSlot, StorageSlot,
3 StorageWithOriginalValues, TransitionAccount,
4};
5use primitives::{HashMap, U256};
6use state::AccountInfo;
7
8/// Account information focused on creating of database changesets
9/// and Reverts.
10///
11/// Status is needed as to know from what state we are applying the TransitionAccount.
12///
13/// Original account info is needed to know if there was a change.
14///
15/// Same thing for storage with original value.
16///
17/// On selfdestruct storage original value is ignored.
18#[derive(Clone, Debug, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct BundleAccount {
21 pub info: Option<AccountInfo>,
22 pub original_info: Option<AccountInfo>,
23 /// Contains both original and present state.
24 /// When extracting changeset we compare if original value is different from present value.
25 /// If it is different we add it to changeset.
26 ///
27 /// If Account was destroyed we ignore original value and compare present state with U256::ZERO.
28 pub storage: StorageWithOriginalValues,
29 /// Account status.
30 pub status: AccountStatus,
31}
32
33impl BundleAccount {
34 /// Create new BundleAccount.
35 pub fn new(
36 original_info: Option<AccountInfo>,
37 present_info: Option<AccountInfo>,
38 storage: StorageWithOriginalValues,
39 status: AccountStatus,
40 ) -> Self {
41 Self {
42 info: present_info,
43 original_info,
44 storage,
45 status,
46 }
47 }
48
49 /// The approximate size of changes needed to store this account.
50 ///
51 /// `1 + storage_len`
52 pub fn size_hint(&self) -> usize {
53 1 + self.storage.len()
54 }
55
56 /// Return storage slot if it exists.
57 ///
58 /// In case we know that account is newly created or destroyed, return `Some(U256::ZERO)`
59 pub fn storage_slot(&self, slot: U256) -> Option<U256> {
60 let slot = self.storage.get(&slot).map(|s| s.present_value);
61 if slot.is_some() {
62 slot
63 } else if self.status.is_storage_known() {
64 Some(U256::ZERO)
65 } else {
66 None
67 }
68 }
69
70 /// Fetch account info if it exists.
71 pub fn account_info(&self) -> Option<AccountInfo> {
72 self.info.clone()
73 }
74
75 /// Was this account destroyed.
76 pub fn was_destroyed(&self) -> bool {
77 self.status.was_destroyed()
78 }
79
80 /// Return true of account info was changed.
81 pub fn is_info_changed(&self) -> bool {
82 self.info != self.original_info
83 }
84
85 /// Return true if contract was changed
86 pub fn is_contract_changed(&self) -> bool {
87 self.info.as_ref().map(|a| a.code_hash) != self.original_info.as_ref().map(|a| a.code_hash)
88 }
89
90 /// Revert account to previous state and return true if account can be removed.
91 pub fn revert(&mut self, revert: AccountRevert) -> bool {
92 self.status = revert.previous_status;
93
94 match revert.account {
95 AccountInfoRevert::DoNothing => (),
96 AccountInfoRevert::DeleteIt => {
97 self.info = None;
98 if self.original_info.is_none() {
99 self.storage = HashMap::default();
100 return true;
101 } else {
102 // Set all storage to zero but preserve original values.
103 self.storage.iter_mut().for_each(|(_, v)| {
104 v.present_value = U256::ZERO;
105 });
106 return false;
107 }
108 }
109 AccountInfoRevert::RevertTo(info) => self.info = Some(info),
110 };
111 // Revert storage
112 for (key, slot) in revert.storage {
113 match slot {
114 RevertToSlot::Some(value) => {
115 // Don't overwrite original values if present
116 // if storage is not present set original value as current value.
117 self.storage
118 .entry(key)
119 .or_insert(StorageSlot::new(value))
120 .present_value = value;
121 }
122 RevertToSlot::Destroyed => {
123 // If it was destroyed this means that storage was created and we need to remove it.
124 self.storage.remove(&key);
125 }
126 }
127 }
128 false
129 }
130
131 /// Update to new state and generate AccountRevert that if applied to new state will
132 /// revert it to previous state.
133 ///
134 /// If no revert is present, update is noop.
135 pub fn update_and_create_revert(
136 &mut self,
137 transition: TransitionAccount,
138 ) -> Option<AccountRevert> {
139 let updated_info = transition.info;
140 let updated_storage = transition.storage;
141 let updated_status = transition.status;
142
143 // The helper that extends this storage but preserves original value.
144 let extend_storage =
145 |this_storage: &mut StorageWithOriginalValues,
146 storage_update: StorageWithOriginalValues| {
147 for (key, value) in storage_update {
148 this_storage.entry(key).or_insert(value).present_value = value.present_value;
149 }
150 };
151
152 let previous_storage_from_update =
153 |updated_storage: &StorageWithOriginalValues| -> HashMap<U256, RevertToSlot> {
154 updated_storage
155 .iter()
156 .filter(|s| s.1.is_changed())
157 .map(|(key, value)| {
158 (*key, RevertToSlot::Some(value.previous_or_original_value))
159 })
160 .collect()
161 };
162
163 // Needed for some reverts.
164 let info_revert = if self.info != updated_info {
165 AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default())
166 } else {
167 AccountInfoRevert::DoNothing
168 };
169
170 let account_revert = match updated_status {
171 AccountStatus::Changed => {
172 let previous_storage = previous_storage_from_update(&updated_storage);
173 match self.status {
174 AccountStatus::Changed | AccountStatus::Loaded => {
175 // Extend the storage. original values is not used inside bundle.
176 extend_storage(&mut self.storage, updated_storage);
177 }
178 AccountStatus::LoadedEmptyEIP161 => {
179 // Do nothing.
180 // Only change that can happen from LoadedEmpty to Changed is if balance
181 // is send to account. So we are only checking account change here.
182 }
183 _ => unreachable!("Invalid state transfer to Changed from {self:?}"),
184 };
185 let previous_status = self.status;
186 self.status = AccountStatus::Changed;
187 self.info = updated_info;
188 Some(AccountRevert {
189 account: info_revert,
190 storage: previous_storage,
191 previous_status,
192 wipe_storage: false,
193 })
194 }
195 AccountStatus::InMemoryChange => {
196 let previous_storage = previous_storage_from_update(&updated_storage);
197 let in_memory_info_revert = match self.status {
198 AccountStatus::Loaded | AccountStatus::InMemoryChange => {
199 // From loaded (Or LoadedEmpty) to InMemoryChange can happen if there is balance change
200 // or new created account but Loaded didn't have contract.
201 extend_storage(&mut self.storage, updated_storage);
202 info_revert
203 }
204 AccountStatus::LoadedEmptyEIP161 => {
205 self.storage = updated_storage;
206 info_revert
207 }
208 AccountStatus::LoadedNotExisting => {
209 self.storage = updated_storage;
210 AccountInfoRevert::DeleteIt
211 }
212 _ => unreachable!("Invalid change to InMemoryChange from {self:?}"),
213 };
214 let previous_status = self.status;
215 self.status = AccountStatus::InMemoryChange;
216 self.info = updated_info;
217 Some(AccountRevert {
218 account: in_memory_info_revert,
219 storage: previous_storage,
220 previous_status,
221 wipe_storage: false,
222 })
223 }
224 AccountStatus::Loaded
225 | AccountStatus::LoadedNotExisting
226 | AccountStatus::LoadedEmptyEIP161 => {
227 // No changeset, maybe just update data
228 // Do nothing for now.
229 None
230 }
231 AccountStatus::Destroyed => {
232 // Clear this storage and move it to the Revert.
233 let this_storage = self.storage.drain().collect();
234 let ret = match self.status {
235 AccountStatus::InMemoryChange | AccountStatus::Changed | AccountStatus::Loaded | AccountStatus::LoadedEmptyEIP161 => {
236 Some(AccountRevert::new_selfdestructed(self.status, info_revert, this_storage))
237 }
238 AccountStatus::LoadedNotExisting => {
239 // Do nothing as we have LoadedNotExisting -> Destroyed (It is noop)
240 None
241 }
242 _ => unreachable!("Invalid transition to Destroyed account from: {self:?} to {updated_info:?} {updated_status:?}"),
243 };
244
245 if ret.is_some() {
246 self.status = AccountStatus::Destroyed;
247 self.info = None;
248 }
249
250 // Set present to destroyed.
251 ret
252 }
253 AccountStatus::DestroyedChanged => {
254 // Previous block created account or changed.
255 // (It was destroyed on previous block or one before).
256
257 // Check common pre destroy paths.
258 // If common path is there it will drain the storage.
259 if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
260 info_revert.clone(),
261 self,
262 &updated_storage,
263 ) {
264 // Set to destroyed and revert state.
265 self.status = AccountStatus::DestroyedChanged;
266 self.info = updated_info;
267 self.storage = updated_storage;
268
269 Some(revert_state)
270 } else {
271 let ret = match self.status {
272 AccountStatus::Destroyed | AccountStatus::LoadedNotExisting => {
273 // From destroyed state new account is made
274 Some(AccountRevert {
275 account: AccountInfoRevert::DeleteIt,
276 storage: previous_storage_from_update(&updated_storage),
277 previous_status: self.status,
278 wipe_storage: false,
279 })
280 }
281 AccountStatus::DestroyedChanged => {
282 // Account was destroyed in this transition. So we should clear present storage
283 // and insert it inside revert.
284
285 let previous_storage = if transition.storage_was_destroyed {
286 let mut storage = core::mem::take(&mut self.storage)
287 .into_iter()
288 .map(|t| (t.0, RevertToSlot::Some(t.1.present_value)))
289 .collect::<HashMap<_, _>>();
290 for key in updated_storage.keys() {
291 // As it is not existing inside Destroyed storage this means
292 // that previous values must be zero
293 storage.entry(*key).or_insert(RevertToSlot::Destroyed);
294 }
295 storage
296 } else {
297 previous_storage_from_update(&updated_storage)
298 };
299
300 Some(AccountRevert {
301 account: info_revert,
302 storage: previous_storage,
303 previous_status: AccountStatus::DestroyedChanged,
304 wipe_storage: false,
305 })
306 }
307 AccountStatus::DestroyedAgain => {
308 Some(AccountRevert::new_selfdestructed_again(
309 // Destroyed again will set empty account.
310 AccountStatus::DestroyedAgain,
311 AccountInfoRevert::DeleteIt,
312 HashMap::default(),
313 updated_storage.clone(),
314 ))
315 }
316 _ => unreachable!("Invalid state transfer to DestroyedNew from {self:?}"),
317 };
318 self.status = AccountStatus::DestroyedChanged;
319 self.info = updated_info;
320 // Extends current storage.
321 extend_storage(&mut self.storage, updated_storage);
322
323 ret
324 }
325 }
326 AccountStatus::DestroyedAgain => {
327 // Previous block created account
328 // (It was destroyed on previous block or one before).
329
330 // Check common pre destroy paths.
331 // This will drain the storage if it is common transition.
332 let ret = if let Some(revert_state) = AccountRevert::new_selfdestructed_from_bundle(
333 info_revert,
334 self,
335 &HashMap::default(),
336 ) {
337 Some(revert_state)
338 } else {
339 match self.status {
340 AccountStatus::Destroyed
341 | AccountStatus::DestroyedAgain
342 | AccountStatus::LoadedNotExisting => {
343 // From destroyed to destroyed again is noop
344 //
345 // From DestroyedAgain to DestroyedAgain is noop
346 //
347 // From LoadedNotExisting to DestroyedAgain is noop
348 // as account is destroyed again
349 None
350 }
351 AccountStatus::DestroyedChanged => {
352 // From destroyed changed to destroyed again.
353 Some(AccountRevert::new_selfdestructed_again(
354 // Destroyed again will set empty account.
355 AccountStatus::DestroyedChanged,
356 AccountInfoRevert::RevertTo(self.info.clone().unwrap_or_default()),
357 self.storage.drain().collect(),
358 HashMap::default(),
359 ))
360 }
361 _ => unreachable!("Invalid state to DestroyedAgain from {self:?}"),
362 }
363 };
364 // Set to destroyed and revert state.
365 self.status = AccountStatus::DestroyedAgain;
366 self.info = None;
367 self.storage.clear();
368 ret
369 }
370 };
371
372 account_revert.and_then(|acc| if acc.is_empty() { None } else { Some(acc) })
373 }
374}