Skip to main content

forest/libp2p_bitswap/
store.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use super::*;
5use crate::utils::multihash::MultihashCode;
6use ambassador::delegatable_trait;
7use multihash_derive::MultihashDigest;
8use std::marker::PhantomData;
9
10/// Trait implemented by a block store for reading.
11#[auto_impl::auto_impl(&, Arc)]
12#[delegatable_trait]
13pub trait BitswapStoreRead {
14    /// A have query needs to know if the block store contains the block.
15    fn contains(&self, cid: &Cid) -> anyhow::Result<bool>;
16
17    /// A block query needs to retrieve the block from the store.
18    fn get(&self, cid: &Cid) -> anyhow::Result<Option<Vec<u8>>>;
19}
20
21/// Trait implemented by a block store for reading and writing.
22#[auto_impl::auto_impl(&, Arc)]
23#[delegatable_trait]
24pub trait BitswapStoreReadWrite: BitswapStoreRead + Send + Sync + 'static {
25    /// The hashes parameters.
26    type Hashes: MultihashDigest<64>;
27
28    /// A block response needs to insert the block into the store.
29    fn insert(&self, block: &Block64<Self::Hashes>) -> anyhow::Result<()>;
30}
31
32pub type Block64<H> = Block<H, 64>;
33
34/// Block
35#[derive(Clone, Debug)]
36pub struct Block<H, const S: usize> {
37    /// Content identifier.
38    cid: Cid,
39    /// Binary data.
40    data: Vec<u8>,
41    _pd: PhantomData<H>,
42}
43
44impl<H, const S: usize> PartialEq for Block<H, S> {
45    fn eq(&self, other: &Self) -> bool {
46        self.cid == other.cid
47    }
48}
49
50impl<H, const S: usize> Eq for Block<H, S> {}
51
52impl<H: MultihashDigest<S>, const S: usize> Block<H, S> {
53    /// Creates a new block. Returns an error if the hash doesn't match
54    /// the data.
55    pub fn new(cid: Cid, data: Vec<u8>) -> anyhow::Result<Self> {
56        Self::verify_cid(&cid, &data)?;
57        Ok(Self {
58            cid,
59            data,
60            _pd: Default::default(),
61        })
62    }
63
64    /// Returns the [`Cid`].
65    pub fn cid(&self) -> &Cid {
66        &self.cid
67    }
68
69    /// Returns the payload.
70    pub fn data(&self) -> &[u8] {
71        &self.data
72    }
73
74    fn verify_cid(cid: &Cid, payload: &[u8]) -> anyhow::Result<()> {
75        let code = cid.hash().code();
76        // Identity multihash can't hold a payload larger than its buffer.
77        anyhow::ensure!(
78            code != u64::from(MultihashCode::Identity) || payload.len() <= S,
79            "identity multihash payload of {} bytes exceeds the {S}-byte limit",
80            payload.len()
81        );
82        let mh = H::try_from(code)
83            .map_err(|_| anyhow::anyhow!("unsupported multihash code {code}"))?
84            .digest(payload);
85        if mh.digest() != cid.hash().digest() {
86            anyhow::bail!("invalid multihash digest");
87        }
88        Ok(())
89    }
90}