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}