penumbra_sdk_tct/
internal.rs

1//! The internal implementation of the tree, exposed here for documentation.
2//!
3//! This module and its submodules should not be expected to follow semantic versioning.
4//!
5//! ## Structure of Implementation
6//!
7//! The tiered commitment tree is not accessed directly _as a tree_; rather, the
8//! [`Tree`](crate::Tree), [`epoch::Builder`](crate::builder::epoch::Builder),
9//! [`epoch::Finalized`](crate::builder::epoch::Finalized),
10//! [`block::Builder`](crate::builder::block::Builder), and
11//! [`block::Finalized`](crate::builder::block::Finalized) structs from the top level of the crate
12//! contain a tree together with a hashmap which maps commitments to their corresponding index
13//! within the tree. This `internal` module and all its submodules concern themselves solely with
14//! the implementation of the tree itself, wherein commitments and their authentication paths are
15//! accessed by index. The surrounding pieces of the crate make use of the internal-facing API
16//! exposed by this module to implement an external API specific to the three-tiered
17//! tree/epoch/block commitment tree required by Penumbra.
18//!
19//! The tiered commitment tree has a very specific structure, and in this implementation we make
20//! strong Rust's type system to enforce that structure. In particular, we ensure that internal
21//! nodes are non-empty, and that leaves occur only at the bottom-most level of the tree. A
22//! consequence of this is that it is unrepresentable to build a tree which fails to be pruned of
23//! internal nodes holding no witnessed leaves, or whose leaves are of mismatched depth. A lesser
24//! benefit of this design is that recursive methods on trees can be monomorphized and therefore
25//! potentially inlined to the height of tree they are called upon, avoiding ever performing runtime
26//! checks about the tree structure (there no need to check whether a particular part of the tree is
27//! a leaf, internal node, etc., because this is statically known).
28//!
29//! This structural guarantee is achieved by defining these tree structures as _nested generic
30//! types_, and defining their methods as _recursive trait implementations_ (rather than as
31//! inductive non-generic enumeration types with recursive methods). These traits are all defined in
32//! [`interface`], but they are re-exported from [`frontier`] and [`complete`] as relevant.
33//!
34//! ## Tiers of Nodes of Leaves of Items: Frontier and Complete
35//!
36//! The primary exports of this module is the type [`frontier::Tier`]. It is in terms of this type
37//! that the [`Tree`](crate::Tree) and [`builder`](crate::builder) structs are defined: a
38//! [`Tree`](crate::Tree) is a `Top<Tier<Tier<Item>>>`, an epoch is a `Top<Tier<Item>>`, and a
39//! `Block` is a `Top<Item>` (each with a managed index of commitments alongside).
40//!
41//! Internally, a [`Tier`](frontier::Tier) is a quadtree where every internal node is annotated with
42//! the hash of its children, into which leaves (all at depth 8) are inserted in left to right
43//! order. The "frontier" represents the path from the root to the rightmost leaf, at every level
44//! containing any leftward siblings of a given frontier node, each of which is a [`complete`] tree
45//! which stores the finalized bulk of the items inserted into the tree. As new leaves are created,
46//! the frontier zig-zags rightwards, pushing finalized portions of itself into the leftward
47//! complete tree.
48//!
49//! All [`Tier`](frontier::Tier)s must contain at least one child, and may be either unfinalized or
50//! finalized; a [`Top`](frontier::Top) is like a tier, but it may be empty, and may not be
51//! finalized. Stacking a [`Top`](frontier::Top) on top of [`Tier`](frontier::Tier)s allows there to
52//! be a canonical representation for empty trees, and prevents the illegal state of a finalized
53//! top-level tree.
54//!
55//! As described above, a variety of recursively defined traits are used to define the behavior of
56//! trees. The [`Frontier`](frontier::Frontier) trait defines the operations possible on a frontier
57//! of a tree, while the [`Focus`](frontier::Focus) trait defines how to operate over the tip of a
58//! frontier, and the [`Forget`](frontier::Forget) trait defines how to remove witnessed leaves from
59//! a frontier.
60//!
61//! While the frontier changes on every inserted leaf, within the complete portion of the tree the
62//! only changes that occur are when leaves are forgotten and their containing nodes pruned. As a
63//! result, the traits exposed by the [`complete`] module are merely
64//! [`Complete`](complete::Complete), which is a marker trait used to ensure that every
65//! [`Frontier`](frontier::Frontier) type is paired with a unique corresponding type of complete
66//! tree, and the [`ForgetOwned`](complete::ForgetOwned) trait, which defines an equivalent to
67//! [`frontier::Forget`] that is applicable to the by-value usage pattern of complete trees.
68
69pub mod hash;
70pub mod height;
71pub mod interface;
72pub mod path;
73pub mod proof;
74pub mod three;
75
76mod insert;
77
78#[allow(missing_docs)]
79pub mod frontier {
80    //! [`Frontier`] things can be inserted into and updated, always representing the rightmost
81    //! (most recently inserted) element of a tree.
82    //!
83    //! In sketch: the structure of a single [`Tier`] contains eight [`Node`]s, the bottom-most of
84    //! which contains a [`Leaf`]. Alternatively, a tier can be a summarized [`Hash`] of what its
85    //! contents _would be_, and contain nothing at all besides this hash.
86    //!
87    //! At every level of a [`frontier::Tier`](Tier), the rightmost child of a
88    //! [`frontier::Node`](Node) is a [`frontier::Node`](Node); all leftward siblings are
89    //! [`complete::Node`](super::complete::Node)s. When the child of a [`frontier::Node`](Node)
90    //! becomes entirely full (all its possible leaves are inserted), it is transformed into a
91    //! [`complete::Node`](super::complete::Node) and appended to the list of complete siblings of
92    //! its parent, thus shifting the frontier rightwards.
93    //!
94    //! At any given time, the frontier is always fully materialized; no node within it is ever
95    //! summarized as a hash. It is at the point when a [`frontier::Node`](Node) becomes full and is
96    //! then finalized into a [`complete::Node`](super::complete::Node) that it is pruned, if it
97    //! contains no witnessed children.
98    //!
99    //! At the tip of the frontier, however deeply nested (perhaps within multiple [`Tier`]s), there
100    //! is a single [`Item`], which is either a [`Commitment`](crate::Commitment) or a hash of one.
101    //! Commitments can be inserted either with the intent to remember them, or with the intent to
102    //! immediately forget them; this determines whether the [`Item`] is a commitment or merely its
103    //! hash.
104    pub(crate) use super::interface::OutOfOrder;
105    #[doc(inline)]
106    pub use super::interface::{Focus, Forget, Frontier, Full, GetPosition};
107    pub(super) mod item;
108    pub(super) mod leaf;
109    pub(super) mod node;
110    pub(super) mod tier;
111    pub(super) mod top;
112    pub use super::insert::{Insert, InsertMut};
113    #[doc(inline)]
114    pub use {
115        item::Item,
116        leaf::Leaf,
117        node::Node,
118        tier::Tier,
119        top::{Top, TrackForgotten},
120    };
121}
122
123#[allow(missing_docs)]
124pub mod complete {
125    //! [`Complete`] things are sparse representations of only the data that was inserted using
126    //! [`Witness::Keep`](crate::Witness::Keep), with the data that was inserted using
127    //! [`Witness::Forget`](crate::Witness::Forget) being pruned eagerly.
128    //!
129    //! The structure of a single [`Tier`] contains eight levels of [`Node`]s, the bottom-most level
130    //! of which contains [`Leaf`]s. Alternatively, a tier can be a summarized [`Hash`] of what its
131    //! contents _would be_, and contain nothing at all besides this hash.
132    //!
133    //! In the internal levels of a [`complete::Tier`](Tier) are eight levels of
134    //! [`complete::Node`](Node)s, each of which may have between one and four children. If a node
135    //! does not have a given child, then it instead stores the hash that child used to have, when
136    //! it existed. Empty nodes (all of whose children would be hashes) are unrepresentable, and
137    //! instead their own hash is immediately stored in their parent node when their last child is
138    //! forgotten.
139    //!
140    //! At the bottom of the bottom-most tier (perhaps at the bottom of multiple [`Tier`]s), there
141    //! are [`Item`]s, each of which is merely a wrapper for a single
142    //! [`Commitment`](crate::Commitment).
143    pub(crate) use super::interface::OutOfOrderOwned;
144    #[doc(inline)]
145    pub use super::interface::{Complete, ForgetOwned};
146    pub(super) mod item;
147    pub(super) mod leaf;
148    pub(super) mod node;
149    pub(super) mod tier;
150    pub(super) mod top;
151    #[doc(inline)]
152    pub use {
153        item::Item,
154        leaf::Leaf,
155        node::Node,
156        tier::{Nested, Tier},
157        top::Top,
158    };
159}
160
161pub(crate) use interface::UncheckedSetHash;