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