Skip to main content

zebra_chain/
subtree.rs

1//! Struct representing Sapling/Orchard note commitment subtrees
2
3use std::{fmt, num::TryFromIntError};
4
5use serde::{Deserialize, Serialize};
6
7use crate::block::Height;
8
9#[cfg(any(test, feature = "proptest-impl"))]
10use proptest_derive::Arbitrary;
11
12/// Height at which Zebra tracks subtree roots
13pub const TRACKED_SUBTREE_HEIGHT: u8 = 16;
14
15/// A note commitment subtree index, used to identify a subtree in a shielded pool.
16/// Also used to count subtrees.
17#[derive(
18    Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, schemars::JsonSchema,
19)]
20#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
21#[serde(transparent)]
22pub struct NoteCommitmentSubtreeIndex(pub u16);
23
24impl fmt::Display for NoteCommitmentSubtreeIndex {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        f.write_str(&self.0.to_string())
27    }
28}
29
30impl From<u16> for NoteCommitmentSubtreeIndex {
31    fn from(value: u16) -> Self {
32        Self(value)
33    }
34}
35
36impl TryFrom<u64> for NoteCommitmentSubtreeIndex {
37    type Error = TryFromIntError;
38
39    fn try_from(value: u64) -> Result<Self, Self::Error> {
40        u16::try_from(value).map(Self)
41    }
42}
43
44// If we want to automatically convert NoteCommitmentSubtreeIndex to the generic integer literal
45// type, we can only implement conversion into u64. (Or u16, but not both.)
46impl From<NoteCommitmentSubtreeIndex> for u64 {
47    fn from(value: NoteCommitmentSubtreeIndex) -> Self {
48        value.0.into()
49    }
50}
51
52// TODO:
53// - consider defining sapling::SubtreeRoot and orchard::SubtreeRoot types or type wrappers,
54//   to avoid type confusion between the leaf Node and subtree root types.
55
56/// Subtree root of Sapling or Orchard note commitment tree,
57/// with its associated block height and subtree index.
58#[derive(Copy, Clone, Debug, Eq, PartialEq)]
59#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
60pub struct NoteCommitmentSubtree<SubtreeRoot> {
61    /// Index of this subtree
62    pub index: NoteCommitmentSubtreeIndex,
63    /// Root of this subtree.
64    pub root: SubtreeRoot,
65    /// End boundary of this subtree, the block height of its last leaf.
66    pub end_height: Height,
67}
68
69impl<SubtreeRoot> NoteCommitmentSubtree<SubtreeRoot> {
70    /// Creates new [`NoteCommitmentSubtree`]
71    pub fn new(
72        index: impl Into<NoteCommitmentSubtreeIndex>,
73        end_height: Height,
74        root: SubtreeRoot,
75    ) -> Self {
76        let index = index.into();
77        Self {
78            index,
79            end_height,
80            root,
81        }
82    }
83
84    /// Converts struct to [`NoteCommitmentSubtreeData`].
85    pub fn into_data(self) -> NoteCommitmentSubtreeData<SubtreeRoot> {
86        NoteCommitmentSubtreeData::new(self.end_height, self.root)
87    }
88}
89
90/// Subtree root of Sapling or Orchard note commitment tree, with block height, but without the subtree index.
91/// Used for database key-value serialization, where the subtree index is the key, and this struct is the value.
92#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
93#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
94pub struct NoteCommitmentSubtreeData<SubtreeRoot> {
95    /// Merkle root of the 2^16-leaf subtree.
96    pub root: SubtreeRoot,
97
98    /// Height of the block containing the note that completed this subtree.
99    pub end_height: Height,
100}
101
102impl<SubtreeRoot> NoteCommitmentSubtreeData<SubtreeRoot> {
103    /// Creates new [`NoteCommitmentSubtreeData`]
104    pub fn new(end_height: Height, root: SubtreeRoot) -> Self {
105        Self { end_height, root }
106    }
107
108    /// Creates new [`NoteCommitmentSubtree`] from a [`NoteCommitmentSubtreeData`] and index
109    pub fn with_index(
110        self,
111        index: impl Into<NoteCommitmentSubtreeIndex>,
112    ) -> NoteCommitmentSubtree<SubtreeRoot> {
113        NoteCommitmentSubtree::new(index, self.end_height, self.root)
114    }
115}