reth_provider/providers/
consistent_view.rs1use crate::{BlockNumReader, DatabaseProviderFactory, HeaderProvider};
2use alloy_primitives::B256;
3use reth_storage_api::StateCommitmentProvider;
4pub use reth_storage_errors::provider::ConsistentViewError;
5use reth_storage_errors::provider::ProviderResult;
6
7#[derive(Clone, Debug)]
23pub struct ConsistentDbView<Factory> {
24 factory: Factory,
25 tip: Option<(B256, u64)>,
26}
27
28impl<Factory> ConsistentDbView<Factory>
29where
30 Factory: DatabaseProviderFactory<Provider: BlockNumReader + HeaderProvider>
31 + StateCommitmentProvider,
32{
33 pub const fn new(factory: Factory, tip: Option<(B256, u64)>) -> Self {
35 Self { factory, tip }
36 }
37
38 pub fn new_with_latest_tip(provider: Factory) -> ProviderResult<Self> {
40 let provider_ro = provider.database_provider_ro()?;
41 let last_num = provider_ro.last_block_number()?;
42 let tip = provider_ro.sealed_header(last_num)?.map(|h| (h.hash(), last_num));
43 Ok(Self::new(provider, tip))
44 }
45
46 pub fn provider_ro(&self) -> ProviderResult<Factory::Provider> {
48 let provider_ro = self.factory.database_provider_ro()?;
50
51 if let Some((hash, number)) = self.tip {
73 if provider_ro.sealed_header(number)?.is_none_or(|header| header.hash() != hash) {
74 return Err(ConsistentViewError::Reorged { block: hash }.into())
75 }
76 }
77
78 Ok(provider_ro)
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use reth_errors::ProviderError;
85 use std::str::FromStr;
86
87 use super::*;
88 use crate::{
89 test_utils::create_test_provider_factory_with_chain_spec, BlockWriter,
90 StaticFileProviderFactory, StaticFileWriter,
91 };
92 use alloy_primitives::Bytes;
93 use assert_matches::assert_matches;
94 use reth_chainspec::{EthChainSpec, MAINNET};
95 use reth_ethereum_primitives::{Block, BlockBody};
96 use reth_primitives_traits::{block::TestBlock, RecoveredBlock, SealedBlock};
97 use reth_static_file_types::StaticFileSegment;
98 use reth_storage_api::StorageLocation;
99
100 #[test]
101 fn test_consistent_view_extend() {
102 let provider_factory = create_test_provider_factory_with_chain_spec(MAINNET.clone());
103
104 let genesis_header = MAINNET.genesis_header();
105 let genesis_block =
106 SealedBlock::<Block>::seal_parts(genesis_header.clone(), BlockBody::default());
107 let genesis_hash: B256 = genesis_block.hash();
108 let genesis_block = RecoveredBlock::new_sealed(genesis_block, vec![]);
109
110 let provider_rw = provider_factory.provider_rw().unwrap();
112 provider_rw.insert_block(genesis_block, StorageLocation::StaticFiles).unwrap();
113 provider_rw.commit().unwrap();
114
115 let view = ConsistentDbView::new_with_latest_tip(provider_factory.clone()).unwrap();
117
118 assert_matches!(view.provider_ro(), Ok(_));
120
121 let mut block = Block::default();
123 block.header_mut().parent_hash = genesis_hash;
124 block.header_mut().number = 1;
125 let sealed_block = SealedBlock::seal_slow(block);
126 let recovered_block = RecoveredBlock::new_sealed(sealed_block, vec![]);
127
128 let provider_rw = provider_factory.provider_rw().unwrap();
130 provider_rw.insert_block(recovered_block, StorageLocation::StaticFiles).unwrap();
131 provider_rw.commit().unwrap();
132
133 assert_matches!(view.provider_ro(), Ok(_));
135
136 let mut block = Block::default();
138 block.header_mut().parent_hash = genesis_hash;
139 block.header_mut().number = 2;
140 let sealed_block = SealedBlock::seal_slow(block);
141 let recovered_block = RecoveredBlock::new_sealed(sealed_block, vec![]);
142
143 let provider_rw = provider_factory.provider_rw().unwrap();
145 provider_rw.insert_block(recovered_block, StorageLocation::StaticFiles).unwrap();
146 provider_rw.commit().unwrap();
147
148 assert_matches!(view.provider_ro(), Ok(_));
150 }
151
152 #[test]
153 fn test_consistent_view_remove() {
154 let provider_factory = create_test_provider_factory_with_chain_spec(MAINNET.clone());
155
156 let genesis_header = MAINNET.genesis_header();
157 let genesis_block =
158 SealedBlock::<Block>::seal_parts(genesis_header.clone(), BlockBody::default());
159 let genesis_hash: B256 = genesis_block.hash();
160 let genesis_block = RecoveredBlock::new_sealed(genesis_block, vec![]);
161
162 let provider_rw = provider_factory.provider_rw().unwrap();
164 provider_rw.insert_block(genesis_block, StorageLocation::Both).unwrap();
165 provider_rw.0.static_file_provider().commit().unwrap();
166 provider_rw.commit().unwrap();
167
168 let view = ConsistentDbView::new_with_latest_tip(provider_factory.clone()).unwrap();
170
171 assert_matches!(view.provider_ro(), Ok(_));
173
174 let mut block = Block::default();
176 block.header_mut().parent_hash = genesis_hash;
177 block.header_mut().number = 1;
178 let sealed_block = SealedBlock::seal_slow(block);
179 let recovered_block = RecoveredBlock::new_sealed(sealed_block.clone(), vec![]);
180
181 let provider_rw = provider_factory.provider_rw().unwrap();
183 provider_rw.insert_block(recovered_block, StorageLocation::Both).unwrap();
184 provider_rw.0.static_file_provider().commit().unwrap();
185 provider_rw.commit().unwrap();
186
187 let view = ConsistentDbView::new_with_latest_tip(provider_factory.clone()).unwrap();
189 let initial_tip_hash = sealed_block.hash();
190
191 assert_matches!(view.provider_ro(), Ok(_));
193
194 let provider_rw = provider_factory.provider_rw().unwrap();
196 provider_rw.remove_blocks_above(0, StorageLocation::Both).unwrap();
197 let sf_provider = provider_rw.0.static_file_provider();
198 sf_provider.get_writer(1, StaticFileSegment::Headers).unwrap().prune_headers(1).unwrap();
199 sf_provider.commit().unwrap();
200 provider_rw.commit().unwrap();
201
202 let Err(ProviderError::ConsistentView(boxed_consistent_view_err)) = view.provider_ro()
204 else {
205 panic!("expected reorged consistent view error, got success");
206 };
207 let unboxed = *boxed_consistent_view_err;
208 assert_eq!(unboxed, ConsistentViewError::Reorged { block: initial_tip_hash });
209
210 let mut block = Block::default();
212 block.header_mut().parent_hash = genesis_hash;
213 block.header_mut().number = 1;
214 block.header_mut().extra_data =
215 Bytes::from_str("6a6f75726e657920746f20697468616361").unwrap();
216 let sealed_block = SealedBlock::seal_slow(block);
217 let recovered_block = RecoveredBlock::new_sealed(sealed_block, vec![]);
218
219 let provider_rw = provider_factory.provider_rw().unwrap();
221 provider_rw.insert_block(recovered_block, StorageLocation::Both).unwrap();
222 provider_rw.0.static_file_provider().commit().unwrap();
223 provider_rw.commit().unwrap();
224
225 let Err(ProviderError::ConsistentView(boxed_consistent_view_err)) = view.provider_ro()
227 else {
228 panic!("expected reorged consistent view error, got success");
229 };
230 let unboxed = *boxed_consistent_view_err;
231 assert_eq!(unboxed, ConsistentViewError::Reorged { block: initial_tip_hash });
232 }
233}