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: FlaggedStorage::public(1) },
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((address_a, StorageEntry { key: B256::ZERO, value: FlaggedStorage::private(1) })),
544 "Slot 0 for account A should be a private 1"
545 );
546 assert_eq!(
547 storage_cursor.next_dup().unwrap(),
548 Some((
549 address_a,
550 StorageEntry {
551 key: B256::from(U256::from(1).to_be_bytes()),
552 value: FlaggedStorage::private(2),
553 }
554 )),
555 "Slot 1 for account A should be a private 2"
556 );
557 assert_eq!(
558 storage_cursor.next_dup().unwrap(),
559 None,
560 "Account A should only have 2 storage slots"
561 );
562
563 assert_eq!(
564 storage_cursor.seek_exact(address_b).unwrap(),
565 Some((
566 address_b,
567 StorageEntry {
568 key: B256::from(U256::from(1).to_be_bytes()),
569 value: FlaggedStorage::public(2),
570 }
571 )),
572 "Slot 1 for account B should be a public 2"
573 );
574 assert_eq!(
575 storage_cursor.next_dup().unwrap(),
576 None,
577 "Account B should only have 1 storage slot"
578 );
579 assert_eq!(
580 storage_cursor.seek_exact(address_c).unwrap(),
581 Some((
582 address_c,
583 StorageEntry {
584 key: B256::from(U256::from(3).to_be_bytes()),
585 value: FlaggedStorage::private(2),
586 }
587 )),
588 "Slot 3 for account C should be a private 2"
589 );
590 assert_eq!(
591 storage_cursor.next_dup().unwrap(),
592 None,
593 "Account C should only have 1 storage slot"
594 );
595
596 let mut changeset_cursor = provider
598 .tx_ref()
599 .cursor_dup_read::<tables::StorageChangeSets>()
600 .expect("Could not open storage changeset cursor");
601 assert_eq!(
602 changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(),
603 Some((
604 BlockNumberAddress((1, address_a)),
605 StorageEntry { key: B256::ZERO, value: FlaggedStorage::ZERO }
606 )),
607 "Slot 0 for account A should have changed from a public 0"
608 );
609 assert_eq!(
610 changeset_cursor.next_dup().unwrap(),
611 Some((
612 BlockNumberAddress((1, address_a)),
613 StorageEntry {
614 key: B256::from(U256::from(1).to_be_bytes()),
615 value: FlaggedStorage::ZERO,
616 }
617 )),
618 "Slot 1 for account A should have changed from a public 0"
619 );
620 assert_eq!(
621 changeset_cursor.next_dup().unwrap(),
622 None,
623 "Account A should only be in the changeset 2 times"
624 );
625
626 assert_eq!(
627 changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(),
628 Some((
629 BlockNumberAddress((1, address_b)),
630 StorageEntry {
631 key: B256::from(U256::from(1).to_be_bytes()),
632 value: FlaggedStorage::public(1),
633 }
634 )),
635 "Slot 1 for account B should have changed from a public 1"
636 );
637 assert_eq!(
638 changeset_cursor.next_dup().unwrap(),
639 None,
640 "Account B should only be in the changeset 1 time"
641 );
642
643 assert_eq!(
644 changeset_cursor.seek_exact(BlockNumberAddress((1, address_c))).unwrap(),
645 Some((
646 BlockNumberAddress((1, address_c)),
647 StorageEntry {
648 key: B256::from(U256::from(3).to_be_bytes()),
649 value: FlaggedStorage::public(1),
650 }
651 )),
652 "Slot 1 for account C should have changed from a public 1"
653 );
654 assert_eq!(
655 changeset_cursor.next_dup().unwrap(),
656 None,
657 "Account C should only be in the changeset 1 time"
658 );
659
660 let mut state = State::builder().with_bundle_update().build();
662 state.insert_account(address_a, RevmAccountInfo::default());
663
664 state.commit(HashMap::from_iter([(
665 address_a,
666 RevmAccount {
667 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
668 info: RevmAccountInfo::default(),
669 storage: HashMap::default(),
670 },
671 )]));
672
673 state.merge_transitions(BundleRetention::Reverts);
674 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 2, Vec::new());
675 provider
676 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
677 .expect("Could not write bundle state to DB");
678
679 assert_eq!(
680 storage_cursor.seek_exact(address_a).unwrap(),
681 None,
682 "Account A should have no storage slots after deletion"
683 );
684
685 assert_eq!(
686 changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(),
687 Some((
688 BlockNumberAddress((2, address_a)),
689 StorageEntry { key: B256::ZERO, value: FlaggedStorage::private(1) }
690 )),
691 "Slot 0 for account A should have changed from a private 1 on deletion"
692 );
693 assert_eq!(
694 changeset_cursor.next_dup().unwrap(),
695 Some((
696 BlockNumberAddress((2, address_a)),
697 StorageEntry {
698 key: B256::from(U256::from(1).to_be_bytes()),
699 value: FlaggedStorage::private(2),
700 }
701 )),
702 "Slot 1 for account A should have changed from a private 2 on deletion"
703 );
704 assert_eq!(
705 changeset_cursor.next_dup().unwrap(),
706 None,
707 "Account A should only be in the changeset 2 times on deletion"
708 );
709 }
710
711 #[test]
712 fn write_to_db_multiple_selfdestructs() {
713 let factory = create_test_provider_factory();
714 let provider = factory.database_provider_rw().unwrap();
715
716 let address1 = Address::random();
717 let account_info = RevmAccountInfo { nonce: 1, ..Default::default() };
718
719 let mut init_state = State::builder().with_bundle_update().build();
721 init_state.insert_not_existing(address1);
722 init_state.commit(HashMap::from_iter([(
723 address1,
724 RevmAccount {
725 info: account_info.clone(),
726 status: AccountStatus::Touched | AccountStatus::Created,
727 storage: HashMap::from_iter([
730 (
731 U256::ZERO,
732 EvmStorageSlot {
733 present_value: FlaggedStorage::public(1),
734 ..Default::default()
735 },
736 ),
737 (
738 U256::from(1),
739 EvmStorageSlot {
740 present_value: FlaggedStorage::public(2),
741 ..Default::default()
742 },
743 ),
744 ]),
745 },
746 )]));
747 init_state.merge_transitions(BundleRetention::Reverts);
748
749 let outcome =
750 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
751 provider
752 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
753 .expect("Could not write bundle state to DB");
754
755 let mut state = State::builder().with_bundle_update().build();
756 state.insert_account_with_storage(
757 address1,
758 account_info.clone(),
759 HashMap::from_iter([
760 (U256::ZERO, FlaggedStorage::public(1)),
761 (U256::from(1), FlaggedStorage::public(2)),
762 ]),
763 );
764
765 state.commit(HashMap::from_iter([(
767 address1,
768 RevmAccount {
769 status: AccountStatus::Touched,
770 info: account_info.clone(),
771 storage: HashMap::from_iter([(
773 U256::ZERO,
774 EvmStorageSlot {
775 original_value: FlaggedStorage::public(1),
776 present_value: FlaggedStorage::public(2),
777 ..Default::default()
778 },
779 )]),
780 },
781 )]));
782 state.merge_transitions(BundleRetention::Reverts);
783
784 state.commit(HashMap::from_iter([(
786 address1,
787 RevmAccount {
788 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
789 info: account_info.clone(),
790 storage: HashMap::default(),
791 },
792 )]));
793 state.merge_transitions(BundleRetention::Reverts);
794
795 state.commit(HashMap::from_iter([(
797 address1,
798 RevmAccount {
799 status: AccountStatus::Touched | AccountStatus::Created,
800 info: account_info.clone(),
801 storage: HashMap::default(),
802 },
803 )]));
804 state.merge_transitions(BundleRetention::Reverts);
805
806 state.commit(HashMap::from_iter([(
808 address1,
809 RevmAccount {
810 status: AccountStatus::Touched,
811 info: account_info.clone(),
812 storage: HashMap::from_iter([
816 (
817 U256::ZERO,
818 EvmStorageSlot {
819 present_value: FlaggedStorage::public(2),
820 ..Default::default()
821 },
822 ),
823 (
824 U256::from(2),
825 EvmStorageSlot {
826 present_value: FlaggedStorage::public(4),
827 ..Default::default()
828 },
829 ),
830 (
831 U256::from(6),
832 EvmStorageSlot {
833 present_value: FlaggedStorage::public(6),
834 ..Default::default()
835 },
836 ),
837 ]),
838 },
839 )]));
840 state.merge_transitions(BundleRetention::Reverts);
841
842 state.commit(HashMap::from_iter([(
844 address1,
845 RevmAccount {
846 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
847 info: account_info.clone(),
848 storage: HashMap::default(),
849 },
850 )]));
851 state.merge_transitions(BundleRetention::Reverts);
852
853 state.commit(HashMap::from_iter([(
855 address1,
856 RevmAccount {
857 status: AccountStatus::Touched | AccountStatus::Created,
858 info: account_info.clone(),
859 storage: HashMap::default(),
860 },
861 )]));
862 state.commit(HashMap::from_iter([(
863 address1,
864 RevmAccount {
865 status: AccountStatus::Touched,
866 info: account_info.clone(),
867 storage: HashMap::from_iter([(
869 U256::ZERO,
870 EvmStorageSlot {
871 present_value: FlaggedStorage::public(2),
872 ..Default::default()
873 },
874 )]),
875 },
876 )]));
877 state.commit(HashMap::from_iter([(
878 address1,
879 RevmAccount {
880 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
881 info: account_info.clone(),
882 storage: HashMap::default(),
883 },
884 )]));
885 state.commit(HashMap::from_iter([(
886 address1,
887 RevmAccount {
888 status: AccountStatus::Touched | AccountStatus::Created,
889 info: account_info.clone(),
890 storage: HashMap::default(),
891 },
892 )]));
893 state.merge_transitions(BundleRetention::Reverts);
894
895 state.commit(HashMap::from_iter([(
897 address1,
898 RevmAccount {
899 status: AccountStatus::Touched,
900 info: account_info,
901 storage: HashMap::from_iter([(
903 U256::ZERO,
904 EvmStorageSlot {
905 present_value: FlaggedStorage::public(9),
906 ..Default::default()
907 },
908 )]),
909 },
910 )]));
911 state.merge_transitions(BundleRetention::Reverts);
912
913 let bundle = state.take_bundle();
914
915 let outcome: ExecutionOutcome =
916 ExecutionOutcome::new(bundle, Default::default(), 1, Vec::new());
917 provider
918 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
919 .expect("Could not write bundle state to DB");
920
921 let mut storage_changeset_cursor = provider
922 .tx_ref()
923 .cursor_dup_read::<tables::StorageChangeSets>()
924 .expect("Could not open plain storage state cursor");
925 let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
926
927 assert_eq!(
937 storage_changes.next(),
938 Some(Ok((
939 BlockNumberAddress((0, address1)),
940 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::ZERO }
941 )))
942 );
943 assert_eq!(
944 storage_changes.next(),
945 Some(Ok((
946 BlockNumberAddress((0, address1)),
947 StorageEntry { key: B256::with_last_byte(1), value: FlaggedStorage::ZERO }
948 )))
949 );
950
951 assert_eq!(
954 storage_changes.next(),
955 Some(Ok((
956 BlockNumberAddress((1, address1)),
957 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::public(1) }
958 )))
959 );
960
961 assert_eq!(
965 storage_changes.next(),
966 Some(Ok((
967 BlockNumberAddress((2, address1)),
968 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::public(2) }
969 )))
970 );
971 assert_eq!(
972 storage_changes.next(),
973 Some(Ok((
974 BlockNumberAddress((2, address1)),
975 StorageEntry { key: B256::with_last_byte(1), value: FlaggedStorage::public(2) }
976 )))
977 );
978
979 assert_eq!(
987 storage_changes.next(),
988 Some(Ok((
989 BlockNumberAddress((4, address1)),
990 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::ZERO }
991 )))
992 );
993 assert_eq!(
994 storage_changes.next(),
995 Some(Ok((
996 BlockNumberAddress((4, address1)),
997 StorageEntry { key: B256::with_last_byte(2), value: FlaggedStorage::ZERO }
998 )))
999 );
1000 assert_eq!(
1001 storage_changes.next(),
1002 Some(Ok((
1003 BlockNumberAddress((4, address1)),
1004 StorageEntry { key: B256::with_last_byte(6), value: FlaggedStorage::ZERO }
1005 )))
1006 );
1007
1008 assert_eq!(
1013 storage_changes.next(),
1014 Some(Ok((
1015 BlockNumberAddress((5, address1)),
1016 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::public(2) }
1017 )))
1018 );
1019 assert_eq!(
1020 storage_changes.next(),
1021 Some(Ok((
1022 BlockNumberAddress((5, address1)),
1023 StorageEntry { key: B256::with_last_byte(2), value: FlaggedStorage::public(4) }
1024 )))
1025 );
1026 assert_eq!(
1027 storage_changes.next(),
1028 Some(Ok((
1029 BlockNumberAddress((5, address1)),
1030 StorageEntry { key: B256::with_last_byte(6), value: FlaggedStorage::public(6) }
1031 )))
1032 );
1033
1034 assert_eq!(
1040 storage_changes.next(),
1041 Some(Ok((
1042 BlockNumberAddress((7, address1)),
1043 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::ZERO }
1044 )))
1045 );
1046 assert_eq!(storage_changes.next(), None);
1047 }
1048
1049 #[test]
1050 fn storage_change_after_selfdestruct_within_block() {
1051 let factory = create_test_provider_factory();
1052 let provider = factory.database_provider_rw().unwrap();
1053
1054 let address1 = Address::random();
1055 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1056
1057 let mut init_state = State::builder().with_bundle_update().build();
1059 init_state.insert_not_existing(address1);
1060 init_state.commit(HashMap::from_iter([(
1061 address1,
1062 RevmAccount {
1063 info: account1.clone(),
1064 status: AccountStatus::Touched | AccountStatus::Created,
1065 storage: HashMap::from_iter([
1068 (
1069 U256::ZERO,
1070 EvmStorageSlot {
1071 present_value: FlaggedStorage::public(1),
1072 ..Default::default()
1073 },
1074 ),
1075 (
1076 U256::from(1),
1077 EvmStorageSlot {
1078 present_value: FlaggedStorage::public(2),
1079 ..Default::default()
1080 },
1081 ),
1082 ]),
1083 },
1084 )]));
1085 init_state.merge_transitions(BundleRetention::Reverts);
1086 let outcome =
1087 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
1088 provider
1089 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1090 .expect("Could not write bundle state to DB");
1091
1092 let mut state = State::builder().with_bundle_update().build();
1093 state.insert_account_with_storage(
1094 address1,
1095 account1.clone(),
1096 HashMap::from_iter([
1097 (U256::ZERO, FlaggedStorage::public(1)),
1098 (U256::from(1), FlaggedStorage::public(2)),
1099 ]),
1100 );
1101
1102 state.commit(HashMap::from_iter([(
1104 address1,
1105 RevmAccount {
1106 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1107 info: account1.clone(),
1108 storage: HashMap::default(),
1109 },
1110 )]));
1111
1112 state.commit(HashMap::from_iter([(
1113 address1,
1114 RevmAccount {
1115 status: AccountStatus::Touched | AccountStatus::Created,
1116 info: account1.clone(),
1117 storage: HashMap::default(),
1118 },
1119 )]));
1120
1121 state.commit(HashMap::from_iter([(
1122 address1,
1123 RevmAccount {
1124 status: AccountStatus::Touched,
1125 info: account1,
1126 storage: HashMap::from_iter([(
1128 U256::from(1),
1129 EvmStorageSlot {
1130 present_value: FlaggedStorage::public(5),
1131 ..Default::default()
1132 },
1133 )]),
1134 },
1135 )]));
1136
1137 state.merge_transitions(BundleRetention::Reverts);
1139 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
1140 provider
1141 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1142 .expect("Could not write bundle state to DB");
1143
1144 let mut storage_changeset_cursor = provider
1145 .tx_ref()
1146 .cursor_dup_read::<tables::StorageChangeSets>()
1147 .expect("Could not open plain storage state cursor");
1148 let range = BlockNumberAddress::range(1..=1);
1149 let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap();
1150
1151 assert_eq!(
1152 storage_changes.next(),
1153 Some(Ok((
1154 BlockNumberAddress((1, address1)),
1155 StorageEntry { key: B256::with_last_byte(0), value: FlaggedStorage::public(1) }
1156 )))
1157 );
1158 assert_eq!(
1159 storage_changes.next(),
1160 Some(Ok((
1161 BlockNumberAddress((1, address1)),
1162 StorageEntry { key: B256::with_last_byte(1), value: FlaggedStorage::public(2) }
1163 )))
1164 );
1165 assert_eq!(storage_changes.next(), None);
1166 }
1167
1168 #[test]
1169 fn revert_to_indices() {
1170 let base: ExecutionOutcome = ExecutionOutcome {
1171 bundle: BundleState::default(),
1172 receipts: vec![vec![Receipt::default(); 2]; 7],
1173 first_block: 10,
1174 requests: Vec::new(),
1175 };
1176
1177 let mut this = base.clone();
1178 assert!(this.revert_to(10));
1179 assert_eq!(this.receipts.len(), 1);
1180
1181 let mut this = base.clone();
1182 assert!(!this.revert_to(9));
1183 assert_eq!(this.receipts.len(), 7);
1184
1185 let mut this = base.clone();
1186 assert!(this.revert_to(15));
1187 assert_eq!(this.receipts.len(), 6);
1188
1189 let mut this = base.clone();
1190 assert!(this.revert_to(16));
1191 assert_eq!(this.receipts.len(), 7);
1192
1193 let mut this = base;
1194 assert!(!this.revert_to(17));
1195 assert_eq!(this.receipts.len(), 7);
1196 }
1197
1198 #[test]
1199 fn bundle_state_state_root() {
1200 type PreState = BTreeMap<Address, (Account, BTreeMap<B256, U256>)>;
1201 let mut prestate: PreState = (0..10)
1202 .map(|key| {
1203 let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None };
1204 let storage =
1205 (1..11).map(|key| (B256::with_last_byte(key), U256::from(key))).collect();
1206 (Address::with_last_byte(key), (account, storage))
1207 })
1208 .collect();
1209
1210 let provider_factory = create_test_provider_factory();
1211 let provider_rw = provider_factory.database_provider_rw().unwrap();
1212
1213 let tx = provider_rw.tx_ref();
1215 for (address, (account, storage)) in &prestate {
1216 let hashed_address = keccak256(address);
1217 tx.put::<tables::HashedAccounts>(hashed_address, *account).unwrap();
1218 for (slot, value) in storage {
1219 tx.put::<tables::HashedStorages>(
1220 hashed_address,
1221 StorageEntry { key: keccak256(slot), value: FlaggedStorage::public(*value) },
1222 )
1223 .unwrap();
1224 }
1225 }
1226
1227 let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap();
1228 provider_rw.write_trie_updates(&updates).unwrap();
1229
1230 let mut state = State::builder().with_bundle_update().build();
1231
1232 let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
1233 assert_eq!(
1234 StateRoot::overlay_root(
1235 tx,
1236 provider_factory.hashed_post_state(&state.bundle_state)
1237 )
1238 .unwrap(),
1239 state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
1240 address,
1241 (account, storage.into_iter())
1242 ))),
1243 "{msg}"
1244 );
1245 };
1246
1247 assert_state_root(&state, &prestate, "empty");
1249
1250 let address1 = Address::with_last_byte(1);
1252 let account1_old = prestate.remove(&address1).unwrap();
1253 state.insert_account(address1, account1_old.0.into());
1254 state.commit(HashMap::from_iter([(
1255 address1,
1256 RevmAccount {
1257 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1258 info: RevmAccountInfo::default(),
1259 storage: HashMap::default(),
1260 },
1261 )]));
1262 state.merge_transitions(BundleRetention::PlainState);
1263 assert_state_root(&state, &prestate, "destroyed account");
1264
1265 let address2 = Address::with_last_byte(2);
1267 let slot2 = U256::from(2);
1268 let slot2_key = B256::from(slot2);
1269 let account2 = prestate.get_mut(&address2).unwrap();
1270 let account2_slot2_old_value = *account2.1.get(&slot2_key).unwrap();
1271 state.insert_account_with_storage(
1272 address2,
1273 account2.0.into(),
1274 HashMap::from_iter([(slot2, FlaggedStorage::public(account2_slot2_old_value))]),
1275 );
1276
1277 let account2_slot2_new_value = U256::from(100);
1278 account2.1.insert(slot2_key, account2_slot2_new_value);
1279 state.commit(HashMap::from_iter([(
1280 address2,
1281 RevmAccount {
1282 status: AccountStatus::Touched,
1283 info: account2.0.into(),
1284 storage: HashMap::from_iter([(
1285 slot2,
1286 EvmStorageSlot::new_changed(
1287 FlaggedStorage::public(account2_slot2_old_value),
1288 FlaggedStorage::public(account2_slot2_new_value),
1289 ),
1290 )]),
1291 },
1292 )]));
1293 state.merge_transitions(BundleRetention::PlainState);
1294 assert_state_root(&state, &prestate, "changed storage");
1295
1296 let address3 = Address::with_last_byte(3);
1298 let account3 = prestate.get_mut(&address3).unwrap();
1299 state.insert_account(address3, account3.0.into());
1300
1301 account3.0.balance = U256::from(24);
1302 state.commit(HashMap::from_iter([(
1303 address3,
1304 RevmAccount {
1305 status: AccountStatus::Touched,
1306 info: account3.0.into(),
1307 storage: HashMap::default(),
1308 },
1309 )]));
1310 state.merge_transitions(BundleRetention::PlainState);
1311 assert_state_root(&state, &prestate, "changed balance");
1312
1313 let address4 = Address::with_last_byte(4);
1315 let account4 = prestate.get_mut(&address4).unwrap();
1316 state.insert_account(address4, account4.0.into());
1317
1318 account4.0.nonce = 128;
1319 state.commit(HashMap::from_iter([(
1320 address4,
1321 RevmAccount {
1322 status: AccountStatus::Touched,
1323 info: account4.0.into(),
1324 storage: HashMap::default(),
1325 },
1326 )]));
1327 state.merge_transitions(BundleRetention::PlainState);
1328 assert_state_root(&state, &prestate, "changed nonce");
1329
1330 let account1_new =
1332 Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(B256::random()) };
1333 prestate.insert(address1, (account1_new, BTreeMap::default()));
1334 state.commit(HashMap::from_iter([(
1335 address1,
1336 RevmAccount {
1337 status: AccountStatus::Touched | AccountStatus::Created,
1338 info: account1_new.into(),
1339 storage: HashMap::default(),
1340 },
1341 )]));
1342 state.merge_transitions(BundleRetention::PlainState);
1343 assert_state_root(&state, &prestate, "recreated");
1344
1345 let slot20 = U256::from(20);
1347 let slot20_key = B256::from(slot20);
1348 let account1_slot20_value = U256::from(12345);
1349 prestate.get_mut(&address1).unwrap().1.insert(slot20_key, account1_slot20_value);
1350 state.commit(HashMap::from_iter([(
1351 address1,
1352 RevmAccount {
1353 status: AccountStatus::Touched | AccountStatus::Created,
1354 info: account1_new.into(),
1355 storage: HashMap::from_iter([(
1356 slot20,
1357 EvmStorageSlot::new_changed(
1358 FlaggedStorage::ZERO,
1359 FlaggedStorage::public(account1_slot20_value),
1360 ),
1361 )]),
1362 },
1363 )]));
1364 state.merge_transitions(BundleRetention::PlainState);
1365 assert_state_root(&state, &prestate, "recreated changed storage");
1366 }
1367
1368 #[test]
1369 fn prepend_state() {
1370 let address1 = Address::random();
1371 let address2 = Address::random();
1372
1373 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1374 let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() };
1375 let account2 = RevmAccountInfo { nonce: 1, ..Default::default() };
1376
1377 let present_state = BundleState::builder(2..=2)
1378 .state_present_account_info(address1, account1_changed.clone())
1379 .build();
1380 assert_eq!(present_state.reverts.len(), 1);
1381 let previous_state = BundleState::builder(1..=1)
1382 .state_present_account_info(address1, account1)
1383 .state_present_account_info(address2, account2.clone())
1384 .build();
1385 assert_eq!(previous_state.reverts.len(), 1);
1386
1387 let mut test: ExecutionOutcome = ExecutionOutcome {
1388 bundle: present_state,
1389 receipts: vec![vec![Receipt::default(); 2]; 1],
1390 first_block: 2,
1391 requests: Vec::new(),
1392 };
1393
1394 test.prepend_state(previous_state);
1395
1396 assert_eq!(test.receipts.len(), 1);
1397 let end_state = test.state();
1398 assert_eq!(end_state.state.len(), 2);
1399 assert_eq!(end_state.reverts.len(), 1);
1401 assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed));
1403 assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2));
1405 }
1406
1407 #[test]
1408 fn hashed_state_storage_root() {
1409 let address = Address::random();
1410 let hashed_address = keccak256(address);
1411 let provider_factory = create_test_provider_factory();
1412 let provider_rw = provider_factory.provider_rw().unwrap();
1413 let tx = provider_rw.tx_ref();
1414
1415 let init_storage = HashedStorage::from_iter(
1417 false,
1418 [
1419 "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
1420 "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
1421 "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
1422 ]
1423 .into_iter()
1424 .map(|str| (B256::from_str(str).unwrap(), FlaggedStorage::public(1))),
1425 );
1426 let mut state = HashedPostState::default();
1427 state.storages.insert(hashed_address, init_storage.clone());
1428 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1429
1430 let (storage_root, _, storage_updates) =
1432 StorageRoot::from_tx_hashed(tx, hashed_address).calculate(true).unwrap();
1433 assert_eq!(
1434 storage_root,
1435 storage_root_prehashed(FlaggedStorage::collect_value(init_storage.storage))
1436 );
1437 assert!(!storage_updates.is_empty());
1438 provider_rw
1439 .write_individual_storage_trie_updates(hashed_address, &storage_updates)
1440 .unwrap();
1441
1442 let updated_storage = HashedStorage::from_iter(
1444 true,
1445 [
1446 "00deb8486ad8edccfdedfc07109b3667b38a03a8009271aac250cce062d90917",
1447 "88d233b7380bb1bcdc866f6871c94685848f54cf0ee033b1480310b4ddb75fc9",
1448 ]
1449 .into_iter()
1450 .map(|str| (B256::from_str(str).unwrap(), FlaggedStorage::public(1))),
1451 );
1452 let mut state = HashedPostState::default();
1453 state.storages.insert(hashed_address, updated_storage.clone());
1454 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1455
1456 let storage_root = StorageRoot::overlay_root(tx, address, updated_storage.clone()).unwrap();
1458 assert_eq!(
1459 storage_root,
1460 storage_root_prehashed(FlaggedStorage::collect_value(updated_storage.storage))
1461 );
1462 }
1463}