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