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    #[default]
21    LoadedNotExisting,
22    Loaded,
23    LoadedEmptyEIP161,
24    InMemoryChange,
25    Changed,
26    Destroyed,
27    DestroyedChanged,
28    DestroyedAgain,
29}
30
31impl AccountStatus {
32    /// Account is not modified and just loaded from database.
33    pub fn is_not_modified(&self) -> bool {
34        matches!(
35            self,
36            AccountStatus::LoadedNotExisting
37                | AccountStatus::Loaded
38                | AccountStatus::LoadedEmptyEIP161
39        )
40    }
41
42    /// Account was destroyed by calling SELFDESTRUCT.
43    /// This means that full account and storage are inside memory.
44    pub fn was_destroyed(&self) -> bool {
45        matches!(
46            self,
47            AccountStatus::Destroyed
48                | AccountStatus::DestroyedChanged
49                | AccountStatus::DestroyedAgain
50        )
51    }
52
53    /// This means storage is known, it can be newly created or storage got destroyed.
54    pub fn is_storage_known(&self) -> bool {
55        matches!(
56            self,
57            AccountStatus::LoadedNotExisting
58                | AccountStatus::InMemoryChange
59                | AccountStatus::Destroyed
60                | AccountStatus::DestroyedChanged
61                | AccountStatus::DestroyedAgain
62        )
63    }
64
65    /// Account is modified but not destroyed.
66    /// This means that some storage values can be found in both
67    /// memory and database.
68    pub fn is_modified_and_not_destroyed(&self) -> bool {
69        matches!(self, AccountStatus::Changed | AccountStatus::InMemoryChange)
70    }
71
72    /// Returns the next account status on creation.
73    pub fn on_created(&self) -> AccountStatus {
74        match self {
75            // If account was destroyed previously just copy new info to it.
76            AccountStatus::DestroyedAgain
77            | AccountStatus::Destroyed
78            | AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
79            // If account is loaded from db.
80            AccountStatus::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.
89                AccountStatus::InMemoryChange
90            }
91        }
92    }
93
94    /// 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].
99    pub fn on_touched_empty_post_eip161(&self) -> AccountStatus {
100        match self {
101            // Account can be touched but not existing. The status should remain the same.
102            AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
103            // Account can be created empty and only then touched.
104            AccountStatus::InMemoryChange
105            | AccountStatus::Destroyed
106            | AccountStatus::LoadedEmptyEIP161 => AccountStatus::Destroyed,
107            // Transition to destroy the account.
108            AccountStatus::DestroyedAgain | AccountStatus::DestroyedChanged => {
109                AccountStatus::DestroyedAgain
110            }
111            // Account statuses considered unreachable.
112            AccountStatus::Loaded | AccountStatus::Changed => {
113                unreachable!("Wrong state transition, touch empty is not possible from {self:?}");
114            }
115        }
116    }
117
118    /// 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].
124    pub fn on_touched_created_pre_eip161(&self, had_no_info: bool) -> Option<AccountStatus> {
125        match self {
126            AccountStatus::LoadedEmptyEIP161 => None,
127            AccountStatus::DestroyedChanged => {
128                if had_no_info {
129                    None
130                } else {
131                    Some(AccountStatus::DestroyedChanged)
132                }
133            }
134            AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
135                Some(AccountStatus::DestroyedChanged)
136            }
137            AccountStatus::InMemoryChange | AccountStatus::LoadedNotExisting => {
138                Some(AccountStatus::InMemoryChange)
139            }
140            AccountStatus::Loaded | AccountStatus::Changed => {
141                unreachable!("Wrong state transition, touch crate is not possible from {self:?}")
142            }
143        }
144    }
145
146    /// Returns the next account status on change.
147    pub fn on_changed(&self, had_no_nonce_and_code: bool) -> AccountStatus {
148        match self {
149            // If the account was loaded as not existing, promote it to changed.
150            // This account was likely created by a balance transfer.
151            AccountStatus::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.
155            AccountStatus::LoadedEmptyEIP161 => AccountStatus::InMemoryChange,
156            // The account was loaded as existing.
157            AccountStatus::Loaded => {
158                if had_no_nonce_and_code {
159                    // Account is fully in memory
160                    AccountStatus::InMemoryChange
161                } else {
162                    // Can be contract and some of storage slots can be present inside db.
163                    AccountStatus::Changed
164                }
165            }
166
167            // On change, the "changed" type account statuses are preserved.
168            // Any checks for empty accounts are done outside of this fn.
169            AccountStatus::Changed => AccountStatus::Changed,
170            AccountStatus::InMemoryChange => AccountStatus::InMemoryChange,
171            AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
172
173            // If account is destroyed and then changed this means this is
174            // balance transfer.
175            AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
176                AccountStatus::DestroyedChanged
177            }
178        }
179    }
180
181    /// Returns the next account status on selfdestruct.
182    pub fn on_selfdestructed(&self) -> AccountStatus {
183        match self {
184            // Non existing account can't be destroyed.
185            AccountStatus::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.
189            AccountStatus::DestroyedChanged
190            | AccountStatus::DestroyedAgain
191            | AccountStatus::Destroyed => AccountStatus::DestroyedAgain,
192
193            // Transition to destroyed status.
194            _ => AccountStatus::Destroyed,
195        }
196    }
197
198    /// 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    /// -  Sets other status to extended account.
210    pub 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}
218
219#[cfg(test)]
220mod test {
221
222    use super::*;
223
224    #[test]
225    fn test_account_status() {
226        // Account not modified
227        assert!(AccountStatus::Loaded.is_not_modified());
228        assert!(AccountStatus::LoadedEmptyEIP161.is_not_modified());
229        assert!(AccountStatus::LoadedNotExisting.is_not_modified());
230        assert!(!AccountStatus::Changed.is_not_modified());
231        assert!(!AccountStatus::InMemoryChange.is_not_modified());
232        assert!(!AccountStatus::Destroyed.is_not_modified());
233        assert!(!AccountStatus::DestroyedChanged.is_not_modified());
234        assert!(!AccountStatus::DestroyedAgain.is_not_modified());
235
236        // We know full storage
237        assert!(!AccountStatus::LoadedEmptyEIP161.is_storage_known());
238        assert!(AccountStatus::LoadedNotExisting.is_storage_known());
239        assert!(AccountStatus::InMemoryChange.is_storage_known());
240        assert!(AccountStatus::Destroyed.is_storage_known());
241        assert!(AccountStatus::DestroyedChanged.is_storage_known());
242        assert!(AccountStatus::DestroyedAgain.is_storage_known());
243        assert!(!AccountStatus::Loaded.is_storage_known());
244        assert!(!AccountStatus::Changed.is_storage_known());
245
246        // Account was destroyed
247        assert!(!AccountStatus::LoadedEmptyEIP161.was_destroyed());
248        assert!(!AccountStatus::LoadedNotExisting.was_destroyed());
249        assert!(!AccountStatus::InMemoryChange.was_destroyed());
250        assert!(AccountStatus::Destroyed.was_destroyed());
251        assert!(AccountStatus::DestroyedChanged.was_destroyed());
252        assert!(AccountStatus::DestroyedAgain.was_destroyed());
253        assert!(!AccountStatus::Loaded.was_destroyed());
254        assert!(!AccountStatus::Changed.was_destroyed());
255
256        // Account modified but not destroyed
257        assert!(AccountStatus::Changed.is_modified_and_not_destroyed());
258        assert!(AccountStatus::InMemoryChange.is_modified_and_not_destroyed());
259        assert!(!AccountStatus::Loaded.is_modified_and_not_destroyed());
260        assert!(!AccountStatus::LoadedEmptyEIP161.is_modified_and_not_destroyed());
261        assert!(!AccountStatus::LoadedNotExisting.is_modified_and_not_destroyed());
262        assert!(!AccountStatus::Destroyed.is_modified_and_not_destroyed());
263        assert!(!AccountStatus::DestroyedChanged.is_modified_and_not_destroyed());
264        assert!(!AccountStatus::DestroyedAgain.is_modified_and_not_destroyed());
265    }
266}