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 HashedPostStateSorted, StateRoot,
14};
15use reth_trie_common::{updates::TrieUpdatesSorted, TrieInputSorted};
16use std::{iter::once, ops::RangeInclusive, sync::Arc};
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 &[],
42 block_number..=block_number,
43 db_tip_block,
44 )
45}
46
47pub fn compute_range_trie_changesets<Provider, StateTrieProvider>(
64 provider: &Provider,
65 state_trie_provider: &StateTrieProvider,
66 forward_updates: &[(BlockNumber, Arc<TrieUpdatesSorted>)],
67 range: RangeInclusive<BlockNumber>,
68 db_tip_block: BlockNumber,
69) -> Result<TrieUpdatesSorted, ProviderError>
70where
71 Provider: ChangeSetReader + StorageChangeSetReader + BlockNumReader,
72 StateTrieProvider: TrieCursorFactory + HashedCursorFactory,
73{
74 debug_assert!(
75 forward_updates.is_sorted_by(|(a, _), (b, _)| a < b),
76 "forward updates must have strictly increasing block numbers"
77 );
78
79 if range.is_empty() {
80 return Ok(TrieUpdatesSorted::default())
81 }
82
83 let start_block = *range.start();
84 let end_block = *range.end();
85
86 if end_block > db_tip_block {
87 return Err(ProviderError::InsufficientChangesets {
88 requested: end_block,
89 available: 0..=db_tip_block,
90 })
91 }
92
93 debug!(
94 target: "trie::changesets",
95 start_block,
96 end_block,
97 db_tip_block,
98 "Computing range trie changesets from database state"
99 );
100
101 let mut overlay = TrieInputSorted::default();
102 if end_block < db_tip_block {
104 rewind_trie_range(
105 provider,
106 state_trie_provider,
107 forward_updates,
108 (end_block + 1)..=db_tip_block,
109 &mut overlay,
110 &mut None,
111 )?;
112 }
113
114 let mut reverts = Some(TrieUpdatesSorted::default());
115 rewind_trie_range(
116 provider,
117 state_trie_provider,
118 forward_updates,
119 range,
120 &mut overlay,
121 &mut reverts,
122 )?;
123 let reverts = reverts.expect("revert collection is enabled");
124
125 debug!(
126 target: "trie::changesets",
127 start_block,
128 end_block,
129 num_account_nodes = reverts.account_nodes_ref().len(),
130 num_storage_tries = reverts.storage_tries_ref().len(),
131 "Computed range trie changesets successfully"
132 );
133
134 Ok(reverts)
135}
136
137fn rewind_trie_range<Provider, StateTrieProvider>(
139 provider: &Provider,
140 state_trie_provider: &StateTrieProvider,
141 forward_updates: &[(BlockNumber, Arc<TrieUpdatesSorted>)],
142 blocks: RangeInclusive<BlockNumber>,
143 overlay: &mut TrieInputSorted,
144 reverts: &mut Option<TrieUpdatesSorted>,
145) -> Result<(), ProviderError>
146where
147 Provider: ChangeSetReader + StorageChangeSetReader + BlockNumReader,
148 StateTrieProvider: TrieCursorFactory + HashedCursorFactory,
149{
150 let mut end = *blocks.end();
151 while end >= *blocks.start() {
152 let mut start = end;
153 let mut segment_range = 0..0;
154
155 if let Ok(index) = forward_updates.binary_search_by_key(&end, |(block, _)| *block) {
159 segment_range = index..index + 1;
160 while segment_range.start > 0 && start > *blocks.start() {
161 if forward_updates[segment_range.start - 1].0 != start - 1 {
162 break
163 }
164 segment_range.start -= 1;
165 start -= 1;
166 }
167 }
168
169 let segment_state_reverts = HashedPostStateSorted::from_reverts(provider, start..=end)?;
172 let prefixes = segment_state_reverts.construct_prefix_sets().freeze();
173 Arc::make_mut(&mut overlay.state).extend_ref_and_sort(&segment_state_reverts);
174
175 let segment_trie_reverts = StateRoot::new(
176 InMemoryTrieCursorFactory::new(state_trie_provider, overlay.nodes.as_ref()),
177 HashedPostStateCursorFactory::new(state_trie_provider, overlay.state.as_ref()),
178 )
179 .with_prefix_sets(prefixes)
180 .root_with_updates()
181 .map_err(ProviderError::other)?
182 .1
183 .into_sorted();
184
185 let segment_trie_forward =
190 forward_updates[segment_range].iter().rev().map(|(_, updates)| updates.as_ref());
191 let segment_trie_reverts =
192 TrieUpdatesSorted::merge_iter(once(&segment_trie_reverts).chain(segment_trie_forward));
193
194 Arc::make_mut(&mut overlay.nodes).extend_ref_and_sort(&segment_trie_reverts);
195 if let Some(reverts) = reverts {
196 reverts.extend_ref_and_sort(&segment_trie_reverts);
197 }
198
199 if start == *blocks.start() {
200 break
201 }
202 end = start - 1;
203 }
204
205 Ok(())
206}