1use datasize::DataSize;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4
5use casper_types::bytesrepr::{self, Bytes, FromBytes, ToBytes};
6
7use crate::{
8 error::{ChunkWithProofVerificationError, MerkleConstructionError},
9 indexed_merkle_proof::IndexedMerkleProof,
10 Digest,
11};
12
13#[derive(DataSize, PartialEq, Eq, Debug, Clone, JsonSchema, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16pub struct ChunkWithProof {
17 proof: IndexedMerkleProof,
18 #[schemars(with = "String", description = "Hex-encoded bytes.")]
19 chunk: Bytes,
20}
21
22impl ToBytes for ChunkWithProof {
23 fn write_bytes(&self, buf: &mut Vec<u8>) -> Result<(), bytesrepr::Error> {
24 buf.append(&mut self.proof.to_bytes()?);
25 buf.append(&mut self.chunk.to_bytes()?);
26
27 Ok(())
28 }
29
30 fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
31 let mut result = bytesrepr::allocate_buffer(self)?;
32 self.write_bytes(&mut result)?;
33 Ok(result)
34 }
35
36 fn serialized_length(&self) -> usize {
37 self.proof.serialized_length() + self.chunk.serialized_length()
38 }
39}
40
41impl FromBytes for ChunkWithProof {
42 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
43 let (proof, remainder) = FromBytes::from_bytes(bytes)?;
44 let (chunk, remainder) = FromBytes::from_bytes(remainder)?;
45
46 Ok((ChunkWithProof { proof, chunk }, remainder))
47 }
48}
49
50impl ChunkWithProof {
51 #[cfg(test)]
52 pub const CHUNK_SIZE_BYTES: usize = 10;
54
55 #[cfg(not(test))]
56 pub const CHUNK_SIZE_BYTES: usize = 8 * 1024 * 1024;
58
59 pub fn new(data: &[u8], index: u64) -> Result<Self, MerkleConstructionError> {
64 Ok(if data.is_empty() {
65 ChunkWithProof {
66 proof: IndexedMerkleProof::new([Digest::blake2b_hash([])], index)?,
67 chunk: Bytes::new(),
68 }
69 } else {
70 ChunkWithProof {
71 proof: IndexedMerkleProof::new(
72 data.chunks(Self::CHUNK_SIZE_BYTES)
73 .map(Digest::blake2b_hash),
74 index,
75 )?,
76 chunk: Bytes::from(
77 data.chunks(Self::CHUNK_SIZE_BYTES)
78 .nth(index as usize)
79 .ok_or_else(|| MerkleConstructionError::IndexOutOfBounds {
80 count: data.chunks(Self::CHUNK_SIZE_BYTES).len() as u64,
81 index,
82 })?,
83 ),
84 }
85 })
86 }
87
88 pub fn chunk(&self) -> &[u8] {
90 self.chunk.as_slice()
91 }
92
93 pub fn into_chunk(self) -> Bytes {
95 self.chunk
96 }
97
98 pub fn proof(&self) -> &IndexedMerkleProof {
100 &self.proof
101 }
102
103 pub fn verify(&self) -> Result<(), ChunkWithProofVerificationError> {
105 self.proof().verify()?;
106 let first_digest_in_indexed_merkle_proof =
107 self.proof().merkle_proof().first().ok_or_else(|| {
108 ChunkWithProofVerificationError::ChunkWithProofHasEmptyMerkleProof {
109 chunk_with_proof: self.clone(),
110 }
111 })?;
112 let hash_of_chunk = Digest::hash(self.chunk());
113 if *first_digest_in_indexed_merkle_proof != hash_of_chunk {
114 return Err(
115 ChunkWithProofVerificationError::FirstDigestInMerkleProofDidNotMatchHashOfChunk {
116 first_digest_in_indexed_merkle_proof: *first_digest_in_indexed_merkle_proof,
117 hash_of_chunk,
118 },
119 );
120 }
121 Ok(())
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use proptest::{
128 arbitrary::Arbitrary,
129 strategy::{BoxedStrategy, Strategy},
130 };
131 use proptest_attr_macro::proptest;
132 use rand::Rng;
133
134 use casper_types::bytesrepr::{self, FromBytes, ToBytes};
135
136 use crate::{chunk_with_proof::ChunkWithProof, error::MerkleConstructionError, Digest};
137
138 fn prepare_bytes(length: usize) -> Vec<u8> {
139 let mut rng = rand::thread_rng();
140
141 (0..length).map(|_| rng.gen()).collect()
142 }
143
144 fn random_chunk_with_proof() -> ChunkWithProof {
145 let mut rng = rand::thread_rng();
146 let data: Vec<u8> = prepare_bytes(rng.gen_range(1..1024));
147 let index = rng.gen_range(0..data.chunks(ChunkWithProof::CHUNK_SIZE_BYTES).len() as u64);
148
149 ChunkWithProof::new(&data, index).unwrap()
150 }
151
152 impl ChunkWithProof {
153 fn replace_first_proof(self) -> Self {
154 let mut rng = rand::thread_rng();
155 let ChunkWithProof { mut proof, chunk } = self;
156
157 let mut merkle_proof: Vec<_> = proof.merkle_proof().to_vec();
159 merkle_proof.pop();
160 merkle_proof.insert(0, Digest::hash(rng.gen::<usize>().to_string()));
161 proof.inject_merkle_proof(merkle_proof);
162
163 ChunkWithProof { proof, chunk }
164 }
165 }
166
167 #[derive(Debug)]
168 pub struct TestDataSize(usize);
169 impl Arbitrary for TestDataSize {
170 type Parameters = ();
171 type Strategy = BoxedStrategy<Self>;
172
173 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
174 (0usize..32usize)
175 .prop_map(|chunk_count| {
176 TestDataSize(chunk_count * ChunkWithProof::CHUNK_SIZE_BYTES)
177 })
178 .boxed()
179 }
180 }
181
182 #[derive(Debug)]
183 pub struct TestDataSizeAtLeastTwoChunks(usize);
184 impl Arbitrary for TestDataSizeAtLeastTwoChunks {
185 type Parameters = ();
186 type Strategy = BoxedStrategy<Self>;
187
188 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
189 (2usize..32usize)
190 .prop_map(|chunk_count| {
191 TestDataSizeAtLeastTwoChunks(chunk_count * ChunkWithProof::CHUNK_SIZE_BYTES)
192 })
193 .boxed()
194 }
195 }
196
197 #[proptest]
198 fn generates_valid_proof(test_data: TestDataSize) {
199 for data in [prepare_bytes(test_data.0), vec![0u8; test_data.0]] {
200 let number_of_chunks: u64 = data
201 .chunks(ChunkWithProof::CHUNK_SIZE_BYTES)
202 .len()
203 .try_into()
204 .unwrap();
205
206 assert!((0..number_of_chunks)
207 .map(|chunk_index| { ChunkWithProof::new(data.as_slice(), chunk_index).unwrap() })
208 .all(|chunk_with_proof| chunk_with_proof.verify().is_ok()));
209 }
210 }
211
212 #[proptest]
213 fn validate_chunks_against_hash_merkle_tree(test_data: TestDataSizeAtLeastTwoChunks) {
214 assert!(test_data.0 >= ChunkWithProof::CHUNK_SIZE_BYTES * 2);
216
217 for data in [prepare_bytes(test_data.0), vec![0u8; test_data.0]] {
218 let expected_root = Digest::hash_merkle_tree(
219 data.chunks(ChunkWithProof::CHUNK_SIZE_BYTES)
220 .map(Digest::hash),
221 );
222
223 let ChunkWithProof {
225 proof: proof_0,
226 chunk: _,
227 } = ChunkWithProof::new(data.as_slice(), 0).unwrap();
228 let ChunkWithProof {
229 proof: proof_1,
230 chunk: _,
231 } = ChunkWithProof::new(data.as_slice(), 1).unwrap();
232
233 assert_eq!(proof_0.root_hash(), expected_root);
234 assert_eq!(proof_1.root_hash(), expected_root);
235 }
236 }
237
238 #[proptest]
239 fn verifies_chunk_with_proofs(test_data: TestDataSize) {
240 for data in [prepare_bytes(test_data.0), vec![0u8; test_data.0]] {
241 let chunk_with_proof = ChunkWithProof::new(data.as_slice(), 0).unwrap();
242 assert!(chunk_with_proof.verify().is_ok());
243
244 let chunk_with_incorrect_proof = chunk_with_proof.replace_first_proof();
245 assert!(chunk_with_incorrect_proof.verify().is_err());
246 }
247 }
248
249 #[proptest]
250 fn serde_deserialization_of_malformed_chunk_should_work(test_data: TestDataSize) {
251 for data in [prepare_bytes(test_data.0), vec![0u8; test_data.0]] {
252 let chunk_with_proof = ChunkWithProof::new(data.as_slice(), 0).unwrap();
253
254 let json = serde_json::to_string(&chunk_with_proof).unwrap();
255 assert_eq!(
256 chunk_with_proof,
257 serde_json::from_str::<ChunkWithProof>(&json)
258 .expect("should deserialize correctly")
259 );
260
261 let chunk_with_incorrect_proof = chunk_with_proof.replace_first_proof();
262 let json = serde_json::to_string(&chunk_with_incorrect_proof).unwrap();
263 serde_json::from_str::<ChunkWithProof>(&json).expect("should deserialize correctly");
264 }
265 }
266
267 #[proptest]
268 fn bytesrepr_deserialization_of_malformed_chunk_should_work(test_data: TestDataSize) {
269 for data in [prepare_bytes(test_data.0), vec![0u8; test_data.0]] {
270 let chunk_with_proof = ChunkWithProof::new(data.as_slice(), 0).unwrap();
271
272 let bytes = chunk_with_proof
273 .to_bytes()
274 .expect("should serialize correctly");
275
276 let (deserialized_chunk_with_proof, _) =
277 ChunkWithProof::from_bytes(&bytes).expect("should deserialize correctly");
278
279 assert_eq!(chunk_with_proof, deserialized_chunk_with_proof);
280
281 let chunk_with_incorrect_proof = chunk_with_proof.replace_first_proof();
282 let bytes = chunk_with_incorrect_proof
283 .to_bytes()
284 .expect("should serialize correctly");
285
286 ChunkWithProof::from_bytes(&bytes).expect("should deserialize correctly");
287 }
288 }
289
290 #[test]
291 fn returns_error_on_incorrect_index() {
292 let chunk_with_proof = ChunkWithProof::new(&[], 0).expect("should create with empty data");
295 assert!(chunk_with_proof.verify().is_ok());
296
297 let chunk_with_proof =
298 ChunkWithProof::new(&[], 1).expect_err("should error with empty data and index > 0");
299 if let MerkleConstructionError::IndexOutOfBounds { count, index } = chunk_with_proof {
300 assert_eq!(count, 1);
301 assert_eq!(index, 1);
302 } else {
303 panic!("expected MerkleConstructionError::IndexOutOfBounds");
304 }
305
306 let data_larger_than_single_chunk = vec![0u8; ChunkWithProof::CHUNK_SIZE_BYTES * 10];
307 ChunkWithProof::new(data_larger_than_single_chunk.as_slice(), 9).unwrap();
308
309 let chunk_with_proof =
310 ChunkWithProof::new(data_larger_than_single_chunk.as_slice(), 10).unwrap_err();
311 if let MerkleConstructionError::IndexOutOfBounds { count, index } = chunk_with_proof {
312 assert_eq!(count, 10);
313 assert_eq!(index, 10);
314 } else {
315 panic!("expected MerkleConstructionError::IndexOutOfBounds");
316 }
317 }
318
319 #[test]
320 fn bytesrepr_serialization() {
321 let chunk_with_proof = random_chunk_with_proof();
322 bytesrepr::test_serialization_roundtrip(&chunk_with_proof);
323 }
324
325 #[test]
326 fn chunk_with_empty_data_contains_a_single_proof() {
327 let chunk_with_proof = ChunkWithProof::new(&[], 0).unwrap();
328 assert_eq!(chunk_with_proof.proof.merkle_proof().len(), 1)
329 }
330}