1use crate::{
2 providers::state::macros::delegate_provider_impls, AccountReader, BlockHashReader,
3 ChangeSetReader, HashedPostStateProvider, ProviderError, StateProvider, StateRootProvider,
4};
5use alloy_eips::merge::EPOCH_SLOTS;
6use alloy_primitives::{Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
7use reth_db_api::{
8 cursor::{DbCursorRO, DbDupCursorRO},
9 models::{storage_sharded_key::StorageShardedKey, ShardedKey},
10 table::Table,
11 tables,
12 transaction::DbTx,
13 BlockNumberList,
14};
15use reth_primitives_traits::{Account, Bytecode};
16use reth_storage_api::{
17 BlockNumReader, BytecodeReader, DBProvider, StateProofProvider, StorageRootProvider,
18};
19use reth_storage_errors::provider::ProviderResult;
20use reth_trie::{
21 proof::{Proof, StorageProof},
22 updates::TrieUpdates,
23 witness::TrieWitness,
24 AccountProof, HashedPostState, HashedStorage, KeccakKeyHasher, MultiProof, MultiProofTargets,
25 StateRoot, StorageMultiProof, StorageRoot, TrieInput,
26};
27use reth_trie_db::{
28 DatabaseHashedPostState, DatabaseHashedStorage, DatabaseProof, DatabaseStateRoot,
29 DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness,
30};
31
32use std::fmt::Debug;
33
34#[derive(Debug)]
46pub struct HistoricalStateProviderRef<'b, Provider> {
47 provider: &'b Provider,
49 block_number: BlockNumber,
51 lowest_available_blocks: LowestAvailableBlocks,
53}
54
55#[derive(Debug, Eq, PartialEq)]
56pub enum HistoryInfo {
57 NotYetWritten,
58 InChangeset(u64),
59 InPlainState,
60 MaybeInPlainState,
61}
62
63impl<'b, Provider: DBProvider + BlockNumReader> HistoricalStateProviderRef<'b, Provider> {
64 pub fn new(provider: &'b Provider, block_number: BlockNumber) -> Self {
66 Self { provider, block_number, lowest_available_blocks: Default::default() }
67 }
68
69 pub const fn new_with_lowest_available_blocks(
72 provider: &'b Provider,
73 block_number: BlockNumber,
74 lowest_available_blocks: LowestAvailableBlocks,
75 ) -> Self {
76 Self { provider, block_number, lowest_available_blocks }
77 }
78
79 pub fn account_history_lookup(&self, address: Address) -> ProviderResult<HistoryInfo> {
81 if !self.lowest_available_blocks.is_account_history_available(self.block_number) {
82 return Err(ProviderError::StateAtBlockPruned(self.block_number))
83 }
84
85 let history_key = ShardedKey::new(address, self.block_number);
87 self.history_info::<tables::AccountsHistory, _>(
88 history_key,
89 |key| key.key == address,
90 self.lowest_available_blocks.account_history_block_number,
91 )
92 }
93
94 pub fn storage_history_lookup(
96 &self,
97 address: Address,
98 storage_key: StorageKey,
99 ) -> ProviderResult<HistoryInfo> {
100 if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
101 return Err(ProviderError::StateAtBlockPruned(self.block_number))
102 }
103
104 let history_key = StorageShardedKey::new(address, storage_key, self.block_number);
106 self.history_info::<tables::StoragesHistory, _>(
107 history_key,
108 |key| key.address == address && key.sharded_key.key == storage_key,
109 self.lowest_available_blocks.storage_history_block_number,
110 )
111 }
112
113 fn check_distance_against_limit(&self, limit: u64) -> ProviderResult<bool> {
115 let tip = self.provider.last_block_number()?;
116
117 Ok(tip.saturating_sub(self.block_number) > limit)
118 }
119
120 fn revert_state(&self) -> ProviderResult<HashedPostState> {
122 if !self.lowest_available_blocks.is_account_history_available(self.block_number) ||
123 !self.lowest_available_blocks.is_storage_history_available(self.block_number)
124 {
125 return Err(ProviderError::StateAtBlockPruned(self.block_number))
126 }
127
128 if self.check_distance_against_limit(EPOCH_SLOTS)? {
129 tracing::warn!(
130 target: "provider::historical_sp",
131 target = self.block_number,
132 "Attempt to calculate state root for an old block might result in OOM"
133 );
134 }
135
136 Ok(HashedPostState::from_reverts::<KeccakKeyHasher>(self.tx(), self.block_number..)?)
137 }
138
139 fn revert_storage(&self, address: Address) -> ProviderResult<HashedStorage> {
141 if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
142 return Err(ProviderError::StateAtBlockPruned(self.block_number))
143 }
144
145 if self.check_distance_against_limit(EPOCH_SLOTS * 10)? {
146 tracing::warn!(
147 target: "provider::historical_sp",
148 target = self.block_number,
149 "Attempt to calculate storage root for an old block might result in OOM"
150 );
151 }
152
153 Ok(HashedStorage::from_reverts(self.tx(), address, self.block_number)?)
154 }
155
156 fn history_info<T, K>(
157 &self,
158 key: K,
159 key_filter: impl Fn(&K) -> bool,
160 lowest_available_block_number: Option<BlockNumber>,
161 ) -> ProviderResult<HistoryInfo>
162 where
163 T: Table<Key = K, Value = BlockNumberList>,
164 {
165 let mut cursor = self.tx().cursor_read::<T>()?;
166
167 if let Some(chunk) = cursor.seek(key)?.filter(|(key, _)| key_filter(key)).map(|x| x.1 .0) {
171 let mut rank = chunk.rank(self.block_number);
173
174 if rank.checked_sub(1).and_then(|rank| chunk.select(rank)) == Some(self.block_number) {
177 rank -= 1
178 };
179
180 let block_number = chunk.select(rank);
181
182 if rank == 0 &&
189 block_number != Some(self.block_number) &&
190 !cursor.prev()?.is_some_and(|(key, _)| key_filter(&key))
191 {
192 if let (Some(_), Some(block_number)) = (lowest_available_block_number, block_number)
193 {
194 Ok(HistoryInfo::InChangeset(block_number))
197 } else {
198 Ok(HistoryInfo::NotYetWritten)
200 }
201 } else if let Some(block_number) = block_number {
202 Ok(HistoryInfo::InChangeset(block_number))
204 } else {
205 Ok(HistoryInfo::InPlainState)
208 }
209 } else if lowest_available_block_number.is_some() {
210 Ok(HistoryInfo::MaybeInPlainState)
213 } else {
214 Ok(HistoryInfo::NotYetWritten)
216 }
217 }
218
219 pub const fn with_lowest_available_account_history_block_number(
221 mut self,
222 block_number: BlockNumber,
223 ) -> Self {
224 self.lowest_available_blocks.account_history_block_number = Some(block_number);
225 self
226 }
227
228 pub const fn with_lowest_available_storage_history_block_number(
230 mut self,
231 block_number: BlockNumber,
232 ) -> Self {
233 self.lowest_available_blocks.storage_history_block_number = Some(block_number);
234 self
235 }
236}
237
238impl<Provider: DBProvider + BlockNumReader> HistoricalStateProviderRef<'_, Provider> {
239 fn tx(&self) -> &Provider::Tx {
240 self.provider.tx_ref()
241 }
242}
243
244impl<Provider: DBProvider + BlockNumReader + ChangeSetReader> AccountReader
245 for HistoricalStateProviderRef<'_, Provider>
246{
247 fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
249 match self.account_history_lookup(*address)? {
250 HistoryInfo::NotYetWritten => Ok(None),
251 HistoryInfo::InChangeset(changeset_block_number) => {
252 self.provider
254 .get_account_before_block(changeset_block_number, *address)?
255 .ok_or(ProviderError::AccountChangesetNotFound {
256 block_number: changeset_block_number,
257 address: *address,
258 })
259 .map(|account_before| account_before.info)
260 }
261 HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
262 Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
263 }
264 }
265 }
266}
267
268impl<Provider: DBProvider + BlockNumReader + BlockHashReader> BlockHashReader
269 for HistoricalStateProviderRef<'_, Provider>
270{
271 fn block_hash(&self, number: u64) -> ProviderResult<Option<B256>> {
273 self.provider.block_hash(number)
274 }
275
276 fn canonical_hashes_range(
277 &self,
278 start: BlockNumber,
279 end: BlockNumber,
280 ) -> ProviderResult<Vec<B256>> {
281 self.provider.canonical_hashes_range(start, end)
282 }
283}
284
285impl<Provider: DBProvider + BlockNumReader> StateRootProvider
286 for HistoricalStateProviderRef<'_, Provider>
287{
288 fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
289 let mut revert_state = self.revert_state()?;
290 revert_state.extend(hashed_state);
291 StateRoot::overlay_root(self.tx(), revert_state)
292 .map_err(|err| ProviderError::Database(err.into()))
293 }
294
295 fn state_root_from_nodes(&self, mut input: TrieInput) -> ProviderResult<B256> {
296 input.prepend(self.revert_state()?);
297 StateRoot::overlay_root_from_nodes(self.tx(), input)
298 .map_err(|err| ProviderError::Database(err.into()))
299 }
300
301 fn state_root_with_updates(
302 &self,
303 hashed_state: HashedPostState,
304 ) -> ProviderResult<(B256, TrieUpdates)> {
305 let mut revert_state = self.revert_state()?;
306 revert_state.extend(hashed_state);
307 StateRoot::overlay_root_with_updates(self.tx(), revert_state)
308 .map_err(|err| ProviderError::Database(err.into()))
309 }
310
311 fn state_root_from_nodes_with_updates(
312 &self,
313 mut input: TrieInput,
314 ) -> ProviderResult<(B256, TrieUpdates)> {
315 input.prepend(self.revert_state()?);
316 StateRoot::overlay_root_from_nodes_with_updates(self.tx(), input)
317 .map_err(|err| ProviderError::Database(err.into()))
318 }
319}
320
321impl<Provider: DBProvider + BlockNumReader> StorageRootProvider
322 for HistoricalStateProviderRef<'_, Provider>
323{
324 fn storage_root(
325 &self,
326 address: Address,
327 hashed_storage: HashedStorage,
328 ) -> ProviderResult<B256> {
329 let mut revert_storage = self.revert_storage(address)?;
330 revert_storage.extend(&hashed_storage);
331 StorageRoot::overlay_root(self.tx(), address, revert_storage)
332 .map_err(|err| ProviderError::Database(err.into()))
333 }
334
335 fn storage_proof(
336 &self,
337 address: Address,
338 slot: B256,
339 hashed_storage: HashedStorage,
340 ) -> ProviderResult<reth_trie::StorageProof> {
341 let mut revert_storage = self.revert_storage(address)?;
342 revert_storage.extend(&hashed_storage);
343 StorageProof::overlay_storage_proof(self.tx(), address, slot, revert_storage)
344 .map_err(ProviderError::from)
345 }
346
347 fn storage_multiproof(
348 &self,
349 address: Address,
350 slots: &[B256],
351 hashed_storage: HashedStorage,
352 ) -> ProviderResult<StorageMultiProof> {
353 let mut revert_storage = self.revert_storage(address)?;
354 revert_storage.extend(&hashed_storage);
355 StorageProof::overlay_storage_multiproof(self.tx(), address, slots, revert_storage)
356 .map_err(ProviderError::from)
357 }
358}
359
360impl<Provider: DBProvider + BlockNumReader> StateProofProvider
361 for HistoricalStateProviderRef<'_, Provider>
362{
363 fn proof(
365 &self,
366 mut input: TrieInput,
367 address: Address,
368 slots: &[B256],
369 ) -> ProviderResult<AccountProof> {
370 input.prepend(self.revert_state()?);
371 let proof = <Proof<_, _> as DatabaseProof>::from_tx(self.tx());
372 proof.overlay_account_proof(input, address, slots).map_err(ProviderError::from)
373 }
374
375 fn multiproof(
376 &self,
377 mut input: TrieInput,
378 targets: MultiProofTargets,
379 ) -> ProviderResult<MultiProof> {
380 input.prepend(self.revert_state()?);
381 let proof = <Proof<_, _> as DatabaseProof>::from_tx(self.tx());
382 proof.overlay_multiproof(input, targets).map_err(ProviderError::from)
383 }
384
385 fn witness(&self, mut input: TrieInput, target: HashedPostState) -> ProviderResult<Vec<Bytes>> {
386 input.prepend(self.revert_state()?);
387 TrieWitness::overlay_witness(self.tx(), input, target)
388 .map_err(ProviderError::from)
389 .map(|hm| hm.into_values().collect())
390 }
391}
392
393impl<Provider: Sync> HashedPostStateProvider for HistoricalStateProviderRef<'_, Provider> {
394 fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
395 HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
396 }
397}
398
399impl<Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader> StateProvider
400 for HistoricalStateProviderRef<'_, Provider>
401{
402 fn storage(
404 &self,
405 address: Address,
406 storage_key: StorageKey,
407 ) -> ProviderResult<Option<StorageValue>> {
408 match self.storage_history_lookup(address, storage_key)? {
409 HistoryInfo::NotYetWritten => Ok(None),
410 HistoryInfo::InChangeset(changeset_block_number) => Ok(Some(
411 self.tx()
412 .cursor_dup_read::<tables::StorageChangeSets>()?
413 .seek_by_key_subkey((changeset_block_number, address).into(), storage_key)?
414 .filter(|entry| entry.key == storage_key)
415 .ok_or_else(|| ProviderError::StorageChangesetNotFound {
416 block_number: changeset_block_number,
417 address,
418 storage_key: Box::new(storage_key),
419 })?
420 .value,
421 )),
422 HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => Ok(self
423 .tx()
424 .cursor_dup_read::<tables::PlainStorageState>()?
425 .seek_by_key_subkey(address, storage_key)?
426 .filter(|entry| entry.key == storage_key)
427 .map(|entry| entry.value)
428 .or(Some(StorageValue::ZERO))),
429 }
430 }
431}
432
433impl<Provider: DBProvider + BlockNumReader> BytecodeReader
434 for HistoricalStateProviderRef<'_, Provider>
435{
436 fn bytecode_by_hash(&self, code_hash: &B256) -> ProviderResult<Option<Bytecode>> {
438 self.tx().get_by_encoded_key::<tables::Bytecodes>(code_hash).map_err(Into::into)
439 }
440}
441
442#[derive(Debug)]
445pub struct HistoricalStateProvider<Provider> {
446 provider: Provider,
448 block_number: BlockNumber,
450 lowest_available_blocks: LowestAvailableBlocks,
452}
453
454impl<Provider: DBProvider + BlockNumReader> HistoricalStateProvider<Provider> {
455 pub fn new(provider: Provider, block_number: BlockNumber) -> Self {
457 Self { provider, block_number, lowest_available_blocks: Default::default() }
458 }
459
460 pub const fn with_lowest_available_account_history_block_number(
462 mut self,
463 block_number: BlockNumber,
464 ) -> Self {
465 self.lowest_available_blocks.account_history_block_number = Some(block_number);
466 self
467 }
468
469 pub const fn with_lowest_available_storage_history_block_number(
471 mut self,
472 block_number: BlockNumber,
473 ) -> Self {
474 self.lowest_available_blocks.storage_history_block_number = Some(block_number);
475 self
476 }
477
478 #[inline(always)]
480 const fn as_ref(&self) -> HistoricalStateProviderRef<'_, Provider> {
481 HistoricalStateProviderRef::new_with_lowest_available_blocks(
482 &self.provider,
483 self.block_number,
484 self.lowest_available_blocks,
485 )
486 }
487}
488
489delegate_provider_impls!(HistoricalStateProvider<Provider> where [Provider: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader]);
491
492#[derive(Clone, Copy, Debug, Default)]
495pub struct LowestAvailableBlocks {
496 pub account_history_block_number: Option<BlockNumber>,
500 pub storage_history_block_number: Option<BlockNumber>,
504}
505
506impl LowestAvailableBlocks {
507 pub fn is_account_history_available(&self, at: BlockNumber) -> bool {
510 self.account_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
511 }
512
513 pub fn is_storage_history_available(&self, at: BlockNumber) -> bool {
516 self.storage_history_block_number.map(|block_number| block_number <= at).unwrap_or(true)
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use crate::{
523 providers::state::historical::{HistoryInfo, LowestAvailableBlocks},
524 test_utils::create_test_provider_factory,
525 AccountReader, HistoricalStateProvider, HistoricalStateProviderRef, StateProvider,
526 };
527 use alloy_primitives::{address, b256, Address, B256, U256};
528 use reth_db_api::{
529 models::{storage_sharded_key::StorageShardedKey, AccountBeforeTx, ShardedKey},
530 tables,
531 transaction::{DbTx, DbTxMut},
532 BlockNumberList,
533 };
534 use reth_primitives_traits::{Account, StorageEntry};
535 use reth_storage_api::{
536 BlockHashReader, BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
537 };
538 use reth_storage_errors::provider::ProviderError;
539
540 const ADDRESS: Address = address!("0x0000000000000000000000000000000000000001");
541 const HIGHER_ADDRESS: Address = address!("0x0000000000000000000000000000000000000005");
542 const STORAGE: B256 =
543 b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
544
545 const fn assert_state_provider<T: StateProvider>() {}
546 #[expect(dead_code)]
547 const fn assert_historical_state_provider<
548 T: DBProvider + BlockNumReader + BlockHashReader + ChangeSetReader,
549 >() {
550 assert_state_provider::<HistoricalStateProvider<T>>();
551 }
552
553 #[test]
554 fn history_provider_get_account() {
555 let factory = create_test_provider_factory();
556 let tx = factory.provider_rw().unwrap().into_tx();
557
558 tx.put::<tables::AccountsHistory>(
559 ShardedKey { key: ADDRESS, highest_block_number: 7 },
560 BlockNumberList::new([1, 3, 7]).unwrap(),
561 )
562 .unwrap();
563 tx.put::<tables::AccountsHistory>(
564 ShardedKey { key: ADDRESS, highest_block_number: u64::MAX },
565 BlockNumberList::new([10, 15]).unwrap(),
566 )
567 .unwrap();
568 tx.put::<tables::AccountsHistory>(
569 ShardedKey { key: HIGHER_ADDRESS, highest_block_number: u64::MAX },
570 BlockNumberList::new([4]).unwrap(),
571 )
572 .unwrap();
573
574 let acc_plain = Account { nonce: 100, balance: U256::ZERO, bytecode_hash: None };
575 let acc_at15 = Account { nonce: 15, balance: U256::ZERO, bytecode_hash: None };
576 let acc_at10 = Account { nonce: 10, balance: U256::ZERO, bytecode_hash: None };
577 let acc_at7 = Account { nonce: 7, balance: U256::ZERO, bytecode_hash: None };
578 let acc_at3 = Account { nonce: 3, balance: U256::ZERO, bytecode_hash: None };
579
580 let higher_acc_plain = Account { nonce: 4, balance: U256::ZERO, bytecode_hash: None };
581
582 tx.put::<tables::AccountChangeSets>(1, AccountBeforeTx { address: ADDRESS, info: None })
584 .unwrap();
585 tx.put::<tables::AccountChangeSets>(
586 3,
587 AccountBeforeTx { address: ADDRESS, info: Some(acc_at3) },
588 )
589 .unwrap();
590 tx.put::<tables::AccountChangeSets>(
591 4,
592 AccountBeforeTx { address: HIGHER_ADDRESS, info: None },
593 )
594 .unwrap();
595 tx.put::<tables::AccountChangeSets>(
596 7,
597 AccountBeforeTx { address: ADDRESS, info: Some(acc_at7) },
598 )
599 .unwrap();
600 tx.put::<tables::AccountChangeSets>(
601 10,
602 AccountBeforeTx { address: ADDRESS, info: Some(acc_at10) },
603 )
604 .unwrap();
605 tx.put::<tables::AccountChangeSets>(
606 15,
607 AccountBeforeTx { address: ADDRESS, info: Some(acc_at15) },
608 )
609 .unwrap();
610
611 tx.put::<tables::PlainAccountState>(ADDRESS, acc_plain).unwrap();
613 tx.put::<tables::PlainAccountState>(HIGHER_ADDRESS, higher_acc_plain).unwrap();
614 tx.commit().unwrap();
615
616 let db = factory.provider().unwrap();
617
618 assert!(matches!(
620 HistoricalStateProviderRef::new(&db, 1).basic_account(&ADDRESS),
621 Ok(None)
622 ));
623 assert!(matches!(
624 HistoricalStateProviderRef::new(&db, 2).basic_account(&ADDRESS),
625 Ok(Some(acc)) if acc == acc_at3
626 ));
627 assert!(matches!(
628 HistoricalStateProviderRef::new(&db, 3).basic_account(&ADDRESS),
629 Ok(Some(acc)) if acc == acc_at3
630 ));
631 assert!(matches!(
632 HistoricalStateProviderRef::new(&db, 4).basic_account(&ADDRESS),
633 Ok(Some(acc)) if acc == acc_at7
634 ));
635 assert!(matches!(
636 HistoricalStateProviderRef::new(&db, 7).basic_account(&ADDRESS),
637 Ok(Some(acc)) if acc == acc_at7
638 ));
639 assert!(matches!(
640 HistoricalStateProviderRef::new(&db, 9).basic_account(&ADDRESS),
641 Ok(Some(acc)) if acc == acc_at10
642 ));
643 assert!(matches!(
644 HistoricalStateProviderRef::new(&db, 10).basic_account(&ADDRESS),
645 Ok(Some(acc)) if acc == acc_at10
646 ));
647 assert!(matches!(
648 HistoricalStateProviderRef::new(&db, 11).basic_account(&ADDRESS),
649 Ok(Some(acc)) if acc == acc_at15
650 ));
651 assert!(matches!(
652 HistoricalStateProviderRef::new(&db, 16).basic_account(&ADDRESS),
653 Ok(Some(acc)) if acc == acc_plain
654 ));
655
656 assert!(matches!(
657 HistoricalStateProviderRef::new(&db, 1).basic_account(&HIGHER_ADDRESS),
658 Ok(None)
659 ));
660 assert!(matches!(
661 HistoricalStateProviderRef::new(&db, 1000).basic_account(&HIGHER_ADDRESS),
662 Ok(Some(acc)) if acc == higher_acc_plain
663 ));
664 }
665
666 #[test]
667 fn history_provider_get_storage() {
668 let factory = create_test_provider_factory();
669 let tx = factory.provider_rw().unwrap().into_tx();
670
671 tx.put::<tables::StoragesHistory>(
672 StorageShardedKey {
673 address: ADDRESS,
674 sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
675 },
676 BlockNumberList::new([3, 7]).unwrap(),
677 )
678 .unwrap();
679 tx.put::<tables::StoragesHistory>(
680 StorageShardedKey {
681 address: ADDRESS,
682 sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
683 },
684 BlockNumberList::new([10, 15]).unwrap(),
685 )
686 .unwrap();
687 tx.put::<tables::StoragesHistory>(
688 StorageShardedKey {
689 address: HIGHER_ADDRESS,
690 sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
691 },
692 BlockNumberList::new([4]).unwrap(),
693 )
694 .unwrap();
695
696 let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
697 let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
698 let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
699 let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
700 let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
701 let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
702 let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
703
704 tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
706 tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
707 tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
708 tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
709 tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
710
711 tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
713 tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
714 tx.commit().unwrap();
715
716 let db = factory.provider().unwrap();
717
718 assert!(matches!(
720 HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
721 Ok(None)
722 ));
723 assert!(matches!(
724 HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
725 Ok(Some(U256::ZERO))
726 ));
727 assert!(matches!(
728 HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
729 Ok(Some(expected_value)) if expected_value == entry_at7.value
730 ));
731 assert!(matches!(
732 HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
733 Ok(Some(expected_value)) if expected_value == entry_at7.value
734 ));
735 assert!(matches!(
736 HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
737 Ok(Some(expected_value)) if expected_value == entry_at10.value
738 ));
739 assert!(matches!(
740 HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
741 Ok(Some(expected_value)) if expected_value == entry_at10.value
742 ));
743 assert!(matches!(
744 HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
745 Ok(Some(expected_value)) if expected_value == entry_at15.value
746 ));
747 assert!(matches!(
748 HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
749 Ok(Some(expected_value)) if expected_value == entry_plain.value
750 ));
751 assert!(matches!(
752 HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
753 Ok(None)
754 ));
755 assert!(matches!(
756 HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
757 Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
758 ));
759 }
760
761 #[test]
762 fn history_provider_unavailable() {
763 let factory = create_test_provider_factory();
764 let db = factory.database_provider_rw().unwrap();
765
766 let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
769 &db,
770 2,
771 LowestAvailableBlocks {
772 account_history_block_number: Some(3),
773 storage_history_block_number: Some(3),
774 },
775 );
776 assert!(matches!(
777 provider.account_history_lookup(ADDRESS),
778 Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
779 ));
780 assert!(matches!(
781 provider.storage_history_lookup(ADDRESS, STORAGE),
782 Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
783 ));
784
785 let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
788 &db,
789 2,
790 LowestAvailableBlocks {
791 account_history_block_number: Some(2),
792 storage_history_block_number: Some(2),
793 },
794 );
795 assert!(matches!(
796 provider.account_history_lookup(ADDRESS),
797 Ok(HistoryInfo::MaybeInPlainState)
798 ));
799 assert!(matches!(
800 provider.storage_history_lookup(ADDRESS, STORAGE),
801 Ok(HistoryInfo::MaybeInPlainState)
802 ));
803
804 let provider = HistoricalStateProviderRef::new_with_lowest_available_blocks(
807 &db,
808 2,
809 LowestAvailableBlocks {
810 account_history_block_number: Some(1),
811 storage_history_block_number: Some(1),
812 },
813 );
814 assert!(matches!(
815 provider.account_history_lookup(ADDRESS),
816 Ok(HistoryInfo::MaybeInPlainState)
817 ));
818 assert!(matches!(
819 provider.storage_history_lookup(ADDRESS, STORAGE),
820 Ok(HistoryInfo::MaybeInPlainState)
821 ));
822 }
823}