1#![cfg(feature = "file-system-data-source")]
14
15use std::{
16 collections::{
17 BTreeMap,
18 hash_map::{Entry, HashMap},
19 },
20 hash::Hash,
21 iter,
22 ops::{Bound, Deref, RangeBounds},
23 path::Path,
24};
25
26use async_lock::{RwLock, RwLockReadGuard, RwLockWriteGuard};
27use async_trait::async_trait;
28use atomic_store::{AtomicStore, AtomicStoreLoader, PersistenceError};
29use committable::Committable;
30use futures::future::Future;
31use hotshot_types::{
32 data::{VidCommitment, VidShare},
33 simple_certificate::CertificatePair,
34 traits::{block_contents::BlockHeader, node_implementation::NodeType},
35};
36use serde::{Serialize, de::DeserializeOwned};
37use snafu::OptionExt;
38
39use super::{
40 Aggregate, AggregatesStorage, AvailabilityStorage, NodeStorage, PayloadMetadata,
41 SerializableRetry, UpdateAggregatesStorage, UpdateAvailabilityStorage, VidCommonMetadata,
42 ledger_log::{Iter, LedgerLog},
43 pruning::{PruneStorage, PrunedHeightStorage, PrunerConfig},
44};
45use crate::{
46 Header, MissingSnafu, NotFoundSnafu, Payload, QueryError, QueryResult,
47 availability::{
48 Certificate2, NamespaceId,
49 data_source::{BlockId, LeafId},
50 query_data::{
51 BlockHash, BlockQueryData, LeafHash, LeafQueryData, PayloadQueryData, QueryableHeader,
52 QueryablePayload, TransactionHash, VidCommonQueryData,
53 },
54 },
55 data_source::{VersionedDataSource, update},
56 metrics::PrometheusMetrics,
57 node::{SyncStatusQueryData, TimeWindowQueryData, WindowStart},
58 status::HasMetrics,
59 types::HeightIndexed,
60};
61
62const CACHED_LEAVES_COUNT: usize = 100;
63const CACHED_BLOCKS_COUNT: usize = 100;
64const CACHED_VID_COMMON_COUNT: usize = 100;
65const CACHED_CERT2_COUNT: usize = 100;
66
67#[derive(custom_debug::Debug)]
68pub struct FileSystemStorageInner<Types>
69where
70 Types: NodeType,
71 Header<Types>: QueryableHeader<Types>,
72 Payload<Types>: QueryablePayload<Types>,
73{
74 index_by_leaf_hash: HashMap<LeafHash<Types>, u64>,
75 index_by_block_hash: HashMap<BlockHash<Types>, u64>,
76 index_by_payload_hash: HashMap<VidCommitment, u64>,
77 index_by_txn_hash: HashMap<TransactionHash<Types>, u64>,
78 index_by_time: BTreeMap<u64, Vec<u64>>,
79 num_transactions: usize,
80 payload_size: usize,
81 #[debug(skip)]
82 top_storage: Option<AtomicStore>,
83 leaf_storage: LedgerLog<LeafQueryData<Types>>,
84 block_storage: LedgerLog<BlockQueryData<Types>>,
85 vid_storage: LedgerLog<(VidCommonQueryData<Types>, Option<VidShare>)>,
86 latest_qc_chain: Option<[CertificatePair<Types>; 2]>,
87 cert2_storage: LedgerLog<Certificate2<Types>>,
88}
89
90impl<Types> FileSystemStorageInner<Types>
91where
92 Types: NodeType,
93 Header<Types>: QueryableHeader<Types>,
94 Payload<Types>: QueryablePayload<Types>,
95{
96 fn get_block_index(&self, id: BlockId<Types>) -> QueryResult<usize> {
97 match id {
98 BlockId::Number(n) => Ok(n),
99 BlockId::Hash(h) => {
100 Ok(*self.index_by_block_hash.get(&h).context(NotFoundSnafu)? as usize)
101 },
102 BlockId::PayloadHash(h) => {
103 Ok(*self.index_by_payload_hash.get(&h).context(NotFoundSnafu)? as usize)
104 },
105 }
106 }
107
108 fn get_block(&self, id: BlockId<Types>) -> QueryResult<BlockQueryData<Types>> {
109 self.block_storage
110 .iter()
111 .nth(self.get_block_index(id)?)
112 .context(NotFoundSnafu)?
113 .context(MissingSnafu)
114 }
115
116 fn get_header(&self, id: BlockId<Types>) -> QueryResult<Header<Types>> {
117 self.get_block(id).map(|block| block.header)
118 }
119
120 fn get_block_range<R>(&self, range: R) -> QueryResult<Vec<QueryResult<BlockQueryData<Types>>>>
121 where
122 R: RangeBounds<usize> + Send,
123 {
124 Ok(range_iter(self.block_storage.iter(), range).collect())
125 }
126}
127
128#[derive(Debug)]
130pub struct FileSystemStorage<Types: NodeType>
131where
132 Header<Types>: QueryableHeader<Types>,
133 Payload<Types>: QueryablePayload<Types>,
134{
135 inner: RwLock<FileSystemStorageInner<Types>>,
136 metrics: PrometheusMetrics,
137}
138
139impl<Types: NodeType> PrunerConfig for FileSystemStorage<Types>
140where
141 Header<Types>: QueryableHeader<Types>,
142 Payload<Types>: QueryablePayload<Types>,
143{
144}
145impl<Types: NodeType> PruneStorage for FileSystemStorage<Types>
146where
147 Header<Types>: QueryableHeader<Types>,
148 Payload<Types>: QueryablePayload<Types>,
149{
150 type Pruner<'a> = ();
151}
152
153impl<Types: NodeType> FileSystemStorage<Types>
154where
155 Payload<Types>: QueryablePayload<Types>,
156 Header<Types>: QueryableHeader<Types>,
157{
158 pub async fn create(path: &Path) -> Result<Self, PersistenceError> {
164 let mut loader = AtomicStoreLoader::create(path, "hotshot_data_source")?;
165 loader.retain_archives(1);
166 let data_source = Self::create_with_store(&mut loader).await?;
167 data_source.inner.write().await.top_storage = Some(AtomicStore::open(loader)?);
168 Ok(data_source)
169 }
170
171 pub async fn open(path: &Path) -> Result<Self, PersistenceError> {
177 let mut loader = AtomicStoreLoader::load(path, "hotshot_data_source")?;
178 loader.retain_archives(1);
179 let data_source = Self::open_with_store(&mut loader).await?;
180 data_source.inner.write().await.top_storage = Some(AtomicStore::open(loader)?);
181 Ok(data_source)
182 }
183
184 pub async fn create_with_store(
193 loader: &mut AtomicStoreLoader,
194 ) -> Result<Self, PersistenceError> {
195 Ok(Self {
196 inner: RwLock::new(FileSystemStorageInner {
197 index_by_leaf_hash: Default::default(),
198 index_by_block_hash: Default::default(),
199 index_by_payload_hash: Default::default(),
200 index_by_txn_hash: Default::default(),
201 index_by_time: Default::default(),
202 num_transactions: 0,
203 payload_size: 0,
204 top_storage: None,
205 leaf_storage: LedgerLog::create(loader, "leaves", CACHED_LEAVES_COUNT)?,
206 block_storage: LedgerLog::create(loader, "blocks", CACHED_BLOCKS_COUNT)?,
207 vid_storage: LedgerLog::create(loader, "vid_common", CACHED_VID_COMMON_COUNT)?,
208 cert2_storage: LedgerLog::create(loader, "cert2", CACHED_CERT2_COUNT)?,
209 latest_qc_chain: None,
210 }),
211 metrics: Default::default(),
212 })
213 }
214
215 pub async fn open_with_store(loader: &mut AtomicStoreLoader) -> Result<Self, PersistenceError> {
224 let leaf_storage =
225 LedgerLog::<LeafQueryData<Types>>::open(loader, "leaves", CACHED_LEAVES_COUNT)?;
226 let block_storage =
227 LedgerLog::<BlockQueryData<Types>>::open(loader, "blocks", CACHED_BLOCKS_COUNT)?;
228 let vid_storage = LedgerLog::<(VidCommonQueryData<Types>, Option<VidShare>)>::open(
229 loader,
230 "vid_common",
231 CACHED_VID_COMMON_COUNT,
232 )?;
233 let cert2_storage =
234 LedgerLog::<Certificate2<Types>>::open(loader, "cert2", CACHED_CERT2_COUNT)?;
235
236 let mut index_by_block_hash = HashMap::new();
237 let mut index_by_payload_hash = HashMap::new();
238 let mut index_by_time = BTreeMap::<u64, Vec<u64>>::new();
239 let index_by_leaf_hash = leaf_storage
240 .iter()
241 .flatten()
242 .map(|leaf| {
243 update_index_by_hash(&mut index_by_block_hash, leaf.block_hash(), leaf.height());
244 update_index_by_hash(
245 &mut index_by_payload_hash,
246 leaf.payload_hash(),
247 leaf.height(),
248 );
249 index_by_time
250 .entry(leaf.header().timestamp())
251 .or_default()
252 .push(leaf.height());
253 (leaf.hash(), leaf.height())
254 })
255 .collect();
256
257 let mut index_by_txn_hash = HashMap::new();
258 let mut num_transactions = 0;
259 let mut payload_size = 0;
260 for block in block_storage.iter().flatten() {
261 num_transactions += block.len();
262 payload_size += block.size() as usize;
263
264 let height = block.height();
265 for (_, txn) in block.enumerate() {
266 update_index_by_hash(&mut index_by_txn_hash, txn.commit(), height);
267 }
268 }
269
270 Ok(Self {
271 inner: RwLock::new(FileSystemStorageInner {
272 index_by_leaf_hash,
273 index_by_block_hash,
274 index_by_payload_hash,
275 index_by_txn_hash,
276 index_by_time,
277 num_transactions,
278 payload_size,
279 leaf_storage,
280 block_storage,
281 vid_storage,
282 cert2_storage,
283 top_storage: None,
284 latest_qc_chain: None,
285 }),
286 metrics: Default::default(),
287 })
288 }
289
290 pub async fn skip_version(&self) -> Result<(), PersistenceError> {
292 let mut inner = self.inner.write().await;
293 inner.leaf_storage.skip_version()?;
294 inner.block_storage.skip_version()?;
295 inner.vid_storage.skip_version()?;
296 inner.cert2_storage.skip_version()?;
297 if let Some(store) = &mut inner.top_storage {
298 store.commit_version()?;
299 }
300 Ok(())
301 }
302
303 pub async fn get_vid_share(&self, block_id: BlockId<Types>) -> QueryResult<VidShare> {
305 let mut tx = self.read().await.map_err(|err| QueryError::Error {
306 message: err.to_string(),
307 })?;
308 let share = tx.vid_share(block_id).await?;
309 Ok(share)
310 }
311
312 pub async fn get_vid_common(
314 &self,
315 block_id: BlockId<Types>,
316 ) -> QueryResult<VidCommonQueryData<Types>> {
317 let mut tx = self.read().await.map_err(|err| QueryError::Error {
318 message: err.to_string(),
319 })?;
320 let share = tx.get_vid_common(block_id).await?;
321 Ok(share)
322 }
323
324 pub async fn get_vid_common_metadata(
326 &self,
327 block_id: BlockId<Types>,
328 ) -> QueryResult<VidCommonMetadata<Types>> {
329 let mut tx = self.read().await.map_err(|err| QueryError::Error {
330 message: err.to_string(),
331 })?;
332 let share = tx.get_vid_common_metadata(block_id).await?;
333 Ok(share)
334 }
335}
336
337pub trait Revert {
338 fn revert(&mut self);
339}
340
341impl<Types> Revert for RwLockWriteGuard<'_, FileSystemStorageInner<Types>>
342where
343 Types: NodeType,
344 Header<Types>: QueryableHeader<Types>,
345 Payload<Types>: QueryablePayload<Types>,
346{
347 fn revert(&mut self) {
348 self.leaf_storage.revert_version().unwrap();
349 self.block_storage.revert_version().unwrap();
350 self.vid_storage.revert_version().unwrap();
351 self.cert2_storage.revert_version().unwrap();
352 }
353}
354
355impl<Types> Revert for RwLockReadGuard<'_, FileSystemStorageInner<Types>>
356where
357 Types: NodeType,
358 Header<Types>: QueryableHeader<Types>,
359 Payload<Types>: QueryablePayload<Types>,
360{
361 fn revert(&mut self) {
362 }
364}
365
366#[derive(Debug)]
367pub struct Transaction<T: Revert> {
368 inner: T,
369}
370
371impl<T: Revert> Drop for Transaction<T> {
372 fn drop(&mut self) {
373 self.inner.revert();
374 }
375}
376impl<Types> update::Transaction for Transaction<RwLockWriteGuard<'_, FileSystemStorageInner<Types>>>
377where
378 Types: NodeType,
379 Header<Types>: QueryableHeader<Types>,
380 Payload<Types>: QueryablePayload<Types>,
381{
382 async fn commit(mut self) -> anyhow::Result<()> {
383 self.inner.leaf_storage.commit_version().await?;
384 self.inner.block_storage.commit_version().await?;
385 self.inner.vid_storage.commit_version().await?;
386 self.inner.cert2_storage.commit_version().await?;
387 if let Some(store) = &mut self.inner.top_storage {
388 store.commit_version()?;
389 }
390 Ok(())
391 }
392
393 fn revert(self) -> impl Future + Send {
394 async move {}
396 }
397}
398
399impl<Types> update::Transaction for Transaction<RwLockReadGuard<'_, FileSystemStorageInner<Types>>>
400where
401 Types: NodeType,
402 Header<Types>: QueryableHeader<Types>,
403 Payload<Types>: QueryablePayload<Types>,
404{
405 async fn commit(self) -> anyhow::Result<()> {
406 Ok(())
408 }
409
410 fn revert(self) -> impl Future + Send {
411 async move {}
413 }
414}
415
416impl<Types: NodeType> VersionedDataSource for FileSystemStorage<Types>
417where
418 Header<Types>: QueryableHeader<Types>,
419 Payload<Types>: QueryablePayload<Types>,
420{
421 type Transaction<'a>
422 = Transaction<RwLockWriteGuard<'a, FileSystemStorageInner<Types>>>
423 where
424 Self: 'a;
425 type ReadOnly<'a>
426 = Transaction<RwLockReadGuard<'a, FileSystemStorageInner<Types>>>
427 where
428 Self: 'a;
429
430 async fn write(&self) -> anyhow::Result<Self::Transaction<'_>> {
431 Ok(Transaction {
432 inner: self.inner.write().await,
433 })
434 }
435
436 async fn read(&self) -> anyhow::Result<Self::ReadOnly<'_>> {
437 Ok(Transaction {
438 inner: self.inner.read().await,
439 })
440 }
441}
442
443#[async_trait]
444impl<Types: NodeType> SerializableRetry for FileSystemStorage<Types>
445where
446 Header<Types>: QueryableHeader<Types>,
447 Payload<Types>: QueryablePayload<Types>,
448{
449 async fn serializable_retry<T, E, F, Fut>(&self, _op: &'static str, f: F) -> Result<T, E>
452 where
453 T: Send,
454 E: std::fmt::Display + Send,
455 F: Fn() -> Fut + Send + Sync,
456 Fut: Future<Output = Result<T, E>> + Send,
457 {
458 f().await
459 }
460}
461
462fn range_iter<T>(
463 mut iter: Iter<'_, T>,
464 range: impl RangeBounds<usize>,
465) -> impl '_ + Iterator<Item = QueryResult<T>>
466where
467 T: Clone + Serialize + DeserializeOwned,
468{
469 let start = range.start_bound().cloned();
470 let end = range.end_bound().cloned();
471
472 let mut pos = match start {
474 Bound::Included(n) => {
475 if n > 0 {
476 iter.nth(n - 1);
477 }
478 n
479 },
480 Bound::Excluded(n) => {
481 iter.nth(n);
482 n + 1
483 },
484 Bound::Unbounded => 0,
485 };
486
487 iter::from_fn(move || {
488 let reached_end = match end {
490 Bound::Included(n) => pos > n,
491 Bound::Excluded(n) => pos >= n,
492 Bound::Unbounded => false,
493 };
494 if reached_end {
495 return None;
496 }
497 let opt = iter.next()?;
498 pos += 1;
499 Some(opt.context(MissingSnafu))
500 })
501}
502
503#[async_trait]
504impl<Types, T> AvailabilityStorage<Types> for Transaction<T>
505where
506 Types: NodeType,
507 Payload<Types>: QueryablePayload<Types>,
508 Header<Types>: QueryableHeader<Types>,
509 T: Revert + Deref<Target = FileSystemStorageInner<Types>> + Send + Sync,
510{
511 async fn get_leaf(&mut self, id: LeafId<Types>) -> QueryResult<LeafQueryData<Types>> {
512 let n = match id {
513 LeafId::Number(n) => n,
514 LeafId::Hash(h) => *self
515 .inner
516 .index_by_leaf_hash
517 .get(&h)
518 .context(NotFoundSnafu)? as usize,
519 };
520 self.inner
521 .leaf_storage
522 .iter()
523 .nth(n)
524 .context(NotFoundSnafu)?
525 .context(MissingSnafu)
526 }
527
528 async fn get_block(&mut self, id: BlockId<Types>) -> QueryResult<BlockQueryData<Types>> {
529 self.inner.get_block(id)
530 }
531
532 async fn get_header(&mut self, id: BlockId<Types>) -> QueryResult<Header<Types>> {
533 self.inner.get_header(id)
534 }
535
536 async fn get_payload(&mut self, id: BlockId<Types>) -> QueryResult<PayloadQueryData<Types>> {
537 self.get_block(id).await.map(PayloadQueryData::from)
538 }
539
540 async fn get_payload_metadata(
541 &mut self,
542 id: BlockId<Types>,
543 ) -> QueryResult<PayloadMetadata<Types>> {
544 self.get_block(id).await.map(PayloadMetadata::from)
545 }
546
547 async fn get_vid_common(
548 &mut self,
549 id: BlockId<Types>,
550 ) -> QueryResult<VidCommonQueryData<Types>> {
551 Ok(self
552 .inner
553 .vid_storage
554 .iter()
555 .nth(self.inner.get_block_index(id)?)
556 .context(NotFoundSnafu)?
557 .context(MissingSnafu)?
558 .0)
559 }
560
561 async fn get_vid_common_metadata(
562 &mut self,
563 id: BlockId<Types>,
564 ) -> QueryResult<VidCommonMetadata<Types>> {
565 self.get_vid_common(id).await.map(VidCommonMetadata::from)
566 }
567
568 async fn get_leaf_range<R>(
569 &mut self,
570 range: R,
571 ) -> QueryResult<Vec<QueryResult<LeafQueryData<Types>>>>
572 where
573 R: RangeBounds<usize> + Send,
574 {
575 Ok(range_iter(self.inner.leaf_storage.iter(), range).collect())
576 }
577
578 async fn get_block_range<R>(
579 &mut self,
580 range: R,
581 ) -> QueryResult<Vec<QueryResult<BlockQueryData<Types>>>>
582 where
583 R: RangeBounds<usize> + Send,
584 {
585 self.inner.get_block_range(range)
586 }
587
588 async fn get_payload_range<R>(
589 &mut self,
590 range: R,
591 ) -> QueryResult<Vec<QueryResult<PayloadQueryData<Types>>>>
592 where
593 R: RangeBounds<usize> + Send,
594 {
595 Ok(range_iter(self.inner.block_storage.iter(), range)
596 .map(|res| res.map(PayloadQueryData::from))
597 .collect())
598 }
599
600 async fn get_payload_metadata_range<R>(
601 &mut self,
602 range: R,
603 ) -> QueryResult<Vec<QueryResult<PayloadMetadata<Types>>>>
604 where
605 R: RangeBounds<usize> + Send + 'static,
606 {
607 Ok(range_iter(self.inner.block_storage.iter(), range)
608 .map(|res| res.map(PayloadMetadata::from))
609 .collect())
610 }
611
612 async fn get_vid_common_range<R>(
613 &mut self,
614 range: R,
615 ) -> QueryResult<Vec<QueryResult<VidCommonQueryData<Types>>>>
616 where
617 R: RangeBounds<usize> + Send,
618 {
619 Ok(range_iter(self.inner.vid_storage.iter(), range)
620 .map(|res| res.map(|(common, _)| common))
621 .collect())
622 }
623
624 async fn get_vid_common_metadata_range<R>(
625 &mut self,
626 range: R,
627 ) -> QueryResult<Vec<QueryResult<VidCommonMetadata<Types>>>>
628 where
629 R: RangeBounds<usize> + Send,
630 {
631 Ok(range_iter(self.inner.vid_storage.iter(), range)
632 .map(|res| res.map(|(common, _)| common.into()))
633 .collect())
634 }
635
636 async fn get_block_with_transaction(
637 &mut self,
638 hash: TransactionHash<Types>,
639 ) -> QueryResult<BlockQueryData<Types>> {
640 let height = self
641 .inner
642 .index_by_txn_hash
643 .get(&hash)
644 .context(NotFoundSnafu)?;
645 self.inner.get_block((*height as usize).into())
646 }
647}
648
649impl<Types: NodeType> UpdateAvailabilityStorage<Types>
650 for Transaction<RwLockWriteGuard<'_, FileSystemStorageInner<Types>>>
651where
652 Payload<Types>: QueryablePayload<Types>,
653 Header<Types>: QueryableHeader<Types>,
654{
655 async fn insert_qc_chain(
656 &mut self,
657 height: u64,
658 qc_chain: Option<[CertificatePair<Types>; 2]>,
659 ) -> anyhow::Result<()> {
660 if height + 1 >= (self.inner.leaf_storage.iter().len() as u64) {
661 if let Some(qc_chain) = qc_chain {
666 self.inner.latest_qc_chain = Some(qc_chain);
667 } else {
668 self.inner.latest_qc_chain = None;
671 }
672 }
673
674 Ok(())
675 }
676
677 async fn insert_cert2(
678 &mut self,
679 height: u64,
680 cert2: Certificate2<Types>,
681 ) -> anyhow::Result<()> {
682 self.inner.cert2_storage.insert(height as usize, cert2)?;
683 Ok(())
684 }
685
686 async fn insert_leaf_range<'a>(
687 &mut self,
688 leaves: impl Send + IntoIterator<Item = &'a LeafQueryData<Types>>,
689 ) -> anyhow::Result<()> {
690 for leaf in leaves {
691 if !self
692 .inner
693 .leaf_storage
694 .insert(leaf.height() as usize, leaf.clone())?
695 {
696 continue;
698 }
699 self.inner
700 .index_by_leaf_hash
701 .insert(leaf.hash(), leaf.height());
702 update_index_by_hash(
703 &mut self.inner.index_by_block_hash,
704 leaf.block_hash(),
705 leaf.height(),
706 );
707 update_index_by_hash(
708 &mut self.inner.index_by_payload_hash,
709 leaf.payload_hash(),
710 leaf.height(),
711 );
712 self.inner
713 .index_by_time
714 .entry(leaf.header().timestamp())
715 .or_default()
716 .push(leaf.height());
717 }
718
719 Ok(())
720 }
721
722 async fn insert_block_range<'a>(
723 &mut self,
724 blocks: impl Send + IntoIterator<IntoIter: Send, Item = &'a BlockQueryData<Types>>,
725 ) -> anyhow::Result<()> {
726 for block in blocks {
727 if !self
728 .inner
729 .block_storage
730 .insert(block.height() as usize, block.clone())?
731 {
732 continue;
734 }
735 self.inner.num_transactions += block.len();
736 self.inner.payload_size += block.size() as usize;
737 for (_, txn) in block.enumerate() {
738 update_index_by_hash(
739 &mut self.inner.index_by_txn_hash,
740 txn.commit(),
741 block.height(),
742 );
743 }
744 }
745 Ok(())
746 }
747
748 async fn insert_vid_range<'a>(
749 &mut self,
750 vid: impl Send
751 + IntoIterator<
752 IntoIter: Send,
753 Item = (&'a VidCommonQueryData<Types>, Option<&'a VidShare>),
754 >,
755 ) -> anyhow::Result<()> {
756 for (common, share) in vid {
757 self.inner
758 .vid_storage
759 .insert(common.height() as usize, (common.clone(), share.cloned()))?;
760 }
761 Ok(())
762 }
763}
764
765fn update_index_by_hash<H: Eq + Hash, P: Ord>(index: &mut HashMap<H, P>, hash: H, pos: P) {
770 match index.entry(hash) {
771 Entry::Occupied(mut e) => {
772 if &pos < e.get() {
773 e.insert(pos);
775 }
776 },
777 Entry::Vacant(e) => {
778 e.insert(pos);
779 },
780 }
781}
782
783#[async_trait]
784impl<Types, T> NodeStorage<Types> for Transaction<T>
785where
786 Types: NodeType,
787 Payload<Types>: QueryablePayload<Types>,
788 Header<Types>: QueryableHeader<Types>,
789 T: Revert + Deref<Target = FileSystemStorageInner<Types>> + Send + Sync,
790{
791 async fn block_height(&mut self) -> QueryResult<usize> {
792 Ok(self.inner.leaf_storage.iter().len())
793 }
794
795 async fn count_transactions_in_range(
796 &mut self,
797 range: impl RangeBounds<usize> + Send,
798 namespace: Option<NamespaceId<Types>>,
799 ) -> QueryResult<usize> {
800 if !matches!(range.start_bound(), Bound::Unbounded | Bound::Included(0))
801 || !matches!(range.end_bound(), Bound::Unbounded)
802 {
803 return Err(QueryError::Error {
804 message: "partial aggregates are not supported with file system backend".into(),
805 });
806 }
807
808 if namespace.is_some() {
809 return Err(QueryError::Error {
810 message: "file system does not support per-namespace stats".into(),
811 });
812 }
813
814 Ok(self.inner.num_transactions)
815 }
816
817 async fn payload_size_in_range(
818 &mut self,
819 range: impl RangeBounds<usize> + Send,
820 namespace: Option<NamespaceId<Types>>,
821 ) -> QueryResult<usize> {
822 if !matches!(range.start_bound(), Bound::Unbounded | Bound::Included(0))
823 || !matches!(range.end_bound(), Bound::Unbounded)
824 {
825 return Err(QueryError::Error {
826 message: "partial aggregates are not supported with file system backend".into(),
827 });
828 }
829
830 if namespace.is_some() {
831 return Err(QueryError::Error {
832 message: "file system does not support per-namespace stats".into(),
833 });
834 }
835
836 Ok(self.inner.payload_size)
837 }
838
839 async fn vid_share<ID>(&mut self, id: ID) -> QueryResult<VidShare>
840 where
841 ID: Into<BlockId<Types>> + Send + Sync,
842 {
843 self.inner
844 .vid_storage
845 .iter()
846 .nth(self.inner.get_block_index(id.into())?)
847 .context(NotFoundSnafu)?
848 .context(MissingSnafu)?
849 .1
850 .context(MissingSnafu)
851 }
852
853 async fn sync_status_for_range(
854 &mut self,
855 start: usize,
856 end: usize,
857 ) -> QueryResult<SyncStatusQueryData> {
858 Ok(SyncStatusQueryData {
859 leaves: self.inner.leaf_storage.sync_status(start, end),
860 blocks: self.inner.block_storage.sync_status(start, end),
861 vid_common: self.inner.vid_storage.sync_status(start, end),
862 pruned_height: None,
863 })
864 }
865
866 async fn get_header_window(
867 &mut self,
868 start: impl Into<WindowStart<Types>> + Send + Sync,
869 end: u64,
870 limit: usize,
871 ) -> QueryResult<TimeWindowQueryData<Header<Types>>> {
872 let first_block = match start.into() {
873 WindowStart::Height(h) => h,
874 WindowStart::Hash(h) => self.inner.get_header(h.into())?.block_number(),
875 WindowStart::Time(t) => {
876 let blocks = self
879 .inner
880 .index_by_time
881 .range(t..)
882 .next()
883 .context(NotFoundSnafu)?
884 .1;
885 blocks[0]
890 },
891 } as usize;
892
893 let mut res = TimeWindowQueryData::default();
894
895 if first_block > 0 {
897 res.prev = Some(self.inner.get_header((first_block - 1).into())?);
898 }
899
900 for block in self.inner.get_block_range(first_block..)? {
903 let header = block?.header().clone();
904 if header.timestamp() >= end {
905 res.next = Some(header);
906 break;
907 }
908 res.window.push(header);
909 if res.window.len() >= limit {
910 break;
911 }
912 }
913
914 Ok(res)
915 }
916
917 async fn latest_qc_chain(&mut self) -> QueryResult<Option<[CertificatePair<Types>; 2]>> {
918 Ok(self.inner.latest_qc_chain.clone())
919 }
920
921 async fn load_cert2(&mut self, height: u64) -> QueryResult<Option<Certificate2<Types>>> {
922 Ok(self
923 .inner
924 .cert2_storage
925 .iter()
926 .nth(height as usize)
927 .flatten())
928 }
929
930 async fn load_earliest_cert2(
931 &mut self,
932 height: u64,
933 ) -> QueryResult<Option<Certificate2<Types>>> {
934 Ok(self
935 .inner
936 .cert2_storage
937 .iter()
938 .skip(height as usize)
939 .flatten()
940 .next())
941 }
942}
943
944impl<Types, T: Revert + Send> AggregatesStorage<Types> for Transaction<T>
945where
946 Types: NodeType,
947 Header<Types>: QueryableHeader<Types>,
948{
949 async fn aggregates_height(&mut self) -> anyhow::Result<usize> {
950 Ok(0)
951 }
952
953 async fn load_prev_aggregate(&mut self) -> anyhow::Result<Option<Aggregate<Types>>> {
954 Ok(None)
955 }
956}
957
958impl<Types, T: Revert + Send> UpdateAggregatesStorage<Types> for Transaction<T>
959where
960 Types: NodeType,
961 Header<Types>: QueryableHeader<Types>,
962{
963 async fn update_aggregates(
964 &mut self,
965 _prev: Aggregate<Types>,
966 _blocks: &[PayloadMetadata<Types>],
967 ) -> anyhow::Result<Aggregate<Types>> {
968 Ok(Aggregate::default())
969 }
970}
971
972impl<T: Revert> PrunedHeightStorage for Transaction<T> {}
973
974impl<Types> HasMetrics for FileSystemStorage<Types>
975where
976 Types: NodeType,
977 Header<Types>: QueryableHeader<Types>,
978 Payload<Types>: QueryablePayload<Types>,
979{
980 fn metrics(&self) -> &PrometheusMetrics {
981 &self.metrics
982 }
983}