fuchsia_merkle/
builder.rs
1use std::cmp::min;
6
7use crate::tree::MerkleTree;
8use crate::util::{hash_block, hash_hashes, HASHES_PER_BLOCK};
9use crate::{Hash, BLOCK_SIZE};
10
11#[derive(Clone, Debug)]
29pub struct MerkleTreeBuilder {
30 block: Vec<u8>,
33 levels: Vec<Vec<Hash>>,
34}
35
36impl Default for MerkleTreeBuilder {
37 fn default() -> Self {
38 Self { levels: vec![Vec::new()], block: Vec::with_capacity(BLOCK_SIZE) }
39 }
40}
41
42impl MerkleTreeBuilder {
43 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn write(&mut self, buf: &[u8]) {
52 let buf = if self.block.is_empty() {
54 buf
55 } else {
56 let left = BLOCK_SIZE - self.block.len();
57 let prefix = min(buf.len(), left);
58 let (buf, rest) = buf.split_at(prefix);
59 self.block.extend_from_slice(buf);
60 if self.block.len() == BLOCK_SIZE {
61 self.push_data_hash(self.hash_block(&self.block[..]));
62 }
63 rest
64 };
65
66 for block in buf.chunks(BLOCK_SIZE) {
68 if block.len() == BLOCK_SIZE {
69 self.push_data_hash(self.hash_block(block));
70 } else {
71 self.block.extend_from_slice(block);
72 }
73 }
74 }
75
76 fn hash_block(&self, block: &[u8]) -> Hash {
79 hash_block(block, self.levels[0].len() * BLOCK_SIZE)
80 }
81
82 pub fn push_data_hash(&mut self, hash: Hash) {
85 self.block.clear();
86 self.levels[0].push(hash);
87 if self.levels[0].len() % HASHES_PER_BLOCK == 0 {
88 self.commit_tail_block(0);
89 }
90 }
91
92 fn commit_tail_block(&mut self, level: usize) {
94 let len = self.levels[level].len();
95 let next_level = level + 1;
96
97 if next_level >= self.levels.len() {
98 self.levels.push(Vec::new());
99 }
100
101 let first_hash = if len % HASHES_PER_BLOCK == 0 {
102 len - HASHES_PER_BLOCK
103 } else {
104 len - (len % HASHES_PER_BLOCK)
105 };
106
107 let hash = hash_hashes(
108 &self.levels[level][first_hash..],
109 next_level,
110 self.levels[next_level].len() * BLOCK_SIZE,
111 );
112
113 self.levels[next_level].push(hash);
114 if self.levels[next_level].len() % HASHES_PER_BLOCK == 0 {
115 self.commit_tail_block(next_level);
116 }
117 }
118
119 pub fn finish(mut self) -> MerkleTree {
122 if !self.block.is_empty() || self.levels[0].is_empty() {
126 self.push_data_hash(self.hash_block(&self.block[..]));
127 }
128
129 for level in 0.. {
132 if level >= self.levels.len() {
133 break;
134 }
135
136 let len = self.levels[level].len();
137 if len > 1 && len % HASHES_PER_BLOCK != 0 {
138 self.commit_tail_block(level);
139 }
140 }
141
142 MerkleTree::from_levels(self.levels)
143 }
144}
145
146impl From<MerkleTreeBuilder> for MerkleTree {
147 fn from(builder: MerkleTreeBuilder) -> Self {
148 builder.finish()
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155 use test_case::test_case;
156
157 #[allow(clippy::unused_unit)]
158 #[test_case(vec![], "15ec7bf0b50732b49f8228e07d24365338f9e3ab994b00af08e5a3bffe55fd8b" ; "test_empty")]
159 #[test_case(vec![0xFF; 8192], "68d131bc271f9c192d4f6dcd8fe61bef90004856da19d0f2f514a7f4098b0737"; "test_oneblock")]
160 #[test_case(vec![0xFF; 65536], "f75f59a944d2433bc6830ec243bfefa457704d2aed12f30539cd4f18bf1d62cf"; "test_small")]
161 #[test_case(vec![0xFF; 2105344], "7d75dfb18bfd48e03b5be4e8e9aeea2f89880cb81c1551df855e0d0a0cc59a67"; "test_large")]
162 #[test_case(vec![0xFF; 2109440], "7577266aa98ce587922fdc668c186e27f3c742fb1b732737153b70ae46973e43"; "test_unaligned")]
163 fn tests(input: Vec<u8>, output: &str) {
164 let mut tree = MerkleTreeBuilder::new();
165 tree.write(input.as_slice());
166 let actual = tree.finish().root();
167 let expected: Hash = output.parse().unwrap();
168 assert_eq!(expected, actual);
169 }
170
171 #[test]
172 fn test_unaligned_single_block() {
173 let data = vec![0xFF; 8192];
174 let mut tree = MerkleTreeBuilder::new();
175 let (first, second) = &data[..].split_at(1024);
176 tree.write(first);
177 tree.write(second);
178
179 let root = tree.finish().root();
180
181 let expected =
182 "68d131bc271f9c192d4f6dcd8fe61bef90004856da19d0f2f514a7f4098b0737".parse().unwrap();
183 assert_eq!(root, expected);
184 }
185
186 #[test]
187 fn test_unaligned_n_block() {
188 let data = vec![0xFF; 65536];
189 let expected =
190 "f75f59a944d2433bc6830ec243bfefa457704d2aed12f30539cd4f18bf1d62cf".parse().unwrap();
191
192 for chunk_size in &[1, 100, 1024, 8193] {
193 let mut tree = MerkleTreeBuilder::new();
194 for block in data.as_slice().chunks(*chunk_size) {
195 tree.write(block);
196 }
197 let root = tree.finish().root();
198
199 assert_eq!(root, expected);
200 }
201 }
202
203 #[test]
204 fn test_fuchsia() {
205 let fuchsia: Vec<_> =
206 vec![0xff, 0x00, 0x80].into_iter().cycle().take(3 * BLOCK_SIZE).collect();
207
208 let mut t = MerkleTreeBuilder::new();
209
210 let mut remaining = 0xff0080;
211 while remaining > 0 {
212 let n = min(remaining, fuchsia.len());
213 t.write(&fuchsia[..n]);
214 remaining -= n;
215 }
216 let actual = t.finish().root();
217 let expected: Hash =
218 "2feb488cffc976061998ac90ce7292241dfa86883c0edc279433b5c4370d0f30".parse().unwrap();
219 assert_eq!(expected, actual);
220 }
221}