revm_database/states/account_status.rs
1/// AccountStatus represents the various states an account can be in after being loaded from the database.
2///
3/// After account get loaded from database it can be in a lot of different states
4/// while we execute multiple transaction and even blocks over account that is in memory.
5/// This structure models all possible states that account can be in.
6///
7/// # Variants
8///
9/// - `LoadedNotExisting`: the account has been loaded but does not exist.
10/// - `Loaded`: the account has been loaded and exists.
11/// - `LoadedEmptyEIP161`: the account is loaded and empty, as per EIP-161.
12/// - `InMemoryChange`: there are changes in the account that exist only in memory.
13/// - `Changed`: the account has been modified.
14/// - `Destroyed`: the account has been destroyed.
15/// - `DestroyedChanged`: the account has been destroyed and then modified.
16/// - `DestroyedAgain`: the account has been destroyed again.
17#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub enum AccountStatus {
20 /// The account has been loaded but does not exist.
21 #[default]
22 LoadedNotExisting,
23 /// The account has been loaded and exists.
24 Loaded,
25 /// The account is loaded and empty, as per EIP-161.
26 LoadedEmptyEIP161,
27 /// There are changes in the account that exist only in memory.
28 InMemoryChange,
29 /// The account has been modified.
30 Changed,
31 /// The account has been destroyed.
32 Destroyed,
33 /// The account has been destroyed and then modified.
34 DestroyedChanged,
35 /// The account has been destroyed again.
36 DestroyedAgain,
37}
38
39impl AccountStatus {
40 /// Account is not modified and just loaded from database.
41 pub fn is_not_modified(&self) -> bool {
42 matches!(
43 self,
44 AccountStatus::LoadedNotExisting
45 | AccountStatus::Loaded
46 | AccountStatus::LoadedEmptyEIP161
47 )
48 }
49
50 /// Account was destroyed by calling SELFDESTRUCT.
51 /// This means that full account and storage are inside memory.
52 pub fn was_destroyed(&self) -> bool {
53 matches!(
54 self,
55 AccountStatus::Destroyed
56 | AccountStatus::DestroyedChanged
57 | AccountStatus::DestroyedAgain
58 )
59 }
60
61 /// This means storage is known, it can be newly created or storage got destroyed.
62 pub fn is_storage_known(&self) -> bool {
63 matches!(
64 self,
65 AccountStatus::LoadedNotExisting
66 | AccountStatus::InMemoryChange
67 | AccountStatus::Destroyed
68 | AccountStatus::DestroyedChanged
69 | AccountStatus::DestroyedAgain
70 )
71 }
72
73 /// Account is modified but not destroyed.
74 /// This means that some storage values can be found in both
75 /// memory and database.
76 pub fn is_modified_and_not_destroyed(&self) -> bool {
77 matches!(self, AccountStatus::Changed | AccountStatus::InMemoryChange)
78 }
79
80 /// Returns the next account status on creation.
81 pub fn on_created(&self) -> AccountStatus {
82 match self {
83 // If account was destroyed previously just copy new info to it.
84 AccountStatus::DestroyedAgain
85 | AccountStatus::Destroyed
86 | AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
87 // If account is loaded from db.
88 AccountStatus::LoadedNotExisting
89 // Loaded empty eip161 to creates is not possible as CREATE2 was added after EIP-161
90 | AccountStatus::LoadedEmptyEIP161
91 | AccountStatus::Loaded
92 | AccountStatus::Changed
93 | AccountStatus::InMemoryChange => {
94 // If account is loaded and not empty this means that account has some balance.
95 // This means that account cannot be created.
96 // We are assuming that EVM did necessary checks before allowing account to be created.
97 AccountStatus::InMemoryChange
98 }
99 }
100 }
101
102 /// Returns the next account status on touched empty account post state clear EIP (EIP-161).
103 ///
104 /// # Panics
105 ///
106 /// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
107 pub fn on_touched_empty_post_eip161(&self) -> AccountStatus {
108 match self {
109 // Account can be touched but not existing. The status should remain the same.
110 AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
111 // Account can be created empty and only then touched.
112 AccountStatus::InMemoryChange
113 | AccountStatus::Destroyed
114 | AccountStatus::LoadedEmptyEIP161 => AccountStatus::Destroyed,
115 // Transition to destroy the account.
116 AccountStatus::DestroyedAgain | AccountStatus::DestroyedChanged => {
117 AccountStatus::DestroyedAgain
118 }
119 // Account statuses considered unreachable.
120 AccountStatus::Loaded | AccountStatus::Changed => {
121 unreachable!("Wrong state transition, touch empty is not possible from {self:?}");
122 }
123 }
124 }
125
126 /// Returns the next account status on touched or created account pre state clear EIP (EIP-161).
127 /// Returns `None` if the account status didn't change.
128 ///
129 /// # Panics
130 ///
131 /// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
132 pub fn on_touched_created_pre_eip161(&self, had_no_info: bool) -> Option<AccountStatus> {
133 match self {
134 AccountStatus::LoadedEmptyEIP161 => None,
135 AccountStatus::DestroyedChanged => {
136 if had_no_info {
137 None
138 } else {
139 Some(AccountStatus::DestroyedChanged)
140 }
141 }
142 AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
143 Some(AccountStatus::DestroyedChanged)
144 }
145 AccountStatus::InMemoryChange | AccountStatus::LoadedNotExisting => {
146 Some(AccountStatus::InMemoryChange)
147 }
148 AccountStatus::Loaded | AccountStatus::Changed => {
149 unreachable!("Wrong state transition, touch crate is not possible from {self:?}")
150 }
151 }
152 }
153
154 /// Returns the next account status on change.
155 pub fn on_changed(&self, had_no_nonce_and_code: bool) -> AccountStatus {
156 match self {
157 // If the account was loaded as not existing, promote it to changed.
158 // This account was likely created by a balance transfer.
159 AccountStatus::LoadedNotExisting => AccountStatus::InMemoryChange,
160 // Change on empty account, should transfer storage if there is any.
161 // There is possibility that there are storage entries inside db.
162 // That storage is used in merkle tree calculation before state clear EIP.
163 AccountStatus::LoadedEmptyEIP161 => AccountStatus::InMemoryChange,
164 // The account was loaded as existing.
165 AccountStatus::Loaded => {
166 if had_no_nonce_and_code {
167 // Account is fully in memory
168 AccountStatus::InMemoryChange
169 } else {
170 // Can be contract and some of storage slots can be present inside db.
171 AccountStatus::Changed
172 }
173 }
174
175 // On change, the "changed" type account statuses are preserved.
176 // Any checks for empty accounts are done outside of this fn.
177 AccountStatus::Changed => AccountStatus::Changed,
178 AccountStatus::InMemoryChange => AccountStatus::InMemoryChange,
179 AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
180
181 // If account is destroyed and then changed this means this is
182 // balance transfer.
183 AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
184 AccountStatus::DestroyedChanged
185 }
186 }
187 }
188
189 /// Returns the next account status on selfdestruct.
190 pub fn on_selfdestructed(&self) -> AccountStatus {
191 match self {
192 // Non existing account can't be destroyed.
193 AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
194 // If account is created and selfdestructed in the same block, mark it as destroyed again.
195 // Note: There is no big difference between Destroyed and DestroyedAgain in this case,
196 // but was added for clarity.
197 AccountStatus::DestroyedChanged
198 | AccountStatus::DestroyedAgain
199 | AccountStatus::Destroyed => AccountStatus::DestroyedAgain,
200
201 // Transition to destroyed status.
202 _ => AccountStatus::Destroyed,
203 }
204 }
205
206 /// Transition to other state while preserving invariance of this state.
207 ///
208 /// It this account was Destroyed and other account is not:
209 /// - We should mark extended account as destroyed too.
210 /// - And as other account had some changes, extended account
211 /// should be marked as DestroyedChanged.
212 ///
213 /// If both account are not destroyed and if this account is in memory:
214 /// - This means that extended account is in memory too.
215 ///
216 /// Otherwise, if both are destroyed or other is destroyed:
217 /// - Sets other status to extended account.
218 pub fn transition(&mut self, other: Self) {
219 *self = match (self.was_destroyed(), other.was_destroyed()) {
220 (true, false) => Self::DestroyedChanged,
221 (false, false) if *self == Self::InMemoryChange => Self::InMemoryChange,
222 _ => other,
223 };
224 }
225}
226
227#[cfg(test)]
228mod test {
229
230 use super::*;
231
232 #[test]
233 fn test_account_status() {
234 // Account not modified
235 assert!(AccountStatus::Loaded.is_not_modified());
236 assert!(AccountStatus::LoadedEmptyEIP161.is_not_modified());
237 assert!(AccountStatus::LoadedNotExisting.is_not_modified());
238 assert!(!AccountStatus::Changed.is_not_modified());
239 assert!(!AccountStatus::InMemoryChange.is_not_modified());
240 assert!(!AccountStatus::Destroyed.is_not_modified());
241 assert!(!AccountStatus::DestroyedChanged.is_not_modified());
242 assert!(!AccountStatus::DestroyedAgain.is_not_modified());
243
244 // We know full storage
245 assert!(!AccountStatus::LoadedEmptyEIP161.is_storage_known());
246 assert!(AccountStatus::LoadedNotExisting.is_storage_known());
247 assert!(AccountStatus::InMemoryChange.is_storage_known());
248 assert!(AccountStatus::Destroyed.is_storage_known());
249 assert!(AccountStatus::DestroyedChanged.is_storage_known());
250 assert!(AccountStatus::DestroyedAgain.is_storage_known());
251 assert!(!AccountStatus::Loaded.is_storage_known());
252 assert!(!AccountStatus::Changed.is_storage_known());
253
254 // Account was destroyed
255 assert!(!AccountStatus::LoadedEmptyEIP161.was_destroyed());
256 assert!(!AccountStatus::LoadedNotExisting.was_destroyed());
257 assert!(!AccountStatus::InMemoryChange.was_destroyed());
258 assert!(AccountStatus::Destroyed.was_destroyed());
259 assert!(AccountStatus::DestroyedChanged.was_destroyed());
260 assert!(AccountStatus::DestroyedAgain.was_destroyed());
261 assert!(!AccountStatus::Loaded.was_destroyed());
262 assert!(!AccountStatus::Changed.was_destroyed());
263
264 // Account modified but not destroyed
265 assert!(AccountStatus::Changed.is_modified_and_not_destroyed());
266 assert!(AccountStatus::InMemoryChange.is_modified_and_not_destroyed());
267 assert!(!AccountStatus::Loaded.is_modified_and_not_destroyed());
268 assert!(!AccountStatus::LoadedEmptyEIP161.is_modified_and_not_destroyed());
269 assert!(!AccountStatus::LoadedNotExisting.is_modified_and_not_destroyed());
270 assert!(!AccountStatus::Destroyed.is_modified_and_not_destroyed());
271 assert!(!AccountStatus::DestroyedChanged.is_modified_and_not_destroyed());
272 assert!(!AccountStatus::DestroyedAgain.is_modified_and_not_destroyed());
273 }
274}