filecoin_proofs/
commitment_reader.rs1use std::cmp::min;
2use std::io::{self, Read};
3
4use anyhow::{ensure, Result};
5use filecoin_hashers::{HashFunction, Hasher};
6use rayon::prelude::{ParallelIterator, ParallelSlice};
7
8use crate::{constants::DefaultPieceHasher, pieces::piece_hash};
9
10const BUFFER_SIZE: usize = 4096;
11
12pub struct CommitmentReader<R> {
15 source: R,
16 buffer: Vec<u8>,
17 buffer_pos: usize,
18 current_tree: Vec<<DefaultPieceHasher as Hasher>::Domain>,
19}
20
21impl<R: Read> CommitmentReader<R> {
22 pub fn new(source: R) -> Self {
23 CommitmentReader {
24 source,
25 buffer: vec![0u8; BUFFER_SIZE],
26 buffer_pos: 0,
27 current_tree: Vec::new(),
28 }
29 }
30
31 fn try_hash(&mut self) {
33 if self.buffer_pos % 64 != 0 {
35 return;
36 }
37
38 for chunk in self.buffer[..self.buffer_pos].chunks_exact(64) {
39 let hash = <DefaultPieceHasher as Hasher>::Function::hash(chunk);
41 self.current_tree.push(hash);
42 }
43 self.buffer_pos = 0;
44
45 }
47
48 pub fn finish(self) -> Result<<DefaultPieceHasher as Hasher>::Domain> {
49 ensure!(self.buffer_pos == 0, "not enough inputs provided");
50
51 let CommitmentReader { current_tree, .. } = self;
52
53 let mut current_row = current_tree;
54
55 while current_row.len() > 1 {
56 let next_row = current_row
57 .par_chunks(2)
58 .map(|chunk| piece_hash(chunk[0].as_ref(), chunk[1].as_ref()))
59 .collect::<Vec<_>>();
60
61 current_row = next_row;
62 }
63 debug_assert_eq!(current_row.len(), 1);
64
65 Ok(current_row
66 .into_iter()
67 .next()
68 .expect("should have been caught by debug build: len==1"))
69 }
70}
71
72impl<R: Read> Read for CommitmentReader<R> {
73 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
74 let start = self.buffer_pos;
75 let left = BUFFER_SIZE - self.buffer_pos;
76 let end = start + min(left, buf.len());
77
78 let r = self.source.read(&mut self.buffer[start..end])?;
80
81 buf[..r].copy_from_slice(&self.buffer[start..start + r]);
83
84 self.buffer_pos += r;
85
86 self.try_hash();
88
89 Ok(r)
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96
97 use std::io::Cursor;
98
99 use fr32::Fr32Reader;
100 use storage_proofs_core::pieces::generate_piece_commitment_bytes_from_source;
101
102 use crate::types::{PaddedBytesAmount, UnpaddedBytesAmount};
103
104 #[test]
105 fn test_commitment_reader() {
106 let piece_size = 127 * 8;
107 let source = vec![255u8; piece_size];
108 let mut fr32_reader = Fr32Reader::new(Cursor::new(&source));
109
110 let commitment1 = generate_piece_commitment_bytes_from_source::<DefaultPieceHasher>(
111 &mut fr32_reader,
112 PaddedBytesAmount::from(UnpaddedBytesAmount(piece_size as u64)).into(),
113 )
114 .expect("failed to generate piece commitment bytes from source");
115
116 let fr32_reader = Fr32Reader::new(Cursor::new(&source));
117 let mut commitment_reader = CommitmentReader::new(fr32_reader);
118 io::copy(&mut commitment_reader, &mut io::sink()).expect("io copy failed");
119
120 let commitment2 = commitment_reader.finish().expect("failed to finish");
121
122 assert_eq!(&commitment1[..], AsRef::<[u8]>::as_ref(&commitment2));
123 }
124}