1use alloc::vec::Vec;
4use alloy_primitives::{
5 keccak256,
6 map::{hash_map::Entry, AddressMap, B256Map, U256Map},
7 Address, B256, U256,
8};
9use evm2::{bytecode::Bytecode, evm::AccountInfo};
10use reth_primitives_traits::Account;
11use reth_trie_common::{
12 bundle_state::{
13 AccountStatus, BundleRetention, BundleState, StorageSlot, TransitionAccount,
14 TransitionState,
15 },
16 HashedPostState, HashedStorage,
17};
18
19#[derive(Debug, Default)]
21pub struct BlockState {
22 transitions: AddressMap<TransitionAccount>,
23 contracts: B256Map<Bytecode>,
24}
25
26impl BlockState {
27 pub fn new() -> Self {
29 Self::default()
30 }
31
32 pub fn commit(&mut self, changes: &TransactionChanges) {
34 self.contracts.extend(changes.contracts.iter().map(|(hash, code)| (*hash, code.clone())));
35 for (address, account) in &changes.accounts {
36 self.commit_account(
37 *address,
38 account.original.clone(),
39 (!account.selfdestructed).then(|| account.info.clone()),
40 account.created,
41 account.wiped,
42 account
43 .storage
44 .iter()
45 .filter_map(|(key, slot)| slot.is_changed().then_some((*key, *slot))),
46 );
47 }
48 }
49
50 pub fn commit_account_changes(
56 &mut self,
57 changes: evm2::evm::AccountChanges<'_>,
58 update: Option<&mut StateUpdate>,
59 ) {
60 if !changes.is_changed() {
61 return;
62 }
63 if let Some((hash, code)) = changes.code {
64 self.contracts.entry(hash).or_insert_with(|| code.clone());
65 }
66 let address = changes.address;
67 let created = changes.created;
68 let wiped = changes.storage.wiped;
69 let original = changes.original.map(without_code);
70 let current = changes.current.map(without_code);
71 let slots = changes
72 .storage
73 .changed_slots()
74 .map(|(&key, value)| (key, StorageSlot::new_changed(value.original, value.current)));
75 let Some(update) = update else {
76 self.commit_account(address, original, current, created, wiped, slots);
77 return;
78 };
79 let storage: Vec<_> = slots.collect();
80 self.commit_account(
81 address,
82 original.clone(),
83 current.clone(),
84 created,
85 wiped,
86 storage.iter().copied(),
87 );
88 update.accounts.push(AccountUpdate { address, original, current, created, wiped, storage });
89 }
90
91 fn commit_account(
92 &mut self,
93 address: Address,
94 original: Option<AccountInfo>,
95 current: Option<AccountInfo>,
96 created: bool,
97 wiped: bool,
98 storage: impl Iterator<Item = (U256, StorageSlot)>,
99 ) {
100 let entry = self.transitions.entry(address);
101 let previous_status = match &entry {
102 Entry::Occupied(entry) => entry.get().status,
103 Entry::Vacant(_) => loaded_status(original.as_ref()),
104 };
105 let status = if current.is_none() {
106 previous_status.on_selfdestructed()
107 } else if wiped && !created {
108 AccountStatus::DestroyedChanged
109 } else if created {
110 previous_status.on_created()
111 } else {
112 previous_status.on_changed(
113 original.as_ref().is_none_or(|i| i.nonce == 0 && i.is_empty_code_hash()),
114 )
115 };
116 match entry {
117 Entry::Vacant(entry) => {
118 entry.insert(TransitionAccount {
119 info: current,
120 status,
121 previous_info: original,
122 previous_status,
123 storage: storage.collect(),
124 storage_was_destroyed: wiped,
125 });
126 }
127 Entry::Occupied(mut entry) => {
128 let account = entry.get_mut();
129 account.info = current;
130 account.status = status;
131 if matches!(status, AccountStatus::Destroyed | AccountStatus::DestroyedAgain) {
133 account.storage.clear();
134 account.storage.extend(storage);
135 account.storage_was_destroyed = true;
136 } else {
137 for (key, slot) in storage {
138 match account.storage.entry(key) {
139 Entry::Vacant(entry) => {
140 entry.insert(slot);
141 }
142 Entry::Occupied(mut entry) => {
143 if entry.get().original_value() == slot.present_value() {
144 entry.remove();
145 } else {
146 entry.get_mut().present_value = slot.present_value();
147 }
148 }
149 }
150 }
151 }
152 }
153 }
154 }
155
156 pub fn into_bundle(self) -> BundleState {
158 let mut bundle = BundleState::default();
159 bundle.apply_transitions_and_create_reverts(
160 TransitionState { transitions: self.transitions },
161 BundleRetention::Reverts,
162 );
163 bundle.contracts.extend(self.contracts);
164 bundle
165 }
166}
167
168#[derive(Debug, Default)]
170pub struct TransactionChanges {
171 accounts: AddressMap<TransactionAccount>,
172 contracts: B256Map<Bytecode>,
173}
174
175#[derive(Debug)]
177struct TransactionAccount {
178 original: Option<AccountInfo>,
180 info: AccountInfo,
182 created: bool,
183 selfdestructed: bool,
184 wiped: bool,
185 storage: U256Map<StorageSlot>,
186}
187
188impl Default for TransactionAccount {
189 fn default() -> Self {
190 Self {
191 original: Some(AccountInfo::empty()),
192 info: AccountInfo::empty(),
193 created: false,
194 selfdestructed: false,
195 wiped: false,
196 storage: U256Map::default(),
197 }
198 }
199}
200
201impl TransactionAccount {
202 fn update(&mut self, change: evm2::evm::AccountChangeRef<'_>) {
203 self.original = change.original.map(without_code);
204 self.info = change.current.map(without_code).unwrap_or_else(AccountInfo::empty);
205 self.created |= change.created;
206 self.selfdestructed |= change.current.is_none();
207 }
208}
209
210impl evm2::evm::StateChangeSink for TransactionChanges {
211 type Error = core::convert::Infallible;
212 fn bytecode(&mut self, hash: B256, code: &Bytecode) -> Result<(), Self::Error> {
213 self.contracts.entry(hash).or_insert_with(|| code.clone());
214 Ok(())
215 }
216 fn storage_wipe(&mut self, address: Address) -> Result<(), Self::Error> {
217 let account = self.accounts.entry(address).or_default();
218 account.wiped = true;
219 account.storage.clear();
220 Ok(())
221 }
222 fn storage(&mut self, change: evm2::evm::StorageChange) -> Result<(), Self::Error> {
223 self.accounts
224 .entry(change.address)
225 .or_default()
226 .storage
227 .insert(change.key, StorageSlot::new_changed(change.original, change.current));
228 Ok(())
229 }
230 fn account(&mut self, change: evm2::evm::AccountChangeRef<'_>) -> Result<(), Self::Error> {
231 self.accounts.entry(change.address).or_default().update(change);
232 Ok(())
233 }
234 fn account_read(
235 &mut self,
236 address: Address,
237 info: Option<&AccountInfo>,
238 ) -> Result<(), Self::Error> {
239 if let Some(account) = self.accounts.get_mut(&address) {
240 account.update(evm2::evm::AccountChangeRef {
241 address,
242 original: info,
243 current: info,
244 created: false,
245 selfdestructed: false,
246 });
247 }
248 Ok(())
249 }
250}
251
252#[derive(Clone, Debug, Default, PartialEq, Eq)]
257pub struct StateUpdate {
258 pub accounts: Vec<AccountUpdate>,
260}
261
262impl StateUpdate {
263 pub const fn is_empty(&self) -> bool {
265 self.accounts.is_empty()
266 }
267
268 pub fn account(&self, address: &Address) -> Option<&AccountUpdate> {
270 self.accounts.iter().find(|account| account.address == *address)
271 }
272}
273
274#[derive(Clone, Debug, PartialEq, Eq)]
276pub struct AccountUpdate {
277 pub address: Address,
279 pub original: Option<AccountInfo>,
281 pub current: Option<AccountInfo>,
283 pub created: bool,
285 pub wiped: bool,
287 pub storage: Vec<(U256, StorageSlot)>,
289}
290
291pub fn state_update_to_hashed_post_state(update: &StateUpdate) -> HashedPostState {
293 let mut hashed_state = HashedPostState::with_capacity(update.accounts.len());
294 for account in &update.accounts {
295 let hashed_address = keccak256(account.address);
296 let destroyed = account.current.is_none();
297 let info = account.current.clone().unwrap_or_default();
298 let unchanged = account
299 .original
300 .as_ref()
301 .map_or_else(|| info == AccountInfo::default(), |original| info == *original);
302 if destroyed || (info.is_empty() && account.original.is_some()) {
304 hashed_state.accounts.insert(hashed_address, None);
305 } else if !unchanged {
306 hashed_state.accounts.insert(hashed_address, Some(Account::from(&info)));
307 }
308 if !destroyed && !account.storage.is_empty() {
309 hashed_state.storages.insert(
310 hashed_address,
311 HashedStorage::from_iter(
312 account
313 .storage
314 .iter()
315 .map(|(key, slot)| (keccak256(B256::from(*key)), slot.present_value)),
316 ),
317 );
318 }
319 }
320 hashed_state
321}
322
323fn without_code(info: &AccountInfo) -> AccountInfo {
324 AccountInfo { code: None, ..info.clone() }
325}
326
327#[derive(Debug)]
329pub struct BundleSource<'a>(pub &'a BundleState);
330
331impl evm2::evm::StateChangeSource for BundleSource<'_> {
332 fn visit<S: evm2::evm::StateChangeSink>(&self, sink: &mut S) -> Result<(), S::Error> {
333 for (hash, code) in &self.0.contracts {
334 sink.bytecode(*hash, code)?;
335 }
336 for (address, account) in &self.0.state {
337 let current = account.info.as_ref();
338 if account.status.was_destroyed() {
339 sink.storage_wipe(*address)?;
340 }
341 sink.account(evm2::evm::AccountChangeRef {
342 address: *address,
343 original: account.original_info.as_ref(),
344 current,
345 created: account.status.is_storage_known() && current.is_some(),
346 selfdestructed: current.is_none(),
347 })?;
348 for (key, value) in &account.storage {
349 sink.storage(evm2::evm::StorageChange {
350 address: *address,
351 key: *key,
352 original: value.original_value(),
353 current: value.present_value(),
354 })?;
355 }
356 }
357 Ok(())
358 }
359}
360
361fn loaded_status(info: Option<&AccountInfo>) -> AccountStatus {
362 match info {
363 None => AccountStatus::LoadedNotExisting,
364 Some(info) if info.is_empty() => AccountStatus::LoadedEmptyEIP161,
365 Some(_) => AccountStatus::Loaded,
366 }
367}
368
369#[cfg(test)]
370mod tests {
371 use super::*;
372 use evm2::evm::{
373 AccountChangeRef, AccountChanges, StateChangeSink, StorageChange, StorageOverlay,
374 StorageSlot as NativeSlot, Tracked,
375 };
376
377 fn overlay(wiped: bool, slots: &[(U256, U256, U256)]) -> StorageOverlay {
379 let mut storage = StorageOverlay { wiped, ..Default::default() };
380 for &(key, original, current) in slots {
381 let slot =
382 NativeSlot { value: Tracked::from_parts(original, current), ..Default::default() };
383 storage.slots.insert(key, slot);
384 }
385 storage
386 }
387
388 #[cfg(feature = "account-ext")]
389 #[test]
390 fn account_extensions_survive_native_updates_and_reverts() {
391 let address = Address::with_last_byte(1);
392 let original = AccountInfo {
393 balance: U256::from(10),
394 extension: evm2::evm::AccountExtension::copy_from_slice(b"original extension"),
395 ..Default::default()
396 };
397 let mut updated = original.clone();
398 updated.balance = U256::from(20);
399 updated.extension = evm2::evm::AccountExtension::copy_from_slice(b"updated extension");
400 let mut state = BlockState::new();
401 state.commit_account_changes(
402 AccountChanges {
403 address,
404 original: Some(&original),
405 current: Some(&updated),
406 created: false,
407 selfdestructed: false,
408 touched: false,
409 code: None,
410 storage: &StorageOverlay::default(),
411 },
412 None,
413 );
414 let mut bundle = state.into_bundle();
415 let account = bundle.state.get(&address).unwrap();
416 assert_eq!(
417 account.original_info.as_ref().unwrap().extension.as_ref(),
418 b"original extension"
419 );
420 assert_eq!(account.info.as_ref().unwrap().extension.as_ref(), b"updated extension");
421 assert!(bundle.revert_latest());
422 assert_eq!(
423 bundle.state.get(&address).unwrap().info.as_ref().unwrap().extension.as_ref(),
424 b"original extension"
425 );
426 }
427
428 #[test]
429 fn native_sink_preserves_storage_lifetimes_and_reverts() {
430 let address = Address::with_last_byte(1);
431 let code = evm2::bytecode::Bytecode::new_raw(alloy_primitives::bytes!("60015b00"));
432 let info = AccountInfo::default().with_nonce(1).with_code(code.clone());
433 let mut converted = BlockState::new();
434 let mut native = BlockState::new();
435 for step in 0..7 {
438 let slots: &[_] = if step < 2 || step == 3 || step == 4 {
439 let (original, current) = if step == 1 { (8, 7) } else { (7, 8) };
440 &[(U256::from(3), U256::from(original), U256::from(current))]
441 } else {
442 &[]
443 };
444 let storage = overlay(step >= 2, slots);
445 let changes = if step < 2 {
446 AccountChanges {
447 address,
448 original: Some(&info),
449 current: Some(&info),
450 created: false,
451 selfdestructed: false,
452 touched: false,
453 code: None,
454 storage: &storage,
455 }
456 } else {
457 AccountChanges {
458 address,
459 original: (step != 3 && step != 6).then_some(&info),
460 current: (step == 3 || step == 4).then_some(&info),
461 created: step == 3,
462 selfdestructed: step != 3,
463 touched: false,
464 code: (step == 3).then(|| (code.hash_slow(), &code)),
465 storage: &storage,
466 }
467 };
468 let empty = StorageOverlay::default();
469 let read = AccountChanges {
471 address: Address::with_last_byte(2),
472 original: Some(&info),
473 current: Some(&info),
474 created: false,
475 selfdestructed: false,
476 touched: false,
477 code: None,
478 storage: &empty,
479 };
480 let mut transaction = TransactionChanges::default();
481 let Ok(()) = changes.visit(&mut transaction);
482 let Ok(()) = read.visit(&mut transaction);
483 converted.commit(&transaction);
484 native.commit_account_changes(changes, None);
485 native.commit_account_changes(read, None);
486 assert_eq!(native.transitions, converted.transitions, "step {step}");
487 assert_eq!(native.contracts, converted.contracts);
488 }
489 let mut native = native.into_bundle();
490 let mut converted = converted.into_bundle();
491 assert_eq!(native, converted);
492 assert!(native.revert_latest());
493 assert!(converted.revert_latest());
494 assert_eq!(native, converted);
495 }
496
497 #[test]
498 fn cached_bytecode_does_not_change_bundle_output() {
499 let address = Address::with_last_byte(1);
500 let code = evm2::bytecode::Bytecode::new_raw(alloy_primitives::bytes!("60015b00"));
501 let info = AccountInfo {
502 balance: U256::from(5),
503 code_hash: code.hash_slow(),
504 ..Default::default()
505 };
506 let bundle = |info: &AccountInfo| {
507 let mut changes = TransactionChanges::default();
508 changes
509 .account(AccountChangeRef {
510 address,
511 original: None,
512 current: Some(info),
513 created: false,
514 selfdestructed: false,
515 })
516 .unwrap();
517 let mut block = BlockState::new();
518 block.commit(&changes);
519 block.into_bundle()
520 };
521 let uncached = bundle(&info);
522 let cached = bundle(&AccountInfo { code: Some(code), ..info });
523 assert_eq!(uncached, cached);
524 assert!(cached.contracts.is_empty());
525 }
526
527 #[test]
528 fn deployed_bytecode_is_retained_separately_from_account_metadata() {
529 let address = Address::with_last_byte(1);
530 let code = evm2::bytecode::Bytecode::new_raw(alloy_primitives::bytes!("60015b00"));
531 let hash = code.hash_slow();
532 let info = AccountInfo {
533 nonce: 1,
534 code_hash: hash,
535 code: Some(code.clone()),
536 ..Default::default()
537 };
538 let mut changes = TransactionChanges::default();
539 changes.bytecode(hash, &code).unwrap();
540 changes
541 .account(AccountChangeRef {
542 address,
543 original: None,
544 current: Some(&info),
545 created: true,
546 selfdestructed: false,
547 })
548 .unwrap();
549 let mut block = BlockState::new();
550 block.commit(&changes);
551 let bundle = block.into_bundle();
552 assert_eq!(bundle.contracts[&hash].original_bytes(), code.original_bytes());
553 assert_eq!(bundle.account(&address).unwrap().info.as_ref().unwrap().code_hash, hash);
554 }
555
556 #[test]
557 fn storage_only_change_retains_account_and_reverts() {
558 let address = Address::with_last_byte(1);
559 let info = AccountInfo { nonce: 1, ..Default::default() };
560 let mut changes = TransactionChanges::default();
561 changes
562 .storage(StorageChange {
563 address,
564 key: U256::from(3),
565 original: U256::from(7),
566 current: U256::from(8),
567 })
568 .unwrap();
569 changes.account_read(address, Some(&info)).unwrap();
570 let mut block = BlockState::new();
571 block.commit(&changes);
572 let mut bundle = block.into_bundle();
573 assert_eq!(bundle.account(&address).unwrap().info.as_ref().unwrap().nonce, 1);
574 assert_eq!(bundle.storage(&address, U256::from(3)), Some(U256::from(8)));
575 assert_eq!(bundle.reverts.len(), 1);
576 assert!(bundle.revert_latest());
577 assert_eq!(bundle.storage(&address, U256::from(3)), Some(U256::from(7)));
578 }
579
580 #[test]
581 fn destroy_then_recreate_retains_parent_storage_wipe() {
582 let address = Address::with_last_byte(1);
583 let original = AccountInfo { nonce: 1, balance: U256::from(7), ..Default::default() };
584 let recreated = AccountInfo { nonce: 1, balance: U256::from(9), ..Default::default() };
585 let mut block = BlockState::new();
586 let mut destroyed = TransactionChanges::default();
587 destroyed
588 .account(AccountChangeRef {
589 address,
590 original: Some(&original),
591 current: None,
592 created: false,
593 selfdestructed: true,
594 })
595 .unwrap();
596 block.commit(&destroyed);
597 let mut created = TransactionChanges::default();
598 created.storage_wipe(address).unwrap();
599 created
600 .storage(StorageChange {
601 address,
602 key: U256::from(3),
603 original: U256::ZERO,
604 current: U256::from(8),
605 })
606 .unwrap();
607 created
608 .account(AccountChangeRef {
609 address,
610 original: None,
611 current: Some(&recreated),
612 created: true,
613 selfdestructed: false,
614 })
615 .unwrap();
616 block.commit(&created);
617 let bundle = block.into_bundle();
618 let account = bundle.account(&address).unwrap();
619 assert!(account.was_destroyed());
620 assert_eq!(account.original_info.as_ref().unwrap().balance, U256::from(7));
621 assert_eq!(account.info.as_ref().unwrap().balance, U256::from(9));
622 assert_eq!(account.storage_slot(U256::from(4)), Some(U256::ZERO));
623 assert_eq!(account.storage_slot(U256::from(3)), Some(U256::from(8)));
624 assert!(bundle.reverts[0][0].1.wipe_storage);
625 }
626}