diff --git a/Cargo.lock b/Cargo.lock index 7d42555e..74b19d66 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -665,9 +665,9 @@ dependencies = [ [[package]] name = "blockstore" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7679095248a6dc7555fae81154ed1baef264383c16621ef881a219576c72a9be" +checksum = "0a8962daed8fb337472d9c4215006443acba1e40c6c91c9d4a3f440d1fb30436" dependencies = [ "cid", "dashmap 6.0.1", diff --git a/Cargo.toml b/Cargo.toml index 0dbc0957..01256887 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["cli", "grpc", "node", "node-wasm", "proto", "rpc", "types"] [workspace.dependencies] -blockstore = "0.7.0" +blockstore = "0.7.1" lumina-node = { version = "0.8.0", path = "node" } lumina-node-wasm = { version = "0.7.0", path = "node-wasm" } celestia-proto = { version = "0.6.0", path = "proto" } diff --git a/node/src/store.rs b/node/src/store.rs index 6b26a638..80ebd8ab 100644 --- a/node/src/store.rs +++ b/node/src/store.rs @@ -15,6 +15,7 @@ use tendermint_proto::Protobuf; use thiserror::Error; pub use crate::block_ranges::{BlockRange, BlockRanges, BlockRangesError}; +pub use crate::store::either_store::EitherStore; pub use crate::store::utils::VerifiedExtendedHeaders; pub use in_memory_store::InMemoryStore; @@ -23,6 +24,7 @@ pub use indexed_db_store::IndexedDbStore; #[cfg(not(target_arch = "wasm32"))] pub use redb_store::RedbStore; +mod either_store; mod in_memory_store; #[cfg(target_arch = "wasm32")] mod indexed_db_store; diff --git a/node/src/store/either_store.rs b/node/src/store/either_store.rs new file mode 100644 index 00000000..81069772 --- /dev/null +++ b/node/src/store/either_store.rs @@ -0,0 +1,243 @@ +use std::fmt::{self, Debug, Display}; +use std::ops::RangeBounds; + +use async_trait::async_trait; +use celestia_types::hash::Hash; +use celestia_types::ExtendedHeader; +use cid::Cid; + +use crate::store::{ + BlockRanges, Result, SamplingMetadata, SamplingStatus, Store, VerifiedExtendedHeaders, +}; + +/// Struct that can be used to build combinations of different [`Store`] types. +/// +/// # Example +/// +/// ```ignore +/// type SuperStore = EitherStore; +/// ``` +pub enum EitherStore +where + L: Store, + R: Store, +{ + /// A value of type `L`. + Left(L), + /// A value of type `R`. + Right(R), +} + +impl EitherStore +where + L: Store, + R: Store, +{ + /// Returns true if value is the `Left` variant. + pub fn is_left(&self) -> bool { + match self { + EitherStore::Left(_) => true, + EitherStore::Right(_) => false, + } + } + + /// Returns true if value is the `Right` variant. + pub fn is_right(&self) -> bool { + match self { + EitherStore::Left(_) => false, + EitherStore::Right(_) => true, + } + } + + /// Returns a reference of the left side of `EitherStore`. + pub fn left(&self) -> Option<&L> { + match self { + EitherStore::Left(store) => Some(store), + EitherStore::Right(_) => None, + } + } + + /// Returns a reference of the right side of `EitherStore`. + pub fn right(&self) -> Option<&R> { + match self { + EitherStore::Left(_) => None, + EitherStore::Right(store) => Some(store), + } + } + + /// Returns a mutable reference of the left side of `EitherStore`. + pub fn left_mut(&mut self) -> Option<&mut L> { + match self { + EitherStore::Left(store) => Some(store), + EitherStore::Right(_) => None, + } + } + + /// Returns a mutable reference of the right side of `EitherStore`. + pub fn right_mut(&mut self) -> Option<&mut R> { + match self { + EitherStore::Left(_) => None, + EitherStore::Right(store) => Some(store), + } + } + + /// Returns the left side of `EitherStore`. + pub fn into_left(self) -> Option { + match self { + EitherStore::Left(store) => Some(store), + EitherStore::Right(_) => None, + } + } + + /// Returns the right side of `EitherStore`. + pub fn into_right(self) -> Option { + match self { + EitherStore::Left(_) => None, + EitherStore::Right(store) => Some(store), + } + } +} + +impl Clone for EitherStore +where + L: Store + Clone, + R: Store + Clone, +{ + fn clone(&self) -> Self { + match self { + EitherStore::Left(store) => EitherStore::Left(store.clone()), + EitherStore::Right(store) => EitherStore::Right(store.clone()), + } + } + + fn clone_from(&mut self, source: &Self) { + match source { + EitherStore::Left(source_store) => match self { + EitherStore::Left(ref mut self_store) => self_store.clone_from(source_store), + EitherStore::Right(_) => *self = EitherStore::Left(source_store.clone()), + }, + EitherStore::Right(source_store) => match self { + EitherStore::Left(_) => *self = EitherStore::Right(source_store.clone()), + EitherStore::Right(ref mut self_store) => self_store.clone_from(source_store), + }, + }; + } +} + +impl Debug for EitherStore +where + L: Store + Debug, + R: Store + Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EitherStore::Left(ref store) => Debug::fmt(store, f), + EitherStore::Right(ref store) => Debug::fmt(store, f), + } + } +} + +impl Display for EitherStore +where + L: Store + Display, + R: Store + Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + EitherStore::Left(ref store) => Display::fmt(store, f), + EitherStore::Right(ref store) => Display::fmt(store, f), + } + } +} + +macro_rules! call { + ($self:ident, $method:ident($($param:expr),*)) => { + match $self { + EitherStore::Left(store) => store.$method($($param),*).await, + EitherStore::Right(store) => store.$method($($param),*).await, + } + }; +} + +#[async_trait] +impl Store for EitherStore +where + L: Store, + R: Store, +{ + async fn get_head(&self) -> Result { + call!(self, get_head()) + } + + async fn get_by_hash(&self, hash: &Hash) -> Result { + call!(self, get_by_hash(hash)) + } + + async fn get_by_height(&self, height: u64) -> Result { + call!(self, get_by_height(height)) + } + + async fn wait_new_head(&self) -> u64 { + call!(self, wait_new_head()) + } + + async fn wait_height(&self, height: u64) -> Result<()> { + call!(self, wait_height(height)) + } + + async fn get_range(&self, range: RB) -> Result> + where + RB: RangeBounds + Send, + { + call!(self, get_range(range)) + } + + async fn head_height(&self) -> Result { + call!(self, head_height()) + } + + async fn has(&self, hash: &Hash) -> bool { + call!(self, has(hash)) + } + + async fn has_at(&self, height: u64) -> bool { + call!(self, has_at(height)) + } + + async fn update_sampling_metadata( + &self, + height: u64, + status: SamplingStatus, + cids: Vec, + ) -> Result<()> { + call!(self, update_sampling_metadata(height, status, cids)) + } + + async fn get_sampling_metadata(&self, height: u64) -> Result> { + call!(self, get_sampling_metadata(height)) + } + + async fn insert(&self, headers: H) -> Result<()> + where + H: TryInto + Send, + >::Error: Display, + { + call!(self, insert(headers)) + } + + async fn get_stored_header_ranges(&self) -> Result { + call!(self, get_stored_header_ranges()) + } + + async fn get_accepted_sampling_ranges(&self) -> Result { + call!(self, get_accepted_sampling_ranges()) + } + + async fn remove_last(&self) -> Result { + call!(self, remove_last()) + } + + async fn close(self) -> Result<()> { + call!(self, close()) + } +}