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