reth_trie_db/
changesets.rs1use crate::DatabaseHashedPostState;
7use alloy_primitives::BlockNumber;
8use reth_storage_api::{BlockNumReader, ChangeSetReader, StorageChangeSetReader};
9use reth_storage_errors::provider::ProviderError;
10use reth_trie::{
11 hashed_cursor::{HashedCursorFactory, HashedPostStateCursorFactory},
12 trie_cursor::{InMemoryTrieCursorFactory, TrieCursorFactory},
13 StateRoot,
14};
15use reth_trie_common::updates::TrieUpdatesSorted;
16use std::ops::RangeInclusive;
17use tracing::debug;
18
19pub fn compute_block_trie_changesets<Provider, StateTrieProvider>(
29 provider: &Provider,
30 state_trie_provider: &StateTrieProvider,
31 block_number: BlockNumber,
32) -> Result<TrieUpdatesSorted, ProviderError>
33where
34 Provider: ChangeSetReader + StorageChangeSetReader + BlockNumReader,
35 StateTrieProvider: TrieCursorFactory + HashedCursorFactory,
36{
37 let db_tip_block = provider.best_block_number()?;
38 compute_range_trie_changesets(
39 provider,
40 state_trie_provider,
41 block_number..=block_number,
42 db_tip_block,
43 )
44}
45
46pub fn compute_range_trie_changesets<Provider, StateTrieProvider>(
56 provider: &Provider,
57 state_trie_provider: &StateTrieProvider,
58 range: RangeInclusive<BlockNumber>,
59 db_tip_block: BlockNumber,
60) -> Result<TrieUpdatesSorted, ProviderError>
61where
62 Provider: ChangeSetReader + StorageChangeSetReader + BlockNumReader,
63 StateTrieProvider: TrieCursorFactory + HashedCursorFactory,
64{
65 let start_block = *range.start();
66 let end_block = *range.end();
67
68 if start_block > end_block {
69 return Ok(TrieUpdatesSorted::default())
70 }
71
72 if end_block > db_tip_block {
73 return Err(ProviderError::InsufficientChangesets {
74 requested: end_block,
75 available: 0..=db_tip_block,
76 })
77 }
78
79 debug!(
80 target: "trie::changesets",
81 start_block,
82 end_block,
83 db_tip_block,
84 "Computing range trie changesets from database state"
85 );
86
87 let range_state_revert = reth_trie::HashedPostStateSorted::from_reverts(provider, range)?;
89 let range_prefix_sets = range_state_revert.construct_prefix_sets();
90
91 let (range_nodes, range_state) = if end_block == db_tip_block {
92 debug!(
93 target: "trie::changesets",
94 start_block,
95 end_block,
96 db_tip_block,
97 "Skipping tail trie revert computation for tip-ended range"
98 );
99
100 (TrieUpdatesSorted::default(), range_state_revert)
101 } else {
102 let tail_state_revert = end_block
104 .checked_add(1)
105 .map(|next_block| {
106 reth_trie::HashedPostStateSorted::from_reverts(provider, next_block..)
107 })
108 .transpose()?
109 .unwrap_or_default();
110
111 let tail_prefix_sets = tail_state_revert.construct_prefix_sets().freeze();
113 let tail_trie_revert = StateRoot::new(
114 state_trie_provider,
115 HashedPostStateCursorFactory::new(state_trie_provider, &tail_state_revert),
116 )
117 .with_prefix_sets(tail_prefix_sets)
118 .root_with_updates()
119 .map_err(ProviderError::other)?
120 .1
121 .into_sorted();
122
123 let mut pre_range_state_revert = tail_state_revert;
125 pre_range_state_revert.extend_ref_and_sort(&range_state_revert);
126
127 (tail_trie_revert, pre_range_state_revert)
128 };
129
130 let range_trie_revert = StateRoot::new(
131 InMemoryTrieCursorFactory::new(state_trie_provider, &range_nodes),
132 HashedPostStateCursorFactory::new(state_trie_provider, &range_state),
133 )
134 .with_prefix_sets(range_prefix_sets.freeze())
135 .root_with_updates()
136 .map_err(ProviderError::other)?
137 .1
138 .into_sorted();
139
140 debug!(
141 target: "trie::changesets",
142 start_block,
143 end_block,
144 num_account_nodes = range_trie_revert.account_nodes_ref().len(),
145 num_storage_tries = range_trie_revert.storage_tries_ref().len(),
146 "Computed range trie changesets successfully"
147 );
148
149 Ok(range_trie_revert)
150}