mod blockstore_with_read_cache;
mod blockstore_with_write_buffer;
pub mod car;
mod db_impl;
mod db_mode;
mod either;
pub mod gc;
mod memory;
pub mod migration;
pub mod parity_db;
pub mod parity_db_config;
pub mod ttl;
pub use blockstore_with_read_cache::*;
pub use blockstore_with_write_buffer::BlockstoreWithWriteBuffer;
pub use db_impl::DbImpl;
pub use either::Either;
pub use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore};
pub use memory::{IndexMapBlockstore, MemoryDB};
use crate::blocks::{Tipset, TipsetKey};
use crate::prelude::*;
use crate::rpc::eth::types::EthHash;
use crate::shim::clock::ChainEpoch;
use ambassador::delegatable_trait;
use serde::Serialize;
use serde::de::DeserializeOwned;
pub const CAR_DB_DIR_NAME: &str = "car_db";
pub mod setting_keys {
pub const HEAD_KEY: &str = "head";
pub const MPOOL_CONFIG_KEY: &str = "/mpool/config";
}
#[auto_impl::auto_impl(&, Arc)]
#[delegatable_trait]
pub trait SettingsStore {
fn read_bin(&self, key: &str) -> anyhow::Result<Option<Vec<u8>>>;
fn write_bin(&self, key: &str, value: &[u8]) -> anyhow::Result<()>;
fn exists(&self, key: &str) -> anyhow::Result<bool>;
#[allow(dead_code)]
fn setting_keys(&self) -> anyhow::Result<Vec<String>>;
}
pub trait SettingsStoreExt {
fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>>;
fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()>;
#[allow(dead_code)]
fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V>;
}
impl<T: ?Sized + SettingsStore> SettingsStoreExt for T {
fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>> {
match self.read_bin(key)? {
Some(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
None => Ok(None),
}
}
fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()> {
self.write_bin(key, &serde_json::to_vec(value)?)
}
fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V> {
self.read_bin(key)?
.with_context(|| format!("Key {key} not found"))
.and_then(|bytes| serde_json::from_slice(&bytes).map_err(Into::into))
}
}
#[auto_impl::auto_impl(&, Arc)]
#[delegatable_trait]
pub trait EthMappingsStore {
fn read_bin(&self, key: &EthHash) -> anyhow::Result<Option<Vec<u8>>>;
fn write_bin(&self, key: &EthHash, value: &[u8]) -> anyhow::Result<()>;
#[allow(dead_code)]
fn exists(&self, key: &EthHash) -> anyhow::Result<bool>;
fn get_message_cids(&self) -> anyhow::Result<Vec<(Cid, u64)>>;
fn delete(&self, keys: Vec<EthHash>) -> anyhow::Result<()>;
fn tipset_key_by_epoch(&self, epoch: ChainEpoch) -> anyhow::Result<Option<TipsetKey>>;
fn delete_tipset_key_at_epoch(&self, epoch: ChainEpoch) -> anyhow::Result<()>;
fn set_tipset_key_at_epoch_raw(&self, epoch: ChainEpoch, tsk: &TipsetKey)
-> anyhow::Result<()>;
fn set_tipset_key_at_epoch(&self, ts: &Tipset) -> anyhow::Result<()> {
EthMappingsStore::set_tipset_key_at_epoch_raw(self, ts.epoch(), ts.key())
}
}
pub trait EthMappingsStoreExt {
fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>>;
fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()>;
}
impl<T: ?Sized + EthMappingsStore> EthMappingsStoreExt for T {
fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>> {
match self.read_bin(key)? {
Some(bytes) => Ok(Some(fvm_ipld_encoding::from_slice(&bytes)?)),
None => Ok(None),
}
}
fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()> {
self.write_bin(key, &fvm_ipld_encoding::to_vec(value)?)
}
}
pub(crate) const BLOCK_BLOOM_LEN: usize = 256;
#[auto_impl::auto_impl(&, Arc)]
#[delegatable_trait]
pub trait EthBlockBloomStore {
fn read_bloom(&self, key: &Cid) -> anyhow::Result<Option<[u8; BLOCK_BLOOM_LEN]>>;
fn write_bloom(
&self,
key: &Cid,
height: ChainEpoch,
bloom: &[u8; BLOCK_BLOOM_LEN],
) -> anyhow::Result<()>;
fn delete_blooms_before_height(&self, height: ChainEpoch) -> anyhow::Result<()>;
}
pub(crate) fn encode_block_bloom(height: ChainEpoch, bloom: &[u8; BLOCK_BLOOM_LEN]) -> Vec<u8> {
let mut entry = Vec::with_capacity(size_of::<i64>() + BLOCK_BLOOM_LEN);
entry.extend_from_slice(&height.to_le_bytes());
entry.extend_from_slice(bloom);
entry
}
pub(crate) fn decode_block_bloom(entry: &[u8]) -> Option<(ChainEpoch, &[u8; BLOCK_BLOOM_LEN])> {
if entry.len() != size_of::<i64>() + BLOCK_BLOOM_LEN {
return None;
}
let (height, bloom) = entry.split_at(size_of::<i64>());
Some((
i64::from_le_bytes(height.try_into().ok()?),
bloom.try_into().ok()?,
))
}
pub trait DBStatistics {
fn get_statistics(&self) -> Option<String> {
None
}
}
impl<DB: DBStatistics> DBStatistics for std::sync::Arc<DB> {
fn get_statistics(&self) -> Option<String> {
self.as_ref().get_statistics()
}
}
#[auto_impl::auto_impl(&, Arc)]
pub trait PersistentStore: Blockstore {
fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()>;
}
impl PersistentStore for MemoryBlockstore {
fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> {
self.put_keyed(k, block)
}
}
#[auto_impl::auto_impl(&, Arc)]
#[delegatable_trait]
pub trait HeaviestTipsetKeyProvider {
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>>;
fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()>;
}
#[auto_impl::auto_impl(&, Arc)]
pub trait BlockstoreWriteOpsSubscribable {
fn subscribe_write_ops(
&self,
) -> anyhow::Result<tokio::sync::broadcast::Receiver<Vec<(Cid, bytes::Bytes)>>>;
fn unsubscribe_write_ops(&self);
}
pub mod db_engine {
use std::path::{Path, PathBuf};
use super::db_mode::choose_db;
pub type Db = crate::db::parity_db::ParityDb;
pub type DbConfig = crate::db::parity_db_config::ParityDbConfig;
pub fn db_root(chain_data_root: &Path) -> anyhow::Result<PathBuf> {
choose_db(chain_data_root)
}
pub fn open_db(path: PathBuf, config: &DbConfig) -> anyhow::Result<Db> {
Db::open(path, config)
}
}
#[cfg(test)]
mod tests {
pub mod db_utils;
mod mem_test;
mod parity_test;
pub mod subtests;
}