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)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub enum AccountStatus {
20#[default]
21LoadedNotExisting,
22 Loaded,
23 LoadedEmptyEIP161,
24 InMemoryChange,
25 Changed,
26 Destroyed,
27 DestroyedChanged,
28 DestroyedAgain,
29}
3031impl AccountStatus {
32/// Account is not modified and just loaded from database.
33pub fn is_not_modified(&self) -> bool {
34matches!(
35self,
36 AccountStatus::LoadedNotExisting
37 | AccountStatus::Loaded
38 | AccountStatus::LoadedEmptyEIP161
39 )
40 }
4142/// Account was destroyed by calling SELFDESTRUCT.
43 /// This means that full account and storage are inside memory.
44pub fn was_destroyed(&self) -> bool {
45matches!(
46self,
47 AccountStatus::Destroyed
48 | AccountStatus::DestroyedChanged
49 | AccountStatus::DestroyedAgain
50 )
51 }
5253/// This means storage is known, it can be newly created or storage got destroyed.
54pub fn is_storage_known(&self) -> bool {
55matches!(
56self,
57 AccountStatus::LoadedNotExisting
58 | AccountStatus::InMemoryChange
59 | AccountStatus::Destroyed
60 | AccountStatus::DestroyedChanged
61 | AccountStatus::DestroyedAgain
62 )
63 }
6465/// Account is modified but not destroyed.
66 /// This means that some storage values can be found in both
67 /// memory and database.
68pub fn is_modified_and_not_destroyed(&self) -> bool {
69matches!(self, AccountStatus::Changed | AccountStatus::InMemoryChange)
70 }
7172/// Returns the next account status on creation.
73pub fn on_created(&self) -> AccountStatus {
74match self {
75// if account was destroyed previously just copy new info to it.
76AccountStatus::DestroyedAgain
77 | AccountStatus::Destroyed
78 | AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
79// if account is loaded from db.
80AccountStatus::LoadedNotExisting
81// Loaded empty eip161 to creates is not possible as CREATE2 was added after EIP-161
82| AccountStatus::LoadedEmptyEIP161
83 | AccountStatus::Loaded
84 | AccountStatus::Changed
85 | AccountStatus::InMemoryChange => {
86// If account is loaded and not empty this means that account has some balance.
87 // This means that account cannot be created.
88 // We are assuming that EVM did necessary checks before allowing account to be created.
89AccountStatus::InMemoryChange
90 }
91 }
92 }
9394/// Returns the next account status on touched empty account post state clear EIP (EIP-161).
95 ///
96 /// # Panics
97 ///
98 /// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
99pub fn on_touched_empty_post_eip161(&self) -> AccountStatus {
100match self {
101// Account can be touched but not existing. The status should remain the same.
102AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
103// Account can be created empty and only then touched.
104AccountStatus::InMemoryChange
105 | AccountStatus::Destroyed
106 | AccountStatus::LoadedEmptyEIP161 => AccountStatus::Destroyed,
107// Transition to destroy the account.
108AccountStatus::DestroyedAgain | AccountStatus::DestroyedChanged => {
109 AccountStatus::DestroyedAgain
110 }
111// Account statuses considered unreachable.
112AccountStatus::Loaded | AccountStatus::Changed => {
113unreachable!("Wrong state transition, touch empty is not possible from {self:?}");
114 }
115 }
116 }
117118/// Returns the next account status on touched or created account pre state clear EIP (EIP-161).
119 /// Returns `None` if the account status didn't change.
120 ///
121 /// # Panics
122 ///
123 /// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
124pub fn on_touched_created_pre_eip161(&self, had_no_info: bool) -> Option<AccountStatus> {
125match self {
126 AccountStatus::LoadedEmptyEIP161 => None,
127 AccountStatus::DestroyedChanged => {
128if had_no_info {
129None
130} else {
131Some(AccountStatus::DestroyedChanged)
132 }
133 }
134 AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
135Some(AccountStatus::DestroyedChanged)
136 }
137 AccountStatus::InMemoryChange | AccountStatus::LoadedNotExisting => {
138Some(AccountStatus::InMemoryChange)
139 }
140 AccountStatus::Loaded | AccountStatus::Changed => {
141unreachable!("Wrong state transition, touch crate is not possible from {self:?}")
142 }
143 }
144 }
145146/// Returns the next account status on change.
147pub fn on_changed(&self, had_no_nonce_and_code: bool) -> AccountStatus {
148match self {
149// If the account was loaded as not existing, promote it to changed.
150 // This account was likely created by a balance transfer.
151AccountStatus::LoadedNotExisting => AccountStatus::InMemoryChange,
152// Change on empty account, should transfer storage if there is any.
153 // There is possibility that there are storage entries inside db.
154 // That storage is used in merkle tree calculation before state clear EIP.
155AccountStatus::LoadedEmptyEIP161 => AccountStatus::InMemoryChange,
156// The account was loaded as existing.
157AccountStatus::Loaded => {
158if had_no_nonce_and_code {
159// account is fully in memory
160AccountStatus::InMemoryChange
161 } else {
162// can be contract and some of storage slots can be present inside db.
163AccountStatus::Changed
164 }
165 }
166167// On change, the "changed" type account statuses are preserved.
168 // Any checks for empty accounts are done outside of this fn.
169AccountStatus::Changed => AccountStatus::Changed,
170 AccountStatus::InMemoryChange => AccountStatus::InMemoryChange,
171 AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
172173// If account is destroyed and then changed this means this is
174 // balance transfer.
175AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
176 AccountStatus::DestroyedChanged
177 }
178 }
179 }
180181/// Returns the next account status on selfdestruct.
182pub fn on_selfdestructed(&self) -> AccountStatus {
183match self {
184// Non existing account can't be destroyed.
185AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
186// If account is created and selfdestructed in the same block, mark it as destroyed again.
187 // Note: there is no big difference between Destroyed and DestroyedAgain in this case,
188 // but was added for clarity.
189AccountStatus::DestroyedChanged
190 | AccountStatus::DestroyedAgain
191 | AccountStatus::Destroyed => AccountStatus::DestroyedAgain,
192193// Transition to destroyed status.
194_ => AccountStatus::Destroyed,
195 }
196 }
197198/// Transition to other state while preserving invariance of this state.
199 ///
200 /// It this account was Destroyed and other account is not:
201 /// we should mark extended account as destroyed too.
202 /// and as other account had some changes, extended account
203 /// should be marked as DestroyedChanged.
204 ///
205 /// If both account are not destroyed and if this account is in memory:
206 /// this means that extended account is in memory too.
207 ///
208 /// Otherwise, if both are destroyed or other is destroyed:
209 /// set other status to extended account.
210pub fn transition(&mut self, other: Self) {
211*self = match (self.was_destroyed(), other.was_destroyed()) {
212 (true, false) => Self::DestroyedChanged,
213 (false, false) if *self == Self::InMemoryChange => Self::InMemoryChange,
214_ => other,
215 };
216 }
217}
218219#[cfg(test)]
220mod test {
221222use super::*;
223224#[test]
225fn test_account_status() {
226// account not modified
227assert!(AccountStatus::Loaded.is_not_modified());
228assert!(AccountStatus::LoadedEmptyEIP161.is_not_modified());
229assert!(AccountStatus::LoadedNotExisting.is_not_modified());
230assert!(!AccountStatus::Changed.is_not_modified());
231assert!(!AccountStatus::InMemoryChange.is_not_modified());
232assert!(!AccountStatus::Destroyed.is_not_modified());
233assert!(!AccountStatus::DestroyedChanged.is_not_modified());
234assert!(!AccountStatus::DestroyedAgain.is_not_modified());
235236// we know full storage
237assert!(!AccountStatus::LoadedEmptyEIP161.is_storage_known());
238assert!(AccountStatus::LoadedNotExisting.is_storage_known());
239assert!(AccountStatus::InMemoryChange.is_storage_known());
240assert!(AccountStatus::Destroyed.is_storage_known());
241assert!(AccountStatus::DestroyedChanged.is_storage_known());
242assert!(AccountStatus::DestroyedAgain.is_storage_known());
243assert!(!AccountStatus::Loaded.is_storage_known());
244assert!(!AccountStatus::Changed.is_storage_known());
245246// account was destroyed
247assert!(!AccountStatus::LoadedEmptyEIP161.was_destroyed());
248assert!(!AccountStatus::LoadedNotExisting.was_destroyed());
249assert!(!AccountStatus::InMemoryChange.was_destroyed());
250assert!(AccountStatus::Destroyed.was_destroyed());
251assert!(AccountStatus::DestroyedChanged.was_destroyed());
252assert!(AccountStatus::DestroyedAgain.was_destroyed());
253assert!(!AccountStatus::Loaded.was_destroyed());
254assert!(!AccountStatus::Changed.was_destroyed());
255256// account modified but not destroyed
257assert!(AccountStatus::Changed.is_modified_and_not_destroyed());
258assert!(AccountStatus::InMemoryChange.is_modified_and_not_destroyed());
259assert!(!AccountStatus::Loaded.is_modified_and_not_destroyed());
260assert!(!AccountStatus::LoadedEmptyEIP161.is_modified_and_not_destroyed());
261assert!(!AccountStatus::LoadedNotExisting.is_modified_and_not_destroyed());
262assert!(!AccountStatus::Destroyed.is_modified_and_not_destroyed());
263assert!(!AccountStatus::DestroyedChanged.is_modified_and_not_destroyed());
264assert!(!AccountStatus::DestroyedAgain.is_modified_and_not_destroyed());
265 }
266}