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 memory;
8pub mod parity_db;
9pub mod parity_db_config;
10
11pub mod gc;
12pub mod ttl;
13pub use blockstore_with_read_cache::*;
14pub use blockstore_with_write_buffer::BlockstoreWithWriteBuffer;
15pub use memory::MemoryDB;
16mod db_mode;
17mod either;
18pub mod migration;
19pub use either::Either;
20
21use crate::blocks::TipsetKey;
22use crate::rpc::eth::types::EthHash;
23use anyhow::{Context as _, bail};
24use cid::Cid;
25pub use fvm_ipld_blockstore::{Blockstore, MemoryBlockstore};
26use serde::Serialize;
27use serde::de::DeserializeOwned;
28
29pub const CAR_DB_DIR_NAME: &str = "car_db";
30
31pub mod setting_keys {
32    /// Key used to store the heaviest tipset in the settings store. This is expected to be a [`crate::blocks::TipsetKey`]s
33    pub const HEAD_KEY: &str = "head";
34    /// Key used to store the memory pool configuration in the settings store.
35    pub const MPOOL_CONFIG_KEY: &str = "/mpool/config";
36}
37
38/// Interface used to store and retrieve settings from the database.
39/// To store IPLD blocks, use the `BlockStore` trait.
40#[auto_impl::auto_impl(&, Arc)]
41pub trait SettingsStore {
42    /// Reads binary field from the Settings store. This should be used for
43    /// non-serializable data. For serializable data, use [`SettingsStoreExt::read_obj`].
44    fn read_bin(&self, key: &str) -> anyhow::Result<Option<Vec<u8>>>;
45
46    /// Writes binary field to the Settings store. This should be used for
47    /// non-serializable data. For serializable data, use [`SettingsStoreExt::write_obj`].
48    fn write_bin(&self, key: &str, value: &[u8]) -> anyhow::Result<()>;
49
50    /// Returns `Ok(true)` if key exists in store.
51    fn exists(&self, key: &str) -> anyhow::Result<bool>;
52
53    /// Returns all setting keys.
54    #[allow(dead_code)]
55    fn setting_keys(&self) -> anyhow::Result<Vec<String>>;
56}
57
58/// Extension trait for the [`SettingsStore`] trait. It is implemented for all types that implement
59/// [`SettingsStore`].
60/// It provides methods for writing and reading any serializable object from the store.
61pub trait SettingsStoreExt {
62    fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>>;
63    fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()>;
64
65    #[allow(dead_code)]
66    /// Same as [`SettingsStoreExt::read_obj`], but returns an error if the key does not exist.
67    fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V>;
68}
69
70impl<T: ?Sized + SettingsStore> SettingsStoreExt for T {
71    fn read_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<Option<V>> {
72        match self.read_bin(key)? {
73            Some(bytes) => Ok(Some(serde_json::from_slice(&bytes)?)),
74            None => Ok(None),
75        }
76    }
77
78    fn write_obj<V: Serialize>(&self, key: &str, value: &V) -> anyhow::Result<()> {
79        self.write_bin(key, &serde_json::to_vec(value)?)
80    }
81
82    fn require_obj<V: DeserializeOwned>(&self, key: &str) -> anyhow::Result<V> {
83        self.read_bin(key)?
84            .with_context(|| format!("Key {key} not found"))
85            .and_then(|bytes| serde_json::from_slice(&bytes).map_err(Into::into))
86    }
87}
88
89/// Interface used to store and retrieve Ethereum mappings from the database.
90/// To store IPLD blocks, use the `BlockStore` trait.
91#[auto_impl::auto_impl(&, Arc)]
92pub trait EthMappingsStore {
93    /// Reads binary field from the `EthMappings` store. This should be used for
94    /// non-serializable data. For serializable data, use [`EthMappingsStoreExt::read_obj`].
95    fn read_bin(&self, key: &EthHash) -> anyhow::Result<Option<Vec<u8>>>;
96
97    /// Writes binary field to the `EthMappings` store. This should be used for
98    /// non-serializable data. For serializable data, use [`EthMappingsStoreExt::write_obj`].
99    fn write_bin(&self, key: &EthHash, value: &[u8]) -> anyhow::Result<()>;
100
101    /// Returns `Ok(true)` if key exists in store.
102    #[allow(dead_code)]
103    fn exists(&self, key: &EthHash) -> anyhow::Result<bool>;
104
105    /// Returns all message CIDs with their timestamp.
106    fn get_message_cids(&self) -> anyhow::Result<Vec<(Cid, u64)>>;
107
108    /// Deletes `keys` if keys exist in store.
109    fn delete(&self, keys: Vec<EthHash>) -> anyhow::Result<()>;
110}
111
112pub struct DummyStore {}
113
114const INDEXER_ERROR: &str =
115    "indexer disabled, enable with chain_indexer.enable_indexer / FOREST_CHAIN_INDEXER_ENABLED";
116
117impl EthMappingsStore for DummyStore {
118    fn read_bin(&self, _key: &EthHash) -> anyhow::Result<Option<Vec<u8>>> {
119        bail!(INDEXER_ERROR)
120    }
121
122    fn write_bin(&self, _key: &EthHash, _value: &[u8]) -> anyhow::Result<()> {
123        bail!(INDEXER_ERROR)
124    }
125
126    fn exists(&self, _key: &EthHash) -> anyhow::Result<bool> {
127        bail!(INDEXER_ERROR)
128    }
129
130    fn get_message_cids(&self) -> anyhow::Result<Vec<(Cid, u64)>> {
131        bail!(INDEXER_ERROR)
132    }
133
134    fn delete(&self, _keys: Vec<EthHash>) -> anyhow::Result<()> {
135        bail!(INDEXER_ERROR)
136    }
137}
138
139pub trait EthMappingsStoreExt {
140    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>>;
141    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()>;
142}
143
144impl<T: ?Sized + EthMappingsStore> EthMappingsStoreExt for T {
145    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>> {
146        match self.read_bin(key)? {
147            Some(bytes) => Ok(Some(fvm_ipld_encoding::from_slice(&bytes)?)),
148            None => Ok(None),
149        }
150    }
151
152    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()> {
153        self.write_bin(key, &fvm_ipld_encoding::to_vec(value)?)
154    }
155}
156
157/// Traits for collecting DB stats
158pub trait DBStatistics {
159    fn get_statistics(&self) -> Option<String> {
160        None
161    }
162}
163
164impl<DB: DBStatistics> DBStatistics for std::sync::Arc<DB> {
165    fn get_statistics(&self) -> Option<String> {
166        self.as_ref().get_statistics()
167    }
168}
169
170/// A trait that allows for storing data that is not garbage collected.
171#[auto_impl::auto_impl(&, Arc)]
172pub trait PersistentStore: Blockstore {
173    /// Puts a keyed block with pre-computed CID into the database.
174    ///
175    /// # Arguments
176    ///
177    /// * `k` - The key to be stored.
178    /// * `block` - The block to be stored.
179    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()>;
180}
181
182impl PersistentStore for MemoryBlockstore {
183    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> {
184        self.put_keyed(k, block)
185    }
186}
187
188#[auto_impl::auto_impl(&, Arc)]
189pub trait HeaviestTipsetKeyProvider {
190    /// Returns the currently tracked heaviest tipset.
191    fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>>;
192
193    /// Sets heaviest tipset.
194    fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()>;
195}
196
197#[auto_impl::auto_impl(&, Arc)]
198pub trait BlockstoreWriteOpsSubscribable {
199    fn subscribe_write_ops(&self) -> tokio::sync::broadcast::Receiver<Vec<(Cid, bytes::Bytes)>>;
200
201    fn unsubscribe_write_ops(&self);
202}
203
204pub mod db_engine {
205    use std::path::{Path, PathBuf};
206
207    use super::db_mode::choose_db;
208
209    pub type Db = crate::db::parity_db::ParityDb;
210    pub type DbConfig = crate::db::parity_db_config::ParityDbConfig;
211
212    /// Returns the path to the database directory to be used by the daemon.
213    pub fn db_root(chain_data_root: &Path) -> anyhow::Result<PathBuf> {
214        choose_db(chain_data_root)
215    }
216
217    pub fn open_db(path: PathBuf, config: &DbConfig) -> anyhow::Result<Db> {
218        Db::open(path, config)
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    pub mod db_utils;
225    mod mem_test;
226    mod parity_test;
227    pub mod subtests;
228}