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::{ProviderError, 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 let block_hash = recovered_block.hash();
169 self.database()
170 .insert_block(Arc::unwrap_or_clone(recovered_block), StorageLocation::Both)?;
171
172 self.database().write_state(
175 &execution_output,
176 OriginalValuesKnown::No,
177 StorageLocation::StaticFiles,
178 )?;
179
180 self.database()
182 .write_hashed_state(&Arc::unwrap_or_clone(hashed_state).into_sorted())?;
183 self.database().write_trie_updates(
184 trie.as_ref().ok_or(ProviderError::MissingTrieUpdates(block_hash))?,
185 )?;
186 }
187
188 self.database().update_history_indices(first_number..=last_block_number)?;
190
191 self.database().update_pipeline_stages(last_block_number, false)?;
193
194 debug!(target: "provider::storage_writer", range = ?first_number..=last_block_number, "Appended block data");
195
196 Ok(())
197 }
198
199 pub fn remove_blocks_above(&self, block_number: u64) -> ProviderResult<()> {
203 debug!(target: "provider::storage_writer", ?block_number, "Removing blocks from database above block_number");
205 self.database().remove_block_and_execution_above(block_number, StorageLocation::Both)?;
206
207 let highest_static_file_block = self
209 .static_file()
210 .get_highest_static_file_block(StaticFileSegment::Headers)
211 .expect("todo: error handling, headers should exist");
212
213 debug!(target: "provider::storage_writer", ?block_number, "Removing static file blocks above block_number");
219 self.static_file()
220 .get_writer(block_number, StaticFileSegment::Headers)?
221 .prune_headers(highest_static_file_block.saturating_sub(block_number))?;
222
223 Ok(())
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use crate::{
231 test_utils::create_test_provider_factory, AccountReader, StorageTrieWriter, TrieWriter,
232 };
233 use alloy_primitives::{keccak256, map::HashMap, Address, B256, U256};
234 use reth_db_api::{
235 cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
236 models::{AccountBeforeTx, BlockNumberAddress},
237 tables,
238 transaction::{DbTx, DbTxMut},
239 };
240 use reth_ethereum_primitives::Receipt;
241 use reth_execution_types::ExecutionOutcome;
242 use reth_primitives_traits::{Account, StorageEntry};
243 use reth_storage_api::{DatabaseProviderFactory, HashedPostStateProvider};
244 use reth_trie::{
245 test_utils::{state_root, storage_root_prehashed},
246 HashedPostState, HashedStorage, StateRoot, StorageRoot, StorageRootProgress,
247 };
248 use reth_trie_db::{DatabaseStateRoot, DatabaseStorageRoot};
249 use revm_database::{
250 states::{
251 bundle_state::BundleRetention, changes::PlainStorageRevert, PlainStorageChangeset,
252 },
253 BundleState, State,
254 };
255 use revm_database_interface::{DatabaseCommit, EmptyDB};
256 use revm_state::{
257 Account as RevmAccount, AccountInfo as RevmAccountInfo, AccountStatus, EvmStorageSlot,
258 };
259 use std::{collections::BTreeMap, str::FromStr};
260
261 #[test]
262 fn wiped_entries_are_removed() {
263 let provider_factory = create_test_provider_factory();
264
265 let addresses = (0..10).map(|_| Address::random()).collect::<Vec<_>>();
266 let destroyed_address = *addresses.first().unwrap();
267 let destroyed_address_hashed = keccak256(destroyed_address);
268 let slot = B256::with_last_byte(1);
269 let hashed_slot = keccak256(slot);
270 {
271 let provider_rw = provider_factory.provider_rw().unwrap();
272 let mut accounts_cursor =
273 provider_rw.tx_ref().cursor_write::<tables::HashedAccounts>().unwrap();
274 let mut storage_cursor =
275 provider_rw.tx_ref().cursor_write::<tables::HashedStorages>().unwrap();
276
277 for address in addresses {
278 let hashed_address = keccak256(address);
279 accounts_cursor
280 .insert(hashed_address, &Account { nonce: 1, ..Default::default() })
281 .unwrap();
282 storage_cursor
283 .insert(
284 hashed_address,
285 &StorageEntry { key: hashed_slot, value: U256::from(1) },
286 )
287 .unwrap();
288 }
289 provider_rw.commit().unwrap();
290 }
291
292 let mut hashed_state = HashedPostState::default();
293 hashed_state.accounts.insert(destroyed_address_hashed, None);
294 hashed_state.storages.insert(destroyed_address_hashed, HashedStorage::new(true));
295
296 let provider_rw = provider_factory.provider_rw().unwrap();
297 assert!(matches!(provider_rw.write_hashed_state(&hashed_state.into_sorted()), Ok(())));
298 provider_rw.commit().unwrap();
299
300 let provider = provider_factory.provider().unwrap();
301 assert_eq!(
302 provider.tx_ref().get::<tables::HashedAccounts>(destroyed_address_hashed),
303 Ok(None)
304 );
305 assert_eq!(
306 provider
307 .tx_ref()
308 .cursor_read::<tables::HashedStorages>()
309 .unwrap()
310 .seek_by_key_subkey(destroyed_address_hashed, hashed_slot),
311 Ok(None)
312 );
313 }
314
315 #[test]
316 fn write_to_db_account_info() {
317 let factory = create_test_provider_factory();
318 let provider = factory.provider_rw().unwrap();
319
320 let address_a = Address::ZERO;
321 let address_b = Address::repeat_byte(0xff);
322
323 let account_a = RevmAccountInfo { balance: U256::from(1), nonce: 1, ..Default::default() };
324 let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
325 let account_b_changed =
326 RevmAccountInfo { balance: U256::from(3), nonce: 3, ..Default::default() };
327
328 let mut state = State::builder().with_bundle_update().build();
329 state.insert_not_existing(address_a);
330 state.insert_account(address_b, account_b.clone());
331
332 state.commit(HashMap::from_iter([(
334 address_a,
335 RevmAccount {
336 info: account_a.clone(),
337 status: AccountStatus::Touched | AccountStatus::Created,
338 storage: HashMap::default(),
339 transaction_id: 0,
340 },
341 )]));
342
343 state.commit(HashMap::from_iter([(
345 address_b,
346 RevmAccount {
347 info: account_b_changed.clone(),
348 status: AccountStatus::Touched,
349 storage: HashMap::default(),
350 transaction_id: 0,
351 },
352 )]));
353
354 state.merge_transitions(BundleRetention::Reverts);
355 let mut revm_bundle_state = state.take_bundle();
356
357 let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
359 let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
360 assert!(plain_state.storage.is_empty());
361 assert!(plain_state.contracts.is_empty());
362 provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
363
364 assert_eq!(reverts.storage, [[]]);
365 provider.write_state_reverts(reverts, 1).expect("Could not write reverts to DB");
366
367 let reth_account_a = account_a.into();
368 let reth_account_b = account_b.into();
369 let reth_account_b_changed = (&account_b_changed).into();
370
371 assert_eq!(
373 provider.basic_account(&address_a).expect("Could not read account state"),
374 Some(reth_account_a),
375 "Account A state is wrong"
376 );
377 assert_eq!(
378 provider.basic_account(&address_b).expect("Could not read account state"),
379 Some(reth_account_b_changed),
380 "Account B state is wrong"
381 );
382
383 let mut changeset_cursor = provider
385 .tx_ref()
386 .cursor_dup_read::<tables::AccountChangeSets>()
387 .expect("Could not open changeset cursor");
388 assert_eq!(
389 changeset_cursor.seek_exact(1).expect("Could not read account change set"),
390 Some((1, AccountBeforeTx { address: address_a, info: None })),
391 "Account A changeset is wrong"
392 );
393 assert_eq!(
394 changeset_cursor.next_dup().expect("Changeset table is malformed"),
395 Some((1, AccountBeforeTx { address: address_b, info: Some(reth_account_b) })),
396 "Account B changeset is wrong"
397 );
398
399 let mut state = State::builder().with_bundle_update().build();
400 state.insert_account(address_b, account_b_changed.clone());
401
402 state.commit(HashMap::from_iter([(
404 address_b,
405 RevmAccount {
406 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
407 info: account_b_changed,
408 storage: HashMap::default(),
409 transaction_id: 0,
410 },
411 )]));
412
413 state.merge_transitions(BundleRetention::Reverts);
414 let mut revm_bundle_state = state.take_bundle();
415
416 let reverts = revm_bundle_state.take_all_reverts().to_plain_state_reverts();
418 let plain_state = revm_bundle_state.to_plain_state(OriginalValuesKnown::Yes);
419 assert_eq!(
421 plain_state.storage,
422 [PlainStorageChangeset { address: address_b, wipe_storage: true, storage: vec![] }]
423 );
424 assert!(plain_state.contracts.is_empty());
425 provider.write_state_changes(plain_state).expect("Could not write plain state to DB");
426
427 assert_eq!(
428 reverts.storage,
429 [[PlainStorageRevert { address: address_b, wiped: true, storage_revert: vec![] }]]
430 );
431 provider.write_state_reverts(reverts, 2).expect("Could not write reverts to DB");
432
433 assert_eq!(
435 provider.basic_account(&address_b).expect("Could not read account state"),
436 None,
437 "Account B should be deleted"
438 );
439
440 assert_eq!(
442 changeset_cursor.seek_exact(2).expect("Could not read account change set"),
443 Some((2, AccountBeforeTx { address: address_b, info: Some(reth_account_b_changed) })),
444 "Account B changeset is wrong after deletion"
445 );
446 }
447
448 #[test]
449 fn write_to_db_storage() {
450 let factory = create_test_provider_factory();
451 let provider = factory.database_provider_rw().unwrap();
452
453 let address_a = Address::ZERO;
454 let address_b = Address::repeat_byte(0xff);
455
456 let account_b = RevmAccountInfo { balance: U256::from(2), nonce: 2, ..Default::default() };
457
458 let mut state = State::builder().with_bundle_update().build();
459 state.insert_not_existing(address_a);
460 state.insert_account_with_storage(
461 address_b,
462 account_b.clone(),
463 HashMap::from_iter([(U256::from(1), U256::from(1))]),
464 );
465
466 state.commit(HashMap::from_iter([
467 (
468 address_a,
469 RevmAccount {
470 status: AccountStatus::Touched | AccountStatus::Created,
471 info: RevmAccountInfo::default(),
472 storage: HashMap::from_iter([
475 (
476 U256::from(0),
477 EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
478 ),
479 (
480 U256::from(1),
481 EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
482 ),
483 ]),
484 transaction_id: 0,
485 },
486 ),
487 (
488 address_b,
489 RevmAccount {
490 status: AccountStatus::Touched,
491 info: account_b,
492 storage: HashMap::from_iter([(
494 U256::from(1),
495 EvmStorageSlot {
496 present_value: U256::from(2),
497 original_value: U256::from(1),
498 ..Default::default()
499 },
500 )]),
501 transaction_id: 0,
502 },
503 ),
504 ]));
505
506 state.merge_transitions(BundleRetention::Reverts);
507
508 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
509 provider
510 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
511 .expect("Could not write bundle state to DB");
512
513 let mut storage_cursor = provider
515 .tx_ref()
516 .cursor_dup_read::<tables::PlainStorageState>()
517 .expect("Could not open plain storage state cursor");
518
519 assert_eq!(
520 storage_cursor.seek_exact(address_a).unwrap(),
521 Some((address_a, StorageEntry { key: B256::ZERO, value: U256::from(1) })),
522 "Slot 0 for account A should be 1"
523 );
524 assert_eq!(
525 storage_cursor.next_dup().unwrap(),
526 Some((
527 address_a,
528 StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
529 )),
530 "Slot 1 for account A should be 2"
531 );
532 assert_eq!(
533 storage_cursor.next_dup().unwrap(),
534 None,
535 "Account A should only have 2 storage slots"
536 );
537
538 assert_eq!(
539 storage_cursor.seek_exact(address_b).unwrap(),
540 Some((
541 address_b,
542 StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
543 )),
544 "Slot 1 for account B should be 2"
545 );
546 assert_eq!(
547 storage_cursor.next_dup().unwrap(),
548 None,
549 "Account B should only have 1 storage slot"
550 );
551
552 let mut changeset_cursor = provider
554 .tx_ref()
555 .cursor_dup_read::<tables::StorageChangeSets>()
556 .expect("Could not open storage changeset cursor");
557 assert_eq!(
558 changeset_cursor.seek_exact(BlockNumberAddress((1, address_a))).unwrap(),
559 Some((
560 BlockNumberAddress((1, address_a)),
561 StorageEntry { key: B256::ZERO, value: U256::from(0) }
562 )),
563 "Slot 0 for account A should have changed from 0"
564 );
565 assert_eq!(
566 changeset_cursor.next_dup().unwrap(),
567 Some((
568 BlockNumberAddress((1, address_a)),
569 StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(0) }
570 )),
571 "Slot 1 for account A should have changed from 0"
572 );
573 assert_eq!(
574 changeset_cursor.next_dup().unwrap(),
575 None,
576 "Account A should only be in the changeset 2 times"
577 );
578
579 assert_eq!(
580 changeset_cursor.seek_exact(BlockNumberAddress((1, address_b))).unwrap(),
581 Some((
582 BlockNumberAddress((1, address_b)),
583 StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(1) }
584 )),
585 "Slot 1 for account B should have changed from 1"
586 );
587 assert_eq!(
588 changeset_cursor.next_dup().unwrap(),
589 None,
590 "Account B should only be in the changeset 1 time"
591 );
592
593 let mut state = State::builder().with_bundle_update().build();
595 state.insert_account(address_a, RevmAccountInfo::default());
596
597 state.commit(HashMap::from_iter([(
598 address_a,
599 RevmAccount {
600 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
601 info: RevmAccountInfo::default(),
602 storage: HashMap::default(),
603 transaction_id: 0,
604 },
605 )]));
606
607 state.merge_transitions(BundleRetention::Reverts);
608 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 2, Vec::new());
609 provider
610 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
611 .expect("Could not write bundle state to DB");
612
613 assert_eq!(
614 storage_cursor.seek_exact(address_a).unwrap(),
615 None,
616 "Account A should have no storage slots after deletion"
617 );
618
619 assert_eq!(
620 changeset_cursor.seek_exact(BlockNumberAddress((2, address_a))).unwrap(),
621 Some((
622 BlockNumberAddress((2, address_a)),
623 StorageEntry { key: B256::ZERO, value: U256::from(1) }
624 )),
625 "Slot 0 for account A should have changed from 1 on deletion"
626 );
627 assert_eq!(
628 changeset_cursor.next_dup().unwrap(),
629 Some((
630 BlockNumberAddress((2, address_a)),
631 StorageEntry { key: B256::from(U256::from(1).to_be_bytes()), value: U256::from(2) }
632 )),
633 "Slot 1 for account A should have changed from 2 on deletion"
634 );
635 assert_eq!(
636 changeset_cursor.next_dup().unwrap(),
637 None,
638 "Account A should only be in the changeset 2 times on deletion"
639 );
640 }
641
642 #[test]
643 fn write_to_db_multiple_selfdestructs() {
644 let factory = create_test_provider_factory();
645 let provider = factory.database_provider_rw().unwrap();
646
647 let address1 = Address::random();
648 let account_info = RevmAccountInfo { nonce: 1, ..Default::default() };
649
650 let mut init_state = State::builder().with_bundle_update().build();
652 init_state.insert_not_existing(address1);
653 init_state.commit(HashMap::from_iter([(
654 address1,
655 RevmAccount {
656 info: account_info.clone(),
657 status: AccountStatus::Touched | AccountStatus::Created,
658 storage: HashMap::from_iter([
661 (
662 U256::ZERO,
663 EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
664 ),
665 (
666 U256::from(1),
667 EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
668 ),
669 ]),
670 transaction_id: 0,
671 },
672 )]));
673 init_state.merge_transitions(BundleRetention::Reverts);
674
675 let outcome =
676 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
677 provider
678 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
679 .expect("Could not write bundle state to DB");
680
681 let mut state = State::builder().with_bundle_update().build();
682 state.insert_account_with_storage(
683 address1,
684 account_info.clone(),
685 HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
686 );
687
688 state.commit(HashMap::from_iter([(
690 address1,
691 RevmAccount {
692 status: AccountStatus::Touched,
693 info: account_info.clone(),
694 storage: HashMap::from_iter([(
696 U256::ZERO,
697 EvmStorageSlot {
698 original_value: U256::from(1),
699 present_value: U256::from(2),
700 ..Default::default()
701 },
702 )]),
703 transaction_id: 0,
704 },
705 )]));
706 state.merge_transitions(BundleRetention::Reverts);
707
708 state.commit(HashMap::from_iter([(
710 address1,
711 RevmAccount {
712 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
713 info: account_info.clone(),
714 storage: HashMap::default(),
715 transaction_id: 0,
716 },
717 )]));
718 state.merge_transitions(BundleRetention::Reverts);
719
720 state.commit(HashMap::from_iter([(
722 address1,
723 RevmAccount {
724 status: AccountStatus::Touched | AccountStatus::Created,
725 info: account_info.clone(),
726 storage: HashMap::default(),
727 transaction_id: 0,
728 },
729 )]));
730 state.merge_transitions(BundleRetention::Reverts);
731
732 state.commit(HashMap::from_iter([(
734 address1,
735 RevmAccount {
736 status: AccountStatus::Touched,
737 info: account_info.clone(),
738 storage: HashMap::from_iter([
742 (
743 U256::ZERO,
744 EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
745 ),
746 (
747 U256::from(2),
748 EvmStorageSlot { present_value: U256::from(4), ..Default::default() },
749 ),
750 (
751 U256::from(6),
752 EvmStorageSlot { present_value: U256::from(6), ..Default::default() },
753 ),
754 ]),
755 transaction_id: 0,
756 },
757 )]));
758 state.merge_transitions(BundleRetention::Reverts);
759
760 state.commit(HashMap::from_iter([(
762 address1,
763 RevmAccount {
764 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
765 info: account_info.clone(),
766 storage: HashMap::default(),
767 transaction_id: 0,
768 },
769 )]));
770 state.merge_transitions(BundleRetention::Reverts);
771
772 state.commit(HashMap::from_iter([(
774 address1,
775 RevmAccount {
776 status: AccountStatus::Touched | AccountStatus::Created,
777 info: account_info.clone(),
778 storage: HashMap::default(),
779 transaction_id: 0,
780 },
781 )]));
782 state.commit(HashMap::from_iter([(
783 address1,
784 RevmAccount {
785 status: AccountStatus::Touched,
786 info: account_info.clone(),
787 storage: HashMap::from_iter([(
789 U256::ZERO,
790 EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
791 )]),
792 transaction_id: 0,
793 },
794 )]));
795 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 transaction_id: 0,
802 },
803 )]));
804 state.commit(HashMap::from_iter([(
805 address1,
806 RevmAccount {
807 status: AccountStatus::Touched | AccountStatus::Created,
808 info: account_info.clone(),
809 storage: HashMap::default(),
810 transaction_id: 0,
811 },
812 )]));
813 state.merge_transitions(BundleRetention::Reverts);
814
815 state.commit(HashMap::from_iter([(
817 address1,
818 RevmAccount {
819 status: AccountStatus::Touched,
820 info: account_info,
821 storage: HashMap::from_iter([(
823 U256::ZERO,
824 EvmStorageSlot { present_value: U256::from(9), ..Default::default() },
825 )]),
826 transaction_id: 0,
827 },
828 )]));
829
830 state.merge_transitions(BundleRetention::Reverts);
831
832 let bundle = state.take_bundle();
833
834 let outcome: ExecutionOutcome =
835 ExecutionOutcome::new(bundle, Default::default(), 1, Vec::new());
836 provider
837 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
838 .expect("Could not write bundle state to DB");
839
840 let mut storage_changeset_cursor = provider
841 .tx_ref()
842 .cursor_dup_read::<tables::StorageChangeSets>()
843 .expect("Could not open plain storage state cursor");
844 let mut storage_changes = storage_changeset_cursor.walk_range(..).unwrap();
845
846 assert_eq!(
856 storage_changes.next(),
857 Some(Ok((
858 BlockNumberAddress((0, address1)),
859 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
860 )))
861 );
862 assert_eq!(
863 storage_changes.next(),
864 Some(Ok((
865 BlockNumberAddress((0, address1)),
866 StorageEntry { key: B256::with_last_byte(1), value: U256::ZERO }
867 )))
868 );
869
870 assert_eq!(
873 storage_changes.next(),
874 Some(Ok((
875 BlockNumberAddress((1, address1)),
876 StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
877 )))
878 );
879
880 assert_eq!(
884 storage_changes.next(),
885 Some(Ok((
886 BlockNumberAddress((2, address1)),
887 StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
888 )))
889 );
890 assert_eq!(
891 storage_changes.next(),
892 Some(Ok((
893 BlockNumberAddress((2, address1)),
894 StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
895 )))
896 );
897
898 assert_eq!(
906 storage_changes.next(),
907 Some(Ok((
908 BlockNumberAddress((4, address1)),
909 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
910 )))
911 );
912 assert_eq!(
913 storage_changes.next(),
914 Some(Ok((
915 BlockNumberAddress((4, address1)),
916 StorageEntry { key: B256::with_last_byte(2), value: U256::ZERO }
917 )))
918 );
919 assert_eq!(
920 storage_changes.next(),
921 Some(Ok((
922 BlockNumberAddress((4, address1)),
923 StorageEntry { key: B256::with_last_byte(6), value: U256::ZERO }
924 )))
925 );
926
927 assert_eq!(
932 storage_changes.next(),
933 Some(Ok((
934 BlockNumberAddress((5, address1)),
935 StorageEntry { key: B256::with_last_byte(0), value: U256::from(2) }
936 )))
937 );
938 assert_eq!(
939 storage_changes.next(),
940 Some(Ok((
941 BlockNumberAddress((5, address1)),
942 StorageEntry { key: B256::with_last_byte(2), value: U256::from(4) }
943 )))
944 );
945 assert_eq!(
946 storage_changes.next(),
947 Some(Ok((
948 BlockNumberAddress((5, address1)),
949 StorageEntry { key: B256::with_last_byte(6), value: U256::from(6) }
950 )))
951 );
952
953 assert_eq!(
959 storage_changes.next(),
960 Some(Ok((
961 BlockNumberAddress((7, address1)),
962 StorageEntry { key: B256::with_last_byte(0), value: U256::ZERO }
963 )))
964 );
965 assert_eq!(storage_changes.next(), None);
966 }
967
968 #[test]
969 fn storage_change_after_selfdestruct_within_block() {
970 let factory = create_test_provider_factory();
971 let provider = factory.database_provider_rw().unwrap();
972
973 let address1 = Address::random();
974 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
975
976 let mut init_state = State::builder().with_bundle_update().build();
978 init_state.insert_not_existing(address1);
979 init_state.commit(HashMap::from_iter([(
980 address1,
981 RevmAccount {
982 info: account1.clone(),
983 status: AccountStatus::Touched | AccountStatus::Created,
984 storage: HashMap::from_iter([
987 (
988 U256::ZERO,
989 EvmStorageSlot { present_value: U256::from(1), ..Default::default() },
990 ),
991 (
992 U256::from(1),
993 EvmStorageSlot { present_value: U256::from(2), ..Default::default() },
994 ),
995 ]),
996 transaction_id: 0,
997 },
998 )]));
999 init_state.merge_transitions(BundleRetention::Reverts);
1000 let outcome =
1001 ExecutionOutcome::new(init_state.take_bundle(), Default::default(), 0, Vec::new());
1002 provider
1003 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1004 .expect("Could not write bundle state to DB");
1005
1006 let mut state = State::builder().with_bundle_update().build();
1007 state.insert_account_with_storage(
1008 address1,
1009 account1.clone(),
1010 HashMap::from_iter([(U256::ZERO, U256::from(1)), (U256::from(1), U256::from(2))]),
1011 );
1012
1013 state.commit(HashMap::from_iter([(
1015 address1,
1016 RevmAccount {
1017 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1018 info: account1.clone(),
1019 storage: HashMap::default(),
1020 transaction_id: 0,
1021 },
1022 )]));
1023
1024 state.commit(HashMap::from_iter([(
1025 address1,
1026 RevmAccount {
1027 status: AccountStatus::Touched | AccountStatus::Created,
1028 info: account1.clone(),
1029 storage: HashMap::default(),
1030 transaction_id: 0,
1031 },
1032 )]));
1033
1034 state.commit(HashMap::from_iter([(
1035 address1,
1036 RevmAccount {
1037 status: AccountStatus::Touched,
1038 info: account1,
1039 storage: HashMap::from_iter([(
1041 U256::from(1),
1042 EvmStorageSlot { present_value: U256::from(5), ..Default::default() },
1043 )]),
1044 transaction_id: 0,
1045 },
1046 )]));
1047
1048 state.merge_transitions(BundleRetention::Reverts);
1050 let outcome = ExecutionOutcome::new(state.take_bundle(), Default::default(), 1, Vec::new());
1051 provider
1052 .write_state(&outcome, OriginalValuesKnown::Yes, StorageLocation::Database)
1053 .expect("Could not write bundle state to DB");
1054
1055 let mut storage_changeset_cursor = provider
1056 .tx_ref()
1057 .cursor_dup_read::<tables::StorageChangeSets>()
1058 .expect("Could not open plain storage state cursor");
1059 let range = BlockNumberAddress::range(1..=1);
1060 let mut storage_changes = storage_changeset_cursor.walk_range(range).unwrap();
1061
1062 assert_eq!(
1063 storage_changes.next(),
1064 Some(Ok((
1065 BlockNumberAddress((1, address1)),
1066 StorageEntry { key: B256::with_last_byte(0), value: U256::from(1) }
1067 )))
1068 );
1069 assert_eq!(
1070 storage_changes.next(),
1071 Some(Ok((
1072 BlockNumberAddress((1, address1)),
1073 StorageEntry { key: B256::with_last_byte(1), value: U256::from(2) }
1074 )))
1075 );
1076 assert_eq!(storage_changes.next(), None);
1077 }
1078
1079 #[test]
1080 fn revert_to_indices() {
1081 let base: ExecutionOutcome = ExecutionOutcome {
1082 bundle: BundleState::default(),
1083 receipts: vec![vec![Receipt::default(); 2]; 7],
1084 first_block: 10,
1085 requests: Vec::new(),
1086 };
1087
1088 let mut this = base.clone();
1089 assert!(this.revert_to(10));
1090 assert_eq!(this.receipts.len(), 1);
1091
1092 let mut this = base.clone();
1093 assert!(!this.revert_to(9));
1094 assert_eq!(this.receipts.len(), 7);
1095
1096 let mut this = base.clone();
1097 assert!(this.revert_to(15));
1098 assert_eq!(this.receipts.len(), 6);
1099
1100 let mut this = base.clone();
1101 assert!(this.revert_to(16));
1102 assert_eq!(this.receipts.len(), 7);
1103
1104 let mut this = base;
1105 assert!(!this.revert_to(17));
1106 assert_eq!(this.receipts.len(), 7);
1107 }
1108
1109 #[test]
1110 fn bundle_state_state_root() {
1111 type PreState = BTreeMap<Address, (Account, BTreeMap<B256, U256>)>;
1112 let mut prestate: PreState = (0..10)
1113 .map(|key| {
1114 let account = Account { nonce: 1, balance: U256::from(key), bytecode_hash: None };
1115 let storage =
1116 (1..11).map(|key| (B256::with_last_byte(key), U256::from(key))).collect();
1117 (Address::with_last_byte(key), (account, storage))
1118 })
1119 .collect();
1120
1121 let provider_factory = create_test_provider_factory();
1122 let provider_rw = provider_factory.database_provider_rw().unwrap();
1123
1124 let tx = provider_rw.tx_ref();
1126 for (address, (account, storage)) in &prestate {
1127 let hashed_address = keccak256(address);
1128 tx.put::<tables::HashedAccounts>(hashed_address, *account).unwrap();
1129 for (slot, value) in storage {
1130 tx.put::<tables::HashedStorages>(
1131 hashed_address,
1132 StorageEntry { key: keccak256(slot), value: *value },
1133 )
1134 .unwrap();
1135 }
1136 }
1137
1138 let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap();
1139 provider_rw.write_trie_updates(&updates).unwrap();
1140
1141 let mut state = State::builder().with_bundle_update().build();
1142
1143 let assert_state_root = |state: &State<EmptyDB>, expected: &PreState, msg| {
1144 assert_eq!(
1145 StateRoot::overlay_root(
1146 tx,
1147 provider_factory.hashed_post_state(&state.bundle_state)
1148 )
1149 .unwrap(),
1150 state_root(expected.clone().into_iter().map(|(address, (account, storage))| (
1151 address,
1152 (account, storage.into_iter())
1153 ))),
1154 "{msg}"
1155 );
1156 };
1157
1158 assert_state_root(&state, &prestate, "empty");
1160
1161 let address1 = Address::with_last_byte(1);
1163 let account1_old = prestate.remove(&address1).unwrap();
1164 state.insert_account(address1, account1_old.0.into());
1165 state.commit(HashMap::from_iter([(
1166 address1,
1167 RevmAccount {
1168 status: AccountStatus::Touched | AccountStatus::SelfDestructed,
1169 info: RevmAccountInfo::default(),
1170 storage: HashMap::default(),
1171 transaction_id: 0,
1172 },
1173 )]));
1174 state.merge_transitions(BundleRetention::PlainState);
1175 assert_state_root(&state, &prestate, "destroyed account");
1176
1177 let address2 = Address::with_last_byte(2);
1179 let slot2 = U256::from(2);
1180 let slot2_key = B256::from(slot2);
1181 let account2 = prestate.get_mut(&address2).unwrap();
1182 let account2_slot2_old_value = *account2.1.get(&slot2_key).unwrap();
1183 state.insert_account_with_storage(
1184 address2,
1185 account2.0.into(),
1186 HashMap::from_iter([(slot2, account2_slot2_old_value)]),
1187 );
1188
1189 let account2_slot2_new_value = U256::from(100);
1190 account2.1.insert(slot2_key, account2_slot2_new_value);
1191 state.commit(HashMap::from_iter([(
1192 address2,
1193 RevmAccount {
1194 status: AccountStatus::Touched,
1195 info: account2.0.into(),
1196 storage: HashMap::from_iter([(
1197 slot2,
1198 EvmStorageSlot::new_changed(
1199 account2_slot2_old_value,
1200 account2_slot2_new_value,
1201 0,
1202 ),
1203 )]),
1204 transaction_id: 0,
1205 },
1206 )]));
1207 state.merge_transitions(BundleRetention::PlainState);
1208 assert_state_root(&state, &prestate, "changed storage");
1209
1210 let address3 = Address::with_last_byte(3);
1212 let account3 = prestate.get_mut(&address3).unwrap();
1213 state.insert_account(address3, account3.0.into());
1214
1215 account3.0.balance = U256::from(24);
1216 state.commit(HashMap::from_iter([(
1217 address3,
1218 RevmAccount {
1219 status: AccountStatus::Touched,
1220 info: account3.0.into(),
1221 storage: HashMap::default(),
1222 transaction_id: 0,
1223 },
1224 )]));
1225 state.merge_transitions(BundleRetention::PlainState);
1226 assert_state_root(&state, &prestate, "changed balance");
1227
1228 let address4 = Address::with_last_byte(4);
1230 let account4 = prestate.get_mut(&address4).unwrap();
1231 state.insert_account(address4, account4.0.into());
1232
1233 account4.0.nonce = 128;
1234 state.commit(HashMap::from_iter([(
1235 address4,
1236 RevmAccount {
1237 status: AccountStatus::Touched,
1238 info: account4.0.into(),
1239 storage: HashMap::default(),
1240 transaction_id: 0,
1241 },
1242 )]));
1243 state.merge_transitions(BundleRetention::PlainState);
1244 assert_state_root(&state, &prestate, "changed nonce");
1245
1246 let account1_new =
1248 Account { nonce: 56, balance: U256::from(123), bytecode_hash: Some(B256::random()) };
1249 prestate.insert(address1, (account1_new, BTreeMap::default()));
1250 state.commit(HashMap::from_iter([(
1251 address1,
1252 RevmAccount {
1253 status: AccountStatus::Touched | AccountStatus::Created,
1254 info: account1_new.into(),
1255 storage: HashMap::default(),
1256 transaction_id: 0,
1257 },
1258 )]));
1259 state.merge_transitions(BundleRetention::PlainState);
1260 assert_state_root(&state, &prestate, "recreated");
1261
1262 let slot20 = U256::from(20);
1264 let slot20_key = B256::from(slot20);
1265 let account1_slot20_value = U256::from(12345);
1266 prestate.get_mut(&address1).unwrap().1.insert(slot20_key, account1_slot20_value);
1267 state.commit(HashMap::from_iter([(
1268 address1,
1269 RevmAccount {
1270 status: AccountStatus::Touched | AccountStatus::Created,
1271 info: account1_new.into(),
1272 storage: HashMap::from_iter([(
1273 slot20,
1274 EvmStorageSlot::new_changed(U256::ZERO, account1_slot20_value, 0),
1275 )]),
1276 transaction_id: 0,
1277 },
1278 )]));
1279 state.merge_transitions(BundleRetention::PlainState);
1280 assert_state_root(&state, &prestate, "recreated changed storage");
1281 }
1282
1283 #[test]
1284 fn prepend_state() {
1285 let address1 = Address::random();
1286 let address2 = Address::random();
1287
1288 let account1 = RevmAccountInfo { nonce: 1, ..Default::default() };
1289 let account1_changed = RevmAccountInfo { nonce: 1, ..Default::default() };
1290 let account2 = RevmAccountInfo { nonce: 1, ..Default::default() };
1291
1292 let present_state = BundleState::builder(2..=2)
1293 .state_present_account_info(address1, account1_changed.clone())
1294 .build();
1295 assert_eq!(present_state.reverts.len(), 1);
1296 let previous_state = BundleState::builder(1..=1)
1297 .state_present_account_info(address1, account1)
1298 .state_present_account_info(address2, account2.clone())
1299 .build();
1300 assert_eq!(previous_state.reverts.len(), 1);
1301
1302 let mut test: ExecutionOutcome = ExecutionOutcome {
1303 bundle: present_state,
1304 receipts: vec![vec![Receipt::default(); 2]; 1],
1305 first_block: 2,
1306 requests: Vec::new(),
1307 };
1308
1309 test.prepend_state(previous_state);
1310
1311 assert_eq!(test.receipts.len(), 1);
1312 let end_state = test.state();
1313 assert_eq!(end_state.state.len(), 2);
1314 assert_eq!(end_state.reverts.len(), 1);
1316 assert_eq!(end_state.state.get(&address1).unwrap().info, Some(account1_changed));
1318 assert_eq!(end_state.state.get(&address2).unwrap().info, Some(account2));
1320 }
1321
1322 #[test]
1323 fn hashed_state_storage_root() {
1324 let address = Address::random();
1325 let hashed_address = keccak256(address);
1326 let provider_factory = create_test_provider_factory();
1327 let provider_rw = provider_factory.provider_rw().unwrap();
1328 let tx = provider_rw.tx_ref();
1329
1330 let init_storage = HashedStorage::from_iter(
1332 false,
1333 [
1334 "50000000000000000000000000000004253371b55351a08cb3267d4d265530b6",
1335 "512428ed685fff57294d1a9cbb147b18ae5db9cf6ae4b312fa1946ba0561882e",
1336 "51e6784c736ef8548f856909870b38e49ef7a4e3e77e5e945e0d5e6fcaa3037f",
1337 ]
1338 .into_iter()
1339 .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1340 );
1341 let mut state = HashedPostState::default();
1342 state.storages.insert(hashed_address, init_storage.clone());
1343 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1344
1345 let StorageRootProgress::Complete(storage_root, _, storage_updates) =
1347 StorageRoot::from_tx_hashed(tx, hashed_address)
1348 .with_no_threshold()
1349 .calculate(true)
1350 .unwrap()
1351 else {
1352 panic!("no threshold for root");
1353 };
1354 assert_eq!(storage_root, storage_root_prehashed(init_storage.storage));
1355 assert!(!storage_updates.is_empty());
1356 provider_rw
1357 .write_individual_storage_trie_updates(hashed_address, &storage_updates)
1358 .unwrap();
1359
1360 let updated_storage = HashedStorage::from_iter(
1362 true,
1363 [
1364 "00deb8486ad8edccfdedfc07109b3667b38a03a8009271aac250cce062d90917",
1365 "88d233b7380bb1bcdc866f6871c94685848f54cf0ee033b1480310b4ddb75fc9",
1366 ]
1367 .into_iter()
1368 .map(|str| (B256::from_str(str).unwrap(), U256::from(1))),
1369 );
1370 let mut state = HashedPostState::default();
1371 state.storages.insert(hashed_address, updated_storage.clone());
1372 provider_rw.write_hashed_state(&state.clone().into_sorted()).unwrap();
1373
1374 let storage_root = StorageRoot::overlay_root(tx, address, updated_storage.clone()).unwrap();
1376 assert_eq!(storage_root, storage_root_prehashed(updated_storage.storage));
1377 }
1378}