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::{Tipset, 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    /// Reads the tipset key for a given epoch(height) from the store.
112    fn tipset_key_by_epoch(&self, epoch: i64) -> anyhow::Result<Option<TipsetKey>>;
113
114    /// Writes the tipset key for a given epoch(height) to the store.
115    fn set_tipset_key_at_epoch(&self, ts: &Tipset) -> anyhow::Result<()>;
116}
117
118pub struct DummyStore {}
119
120const INDEXER_ERROR: &str =
121    "indexer disabled, enable with chain_indexer.enable_indexer / FOREST_CHAIN_INDEXER_ENABLED";
122
123impl EthMappingsStore for DummyStore {
124    fn read_bin(&self, _key: &EthHash) -> anyhow::Result<Option<Vec<u8>>> {
125        bail!(INDEXER_ERROR)
126    }
127
128    fn write_bin(&self, _key: &EthHash, _value: &[u8]) -> anyhow::Result<()> {
129        bail!(INDEXER_ERROR)
130    }
131
132    fn exists(&self, _key: &EthHash) -> anyhow::Result<bool> {
133        bail!(INDEXER_ERROR)
134    }
135
136    fn get_message_cids(&self) -> anyhow::Result<Vec<(Cid, u64)>> {
137        bail!(INDEXER_ERROR)
138    }
139
140    fn delete(&self, _keys: Vec<EthHash>) -> anyhow::Result<()> {
141        bail!(INDEXER_ERROR)
142    }
143
144    fn tipset_key_by_epoch(&self, _epoch: i64) -> anyhow::Result<Option<TipsetKey>> {
145        bail!(INDEXER_ERROR)
146    }
147
148    fn set_tipset_key_at_epoch(&self, _ts: &Tipset) -> anyhow::Result<()> {
149        bail!(INDEXER_ERROR)
150    }
151}
152
153pub trait EthMappingsStoreExt {
154    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>>;
155    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()>;
156}
157
158impl<T: ?Sized + EthMappingsStore> EthMappingsStoreExt for T {
159    fn read_obj<V: DeserializeOwned>(&self, key: &EthHash) -> anyhow::Result<Option<V>> {
160        match self.read_bin(key)? {
161            Some(bytes) => Ok(Some(fvm_ipld_encoding::from_slice(&bytes)?)),
162            None => Ok(None),
163        }
164    }
165
166    fn write_obj<V: Serialize>(&self, key: &EthHash, value: &V) -> anyhow::Result<()> {
167        self.write_bin(key, &fvm_ipld_encoding::to_vec(value)?)
168    }
169}
170
171/// Traits for collecting DB stats
172pub trait DBStatistics {
173    fn get_statistics(&self) -> Option<String> {
174        None
175    }
176}
177
178impl<DB: DBStatistics> DBStatistics for std::sync::Arc<DB> {
179    fn get_statistics(&self) -> Option<String> {
180        self.as_ref().get_statistics()
181    }
182}
183
184/// A trait that allows for storing data that is not garbage collected.
185#[auto_impl::auto_impl(&, Arc)]
186pub trait PersistentStore: Blockstore {
187    /// Puts a keyed block with pre-computed CID into the database.
188    ///
189    /// # Arguments
190    ///
191    /// * `k` - The key to be stored.
192    /// * `block` - The block to be stored.
193    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()>;
194}
195
196impl PersistentStore for MemoryBlockstore {
197    fn put_keyed_persistent(&self, k: &Cid, block: &[u8]) -> anyhow::Result<()> {
198        self.put_keyed(k, block)
199    }
200}
201
202#[auto_impl::auto_impl(&, Arc)]
203pub trait HeaviestTipsetKeyProvider {
204    /// Returns the currently tracked heaviest tipset.
205    fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>>;
206
207    /// Sets heaviest tipset.
208    fn set_heaviest_tipset_key(&self, tsk: &TipsetKey) -> anyhow::Result<()>;
209}
210
211#[auto_impl::auto_impl(&, Arc)]
212pub trait BlockstoreWriteOpsSubscribable {
213    fn subscribe_write_ops(&self) -> tokio::sync::broadcast::Receiver<Vec<(Cid, bytes::Bytes)>>;
214
215    fn unsubscribe_write_ops(&self);
216}
217
218pub mod db_engine {
219    use std::path::{Path, PathBuf};
220
221    use super::db_mode::choose_db;
222
223    pub type Db = crate::db::parity_db::ParityDb;
224    pub type DbConfig = crate::db::parity_db_config::ParityDbConfig;
225
226    /// Returns the path to the database directory to be used by the daemon.
227    pub fn db_root(chain_data_root: &Path) -> anyhow::Result<PathBuf> {
228        choose_db(chain_data_root)
229    }
230
231    pub fn open_db(path: PathBuf, config: &DbConfig) -> anyhow::Result<Db> {
232        Db::open(path, config)
233    }
234}
235
236#[cfg(test)]
237mod tests {
238    pub mod db_utils;
239    mod mem_test;
240    mod parity_test;
241    pub mod subtests;
242}