forest/utils/db/
mod.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4pub mod car_stream;
5pub mod car_util;
6
7use crate::utils::multihash::prelude::*;
8use anyhow::Context as _;
9use cid::Cid;
10use fvm_ipld_blockstore::Blockstore;
11use fvm_ipld_encoding::CborStore;
12use fvm_ipld_encoding::{DAG_CBOR, to_vec};
13#[allow(clippy::disallowed_types)]
14use multihash_codetable::Code;
15
16use serde::ser::Serialize;
17
18/// Extension methods for inserting and retrieving IPLD data with CIDs
19pub trait BlockstoreExt: Blockstore {
20    /// Batch put CBOR objects into block store and returns vector of CIDs
21    #[allow(clippy::disallowed_types)]
22    fn bulk_put<'a, S, V>(&self, values: V, code: Code) -> anyhow::Result<Vec<Cid>>
23    where
24        Self: Sized,
25        S: Serialize + 'a,
26        V: IntoIterator<Item = &'a S>,
27    {
28        let keyed_objects = values
29            .into_iter()
30            .map(|value| {
31                let bytes = to_vec(value)?;
32                let cid = Cid::new_v1(DAG_CBOR, code.digest(&bytes));
33                Ok((cid, bytes))
34            })
35            .collect::<anyhow::Result<Vec<_>>>()?;
36
37        let cids = keyed_objects
38            .iter()
39            .map(|(cid, _)| cid.to_owned())
40            .collect();
41
42        self.put_many_keyed(keyed_objects)?;
43
44        Ok(cids)
45    }
46
47    /// Gets the block from the blockstore. Return an error when not found.
48    fn get_required(&self, cid: &Cid) -> anyhow::Result<Vec<u8>> {
49        self.get(cid)?
50            .with_context(|| format!("Entry not found in block store: cid={cid}"))
51    }
52}
53
54impl<T: fvm_ipld_blockstore::Blockstore> BlockstoreExt for T {}
55
56/// Extension methods for [`CborStore`] that omits default multihash code from its APIs
57pub trait CborStoreExt: CborStore {
58    /// Default multihash code is [`cid::multihash::Code::Blake2b256`]
59    /// See <https://github.com/ipfs/go-ipld-cbor/blob/v0.0.6/store.go#L92>
60    /// ```go
61    /// mhType := uint64(mh.BLAKE2B_MIN + 31)
62    /// // 45569 + 31 = 45600 = 0xb220
63    /// ```
64    #[allow(clippy::disallowed_types)]
65    fn default_code() -> Code {
66        Code::Blake2b256
67    }
68
69    /// A wrapper of [`CborStore::put_cbor`] that omits code parameter to match store API in go
70    fn put_cbor_default<S: serde::ser::Serialize>(&self, obj: &S) -> anyhow::Result<Cid> {
71        self.put_cbor(obj, Self::default_code())
72    }
73
74    /// Get typed object from block store by `CID`. Return an error when not found.
75    fn get_cbor_required<T>(&self, cid: &Cid) -> anyhow::Result<T>
76    where
77        T: serde::de::DeserializeOwned,
78    {
79        self.get_cbor(cid)?.with_context(|| {
80            format!(
81                "Entry not found in cbor store: cid={cid}, type={}",
82                std::any::type_name::<T>()
83            )
84        })
85    }
86}
87
88impl<T: CborStore> CborStoreExt for T {}