diff --git a/garnet/public/rust/fuchsia-merkle/src/lib.rs b/garnet/public/rust/fuchsia-merkle/src/lib.rs index 3aca6057dbb92e9cfbe143069a45a2180bcd59ce..0696f6463aefd44dad46136b797c89893b752d89 100644 --- a/garnet/public/rust/fuchsia-merkle/src/lib.rs +++ b/garnet/public/rust/fuchsia-merkle/src/lib.rs @@ -4,6 +4,8 @@ //! `fuchsia_merkle` contains types and methods for building and working with merkle trees. +#![deny(missing_docs)] + /// The size of a single block of data (or hashes), in bytes. pub const BLOCK_SIZE: usize = 8192; diff --git a/garnet/public/rust/fuchsia-merkle/src/tree.rs b/garnet/public/rust/fuchsia-merkle/src/tree.rs index 71697a3665a7bf4198e4227974354f2060fe2ad1..eef8d4ce8d311f3a6b9baf41846a36b86e8c4100 100644 --- a/garnet/public/rust/fuchsia-merkle/src/tree.rs +++ b/garnet/public/rust/fuchsia-merkle/src/tree.rs @@ -3,6 +3,8 @@ // found in the LICENSE file. use crate::hash::Hash; +use crate::BLOCK_SIZE; +use std::io; /// A `MerkleTree` contains levels of hashes that can be used to verify the integrity of data. /// @@ -50,6 +52,31 @@ impl MerkleTree { pub fn root(&self) -> Hash { self.levels[self.levels.len() - 1][0] } + + /// Creates a `MerkleTree` from all of the bytes of a `Read`er. + /// + /// # Examples + /// ``` + /// # use fuchsia_merkle::MerkleTree; + /// let data_to_hash = [0xffu8; 8192]; + /// let tree = MerkleTree::from_reader(&data_to_hash[..]).unwrap(); + /// assert_eq!( + /// tree.root(), + /// "68d131bc271f9c192d4f6dcd8fe61bef90004856da19d0f2f514a7f4098b0737".parse().unwrap() + /// ); + /// ``` + pub fn from_reader(mut reader: impl std::io::Read) -> Result<MerkleTree, io::Error> { + let mut builder = crate::builder::MerkleTreeBuilder::new(); + let mut buf = [0u8; BLOCK_SIZE]; + loop { + let size = reader.read(&mut buf)?; + if size == 0 { + break; + } + builder.write(&buf[0..size]); + } + Ok(builder.finish()) + } } #[cfg(test)] @@ -57,7 +84,6 @@ mod tests { use super::*; use crate::util::HASHES_PER_BLOCK; use crate::util::{hash_block, hash_hashes}; - use crate::BLOCK_SIZE; impl MerkleTree { /// Given the index of a block of data, lookup its hash. @@ -84,4 +110,57 @@ mod tests { assert_eq!(tree.leaf_hash(i), leafs[i]); } } + + #[test] + fn test_from_reader_empty() { + let data_to_hash = [0x00u8; 0]; + let tree = MerkleTree::from_reader(&data_to_hash[..]).unwrap(); + let expected: Hash = + "15ec7bf0b50732b49f8228e07d24365338f9e3ab994b00af08e5a3bffe55fd8b".parse().unwrap(); + assert_eq!(tree.root(), expected); + } + + #[test] + fn test_from_reader_oneblock() { + let data_to_hash = [0xffu8; 8192]; + let tree = MerkleTree::from_reader(&data_to_hash[..]).unwrap(); + let expected: Hash = + "68d131bc271f9c192d4f6dcd8fe61bef90004856da19d0f2f514a7f4098b0737".parse().unwrap(); + assert_eq!(tree.root(), expected); + } + + #[test] + fn test_from_reader_unaligned() { + let size = 2_109_440usize; + let mut the_bytes = Vec::with_capacity(size); + the_bytes.extend(std::iter::repeat(0xff).take(size)); + let tree = MerkleTree::from_reader(&the_bytes[..]).unwrap(); + let expected: Hash = + "7577266aa98ce587922fdc668c186e27f3c742fb1b732737153b70ae46973e43".parse().unwrap(); + assert_eq!(tree.root(), expected); + } + + #[test] + fn test_from_reader_error_propagation() { + const CUSTOM_ERROR_MESSAGE: &str = "merkle tree custom error message"; + struct ReaderSuccessThenError { + been_called: bool, + } + + impl std::io::Read for ReaderSuccessThenError { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> { + if !self.been_called { + self.been_called = true; + buf[0] = 0; + Ok(1) + } else { + Err(io::Error::new(io::ErrorKind::Other, CUSTOM_ERROR_MESSAGE)) + } + } + } + + let reader = ReaderSuccessThenError { been_called: false }; + let result = MerkleTree::from_reader(reader); + assert_eq!(result.unwrap_err().to_string(), CUSTOM_ERROR_MESSAGE); + } }