1use crate::{
2 providers::{StaticFileProvider, StaticFileWriter as SfWriter},
3 BlockExecutionWriter, BlockWriter, HistoryWriter, StateWriter, StaticFileProviderFactory,
4 StorageLocation, TrieWriter,
5};
6use alloy_consensus::BlockHeader;
7use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates};
8use reth_db_api::transaction::{DbTx, DbTxMut};
9use reth_errors::ProviderResult;
10use reth_primitives_traits::{NodePrimitives, SignedTransaction};
11use reth_static_file_types::StaticFileSegment;
12use reth_storage_api::{DBProvider, StageCheckpointWriter, TransactionsProviderExt};
13use reth_storage_errors::writer::UnifiedStorageWriterError;
14use revm_database::OriginalValuesKnown;
15use std::sync::Arc;
16use tracing::debug;
17
18#[derive(Debug)]
21pub struct UnifiedStorageWriter<'a, ProviderDB, ProviderSF> {
22 database: &'a ProviderDB,
23 static_file: Option<ProviderSF>,
24}
25
26impl<'a, ProviderDB, ProviderSF> UnifiedStorageWriter<'a, ProviderDB, ProviderSF> {
27 pub const fn new(database: &'a ProviderDB, static_file: Option<ProviderSF>) -> Self {
33 Self { database, static_file }
34 }
35
36 pub fn from<P>(database: &'a P, static_file: ProviderSF) -> Self
39 where
40 P: AsRef<ProviderDB>,
41 {
42 Self::new(database.as_ref(), Some(static_file))
43 }
44
45 pub fn from_database<P>(database: &'a P) -> Self
47 where
48 P: AsRef<ProviderDB>,
49 {
50 Self::new(database.as_ref(), None)
51 }
52
53 const fn database(&self) -> &ProviderDB {
58 self.database
59 }
60
61 const fn static_file(&self) -> &ProviderSF {
66 self.static_file.as_ref().expect("should exist")
67 }
68
69 #[expect(unused)]
75 const fn ensure_static_file(&self) -> Result<(), UnifiedStorageWriterError> {
76 if self.static_file.is_none() {
77 return Err(UnifiedStorageWriterError::MissingStaticFileWriter)
78 }
79 Ok(())
80 }
81}
82
83impl UnifiedStorageWriter<'_, (), ()> {
84 pub fn commit<P>(provider: P) -> ProviderResult<()>
93 where
94 P: DBProvider<Tx: DbTxMut> + StaticFileProviderFactory,
95 {
96 let static_file = provider.static_file_provider();
97 static_file.commit()?;
98 provider.commit()?;
99 Ok(())
100 }
101
102 pub fn commit_unwind<P>(provider: P) -> ProviderResult<()>
111 where
112 P: DBProvider<Tx: DbTxMut> + StaticFileProviderFactory,
113 {
114 let static_file = provider.static_file_provider();
115 provider.commit()?;
116 static_file.commit()?;
117 Ok(())
118 }
119}
120
121impl<ProviderDB> UnifiedStorageWriter<'_, ProviderDB, &StaticFileProvider<ProviderDB::Primitives>>
122where
123 ProviderDB: DBProvider<Tx: DbTx + DbTxMut>
124 + BlockWriter
125 + TransactionsProviderExt
126 + TrieWriter
127 + StateWriter
128 + HistoryWriter
129 + StageCheckpointWriter
130 + BlockExecutionWriter
131 + AsRef<ProviderDB>
132 + StaticFileProviderFactory,
133{
134 pub fn save_blocks<N>(&self, blocks: Vec<ExecutedBlockWithTrieUpdates<N>>) -> ProviderResult<()>
136 where
137 N: NodePrimitives<SignedTx: SignedTransaction>,
138 ProviderDB: BlockWriter<Block = N::Block> + StateWriter<Receipt = N::Receipt>,
139 {
140 if blocks.is_empty() {
141 debug!(target: "provider::storage_writer", "Attempted to write empty block range");
142 return Ok(())
143 }
144
145 let first_block = blocks.first().unwrap().recovered_block();
147
148 let last_block = blocks.last().unwrap().recovered_block();
149 let first_number = first_block.number();
150 let last_block_number = last_block.number();
151
152 debug!(target: "provider::storage_writer", block_count = %blocks.len(), "Writing blocks and execution data to storage");
153
154 for ExecutedBlockWithTrieUpdates {
164 block: ExecutedBlock { recovered_block, execution_output, hashed_state },
165 trie,
166 } in blocks
167 {
168 self.database()
169 .insert_block(Arc::unwrap_or_clone(recovered_block), StorageLocation::Both)?;
170
171 self.database().write_state(
174 &execution_output,
175 OriginalValuesKnown::No,
176 StorageLocation::StaticFiles,
177 )?;
178
179 self.database()
181 .write_hashed_state(&Arc::unwrap_or_clone(hashed_state).into_sorted())?;
182 self.database().write_trie_updates(&trie)?;
183 }
184
185 self.database().update_history_indices(first_number..=last_block_number)?;
187
188 self.database().update_pipeline_stages(last_block_number, false)?;
190
191 debug!(target: "provider::storage_writer", range = ?first_number..=last_block_number, "Appended block data");
192
193 Ok(())
194 }
195
196 pub fn remove_blocks_above(&self, block_number: u64) -> ProviderResult<()> {
200 debug!(target: "provider::storage_writer", ?block_number, "Removing blocks from database above block_number");
202 self.database().remove_block_and_execution_above(block_number, StorageLocation::Both)?;
203
204 let highest_static_file_block = self
206 .static_file()
207 .get_highest_static_file_block(StaticFileSegment::Headers)
208 .expect("todo: error handling, headers should exist");
209
210 debug!(target: "provider::storage_writer", ?block_number, "Removing static file blocks above block_number");
216 self.static_file()
217 .get_writer(block_number, StaticFileSegment::Headers)?
218 .prune_headers(highest_static_file_block.saturating_sub(block_number))?;
219
220 Ok(())
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use crate::{
228 test_utils::create_test_provider_factory, AccountReader, StorageTrieWriter, TrieWriter,
229 };
230 use alloy_primitives::{keccak256, map::HashMap, Address, B256, U256};
231 use reth_db_api::{
232 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
233 models::{AccountBeforeTx, BlockNumberAddress},
234 tables,
235 transaction::{DbTx, DbTxMut},
236 };
237 use reth_ethereum_primitives::Receipt;
238 use reth_execution_types::ExecutionOutcome;
239 use reth_primitives_traits::{Account, StorageEntry};
240 use reth_storage_api::{DatabaseProviderFactory, HashedPostStateProvider};
241 use reth_trie::{
242 test_utils::{state_root, storage_root_prehashed},
243 HashedPostState, HashedStorage, StateRoot, StorageRoot,
244 };
245 use reth_trie_db::{DatabaseStateRoot, DatabaseStorageRoot};
246 use revm_database::{
247 states::{
248 bundle_state::BundleRetention, changes::PlainStorageRevert, PlainStorageChangeset,
249 },
250 BundleState, State,
251 };
252 use revm_database_interface::{DatabaseCommit, EmptyDB};
253 use revm_state::{
254 Account as RevmAccount, AccountInfo as RevmAccountInfo, AccountStatus, EvmStorageSlot,
255 FlaggedStorage,
256 };
257 use std::{collections::BTreeMap, str::FromStr};
258
259 #[test]
260 fn wiped_entries_are_removed() {
261 let provider_factory = create_test_provider_factory();
262
263 let addresses = (0..10).map(|_| Address::random()).collect::<Vec<_>>();
264 let destroyed_address = *addresses.first().unwrap();
265 let destroyed_address_hashed = keccak256(destroyed_address);
266 let slot = B256::with_last_byte(1);
267 let hashed_slot = keccak256(slot);
268 {
269 let provider_rw = provider_factory.provider_rw().unwrap();
270 let mut accounts_cursor =
271 provider_rw.tx_ref().cursor_write::<tables::HashedAccounts>().unwrap();
272 let mut storage_cursor =
273 provider_rw.tx_ref().cursor_write::<tables::HashedStorages>().unwrap();
274
275 for address in addresses {
276 let hashed_address = keccak256(address);
277 accounts_cursor
278 .insert(hashed_address, &Account { nonce: 1, ..Default::default() })
279 .unwrap();
280 storage_cursor
281 .insert(
282 hashed_address,
283 &StorageEntry { key: hashed_slot, value: U256::from(1), is_private: false },
284 )
285 .unwrap();
286 }
287 provider_rw.commit().unwrap();
288 }
289
290 let mut hashed_state = HashedPostState::default();
291 hashed_state.accounts.insert(destroyed_address_hashed, None);
292 hashed_state.storages.insert(destroyed_address_hashed, HashedStorage::new(true));
293
294 let provider_rw = provider_factory.provider_rw().unwrap();
295 assert!(matches!(provider_rw.write_hashed_state(&hashed_state.into_sorted()), Ok(())));
296 provider_rw.commit().unwrap();
297
298 let provider = provider_factory.provider().unwrap();
299 assert_eq!(
300 provider.tx_ref().get::<tables::HashedAccounts>(destroyed_address_hashed),
301 Ok(None)
302 );
303 assert_eq!(
304 provider
305 .tx_ref()
306 .cursor_read::<tables::HashedStorages>()
307 .unwrap()
308 .seek_by_key_subkey(destroyed_address_hashed, hashed_slot),
309 Ok(None)
310 );
311 }
312
313 #[test]
314 fn write_to_db_account_info() {
315 let factory = create_test_provider_factory();
316 let provider = factory.provider_rw().unwrap();
317
318 let address_a = Address::ZERO;
319 let address_b = Address::repeat_byte(0xff);
320
321 let account_a = RevmAccountInfo { balance: U256::from(1), nonce: 1, ..Default::default() };
322 let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
323 let account_b_changed =
324 RevmAccountInfo { balance: U256::from(3), nonce: 3, ..Default::default() };
325
326 let mut state = State::builder().with_bundle_update().build();
327 state.insert_not_existing(address_a);
328 state.insert_account(address_b, account_b.clone());
329
330 state.commit(HashMap::from_iter([(
332 address_a,
333 RevmAccount {
334 info: account_a.clone(),
335 status: AccountStatus::Touched | AccountStatus::Created,
336 storage: HashMap::default(),
337 },
338 )]));
339
340 state.commit(HashMap::from_iter([(
342 address_b,
343 RevmAccount {
344 info: account_b_changed.clone(),
345 status: AccountStatus::Touched,
346 storage: HashMap::default(),
347 },
348 )]));
349
350 state.merge_transitions(BundleRetention::Reverts);
351 let mut revm_bundle_state = state.take_bundle();
352
353 let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
355 let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
356 assert!(plain_state.storage.is_empty());
357 assert!(plain_state.contracts.is_empty());
358 provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
359
360 assert_eq!(reverts.storage, [[]]);
361 provider.write_state_reverts(reverts, 1).expect("Could not write reverts to DB");
362
363 let reth_account_a = account_a.into();
364 let reth_account_b = account_b.into();
365 let reth_account_b_changed = (&account_b_changed).into();
366
367 assert_eq!(
369 provider.basic_account(&address_a).expect("Could not read account state"),
370 Some(reth_account_a),
371 "Account A state is wrong"
372 );
373 assert_eq!(
374 provider.basic_account(&address_b).expect("Could not read account state"),
375 Some(reth_account_b_changed),
376 "Account B state is wrong"
377 );
378
379 let mut changeset_cursor = provider
381 .tx_ref()
382 .cursor_dup_read::<tables::AccountChangeSets>()
383 .expect("Could not open changeset cursor");
384 assert_eq!(
385 changeset_cursor.seek_exact(1).expect("Could not read account change set"),
386 Some((1, AccountBeforeTx { address: address_a, info: None })),
387 "Account A changeset is wrong"
388 );
389 assert_eq!(
390 changeset_cursor.next_dup().expect("Changeset table is malformed"),
391 Some((1, AccountBeforeTx { address: address_b, info: Some(reth_account_b) })),
392 "Account B changeset is wrong"
393 );
394
395 let mut state = State::builder().with_bundle_update().build();
396 state.insert_account(address_b, account_b_changed.clone());
397
398 state.commit(HashMap::from_iter([(
400 address_b,
401 RevmAccount {
402 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
403 info: account_b_changed,
404 storage: HashMap::default(),
405 },
406 )]));
407
408 state.merge_transitions(BundleRetention::Reverts);
409 let mut revm_bundle_state = state.take_bundle();
410
411 let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
413 let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
414 assert_eq!(
416 plain_state.storage,
417 [PlainStorageChangeset { address: address_b, wipe_storage: true, storage: vec![] }]
418 );
419 assert!(plain_state.contracts.is_empty());
420 provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
421
422 assert_eq!(
423 reverts.storage,
424 [[PlainStorageRevert { address: address_b, wiped: true, storage_revert: vec![] }]]
425 );
426 provider.write_state_reverts(reverts, 2).expect("Could not write reverts to DB");
427
428 assert_eq!(
430 provider.basic_account(&address_b).expect("Could not read account state"),
431 None,
432 "Account B should be deleted"
433 );
434
435 assert_eq!(
437 changeset_cursor.seek_exact(2).expect("Could not read account change set"),
438 Some((2, AccountBeforeTx { address: address_b, info: Some(reth_account_b_changed) })),
439 "Account B changeset is wrong after deletion"
440 );
441 }
442
443 #[test]
444 fn write_to_db_storage() {
445 let factory = create_test_provider_factory();
446 let provider = factory.database_provider_rw().unwrap();
447
448 let address_a = Address::ZERO;
449 let address_b = Address::repeat_byte(0xff);
450 let address_c = Address::random();
451
452 let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
453 let account_c = RevmAccountInfo { balance: U256::from(1), nonce: 3, ..Default::default() };
454
455 let mut state = State::builder().with_bundle_update().build();
456 state.insert_not_existing(address_a);
457 state.insert_account_with_storage(
458 address_b,
459 account_b.clone(),
460 HashMap::from_iter([(U256::from(1), FlaggedStorage::new(1, false))]),
461 );
462 state.insert_account_with_storage(
463 address_c,
464 account_c.clone(),
465 HashMap::from_iter([(U256::from(3), FlaggedStorage::new(1, false))]),
466 );
467
468 state.commit(HashMap::from_iter([
469 (
470 address_a,
471 RevmAccount {
472 status: AccountStatus::Touched | AccountStatus::Created,
473 info: RevmAccountInfo::default(),
474 storage: HashMap::from_iter([
477 (
478 U256::from(0),
479 EvmStorageSlot {
480 present_value: FlaggedStorage::new(1, true),
481 ..Default::default()
482 },
483 ),
484 (
485 U256::from(1),
486 EvmStorageSlot {
487 present_value: FlaggedStorage::new(2, true),
488 ..Default::default()
489 },
490 ),
491 ]),
492 },
493 ),
494 (
495 address_b,
496 RevmAccount {
497 status: AccountStatus::Touched,
498 info: account_b,
499 storage: HashMap::from_iter([(
501 U256::from(1),
502 EvmStorageSlot {
503 present_value: FlaggedStorage::new(2, false),
504 original_value: FlaggedStorage::new(1, false),
505 ..Default::default()
506 },
507 )]),
508 },
509 ),
510 (
511 address_c,
512 RevmAccount {
513 status: AccountStatus::Touched,
514 info: account_c,
515 storage: HashMap::from_iter([(
517 U256::from(3),
518 EvmStorageSlot {
519 present_value: FlaggedStorage::new(2, true),
520 original_value: FlaggedStorage::new(1, false),
521 ..Default::default()
522 },
523 )]),
524 },
525 ),
526 ]));
527
528 state.merge_transitions(BundleRetention::Reverts);
529
530 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
531 provider
532 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
533 .expect("Could not write bundle state to DB");
534
535 let mut storage_cursor = provider
537 .tx_ref()
538 .cursor_dup_read::<tables::PlainStorageState>()
539 .expect("Could not open plain storage state cursor");
540
541 assert_eq!(
542 storage_cursor.seek_exact(address_a).unwrap(),
543 Some((
544 address_a,
545 StorageEntry { key: B256::ZERO, value: U256::from(1), is_private: true }
546 )),
547 "Slot 0 for account A should be a private 1"
548 );
549 assert_eq!(
550 storage_cursor.next_dup().unwrap(),
551 Some((
552 address_a,
553 StorageEntry {
554 key: B256::from(U256::from(1).to_be_bytes()),
555 value: U256::from(2),
556 is_private: true
557 }
558 )),
559 "Slot 1 for account A should be a private 2"
560 );
561 assert_eq!(
562 storage_cursor.next_dup().unwrap(),
563 None,
564 "Account A should only have 2 storage slots"
565 );
566
567 assert_eq!(
568 storage_cursor.seek_exact(address_b).unwrap(),
569 Some((
570 address_b,
571 StorageEntry {
572 key: B256::from(U256::from(1).to_be_bytes()),
573 value: U256::from(2),
574 is_private: false
575 }
576 )),
577 "Slot 1 for account B should be a public 2"
578 );
579 assert_eq!(
580 storage_cursor.next_dup().unwrap(),
581 None,
582 "Account B should only have 1 storage slot"
583 );
584 assert_eq!(
585 storage_cursor.seek_exact(address_c).unwrap(),
586 Some((
587 address_c,
588 StorageEntry {
589 key: B256::from(U256::from(3).to_be_bytes()),
590 value: U256::from(2),
591 is_private: true
592 }
593 )),
594 "Slot 3 for account C should be a private 2"
595 );
596 assert_eq!(
597 storage_cursor.next_dup().unwrap(),
598 None,
599 "Account C should only have 1 storage slot"
600 );
601
602 let mut changeset_cursor = provider
604 .tx_ref()
605 .cursor_dup_read::<tables::StorageChangeSets>()
606 .expect("Could not open storage changeset cursor");
607 assert_eq!(
608 changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(),
609 Some((
610 BlockNumberAddress((1, address_a)),
611 StorageEntry { key: B256::ZERO, value: U256::from(0), is_private: false }
612 )),
613 "Slot 0 for account A should have changed from a public 0"
614 );
615 assert_eq!(
616 changeset_cursor.next_dup().unwrap(),
617 Some((
618 BlockNumberAddress((1, address_a)),
619 StorageEntry {
620 key: B256::from(U256::from(1).to_be_bytes()),
621 value: U256::from(0),
622 is_private: false
623 }
624 )),
625 "Slot 1 for account A should have changed from a public 0"
626 );
627 assert_eq!(
628 changeset_cursor.next_dup().unwrap(),
629 None,
630 "Account A should only be in the changeset 2 times"
631 );
632
633 assert_eq!(
634 changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(),
635 Some((
636 BlockNumberAddress((1, address_b)),
637 StorageEntry {
638 key: B256::from(U256::from(1).to_be_bytes()),
639 value: U256::from(1),
640 is_private: false
641 }
642 )),
643 "Slot 1 for account B should have changed from a public 1"
644 );
645 assert_eq!(
646 changeset_cursor.next_dup().unwrap(),
647 None,
648 "Account B should only be in the changeset 1 time"
649 );
650
651 assert_eq!(
652 changeset_cursor.seek_exact(BlockNumberAddress((1, address_c))).unwrap(),
653 Some((
654 BlockNumberAddress((1, address_c)),
655 StorageEntry {
656 key: B256::from(U256::from(3).to_be_bytes()),
657 value: U256::from(1),
658 is_private: false
659 }
660 )),
661 "Slot 1 for account C should have changed from a public 1"
662 );
663 assert_eq!(
664 changeset_cursor.next_dup().unwrap(),
665 None,
666 "Account C should only be in the changeset 1 time"
667 );
668
669 let mut state = State::builder().with_bundle_update().build();
671 state.insert_account(address_a, RevmAccountInfo::default());
672
673 state.commit(HashMap::from_iter([(
674 address_a,
675 RevmAccount {
676 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
677 info: RevmAccountInfo::default(),
678 storage: HashMap::default(),
679 },
680 )]));
681
682 state.merge_transitions(BundleRetention::Reverts);
683 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 2, Vec::new());
684 provider
685 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
686 .expect("Could not write bundle state to DB");
687
688 assert_eq!(
689 storage_cursor.seek_exact(address_a).unwrap(),
690 None,
691 "Account A should have no storage slots after deletion"
692 );
693
694 assert_eq!(
695 changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(),
696 Some((
697 BlockNumberAddress((2, address_a)),
698 StorageEntry { key: B256::ZERO, value: U256::from(1), is_private: true }
699 )),
700 "Slot 0 for account A should have changed from a private 1 on deletion"
701 );
702 assert_eq!(
703 changeset_cursor.next_dup().unwrap(),
704 Some((
705 BlockNumberAddress((2, address_a)),
706 StorageEntry {
707 key: B256::from(U256::from(1).to_be_bytes()),
708 value: U256::from(2),
709 is_private: true
710 }
711 )),
712 "Slot 1 for account A should have changed from a private 2 on deletion"
713 );
714 assert_eq!(
715 changeset_cursor.next_dup().unwrap(),
716 None,
717 "Account A should only be in the changeset 2 times on deletion"
718 );
719 }
720
721 #[test]
722 fn write_to_db_multiple_selfdestructs() {
723 let factory = create_test_provider_factory();
724 let provider = factory.database_provider_rw().unwrap();
725
726 let address1 = Address::random();
727 let account_info = RevmAccountInfo { nonce: 1, ..Default::default() };
728
729 let mut init_state = State::builder().with_bundle_update().build();
731 init_state.insert_not_existing(address1);
732 init_state.commit(HashMap::from_iter([(
733 address1,
734 RevmAccount {
735 info: account_info.clone(),
736 status: AccountStatus::Touched | AccountStatus::Created,
737 storage: HashMap::from_iter([
740 (
741 U256::ZERO,
742 EvmStorageSlot {
743 present_value: FlaggedStorage::new_from_value(1),
744 ..Default::default()
745 },
746 ),
747 (
748 U256::from(1),
749 EvmStorageSlot {
750 present_value: FlaggedStorage::new_from_value(2),
751 ..Default::default()
752 },
753 ),
754 ]),
755 },
756 )]));
757 init_state.merge_transitions(BundleRetention::Reverts);
758
759 let outcome =
760 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
761 provider
762 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
763 .expect("Could not write bundle state to DB");
764
765 let mut state = State::builder().with_bundle_update().build();
766 state.insert_account_with_storage(
767 address1,
768 account_info.clone(),
769 HashMap::from_iter([
770 (U256::ZERO, FlaggedStorage::new_from_value(1)),
771 (U256::from(1), FlaggedStorage::new_from_value(2)),
772 ]),
773 );
774
775 state.commit(HashMap::from_iter([(
777 address1,
778 RevmAccount {
779 status: AccountStatus::Touched,
780 info: account_info.clone(),
781 storage: HashMap::from_iter([(
783 U256::ZERO,
784 EvmStorageSlot {
785 original_value: FlaggedStorage::new_from_value(1),
786 present_value: FlaggedStorage::new_from_value(2),
787 ..Default::default()
788 },
789 )]),
790 },
791 )]));
792 state.merge_transitions(BundleRetention::Reverts);
793
794 state.commit(HashMap::from_iter([(
796 address1,
797 RevmAccount {
798 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
799 info: account_info.clone(),
800 storage: HashMap::default(),
801 },
802 )]));
803 state.merge_transitions(BundleRetention::Reverts);
804
805 state.commit(HashMap::from_iter([(
807 address1,
808 RevmAccount {
809 status: AccountStatus::Touched | AccountStatus::Created,
810 info: account_info.clone(),
811 storage: HashMap::default(),
812 },
813 )]));
814 state.merge_transitions(BundleRetention::Reverts);
815
816 state.commit(HashMap::from_iter([(
818 address1,
819 RevmAccount {
820 status: AccountStatus::Touched,
821 info: account_info.clone(),
822 storage: HashMap::from_iter([
826 (
827 U256::ZERO,
828 EvmStorageSlot {
829 present_value: FlaggedStorage::new_from_value(2),
830 ..Default::default()
831 },
832 ),
833 (
834 U256::from(2),
835 EvmStorageSlot {
836 present_value: FlaggedStorage::new_from_value(4),
837 ..Default::default()
838 },
839 ),
840 (
841 U256::from(6),
842 EvmStorageSlot {
843 present_value: FlaggedStorage::new_from_value(6),
844 ..Default::default()
845 },
846 ),
847 ]),
848 },
849 )]));
850 state.merge_transitions(BundleRetention::Reverts);
851
852 state.commit(HashMap::from_iter([(
854 address1,
855 RevmAccount {
856 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
857 info: account_info.clone(),
858 storage: HashMap::default(),
859 },
860 )]));
861 state.merge_transitions(BundleRetention::Reverts);
862
863 state.commit(HashMap::from_iter([(
865 address1,
866 RevmAccount {
867 status: AccountStatus::Touched | AccountStatus::Created,
868 info: account_info.clone(),
869 storage: HashMap::default(),
870 },
871 )]));
872 state.commit(HashMap::from_iter([(
873 address1,
874 RevmAccount {
875 status: AccountStatus::Touched,
876 info: account_info.clone(),
877 storage: HashMap::from_iter([(
879 U256::ZERO,
880 EvmStorageSlot {
881 present_value: FlaggedStorage::new_from_value(2),
882 ..Default::default()
883 },
884 )]),
885 },
886 )]));
887 state.commit(HashMap::from_iter([(
888 address1,
889 RevmAccount {
890 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
891 info: account_info.clone(),
892 storage: HashMap::default(),
893 },
894 )]));
895 state.commit(HashMap::from_iter([(
896 address1,
897 RevmAccount {
898 status: AccountStatus::Touched | AccountStatus::Created,
899 info: account_info.clone(),
900 storage: HashMap::default(),
901 },
902 )]));
903 state.merge_transitions(BundleRetention::Reverts);
904
905 state.commit(HashMap::from_iter([(
907 address1,
908 RevmAccount {
909 status: AccountStatus::Touched,
910 info: account_info,
911 storage: HashMap::from_iter([(
913 U256::ZERO,
914 EvmStorageSlot {
915 present_value: FlaggedStorage::new_from_value(9),
916 ..Default::default()
917 },
918 )]),
919 },
920 )]));
921 state.merge_transitions(BundleRetention::Reverts);
922
923 let bundle = state.take_bundle();
924
925 let outcome: ExecutionOutcome =
926 ExecutionOutcome::new(bundle, Default::default(), 1, Vec::new());
927 provider
928 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
929 .expect("Could not write bundle state to DB");
930
931 let mut storage_changeset_cursor = provider
932 .tx_ref()
933 .cursor_dup_read::<tables::StorageChangeSets>()
934 .expect("Could not open plain storage state cursor");
935 let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
936
937 assert_eq!(
947 storage_changes.next(),
948 Some(Ok((
949 BlockNumberAddress((0, address1)),
950 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO, is_private: false }
951 )))
952 );
953 assert_eq!(
954 storage_changes.next(),
955 Some(Ok((
956 BlockNumberAddress((0, address1)),
957 StorageEntry { key: B256::with_last_byte(1), value: U256::ZERO, is_private: false }
958 )))
959 );
960
961 assert_eq!(
964 storage_changes.next(),
965 Some(Ok((
966 BlockNumberAddress((1, address1)),
967 StorageEntry {
968 key: B256::with_last_byte(0),
969 value: U256::from(1),
970 is_private: false
971 }
972 )))
973 );
974
975 assert_eq!(
979 storage_changes.next(),
980 Some(Ok((
981 BlockNumberAddress((2, address1)),
982 StorageEntry {
983 key: B256::with_last_byte(0),
984 value: U256::from(2),
985 is_private: false
986 }
987 )))
988 );
989 assert_eq!(
990 storage_changes.next(),
991 Some(Ok((
992 BlockNumberAddress((2, address1)),
993 StorageEntry {
994 key: B256::with_last_byte(1),
995 value: U256::from(2),
996 is_private: false
997 }
998 )))
999 );
1000
1001 assert_eq!(
1009 storage_changes.next(),
1010 Some(Ok((
1011 BlockNumberAddress((4, address1)),
1012 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO, is_private: false }
1013 )))
1014 );
1015 assert_eq!(
1016 storage_changes.next(),
1017 Some(Ok((
1018 BlockNumberAddress((4, address1)),
1019 StorageEntry { key: B256::with_last_byte(2), value: U256::ZERO, is_private: false }
1020 )))
1021 );
1022 assert_eq!(
1023 storage_changes.next(),
1024 Some(Ok((
1025 BlockNumberAddress((4, address1)),
1026 StorageEntry { key: B256::with_last_byte(6), value: U256::ZERO, is_private: false }
1027 )))
1028 );
1029
1030 assert_eq!(
1035 storage_changes.next(),
1036 Some(Ok((
1037 BlockNumberAddress((5, address1)),
1038 StorageEntry {
1039 key: B256::with_last_byte(0),
1040 value: U256::from(2),
1041 is_private: false
1042 }
1043 )))
1044 );
1045 assert_eq!(
1046 storage_changes.next(),
1047 Some(Ok((
1048 BlockNumberAddress((5, address1)),
1049 StorageEntry {
1050 key: B256::with_last_byte(2),
1051 value: U256::from(4),
1052 is_private: false
1053 }
1054 )))
1055 );
1056 assert_eq!(
1057 storage_changes.next(),
1058 Some(Ok((
1059 BlockNumberAddress((5, address1)),
1060 StorageEntry {
1061 key: B256::with_last_byte(6),
1062 value: U256::from(6),
1063 is_private: false
1064 }
1065 )))
1066 );
1067
1068 assert_eq!(
1074 storage_changes.next(),
1075 Some(Ok((
1076 BlockNumberAddress((7, address1)),
1077 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO, is_private: false }
1078 )))
1079 );
1080 assert_eq!(storage_changes.next(), None);
1081 }
1082
1083 #[test]
1084 fn storage_change_after_selfdestruct_within_block() {
1085 let factory = create_test_provider_factory();
1086 let provider = factory.database_provider_rw().unwrap();
1087
1088 let address1 = Address::random();
1089 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1090
1091 let mut init_state = State::builder().with_bundle_update().build();
1093 init_state.insert_not_existing(address1);
1094 init_state.commit(HashMap::from_iter([(
1095 address1,
1096 RevmAccount {
1097 info: account1.clone(),
1098 status: AccountStatus::Touched | AccountStatus::Created,
1099 storage: HashMap::from_iter([
1102 (
1103 U256::ZERO,
1104 EvmStorageSlot {
1105 present_value: FlaggedStorage::new_from_value(1),
1106 ..Default::default()
1107 },
1108 ),
1109 (
1110 U256::from(1),
1111 EvmStorageSlot {
1112 present_value: FlaggedStorage::new_from_value(2),
1113 ..Default::default()
1114 },
1115 ),
1116 ]),
1117 },
1118 )]));
1119 init_state.merge_transitions(BundleRetention::Reverts);
1120 let outcome =
1121 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
1122 provider
1123 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1124 .expect("Could not write bundle state to DB");
1125
1126 let mut state = State::builder().with_bundle_update().build();
1127 state.insert_account_with_storage(
1128 address1,
1129 account1.clone(),
1130 HashMap::from_iter([
1131 (U256::ZERO, FlaggedStorage::new_from_value(1)),
1132 (U256::from(1), FlaggedStorage::new_from_value(2)),
1133 ]),
1134 );
1135
1136 state.commit(HashMap::from_iter([(
1138 address1,
1139 RevmAccount {
1140 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1141 info: account1.clone(),
1142 storage: HashMap::default(),
1143 },
1144 )]));
1145
1146 state.commit(HashMap::from_iter([(
1147 address1,
1148 RevmAccount {
1149 status: AccountStatus::Touched | AccountStatus::Created,
1150 info: account1.clone(),
1151 storage: HashMap::default(),
1152 },
1153 )]));
1154
1155 state.commit(HashMap::from_iter([(
1156 address1,
1157 RevmAccount {
1158 status: AccountStatus::Touched,
1159 info: account1,
1160 storage: HashMap::from_iter([(
1162 U256::from(1),
1163 EvmStorageSlot {
1164 present_value: FlaggedStorage::new_from_value(5),
1165 ..Default::default()
1166 },
1167 )]),
1168 },
1169 )]));
1170
1171 state.merge_transitions(BundleRetention::Reverts);
1173 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
1174 provider
1175 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1176 .expect("Could not write bundle state to DB");
1177
1178 let mut storage_changeset_cursor = provider
1179 .tx_ref()
1180 .cursor_dup_read::<tables::StorageChangeSets>()
1181 .expect("Could not open plain storage state cursor");
1182 let range = BlockNumberAddress::range(1..=1);
1183 let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap();
1184
1185 assert_eq!(
1186 storage_changes.next(),
1187 Some(Ok((
1188 BlockNumberAddress((1, address1)),
1189 StorageEntry {
1190 key: B256::with_last_byte(0),
1191 value: U256::from(1),
1192 is_private: false
1193 }
1194 )))
1195 );
1196 assert_eq!(
1197 storage_changes.next(),
1198 Some(Ok((
1199 BlockNumberAddress((1, address1)),
1200 StorageEntry {
1201 key: B256::with_last_byte(1),
1202 value: U256::from(2),
1203 is_private: false
1204 }
1205 )))
1206 );
1207 assert_eq!(storage_changes.next(), None);
1208 }
1209
1210 #[test]
1211 fn revert_to_indices() {
1212 let base: ExecutionOutcome = ExecutionOutcome {
1213 bundle: BundleState::default(),
1214 receipts: vec![vec![Receipt::default(); 2]; 7],
1215 first_block: 10,
1216 requests: Vec::new(),
1217 };
1218
1219 let mut this = base.clone();
1220 assert!(this.revert_to(10));
1221 assert_eq!(this.receipts.len(), 1);
1222
1223 let mut this = base.clone();
1224 assert!(!this.revert_to(9));
1225 assert_eq!(this.receipts.len(), 7);
1226
1227 let mut this = base.clone();
1228 assert!(this.revert_to(15));
1229 assert_eq!(this.receipts.len(), 6);
1230
1231 let mut this = base.clone();
1232 assert!(this.revert_to(16));
1233 assert_eq!(this.receipts.len(), 7);
1234
1235 let mut this = base;
1236 assert!(!this.revert_to(17));
1237 assert_eq!(this.receipts.len(), 7);
1238 }
1239
1240 #[test]
1241 fn bundle_state_state_root() {
1242 type PreState = BTreeMap<Address, (Account, BTreeMap<B256, U256>)>;
1243 let mut prestate: PreState = (0..10)
1244 .map(|key| {
1245 let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None };
1246 let storage =
1247 (1..11).map(|key| (B256::with_last_byte(key), U256::from(key))).collect();
1248 (Address::with_last_byte(key), (account, storage))
1249 })
1250 .collect();
1251
1252 let provider_factory = create_test_provider_factory();
1253 let provider_rw = provider_factory.database_provider_rw().unwrap();
1254
1255 let tx = provider_rw.tx_ref();
1257 for (address, (account, storage)) in &prestate {
1258 let hashed_address = keccak256(address);
1259 tx.put::<tables::HashedAccounts>(hashed_address, *account).unwrap();
1260 for (slot, value) in storage {
1261 tx.put::<tables::HashedStorages>(
1262 hashed_address,
1263 StorageEntry { key: keccak256(slot), value: *value, is_private: false },
1264 )
1265 .unwrap();
1266 }
1267 }
1268
1269 let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap();
1270 provider_rw.write_trie_updates(&updates).unwrap();
1271
1272 let mut state = State::builder().with_bundle_update().build();
1273
1274 let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
1275 assert_eq!(
1276 StateRoot::overlay_root(
1277 tx,
1278 provider_factory.hashed_post_state(&state.bundle_state)
1279 )
1280 .unwrap(),
1281 state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
1282 address,
1283 (account, storage.into_iter())
1284 ))),
1285 "{msg}"
1286 );
1287 };
1288
1289 assert_state_root(&state, &prestate, "empty");
1291
1292 let address1 = Address::with_last_byte(1);
1294 let account1_old = prestate.remove(&address1).unwrap();
1295 state.insert_account(address1, account1_old.0.into());
1296 state.commit(HashMap::from_iter([(
1297 address1,
1298 RevmAccount {
1299 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1300 info: RevmAccountInfo::default(),
1301 storage: HashMap::default(),
1302 },
1303 )]));
1304 state.merge_transitions(BundleRetention::PlainState);
1305 assert_state_root(&state, &prestate, "destroyed account");
1306
1307 let address2 = Address::with_last_byte(2);
1309 let slot2 = U256::from(2);
1310 let slot2_key = B256::from(slot2);
1311 let account2 = prestate.get_mut(&address2).unwrap();
1312 let account2_slot2_old_value = *account2.1.get(&slot2_key).unwrap();
1313 state.insert_account_with_storage(
1314 address2,
1315 account2.0.into(),
1316 HashMap::from_iter([(slot2, FlaggedStorage::new_from_value(account2_slot2_old_value))]),
1317 );
1318
1319 let account2_slot2_new_value = U256::from(100);
1320 account2.1.insert(slot2_key, account2_slot2_new_value);
1321 state.commit(HashMap::from_iter([(
1322 address2,
1323 RevmAccount {
1324 status: AccountStatus::Touched,
1325 info: account2.0.into(),
1326 storage: HashMap::from_iter([(
1327 slot2,
1328 EvmStorageSlot::new_changed(
1329 FlaggedStorage::new_from_value(account2_slot2_old_value),
1330 FlaggedStorage::new_from_value(account2_slot2_new_value),
1331 ),
1332 )]),
1333 },
1334 )]));
1335 state.merge_transitions(BundleRetention::PlainState);
1336 assert_state_root(&state, &prestate, "changed storage");
1337
1338 let address3 = Address::with_last_byte(3);
1340 let account3 = prestate.get_mut(&address3).unwrap();
1341 state.insert_account(address3, account3.0.into());
1342
1343 account3.0.balance = U256::from(24);
1344 state.commit(HashMap::from_iter([(
1345 address3,
1346 RevmAccount {
1347 status: AccountStatus::Touched,
1348 info: account3.0.into(),
1349 storage: HashMap::default(),
1350 },
1351 )]));
1352 state.merge_transitions(BundleRetention::PlainState);
1353 assert_state_root(&state, &prestate, "changed balance");
1354
1355 let address4 = Address::with_last_byte(4);
1357 let account4 = prestate.get_mut(&address4).unwrap();
1358 state.insert_account(address4, account4.0.into());
1359
1360 account4.0.nonce = 128;
1361 state.commit(HashMap::from_iter([(
1362 address4,
1363 RevmAccount {
1364 status: AccountStatus::Touched,
1365 info: account4.0.into(),
1366 storage: HashMap::default(),
1367 },
1368 )]));
1369 state.merge_transitions(BundleRetention::PlainState);
1370 assert_state_root(&state, &prestate, "changed nonce");
1371
1372 let account1_new =
1374 Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(B256::random()) };
1375 prestate.insert(address1, (account1_new, BTreeMap::default()));
1376 state.commit(HashMap::from_iter([(
1377 address1,
1378 RevmAccount {
1379 status: AccountStatus::Touched | AccountStatus::Created,
1380 info: account1_new.into(),
1381 storage: HashMap::default(),
1382 },
1383 )]));
1384 state.merge_transitions(BundleRetention::PlainState);
1385 assert_state_root(&state, &prestate, "recreated");
1386
1387 let slot20 = U256::from(20);
1389 let slot20_key = B256::from(slot20);
1390 let account1_slot20_value = U256::from(12345);
1391 prestate.get_mut(&address1).unwrap().1.insert(slot20_key, account1_slot20_value);
1392 state.commit(HashMap::from_iter([(
1393 address1,
1394 RevmAccount {
1395 status: AccountStatus::Touched | AccountStatus::Created,
1396 info: account1_new.into(),
1397 storage: HashMap::from_iter([(
1398 slot20,
1399 EvmStorageSlot::new_changed(
1400 FlaggedStorage::ZERO,
1401 FlaggedStorage::new_from_value(account1_slot20_value),
1402 ),
1403 )]),
1404 },
1405 )]));
1406 state.merge_transitions(BundleRetention::PlainState);
1407 assert_state_root(&state, &prestate, "recreated changed storage");
1408 }
1409
1410 #[test]
1411 fn prepend_state() {
1412 let address1 = Address::random();
1413 let address2 = Address::random();
1414
1415 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1416 let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() };
1417 let account2 = RevmAccountInfo { nonce: 1, ..Default::default() };
1418
1419 let present_state = BundleState::builder(2..=2)
1420 .state_present_account_info(address1, account1_changed.clone())
1421 .build();
1422 assert_eq!(present_state.reverts.len(), 1);
1423 let previous_state = BundleState::builder(1..=1)
1424 .state_present_account_info(address1, account1)
1425 .state_present_account_info(address2, account2.clone())
1426 .build();
1427 assert_eq!(previous_state.reverts.len(), 1);
1428
1429 let mut test: ExecutionOutcome = ExecutionOutcome {
1430 bundle: present_state,
1431 receipts: vec![vec![Receipt::default(); 2]; 1],
1432 first_block: 2,
1433 requests: Vec::new(),
1434 };
1435
1436 test.prepend_state(previous_state);
1437
1438 assert_eq!(test.receipts.len(), 1);
1439 let end_state = test.state();
1440 assert_eq!(end_state.state.len(), 2);
1441 assert_eq!(end_state.reverts.len(), 1);
1443 assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed));
1445 assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2));
1447 }
1448
1449 #[test]
1450 fn hashed_state_storage_root() {
1451 let address = Address::random();
1452 let hashed_address = keccak256(address);
1453 let provider_factory = create_test_provider_factory();
1454 let provider_rw = provider_factory.provider_rw().unwrap();
1455 let tx = provider_rw.tx_ref();
1456
1457 let init_storage = HashedStorage::from_iter(
1459 false,
1460 [
1461 "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
1462 "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
1463 "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
1464 ]
1465 .into_iter()
1466 .map(|str| (B256::from_str(str).unwrap(), FlaggedStorage::new_from_value(1))),
1467 );
1468 let mut state = HashedPostState::default();
1469 state.storages.insert(hashed_address, init_storage.clone());
1470 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1471
1472 let (storage_root, _, storage_updates) =
1474 StorageRoot::from_tx_hashed(tx, hashed_address).calculate(true).unwrap();
1475 assert_eq!(
1476 storage_root,
1477 storage_root_prehashed(FlaggedStorage::collect_value(init_storage.storage))
1478 );
1479 assert!(!storage_updates.is_empty());
1480 provider_rw
1481 .write_individual_storage_trie_updates(hashed_address, &storage_updates)
1482 .unwrap();
1483
1484 let updated_storage = HashedStorage::from_iter(
1486 true,
1487 [
1488 "00deb8486ad8edccfdedfc07109b3667b38a03a8009271aac250cce062d90917",
1489 "88d233b7380bb1bcdc866f6871c94685848f54cf0ee033b1480310b4ddb75fc9",
1490 ]
1491 .into_iter()
1492 .map(|str| (B256::from_str(str).unwrap(), FlaggedStorage::new_from_value(1))),
1493 );
1494 let mut state = HashedPostState::default();
1495 state.storages.insert(hashed_address, updated_storage.clone());
1496 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1497
1498 let storage_root = StorageRoot::overlay_root(tx, address, updated_storage.clone()).unwrap();
1500 assert_eq!(
1501 storage_root,
1502 storage_root_prehashed(FlaggedStorage::collect_value(updated_storage.storage))
1503 );
1504 }
1505}