Skip to content

Commit d5e9a14

Browse files
Add unit tests for state diff.
1 parent 7297b28 commit d5e9a14

File tree

1 file changed

+215
-1
lines changed
  • crates/reth/statediff/src

1 file changed

+215
-1
lines changed

crates/reth/statediff/src/lib.rs

+215-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ impl BatchStateDiff {
7878
}
7979

8080
let original_acc_storage = self.original_storage_slots.entry(*addr).or_default();
81-
8281
let cur_acc_storage = self.storage_slots.entry(*addr).or_default();
8382

8483
for (key, value) in &acc.storage {
@@ -97,6 +96,221 @@ impl BatchStateDiff {
9796
cur_acc_storage.insert(*key, cur_storage_value);
9897
}
9998
}
99+
100+
// It can happen that the storage slots for a certain account has been completely
101+
// reverted, so remove the account from the map.
102+
if cur_acc_storage.is_empty() {
103+
self.storage_slots.remove(addr);
104+
}
105+
}
106+
}
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use revm::db::{states::StorageSlot, BundleAccount};
112+
use revm_primitives::{
113+
alloy_primitives::map::HashMap, map::DefaultHashBuilder, AccountInfo, Address, FixedBytes,
114+
KECCAK_EMPTY, U256,
115+
};
116+
117+
use crate::{BatchStateDiff, BlockStateDiff};
118+
119+
const fn account1() -> Address {
120+
Address::new([0x60; 20])
121+
}
122+
123+
const fn account2() -> Address {
124+
Address::new([0x61; 20])
125+
}
126+
127+
fn acc_info1() -> AccountInfo {
128+
AccountInfo {
129+
nonce: 1,
130+
balance: U256::from(10),
131+
code_hash: KECCAK_EMPTY,
132+
code: None,
133+
}
134+
}
135+
136+
fn acc_info2() -> AccountInfo {
137+
AccountInfo {
138+
nonce: 3,
139+
balance: U256::from(20),
140+
code_hash: KECCAK_EMPTY,
141+
code: None,
100142
}
101143
}
144+
145+
fn slot1() -> U256 {
146+
U256::from(5)
147+
}
148+
149+
fn slot2() -> U256 {
150+
U256::from(7)
151+
}
152+
153+
fn slot_changes() -> HashMap<U256, StorageSlot> {
154+
HashMap::from_iter([
155+
(
156+
slot1(),
157+
StorageSlot::new_changed(U256::from(0), U256::from(10)),
158+
),
159+
(
160+
slot2(),
161+
StorageSlot::new_changed(U256::from(10), U256::from(15)),
162+
),
163+
])
164+
}
165+
166+
fn revert_slot_changes(
167+
slot_changes: &HashMap<U256, StorageSlot>,
168+
) -> HashMap<U256, StorageSlot> {
169+
HashMap::<U256, StorageSlot, DefaultHashBuilder>::from_iter(slot_changes.iter().map(
170+
|(k, v)| {
171+
(
172+
*k,
173+
StorageSlot::new_changed(v.present_value, v.previous_or_original_value),
174+
)
175+
},
176+
))
177+
}
178+
179+
fn test_state_diff_acc1() -> BlockStateDiff {
180+
let mut test_diff = BlockStateDiff {
181+
state: HashMap::default(),
182+
contracts: HashMap::default(),
183+
};
184+
185+
test_diff.state.insert(
186+
account1(),
187+
BundleAccount::new(
188+
None,
189+
Some(acc_info1()),
190+
slot_changes(),
191+
revm::db::AccountStatus::Changed,
192+
),
193+
);
194+
195+
test_diff
196+
}
197+
198+
fn test_state_diff_acc_old_slots1() -> BlockStateDiff {
199+
let mut test_diff = BlockStateDiff {
200+
state: HashMap::default(),
201+
contracts: HashMap::default(),
202+
};
203+
204+
test_diff.state.insert(
205+
account1(),
206+
BundleAccount::new(
207+
Some(acc_info1()),
208+
Some(AccountInfo {
209+
nonce: 2,
210+
balance: U256::from(9),
211+
code_hash: KECCAK_EMPTY,
212+
code: None,
213+
}),
214+
revert_slot_changes(&slot_changes()),
215+
revm::db::AccountStatus::Changed,
216+
),
217+
);
218+
219+
test_diff
220+
}
221+
222+
fn test_state_diff_acc2() -> BlockStateDiff {
223+
let mut test_diff = BlockStateDiff {
224+
state: HashMap::default(),
225+
contracts: HashMap::default(),
226+
};
227+
228+
test_diff.state.insert(
229+
account2(),
230+
BundleAccount::new(
231+
None,
232+
Some(acc_info2()),
233+
HashMap::default(),
234+
revm::db::AccountStatus::Changed,
235+
),
236+
);
237+
238+
test_diff
239+
}
240+
241+
#[test]
242+
fn basic_batch_state_diff() {
243+
let mut batch_diff = BatchStateDiff::new();
244+
batch_diff.apply(test_state_diff_acc1());
245+
batch_diff.apply(test_state_diff_acc2());
246+
247+
let acc1 = batch_diff
248+
.accounts
249+
.get(&account1())
250+
.expect("account1 should be present")
251+
.clone();
252+
let info1 = acc1.unwrap();
253+
assert!(info1 == acc_info1());
254+
255+
let acc2 = batch_diff
256+
.accounts
257+
.get(&account2())
258+
.expect("account2 should be present")
259+
.clone();
260+
let info2 = acc2.unwrap();
261+
assert!(info2 == acc_info2());
262+
263+
assert!(batch_diff.storage_slots.contains_key(&account1()));
264+
}
265+
266+
#[test]
267+
fn multiple_slot_writes() {
268+
let mut batch_diff = BatchStateDiff::new();
269+
batch_diff.apply(test_state_diff_acc1());
270+
batch_diff.apply(test_state_diff_acc_old_slots1());
271+
272+
let acc1 = batch_diff
273+
.accounts
274+
.get(&account1())
275+
.expect("account1 should be present")
276+
.clone();
277+
let info1 = acc1.unwrap();
278+
let expected_info = AccountInfo {
279+
nonce: 2,
280+
balance: U256::from(9),
281+
code_hash: KECCAK_EMPTY,
282+
code: None,
283+
};
284+
assert!(info1 == expected_info);
285+
// Slots were reverted to initial values, so the map should be empty.
286+
assert!(batch_diff.storage_slots.is_empty());
287+
288+
// Apply slots again and check it was recorded.
289+
batch_diff.apply(test_state_diff_acc1());
290+
assert!(
291+
batch_diff
292+
.storage_slots
293+
.get(&account1())
294+
.unwrap()
295+
.get(&slot1())
296+
.unwrap()
297+
== &slot_changes().get(&slot1()).unwrap().present_value()
298+
)
299+
}
300+
301+
#[test]
302+
fn smart_contract_diff() {
303+
let mut test_diff = BlockStateDiff {
304+
state: HashMap::default(),
305+
contracts: HashMap::default(),
306+
};
307+
test_diff.contracts.insert(
308+
FixedBytes::default(),
309+
revm_primitives::Bytecode::LegacyRaw(b"123".into()),
310+
);
311+
312+
let mut batch_diff = BatchStateDiff::new();
313+
batch_diff.apply(test_diff);
314+
assert!(!batch_diff.contracts.is_empty())
315+
}
102316
}

0 commit comments

Comments
 (0)