lumina_node/
blockstore.rs

1//! Blockstore types aliases with lumina specific constants.
2
3use blockstore::{Blockstore, Result};
4use celestia_types::sample::SAMPLE_ID_CODEC;
5use cid::CidGeneric;
6
7use crate::p2p::MAX_MH_SIZE;
8
9/// An [`InMemoryBlockstore`] with maximum multihash size used by lumina.
10///
11/// [`InMemoryBlockstore`]: blockstore::InMemoryBlockstore
12pub type InMemoryBlockstore = blockstore::InMemoryBlockstore<MAX_MH_SIZE>;
13
14#[cfg(not(target_arch = "wasm32"))]
15/// A [`RedbBlockstore`].
16///
17/// [`RedbBlockstore`]: blockstore::RedbBlockstore
18pub type RedbBlockstore = blockstore::RedbBlockstore;
19
20#[cfg(target_arch = "wasm32")]
21/// An [`IndexedDbBlockstore`].
22///
23/// [`IndexedDbBlockstore`]: blockstore::IndexedDbBlockstore
24pub type IndexedDbBlockstore = blockstore::IndexedDbBlockstore;
25
26/// A blockstore which only stores samples and discards other CIDs.
27pub(crate) struct SampleBlockstore<B> {
28    blockstore: B,
29}
30
31impl<B> SampleBlockstore<B> {
32    /// Wrap another blockstore with sample blockstore.
33    pub(crate) fn new(blockstore: B) -> Self {
34        Self { blockstore }
35    }
36}
37
38impl<B> Blockstore for SampleBlockstore<B>
39where
40    B: Blockstore,
41{
42    async fn get<const S: usize>(&self, cid: &CidGeneric<S>) -> Result<Option<Vec<u8>>> {
43        self.blockstore.get(cid).await
44    }
45
46    async fn put_keyed<const S: usize>(&self, cid: &CidGeneric<S>, data: &[u8]) -> Result<()> {
47        if cid.codec() == SAMPLE_ID_CODEC {
48            self.blockstore.put_keyed(cid, data).await
49        } else {
50            Ok(())
51        }
52    }
53
54    async fn remove<const S: usize>(&self, cid: &CidGeneric<S>) -> Result<()> {
55        self.blockstore.remove(cid).await
56    }
57
58    async fn has<const S: usize>(&self, cid: &CidGeneric<S>) -> Result<bool> {
59        self.blockstore.has(cid).await
60    }
61
62    async fn close(self) -> Result<()> {
63        self.blockstore.close().await
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use blockstore::Blockstore;
70    use celestia_types::{
71        nmt::Namespace, row::RowId, row_namespace_data::RowNamespaceDataId, sample::SampleId,
72    };
73    use lumina_utils::test_utils::async_test;
74
75    use super::{InMemoryBlockstore, SampleBlockstore};
76
77    #[async_test]
78    async fn should_only_store_samples() {
79        macro_rules! cid {
80            ($bytes:expr) => {
81                ::cid::CidGeneric::try_from($bytes).unwrap()
82            };
83            ($id:ty, $($args:expr),+ $(,)?) => {
84                $crate::p2p::shwap::convert_cid(
85                    &<$id>::new($($args),+).unwrap().into()
86                )
87                .unwrap()
88            };
89        }
90
91        let blockstore = SampleBlockstore::new(InMemoryBlockstore::new());
92
93        let sample_cids = [
94            cid!(SampleId, 1, 2, 3),
95            cid!(SampleId, 1111, 232, 33),
96            cid!(SampleId, 123, 1, 888888888),
97        ];
98
99        let non_sample_cids = [
100            cid!(RowId, 1, 1737),
101            cid!(RowId, 8812, 193139),
102            cid!(RowNamespaceDataId, Namespace::new_v0(b"a").unwrap(), 15, 12),
103            cid!(RowNamespaceDataId, Namespace::new_v0(b"z").unwrap(), 1, 1),
104            cid!([1; 64].as_ref()),
105            cid!([[1].as_ref(), &[18; 63]].concat()),
106        ];
107
108        for cid in sample_cids.iter().chain(non_sample_cids.iter()) {
109            blockstore.put_keyed(cid, &[10; 150]).await.unwrap();
110        }
111
112        for cid in &sample_cids {
113            assert!(blockstore.has(cid).await.unwrap());
114        }
115        for cid in &non_sample_cids {
116            assert!(!blockstore.has(cid).await.unwrap());
117        }
118    }
119}