2023-08-27 17:48:16 -07:00
|
|
|
//! Struct representing Sapling/Orchard note commitment subtrees
|
|
|
|
|
2023-09-19 07:49:36 -07:00
|
|
|
use std::{fmt, num::TryFromIntError};
|
2023-09-05 09:52:06 -07:00
|
|
|
|
2023-09-03 15:18:41 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
use crate::block::Height;
|
2023-08-27 17:48:16 -07:00
|
|
|
|
2023-08-28 01:50:31 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
|
|
use proptest_derive::Arbitrary;
|
|
|
|
|
2023-08-27 17:48:16 -07:00
|
|
|
/// Height at which Zebra tracks subtree roots
|
|
|
|
pub const TRACKED_SUBTREE_HEIGHT: u8 = 16;
|
|
|
|
|
2023-09-03 15:18:41 -07:00
|
|
|
/// A note commitment subtree index, used to identify a subtree in a shielded pool.
|
|
|
|
/// Also used to count subtrees.
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
|
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
|
|
|
#[serde(transparent)]
|
2023-08-27 17:48:16 -07:00
|
|
|
pub struct NoteCommitmentSubtreeIndex(pub u16);
|
|
|
|
|
2023-09-19 07:49:36 -07:00
|
|
|
impl fmt::Display for NoteCommitmentSubtreeIndex {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
f.write_str(&self.0.to_string())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-27 17:48:16 -07:00
|
|
|
impl From<u16> for NoteCommitmentSubtreeIndex {
|
|
|
|
fn from(value: u16) -> Self {
|
|
|
|
Self(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-05 09:52:06 -07:00
|
|
|
impl TryFrom<u64> for NoteCommitmentSubtreeIndex {
|
|
|
|
type Error = TryFromIntError;
|
|
|
|
|
|
|
|
fn try_from(value: u64) -> Result<Self, Self::Error> {
|
|
|
|
u16::try_from(value).map(Self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we want to automatically convert NoteCommitmentSubtreeIndex to the generic integer literal
|
|
|
|
// type, we can only implement conversion into u64. (Or u16, but not both.)
|
|
|
|
impl From<NoteCommitmentSubtreeIndex> for u64 {
|
|
|
|
fn from(value: NoteCommitmentSubtreeIndex) -> Self {
|
|
|
|
value.0.into()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-03 15:18:41 -07:00
|
|
|
// TODO:
|
|
|
|
// - consider defining sapling::SubtreeRoot and orchard::SubtreeRoot types or type wrappers,
|
|
|
|
// to avoid type confusion between the leaf Node and subtree root types.
|
|
|
|
|
2023-08-27 17:48:16 -07:00
|
|
|
/// Subtree root of Sapling or Orchard note commitment tree,
|
|
|
|
/// with its associated block height and subtree index.
|
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
2023-09-03 15:18:41 -07:00
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
2023-10-30 13:06:54 -07:00
|
|
|
pub struct NoteCommitmentSubtree<SubtreeRoot> {
|
2023-08-27 17:48:16 -07:00
|
|
|
/// Index of this subtree
|
|
|
|
pub index: NoteCommitmentSubtreeIndex,
|
|
|
|
/// Root of this subtree.
|
2023-10-30 13:06:54 -07:00
|
|
|
pub root: SubtreeRoot,
|
2023-09-03 15:18:41 -07:00
|
|
|
/// End boundary of this subtree, the block height of its last leaf.
|
2023-10-30 13:06:54 -07:00
|
|
|
pub end_height: Height,
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
|
|
|
|
2023-10-30 13:06:54 -07:00
|
|
|
impl<SubtreeRoot> NoteCommitmentSubtree<SubtreeRoot> {
|
2023-08-27 17:48:16 -07:00
|
|
|
/// Creates new [`NoteCommitmentSubtree`]
|
2023-10-30 13:06:54 -07:00
|
|
|
pub fn new(
|
|
|
|
index: impl Into<NoteCommitmentSubtreeIndex>,
|
|
|
|
end_height: Height,
|
|
|
|
root: SubtreeRoot,
|
|
|
|
) -> Self {
|
2023-08-27 17:48:16 -07:00
|
|
|
let index = index.into();
|
2023-10-30 13:06:54 -07:00
|
|
|
Self {
|
|
|
|
index,
|
|
|
|
end_height,
|
|
|
|
root,
|
|
|
|
}
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
2023-08-28 01:50:31 -07:00
|
|
|
|
|
|
|
/// Converts struct to [`NoteCommitmentSubtreeData`].
|
2023-10-30 13:06:54 -07:00
|
|
|
pub fn into_data(self) -> NoteCommitmentSubtreeData<SubtreeRoot> {
|
|
|
|
NoteCommitmentSubtreeData::new(self.end_height, self.root)
|
2023-08-28 01:50:31 -07:00
|
|
|
}
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Subtree root of Sapling or Orchard note commitment tree, with block height, but without the subtree index.
|
|
|
|
/// Used for database key-value serialization, where the subtree index is the key, and this struct is the value.
|
2023-09-03 15:18:41 -07:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, serde::Serialize)]
|
2023-08-28 01:50:31 -07:00
|
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
2023-10-30 13:06:54 -07:00
|
|
|
pub struct NoteCommitmentSubtreeData<SubtreeRoot> {
|
2023-09-03 15:18:41 -07:00
|
|
|
/// Merkle root of the 2^16-leaf subtree.
|
2023-10-30 13:06:54 -07:00
|
|
|
pub root: SubtreeRoot,
|
2023-09-03 15:18:41 -07:00
|
|
|
|
|
|
|
/// Height of the block containing the note that completed this subtree.
|
2023-10-30 13:06:54 -07:00
|
|
|
pub end_height: Height,
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
|
|
|
|
2023-10-30 13:06:54 -07:00
|
|
|
impl<SubtreeRoot> NoteCommitmentSubtreeData<SubtreeRoot> {
|
2023-08-27 17:48:16 -07:00
|
|
|
/// Creates new [`NoteCommitmentSubtreeData`]
|
2023-10-30 13:06:54 -07:00
|
|
|
pub fn new(end_height: Height, root: SubtreeRoot) -> Self {
|
|
|
|
Self { end_height, root }
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates new [`NoteCommitmentSubtree`] from a [`NoteCommitmentSubtreeData`] and index
|
|
|
|
pub fn with_index(
|
|
|
|
self,
|
|
|
|
index: impl Into<NoteCommitmentSubtreeIndex>,
|
2023-10-30 13:06:54 -07:00
|
|
|
) -> NoteCommitmentSubtree<SubtreeRoot> {
|
|
|
|
NoteCommitmentSubtree::new(index, self.end_height, self.root)
|
2023-08-27 17:48:16 -07:00
|
|
|
}
|
|
|
|
}
|