Skip to content

Commit 5e4811c

Browse files
committed
chore(state): move account status transitions to AccountStatus
1 parent 0d78d1e commit 5e4811c

File tree

5 files changed

+211
-195
lines changed

5 files changed

+211
-195
lines changed

crates/primitives/src/state.rs

+6
Original file line numberDiff line numberDiff line change
@@ -242,10 +242,16 @@ impl AccountInfo {
242242
self.balance == U256::ZERO && self.nonce == 0 && code_empty
243243
}
244244

245+
/// Returns `true` if the account is not empty.
245246
pub fn exists(&self) -> bool {
246247
!self.is_empty()
247248
}
248249

250+
/// Returns `true` if account has no nonce and code.
251+
pub fn has_no_code_and_nonce(&self) -> bool {
252+
self.is_empty_code_hash() && self.nonce == 0
253+
}
254+
249255
/// Return bytecode hash associated with this account.
250256
/// If account does not have code, it return's `KECCAK_EMPTY` hash.
251257
pub fn code_hash(&self) -> B256 {

crates/revm/src/db/states/account_status.rs

+180-55
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,8 @@ pub enum AccountStatus {
1515
}
1616

1717
impl AccountStatus {
18-
/// Transition to other state while preserving invariance of this state.
19-
///
20-
/// It this account was Destroyed and other account is not:
21-
/// we should mark extended account as destroyed too.
22-
/// and as other account had some changes, extended account
23-
/// should be marked as DestroyedChanged.
24-
///
25-
/// If both account are not destroyed and if this account is in memory:
26-
/// this means that extended account is in memory too.
27-
///
28-
/// Otherwise, if both are destroyed or other is destroyed:
29-
/// set other status to extended account.
30-
pub fn transition(&mut self, other: Self) {
31-
*self = match (self.was_destroyed(), other.was_destroyed()) {
32-
(true, false) => Self::DestroyedChanged,
33-
(false, false) if *self == Self::InMemoryChange => Self::InMemoryChange,
34-
_ => other,
35-
};
36-
}
3718
/// Account is not modified and just loaded from database.
38-
pub fn not_modified(&self) -> bool {
19+
pub fn is_not_modified(&self) -> bool {
3920
matches!(
4021
self,
4122
AccountStatus::LoadedNotExisting
@@ -46,7 +27,7 @@ impl AccountStatus {
4627

4728
/// Account was destroyed by calling SELFDESTRUCT.
4829
/// This means that full account and storage are inside memory.
49-
pub fn was_destroyed(&self) -> bool {
30+
pub fn is_destroyed(&self) -> bool {
5031
matches!(
5132
self,
5233
AccountStatus::Destroyed
@@ -56,7 +37,7 @@ impl AccountStatus {
5637
}
5738

5839
/// This means storage is known, it can be newly created or storage got destroyed.
59-
pub fn storage_known(&self) -> bool {
40+
pub fn is_storage_known(&self) -> bool {
6041
matches!(
6142
self,
6243
AccountStatus::LoadedNotExisting
@@ -70,9 +51,153 @@ impl AccountStatus {
7051
/// Account is modified but not destroyed.
7152
/// This means that some storage values can be found in both
7253
/// memory and database.
73-
pub fn modified_but_not_destroyed(&self) -> bool {
54+
pub fn is_modified_and_not_destroyed(&self) -> bool {
7455
matches!(self, AccountStatus::Changed | AccountStatus::InMemoryChange)
7556
}
57+
58+
/// Returns the next account status on creation.
59+
pub fn on_created(&self) -> AccountStatus {
60+
match self {
61+
// if account was destroyed previously just copy new info to it.
62+
AccountStatus::DestroyedAgain
63+
| AccountStatus::Destroyed
64+
| AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
65+
// if account is loaded from db.
66+
AccountStatus::LoadedNotExisting
67+
// Loaded empty eip161 to creates is not possible as CREATE2 was added after EIP-161
68+
| AccountStatus::LoadedEmptyEIP161
69+
| AccountStatus::Loaded
70+
| AccountStatus::Changed
71+
| AccountStatus::InMemoryChange => {
72+
// If account is loaded and not empty this means that account has some balance.
73+
// This means that account cannot be created.
74+
// We are assuming that EVM did necessary checks before allowing account to be created.
75+
AccountStatus::InMemoryChange
76+
}
77+
}
78+
}
79+
80+
/// Returns the next account status on touched empty account post state clear EIP (EIP-161).
81+
///
82+
/// # Panics
83+
///
84+
/// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
85+
pub fn on_touched_empty_post_eip161(&self) -> AccountStatus {
86+
match self {
87+
// Account can be touched but not existing. The status should remain the same.
88+
AccountStatus::LoadedNotExisting => AccountStatus::LoadedNotExisting,
89+
// Account can be created empty and only then touched.
90+
AccountStatus::InMemoryChange
91+
| AccountStatus::Destroyed
92+
| AccountStatus::LoadedEmptyEIP161 => AccountStatus::Destroyed,
93+
// Transition to destroy the account.
94+
AccountStatus::DestroyedAgain | AccountStatus::DestroyedChanged => {
95+
AccountStatus::DestroyedAgain
96+
}
97+
// Account statuses considered unreachable.
98+
AccountStatus::Loaded | AccountStatus::Changed => {
99+
unreachable!("Wrong state transition, touch empty is not possible from {self:?}");
100+
}
101+
}
102+
}
103+
104+
/// Returns the next account status on touched or created account pre state clear EIP (EIP-161).
105+
/// Returns `None` if the account status didn't change.
106+
///
107+
/// # Panics
108+
///
109+
/// If current status is [AccountStatus::Loaded] or [AccountStatus::Changed].
110+
pub fn on_touched_created_pre_eip161(&self, had_no_info: bool) -> Option<AccountStatus> {
111+
match self {
112+
AccountStatus::LoadedEmptyEIP161 => None,
113+
AccountStatus::DestroyedChanged => {
114+
if had_no_info {
115+
None
116+
} else {
117+
Some(AccountStatus::DestroyedChanged)
118+
}
119+
}
120+
AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
121+
Some(AccountStatus::DestroyedChanged)
122+
}
123+
AccountStatus::InMemoryChange | AccountStatus::LoadedNotExisting => {
124+
Some(AccountStatus::InMemoryChange)
125+
}
126+
AccountStatus::Loaded | AccountStatus::Changed => {
127+
unreachable!("Wrong state transition, touch crate is not possible from {self:?}")
128+
}
129+
}
130+
}
131+
132+
/// Returns the next account status on change.
133+
pub fn on_changed(&self, had_no_nonce_and_code: bool) -> AccountStatus {
134+
match self {
135+
// If the account was loaded as not existing, promote it to changed.
136+
// This account was likely created by a balance transfer.
137+
AccountStatus::LoadedNotExisting => AccountStatus::InMemoryChange,
138+
// Change on empty account, should transfer storage if there is any.
139+
// There is possibility that there are storage entries inside db.
140+
// That storage is used in merkle tree calculation before state clear EIP.
141+
AccountStatus::LoadedEmptyEIP161 => AccountStatus::InMemoryChange,
142+
// The account was loaded as existing.
143+
AccountStatus::Loaded => {
144+
if had_no_nonce_and_code {
145+
// account is fully in memory
146+
AccountStatus::InMemoryChange
147+
} else {
148+
// can be contract and some of storage slots can be present inside db.
149+
AccountStatus::Changed
150+
}
151+
}
152+
153+
// On change, the "changed" type account statuses are preserved.
154+
// Any checks for empty accounts are done outside of this fn.
155+
AccountStatus::Changed => AccountStatus::Changed,
156+
AccountStatus::InMemoryChange => AccountStatus::InMemoryChange,
157+
AccountStatus::DestroyedChanged => AccountStatus::DestroyedChanged,
158+
159+
// If account is destroyed and then changed this means this is
160+
// balance transfer.
161+
AccountStatus::Destroyed | AccountStatus::DestroyedAgain => {
162+
AccountStatus::DestroyedChanged
163+
}
164+
}
165+
}
166+
167+
/// Returns the next account status on selfdestruct.
168+
pub fn on_selfdestructed(&self) -> AccountStatus {
169+
match self {
170+
// If account is created and selfdestructed in the same block, mark it as destroyed again.
171+
// Note: there is no big difference between Destroyed and DestroyedAgain in this case,
172+
// but was added for clarity.
173+
AccountStatus::DestroyedChanged
174+
| AccountStatus::DestroyedAgain
175+
| AccountStatus::Destroyed => AccountStatus::DestroyedAgain,
176+
177+
// Transition to destroyed status.
178+
_ => AccountStatus::Destroyed,
179+
}
180+
}
181+
182+
/// Transition to other state while preserving invariance of this state.
183+
///
184+
/// It this account was Destroyed and other account is not:
185+
/// we should mark extended account as destroyed too.
186+
/// and as other account had some changes, extended account
187+
/// should be marked as DestroyedChanged.
188+
///
189+
/// If both account are not destroyed and if this account is in memory:
190+
/// this means that extended account is in memory too.
191+
///
192+
/// Otherwise, if both are destroyed or other is destroyed:
193+
/// set other status to extended account.
194+
pub fn transition(&mut self, other: Self) {
195+
*self = match (self.is_destroyed(), other.is_destroyed()) {
196+
(true, false) => Self::DestroyedChanged,
197+
(false, false) if *self == Self::InMemoryChange => Self::InMemoryChange,
198+
_ => other,
199+
};
200+
}
76201
}
77202

78203
#[cfg(test)]
@@ -83,43 +208,43 @@ mod test {
83208
#[test]
84209
fn test_account_status() {
85210
// account not modified
86-
assert!(AccountStatus::Loaded.not_modified());
87-
assert!(AccountStatus::LoadedEmptyEIP161.not_modified());
88-
assert!(AccountStatus::LoadedNotExisting.not_modified());
89-
assert!(!AccountStatus::Changed.not_modified());
90-
assert!(!AccountStatus::InMemoryChange.not_modified());
91-
assert!(!AccountStatus::Destroyed.not_modified());
92-
assert!(!AccountStatus::DestroyedChanged.not_modified());
93-
assert!(!AccountStatus::DestroyedAgain.not_modified());
211+
assert!(AccountStatus::Loaded.is_not_modified());
212+
assert!(AccountStatus::LoadedEmptyEIP161.is_not_modified());
213+
assert!(AccountStatus::LoadedNotExisting.is_not_modified());
214+
assert!(!AccountStatus::Changed.is_not_modified());
215+
assert!(!AccountStatus::InMemoryChange.is_not_modified());
216+
assert!(!AccountStatus::Destroyed.is_not_modified());
217+
assert!(!AccountStatus::DestroyedChanged.is_not_modified());
218+
assert!(!AccountStatus::DestroyedAgain.is_not_modified());
94219

95220
// we know full storage
96-
assert!(!AccountStatus::LoadedEmptyEIP161.storage_known());
97-
assert!(AccountStatus::LoadedNotExisting.storage_known());
98-
assert!(AccountStatus::InMemoryChange.storage_known());
99-
assert!(AccountStatus::Destroyed.storage_known());
100-
assert!(AccountStatus::DestroyedChanged.storage_known());
101-
assert!(AccountStatus::DestroyedAgain.storage_known());
102-
assert!(!AccountStatus::Loaded.storage_known());
103-
assert!(!AccountStatus::Changed.storage_known());
221+
assert!(!AccountStatus::LoadedEmptyEIP161.is_storage_known());
222+
assert!(AccountStatus::LoadedNotExisting.is_storage_known());
223+
assert!(AccountStatus::InMemoryChange.is_storage_known());
224+
assert!(AccountStatus::Destroyed.is_storage_known());
225+
assert!(AccountStatus::DestroyedChanged.is_storage_known());
226+
assert!(AccountStatus::DestroyedAgain.is_storage_known());
227+
assert!(!AccountStatus::Loaded.is_storage_known());
228+
assert!(!AccountStatus::Changed.is_storage_known());
104229

105230
// account was destroyed
106-
assert!(!AccountStatus::LoadedEmptyEIP161.was_destroyed());
107-
assert!(!AccountStatus::LoadedNotExisting.was_destroyed());
108-
assert!(!AccountStatus::InMemoryChange.was_destroyed());
109-
assert!(AccountStatus::Destroyed.was_destroyed());
110-
assert!(AccountStatus::DestroyedChanged.was_destroyed());
111-
assert!(AccountStatus::DestroyedAgain.was_destroyed());
112-
assert!(!AccountStatus::Loaded.was_destroyed());
113-
assert!(!AccountStatus::Changed.was_destroyed());
231+
assert!(!AccountStatus::LoadedEmptyEIP161.is_destroyed());
232+
assert!(!AccountStatus::LoadedNotExisting.is_destroyed());
233+
assert!(!AccountStatus::InMemoryChange.is_destroyed());
234+
assert!(AccountStatus::Destroyed.is_destroyed());
235+
assert!(AccountStatus::DestroyedChanged.is_destroyed());
236+
assert!(AccountStatus::DestroyedAgain.is_destroyed());
237+
assert!(!AccountStatus::Loaded.is_destroyed());
238+
assert!(!AccountStatus::Changed.is_destroyed());
114239

115240
// account modified but not destroyed
116-
assert!(AccountStatus::Changed.modified_but_not_destroyed());
117-
assert!(AccountStatus::InMemoryChange.modified_but_not_destroyed());
118-
assert!(!AccountStatus::Loaded.modified_but_not_destroyed());
119-
assert!(!AccountStatus::LoadedEmptyEIP161.modified_but_not_destroyed());
120-
assert!(!AccountStatus::LoadedNotExisting.modified_but_not_destroyed());
121-
assert!(!AccountStatus::Destroyed.modified_but_not_destroyed());
122-
assert!(!AccountStatus::DestroyedChanged.modified_but_not_destroyed());
123-
assert!(!AccountStatus::DestroyedAgain.modified_but_not_destroyed());
241+
assert!(AccountStatus::Changed.is_modified_and_not_destroyed());
242+
assert!(AccountStatus::InMemoryChange.is_modified_and_not_destroyed());
243+
assert!(!AccountStatus::Loaded.is_modified_and_not_destroyed());
244+
assert!(!AccountStatus::LoadedEmptyEIP161.is_modified_and_not_destroyed());
245+
assert!(!AccountStatus::LoadedNotExisting.is_modified_and_not_destroyed());
246+
assert!(!AccountStatus::Destroyed.is_modified_and_not_destroyed());
247+
assert!(!AccountStatus::DestroyedChanged.is_modified_and_not_destroyed());
248+
assert!(!AccountStatus::DestroyedAgain.is_modified_and_not_destroyed());
124249
}
125250
}

crates/revm/src/db/states/bundle_account.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ impl BundleAccount {
5757
let slot = self.storage.get(&slot).map(|s| s.present_value);
5858
if slot.is_some() {
5959
slot
60-
} else if self.status.storage_known() {
60+
} else if self.status.is_storage_known() {
6161
Some(U256::ZERO)
6262
} else {
6363
None
@@ -71,7 +71,7 @@ impl BundleAccount {
7171

7272
/// Was this account destroyed.
7373
pub fn was_destroyed(&self) -> bool {
74-
self.status.was_destroyed()
74+
self.status.is_destroyed()
7575
}
7676

7777
/// Return true of account info was changed.

0 commit comments

Comments
 (0)