Skip to main content

forest/db/
mod.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4mod blockstore_with_read_cache;
5mod blockstore_with_write_buffer;
6pub mod car;
7mod db_impl;
8mod db_mode;
9mod either;
10pub mod gc;
11mod memory;
12pub mod migration;
13pub mod parity_db;
14pub mod parity_db_config;
15pub mod ttl;
16
17pub use blockstore_with_read_cache::*;
18pub use blockstore_with_write_buffer::BlockstoreWithWriteBuffer;
19pub use db_impl::DbImpl;
20pub use either::Either;
21pub use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore};
22pub use memory::{IndexMapBlockstore, MemoryDB};
23
24use crate::blocks::{Tipset, TipsetKey};
25use crate::prelude::*;
26use crate::rpc::eth::types::EthHash;
27use crate::shim::clock::ChainEpoch;
28use ambassador::delegatable_trait;
29use serde::Serialize;
30use serde::de::DeserializeOwned;
31
32pub const CAR_DB_DIR_NAME: &str = "car_db";
33
34pub mod setting_keys {
35    /// Key used to store the heaviest tipset in the settings store. This is expected to be a [`crate::blocks::TipsetKey`]s
36    pub const HEAD_KEY: &str = "head";
37    /// Key used to store the memory pool configuration in the settings store.
38    pub const MPOOL_CONFIG_KEY: &str = "/mpool/config";
39}
40
41/// Interface used to store and retrieve settings from the database.
42/// To store IPLD blocks, use the `BlockStore` trait.
43#[auto_impl::auto_impl(&, Arc)]
44#[delegatable_trait]
45pub trait SettingsStore {
46    /// Reads binary field from the Settings store. This should be used for
47    /// non-serializable data. For serializable data, use [`SettingsStoreExt::read_obj`].
48    fn read_bin(&self, key: &str) -> anyhow::Result<Option<Vec<u8>>>;
49
50    /// Writes binary field to the Settings store. This should be used for
51    /// non-serializable data. For serializable data, use [`SettingsStoreExt::write_obj`].
52    fn write_bin(&self, key: &str, value: &[u8]) -> anyhow::Result<()>;
53
54    /// Returns `Ok(true)` if key exists in store.
55    fn exists(&self, key: &str) -> anyhow::Result<bool>;
56
57    /// Returns all setting keys.
58    #[allow(dead_code)]
59    fn setting_keys(&self) -> anyhow::Result<Vec<String>>;
60}
61
62/// Extension trait for the [`SettingsStore`] trait. It is implemented for all types that implement
63/// [`SettingsStore`].
64/// It provides methods for writing and reading any serializable object from the store.
65pub trait SettingsStoreExt {
66    fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>>;
67    fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()>;
68
69    #[allow(dead_code)]
70    /// Same as [`SettingsStoreExt::read_obj`], but returns an error if the key does not exist.
71    fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V>;
72}
73
74impl<T: ?Sized + SettingsStore> SettingsStoreExt for T {
75    fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>> {
76        match self.read_bin(key)? {
77            Some(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
78            None => Ok(None),
79        }
80    }
81
82    fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()> {
83        self.write_bin(key, &serde_json::to_vec(value)?)
84    }
85
86    fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V> {
87        self.read_bin(key)?
88            .with_context(|| format!("Key {key} not found"))
89            .and_then(|bytes| serde_json::from_slice(&bytes).map_err(Into::into))
90    }
91}
92
93/// Interface used to store and retrieve Ethereum mappings from the database.
94/// To store IPLD blocks, use the `BlockStore` trait.
95#[auto_impl::auto_impl(&, Arc)]
96#[delegatable_trait]
97pub trait EthMappingsStore {
98    /// Reads binary field from the `EthMappings` store. This should be used for
99    /// non-serializable data. For serializable data, use [`EthMappingsStoreExt::read_obj`].
100    fn read_bin(&self, key: &EthHash) -> anyhow::Result<Option<Vec<u8>>>;
101
102    /// Writes binary field to the `EthMappings` store. This should be used for
103    /// non-serializable data. For serializable data, use [`EthMappingsStoreExt::write_obj`].
104    fn write_bin(&self, key: &EthHash, value: &[u8]) -> anyhow::Result<()>;
105
106    /// Returns `Ok(true)` if key exists in store.
107    #[allow(dead_code)]
108    fn exists(&self, key: &EthHash) -> anyhow::Result<bool>;
109
110    /// Returns all message CIDs with their timestamp.
111    fn get_message_cids(&self) -> anyhow::Result<Vec<(Cid, u64)>>;
112
113    /// Deletes `keys` if keys exist in store.
114    fn delete(&self, keys: Vec<EthHash>) -> anyhow::Result<()>;
115
116    /// Reads the tipset key for a given epoch(height) from the store.
117    fn tipset_key_by_epoch(&self, epoch: ChainEpoch) -> anyhow::Result<Option<TipsetKey>>;
118
119    /// Deletes the tipset key at a given epoch(height) from the store.
120    fn delete_tipset_key_at_epoch(&self, epoch: ChainEpoch) -> anyhow::Result<()>;
121
122    /// Writes the tipset key for a given epoch(height) to the store.
123    fn set_tipset_key_at_epoch_raw(&self, epoch: ChainEpoch, tsk: &TipsetKey)
124    -> anyhow::Result<()>;
125
126    /// Writes the tipset key for a given epoch(height) to the store.
127    fn set_tipset_key_at_epoch(&self, ts: &Tipset) -> anyhow::Result<()> {
128        EthMappingsStore::set_tipset_key_at_epoch_raw(self, ts.epoch(), ts.key())
129    }
130}
131
132pub trait EthMappingsStoreExt {
133    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>>;
134    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()>;
135}
136
137impl<T: ?Sized + EthMappingsStore> EthMappingsStoreExt for T {
138    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>> {
139        match self.read_bin(key)? {
140            Some(bytes) => Ok(Some(fvm_ipld_encoding::from_slice(&bytes)?)),
141            None => Ok(None),
142        }
143    }
144
145    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()> {
146        self.write_bin(key, &fvm_ipld_encoding::to_vec(value)?)
147    }
148}
149
150/// Length in bytes of a stored Ethereum block logs bloom.
151pub(crate) const BLOCK_BLOOM_LEN: usize = 256;
152
153/// Interface used to store and retrieve per-tipset Ethereum block logs blooms.
154#[auto_impl::auto_impl(&, Arc)]
155#[delegatable_trait]
156pub trait EthBlockBloomStore {
157    /// Reads the logs bloom stored for the given tipset key CID. A missing or malformed entry
158    /// is reported as `None`.
159    fn read_bloom(&self, key: &Cid) -> anyhow::Result<Option<[u8; BLOCK_BLOOM_LEN]>>;
160
161    /// Stores the logs bloom for the given tipset key CID, tagged with its height.
162    fn write_bloom(
163        &self,
164        key: &Cid,
165        height: ChainEpoch,
166        bloom: &[u8; BLOCK_BLOOM_LEN],
167    ) -> anyhow::Result<()>;
168
169    /// Deletes every stored bloom whose height is below `height`.
170    fn delete_blooms_before_height(&self, height: ChainEpoch) -> anyhow::Result<()>;
171}
172
173/// Encodes a block bloom entry as its little-endian height followed by the bloom bytes.
174pub(crate) fn encode_block_bloom(height: ChainEpoch, bloom: &[u8; BLOCK_BLOOM_LEN]) -> Vec<u8> {
175    let mut entry = Vec::with_capacity(size_of::<i64>() + BLOCK_BLOOM_LEN);
176    entry.extend_from_slice(&height.to_le_bytes());
177    entry.extend_from_slice(bloom);
178    entry
179}
180
181/// Splits a block bloom entry into its (height and `BLOCK_BLOOM_LEN`) and raw bloom bytes.
182pub(crate) fn decode_block_bloom(entry: &[u8]) -> Option<(ChainEpoch, &[u8; BLOCK_BLOOM_LEN])> {
183    if entry.len() != size_of::<i64>() + BLOCK_BLOOM_LEN {
184        return None;
185    }
186    let (height, bloom) = entry.split_at(size_of::<i64>());
187    Some((
188        i64::from_le_bytes(height.try_into().ok()?),
189        bloom.try_into().ok()?,
190    ))
191}
192
193/// Traits for collecting DB stats
194pub trait DBStatistics {
195    fn get_statistics(&self) -> Option<String> {
196        None
197    }
198}
199
200impl<DB: DBStatistics> DBStatistics for std::sync::Arc<DB> {
201    fn get_statistics(&self) -> Option<String> {
202        self.as_ref().get_statistics()
203    }
204}
205
206/// A trait that allows for storing data that is not garbage collected.
207#[auto_impl::auto_impl(&, Arc)]
208pub trait PersistentStore: Blockstore {
209    /// Puts a keyed block with pre-computed CID into the database.
210    ///
211    /// # Arguments
212    ///
213    /// * `k` - The key to be stored.
214    /// * `block` - The block to be stored.
215    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()>;
216}
217
218impl PersistentStore for MemoryBlockstore {
219    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> {
220        self.put_keyed(k, block)
221    }
222}
223
224#[auto_impl::auto_impl(&, Arc)]
225#[delegatable_trait]
226pub trait HeaviestTipsetKeyProvider {
227    /// Returns the currently tracked heaviest tipset.
228    fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>>;
229
230    /// Sets heaviest tipset.
231    fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()>;
232}
233
234#[auto_impl::auto_impl(&, Arc)]
235pub trait BlockstoreWriteOpsSubscribable {
236    fn subscribe_write_ops(
237        &self,
238    ) -> anyhow::Result<tokio::sync::broadcast::Receiver<Vec<(Cid, bytes::Bytes)>>>;
239
240    fn unsubscribe_write_ops(&self);
241}
242
243pub mod db_engine {
244    use std::path::{Path, PathBuf};
245
246    use super::db_mode::choose_db;
247
248    pub type Db = crate::db::parity_db::ParityDb;
249    pub type DbConfig = crate::db::parity_db_config::ParityDbConfig;
250
251    /// Returns the path to the database directory to be used by the daemon.
252    pub fn db_root(chain_data_root: &Path) -> anyhow::Result<PathBuf> {
253        choose_db(chain_data_root)
254    }
255
256    pub fn open_db(path: PathBuf, config: &DbConfig) -> anyhow::Result<Db> {
257        Db::open(path, config)
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    pub mod db_utils;
264    mod mem_test;
265    mod parity_test;
266    pub mod subtests;
267}