exocore_core/framing/
multihash.rs1use std::io;
2
3use bytes::Bytes;
4use multihash::Multihash;
5
6use super::{check_from_size, check_into_size, Error, FrameBuilder, FrameReader};
7use crate::sec::hash::MultihashDigestExt;
8
9pub struct MultihashFrame<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> {
11 inner: I,
12 phantom: std::marker::PhantomData<D>,
13}
14
15impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> MultihashFrame<S, D, I> {
16 pub fn new(inner: I) -> Result<MultihashFrame<S, D, I>, Error> {
17 check_from_size(D::multihash_size(), inner.exposed_data())?;
18 Ok(MultihashFrame {
19 inner,
20 phantom: std::marker::PhantomData,
21 })
22 }
23
24 pub fn verify(&self) -> Result<bool, Error> {
25 let mut data_digest = D::default();
26 data_digest.update(self.exposed_data());
27 let data_multihash = data_digest.to_multihash();
28
29 let frame_multihash_bytes = self.multihash_bytes();
30 let frame_multihash = Multihash::<S>::from_bytes(frame_multihash_bytes)?;
31
32 Ok(data_multihash == frame_multihash)
33 }
34
35 pub fn multihash_bytes(&self) -> &[u8] {
36 let multihash_size = D::multihash_size();
37 let inner_exposed_data = self.inner.exposed_data();
38 &inner_exposed_data[inner_exposed_data.len() - multihash_size..]
39 }
40}
41
42impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader> FrameReader
43 for MultihashFrame<S, D, I>
44{
45 type OwnedType = MultihashFrame<S, D, I::OwnedType>;
46
47 fn exposed_data(&self) -> &[u8] {
48 let multihash_size = D::multihash_size();
49 let inner_exposed_data = self.inner.exposed_data();
50 &inner_exposed_data[..inner_exposed_data.len() - multihash_size]
51 }
52
53 fn whole_data(&self) -> &[u8] {
54 self.inner.whole_data()
55 }
56
57 fn to_owned_frame(&self) -> Self::OwnedType {
58 MultihashFrame {
59 inner: self.inner.to_owned_frame(),
60 phantom: std::marker::PhantomData,
61 }
62 }
63}
64
65impl<const S: usize, D: MultihashDigestExt<S>, I: FrameReader + Clone> Clone
66 for MultihashFrame<S, D, I>
67{
68 fn clone(&self) -> Self {
69 MultihashFrame {
70 inner: self.inner.clone(),
71 phantom: std::marker::PhantomData,
72 }
73 }
74}
75
76pub struct MultihashFrameBuilder<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> {
78 inner: I,
79 phantom: std::marker::PhantomData<D>,
80}
81
82impl<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> MultihashFrameBuilder<S, D, I> {
83 pub fn new(inner: I) -> MultihashFrameBuilder<S, D, I> {
84 MultihashFrameBuilder {
85 inner,
86 phantom: std::marker::PhantomData,
87 }
88 }
89
90 pub fn inner(&mut self) -> &mut I {
91 &mut self.inner
92 }
93}
94
95impl<const S: usize, D: MultihashDigestExt<S>, I: FrameBuilder> FrameBuilder
96 for MultihashFrameBuilder<S, D, I>
97{
98 type OwnedFrameType = MultihashFrame<S, D, Bytes>;
99
100 fn write_to<W: io::Write>(&self, writer: &mut W) -> Result<usize, Error> {
101 let mut buffer = Vec::new();
103 self.inner.write_to(&mut buffer)?;
104 writer.write_all(&buffer)?;
105
106 let mut digest = D::default();
107 digest.update(&buffer);
108 let digest_multihash = digest.to_multihash();
109 digest_multihash.write(writer)?;
110
111 Ok(buffer.len() + D::multihash_size())
112 }
113
114 fn write_into(&self, into: &mut [u8]) -> Result<usize, Error> {
115 let inner_size = self.inner.write_into(into)?;
116
117 let mut digest = D::default();
118 digest.update(&into[..inner_size]);
119 let digest_multihash = digest.to_multihash();
120
121 let total_size = inner_size + D::multihash_size();
122 check_into_size(total_size, into)?;
123
124 digest_multihash.write(&mut into[inner_size..total_size])?;
125
126 Ok(total_size)
127 }
128
129 fn expected_size(&self) -> Option<usize> {
130 self.inner
131 .expected_size()
132 .map(|inner_size| inner_size + D::multihash_size())
133 }
134
135 fn as_owned_frame(&self) -> Self::OwnedFrameType {
136 MultihashFrame::new(self.as_bytes()).expect("Couldn't read just-created frame")
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use bytes::BytesMut;
143
144 use super::*;
145 use crate::{
146 framing::assert_builder_equals,
147 sec::hash::{Sha3_256, Sha3_512},
148 };
149
150 #[test]
151 fn can_build_and_read_multihash() -> anyhow::Result<()> {
152 let inner = Bytes::from_static(b"hello");
153 let builder = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner.clone());
154
155 assert_builder_equals(&builder)?;
156 let frame_bytes = builder.as_bytes();
157
158 let reader1 = MultihashFrame::<32, Sha3_256, _>::new(&frame_bytes[..])?;
159 assert_eq!(frame_bytes, reader1.whole_data());
160 assert_eq!(inner, reader1.exposed_data());
161 assert!(reader1.verify()?);
162
163 let mut modified_buffer = BytesMut::from(frame_bytes.as_ref());
164 modified_buffer[0..5].copy_from_slice(b"world");
165 let reader2 = MultihashFrame::<32, Sha3_256, _>::new(&modified_buffer[..])?;
166 assert!(!reader2.verify()?);
167
168 Ok(())
169 }
170
171 #[test]
172 fn can_build_to_owned() -> anyhow::Result<()> {
173 let inner = Bytes::from_static(b"hello");
174 let builder = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner);
175
176 let frame = builder.as_owned_frame();
177 assert!(frame.verify()?);
178
179 assert_eq!(b"hello", frame.exposed_data());
180
181 Ok(())
182 }
183
184 #[test]
185 fn different_hashes() {
186 let inner = Bytes::from_static(b"hello");
187 let sha3_256 = MultihashFrameBuilder::<32, Sha3_256, _>::new(inner.clone());
188 let sha2_256 = MultihashFrameBuilder::<64, Sha3_512, _>::new(inner);
189
190 assert_ne!(sha3_256.as_bytes(), sha2_256.as_bytes());
191 }
192}