penumbra_tct/
internal.rs

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

pub mod hash;
pub mod height;
pub mod interface;
pub mod path;
pub mod proof;
pub mod three;

mod insert;

#[allow(missing_docs)]
pub mod frontier {
    //! [`Frontier`] things can be inserted into and updated, always representing the rightmost
    //! (most recently inserted) element of a tree.
    //!
    //! In sketch: the structure of a single [`Tier`] contains eight [`Node`]s, the bottom-most of
    //! which contains a [`Leaf`]. Alternatively, a tier can be a summarized [`Hash`] of what its
    //! contents _would be_, and contain nothing at all besides this hash.
    //!
    //! At every level of a [`frontier::Tier`](Tier), the rightmost child of a
    //! [`frontier::Node`](Node) is a [`frontier::Node`](Node); all leftward siblings are
    //! [`complete::Node`](super::complete::Node)s. When the child of a [`frontier::Node`](Node)
    //! becomes entirely full (all its possible leaves are inserted), it is transformed into a
    //! [`complete::Node`](super::complete::Node) and appended to the list of complete siblings of
    //! its parent, thus shifting the frontier rightwards.
    //!
    //! At any given time, the frontier is always fully materialized; no node within it is ever
    //! summarized as a hash. It is at the point when a [`frontier::Node`](Node) becomes full and is
    //! then finalized into a [`complete::Node`](super::complete::Node) that it is pruned, if it
    //! contains no witnessed children.
    //!
    //! At the tip of the frontier, however deeply nested (perhaps within multiple [`Tier`]s), there
    //! is a single [`Item`], which is either a [`Commitment`](crate::Commitment) or a hash of one.
    //! Commitments can be inserted either with the intent to remember them, or with the intent to
    //! immediately forget them; this determines whether the [`Item`] is a commitment or merely its
    //! hash.
    pub(crate) use super::interface::OutOfOrder;
    #[doc(inline)]
    pub use super::interface::{Focus, Forget, Frontier, Full, GetPosition};
    pub(super) mod item;
    pub(super) mod leaf;
    pub(super) mod node;
    pub(super) mod tier;
    pub(super) mod top;
    pub use super::insert::{Insert, InsertMut};
    #[doc(inline)]
    pub use {
        item::Item,
        leaf::Leaf,
        node::Node,
        tier::Tier,
        top::{Top, TrackForgotten},
    };
}

#[allow(missing_docs)]
pub mod complete {
    //! [`Complete`] things are sparse representations of only the data that was inserted using
    //! [`Witness::Keep`](crate::Witness::Keep), with the data that was inserted using
    //! [`Witness::Forget`](crate::Witness::Forget) being pruned eagerly.
    //!
    //! The structure of a single [`Tier`] contains eight levels of [`Node`]s, the bottom-most level
    //! of which contains [`Leaf`]s. Alternatively, a tier can be a summarized [`Hash`] of what its
    //! contents _would be_, and contain nothing at all besides this hash.
    //!
    //! In the internal levels of a [`complete::Tier`](Tier) are eight levels of
    //! [`complete::Node`](Node)s, each of which may have between one and four children. If a node
    //! does not have a given child, then it instead stores the hash that child used to have, when
    //! it existed. Empty nodes (all of whose children would be hashes) are unrepresentable, and
    //! instead their own hash is immediately stored in their parent node when their last child is
    //! forgotten.
    //!
    //! At the bottom of the bottom-most tier (perhaps at the bottom of multiple [`Tier`]s), there
    //! are [`Item`]s, each of which is merely a wrapper for a single
    //! [`Commitment`](crate::Commitment).
    pub(crate) use super::interface::OutOfOrderOwned;
    #[doc(inline)]
    pub use super::interface::{Complete, ForgetOwned};
    pub(super) mod item;
    pub(super) mod leaf;
    pub(super) mod node;
    pub(super) mod tier;
    pub(super) mod top;
    #[doc(inline)]
    pub use {
        item::Item,
        leaf::Leaf,
        node::Node,
        tier::{Nested, Tier},
        top::Top,
    };
}

pub(crate) use interface::UncheckedSetHash;