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 multihash_derive::MultihashDigest;
6use std::ops::Deref;
7use std::{marker::PhantomData, sync::Arc};
8
9/// Trait implemented by a block store for reading.
10pub trait BitswapStoreRead {
11    /// A have query needs to know if the block store contains the block.
12    fn contains(&self, cid: &Cid) -> anyhow::Result<bool>;
13
14    /// A block query needs to retrieve the block from the store.
15    fn get(&self, cid: &Cid) -> anyhow::Result<Option<Vec<u8>>>;
16}
17
18/// Trait implemented by a block store for reading and writing.
19pub trait BitswapStoreReadWrite: BitswapStoreRead + Send + Sync + 'static {
20    /// The hashes parameters.
21    type Hashes: MultihashDigest<64>;
22
23    /// A block response needs to insert the block into the store.
24    fn insert(&self, block: &Block64<Self::Hashes>) -> anyhow::Result<()>;
25}
26
27impl<T: BitswapStoreRead> BitswapStoreRead for Arc<T> {
28    fn contains(&self, cid: &Cid) -> anyhow::Result<bool> {
29        BitswapStoreRead::contains(self.as_ref(), cid)
30    }
31
32    fn get(&self, cid: &Cid) -> anyhow::Result<Option<Vec<u8>>> {
33        BitswapStoreRead::get(self.as_ref(), cid)
34    }
35}
36
37impl<T: BitswapStoreReadWrite> BitswapStoreReadWrite for Arc<T> {
38    type Hashes = <T as BitswapStoreReadWrite>::Hashes;
39
40    fn insert(&self, block: &Block64<Self::Hashes>) -> anyhow::Result<()> {
41        BitswapStoreReadWrite::insert(self.as_ref(), block)
42    }
43}
44
45pub type Block64<H> = Block<H, 64>;
46
47/// Block
48#[derive(Clone, Debug)]
49pub struct Block<H, const S: usize> {
50    /// Content identifier.
51    cid: Cid,
52    /// Binary data.
53    data: Vec<u8>,
54    _pd: PhantomData<H>,
55}
56
57impl<H, const S: usize> Deref for Block<H, S> {
58    type Target = Cid;
59
60    fn deref(&self) -> &Self::Target {
61        &self.cid
62    }
63}
64
65impl<H, const S: usize> PartialEq for Block<H, S> {
66    fn eq(&self, other: &Self) -> bool {
67        self.cid == other.cid
68    }
69}
70
71impl<H, const S: usize> Eq for Block<H, S> {}
72
73impl<H: MultihashDigest<S>, const S: usize> Block<H, S> {
74    /// Creates a new block. Returns an error if the hash doesn't match
75    /// the data.
76    pub fn new(cid: Cid, data: Vec<u8>) -> anyhow::Result<Self> {
77        Self::verify_cid(&cid, &data)?;
78        Ok(Self {
79            cid,
80            data,
81            _pd: Default::default(),
82        })
83    }
84
85    /// Returns the [`Cid`].
86    pub fn cid(&self) -> &Cid {
87        &self.cid
88    }
89
90    /// Returns the payload.
91    pub fn data(&self) -> &[u8] {
92        &self.data
93    }
94
95    fn verify_cid(cid: &Cid, payload: &[u8]) -> anyhow::Result<()> {
96        let code = cid.hash().code();
97        let mh = H::try_from(code)
98            .map_err(|_| anyhow::anyhow!("unsupported multihash code {code}"))?
99            .digest(payload);
100        if mh.digest() != cid.hash().digest() {
101            anyhow::bail!("invalid multihash digest");
102        }
103        Ok(())
104    }
105}