0. Add note commitment subtree types to zebra-chain (#7371)
* zebra-chain changes from the subtree-boundaries branch ```sh git checkout -b subtree-boundaries-zebra-chain main git checkout origin/subtree-boundaries zebra-chain git commit ``` * Temporarily populate new subtree fields with None - for revert This temporary commit needs to be reverted in the next PR. * Applies suggestions from code review * removes from_repr_unchecked methods * simplifies loop --------- Co-authored-by: arya2 <aryasolhi@gmail.com>
This commit is contained in:
parent
67e3c26190
commit
62258d51da
|
@ -44,6 +44,7 @@ pub mod sapling;
|
||||||
pub mod serialization;
|
pub mod serialization;
|
||||||
pub mod shutdown;
|
pub mod shutdown;
|
||||||
pub mod sprout;
|
pub mod sprout;
|
||||||
|
pub mod subtree;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
pub mod transparent;
|
pub mod transparent;
|
||||||
pub mod value_balance;
|
pub mod value_balance;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitvec::prelude::*;
|
use bitvec::prelude::*;
|
||||||
use bridgetree;
|
use bridgetree::{self, NonEmptyFrontier};
|
||||||
use halo2::pasta::{group::ff::PrimeField, pallas};
|
use halo2::pasta::{group::ff::PrimeField, pallas};
|
||||||
use incrementalmerkletree::Hashable;
|
use incrementalmerkletree::Hashable;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -27,8 +27,11 @@ use zcash_primitives::merkle_tree::{write_commitment_tree, HashSer};
|
||||||
|
|
||||||
use super::sinsemilla::*;
|
use super::sinsemilla::*;
|
||||||
|
|
||||||
use crate::serialization::{
|
use crate::{
|
||||||
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
serialization::{
|
||||||
|
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
||||||
|
},
|
||||||
|
subtree::TRACKED_SUBTREE_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
@ -170,6 +173,25 @@ impl ZcashDeserialize for Root {
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Node(pallas::Base);
|
pub struct Node(pallas::Base);
|
||||||
|
|
||||||
|
impl Node {
|
||||||
|
/// Calls `to_repr()` on inner value.
|
||||||
|
pub fn to_repr(&self) -> [u8; 32] {
|
||||||
|
self.0.to_repr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Node {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Option::<pallas::Base>::from(pallas::Base::from_repr(
|
||||||
|
bytes.try_into().map_err(|_| "wrong byte slice len")?,
|
||||||
|
))
|
||||||
|
.map(Node)
|
||||||
|
.ok_or("invalid Pallas field element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Required to convert [`NoteCommitmentTree`] into [`SerializedTree`].
|
/// Required to convert [`NoteCommitmentTree`] into [`SerializedTree`].
|
||||||
///
|
///
|
||||||
/// Zebra stores Orchard note commitment trees as [`Frontier`][1]s while the
|
/// Zebra stores Orchard note commitment trees as [`Frontier`][1]s while the
|
||||||
|
@ -317,6 +339,32 @@ impl NoteCommitmentTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the most recently appended leaf completes the subtree
|
||||||
|
pub fn is_complete_subtree(tree: &NonEmptyFrontier<Node>) -> bool {
|
||||||
|
tree.position()
|
||||||
|
.is_complete_subtree(TRACKED_SUBTREE_HEIGHT.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns subtree address at [`TRACKED_SUBTREE_HEIGHT`]
|
||||||
|
pub fn subtree_address(tree: &NonEmptyFrontier<Node>) -> incrementalmerkletree::Address {
|
||||||
|
incrementalmerkletree::Address::above_position(
|
||||||
|
TRACKED_SUBTREE_HEIGHT.into(),
|
||||||
|
tree.position(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns subtree index and root if the most recently appended leaf completes the subtree
|
||||||
|
#[allow(clippy::unwrap_in_result)]
|
||||||
|
pub fn completed_subtree_index_and_root(&self) -> Option<(u16, Node)> {
|
||||||
|
let value = self.inner.value()?;
|
||||||
|
Self::is_complete_subtree(value).then_some(())?;
|
||||||
|
let address = Self::subtree_address(value);
|
||||||
|
let index = address.index().try_into().expect("should fit in u16");
|
||||||
|
let root = value.root(Some(TRACKED_SUBTREE_HEIGHT.into()));
|
||||||
|
|
||||||
|
Some((index, root))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the current root of the tree, used as an anchor in Orchard
|
/// Returns the current root of the tree, used as an anchor in Orchard
|
||||||
/// shielded transactions.
|
/// shielded transactions.
|
||||||
pub fn root(&self) -> Root {
|
pub fn root(&self) -> Root {
|
||||||
|
|
|
@ -1,13 +1,10 @@
|
||||||
//! Parallel note commitment tree update methods.
|
//! Parallel note commitment tree update methods.
|
||||||
|
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{block::Block, orchard, sapling, sprout, subtree::NoteCommitmentSubtree};
|
||||||
block::{Block, Height},
|
|
||||||
orchard, sapling, sprout,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// An argument wrapper struct for note commitment trees.
|
/// An argument wrapper struct for note commitment trees.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -18,8 +15,14 @@ pub struct NoteCommitmentTrees {
|
||||||
/// The sapling note commitment tree.
|
/// The sapling note commitment tree.
|
||||||
pub sapling: Arc<sapling::tree::NoteCommitmentTree>,
|
pub sapling: Arc<sapling::tree::NoteCommitmentTree>,
|
||||||
|
|
||||||
|
/// The sapling note commitment subtree.
|
||||||
|
pub sapling_subtree: Option<Arc<NoteCommitmentSubtree<sapling::tree::Node>>>,
|
||||||
|
|
||||||
/// The orchard note commitment tree.
|
/// The orchard note commitment tree.
|
||||||
pub orchard: Arc<orchard::tree::NoteCommitmentTree>,
|
pub orchard: Arc<orchard::tree::NoteCommitmentTree>,
|
||||||
|
|
||||||
|
/// The orchard note commitment subtree.
|
||||||
|
pub orchard_subtree: Option<Arc<NoteCommitmentSubtree<orchard::tree::Node>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Note commitment tree errors.
|
/// Note commitment tree errors.
|
||||||
|
@ -49,49 +52,34 @@ impl NoteCommitmentTrees {
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &Arc<Block>,
|
block: &Arc<Block>,
|
||||||
) -> Result<(), NoteCommitmentTreeError> {
|
) -> Result<(), NoteCommitmentTreeError> {
|
||||||
self.update_trees_parallel_list(
|
let block = block.clone();
|
||||||
[(
|
let height = block
|
||||||
block
|
.coinbase_height()
|
||||||
.coinbase_height()
|
.expect("height was already validated");
|
||||||
.expect("height was already validated"),
|
|
||||||
block.clone(),
|
|
||||||
)]
|
|
||||||
.into_iter()
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates the note commitment trees using the transactions in `block`,
|
|
||||||
/// then re-calculates the cached tree roots, using parallel `rayon` threads.
|
|
||||||
///
|
|
||||||
/// If any of the tree updates cause an error,
|
|
||||||
/// it will be returned at the end of the parallel batches.
|
|
||||||
pub fn update_trees_parallel_list(
|
|
||||||
&mut self,
|
|
||||||
block_list: BTreeMap<Height, Arc<Block>>,
|
|
||||||
) -> Result<(), NoteCommitmentTreeError> {
|
|
||||||
// Prepare arguments for parallel threads
|
// Prepare arguments for parallel threads
|
||||||
let NoteCommitmentTrees {
|
let NoteCommitmentTrees {
|
||||||
sprout,
|
sprout,
|
||||||
sapling,
|
sapling,
|
||||||
orchard,
|
orchard,
|
||||||
|
..
|
||||||
} = self.clone();
|
} = self.clone();
|
||||||
|
|
||||||
let sprout_note_commitments: Vec<_> = block_list
|
let sprout_note_commitments: Vec<_> = block
|
||||||
.values()
|
.transactions
|
||||||
.flat_map(|block| block.transactions.iter())
|
.iter()
|
||||||
.flat_map(|tx| tx.sprout_note_commitments())
|
.flat_map(|tx| tx.sprout_note_commitments())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
let sapling_note_commitments: Vec<_> = block_list
|
let sapling_note_commitments: Vec<_> = block
|
||||||
.values()
|
.transactions
|
||||||
.flat_map(|block| block.transactions.iter())
|
.iter()
|
||||||
.flat_map(|tx| tx.sapling_note_commitments())
|
.flat_map(|tx| tx.sapling_note_commitments())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
let orchard_note_commitments: Vec<_> = block_list
|
let orchard_note_commitments: Vec<_> = block
|
||||||
.values()
|
.transactions
|
||||||
.flat_map(|block| block.transactions.iter())
|
.iter()
|
||||||
.flat_map(|tx| tx.orchard_note_commitments())
|
.flat_map(|tx| tx.orchard_note_commitments())
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -132,12 +120,20 @@ impl NoteCommitmentTrees {
|
||||||
if let Some(sprout_result) = sprout_result {
|
if let Some(sprout_result) = sprout_result {
|
||||||
self.sprout = sprout_result?;
|
self.sprout = sprout_result?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(sapling_result) = sapling_result {
|
if let Some(sapling_result) = sapling_result {
|
||||||
self.sapling = sapling_result?;
|
let (sapling, subtree_root) = sapling_result?;
|
||||||
}
|
self.sapling = sapling;
|
||||||
|
self.sapling_subtree =
|
||||||
|
subtree_root.map(|(idx, node)| NoteCommitmentSubtree::new(idx, height, node));
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(orchard_result) = orchard_result {
|
if let Some(orchard_result) = orchard_result {
|
||||||
self.orchard = orchard_result?;
|
let (orchard, subtree_root) = orchard_result?;
|
||||||
}
|
self.orchard = orchard;
|
||||||
|
self.orchard_subtree =
|
||||||
|
subtree_root.map(|(idx, node)| NoteCommitmentSubtree::new(idx, height, node));
|
||||||
|
};
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -160,36 +156,74 @@ impl NoteCommitmentTrees {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the sapling note commitment tree.
|
/// Update the sapling note commitment tree.
|
||||||
|
#[allow(clippy::unwrap_in_result)]
|
||||||
fn update_sapling_note_commitment_tree(
|
fn update_sapling_note_commitment_tree(
|
||||||
mut sapling: Arc<sapling::tree::NoteCommitmentTree>,
|
mut sapling: Arc<sapling::tree::NoteCommitmentTree>,
|
||||||
sapling_note_commitments: Vec<sapling::tree::NoteCommitmentUpdate>,
|
sapling_note_commitments: Vec<sapling::tree::NoteCommitmentUpdate>,
|
||||||
) -> Result<Arc<sapling::tree::NoteCommitmentTree>, NoteCommitmentTreeError> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Arc<sapling::tree::NoteCommitmentTree>,
|
||||||
|
Option<(u16, sapling::tree::Node)>,
|
||||||
|
),
|
||||||
|
NoteCommitmentTreeError,
|
||||||
|
> {
|
||||||
let sapling_nct = Arc::make_mut(&mut sapling);
|
let sapling_nct = Arc::make_mut(&mut sapling);
|
||||||
|
|
||||||
|
// It is impossible for blocks to contain more than one level 16 sapling root:
|
||||||
|
// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
|
||||||
|
// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
||||||
|
//
|
||||||
|
// Before NU5, this limit holds due to the minimum size of Sapling outputs (948 bytes)
|
||||||
|
// and the maximum size of a block:
|
||||||
|
// > The size of a block MUST be less than or equal to 2000000 bytes.
|
||||||
|
// <https://zips.z.cash/protocol/protocol.pdf#blockheader>
|
||||||
|
// <https://zips.z.cash/protocol/protocol.pdf#txnencoding>
|
||||||
|
let mut subtree_root = None;
|
||||||
|
|
||||||
for sapling_note_commitment in sapling_note_commitments {
|
for sapling_note_commitment in sapling_note_commitments {
|
||||||
|
if let Some(index_and_node) = sapling_nct.completed_subtree_index_and_root() {
|
||||||
|
subtree_root = Some(index_and_node);
|
||||||
|
}
|
||||||
|
|
||||||
sapling_nct.append(sapling_note_commitment)?;
|
sapling_nct.append(sapling_note_commitment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-calculate and cache the tree root.
|
// Re-calculate and cache the tree root.
|
||||||
let _ = sapling_nct.root();
|
let _ = sapling_nct.root();
|
||||||
|
|
||||||
Ok(sapling)
|
Ok((sapling, subtree_root))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update the orchard note commitment tree.
|
/// Update the orchard note commitment tree.
|
||||||
|
#[allow(clippy::unwrap_in_result)]
|
||||||
fn update_orchard_note_commitment_tree(
|
fn update_orchard_note_commitment_tree(
|
||||||
mut orchard: Arc<orchard::tree::NoteCommitmentTree>,
|
mut orchard: Arc<orchard::tree::NoteCommitmentTree>,
|
||||||
orchard_note_commitments: Vec<orchard::tree::NoteCommitmentUpdate>,
|
orchard_note_commitments: Vec<orchard::tree::NoteCommitmentUpdate>,
|
||||||
) -> Result<Arc<orchard::tree::NoteCommitmentTree>, NoteCommitmentTreeError> {
|
) -> Result<
|
||||||
|
(
|
||||||
|
Arc<orchard::tree::NoteCommitmentTree>,
|
||||||
|
Option<(u16, orchard::tree::Node)>,
|
||||||
|
),
|
||||||
|
NoteCommitmentTreeError,
|
||||||
|
> {
|
||||||
let orchard_nct = Arc::make_mut(&mut orchard);
|
let orchard_nct = Arc::make_mut(&mut orchard);
|
||||||
|
|
||||||
|
// It is impossible for blocks to contain more than one level 16 orchard root:
|
||||||
|
// > [NU5 onward] nSpendsSapling, nOutputsSapling, and nActionsOrchard MUST all be less than 2^16.
|
||||||
|
// <https://zips.z.cash/protocol/protocol.pdf#txnconsensus>
|
||||||
|
let mut subtree_root = None;
|
||||||
|
|
||||||
for orchard_note_commitment in orchard_note_commitments {
|
for orchard_note_commitment in orchard_note_commitments {
|
||||||
|
if let Some(index_and_node) = orchard_nct.completed_subtree_index_and_root() {
|
||||||
|
subtree_root = Some(index_and_node);
|
||||||
|
}
|
||||||
|
|
||||||
orchard_nct.append(orchard_note_commitment)?;
|
orchard_nct.append(orchard_note_commitment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-calculate and cache the tree root.
|
// Re-calculate and cache the tree root.
|
||||||
let _ = orchard_nct.root();
|
let _ = orchard_nct.root();
|
||||||
|
|
||||||
Ok(orchard)
|
Ok((orchard, subtree_root))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use bitvec::prelude::*;
|
use bitvec::prelude::*;
|
||||||
use bridgetree;
|
use bridgetree::{self, NonEmptyFrontier};
|
||||||
use incrementalmerkletree::{frontier::Frontier, Hashable};
|
use incrementalmerkletree::{frontier::Frontier, Hashable};
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
@ -28,8 +28,11 @@ use zcash_primitives::merkle_tree::HashSer;
|
||||||
|
|
||||||
use super::commitment::pedersen_hashes::pedersen_hash;
|
use super::commitment::pedersen_hashes::pedersen_hash;
|
||||||
|
|
||||||
use crate::serialization::{
|
use crate::{
|
||||||
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
serialization::{
|
||||||
|
serde_helpers, ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize,
|
||||||
|
},
|
||||||
|
subtree::TRACKED_SUBTREE_HEIGHT,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod legacy;
|
pub mod legacy;
|
||||||
|
@ -165,6 +168,12 @@ impl ZcashDeserialize for Root {
|
||||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||||
pub struct Node([u8; 32]);
|
pub struct Node([u8; 32]);
|
||||||
|
|
||||||
|
impl AsRef<[u8; 32]> for Node {
|
||||||
|
fn as_ref(&self) -> &[u8; 32] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Node {
|
impl fmt::Debug for Node {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
f.debug_tuple("Node").field(&hex::encode(self.0)).finish()
|
f.debug_tuple("Node").field(&hex::encode(self.0)).finish()
|
||||||
|
@ -217,6 +226,18 @@ impl From<jubjub::Fq> for Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&[u8]> for Node {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
|
||||||
|
Option::<jubjub::Fq>::from(jubjub::Fq::from_bytes(
|
||||||
|
bytes.try_into().map_err(|_| "wrong byte slice len")?,
|
||||||
|
))
|
||||||
|
.map(Node::from)
|
||||||
|
.ok_or("invalid jubjub field element")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl serde::Serialize for Node {
|
impl serde::Serialize for Node {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
|
@ -311,6 +332,32 @@ impl NoteCommitmentTree {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true if the most recently appended leaf completes the subtree
|
||||||
|
pub fn is_complete_subtree(tree: &NonEmptyFrontier<Node>) -> bool {
|
||||||
|
tree.position()
|
||||||
|
.is_complete_subtree(TRACKED_SUBTREE_HEIGHT.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns subtree address at [`TRACKED_SUBTREE_HEIGHT`]
|
||||||
|
pub fn subtree_address(tree: &NonEmptyFrontier<Node>) -> incrementalmerkletree::Address {
|
||||||
|
incrementalmerkletree::Address::above_position(
|
||||||
|
TRACKED_SUBTREE_HEIGHT.into(),
|
||||||
|
tree.position(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns subtree index and root if the most recently appended leaf completes the subtree
|
||||||
|
#[allow(clippy::unwrap_in_result)]
|
||||||
|
pub fn completed_subtree_index_and_root(&self) -> Option<(u16, Node)> {
|
||||||
|
let value = self.inner.value()?;
|
||||||
|
Self::is_complete_subtree(value).then_some(())?;
|
||||||
|
let address = Self::subtree_address(value);
|
||||||
|
let index = address.index().try_into().expect("should fit in u16");
|
||||||
|
let root = value.root(Some(TRACKED_SUBTREE_HEIGHT.into()));
|
||||||
|
|
||||||
|
Some((index, root))
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the current root of the tree, used as an anchor in Sapling
|
/// Returns the current root of the tree, used as an anchor in Sapling
|
||||||
/// shielded transactions.
|
/// shielded transactions.
|
||||||
pub fn root(&self) -> Root {
|
pub fn root(&self) -> Root {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
//! Struct representing Sapling/Orchard note commitment subtrees
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::block::Height;
|
||||||
|
|
||||||
|
/// Height at which Zebra tracks subtree roots
|
||||||
|
pub const TRACKED_SUBTREE_HEIGHT: u8 = 16;
|
||||||
|
|
||||||
|
/// A subtree index
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct NoteCommitmentSubtreeIndex(pub u16);
|
||||||
|
|
||||||
|
impl From<u16> for NoteCommitmentSubtreeIndex {
|
||||||
|
fn from(value: u16) -> Self {
|
||||||
|
Self(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Subtree root of Sapling or Orchard note commitment tree,
|
||||||
|
/// with its associated block height and subtree index.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct NoteCommitmentSubtree<Node> {
|
||||||
|
/// Index of this subtree
|
||||||
|
pub index: NoteCommitmentSubtreeIndex,
|
||||||
|
/// End boundary of this subtree, the block height of its last leaf.
|
||||||
|
pub end: Height,
|
||||||
|
/// Root of this subtree.
|
||||||
|
pub node: Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Node> NoteCommitmentSubtree<Node> {
|
||||||
|
/// Creates new [`NoteCommitmentSubtree`]
|
||||||
|
pub fn new(index: impl Into<NoteCommitmentSubtreeIndex>, end: Height, node: Node) -> Arc<Self> {
|
||||||
|
let index = index.into();
|
||||||
|
Arc::new(Self { index, end, node })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub struct NoteCommitmentSubtreeData<Node> {
|
||||||
|
/// End boundary of this subtree, the block height of its last leaf.
|
||||||
|
pub end: Height,
|
||||||
|
/// Root of this subtree.
|
||||||
|
pub node: Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Node> NoteCommitmentSubtreeData<Node> {
|
||||||
|
/// Creates new [`NoteCommitmentSubtreeData`]
|
||||||
|
pub fn new(end: Height, node: Node) -> Self {
|
||||||
|
Self { end, node }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates new [`NoteCommitmentSubtree`] from a [`NoteCommitmentSubtreeData`] and index
|
||||||
|
pub fn with_index(
|
||||||
|
self,
|
||||||
|
index: impl Into<NoteCommitmentSubtreeIndex>,
|
||||||
|
) -> Arc<NoteCommitmentSubtree<Node>> {
|
||||||
|
NoteCommitmentSubtree::new(index, self.end, self.node)
|
||||||
|
}
|
||||||
|
}
|
|
@ -241,7 +241,9 @@ impl Treestate {
|
||||||
note_commitment_trees: NoteCommitmentTrees {
|
note_commitment_trees: NoteCommitmentTrees {
|
||||||
sprout,
|
sprout,
|
||||||
sapling,
|
sapling,
|
||||||
|
sapling_subtree: None,
|
||||||
orchard,
|
orchard,
|
||||||
|
orchard_subtree: None,
|
||||||
},
|
},
|
||||||
history_tree,
|
history_tree,
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,9 @@ impl ZebraDb {
|
||||||
NoteCommitmentTrees {
|
NoteCommitmentTrees {
|
||||||
sprout: self.sprout_tree(),
|
sprout: self.sprout_tree(),
|
||||||
sapling: self.sapling_tree(),
|
sapling: self.sapling_tree(),
|
||||||
|
sapling_subtree: None,
|
||||||
orchard: self.orchard_tree(),
|
orchard: self.orchard_tree(),
|
||||||
|
orchard_subtree: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1251,7 +1251,9 @@ impl Chain {
|
||||||
let mut nct = NoteCommitmentTrees {
|
let mut nct = NoteCommitmentTrees {
|
||||||
sprout: self.sprout_note_commitment_tree(),
|
sprout: self.sprout_note_commitment_tree(),
|
||||||
sapling: self.sapling_note_commitment_tree(),
|
sapling: self.sapling_note_commitment_tree(),
|
||||||
|
sapling_subtree: None,
|
||||||
orchard: self.orchard_note_commitment_tree(),
|
orchard: self.orchard_note_commitment_tree(),
|
||||||
|
orchard_subtree: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tree_result = None;
|
let mut tree_result = None;
|
||||||
|
|
Loading…
Reference in New Issue