Merge pull request #90 from zcash/shardtree-docs
`shardtree`: Improve documentation
This commit is contained in:
commit
da97e6c399
|
@ -1,3 +1,5 @@
|
|||
//! Helpers for inserting many leaves into a tree at once.
|
||||
|
||||
use std::{collections::BTreeMap, fmt, ops::Range, sync::Arc};
|
||||
|
||||
use incrementalmerkletree::{Address, Hashable, Level, Position, Retention};
|
||||
|
@ -31,6 +33,9 @@ impl<
|
|||
/// introduced to the tree, as well as whether or not those newly introduced nodes will need to
|
||||
/// be filled with values in order to produce witnesses for inserted leaves with
|
||||
/// [`Retention::Marked`] retention.
|
||||
///
|
||||
/// This method operates on a single thread. If you have parallelism available, consider using
|
||||
/// [`LocatedPrunableTree::from_iter`] and [`Self::insert_tree`] instead.
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn batch_insert<I: Iterator<Item = (H, Retention<C>)>>(
|
||||
&mut self,
|
||||
|
@ -101,7 +106,6 @@ pub struct BatchInsertionResult<H, C: Ord, I: Iterator<Item = (H, Retention<C>)>
|
|||
pub remainder: I,
|
||||
}
|
||||
|
||||
/// Operations on [`LocatedTree`]s that are annotated with Merkle hashes.
|
||||
impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
||||
/// Append a values from an iterator, beginning at the first available position in the tree.
|
||||
///
|
||||
|
@ -126,7 +130,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
|||
/// the end of the iterator is outside of the subtree's range, the unconsumed part of the
|
||||
/// iterator will be returned as part of the result.
|
||||
///
|
||||
/// Returns `Ok(None)` if the provided iterator is empty, `Ok(Some<BatchInsertionResult>)` if
|
||||
/// Returns `Ok(None)` if the provided iterator is empty, `Ok(Some(BatchInsertionResult))` if
|
||||
/// values were successfully inserted, or an error if the start position provided is outside
|
||||
/// of this tree's position range or if a conflict with an existing subtree root is detected.
|
||||
pub fn batch_insert<C: Clone + Ord, I: Iterator<Item = (H, Retention<C>)>>(
|
||||
|
@ -162,9 +166,10 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
|||
/// This may be used in conjunction with [`ShardTree::insert_tree`] to support
|
||||
/// partially-parallelizable tree construction. Multiple subtrees may be constructed in
|
||||
/// parallel from iterators over (preferably, though not necessarily) disjoint leaf ranges, and
|
||||
/// [`ShardTree::insert_tree`] may be used to insert those subtrees into the `ShardTree` in
|
||||
/// [`ShardTree::insert_tree`] may be used to insert those subtrees into the [`ShardTree`] in
|
||||
/// arbitrary order.
|
||||
///
|
||||
/// # Parameters:
|
||||
/// * `position_range` - The range of leaf positions at which values will be inserted. This
|
||||
/// range is also used to place an upper bound on the number of items that will be consumed
|
||||
/// from the `values` iterator.
|
||||
|
@ -172,7 +177,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
|||
/// in order to construct a witness for a marked node or to make it possible to rewind to a
|
||||
/// checkpointed node may be pruned so long as their address is at less than the specified
|
||||
/// level.
|
||||
/// * `values` The iterator of `(H, [`Retention`])` pairs from which to construct the tree.
|
||||
/// * `values` - The iterator of `(H, Retention)` pairs from which to construct the tree.
|
||||
pub fn from_iter<C: Clone + Ord, I: Iterator<Item = (H, Retention<C>)>>(
|
||||
position_range: Range<Position>,
|
||||
prune_below: Level,
|
||||
|
|
|
@ -1,8 +1,16 @@
|
|||
//! Error types for this crate.
|
||||
|
||||
use std::fmt;
|
||||
use std::ops::Range;
|
||||
|
||||
use incrementalmerkletree::{Address, Position};
|
||||
|
||||
/// The error type for operations on a [`ShardTree`].
|
||||
///
|
||||
/// The parameter `S` is set to [`ShardStore::Error`].
|
||||
///
|
||||
/// [`ShardTree`]: crate::ShardTree
|
||||
/// [`ShardStore::Error`]: crate::ShardStore::Error
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum ShardTreeError<S> {
|
||||
Query(QueryError),
|
||||
|
@ -50,7 +58,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// An error prevented the insertion of values into the subtree.
|
||||
/// Errors which can occur when inserting values into a subtree.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum InsertionError {
|
||||
/// The caller attempted to insert a subtree into a tree that does not contain
|
||||
|
@ -103,7 +111,9 @@ impl fmt::Display for InsertionError {
|
|||
|
||||
impl std::error::Error for InsertionError {}
|
||||
|
||||
/// Errors that may be returned in the process of querying a [`ShardTree`]
|
||||
/// Errors which can occur when querying a [`ShardTree`].
|
||||
///
|
||||
/// [`ShardTree`]: crate::ShardTree
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum QueryError {
|
||||
/// The caller attempted to query the value at an address within a tree that does not contain
|
||||
|
@ -114,6 +124,8 @@ pub enum QueryError {
|
|||
CheckpointPruned,
|
||||
/// It is not possible to compute a root for one or more subtrees because they contain
|
||||
/// [`Node::Nil`] values at positions that cannot be replaced with default hashes.
|
||||
///
|
||||
/// [`Node::Nil`]: crate::Node::Nil
|
||||
TreeIncomplete(Vec<Address>),
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
//! `shardtree` is a space-efficient fixed-depth Merkle tree that supports:
|
||||
//! - Out-of-order insertion.
|
||||
//! - Witnessing of marked leaves.
|
||||
//! - Checkpointing, and state restoration to a checkpoint.
|
||||
|
||||
use core::fmt::Debug;
|
||||
use either::Either;
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
|
@ -61,7 +66,7 @@ impl<
|
|||
}
|
||||
}
|
||||
|
||||
/// Consumes this tree and returns its underlying `ShardStore`.
|
||||
/// Consumes this tree and returns its underlying [`ShardStore`].
|
||||
pub fn into_store(self) -> S {
|
||||
self.store
|
||||
}
|
||||
|
@ -82,7 +87,7 @@ impl<
|
|||
Address::above_position(Self::subtree_level(), pos)
|
||||
}
|
||||
|
||||
pub fn max_subtree_index() -> u64 {
|
||||
fn max_subtree_index() -> u64 {
|
||||
(0x1 << (DEPTH - SHARD_HEIGHT)) - 1
|
||||
}
|
||||
|
||||
|
@ -513,9 +518,9 @@ impl<
|
|||
|
||||
/// Truncates the tree, discarding all information after the checkpoint at the specified depth.
|
||||
///
|
||||
/// This will also discard all checkpoints with depth <= the specified depth. Returns `true`
|
||||
/// if the truncation succeeds or has no effect, or `false` if no checkpoint exists at the
|
||||
/// specified depth.
|
||||
/// This will also discard all checkpoints with depth less than or equal to the specified depth.
|
||||
/// Returns `true` if the truncation succeeds or has no effect, or `false` if no checkpoint
|
||||
/// exists at the specified depth.
|
||||
pub fn truncate_to_depth(
|
||||
&mut self,
|
||||
checkpoint_depth: usize,
|
||||
|
@ -536,9 +541,9 @@ impl<
|
|||
|
||||
/// Truncates the tree, discarding all information after the specified checkpoint.
|
||||
///
|
||||
/// This will also discard all checkpoints with depth <= the specified depth. Returns `true`
|
||||
/// if the truncation succeeds or has no effect, or `false` if no checkpoint exists for the
|
||||
/// specified checkpoint identifier.
|
||||
/// This will also discard all checkpoints with depth less than or equal to the specified depth.
|
||||
/// Returns `true` if the truncation succeeds or has no effect, or `false` if no checkpoint
|
||||
/// exists for the specified checkpoint identifier.
|
||||
pub fn truncate_removing_checkpoint(
|
||||
&mut self,
|
||||
checkpoint_id: &C,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! Helpers for working with trees that support pruning unneeded leaves and branches.
|
||||
|
||||
use std::collections::{BTreeMap, BTreeSet};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -11,6 +13,7 @@ use crate::error::{InsertionError, QueryError};
|
|||
use crate::{LocatedTree, Node, Tree};
|
||||
|
||||
bitflags! {
|
||||
/// Flags storing the [`Retention`] state of a leaf.
|
||||
pub struct RetentionFlags: u8 {
|
||||
/// An leaf with `EPHEMERAL` retention can be pruned as soon as we are certain that it is not part
|
||||
/// of the witness for a leaf with [`CHECKPOINT`] or [`MARKED`] retention.
|
||||
|
@ -61,6 +64,7 @@ impl<C> From<Retention<C>> for RetentionFlags {
|
|||
}
|
||||
}
|
||||
|
||||
/// A [`Tree`] annotated with Merkle hashes.
|
||||
pub type PrunableTree<H> = Tree<Option<Arc<H>>, (H, RetentionFlags)>;
|
||||
|
||||
impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
|
||||
|
@ -98,7 +102,7 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
|
|||
/// a vector of the addresses of `Nil` nodes that inhibited the computation of
|
||||
/// such a root.
|
||||
///
|
||||
/// ### Parameters:
|
||||
/// # Parameters:
|
||||
/// * `truncate_at` An inclusive lower bound on positions in the tree beyond which all leaf
|
||||
/// values will be treated as `Nil`.
|
||||
pub fn root_hash(&self, root_addr: Address, truncate_at: Position) -> Result<H, Vec<Address>> {
|
||||
|
@ -309,6 +313,7 @@ impl<H: Hashable + Clone + PartialEq> PrunableTree<H> {
|
|||
}
|
||||
}
|
||||
|
||||
/// A [`LocatedTree`] annotated with Merkle hashes.
|
||||
pub type LocatedPrunableTree<H> = LocatedTree<Option<Arc<H>>, (H, RetentionFlags)>;
|
||||
|
||||
/// A data structure describing the nature of a [`Node::Nil`] node in the tree that was introduced
|
||||
|
@ -324,7 +329,6 @@ pub struct IncompleteAt {
|
|||
pub required_for_witness: bool,
|
||||
}
|
||||
|
||||
/// Operations on [`LocatedTree`]s that are annotated with Merkle hashes.
|
||||
impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
||||
/// Computes the root hash of this tree, truncated to the given position.
|
||||
///
|
||||
|
@ -782,7 +786,7 @@ impl<H: Hashable + Clone + PartialEq> LocatedPrunableTree<H> {
|
|||
/// Inserts leaves and subtree roots from the provided frontier into this tree, up to the level
|
||||
/// of this tree's root.
|
||||
///
|
||||
/// Returns the updated tree, along with a `LocatedPrunableTree` containing only the remainder
|
||||
/// Returns the updated tree, along with a [`LocatedPrunableTree`] containing only the remainder
|
||||
/// of the frontier's ommers that had addresses at levels greater than the root of this tree.
|
||||
///
|
||||
/// Returns an error in the following cases:
|
||||
|
|
|
@ -1,3 +1,26 @@
|
|||
//! Traits and structs for storing [`ShardTree`]s.
|
||||
//!
|
||||
//! [`ShardTree`]: crate::ShardTree
|
||||
//!
|
||||
//! # Structure
|
||||
//!
|
||||
//! The tree is represented as an ordered collection of fixed-depth subtrees, or "shards".
|
||||
//! Each shard is a [`LocatedPrunableTree`]. The roots of the shards form the leaves in
|
||||
//! the "cap", which is a [`PrunableTree`].
|
||||
//!
|
||||
//! ```text
|
||||
//! Level
|
||||
//! 3 root \
|
||||
//! / \ |
|
||||
//! / \ |
|
||||
//! 2 / \ } cap
|
||||
//! / \ / \ |
|
||||
//! / \ / \ |
|
||||
//! 1 A B C D / \
|
||||
//! / \ / \ / \ / \ } shards
|
||||
//! 0 /\ /\ /\ /\ /\ /\ /\ /\ /
|
||||
//! ```
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use incrementalmerkletree::{Address, Position};
|
||||
|
@ -7,12 +30,19 @@ use crate::{LocatedPrunableTree, PrunableTree};
|
|||
pub mod caching;
|
||||
pub mod memory;
|
||||
|
||||
/// A capability for storage of fragment subtrees of the `ShardTree` type.
|
||||
/// A capability for storage of fragment subtrees of the [`ShardTree`] type.
|
||||
///
|
||||
/// All fragment subtrees must have roots at level `SHARD_HEIGHT`
|
||||
/// All fragment subtrees must have roots at level `SHARD_HEIGHT`.
|
||||
///
|
||||
/// [`ShardTree`]: crate::ShardTree
|
||||
pub trait ShardStore {
|
||||
/// The type used for leaves and nodes in the tree.
|
||||
type H;
|
||||
|
||||
/// The type used to identify checkpointed positions in the tree.
|
||||
type CheckpointId;
|
||||
|
||||
/// The error type for operations on this store.
|
||||
type Error: std::error::Error;
|
||||
|
||||
/// Returns the subtree at the given root address, if any such subtree exists.
|
||||
|
@ -221,6 +251,9 @@ pub enum TreeState {
|
|||
AtPosition(Position),
|
||||
}
|
||||
|
||||
/// The information required to save the state of a [`ShardTree`] at some [`Position`].
|
||||
///
|
||||
/// [`ShardTree`]: crate::ShardTree
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Checkpoint {
|
||||
tree_state: TreeState,
|
||||
|
|
|
@ -8,6 +8,9 @@ use incrementalmerkletree::Address;
|
|||
use super::{Checkpoint, ShardStore};
|
||||
use crate::{LocatedPrunableTree, LocatedTree, Node, PrunableTree, Tree};
|
||||
|
||||
/// An implementation of [`ShardStore`] that stores all state in memory.
|
||||
///
|
||||
/// State is not persisted anywhere, and will be lost when the struct is dropped.
|
||||
#[derive(Debug)]
|
||||
pub struct MemoryShardStore<H, C: Ord> {
|
||||
shards: Vec<LocatedPrunableTree<H>>,
|
||||
|
@ -16,6 +19,7 @@ pub struct MemoryShardStore<H, C: Ord> {
|
|||
}
|
||||
|
||||
impl<H, C: Ord> MemoryShardStore<H, C> {
|
||||
/// Constructs a new empty `MemoryShardStore`.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
shards: vec![],
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
//! The core tree types.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -22,7 +24,7 @@ pub enum Node<C, A, V> {
|
|||
impl<C, A, V> Node<C, A, V> {
|
||||
/// Returns whether or not this is the `Nil` tree.
|
||||
///
|
||||
/// This is useful for cases where the compiler can automatically dereference an `Rc`, where
|
||||
/// This is useful for cases where the compiler can automatically dereference an [`Arc`], where
|
||||
/// one would otherwise need additional ceremony to make an equality check.
|
||||
pub fn is_nil(&self) -> bool {
|
||||
matches!(self, Node::Nil)
|
||||
|
@ -37,6 +39,7 @@ impl<C, A, V> Node<C, A, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the annotation, if this is a parent node.
|
||||
pub fn annotation(&self) -> Option<&A> {
|
||||
match self {
|
||||
Node::Parent { ann, .. } => Some(ann),
|
||||
|
@ -45,7 +48,7 @@ impl<C, A, V> Node<C, A, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Replaces the annotation on this node, if it is a `Node::Parent`; otherwise
|
||||
/// Replaces the annotation on this node, if it is a [`Node::Parent`]; otherwise
|
||||
/// returns this node unaltered.
|
||||
pub fn reannotate(self, ann: A) -> Self {
|
||||
match self {
|
||||
|
@ -56,6 +59,7 @@ impl<C, A, V> Node<C, A, V> {
|
|||
}
|
||||
|
||||
impl<'a, C: Clone, A: Clone, V: Clone> Node<C, &'a A, &'a V> {
|
||||
/// Maps a `Node<C, &A, &V>` to a `Node<C, A, V>` by cloning the contents of the node.
|
||||
pub fn cloned(&self) -> Node<C, A, V> {
|
||||
match self {
|
||||
Node::Parent { ann, left, right } => Node::Parent {
|
||||
|
@ -83,14 +87,17 @@ impl<A, V> Deref for Tree<A, V> {
|
|||
}
|
||||
|
||||
impl<A, V> Tree<A, V> {
|
||||
/// Constructs the empty tree.
|
||||
pub fn empty() -> Self {
|
||||
Tree(Node::Nil)
|
||||
}
|
||||
|
||||
/// Constructs a tree containing a single leaf.
|
||||
pub fn leaf(value: V) -> Self {
|
||||
Tree(Node::Leaf { value })
|
||||
}
|
||||
|
||||
/// Constructs a tree containing a pair of leaves.
|
||||
pub fn parent(ann: A, left: Self, right: Self) -> Self {
|
||||
Tree(Node::Parent {
|
||||
ann,
|
||||
|
@ -99,12 +106,13 @@ impl<A, V> Tree<A, V> {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns `true` if the tree has no leaves.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.0.is_nil()
|
||||
}
|
||||
|
||||
/// Replaces the annotation at the root of the tree, if the root is a `Node::Parent`; otherwise
|
||||
/// returns this tree unaltered.
|
||||
/// Replaces the annotation at the root of the tree, if the root is a [`Node::Parent`];
|
||||
/// otherwise returns this tree unaltered.
|
||||
pub fn reannotate_root(self, ann: A) -> Self {
|
||||
Tree(self.0.reannotate(ann))
|
||||
}
|
||||
|
@ -175,7 +183,7 @@ impl<A, V> LocatedTree<A, V> {
|
|||
}
|
||||
|
||||
/// Returns a new [`LocatedTree`] with the provided value replacing the annotation of its root
|
||||
/// node, if that root node is a `Node::Parent`. Otherwise .
|
||||
/// node, if that root node is a [`Node::Parent`]. Otherwise returns this tree unaltered.
|
||||
pub fn reannotate_root(self, value: A) -> Self {
|
||||
LocatedTree {
|
||||
root_addr: self.root_addr,
|
||||
|
@ -189,7 +197,7 @@ impl<A, V> LocatedTree<A, V> {
|
|||
self.root.incomplete_nodes(self.root_addr)
|
||||
}
|
||||
|
||||
/// Returns the maximum position at which a non-Nil leaf has been observed in the tree.
|
||||
/// Returns the maximum position at which a non-`Nil` leaf has been observed in the tree.
|
||||
///
|
||||
/// Note that no actual leaf value may exist at this position, as it may have previously been
|
||||
/// pruned.
|
||||
|
|
Loading…
Reference in New Issue