fuchsia_merkle/
builder.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use 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/// A `MerkleTreeBuilder` generates a [`MerkleTree`] from one or more write calls.
12///
13/// # Examples
14/// ```
15/// # use fuchsia_merkle::*;
16/// let data = vec![0xff; 8192];
17/// let mut builder = MerkleTreeBuilder::new();
18/// for i in 0..8 {
19///     builder.write(&data[..]);
20/// }
21/// assert_eq!(
22///     builder.finish().root(),
23///     "f75f59a944d2433bc6830ec243bfefa457704d2aed12f30539cd4f18bf1d62cf"
24///         .parse()
25///         .unwrap()
26/// );
27/// ```
28#[derive(Clone, Debug)]
29pub struct MerkleTreeBuilder {
30    /// Buffer to hold a partial block of data between [`MerkleTreeBuilder::write`] calls.
31    /// `block.len()` will never exceed [`BLOCK_SIZE`].
32    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    /// Creates a new, empty `MerkleTreeBuilder`.
44    pub fn new() -> Self {
45        Self::default()
46    }
47
48    /// Append a buffer of bytes to the merkle tree.
49    ///
50    /// No internal buffering is required if all writes are [`BLOCK_SIZE`] aligned.
51    pub fn write(&mut self, buf: &[u8]) {
52        // Fill the current partial block, if it exists.
53        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        // Write full blocks, saving any final partial block for later writes.
67        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    /// Hash a block of data (level 0), using an offset based on the current number of level 0
77    /// hashes.
78    fn hash_block(&self, block: &[u8]) -> Hash {
79        hash_block(block, self.levels[0].len() * BLOCK_SIZE)
80    }
81
82    /// Save a data block hash, propagating full blocks of hashes to higher layers. Also clear a
83    /// stored data block.
84    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    /// Hash a complete (or final partial) block of hashes, chaining to higher levels as needed.
93    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    /// Finalize all levels of the merkle tree, converting this `MerkleTreeBuilder` instance to a
120    /// [`MerkleTree`].
121    pub fn finish(mut self) -> MerkleTree {
122        // The data protected by the tree may not be BLOCK_SIZE aligned. Commit a partial data
123        // block before finalizing the hash levels.
124        // Also, an empty tree consists of a single, empty block. Handle that case now as well.
125        if !self.block.is_empty() || self.levels[0].is_empty() {
126            self.push_data_hash(self.hash_block(&self.block[..]));
127        }
128
129        // Enumerate the hash levels, finalizing any that have a partial block of hashes.
130        // `commit_tail_block` may add new levels to the tree, so don't assume a length up front.
131        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}