co_primitives/types/
block.rs1use crate::{MultiCodec, StoreParams};
5use cid::Cid;
6use multihash_codetable::{Code, MultihashDigest};
7use std::hash::{Hash, Hasher};
8
9#[derive(Clone)]
11pub struct Block {
12 cid: Cid,
13 data: Vec<u8>,
14}
15impl Block {
16 pub fn new(cid: Cid, data: Vec<u8>) -> Result<Self, BlockError> {
19 verify_cid::<multihash_codetable::Code, 64>(&cid, &data)?;
20 Ok(Self::new_unchecked(cid, data))
21 }
22
23 pub fn new_unchecked(cid: Cid, data: Vec<u8>) -> Self {
25 Self { cid, data }
26 }
27
28 pub fn new_data(codec: impl Into<u64>, data: Vec<u8>) -> Self {
31 Self::new_unchecked(Self::cid_data(codec, &data), data)
32 }
33
34 pub fn new_data_digest(digest: impl MultihashDigest<64>, codec: impl Into<u64>, data: Vec<u8>) -> Self {
36 Self::new_unchecked(Self::cid_data_digest(digest, codec, &data), data)
37 }
38
39 pub fn cid_data(codec: impl Into<u64>, data: &[u8]) -> Cid {
40 Self::cid_data_digest(Code::Blake3_256, codec, data)
41 }
42
43 pub fn cid_data_digest(digest: impl MultihashDigest<64>, codec: impl Into<u64>, data: &[u8]) -> Cid {
44 Cid::new_v1(codec.into(), digest.digest(data))
45 }
46
47 pub fn cid(&self) -> &Cid {
49 &self.cid
50 }
51
52 pub fn data(&self) -> &[u8] {
54 &self.data
55 }
56
57 pub fn into_inner(self) -> (Cid, Vec<u8>) {
59 (self.cid, self.data)
60 }
61
62 pub fn with_verify(self) -> Result<Block, BlockError> {
64 verify_cid::<multihash_codetable::Code, 64>(&self.cid, &self.data)?;
65 Ok(self)
66 }
67
68 pub fn with_store_params<P: StoreParams>(self) -> Result<Block, BlockError> {
70 self.with_block_max_size(P::MAX_BLOCK_SIZE)
71 }
72
73 pub fn with_block_max_size(self, max_block_size: usize) -> Result<Block, BlockError> {
75 if self.data.len() > max_block_size {
76 return Err(BlockError::SizeOverflow(self.data.len(), max_block_size));
77 }
78 Ok(self)
79 }
80}
81impl PartialEq for Block {
82 fn eq(&self, other: &Self) -> bool {
83 self.cid == other.cid
84 }
85}
86impl Eq for Block {}
87impl PartialOrd for Block {
88 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
89 Some(self.cmp(other))
90 }
91}
92impl Ord for Block {
93 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
94 self.cid.cmp(&other.cid)
95 }
96}
97impl Hash for Block {
98 fn hash<H: Hasher>(&self, state: &mut H) {
99 Hash::hash(&self, state);
100 }
101}
102impl std::fmt::Debug for Block {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 let hex = self.data.iter().map(|c| format!("{:02X}", c)).collect::<String>();
105 let codec = MultiCodec::from(&self.cid);
106 f.debug_struct("Block")
107 .field("cid", &self.cid)
108 .field("codec", &codec)
109 .field("size", &self.data.len())
110 .field("data", &hex)
111 .finish()
112 }
113}
114
115#[derive(Debug, thiserror::Error)]
116pub enum BlockError {
117 #[error("Unsupported codec {0:?}.")]
118 UnsupportedCodec(u64),
119
120 #[error("Unsupported multihash {0:?}.")]
121 UnsupportedMultihash(u64),
122
123 #[error("Hash of data does not match the CID.")]
124 InvalidMultihash(Vec<u8>, Cid),
125 #[error("Max block size overflow ({0} > {1})")]
128 SizeOverflow(usize, usize),
129}
130
131fn verify_cid<M: MultihashDigest<S>, const S: usize>(cid: &Cid, payload: &[u8]) -> Result<(), BlockError> {
132 let mh = M::try_from(cid.hash().code())
133 .map_err(|_| BlockError::UnsupportedMultihash(cid.hash().code()))?
134 .digest(payload);
135 if mh.digest() != cid.hash().digest() {
136 return Err(BlockError::InvalidMultihash(mh.to_bytes(), *cid));
137 }
138 Ok(())
139}