Publish ssz_types (and deps) to crates.io (#468)

* Rename `hashing` crate to `eth2_hashing`

* Add license, desc to eth2_hashing Cargo.toml

* Remove merkle root from eth2 hashing

* Remove old benches folder (zombied from old branch)

* Add docs to eth2_hashing

* Prepare tree_hash for publishing on crates.io

* Update deps to use crates.io instead of paths

* Update all crates to pull ssz from crates.io

* Remove cached_tree_hash, add patches to manifest

* Fix compile error in benches

* Remove unused code

* Fix fake_crypto compile error
This commit is contained in:
Paul Hauner
2019-08-08 11:39:47 +10:00
committed by GitHub
parent 88e89f9ab2
commit 8cfa36fedd
87 changed files with 205 additions and 2643 deletions

View File

@@ -1,340 +0,0 @@
use super::*;
/// A schema defining a binary tree over a `TreeHashCache`.
///
/// This structure is used for succinct storage; run-time functionality is gained by converting a
/// `BTreeSchema` into a `BTreeOverlay`.
#[derive(Debug, PartialEq, Clone)]
pub struct BTreeSchema {
/// The depth of a schema defines how far it is nested within other fixed-length items.
///
/// Each time a new variable-length object is created all items within it are assigned a depth
/// of `depth + 1`.
///
/// When storing the schemas in a list, the depth parameter allows for removing all schemas
/// belonging to a specific variable-length item without removing schemas related to adjacent
/// variable-length items.
pub depth: usize,
lengths: Vec<usize>,
}
impl BTreeSchema {
pub fn from_lengths(depth: usize, lengths: Vec<usize>) -> Self {
Self { depth, lengths }
}
pub fn into_overlay(self, offset: usize) -> BTreeOverlay {
BTreeOverlay::from_schema(self, offset)
}
}
impl Into<BTreeSchema> for BTreeOverlay {
fn into(self) -> BTreeSchema {
BTreeSchema {
depth: self.depth,
lengths: self.lengths,
}
}
}
/// Provides a status for some leaf-node in binary tree.
#[derive(Debug, PartialEq, Clone)]
pub enum LeafNode {
/// The leaf node does not exist in this tree.
DoesNotExist,
/// The leaf node exists in the tree and has a real value within the given `chunk` range.
Exists(Range<usize>),
/// The leaf node exists in the tree only as padding.
Padding,
}
/// Instantiated from a `BTreeSchema`, a `BTreeOverlay` allows for interpreting some
/// non-consecutive chunks of a `TreeHashCache` as a perfect binary tree.
///
/// The primary purpose of this struct is to map from binary tree "nodes" to `TreeHashCache`
/// "chunks". Each tree has nodes `0..n` where `n` is the number of nodes and `0` is the root node.
/// Each of these nodes is mapped to a chunk, starting from `self.offset` and increasing in steps
/// of `1` for internal nodes and arbitrary steps for leaf-nodes.
#[derive(Debug, PartialEq, Clone)]
pub struct BTreeOverlay {
offset: usize,
/// See `BTreeSchema.depth` for a description.
pub depth: usize,
lengths: Vec<usize>,
}
impl BTreeOverlay {
/// Instantiates a new instance for `item`, where it's first chunk is `initial_offset` and has
/// the specified `depth`.
pub fn new<T>(item: &T, initial_offset: usize, depth: usize) -> Self
where
T: CachedTreeHash,
{
Self::from_schema(item.tree_hash_cache_schema(depth), initial_offset)
}
/// Instantiates a new instance from a schema, where it's first chunk is `offset`.
pub fn from_schema(schema: BTreeSchema, offset: usize) -> Self {
Self {
offset,
depth: schema.depth,
lengths: schema.lengths,
}
}
/// Returns a `LeafNode` for each of the `n` leaves of the tree.
///
/// `LeafNode::DoesNotExist` is returned for each element `i` in `0..n` where `i >=
/// self.num_leaf_nodes()`.
pub fn get_leaf_nodes(&self, n: usize) -> Vec<LeafNode> {
let mut running_offset = self.offset + self.num_internal_nodes();
let mut leaf_nodes: Vec<LeafNode> = self
.lengths
.iter()
.map(|length| {
let range = running_offset..running_offset + length;
running_offset += length;
LeafNode::Exists(range)
})
.collect();
leaf_nodes.resize(self.num_leaf_nodes(), LeafNode::Padding);
leaf_nodes.resize(n, LeafNode::DoesNotExist);
leaf_nodes
}
/// Returns the number of leaf nodes in the tree.
pub fn num_leaf_nodes(&self) -> usize {
self.lengths.len().next_power_of_two()
}
/// Returns the number of leafs in the tree which are padding.
pub fn num_padding_leaves(&self) -> usize {
self.num_leaf_nodes() - self.lengths.len()
}
/// Returns the number of nodes in the tree.
///
/// Note: this is distinct from `num_chunks`, which returns the total number of chunks in
/// this tree.
pub fn num_nodes(&self) -> usize {
2 * self.num_leaf_nodes() - 1
}
/// Returns the number of internal (non-leaf) nodes in the tree.
pub fn num_internal_nodes(&self) -> usize {
self.num_leaf_nodes() - 1
}
/// Returns the chunk of the first node of the tree.
fn first_node(&self) -> usize {
self.offset
}
/// Returns the root chunk of the tree (the zero-th node)
pub fn root(&self) -> usize {
self.first_node()
}
/// Returns the first chunk outside of the boundary of this tree. It is the root node chunk
/// plus the total number of chunks in the tree.
pub fn next_node(&self) -> usize {
self.first_node() + self.num_internal_nodes() + self.num_leaf_nodes() - self.lengths.len()
+ self.lengths.iter().sum::<usize>()
}
/// Returns the height of the tree where a tree with a single node has a height of 1.
pub fn height(&self) -> usize {
self.num_leaf_nodes().trailing_zeros() as usize
}
/// Returns the range of chunks that belong to the internal nodes of the tree.
pub fn internal_chunk_range(&self) -> Range<usize> {
self.offset..self.offset + self.num_internal_nodes()
}
/// Returns all of the chunks that are encompassed by the tree.
pub fn chunk_range(&self) -> Range<usize> {
self.first_node()..self.next_node()
}
/// Returns the number of chunks inside this tree (including subtrees).
///
/// Note: this is distinct from `num_nodes` which returns the number of nodes in the binary
/// tree.
pub fn num_chunks(&self) -> usize {
self.next_node() - self.first_node()
}
/// Returns the first chunk of the first leaf node in the tree.
pub fn first_leaf_node(&self) -> usize {
self.offset + self.num_internal_nodes()
}
/// Returns the chunks for some given parent node.
///
/// Note: it is a parent _node_ not a parent _chunk_.
pub fn child_chunks(&self, parent: usize) -> (usize, usize) {
let children = children(parent);
if children.1 < self.num_internal_nodes() {
(children.0 + self.offset, children.1 + self.offset)
} else {
let chunks = self.n_leaf_node_chunks(children.1);
(chunks[chunks.len() - 2], chunks[chunks.len() - 1])
}
}
/// Returns a vec of (parent_chunk, (left_child_chunk, right_child_chunk)).
pub fn internal_parents_and_children(&self) -> Vec<(usize, (usize, usize))> {
let mut chunks = Vec::with_capacity(self.num_nodes());
chunks.append(&mut self.internal_node_chunks());
chunks.append(&mut self.leaf_node_chunks());
(0..self.num_internal_nodes())
.map(|parent| {
let children = children(parent);
(chunks[parent], (chunks[children.0], chunks[children.1]))
})
.collect()
}
/// Returns a vec of chunk indices for each internal node of the tree.
pub fn internal_node_chunks(&self) -> Vec<usize> {
(self.offset..self.offset + self.num_internal_nodes()).collect()
}
/// Returns a vec of the first chunk for each leaf node of the tree.
pub fn leaf_node_chunks(&self) -> Vec<usize> {
self.n_leaf_node_chunks(self.num_leaf_nodes())
}
/// Returns a vec of the first chunk index for the first `n` leaf nodes of the tree.
fn n_leaf_node_chunks(&self, n: usize) -> Vec<usize> {
let mut chunks = Vec::with_capacity(n);
let mut chunk = self.offset + self.num_internal_nodes();
for i in 0..n {
chunks.push(chunk);
match self.lengths.get(i) {
Some(len) => {
chunk += len;
}
None => chunk += 1,
}
}
chunks
}
}
fn children(parent: usize) -> (usize, usize) {
((2 * parent + 1), (2 * parent + 2))
}
#[cfg(test)]
mod test {
use super::*;
fn get_tree_a(n: usize) -> BTreeOverlay {
BTreeSchema::from_lengths(0, vec![1; n]).into_overlay(0)
}
#[test]
fn leaf_node_chunks() {
let tree = get_tree_a(4);
assert_eq!(tree.leaf_node_chunks(), vec![3, 4, 5, 6])
}
#[test]
fn internal_node_chunks() {
let tree = get_tree_a(4);
assert_eq!(tree.internal_node_chunks(), vec![0, 1, 2])
}
#[test]
fn internal_parents_and_children() {
let tree = get_tree_a(4);
assert_eq!(
tree.internal_parents_and_children(),
vec![(0, (1, 2)), (1, (3, 4)), (2, (5, 6))]
)
}
#[test]
fn chunk_range() {
let tree = get_tree_a(4);
assert_eq!(tree.chunk_range(), 0..7);
let tree = get_tree_a(1);
assert_eq!(tree.chunk_range(), 0..1);
let tree = get_tree_a(2);
assert_eq!(tree.chunk_range(), 0..3);
let tree = BTreeSchema::from_lengths(0, vec![1, 1]).into_overlay(11);
assert_eq!(tree.chunk_range(), 11..14);
let tree = BTreeSchema::from_lengths(0, vec![7, 7, 7]).into_overlay(0);
assert_eq!(tree.chunk_range(), 0..25);
}
#[test]
fn get_leaf_node() {
let tree = get_tree_a(4);
let leaves = tree.get_leaf_nodes(5);
assert_eq!(leaves[0], LeafNode::Exists(3..4));
assert_eq!(leaves[1], LeafNode::Exists(4..5));
assert_eq!(leaves[2], LeafNode::Exists(5..6));
assert_eq!(leaves[3], LeafNode::Exists(6..7));
assert_eq!(leaves[4], LeafNode::DoesNotExist);
let tree = get_tree_a(3);
let leaves = tree.get_leaf_nodes(5);
assert_eq!(leaves[0], LeafNode::Exists(3..4));
assert_eq!(leaves[1], LeafNode::Exists(4..5));
assert_eq!(leaves[2], LeafNode::Exists(5..6));
assert_eq!(leaves[3], LeafNode::Padding);
assert_eq!(leaves[4], LeafNode::DoesNotExist);
let tree = get_tree_a(0);
let leaves = tree.get_leaf_nodes(2);
assert_eq!(leaves[0], LeafNode::Padding);
assert_eq!(leaves[1], LeafNode::DoesNotExist);
let tree = BTreeSchema::from_lengths(0, vec![3]).into_overlay(0);
let leaves = tree.get_leaf_nodes(2);
assert_eq!(leaves[0], LeafNode::Exists(0..3));
assert_eq!(leaves[1], LeafNode::DoesNotExist);
let tree = BTreeSchema::from_lengths(0, vec![3]).into_overlay(10);
let leaves = tree.get_leaf_nodes(2);
assert_eq!(leaves[0], LeafNode::Exists(10..13));
assert_eq!(leaves[1], LeafNode::DoesNotExist);
}
#[test]
fn root_of_one_node() {
let tree = get_tree_a(1);
assert_eq!(tree.root(), 0);
assert_eq!(tree.num_internal_nodes(), 0);
assert_eq!(tree.num_leaf_nodes(), 1);
}
#[test]
fn child_chunks() {
let tree = get_tree_a(4);
assert_eq!(tree.child_chunks(0), (1, 2))
}
}

View File

@@ -1,19 +0,0 @@
use tree_hash::TreeHashType;
#[derive(Debug, PartialEq, Clone)]
pub enum Error {
ShouldNotProduceBTreeOverlay,
NoFirstNode,
NoBytesForRoot,
UnableToObtainSlices,
UnableToGrowMerkleTree,
UnableToShrinkMerkleTree,
TreeCannotHaveZeroNodes,
CacheNotInitialized,
ShouldNeverBePacked(TreeHashType),
BytesAreNotEvenChunks(usize),
NoModifiedFieldForChunk(usize),
NoBytesForChunk(usize),
NoSchemaForIndex(usize),
NotLeafNode(usize),
}

View File

@@ -1,117 +0,0 @@
use super::*;
use crate::merkleize::merkleize;
use ethereum_types::H256;
pub mod vec;
macro_rules! impl_for_single_leaf_int {
($type: ident) => {
impl CachedTreeHash for $type {
fn new_tree_hash_cache(&self, _depth: usize) -> Result<TreeHashCache, Error> {
Ok(TreeHashCache::from_bytes(
merkleize(self.to_le_bytes().to_vec()),
false,
None,
)?)
}
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema {
BTreeSchema::from_lengths(depth, vec![1])
}
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> {
let leaf = merkleize(self.to_le_bytes().to_vec());
cache.maybe_update_chunk(cache.chunk_index, &leaf)?;
cache.chunk_index += 1;
Ok(())
}
}
};
}
impl_for_single_leaf_int!(u8);
impl_for_single_leaf_int!(u16);
impl_for_single_leaf_int!(u32);
impl_for_single_leaf_int!(u64);
impl_for_single_leaf_int!(usize);
impl CachedTreeHash for bool {
fn new_tree_hash_cache(&self, _depth: usize) -> Result<TreeHashCache, Error> {
Ok(TreeHashCache::from_bytes(
merkleize((*self as u8).to_le_bytes().to_vec()),
false,
None,
)?)
}
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema {
BTreeSchema::from_lengths(depth, vec![1])
}
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> {
let leaf = merkleize((*self as u8).to_le_bytes().to_vec());
cache.maybe_update_chunk(cache.chunk_index, &leaf)?;
cache.chunk_index += 1;
Ok(())
}
}
macro_rules! impl_for_u8_array {
($len: expr) => {
impl CachedTreeHash for [u8; $len] {
fn new_tree_hash_cache(&self, _depth: usize) -> Result<TreeHashCache, Error> {
Ok(TreeHashCache::from_bytes(
merkleize(self.to_vec()),
false,
None,
)?)
}
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema {
BTreeSchema::from_lengths(depth, vec![1])
}
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> {
let leaf = merkleize(self.to_vec());
cache.maybe_update_chunk(cache.chunk_index, &leaf)?;
cache.chunk_index += 1;
Ok(())
}
}
};
}
impl_for_u8_array!(4);
impl_for_u8_array!(32);
impl CachedTreeHash for H256 {
fn new_tree_hash_cache(&self, _depth: usize) -> Result<TreeHashCache, Error> {
Ok(TreeHashCache::from_bytes(
self.as_bytes().to_vec(),
false,
None,
)?)
}
fn num_tree_hash_cache_chunks(&self) -> usize {
1
}
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema {
BTreeSchema::from_lengths(depth, vec![1])
}
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> {
cache.maybe_update_chunk(cache.chunk_index, self.as_bytes())?;
cache.chunk_index += 1;
Ok(())
}
}

View File

@@ -1,338 +0,0 @@
use super::*;
use crate::btree_overlay::LeafNode;
use crate::merkleize::{merkleize, num_sanitized_leaves, sanitise_bytes};
macro_rules! impl_for_list {
($type: ty) => {
impl<T> CachedTreeHash for $type
where
T: CachedTreeHash + TreeHash,
{
fn new_tree_hash_cache(&self, depth: usize) -> Result<TreeHashCache, Error> {
let (mut cache, schema) = new_tree_hash_cache(self, depth)?;
cache.add_length_nodes(schema.into_overlay(0).chunk_range(), self.len())?;
Ok(cache)
}
fn num_tree_hash_cache_chunks(&self) -> usize {
// Add two extra nodes to cater for the node before and after to allow mixing-in length.
BTreeOverlay::new(self, 0, 0).num_chunks() + 2
}
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema {
produce_schema(self, depth)
}
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error> {
// Skip the length-mixed-in root node.
cache.chunk_index += 1;
// Update the cache, returning the new overlay.
let new_overlay = update_tree_hash_cache(&self, cache)?;
// Mix in length
cache.mix_in_length(new_overlay.chunk_range(), self.len())?;
// Skip an extra node to clear the length node.
cache.chunk_index += 1;
Ok(())
}
}
};
}
impl_for_list!(Vec<T>);
impl_for_list!(&[T]);
/// Build a new tree hash cache for some slice.
///
/// Valid for both variable- and fixed-length slices. Does _not_ mix-in the length of the list,
/// the caller must do this.
pub fn new_tree_hash_cache<T: CachedTreeHash>(
vec: &[T],
depth: usize,
) -> Result<(TreeHashCache, BTreeSchema), Error> {
let schema = vec.tree_hash_cache_schema(depth);
let cache = match T::tree_hash_type() {
TreeHashType::Basic => TreeHashCache::from_bytes(
merkleize(get_packed_leaves(vec)?),
false,
Some(schema.clone()),
),
TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => {
let subtrees = vec
.iter()
.map(|item| TreeHashCache::new_at_depth(item, depth + 1))
.collect::<Result<Vec<TreeHashCache>, _>>()?;
TreeHashCache::from_subtrees(&vec, subtrees, depth)
}
}?;
Ok((cache, schema))
}
/// Produce a schema for some slice.
///
/// Valid for both variable- and fixed-length slices. Does _not_ add the mix-in length nodes, the
/// caller must do this.
pub fn produce_schema<T: CachedTreeHash>(vec: &[T], depth: usize) -> BTreeSchema {
let lengths = match T::tree_hash_type() {
TreeHashType::Basic => {
// Ceil division.
let num_leaves =
(vec.len() + T::tree_hash_packing_factor() - 1) / T::tree_hash_packing_factor();
// Disallow zero-length as an empty list still has one all-padding node.
vec![1; std::cmp::max(1, num_leaves)]
}
TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => {
let mut lengths = vec![];
for item in vec {
lengths.push(item.num_tree_hash_cache_chunks())
}
lengths
}
};
BTreeSchema::from_lengths(depth, lengths)
}
/// Updates the cache for some slice.
///
/// Valid for both variable- and fixed-length slices. Does _not_ cater for the mix-in length nodes,
/// the caller must do this.
#[allow(clippy::range_plus_one)] // Minor readability lint requiring structural changes; not worth it.
pub fn update_tree_hash_cache<T: CachedTreeHash>(
vec: &[T],
cache: &mut TreeHashCache,
) -> Result<BTreeOverlay, Error> {
let old_overlay = cache.get_overlay(cache.schema_index, cache.chunk_index)?;
let new_overlay = BTreeOverlay::new(&vec, cache.chunk_index, old_overlay.depth);
cache.replace_overlay(cache.schema_index, cache.chunk_index, new_overlay.clone())?;
cache.schema_index += 1;
match T::tree_hash_type() {
TreeHashType::Basic => {
let mut buf = vec![0; HASHSIZE];
let item_bytes = HASHSIZE / T::tree_hash_packing_factor();
// If the number of leaf nodes has changed, resize the cache.
if new_overlay.num_leaf_nodes() < old_overlay.num_leaf_nodes() {
let start = new_overlay.next_node();
let end = start + (old_overlay.num_leaf_nodes() - new_overlay.num_leaf_nodes());
cache.splice(start..end, vec![], vec![]);
} else if new_overlay.num_leaf_nodes() > old_overlay.num_leaf_nodes() {
let start = old_overlay.next_node();
let new_nodes = new_overlay.num_leaf_nodes() - old_overlay.num_leaf_nodes();
cache.splice(
start..start,
vec![0; new_nodes * HASHSIZE],
vec![true; new_nodes],
);
}
// Iterate through each of the leaf nodes in the new list.
for i in 0..new_overlay.num_leaf_nodes() {
// Iterate through the number of items that may be packing into the leaf node.
for j in 0..T::tree_hash_packing_factor() {
// Create a mut slice that can be filled with either a serialized item or
// padding.
let buf_slice = &mut buf[j * item_bytes..(j + 1) * item_bytes];
// Attempt to get the item for this portion of the chunk. If it exists,
// update `buf` with it's serialized bytes. If it doesn't exist, update
// `buf` with padding.
match vec.get(i * T::tree_hash_packing_factor() + j) {
Some(item) => {
buf_slice.copy_from_slice(&item.tree_hash_packed_encoding());
}
None => buf_slice.copy_from_slice(&vec![0; item_bytes]),
}
}
// Update the chunk if the generated `buf` is not the same as the cache.
let chunk = new_overlay.first_leaf_node() + i;
cache.maybe_update_chunk(chunk, &buf)?;
}
}
TreeHashType::Container | TreeHashType::List | TreeHashType::Vector => {
let longest_len =
std::cmp::max(new_overlay.num_leaf_nodes(), old_overlay.num_leaf_nodes());
let old_leaf_nodes = old_overlay.get_leaf_nodes(longest_len);
let new_leaf_nodes = if old_overlay == new_overlay {
old_leaf_nodes.clone()
} else {
new_overlay.get_leaf_nodes(longest_len)
};
for i in 0..longest_len {
match (&old_leaf_nodes[i], &new_leaf_nodes[i]) {
// The item existed in the previous list and exists in the current list.
//
// Update the item.
(LeafNode::Exists(_old), LeafNode::Exists(new)) => {
cache.chunk_index = new.start;
vec[i].update_tree_hash_cache(cache)?;
}
// The list has been lengthened and this is a new item that did not exist in
// the previous list.
//
// Splice the tree for the new item into the current chunk_index.
(LeafNode::DoesNotExist, LeafNode::Exists(new)) => {
splice_in_new_tree(
&vec[i],
new.start..new.start,
new_overlay.depth + 1,
cache,
)?;
cache.chunk_index = new.end;
}
// The list has been lengthened and this is a new item that was previously a
// padding item.
//
// Splice the tree for the new item over the padding chunk.
(LeafNode::Padding, LeafNode::Exists(new)) => {
splice_in_new_tree(
&vec[i],
new.start..new.start + 1,
new_overlay.depth + 1,
cache,
)?;
cache.chunk_index = new.end;
}
// The list has been shortened and this item was removed from the list and made
// into padding.
//
// Splice a padding node over the number of nodes the previous item occupied,
// starting at the current chunk_index.
(LeafNode::Exists(old), LeafNode::Padding) => {
let num_chunks = old.end - old.start;
cache.splice(
cache.chunk_index..cache.chunk_index + num_chunks,
vec![0; HASHSIZE],
vec![true],
);
cache.chunk_index += 1;
}
// The list has been shortened and the item for this leaf existed in the
// previous list, but does not exist in this list.
//
// Remove the number of nodes the previous item occupied, starting at the
// current chunk_index.
(LeafNode::Exists(old), LeafNode::DoesNotExist) => {
let num_chunks = old.end - old.start;
cache.splice(
cache.chunk_index..cache.chunk_index + num_chunks,
vec![],
vec![],
);
}
// The list has been shortened and this leaf was padding in the previous list,
// however it should not exist in this list.
//
// Remove one node, starting at the current `chunk_index`.
(LeafNode::Padding, LeafNode::DoesNotExist) => {
cache.splice(cache.chunk_index..cache.chunk_index + 1, vec![], vec![]);
}
// The list has been lengthened and this leaf did not exist in the previous
// list, but should be padding for this list.
//
// Splice in a new padding node at the current chunk_index.
(LeafNode::DoesNotExist, LeafNode::Padding) => {
cache.splice(
cache.chunk_index..cache.chunk_index,
vec![0; HASHSIZE],
vec![true],
);
cache.chunk_index += 1;
}
// This leaf was padding in both lists, there's nothing to do.
(LeafNode::Padding, LeafNode::Padding) => (),
// As we are looping through the larger of the lists of leaf nodes, it should
// be impossible for either leaf to be non-existent.
(LeafNode::DoesNotExist, LeafNode::DoesNotExist) => unreachable!(),
}
}
// Clean out any excess schemas that may or may not be remaining if the list was
// shortened.
cache.remove_proceeding_child_schemas(cache.schema_index, new_overlay.depth);
}
}
cache.update_internal_nodes(&new_overlay)?;
cache.chunk_index = new_overlay.next_node();
Ok(new_overlay)
}
/// Create a new `TreeHashCache` from `item` and splice it over the `chunks_to_replace` chunks of
/// the given `cache`.
///
/// Useful for the case where a new element is added to a list.
///
/// The schemas created for `item` will have the given `depth`.
fn splice_in_new_tree<T>(
item: &T,
chunks_to_replace: Range<usize>,
depth: usize,
cache: &mut TreeHashCache,
) -> Result<(), Error>
where
T: CachedTreeHash,
{
let (bytes, mut bools, schemas) = TreeHashCache::new_at_depth(item, depth)?.into_components();
// Record the number of schemas, this will be used later in the fn.
let num_schemas = schemas.len();
// Flag the root node of the new tree as dirty.
bools[0] = true;
cache.splice(chunks_to_replace, bytes, bools);
cache
.schemas
.splice(cache.schema_index..cache.schema_index, schemas);
cache.schema_index += num_schemas;
Ok(())
}
/// Packs all of the leaves of `vec` into a single byte-array, appending `0` to ensure the number
/// of chunks in the byte-array is a power-of-two.
fn get_packed_leaves<T>(vec: &[T]) -> Result<Vec<u8>, Error>
where
T: CachedTreeHash,
{
let num_packed_bytes = (BYTES_PER_CHUNK / T::tree_hash_packing_factor()) * vec.len();
let num_leaves = num_sanitized_leaves(num_packed_bytes);
let mut packed = Vec::with_capacity(num_leaves * HASHSIZE);
for item in vec {
packed.append(&mut item.tree_hash_packed_encoding());
}
Ok(sanitise_bytes(packed))
}

View File

@@ -1,150 +0,0 @@
//! Performs cached merkle-hashing adhering to the Ethereum 2.0 specification defined
//! [here](https://github.com/ethereum/eth2.0-specs/blob/v0.5.1/specs/simple-serialize.md#merkleization).
//!
//! Caching allows for reduced hashing when some object has only been partially modified, which
//! consumes less CPU-time at the cost of additional storage. For example,
//! determining the root of a list of 1024 items with a single modification has been observed to
//! run in 1/25th of the time of a full merkle hash.
//!
//!
//! # Example:
//!
//! ```
//! use cached_tree_hash::TreeHashCache;
//! use tree_hash_derive::{TreeHash, CachedTreeHash};
//!
//! #[derive(TreeHash, CachedTreeHash)]
//! struct Foo {
//! bar: u64,
//! baz: Vec<u64>
//! }
//!
//! let mut foo = Foo {
//! bar: 1,
//! baz: vec![0, 1, 2]
//! };
//!
//! let mut cache = TreeHashCache::new(&foo).unwrap();
//!
//! foo.baz[1] = 0;
//!
//! cache.update(&foo).unwrap();
//!
//! println!("Root is: {:?}", cache.tree_hash_root().unwrap());
//! ```
use hashing::hash;
use std::ops::Range;
use tree_hash::{TreeHash, TreeHashType, BYTES_PER_CHUNK, HASHSIZE};
mod btree_overlay;
mod errors;
mod impls;
pub mod merkleize;
mod resize;
mod tree_hash_cache;
pub use btree_overlay::{BTreeOverlay, BTreeSchema};
pub use errors::Error;
pub use impls::vec;
pub use tree_hash_cache::TreeHashCache;
pub trait CachedTreeHash: TreeHash {
fn tree_hash_cache_schema(&self, depth: usize) -> BTreeSchema;
fn num_tree_hash_cache_chunks(&self) -> usize {
self.tree_hash_cache_schema(0).into_overlay(0).num_chunks()
}
fn new_tree_hash_cache(&self, depth: usize) -> Result<TreeHashCache, Error>;
fn update_tree_hash_cache(&self, cache: &mut TreeHashCache) -> Result<(), Error>;
}
/// Implements `CachedTreeHash` on `$type`, where `$type` is a fixed-length vector and each item in
/// the `$type` is encoded as bytes using `ssz_encode`.
#[macro_export]
macro_rules! cached_tree_hash_ssz_encoding_as_vector {
($type: ident, $num_bytes: expr) => {
impl cached_tree_hash::CachedTreeHash for $type {
fn new_tree_hash_cache(
&self,
depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let (cache, _schema) =
cached_tree_hash::vec::new_tree_hash_cache(&ssz::ssz_encode(self), depth)?;
Ok(cache)
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
let lengths =
vec![1; cached_tree_hash::merkleize::num_unsanitized_leaves($num_bytes)];
cached_tree_hash::BTreeSchema::from_lengths(depth, lengths)
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
cached_tree_hash::vec::update_tree_hash_cache(&ssz::ssz_encode(self), cache)?;
Ok(())
}
}
};
}
/// Implements `CachedTreeHash` on `$type`, where `$type` is a variable-length list and each item
/// in `$type` is encoded as bytes by calling `item.to_bytes()`.
#[macro_export]
macro_rules! cached_tree_hash_bytes_as_list {
($type: ident) => {
impl cached_tree_hash::CachedTreeHash for $type {
fn new_tree_hash_cache(
&self,
depth: usize,
) -> Result<cached_tree_hash::TreeHashCache, cached_tree_hash::Error> {
let bytes = self.to_bytes();
let (mut cache, schema) =
cached_tree_hash::vec::new_tree_hash_cache(&bytes, depth)?;
cache.add_length_nodes(schema.into_overlay(0).chunk_range(), bytes.len())?;
Ok(cache)
}
fn num_tree_hash_cache_chunks(&self) -> usize {
// Add two extra nodes to cater for the node before and after to allow mixing-in length.
cached_tree_hash::BTreeOverlay::new(self, 0, 0).num_chunks() + 2
}
fn tree_hash_cache_schema(&self, depth: usize) -> cached_tree_hash::BTreeSchema {
let bytes = self.to_bytes();
cached_tree_hash::vec::produce_schema(&bytes, depth)
}
fn update_tree_hash_cache(
&self,
cache: &mut cached_tree_hash::TreeHashCache,
) -> Result<(), cached_tree_hash::Error> {
let bytes = self.to_bytes();
// Skip the length-mixed-in root node.
cache.chunk_index += 1;
// Update the cache, returning the new overlay.
let new_overlay = cached_tree_hash::vec::update_tree_hash_cache(&bytes, cache)?;
// Mix in length
cache.mix_in_length(new_overlay.chunk_range(), bytes.len())?;
// Skip an extra node to clear the length node.
cache.chunk_index += 1;
Ok(())
}
}
};
}

View File

@@ -1,83 +0,0 @@
use hashing::hash;
use tree_hash::{BYTES_PER_CHUNK, HASHSIZE, MERKLE_HASH_CHUNK};
/// Split `values` into a power-of-two, identical-length chunks (padding with `0`) and merkleize
/// them, returning the entire merkle tree.
///
/// The root hash is `merkleize(values)[0..BYTES_PER_CHUNK]`.
pub fn merkleize(values: Vec<u8>) -> Vec<u8> {
let values = sanitise_bytes(values);
let leaves = values.len() / HASHSIZE;
if leaves == 0 {
panic!("No full leaves");
}
if !leaves.is_power_of_two() {
panic!("leaves is not power of two");
}
let mut o: Vec<u8> = vec![0; (num_nodes(leaves) - leaves) * HASHSIZE];
o.append(&mut values.to_vec());
let mut i = o.len();
let mut j = o.len() - values.len();
while i >= MERKLE_HASH_CHUNK {
i -= MERKLE_HASH_CHUNK;
let hash = hash(&o[i..i + MERKLE_HASH_CHUNK]);
j -= HASHSIZE;
o[j..j + HASHSIZE].copy_from_slice(&hash);
}
o
}
/// Ensures that the given `bytes` are a power-of-two chunks, padding with zero if not.
pub fn sanitise_bytes(mut bytes: Vec<u8>) -> Vec<u8> {
let present_leaves = num_unsanitized_leaves(bytes.len());
let required_leaves = present_leaves.next_power_of_two();
if (present_leaves != required_leaves) | last_leaf_needs_padding(bytes.len()) {
bytes.resize(num_bytes(required_leaves), 0);
}
bytes
}
/// Pads out `bytes` to ensure it is a clean `num_leaves` chunks.
pub fn pad_for_leaf_count(num_leaves: usize, bytes: &mut Vec<u8>) {
let required_leaves = num_leaves.next_power_of_two();
bytes.resize(
bytes.len() + (required_leaves - num_leaves) * BYTES_PER_CHUNK,
0,
);
}
fn last_leaf_needs_padding(num_bytes: usize) -> bool {
num_bytes % HASHSIZE != 0
}
/// Returns the number of leaves for a given `bytes_len` number of bytes, rounding up if
/// `num_bytes` is not a client multiple of chunk size.
pub fn num_unsanitized_leaves(bytes_len: usize) -> usize {
(bytes_len + HASHSIZE - 1) / HASHSIZE
}
fn num_bytes(num_leaves: usize) -> usize {
num_leaves * HASHSIZE
}
fn num_nodes(num_leaves: usize) -> usize {
2 * num_leaves - 1
}
/// Returns the power-of-two number of leaves that would result from the given `bytes_len` number
/// of bytes.
pub fn num_sanitized_leaves(bytes_len: usize) -> usize {
let leaves = (bytes_len + HASHSIZE - 1) / HASHSIZE;
leaves.next_power_of_two()
}

View File

@@ -1,223 +0,0 @@
#![allow(clippy::range_plus_one)] // Minor readability lint requiring structural changes; not worth it.
use super::*;
/// New vec is bigger than old vec.
pub fn grow_merkle_tree(
old_bytes: &[u8],
old_flags: &[bool],
from_height: usize,
to_height: usize,
) -> Option<(Vec<u8>, Vec<bool>)> {
let to_nodes = nodes_in_tree_of_height(to_height);
let mut bytes = vec![0; to_nodes * HASHSIZE];
let mut flags = vec![true; to_nodes];
for i in 0..=from_height {
let old_byte_slice = old_bytes.get(byte_range_at_height(i))?;
let old_flag_slice = old_flags.get(node_range_at_height(i))?;
let offset = i + to_height - from_height;
let new_byte_slice = bytes.get_mut(byte_range_at_height(offset))?;
let new_flag_slice = flags.get_mut(node_range_at_height(offset))?;
new_byte_slice
.get_mut(0..old_byte_slice.len())?
.copy_from_slice(old_byte_slice);
new_flag_slice
.get_mut(0..old_flag_slice.len())?
.copy_from_slice(old_flag_slice);
}
Some((bytes, flags))
}
/// New vec is smaller than old vec.
pub fn shrink_merkle_tree(
from_bytes: &[u8],
from_flags: &[bool],
from_height: usize,
to_height: usize,
) -> Option<(Vec<u8>, Vec<bool>)> {
let to_nodes = nodes_in_tree_of_height(to_height);
let mut bytes = vec![0; to_nodes * HASHSIZE];
let mut flags = vec![true; to_nodes];
for i in 0..=to_height as usize {
let offset = i + from_height - to_height;
let from_byte_slice = from_bytes.get(byte_range_at_height(offset))?;
let from_flag_slice = from_flags.get(node_range_at_height(offset))?;
let to_byte_slice = bytes.get_mut(byte_range_at_height(i))?;
let to_flag_slice = flags.get_mut(node_range_at_height(i))?;
to_byte_slice.copy_from_slice(from_byte_slice.get(0..to_byte_slice.len())?);
to_flag_slice.copy_from_slice(from_flag_slice.get(0..to_flag_slice.len())?);
}
Some((bytes, flags))
}
pub fn nodes_in_tree_of_height(h: usize) -> usize {
2 * (1 << h) - 1
}
fn byte_range_at_height(h: usize) -> Range<usize> {
let node_range = node_range_at_height(h);
node_range.start * HASHSIZE..node_range.end * HASHSIZE
}
fn node_range_at_height(h: usize) -> Range<usize> {
first_node_at_height(h)..last_node_at_height(h) + 1
}
fn first_node_at_height(h: usize) -> usize {
(1 << h) - 1
}
fn last_node_at_height(h: usize) -> usize {
(1 << (h + 1)) - 2
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn can_grow_and_shrink_three_levels() {
let small: usize = 1;
let big: usize = 15;
let original_bytes = vec![42; small * HASHSIZE];
let original_flags = vec![false; small];
let (grown_bytes, grown_flags) = grow_merkle_tree(
&original_bytes,
&original_flags,
(small + 1).trailing_zeros() as usize - 1,
(big + 1).trailing_zeros() as usize - 1,
)
.unwrap();
let mut expected_bytes = vec![];
let mut expected_flags = vec![];
// First level
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(true);
// Second level
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(true);
expected_flags.push(true);
// Third level
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
// Fourth level
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(false);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
assert_eq!(expected_bytes, grown_bytes);
assert_eq!(expected_flags, grown_flags);
let (shrunk_bytes, shrunk_flags) = shrink_merkle_tree(
&grown_bytes,
&grown_flags,
(big + 1).trailing_zeros() as usize - 1,
(small + 1).trailing_zeros() as usize - 1,
)
.unwrap();
assert_eq!(original_bytes, shrunk_bytes);
assert_eq!(original_flags, shrunk_flags);
}
#[test]
fn can_grow_and_shrink_one_level() {
let small: usize = 7;
let big: usize = 15;
let original_bytes = vec![42; small * HASHSIZE];
let original_flags = vec![false; small];
let (grown_bytes, grown_flags) = grow_merkle_tree(
&original_bytes,
&original_flags,
(small + 1).trailing_zeros() as usize - 1,
(big + 1).trailing_zeros() as usize - 1,
)
.unwrap();
let mut expected_bytes = vec![];
let mut expected_flags = vec![];
// First level
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(true);
// Second level
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(false);
expected_flags.push(true);
// Third level
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(false);
expected_flags.push(false);
expected_flags.push(true);
expected_flags.push(true);
// Fourth level
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![42; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_bytes.append(&mut vec![0; 32]);
expected_flags.push(false);
expected_flags.push(false);
expected_flags.push(false);
expected_flags.push(false);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
expected_flags.push(true);
assert_eq!(expected_bytes, grown_bytes);
assert_eq!(expected_flags, grown_flags);
let (shrunk_bytes, shrunk_flags) = shrink_merkle_tree(
&grown_bytes,
&grown_flags,
(big + 1).trailing_zeros() as usize - 1,
(small + 1).trailing_zeros() as usize - 1,
)
.unwrap();
assert_eq!(original_bytes, shrunk_bytes);
assert_eq!(original_flags, shrunk_flags);
}
}

View File

@@ -1,446 +0,0 @@
#![allow(clippy::range_plus_one)] // Minor readability lint requiring structural changes; not worth it.
use super::*;
use crate::merkleize::{merkleize, pad_for_leaf_count};
use int_to_bytes::int_to_bytes32;
/// Provides cached tree hashing for some object implementing `CachedTreeHash`.
///
/// Caching allows for doing minimal internal-node hashing when an object has only been partially
/// changed.
///
/// See the crate root for an example.
#[derive(Debug, PartialEq, Clone)]
pub struct TreeHashCache {
/// Stores the binary-tree in 32-byte chunks.
pub bytes: Vec<u8>,
/// Maps to each chunk of `self.bytes`, indicating if the chunk is dirty.
pub chunk_modified: Vec<bool>,
/// Contains a schema for each variable-length item stored in the cache.
pub schemas: Vec<BTreeSchema>,
/// A counter used during updates.
pub chunk_index: usize,
/// A counter used during updates.
pub schema_index: usize,
}
impl Default for TreeHashCache {
/// Create an empty cache.
///
/// Note: an empty cache is effectively useless, an error will be raised if `self.update` is
/// called.
fn default() -> TreeHashCache {
TreeHashCache {
bytes: vec![],
chunk_modified: vec![],
schemas: vec![],
chunk_index: 0,
schema_index: 0,
}
}
}
impl TreeHashCache {
/// Instantiates a new cache from `item` at a depth of `0`.
///
/// The returned cache is fully-built and will return an accurate tree-hash root.
pub fn new<T>(item: &T) -> Result<Self, Error>
where
T: CachedTreeHash,
{
Self::new_at_depth(item, 0)
}
/// Instantiates a new cache from `item` at the specified `depth`.
///
/// The returned cache is fully-built and will return an accurate tree-hash root.
pub fn new_at_depth<T>(item: &T, depth: usize) -> Result<Self, Error>
where
T: CachedTreeHash,
{
item.new_tree_hash_cache(depth)
}
/// Updates the cache with `item`.
///
/// `item` _must_ be of the same type as the `item` used to build the cache, otherwise an error
/// may be returned.
///
/// After calling `update`, the cache will return an accurate tree-hash root using
/// `self.tree_hash_root()`.
pub fn update<T>(&mut self, item: &T) -> Result<(), Error>
where
T: CachedTreeHash,
{
if self.is_empty() {
Err(Error::CacheNotInitialized)
} else {
self.reset_modifications();
item.update_tree_hash_cache(self)
}
}
/// Builds a new cache for `item`, given `subtrees` contains a `Self` for field/item of `item`.
///
/// Each `subtree` in `subtree` will become a leaf-node of the merkle-tree of `item`.
pub fn from_subtrees<T>(item: &T, subtrees: Vec<Self>, depth: usize) -> Result<Self, Error>
where
T: CachedTreeHash,
{
let overlay = BTreeOverlay::new(item, 0, depth);
// Note how many leaves were provided. If is not a power-of-two, we'll need to pad it out
// later.
let num_provided_leaf_nodes = subtrees.len();
// Allocate enough bytes to store the internal nodes and the leaves and subtrees, then fill
// all the to-be-built internal nodes with zeros and append the leaves and subtrees.
let internal_node_bytes = overlay.num_internal_nodes() * BYTES_PER_CHUNK;
let subtrees_bytes = subtrees.iter().fold(0, |acc, t| acc + t.bytes.len());
let mut bytes = Vec::with_capacity(subtrees_bytes + internal_node_bytes);
bytes.resize(internal_node_bytes, 0);
// Allocate enough bytes to store all the leaves.
let mut leaves = Vec::with_capacity(overlay.num_leaf_nodes() * HASHSIZE);
let mut schemas = Vec::with_capacity(subtrees.len());
if T::tree_hash_type() == TreeHashType::List {
schemas.push(overlay.into());
}
// Iterate through all of the leaves/subtrees, adding their root as a leaf node and then
// concatenating their merkle trees.
for t in subtrees {
leaves.append(&mut t.tree_hash_root()?.to_vec());
let (mut t_bytes, _bools, mut t_schemas) = t.into_components();
bytes.append(&mut t_bytes);
schemas.append(&mut t_schemas);
}
// Pad the leaves to an even power-of-two, using zeros.
pad_for_leaf_count(num_provided_leaf_nodes, &mut bytes);
// Merkleize the leaves, then split the leaf nodes off them. Then, replace all-zeros
// internal nodes created earlier with the internal nodes generated by `merkleize`.
let mut merkleized = merkleize(leaves);
merkleized.split_off(internal_node_bytes);
bytes.splice(0..internal_node_bytes, merkleized);
Ok(Self {
chunk_modified: vec![true; bytes.len() / BYTES_PER_CHUNK],
bytes,
schemas,
chunk_index: 0,
schema_index: 0,
})
}
/// Instantiate a new cache from the pre-built `bytes` where each `self.chunk_modified` will be
/// set to `initial_modified_state`.
///
/// Note: `bytes.len()` must be a multiple of 32
pub fn from_bytes(
bytes: Vec<u8>,
initial_modified_state: bool,
schema: Option<BTreeSchema>,
) -> Result<Self, Error> {
if bytes.len() % BYTES_PER_CHUNK > 0 {
return Err(Error::BytesAreNotEvenChunks(bytes.len()));
}
let schemas = match schema {
Some(schema) => vec![schema],
None => vec![],
};
Ok(Self {
chunk_modified: vec![initial_modified_state; bytes.len() / BYTES_PER_CHUNK],
bytes,
schemas,
chunk_index: 0,
schema_index: 0,
})
}
/// Returns `true` if this cache is empty (i.e., it has never been built for some item).
///
/// Note: an empty cache is effectively useless, an error will be raised if `self.update` is
/// called.
pub fn is_empty(&self) -> bool {
self.chunk_modified.is_empty()
}
/// Return an overlay, built from the schema at `schema_index` with an offset of `chunk_index`.
pub fn get_overlay(
&self,
schema_index: usize,
chunk_index: usize,
) -> Result<BTreeOverlay, Error> {
Ok(self
.schemas
.get(schema_index)
.ok_or_else(|| Error::NoSchemaForIndex(schema_index))?
.clone()
.into_overlay(chunk_index))
}
/// Resets the per-update counters, allowing a new update to start.
///
/// Note: this does _not_ delete the contents of the cache.
pub fn reset_modifications(&mut self) {
// Reset the per-hash counters.
self.chunk_index = 0;
self.schema_index = 0;
for chunk_modified in &mut self.chunk_modified {
*chunk_modified = false;
}
}
/// Replace the schema at `schema_index` with the schema derived from `new_overlay`.
///
/// If the `new_overlay` schema has a different number of internal nodes to the schema at
/// `schema_index`, the cache will be updated to add/remove these new internal nodes.
pub fn replace_overlay(
&mut self,
schema_index: usize,
// TODO: remove chunk index (if possible)
chunk_index: usize,
new_overlay: BTreeOverlay,
) -> Result<BTreeOverlay, Error> {
let old_overlay = self.get_overlay(schema_index, chunk_index)?;
// If the merkle tree required to represent the new list is of a different size to the one
// required for the previous list, then update the internal nodes.
//
// Leaf nodes are not touched, they should be updated externally to this function.
//
// This grows/shrinks the bytes to accommodate the new tree, preserving as much of the tree
// as possible.
if new_overlay.num_internal_nodes() != old_overlay.num_internal_nodes() {
// Get slices of the existing tree from the cache.
let (old_bytes, old_flags) = self
.slices(old_overlay.internal_chunk_range())
.ok_or_else(|| Error::UnableToObtainSlices)?;
let (new_bytes, new_flags) = if new_overlay.num_internal_nodes() == 0 {
// The new tree has zero internal nodes, simply return empty lists.
(vec![], vec![])
} else if old_overlay.num_internal_nodes() == 0 {
// The old tree has zero nodes and the new tree has some nodes. Create new nodes to
// suit.
let nodes = resize::nodes_in_tree_of_height(new_overlay.height() - 1);
(vec![0; nodes * HASHSIZE], vec![true; nodes])
} else if new_overlay.num_internal_nodes() > old_overlay.num_internal_nodes() {
// The new tree is bigger than the old tree.
//
// Grow the internal nodes, preserving any existing nodes.
resize::grow_merkle_tree(
old_bytes,
old_flags,
old_overlay.height() - 1,
new_overlay.height() - 1,
)
.ok_or_else(|| Error::UnableToGrowMerkleTree)?
} else {
// The new tree is smaller than the old tree.
//
// Shrink the internal nodes, preserving any existing nodes.
resize::shrink_merkle_tree(
old_bytes,
old_flags,
old_overlay.height() - 1,
new_overlay.height() - 1,
)
.ok_or_else(|| Error::UnableToShrinkMerkleTree)?
};
// Splice the resized created elements over the existing elements, effectively updating
// the number of stored internal nodes for this tree.
self.splice(old_overlay.internal_chunk_range(), new_bytes, new_flags);
}
let old_schema = std::mem::replace(&mut self.schemas[schema_index], new_overlay.into());
Ok(old_schema.into_overlay(chunk_index))
}
/// Remove all of the child schemas following `schema_index`.
///
/// Schema `a` is a child of schema `b` if `a.depth < b.depth`.
pub fn remove_proceeding_child_schemas(&mut self, schema_index: usize, depth: usize) {
let end = self
.schemas
.iter()
.skip(schema_index)
.position(|o| o.depth <= depth)
.and_then(|i| Some(i + schema_index))
.unwrap_or_else(|| self.schemas.len());
self.schemas.splice(schema_index..end, vec![]);
}
/// Iterate through the internal nodes chunks of `overlay`, updating the chunk with the
/// merkle-root of it's children if either of those children are dirty.
pub fn update_internal_nodes(&mut self, overlay: &BTreeOverlay) -> Result<(), Error> {
for (parent, children) in overlay.internal_parents_and_children().into_iter().rev() {
if self.either_modified(children)? {
self.modify_chunk(parent, &self.hash_children(children)?)?;
}
}
Ok(())
}
/// Returns to the tree-hash root of the cache.
pub fn tree_hash_root(&self) -> Result<&[u8], Error> {
if self.is_empty() {
Err(Error::CacheNotInitialized)
} else {
self.bytes
.get(0..HASHSIZE)
.ok_or_else(|| Error::NoBytesForRoot)
}
}
/// Splices the given `bytes` over `self.bytes` and `bools` over `self.chunk_modified` at the
/// specified `chunk_range`.
pub fn splice(&mut self, chunk_range: Range<usize>, bytes: Vec<u8>, bools: Vec<bool>) {
// Update the `chunk_modified` vec, marking all spliced-in nodes as changed.
self.chunk_modified.splice(chunk_range.clone(), bools);
self.bytes
.splice(node_range_to_byte_range(&chunk_range), bytes);
}
/// If the bytes at `chunk` are not the same as `to`, `self.bytes` is updated and
/// `self.chunk_modified` is set to `true`.
pub fn maybe_update_chunk(&mut self, chunk: usize, to: &[u8]) -> Result<(), Error> {
let start = chunk * BYTES_PER_CHUNK;
let end = start + BYTES_PER_CHUNK;
if !self.chunk_equals(chunk, to)? {
self.bytes
.get_mut(start..end)
.ok_or_else(|| Error::NoModifiedFieldForChunk(chunk))?
.copy_from_slice(to);
self.chunk_modified[chunk] = true;
}
Ok(())
}
/// Returns the slices of `self.bytes` and `self.chunk_modified` at the given `chunk_range`.
fn slices(&self, chunk_range: Range<usize>) -> Option<(&[u8], &[bool])> {
Some((
self.bytes.get(node_range_to_byte_range(&chunk_range))?,
self.chunk_modified.get(chunk_range)?,
))
}
/// Updates `self.bytes` at `chunk` and sets `self.chunk_modified` for the `chunk` to `true`.
pub fn modify_chunk(&mut self, chunk: usize, to: &[u8]) -> Result<(), Error> {
let start = chunk * BYTES_PER_CHUNK;
let end = start + BYTES_PER_CHUNK;
self.bytes
.get_mut(start..end)
.ok_or_else(|| Error::NoBytesForChunk(chunk))?
.copy_from_slice(to);
self.chunk_modified[chunk] = true;
Ok(())
}
/// Returns the bytes at `chunk`.
fn get_chunk(&self, chunk: usize) -> Result<&[u8], Error> {
let start = chunk * BYTES_PER_CHUNK;
let end = start + BYTES_PER_CHUNK;
Ok(self
.bytes
.get(start..end)
.ok_or_else(|| Error::NoModifiedFieldForChunk(chunk))?)
}
/// Returns `true` if the bytes at `chunk` are equal to `other`.
fn chunk_equals(&mut self, chunk: usize, other: &[u8]) -> Result<bool, Error> {
Ok(self.get_chunk(chunk)? == other)
}
/// Returns `true` if `chunk` is dirty.
pub fn changed(&self, chunk: usize) -> Result<bool, Error> {
self.chunk_modified
.get(chunk)
.cloned()
.ok_or_else(|| Error::NoModifiedFieldForChunk(chunk))
}
/// Returns `true` if either of the `children` chunks is dirty.
fn either_modified(&self, children: (usize, usize)) -> Result<bool, Error> {
Ok(self.changed(children.0)? | self.changed(children.1)?)
}
/// Returns the hash of the concatenation of the given `children`.
pub fn hash_children(&self, children: (usize, usize)) -> Result<Vec<u8>, Error> {
let mut child_bytes = Vec::with_capacity(BYTES_PER_CHUNK * 2);
child_bytes.append(&mut self.get_chunk(children.0)?.to_vec());
child_bytes.append(&mut self.get_chunk(children.1)?.to_vec());
Ok(hash(&child_bytes))
}
/// Adds a chunk before and after the given `chunk` range and calls `self.mix_in_length()`.
pub fn add_length_nodes(
&mut self,
chunk_range: Range<usize>,
length: usize,
) -> Result<(), Error> {
self.chunk_modified[chunk_range.start] = true;
let byte_range = node_range_to_byte_range(&chunk_range);
// Add the last node.
self.bytes
.splice(byte_range.end..byte_range.end, vec![0; HASHSIZE]);
self.chunk_modified
.splice(chunk_range.end..chunk_range.end, vec![false]);
// Add the first node.
self.bytes
.splice(byte_range.start..byte_range.start, vec![0; HASHSIZE]);
self.chunk_modified
.splice(chunk_range.start..chunk_range.start, vec![false]);
self.mix_in_length(chunk_range.start + 1..chunk_range.end + 1, length)?;
Ok(())
}
/// Sets `chunk_range.end + 1` equal to the little-endian serialization of `length`. Sets
/// `chunk_range.start - 1` equal to `self.hash_children(chunk_range.start, chunk_range.end + 1)`.
pub fn mix_in_length(&mut self, chunk_range: Range<usize>, length: usize) -> Result<(), Error> {
// Update the length chunk.
self.maybe_update_chunk(chunk_range.end, &int_to_bytes32(length as u64))?;
// Update the mixed-in root if the main root or the length have changed.
let children = (chunk_range.start, chunk_range.end);
if self.either_modified(children)? {
self.modify_chunk(chunk_range.start - 1, &self.hash_children(children)?)?;
}
Ok(())
}
/// Returns `(self.bytes, self.chunk_modified, self.schemas)`.
pub fn into_components(self) -> (Vec<u8>, Vec<bool>, Vec<BTreeSchema>) {
(self.bytes, self.chunk_modified, self.schemas)
}
}
fn node_range_to_byte_range(node_range: &Range<usize>) -> Range<usize> {
node_range.start * HASHSIZE..node_range.end * HASHSIZE
}