Use const generics to set commitment tree & incremental witness depths.
This is in preparation for extraction into the `incrementalmerkletree` crate, which is not Sapling-specific and therefore cannot hard-code the depths of these data structures.
This commit is contained in:
parent
69430c3da2
commit
ec57d23115
|
@ -10,8 +10,7 @@ use zcash_primitives::{
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
legacy::TransparentAddress,
|
legacy::TransparentAddress,
|
||||||
memo::{Memo, MemoBytes},
|
memo::{Memo, MemoBytes},
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
sapling::{self, Nullifier, PaymentAddress},
|
||||||
sapling::{self, Node, Nullifier, PaymentAddress},
|
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{amount::Amount, OutPoint},
|
components::{amount::Amount, OutPoint},
|
||||||
Transaction, TxId,
|
Transaction, TxId,
|
||||||
|
@ -166,14 +165,14 @@ pub trait WalletRead {
|
||||||
fn get_commitment_tree(
|
fn get_commitment_tree(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Option<CommitmentTree<Node>>, Self::Error>;
|
) -> Result<Option<sapling::CommitmentTree>, Self::Error>;
|
||||||
|
|
||||||
/// Returns the incremental witnesses as of the specified block height.
|
/// Returns the incremental witnesses as of the specified block height.
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
fn get_witnesses(
|
fn get_witnesses(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error>;
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error>;
|
||||||
|
|
||||||
/// Returns the nullifiers for notes that the wallet is tracking, along with their
|
/// Returns the nullifiers for notes that the wallet is tracking, along with their
|
||||||
/// associated account IDs, that are either unspent or have not yet been confirmed as
|
/// associated account IDs, that are either unspent or have not yet been confirmed as
|
||||||
|
@ -239,7 +238,7 @@ pub struct PrunedBlock<'a> {
|
||||||
pub block_height: BlockHeight,
|
pub block_height: BlockHeight,
|
||||||
pub block_hash: BlockHash,
|
pub block_hash: BlockHash,
|
||||||
pub block_time: u32,
|
pub block_time: u32,
|
||||||
pub commitment_tree: &'a CommitmentTree<Node>,
|
pub commitment_tree: &'a sapling::CommitmentTree,
|
||||||
pub transactions: &'a Vec<WalletTx<Nullifier>>,
|
pub transactions: &'a Vec<WalletTx<Nullifier>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,8 +387,8 @@ pub trait WalletWrite: WalletRead {
|
||||||
fn advance_by_block(
|
fn advance_by_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &PrunedBlock,
|
block: &PrunedBlock,
|
||||||
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
|
updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error>;
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error>;
|
||||||
|
|
||||||
/// Caches a decrypted transaction in the persistent wallet store.
|
/// Caches a decrypted transaction in the persistent wallet store.
|
||||||
fn store_decrypted_tx(
|
fn store_decrypted_tx(
|
||||||
|
@ -433,8 +432,7 @@ pub mod testing {
|
||||||
consensus::{BlockHeight, Network},
|
consensus::{BlockHeight, Network},
|
||||||
legacy::TransparentAddress,
|
legacy::TransparentAddress,
|
||||||
memo::Memo,
|
memo::Memo,
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
sapling::{self, Nullifier},
|
||||||
sapling::{Node, Nullifier},
|
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{Amount, OutPoint},
|
components::{Amount, OutPoint},
|
||||||
Transaction, TxId,
|
Transaction, TxId,
|
||||||
|
@ -525,7 +523,7 @@ pub mod testing {
|
||||||
fn get_commitment_tree(
|
fn get_commitment_tree(
|
||||||
&self,
|
&self,
|
||||||
_block_height: BlockHeight,
|
_block_height: BlockHeight,
|
||||||
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
|
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +531,7 @@ pub mod testing {
|
||||||
fn get_witnesses(
|
fn get_witnesses(
|
||||||
&self,
|
&self,
|
||||||
_block_height: BlockHeight,
|
_block_height: BlockHeight,
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -613,8 +611,8 @@ pub mod testing {
|
||||||
fn advance_by_block(
|
fn advance_by_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
_block: &PrunedBlock,
|
_block: &PrunedBlock,
|
||||||
_updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
|
_updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::fmt::Debug;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus::{self, NetworkUpgrade},
|
consensus::{self, NetworkUpgrade},
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey},
|
note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey},
|
||||||
|
@ -701,11 +700,7 @@ fn select_key_for_note<N>(
|
||||||
selected: &SpendableNote<N>,
|
selected: &SpendableNote<N>,
|
||||||
extsk: &ExtendedSpendingKey,
|
extsk: &ExtendedSpendingKey,
|
||||||
dfvk: &DiversifiableFullViewingKey,
|
dfvk: &DiversifiableFullViewingKey,
|
||||||
) -> Option<(
|
) -> Option<(sapling::Note, ExtendedSpendingKey, sapling::MerklePath)> {
|
||||||
sapling::Note,
|
|
||||||
ExtendedSpendingKey,
|
|
||||||
MerklePath<sapling::Node>,
|
|
||||||
)> {
|
|
||||||
let merkle_path = selected.witness.path().expect("the tree is not empty");
|
let merkle_path = selected.witness.path().expect("the tree is not empty");
|
||||||
|
|
||||||
// Attempt to reconstruct the note being spent using both the internal and external dfvks
|
// Attempt to reconstruct the note being spent using both the internal and external dfvks
|
||||||
|
|
|
@ -6,7 +6,6 @@ use zcash_primitives::{
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
keys::OutgoingViewingKey,
|
keys::OutgoingViewingKey,
|
||||||
legacy::TransparentAddress,
|
legacy::TransparentAddress,
|
||||||
merkle_tree::IncrementalWitness,
|
|
||||||
sapling,
|
sapling,
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{
|
components::{
|
||||||
|
@ -118,7 +117,7 @@ pub struct WalletSaplingOutput<N> {
|
||||||
account: AccountId,
|
account: AccountId,
|
||||||
note: sapling::Note,
|
note: sapling::Note,
|
||||||
is_change: bool,
|
is_change: bool,
|
||||||
witness: IncrementalWitness<sapling::Node>,
|
witness: sapling::IncrementalWitness,
|
||||||
nf: N,
|
nf: N,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +131,7 @@ impl<N> WalletSaplingOutput<N> {
|
||||||
account: AccountId,
|
account: AccountId,
|
||||||
note: sapling::Note,
|
note: sapling::Note,
|
||||||
is_change: bool,
|
is_change: bool,
|
||||||
witness: IncrementalWitness<sapling::Node>,
|
witness: sapling::IncrementalWitness,
|
||||||
nf: N,
|
nf: N,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -165,10 +164,10 @@ impl<N> WalletSaplingOutput<N> {
|
||||||
pub fn is_change(&self) -> bool {
|
pub fn is_change(&self) -> bool {
|
||||||
self.is_change
|
self.is_change
|
||||||
}
|
}
|
||||||
pub fn witness(&self) -> &IncrementalWitness<sapling::Node> {
|
pub fn witness(&self) -> &sapling::IncrementalWitness {
|
||||||
&self.witness
|
&self.witness
|
||||||
}
|
}
|
||||||
pub fn witness_mut(&mut self) -> &mut IncrementalWitness<sapling::Node> {
|
pub fn witness_mut(&mut self) -> &mut sapling::IncrementalWitness {
|
||||||
&mut self.witness
|
&mut self.witness
|
||||||
}
|
}
|
||||||
pub fn nf(&self) -> &N {
|
pub fn nf(&self) -> &N {
|
||||||
|
@ -183,7 +182,7 @@ pub struct SpendableNote<NoteRef> {
|
||||||
pub diversifier: sapling::Diversifier,
|
pub diversifier: sapling::Diversifier,
|
||||||
pub note_value: Amount,
|
pub note_value: Amount,
|
||||||
pub rseed: sapling::Rseed,
|
pub rseed: sapling::Rseed,
|
||||||
pub witness: IncrementalWitness<sapling::Node>,
|
pub witness: sapling::IncrementalWitness,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<NoteRef> sapling_fees::InputView<NoteRef> for SpendableNote<NoteRef> {
|
impl<NoteRef> sapling_fees::InputView<NoteRef> for SpendableNote<NoteRef> {
|
||||||
|
|
|
@ -7,7 +7,6 @@ use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
|
||||||
use zcash_note_encryption::batch;
|
use zcash_note_encryption::batch;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
consensus,
|
consensus,
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
||||||
|
@ -60,7 +59,7 @@ pub trait ScanningKey {
|
||||||
fn sapling_nf(
|
fn sapling_nf(
|
||||||
key: &Self::SaplingNk,
|
key: &Self::SaplingNk,
|
||||||
note: &Note,
|
note: &Note,
|
||||||
witness: &IncrementalWitness<Node>,
|
witness: &sapling::IncrementalWitness,
|
||||||
) -> Self::Nf;
|
) -> Self::Nf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +87,7 @@ impl ScanningKey for DiversifiableFullViewingKey {
|
||||||
fn sapling_nf(
|
fn sapling_nf(
|
||||||
key: &Self::SaplingNk,
|
key: &Self::SaplingNk,
|
||||||
note: &Note,
|
note: &Note,
|
||||||
witness: &IncrementalWitness<Node>,
|
witness: &sapling::IncrementalWitness,
|
||||||
) -> Self::Nf {
|
) -> Self::Nf {
|
||||||
note.nf(key, witness.position() as u64)
|
note.nf(key, witness.position() as u64)
|
||||||
}
|
}
|
||||||
|
@ -108,7 +107,7 @@ impl ScanningKey for SaplingIvk {
|
||||||
[((), self.clone(), ())]
|
[((), self.clone(), ())]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sapling_nf(_key: &Self::SaplingNk, _note: &Note, _witness: &IncrementalWitness<Node>) {}
|
fn sapling_nf(_key: &Self::SaplingNk, _note: &Note, _witness: &sapling::IncrementalWitness) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scans a [`CompactBlock`] with a set of [`ScanningKey`]s.
|
/// Scans a [`CompactBlock`] with a set of [`ScanningKey`]s.
|
||||||
|
@ -130,8 +129,8 @@ impl ScanningKey for SaplingIvk {
|
||||||
/// [`SaplingIvk`]: zcash_primitives::sapling::SaplingIvk
|
/// [`SaplingIvk`]: zcash_primitives::sapling::SaplingIvk
|
||||||
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
|
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
|
||||||
/// [`ScanningKey`]: crate::welding_rig::ScanningKey
|
/// [`ScanningKey`]: crate::welding_rig::ScanningKey
|
||||||
/// [`CommitmentTree`]: zcash_primitives::merkle_tree::CommitmentTree
|
/// [`CommitmentTree`]: zcash_primitives::sapling::CommitmentTree
|
||||||
/// [`IncrementalWitness`]: zcash_primitives::merkle_tree::IncrementalWitness
|
/// [`IncrementalWitness`]: zcash_primitives::sapling::IncrementalWitness
|
||||||
/// [`WalletSaplingOutput`]: crate::wallet::WalletSaplingOutput
|
/// [`WalletSaplingOutput`]: crate::wallet::WalletSaplingOutput
|
||||||
/// [`WalletTx`]: crate::wallet::WalletTx
|
/// [`WalletTx`]: crate::wallet::WalletTx
|
||||||
pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
|
pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
|
||||||
|
@ -139,8 +138,8 @@ pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
|
||||||
block: CompactBlock,
|
block: CompactBlock,
|
||||||
vks: &[(&AccountId, &K)],
|
vks: &[(&AccountId, &K)],
|
||||||
nullifiers: &[(AccountId, Nullifier)],
|
nullifiers: &[(AccountId, Nullifier)],
|
||||||
tree: &mut CommitmentTree<Node>,
|
tree: &mut sapling::CommitmentTree,
|
||||||
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
existing_witnesses: &mut [&mut sapling::IncrementalWitness],
|
||||||
) -> Vec<WalletTx<K::Nf>> {
|
) -> Vec<WalletTx<K::Nf>> {
|
||||||
scan_block_with_runner::<_, _, ()>(
|
scan_block_with_runner::<_, _, ()>(
|
||||||
params,
|
params,
|
||||||
|
@ -200,8 +199,8 @@ pub(crate) fn scan_block_with_runner<
|
||||||
block: CompactBlock,
|
block: CompactBlock,
|
||||||
vks: &[(&AccountId, &K)],
|
vks: &[(&AccountId, &K)],
|
||||||
nullifiers: &[(AccountId, Nullifier)],
|
nullifiers: &[(AccountId, Nullifier)],
|
||||||
tree: &mut CommitmentTree<Node>,
|
tree: &mut sapling::CommitmentTree,
|
||||||
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
|
existing_witnesses: &mut [&mut sapling::IncrementalWitness],
|
||||||
mut batch_runner: Option<&mut TaggedBatchRunner<P, K::Scope, T>>,
|
mut batch_runner: Option<&mut TaggedBatchRunner<P, K::Scope, T>>,
|
||||||
) -> Vec<WalletTx<K::Nf>> {
|
) -> Vec<WalletTx<K::Nf>> {
|
||||||
let mut wtxs: Vec<WalletTx<K::Nf>> = vec![];
|
let mut wtxs: Vec<WalletTx<K::Nf>> = vec![];
|
||||||
|
@ -350,7 +349,7 @@ pub(crate) fn scan_block_with_runner<
|
||||||
// - Notes created by consolidation transactions.
|
// - Notes created by consolidation transactions.
|
||||||
// - Notes sent from one account to itself.
|
// - Notes sent from one account to itself.
|
||||||
let is_change = spent_from_accounts.contains(&account);
|
let is_change = spent_from_accounts.contains(&account);
|
||||||
let witness = IncrementalWitness::from_tree(tree);
|
let witness = sapling::IncrementalWitness::from_tree(tree.clone());
|
||||||
let nf = K::sapling_nf(&nk, ¬e, &witness);
|
let nf = K::sapling_nf(&nk, ¬e, &witness);
|
||||||
|
|
||||||
shielded_outputs.push(WalletSaplingOutput::from_parts(
|
shielded_outputs.push(WalletSaplingOutput::from_parts(
|
||||||
|
|
|
@ -43,8 +43,7 @@ use zcash_primitives::{
|
||||||
consensus::{self, BlockHeight},
|
consensus::{self, BlockHeight},
|
||||||
legacy::TransparentAddress,
|
legacy::TransparentAddress,
|
||||||
memo::{Memo, MemoBytes},
|
memo::{Memo, MemoBytes},
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
sapling::{self, Nullifier},
|
||||||
sapling::{Node, Nullifier},
|
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{amount::Amount, OutPoint},
|
components::{amount::Amount, OutPoint},
|
||||||
Transaction, TxId,
|
Transaction, TxId,
|
||||||
|
@ -201,7 +200,7 @@ impl<P: consensus::Parameters> WalletRead for WalletDb<P> {
|
||||||
fn get_commitment_tree(
|
fn get_commitment_tree(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
|
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
|
||||||
wallet::get_sapling_commitment_tree(self, block_height)
|
wallet::get_sapling_commitment_tree(self, block_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +208,7 @@ impl<P: consensus::Parameters> WalletRead for WalletDb<P> {
|
||||||
fn get_witnesses(
|
fn get_witnesses(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
|
||||||
wallet::get_sapling_witnesses(self, block_height)
|
wallet::get_sapling_witnesses(self, block_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +356,7 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
|
||||||
fn get_commitment_tree(
|
fn get_commitment_tree(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
|
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
|
||||||
self.wallet_db.get_commitment_tree(block_height)
|
self.wallet_db.get_commitment_tree(block_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,7 +364,7 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
|
||||||
fn get_witnesses(
|
fn get_witnesses(
|
||||||
&self,
|
&self,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
|
||||||
self.wallet_db.get_witnesses(block_height)
|
self.wallet_db.get_witnesses(block_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,8 +514,8 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
|
||||||
fn advance_by_block(
|
fn advance_by_block(
|
||||||
&mut self,
|
&mut self,
|
||||||
block: &PrunedBlock,
|
block: &PrunedBlock,
|
||||||
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
|
updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
|
||||||
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
|
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
|
||||||
// database updates for each block are transactional
|
// database updates for each block are transactional
|
||||||
self.transactionally(|up| {
|
self.transactionally(|up| {
|
||||||
// Insert the block into the database.
|
// Insert the block into the database.
|
||||||
|
|
|
@ -12,8 +12,7 @@ use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
consensus::{self, BlockHeight},
|
consensus::{self, BlockHeight},
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
sapling::{self, Diversifier, Nullifier},
|
||||||
sapling::{Diversifier, Node, Nullifier},
|
|
||||||
transaction::{components::Amount, TxId},
|
transaction::{components::Amount, TxId},
|
||||||
zip32::{AccountId, DiversifierIndex},
|
zip32::{AccountId, DiversifierIndex},
|
||||||
};
|
};
|
||||||
|
@ -277,7 +276,7 @@ impl<'a, P> DataConnStmtCache<'a, P> {
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
block_hash: BlockHash,
|
block_hash: BlockHash,
|
||||||
block_time: u32,
|
block_time: u32,
|
||||||
commitment_tree: &CommitmentTree<Node>,
|
commitment_tree: &sapling::CommitmentTree,
|
||||||
) -> Result<(), SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
let mut encoded_tree = Vec::new();
|
let mut encoded_tree = Vec::new();
|
||||||
commitment_tree.write(&mut encoded_tree).unwrap();
|
commitment_tree.write(&mut encoded_tree).unwrap();
|
||||||
|
@ -777,7 +776,7 @@ impl<'a, P> DataConnStmtCache<'a, P> {
|
||||||
&mut self,
|
&mut self,
|
||||||
note_id: NoteId,
|
note_id: NoteId,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
witness: &IncrementalWitness<Node>,
|
witness: &sapling::IncrementalWitness,
|
||||||
) -> Result<(), SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
let note_id = match note_id {
|
let note_id = match note_id {
|
||||||
NoteId::ReceivedNoteId(note_id) => Ok(note_id),
|
NoteId::ReceivedNoteId(note_id) => Ok(note_id),
|
||||||
|
|
|
@ -73,8 +73,7 @@ use zcash_primitives::{
|
||||||
block::BlockHash,
|
block::BlockHash,
|
||||||
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
|
||||||
memo::{Memo, MemoBytes},
|
memo::{Memo, MemoBytes},
|
||||||
merkle_tree::{CommitmentTree, IncrementalWitness},
|
sapling::{self, Note, Nullifier},
|
||||||
sapling::{Node, Note, Nullifier},
|
|
||||||
transaction::{components::Amount, Transaction, TxId},
|
transaction::{components::Amount, Transaction, TxId},
|
||||||
zip32::{
|
zip32::{
|
||||||
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
|
||||||
|
@ -687,14 +686,14 @@ pub(crate) fn truncate_to_height<P: consensus::Parameters>(
|
||||||
pub(crate) fn get_sapling_commitment_tree<P>(
|
pub(crate) fn get_sapling_commitment_tree<P>(
|
||||||
wdb: &WalletDb<P>,
|
wdb: &WalletDb<P>,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Option<CommitmentTree<Node>>, SqliteClientError> {
|
) -> Result<Option<sapling::CommitmentTree>, SqliteClientError> {
|
||||||
wdb.conn
|
wdb.conn
|
||||||
.query_row_and_then(
|
.query_row_and_then(
|
||||||
"SELECT sapling_tree FROM blocks WHERE height = ?",
|
"SELECT sapling_tree FROM blocks WHERE height = ?",
|
||||||
[u32::from(block_height)],
|
[u32::from(block_height)],
|
||||||
|row| {
|
|row| {
|
||||||
let row_data: Vec<u8> = row.get(0)?;
|
let row_data: Vec<u8> = row.get(0)?;
|
||||||
CommitmentTree::read(&row_data[..]).map_err(|e| {
|
sapling::CommitmentTree::read(&row_data[..]).map_err(|e| {
|
||||||
rusqlite::Error::FromSqlConversionFailure(
|
rusqlite::Error::FromSqlConversionFailure(
|
||||||
row_data.len(),
|
row_data.len(),
|
||||||
rusqlite::types::Type::Blob,
|
rusqlite::types::Type::Blob,
|
||||||
|
@ -712,7 +711,7 @@ pub(crate) fn get_sapling_commitment_tree<P>(
|
||||||
pub(crate) fn get_sapling_witnesses<P>(
|
pub(crate) fn get_sapling_witnesses<P>(
|
||||||
wdb: &WalletDb<P>,
|
wdb: &WalletDb<P>,
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
) -> Result<Vec<(NoteId, IncrementalWitness<Node>)>, SqliteClientError> {
|
) -> Result<Vec<(NoteId, sapling::IncrementalWitness)>, SqliteClientError> {
|
||||||
let mut stmt_fetch_witnesses = wdb
|
let mut stmt_fetch_witnesses = wdb
|
||||||
.conn
|
.conn
|
||||||
.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?;
|
.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?;
|
||||||
|
@ -720,7 +719,7 @@ pub(crate) fn get_sapling_witnesses<P>(
|
||||||
.query_map([u32::from(block_height)], |row| {
|
.query_map([u32::from(block_height)], |row| {
|
||||||
let id_note = NoteId::ReceivedNoteId(row.get(0)?);
|
let id_note = NoteId::ReceivedNoteId(row.get(0)?);
|
||||||
let wdb: Vec<u8> = row.get(1)?;
|
let wdb: Vec<u8> = row.get(1)?;
|
||||||
Ok(IncrementalWitness::read(&wdb[..]).map(|witness| (id_note, witness)))
|
Ok(sapling::IncrementalWitness::read(&wdb[..]).map(|witness| (id_note, witness)))
|
||||||
})
|
})
|
||||||
.map_err(SqliteClientError::from)?;
|
.map_err(SqliteClientError::from)?;
|
||||||
|
|
||||||
|
@ -884,7 +883,7 @@ pub(crate) fn insert_block<'a, P>(
|
||||||
block_height: BlockHeight,
|
block_height: BlockHeight,
|
||||||
block_hash: BlockHash,
|
block_hash: BlockHash,
|
||||||
block_time: u32,
|
block_time: u32,
|
||||||
commitment_tree: &CommitmentTree<Node>,
|
commitment_tree: &sapling::CommitmentTree,
|
||||||
) -> Result<(), SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
stmts.stmt_insert_block(block_height, block_hash, block_time, commitment_tree)
|
stmts.stmt_insert_block(block_height, block_hash, block_time, commitment_tree)
|
||||||
}
|
}
|
||||||
|
@ -1049,7 +1048,7 @@ pub(crate) fn put_received_note<'a, P, T: ReceivedSaplingOutput>(
|
||||||
pub(crate) fn insert_witness<'a, P>(
|
pub(crate) fn insert_witness<'a, P>(
|
||||||
stmts: &mut DataConnStmtCache<'a, P>,
|
stmts: &mut DataConnStmtCache<'a, P>,
|
||||||
note_id: i64,
|
note_id: i64,
|
||||||
witness: &IncrementalWitness<Node>,
|
witness: &sapling::IncrementalWitness,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
) -> Result<(), SqliteClientError> {
|
) -> Result<(), SqliteClientError> {
|
||||||
stmts.stmt_insert_witness(NoteId::ReceivedNoteId(note_id), height, witness)
|
stmts.stmt_insert_witness(NoteId::ReceivedNoteId(note_id), height, witness)
|
||||||
|
|
|
@ -820,7 +820,7 @@ mod tests {
|
||||||
// fake that the note appears in some previous
|
// fake that the note appears in some previous
|
||||||
// shielded output
|
// shielded output
|
||||||
tree.append(cm1).unwrap();
|
tree.append(cm1).unwrap();
|
||||||
let witness1 = IncrementalWitness::from_tree(&tree);
|
let witness1 = IncrementalWitness::from_tree(tree);
|
||||||
|
|
||||||
let mut builder_a = demo_builder(tx_height);
|
let mut builder_a = demo_builder(tx_height);
|
||||||
builder_a
|
builder_a
|
||||||
|
|
|
@ -12,17 +12,22 @@ and this library adheres to Rust's notion of
|
||||||
`incrementalmerkletree::Hashable` and `merkle_tree::HashSer`.
|
`incrementalmerkletree::Hashable` and `merkle_tree::HashSer`.
|
||||||
- The `Hashable` bound on the `Node` parameter to the `IncrementalWitness`
|
- The `Hashable` bound on the `Node` parameter to the `IncrementalWitness`
|
||||||
type has been removed.
|
type has been removed.
|
||||||
|
- `sapling::SAPLING_COMMITMENT_TREE_DEPTH_U8` and `sapling::SAPLING_COMMITMENT_TREE_DEPTH`
|
||||||
|
have been removed; use `sapling::NOTE_COMMITMENT_TREE_DEPTH` instead.
|
||||||
- `merkle_tree::incremental::write_auth_fragment_v1`
|
- `merkle_tree::incremental::write_auth_fragment_v1`
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- `merkle_tree::incremental::{read_address, write_address}`
|
- `merkle_tree::incremental::{read_address, write_address}`
|
||||||
- `merkle_tree::incremental::read_bridge_v2`
|
- `merkle_tree::incremental::read_bridge_v2`
|
||||||
|
- `sapling::{CommitmentTree, IncrementalWitness, MerklePath, NOTE_COMMITMENT_TREE_DEPTH}`
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- The bounds on the `H` parameter to the following methods have changed:
|
- The bounds on the `H` parameter to the following methods have changed:
|
||||||
- `merkle_tree::incremental::read_frontier_v0`
|
- `merkle_tree::incremental::read_frontier_v0`
|
||||||
- `merkle_tree::incremental::read_auth_fragment_v1`
|
- `merkle_tree::incremental::read_auth_fragment_v1`
|
||||||
|
- The depth of the `merkle_tree::CommitmentTree`, `merkle_tree::IncrementalWitness`
|
||||||
|
and `merkle_tree::incremental::MerklePath` data types are now statically constrained
|
||||||
|
using const generic type parameters.
|
||||||
|
|
||||||
## [0.11.0] - 2023-04-15
|
## [0.11.0] - 2023-04-15
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
//! Implementation of a Merkle tree of commitments used to prove the existence of notes.
|
//! Implementation of a Merkle tree of commitments used to prove the existence of notes.
|
||||||
|
|
||||||
|
use bridgetree::Frontier;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use incrementalmerkletree::Hashable;
|
use incrementalmerkletree::{Hashable, Level};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use std::iter::repeat;
|
use std::iter::repeat;
|
||||||
use zcash_encoding::{Optional, Vector};
|
use zcash_encoding::{Optional, Vector};
|
||||||
|
|
||||||
use crate::sapling::SAPLING_COMMITMENT_TREE_DEPTH_U8;
|
|
||||||
|
|
||||||
pub mod incremental;
|
pub mod incremental;
|
||||||
|
|
||||||
/// A hashable node within a Merkle tree.
|
/// A hashable node within a Merkle tree.
|
||||||
|
@ -34,25 +33,22 @@ impl<Node: Hashable> PathFiller<Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self, depth: u8) -> Node {
|
fn next(&mut self, level: Level) -> Node {
|
||||||
self.queue
|
self.queue
|
||||||
.pop_front()
|
.pop_front()
|
||||||
.unwrap_or_else(|| Node::empty_root(depth.into()))
|
.unwrap_or_else(|| Node::empty_root(level))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Merkle tree of note commitments.
|
/// A Merkle tree of note commitments.
|
||||||
///
|
|
||||||
/// The depth of the Merkle tree is fixed at 32, equal to the depth of the Sapling
|
|
||||||
/// commitment tree.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct CommitmentTree<Node> {
|
pub struct CommitmentTree<Node, const DEPTH: u8> {
|
||||||
pub(crate) left: Option<Node>,
|
pub(crate) left: Option<Node>,
|
||||||
pub(crate) right: Option<Node>,
|
pub(crate) right: Option<Node>,
|
||||||
pub(crate) parents: Vec<Option<Node>>,
|
pub(crate) parents: Vec<Option<Node>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Node> CommitmentTree<Node> {
|
impl<Node, const DEPTH: u8> CommitmentTree<Node, DEPTH> {
|
||||||
/// Creates an empty tree.
|
/// Creates an empty tree.
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
CommitmentTree {
|
CommitmentTree {
|
||||||
|
@ -62,7 +58,7 @@ impl<Node> CommitmentTree<Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_frontier<const DEPTH: u8>(frontier: &bridgetree::Frontier<Node, DEPTH>) -> Self
|
pub fn from_frontier(frontier: &Frontier<Node, DEPTH>) -> Self
|
||||||
where
|
where
|
||||||
Node: Clone,
|
Node: Clone,
|
||||||
{
|
{
|
||||||
|
@ -97,12 +93,12 @@ impl<Node> CommitmentTree<Node> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_frontier<const DEPTH: u8>(&self) -> bridgetree::Frontier<Node, DEPTH>
|
pub fn to_frontier(&self) -> Frontier<Node, DEPTH>
|
||||||
where
|
where
|
||||||
Node: Hashable + Clone,
|
Node: Hashable + Clone,
|
||||||
{
|
{
|
||||||
if self.size() == 0 {
|
if self.size() == 0 {
|
||||||
bridgetree::Frontier::empty()
|
Frontier::empty()
|
||||||
} else {
|
} else {
|
||||||
let ommers_iter = self.parents.iter().filter_map(|v| v.as_ref()).cloned();
|
let ommers_iter = self.parents.iter().filter_map(|v| v.as_ref()).cloned();
|
||||||
let (leaf, ommers) = match (self.left.as_ref(), self.right.as_ref()) {
|
let (leaf, ommers) = match (self.left.as_ref(), self.right.as_ref()) {
|
||||||
|
@ -116,7 +112,7 @@ impl<Node> CommitmentTree<Node> {
|
||||||
|
|
||||||
// If a frontier cannot be successfully constructed from the
|
// If a frontier cannot be successfully constructed from the
|
||||||
// parts of a commitment tree, it is a programming error.
|
// parts of a commitment tree, it is a programming error.
|
||||||
bridgetree::Frontier::from_parts((self.size() - 1).into(), leaf, ommers)
|
Frontier::from_parts((self.size() - 1).into(), leaf, ommers)
|
||||||
.expect("Frontier should be constructable from CommitmentTree.")
|
.expect("Frontier should be constructable from CommitmentTree.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,9 +150,12 @@ impl<Node> CommitmentTree<Node> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Node: Hashable + HashSer> CommitmentTree<Node> {
|
impl<Node: Hashable + Clone, const DEPTH: u8> CommitmentTree<Node, DEPTH> {
|
||||||
/// Reads a `CommitmentTree` from its serialized form.
|
/// Reads a `CommitmentTree` from its serialized form.
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self>
|
||||||
|
where
|
||||||
|
Node: HashSer,
|
||||||
|
{
|
||||||
let left = Optional::read(&mut reader, Node::read)?;
|
let left = Optional::read(&mut reader, Node::read)?;
|
||||||
let right = Optional::read(&mut reader, Node::read)?;
|
let right = Optional::read(&mut reader, Node::read)?;
|
||||||
let parents = Vector::read(&mut reader, |r| Optional::read(r, Node::read))?;
|
let parents = Vector::read(&mut reader, |r| Optional::read(r, Node::read))?;
|
||||||
|
@ -169,23 +168,26 @@ impl<Node: Hashable + HashSer> CommitmentTree<Node> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serializes this tree as an array of bytes.
|
/// Serializes this tree as an array of bytes.
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()>
|
||||||
|
where
|
||||||
|
Node: HashSer,
|
||||||
|
{
|
||||||
Optional::write(&mut writer, self.left.as_ref(), |w, n| n.write(w))?;
|
Optional::write(&mut writer, self.left.as_ref(), |w, n| n.write(w))?;
|
||||||
Optional::write(&mut writer, self.right.as_ref(), |w, n| n.write(w))?;
|
Optional::write(&mut writer, self.right.as_ref(), |w, n| n.write(w))?;
|
||||||
Vector::write(&mut writer, &self.parents, |w, e| {
|
Vector::write(&mut writer, &self.parents, |w, e| {
|
||||||
Optional::write(w, e.as_ref(), |w, n| n.write(w))
|
Optional::write(w, e.as_ref(), |w, n| n.write(w))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<Node: Hashable + Clone> CommitmentTree<Node> {
|
|
||||||
/// Adds a leaf node to the tree.
|
/// Adds a leaf node to the tree.
|
||||||
///
|
///
|
||||||
/// Returns an error if the tree is full.
|
/// Returns an error if the tree is full.
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn append(&mut self, node: Node) -> Result<(), ()> {
|
pub fn append(&mut self, node: Node) -> Result<(), ()> {
|
||||||
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH_U8)
|
self.append_inner(node, DEPTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
fn append_inner(&mut self, node: Node, depth: u8) -> Result<(), ()> {
|
fn append_inner(&mut self, node: Node, depth: u8) -> Result<(), ()> {
|
||||||
if self.is_complete(depth) {
|
if self.is_complete(depth) {
|
||||||
// Tree is full
|
// Tree is full
|
||||||
|
@ -223,7 +225,7 @@ impl<Node: Hashable + Clone> CommitmentTree<Node> {
|
||||||
|
|
||||||
/// Returns the current root of the tree.
|
/// Returns the current root of the tree.
|
||||||
pub fn root(&self) -> Node {
|
pub fn root(&self) -> Node {
|
||||||
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH_U8, PathFiller::empty())
|
self.root_inner(DEPTH, PathFiller::empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_inner(&self, depth: u8, mut filler: PathFiller<Node>) -> Node {
|
fn root_inner(&self, depth: u8, mut filler: PathFiller<Node>) -> Node {
|
||||||
|
@ -231,16 +233,17 @@ impl<Node: Hashable + Clone> CommitmentTree<Node> {
|
||||||
|
|
||||||
// 1) Hash left and right leaves together.
|
// 1) Hash left and right leaves together.
|
||||||
// - Empty leaves are used as needed.
|
// - Empty leaves are used as needed.
|
||||||
|
// - Note that `filler.next` is side-effecting and so cannot be factored out.
|
||||||
let leaf_root = Node::combine(
|
let leaf_root = Node::combine(
|
||||||
0.into(),
|
0.into(),
|
||||||
&self
|
&self
|
||||||
.left
|
.left
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| filler.next(0), |n| n.clone()),
|
.map_or_else(|| filler.next(0.into()), |n| n.clone()),
|
||||||
&self
|
&self
|
||||||
.right
|
.right
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or_else(|| filler.next(0), |n| n.clone()),
|
.map_or_else(|| filler.next(0.into()), |n| n.clone()),
|
||||||
);
|
);
|
||||||
|
|
||||||
// 2) Extend the parents to the desired depth with None values, then hash from leaf to
|
// 2) Extend the parents to the desired depth with None values, then hash from leaf to
|
||||||
|
@ -251,10 +254,10 @@ impl<Node: Hashable + Clone> CommitmentTree<Node> {
|
||||||
.take((depth - 1).into())
|
.take((depth - 1).into())
|
||||||
.zip(0u8..)
|
.zip(0u8..)
|
||||||
.fold(leaf_root, |root, (p, i)| {
|
.fold(leaf_root, |root, (p, i)| {
|
||||||
let parent_level = i + 1;
|
let level = Level::from(i + 1);
|
||||||
match p {
|
match p {
|
||||||
Some(node) => Node::combine(parent_level.into(), node, &root),
|
Some(node) => Node::combine(level, node, &root),
|
||||||
None => Node::combine(parent_level.into(), &root, &filler.next(parent_level)),
|
None => Node::combine(level, &root, &filler.next(level)),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -269,54 +272,53 @@ impl<Node: Hashable + Clone> CommitmentTree<Node> {
|
||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use ff::{Field, PrimeField};
|
/// use zcash_primitives::merkle_tree::{
|
||||||
/// use rand_core::OsRng;
|
/// CommitmentTree,
|
||||||
/// use zcash_primitives::{
|
/// IncrementalWitness,
|
||||||
/// merkle_tree::{CommitmentTree, IncrementalWitness},
|
/// testing::TestNode
|
||||||
/// sapling::Node,
|
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let mut rng = OsRng;
|
/// let mut tree = CommitmentTree::<TestNode, 8>::empty();
|
||||||
///
|
///
|
||||||
/// let mut tree = CommitmentTree::<Node>::empty();
|
/// tree.append(TestNode(0));
|
||||||
///
|
/// tree.append(TestNode(1));
|
||||||
/// tree.append(Node::from_scalar(bls12_381::Scalar::random(&mut rng)));
|
/// let mut witness = IncrementalWitness::from_tree(tree.clone());
|
||||||
/// tree.append(Node::from_scalar(bls12_381::Scalar::random(&mut rng)));
|
|
||||||
/// let mut witness = IncrementalWitness::from_tree(&tree);
|
|
||||||
/// assert_eq!(witness.position(), 1);
|
/// assert_eq!(witness.position(), 1);
|
||||||
/// assert_eq!(tree.root(), witness.root());
|
/// assert_eq!(tree.root(), witness.root());
|
||||||
///
|
///
|
||||||
/// let cmu = Node::from_scalar(bls12_381::Scalar::random(&mut rng));
|
/// let next = TestNode(2);
|
||||||
/// tree.append(cmu);
|
/// tree.append(next.clone());
|
||||||
/// witness.append(cmu);
|
/// witness.append(next);
|
||||||
/// assert_eq!(tree.root(), witness.root());
|
/// assert_eq!(tree.root(), witness.root());
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct IncrementalWitness<Node> {
|
pub struct IncrementalWitness<Node, const DEPTH: u8> {
|
||||||
tree: CommitmentTree<Node>,
|
tree: CommitmentTree<Node, DEPTH>,
|
||||||
filled: Vec<Node>,
|
filled: Vec<Node>,
|
||||||
cursor_depth: u8,
|
cursor_depth: u8,
|
||||||
cursor: Option<CommitmentTree<Node>>,
|
cursor: Option<CommitmentTree<Node, DEPTH>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
impl<Node, const DEPTH: u8> IncrementalWitness<Node, DEPTH> {
|
||||||
/// Creates an `IncrementalWitness` for the most recent commitment added to the given
|
/// Creates an `IncrementalWitness` for the most recent commitment added to the given
|
||||||
/// [`CommitmentTree`].
|
/// [`CommitmentTree`].
|
||||||
pub fn from_tree(tree: &CommitmentTree<Node>) -> IncrementalWitness<Node> {
|
pub fn from_tree(tree: CommitmentTree<Node, DEPTH>) -> IncrementalWitness<Node, DEPTH> {
|
||||||
IncrementalWitness {
|
IncrementalWitness {
|
||||||
tree: tree.clone(),
|
tree,
|
||||||
filled: vec![],
|
filled: vec![],
|
||||||
cursor_depth: 0,
|
cursor_depth: 0,
|
||||||
cursor: None,
|
cursor: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Node: Hashable + HashSer + Clone, const DEPTH: u8> IncrementalWitness<Node, DEPTH> {
|
||||||
/// Reads an `IncrementalWitness` from its serialized form.
|
/// Reads an `IncrementalWitness` from its serialized form.
|
||||||
#[allow(clippy::redundant_closure)]
|
#[allow(clippy::redundant_closure)]
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: R) -> io::Result<IncrementalWitness<Node, DEPTH>> {
|
||||||
let tree = CommitmentTree::read(&mut reader)?;
|
let tree = CommitmentTree::<Node, DEPTH>::read(&mut reader)?;
|
||||||
let filled = Vector::read(&mut reader, |r| Node::read(r))?;
|
let filled = Vector::read(&mut reader, |r| Node::read(r))?;
|
||||||
let cursor = Optional::read(&mut reader, CommitmentTree::read)?;
|
let cursor = Optional::read(&mut reader, CommitmentTree::<Node, DEPTH>::read)?;
|
||||||
|
|
||||||
let mut witness = IncrementalWitness {
|
let mut witness = IncrementalWitness {
|
||||||
tree,
|
tree,
|
||||||
|
@ -395,10 +397,12 @@ impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
||||||
/// Tracks a leaf node that has been added to the underlying tree.
|
/// Tracks a leaf node that has been added to the underlying tree.
|
||||||
///
|
///
|
||||||
/// Returns an error if the tree is full.
|
/// Returns an error if the tree is full.
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
pub fn append(&mut self, node: Node) -> Result<(), ()> {
|
pub fn append(&mut self, node: Node) -> Result<(), ()> {
|
||||||
self.append_inner(node, SAPLING_COMMITMENT_TREE_DEPTH_U8)
|
self.append_inner(node, DEPTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::result_unit_err)]
|
||||||
fn append_inner(&mut self, node: Node, depth: u8) -> Result<(), ()> {
|
fn append_inner(&mut self, node: Node, depth: u8) -> Result<(), ()> {
|
||||||
if let Some(mut cursor) = self.cursor.take() {
|
if let Some(mut cursor) = self.cursor.take() {
|
||||||
cursor
|
cursor
|
||||||
|
@ -433,7 +437,7 @@ impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
||||||
|
|
||||||
/// Returns the current root of the tree corresponding to the witness.
|
/// Returns the current root of the tree corresponding to the witness.
|
||||||
pub fn root(&self) -> Node {
|
pub fn root(&self) -> Node {
|
||||||
self.root_inner(SAPLING_COMMITMENT_TREE_DEPTH_U8)
|
self.root_inner(DEPTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn root_inner(&self, depth: u8) -> Node {
|
fn root_inner(&self, depth: u8) -> Node {
|
||||||
|
@ -441,11 +445,11 @@ impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current witness, or None if the tree is empty.
|
/// Returns the current witness, or None if the tree is empty.
|
||||||
pub fn path(&self) -> Option<MerklePath<Node>> {
|
pub fn path(&self) -> Option<MerklePath<Node, DEPTH>> {
|
||||||
self.path_inner(SAPLING_COMMITMENT_TREE_DEPTH_U8)
|
self.path_inner(DEPTH)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn path_inner(&self, depth: u8) -> Option<MerklePath<Node>> {
|
fn path_inner(&self, depth: u8) -> Option<MerklePath<Node, DEPTH>> {
|
||||||
let mut filler = self.filler();
|
let mut filler = self.filler();
|
||||||
let mut auth_path = Vec::new();
|
let mut auth_path = Vec::new();
|
||||||
|
|
||||||
|
@ -453,7 +457,7 @@ impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
||||||
if self.tree.right.is_some() {
|
if self.tree.right.is_some() {
|
||||||
auth_path.push((node.clone(), true));
|
auth_path.push((node.clone(), true));
|
||||||
} else {
|
} else {
|
||||||
auth_path.push((filler.next(0), false));
|
auth_path.push((filler.next(0.into()), false));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Can't create an authentication path for the beginning of the tree
|
// Can't create an authentication path for the beginning of the tree
|
||||||
|
@ -470,41 +474,51 @@ impl<Node: Hashable + HashSer + Clone> IncrementalWitness<Node> {
|
||||||
{
|
{
|
||||||
auth_path.push(match p {
|
auth_path.push(match p {
|
||||||
Some(node) => (node.clone(), true),
|
Some(node) => (node.clone(), true),
|
||||||
None => (filler.next(i + 1), false),
|
None => (filler.next(Level::from(i + 1)), false),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(auth_path.len(), depth.into());
|
assert_eq!(auth_path.len(), usize::from(depth));
|
||||||
|
|
||||||
Some(MerklePath::from_path(auth_path, self.position() as u64))
|
MerklePath::from_path(auth_path, self.position() as u64).ok()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A path from a position in a particular commitment tree to the root of that tree.
|
/// A path from a position in a particular commitment tree to the root of that tree.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct MerklePath<Node: Hashable> {
|
pub struct MerklePath<Node, const DEPTH: u8> {
|
||||||
pub auth_path: Vec<(Node, bool)>,
|
auth_path: Vec<(Node, bool)>,
|
||||||
pub position: u64,
|
position: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Node: Hashable + HashSer> MerklePath<Node> {
|
impl<Node, const DEPTH: u8> MerklePath<Node, DEPTH> {
|
||||||
|
pub fn auth_path(&self) -> &[(Node, bool)] {
|
||||||
|
&self.auth_path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> u64 {
|
||||||
|
self.position
|
||||||
|
}
|
||||||
|
|
||||||
/// Constructs a Merkle path directly from a path and position.
|
/// Constructs a Merkle path directly from a path and position.
|
||||||
pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Self {
|
pub fn from_path(auth_path: Vec<(Node, bool)>, position: u64) -> Result<Self, ()> {
|
||||||
MerklePath {
|
if auth_path.len() == usize::from(DEPTH) {
|
||||||
auth_path,
|
Ok(MerklePath {
|
||||||
position,
|
auth_path,
|
||||||
|
position,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
Err(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Node: Hashable + HashSer, const DEPTH: u8> MerklePath<Node, DEPTH> {
|
||||||
/// Reads a Merkle path from its serialized form.
|
/// Reads a Merkle path from its serialized form.
|
||||||
pub fn from_slice(witness: &[u8]) -> Result<Self, ()> {
|
pub fn from_slice(mut witness: &[u8]) -> Result<Self, ()> {
|
||||||
Self::from_slice_with_depth(witness, SAPLING_COMMITMENT_TREE_DEPTH_U8)
|
// Skip the first byte, which should be DEPTH to signify the length of
|
||||||
}
|
|
||||||
|
|
||||||
fn from_slice_with_depth(mut witness: &[u8], depth: u8) -> Result<Self, ()> {
|
|
||||||
// Skip the first byte, which should be "depth" to signify the length of
|
|
||||||
// the following vector of Pedersen hashes.
|
// the following vector of Pedersen hashes.
|
||||||
if witness[0] != depth {
|
if witness[0] != DEPTH {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
witness = &witness[1..];
|
witness = &witness[1..];
|
||||||
|
@ -533,7 +547,7 @@ impl<Node: Hashable + HashSer> MerklePath<Node> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
if auth_path.len() != depth.into() {
|
if auth_path.len() != usize::from(DEPTH) {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -583,9 +597,8 @@ mod tests {
|
||||||
use incrementalmerkletree::Hashable;
|
use incrementalmerkletree::Hashable;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use proptest::strategy::Strategy;
|
use proptest::strategy::Strategy;
|
||||||
use std::io::{self, Read, Write};
|
|
||||||
|
|
||||||
use crate::sapling::{testing::arb_node, Node};
|
use crate::sapling::{self, testing::arb_node, Node};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
testing::{arb_commitment_tree, TestNode},
|
testing::{arb_commitment_tree, TestNode},
|
||||||
|
@ -628,68 +641,6 @@ mod tests {
|
||||||
"fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
|
"fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493e",
|
||||||
];
|
];
|
||||||
|
|
||||||
const TESTING_DEPTH: u8 = 4;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TestCommitmentTree(CommitmentTree<Node>);
|
|
||||||
|
|
||||||
impl TestCommitmentTree {
|
|
||||||
fn new() -> Self {
|
|
||||||
TestCommitmentTree(CommitmentTree::empty())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(reader: R) -> io::Result<Self> {
|
|
||||||
let tree = CommitmentTree::read(reader)?;
|
|
||||||
Ok(TestCommitmentTree(tree))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
|
|
||||||
self.0.write(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn size(&self) -> usize {
|
|
||||||
self.0.size()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append(&mut self, node: Node) -> Result<(), ()> {
|
|
||||||
self.0.append_inner(node, TESTING_DEPTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root(&self) -> Node {
|
|
||||||
self.0.root_inner(TESTING_DEPTH, PathFiller::empty())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct TestIncrementalWitness(IncrementalWitness<Node>);
|
|
||||||
|
|
||||||
impl TestIncrementalWitness {
|
|
||||||
fn from_tree(tree: &TestCommitmentTree) -> Self {
|
|
||||||
TestIncrementalWitness(IncrementalWitness::from_tree(&tree.0))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(reader: R) -> io::Result<Self> {
|
|
||||||
let witness = IncrementalWitness::read(reader)?;
|
|
||||||
Ok(TestIncrementalWitness(witness))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, writer: W) -> io::Result<()> {
|
|
||||||
self.0.write(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append(&mut self, node: Node) -> Result<(), ()> {
|
|
||||||
self.0.append_inner(node, TESTING_DEPTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn root(&self) -> Node {
|
|
||||||
self.0.root_inner(TESTING_DEPTH)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn path(&self) -> Option<MerklePath<Node>> {
|
|
||||||
self.0.path_inner(TESTING_DEPTH)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_root_test_vectors() {
|
fn empty_root_test_vectors() {
|
||||||
let mut tmp = [0u8; 32];
|
let mut tmp = [0u8; 32];
|
||||||
|
@ -704,7 +655,7 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn sapling_empty_root() {
|
fn sapling_empty_root() {
|
||||||
let mut tmp = [0u8; 32];
|
let mut tmp = [0u8; 32];
|
||||||
CommitmentTree::<Node>::empty()
|
sapling::CommitmentTree::empty()
|
||||||
.root()
|
.root()
|
||||||
.write(&mut tmp[..])
|
.write(&mut tmp[..])
|
||||||
.expect("length is 32 bytes");
|
.expect("length is 32 bytes");
|
||||||
|
@ -716,7 +667,7 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_commitment_tree_roots() {
|
fn empty_commitment_tree_roots() {
|
||||||
let tree = CommitmentTree::<Node>::empty();
|
let tree = sapling::CommitmentTree::empty();
|
||||||
let mut tmp = [0u8; 32];
|
let mut tmp = [0u8; 32];
|
||||||
for (&expected, i) in HEX_EMPTY_ROOTS.iter().zip(0u8..).skip(1) {
|
for (&expected, i) in HEX_EMPTY_ROOTS.iter().zip(0u8..).skip(1) {
|
||||||
tree.root_inner(i, PathFiller::empty())
|
tree.root_inner(i, PathFiller::empty())
|
||||||
|
@ -1053,40 +1004,46 @@ mod tests {
|
||||||
"01f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0003015991131c5c25911b35fcea2a8343e2dfd7a4d5b45493390e0cb184394d91c34901002df68503da9247dfde6585cb8c9fa94897cf21735f8fc1b32116ef474de05c010d6b42350c11df4fcc17987c13d8492ba4e8b3f31eb9baff9be5d8890cfa512d013a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac1500",
|
"01f43e3aac61e5a753062d4d0508c26ceaf5e4c0c58ba3c956e104b5d2cf67c41c0003015991131c5c25911b35fcea2a8343e2dfd7a4d5b45493390e0cb184394d91c34901002df68503da9247dfde6585cb8c9fa94897cf21735f8fc1b32116ef474de05c010d6b42350c11df4fcc17987c13d8492ba4e8b3f31eb9baff9be5d8890cfa512d013a3661bc12b72646c94bc6c92796e81953985ee62d80a9ec3645a9a95740ac1500",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const TESTING_DEPTH: u8 = 4;
|
||||||
|
|
||||||
fn assert_root_eq(root: Node, expected: &str) {
|
fn assert_root_eq(root: Node, expected: &str) {
|
||||||
let mut tmp = [0u8; 32];
|
let mut tmp = [0u8; 32];
|
||||||
root.write(&mut tmp[..]).expect("length is 32 bytes");
|
root.write(&mut tmp[..]).expect("length is 32 bytes");
|
||||||
assert_eq!(hex::encode(tmp), expected);
|
assert_eq!(hex::encode(tmp), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_tree_ser_eq(tree: &TestCommitmentTree, expected: &str) {
|
fn assert_tree_ser_eq(tree: &CommitmentTree<Node, TESTING_DEPTH>, expected: &str) {
|
||||||
// Check that the tree matches its encoding
|
// Check that the tree matches its encoding
|
||||||
let mut tmp = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
tree.write(&mut tmp).unwrap();
|
tree.write(&mut tmp).unwrap();
|
||||||
assert_eq!(hex::encode(&tmp[..]), expected);
|
assert_eq!(hex::encode(&tmp[..]), expected);
|
||||||
|
|
||||||
// Check round-trip encoding
|
// Check round-trip encoding
|
||||||
let decoded = TestCommitmentTree::read(&hex::decode(expected).unwrap()[..]).unwrap();
|
let decoded: CommitmentTree<Node, TESTING_DEPTH> =
|
||||||
|
CommitmentTree::read(&hex::decode(expected).unwrap()[..]).unwrap();
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
decoded.write(&mut tmp).unwrap();
|
decoded.write(&mut tmp).unwrap();
|
||||||
assert_eq!(hex::encode(tmp), expected);
|
assert_eq!(hex::encode(tmp), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_witness_ser_eq(witness: &TestIncrementalWitness, expected: &str) {
|
fn assert_witness_ser_eq(
|
||||||
|
witness: &IncrementalWitness<Node, TESTING_DEPTH>,
|
||||||
|
expected: &str,
|
||||||
|
) {
|
||||||
// Check that the witness matches its encoding
|
// Check that the witness matches its encoding
|
||||||
let mut tmp = Vec::new();
|
let mut tmp = Vec::new();
|
||||||
witness.write(&mut tmp).unwrap();
|
witness.write(&mut tmp).unwrap();
|
||||||
assert_eq!(hex::encode(&tmp[..]), expected);
|
assert_eq!(hex::encode(&tmp[..]), expected);
|
||||||
|
|
||||||
// Check round-trip encoding
|
// Check round-trip encoding
|
||||||
let decoded =
|
let decoded: IncrementalWitness<Node, TESTING_DEPTH> =
|
||||||
TestIncrementalWitness::read(&hex::decode(expected).unwrap()[..]).unwrap();
|
IncrementalWitness::read(&hex::decode(expected).unwrap()[..]).unwrap();
|
||||||
tmp.clear();
|
tmp.clear();
|
||||||
decoded.write(&mut tmp).unwrap();
|
decoded.write(&mut tmp).unwrap();
|
||||||
assert_eq!(hex::encode(tmp), expected);
|
assert_eq!(hex::encode(tmp), expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut tree = TestCommitmentTree::new();
|
let mut tree = CommitmentTree::<Node, TESTING_DEPTH>::empty();
|
||||||
assert_eq!(tree.size(), 0);
|
assert_eq!(tree.size(), 0);
|
||||||
|
|
||||||
let mut witnesses = vec![];
|
let mut witnesses = vec![];
|
||||||
|
@ -1099,7 +1056,7 @@ mod tests {
|
||||||
let cmu = Node::new(cmu[..].try_into().unwrap());
|
let cmu = Node::new(cmu[..].try_into().unwrap());
|
||||||
|
|
||||||
// Witness here
|
// Witness here
|
||||||
witnesses.push((TestIncrementalWitness::from_tree(&tree), last_cmu));
|
witnesses.push((IncrementalWitness::from_tree(tree.clone()), last_cmu));
|
||||||
|
|
||||||
// Now append a commitment to the tree
|
// Now append a commitment to the tree
|
||||||
assert!(tree.append(cmu).is_ok());
|
assert!(tree.append(cmu).is_ok());
|
||||||
|
@ -1119,11 +1076,8 @@ mod tests {
|
||||||
|
|
||||||
if let Some(leaf) = leaf {
|
if let Some(leaf) = leaf {
|
||||||
let path = witness.path().expect("should be able to create a path");
|
let path = witness.path().expect("should be able to create a path");
|
||||||
let expected = MerklePath::from_slice_with_depth(
|
let expected =
|
||||||
&hex::decode(paths[paths_i]).unwrap(),
|
MerklePath::from_slice(&hex::decode(paths[paths_i]).unwrap()).unwrap();
|
||||||
TESTING_DEPTH,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(path, expected);
|
assert_eq!(path, expected);
|
||||||
|
|
||||||
assert_eq!(path.root(*leaf), witness.root());
|
assert_eq!(path.root(*leaf), witness.root());
|
||||||
|
@ -1176,7 +1130,7 @@ mod tests {
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn prop_commitment_tree_roundtrip_str(ct in arb_commitment_tree(32, any::<char>().prop_map(|c| c.to_string()), 8)) {
|
fn prop_commitment_tree_roundtrip_str(ct in arb_commitment_tree::<_, _, 8>(32, any::<char>().prop_map(|c| c.to_string()))) {
|
||||||
let frontier: Frontier<String, 8> = ct.to_frontier();
|
let frontier: Frontier<String, 8> = ct.to_frontier();
|
||||||
let ct0 = CommitmentTree::from_frontier(&frontier);
|
let ct0 = CommitmentTree::from_frontier(&frontier);
|
||||||
assert_eq!(ct, ct0);
|
assert_eq!(ct, ct0);
|
||||||
|
@ -1185,7 +1139,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prop_commitment_tree_roundtrip_node(ct in arb_commitment_tree(32, arb_node(), 8)) {
|
fn prop_commitment_tree_roundtrip_node(ct in arb_commitment_tree::<_, _, 8>(32, arb_node())) {
|
||||||
let frontier: Frontier<Node, 8> = ct.to_frontier();
|
let frontier: Frontier<Node, 8> = ct.to_frontier();
|
||||||
let ct0 = CommitmentTree::from_frontier(&frontier);
|
let ct0 = CommitmentTree::from_frontier(&frontier);
|
||||||
assert_eq!(ct, ct0);
|
assert_eq!(ct, ct0);
|
||||||
|
@ -1194,21 +1148,21 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn prop_commitment_tree_roundtrip_ser(ct in arb_commitment_tree(32, arb_node(), 8)) {
|
fn prop_commitment_tree_roundtrip_ser(ct in arb_commitment_tree::<_, _, 8>(32, arb_node())) {
|
||||||
let mut serialized = vec![];
|
let mut serialized = vec![];
|
||||||
assert_matches!(ct.write(&mut serialized), Ok(()));
|
assert_matches!(ct.write(&mut serialized), Ok(()));
|
||||||
assert_matches!(CommitmentTree::read(&serialized[..]), Ok(ct_out) if ct == ct_out);
|
assert_matches!(CommitmentTree::<_, 8>::read(&serialized[..]), Ok(ct_out) if ct == ct_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_commitment_tree_complete() {
|
fn test_commitment_tree_complete() {
|
||||||
let mut t: CommitmentTree<TestNode> = CommitmentTree::empty();
|
let mut t: CommitmentTree<TestNode, 32> = CommitmentTree::empty();
|
||||||
for n in 1u64..=32 {
|
for n in 1u64..=32 {
|
||||||
t.append(TestNode(n)).unwrap();
|
t.append(TestNode(n)).unwrap();
|
||||||
// every tree of a power-of-two height is complete
|
// every tree of a power-of-two height is complete
|
||||||
let is_complete = n.count_ones() == 1;
|
let is_complete = n.count_ones() == 1;
|
||||||
let level = 63 - n.leading_zeros(); //log2
|
let level = usize::BITS - 1 - n.leading_zeros(); //log2
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
is_complete,
|
is_complete,
|
||||||
t.is_complete(level.try_into().unwrap()),
|
t.is_complete(level.try_into().unwrap()),
|
||||||
|
@ -1238,18 +1192,21 @@ pub mod testing {
|
||||||
|
|
||||||
use super::{CommitmentTree, HashSer};
|
use super::{CommitmentTree, HashSer};
|
||||||
|
|
||||||
pub fn arb_commitment_tree<Node: Hashable + Clone + Debug, T: Strategy<Value = Node>>(
|
pub fn arb_commitment_tree<
|
||||||
|
Node: Hashable + Clone + Debug,
|
||||||
|
T: Strategy<Value = Node>,
|
||||||
|
const DEPTH: u8,
|
||||||
|
>(
|
||||||
min_size: usize,
|
min_size: usize,
|
||||||
arb_node: T,
|
arb_node: T,
|
||||||
depth: u8,
|
) -> impl Strategy<Value = CommitmentTree<Node, DEPTH>> {
|
||||||
) -> impl Strategy<Value = CommitmentTree<Node>> {
|
assert!((1 << DEPTH) >= min_size + 100);
|
||||||
assert!((1 << depth) >= min_size + 100);
|
|
||||||
vec(arb_node, min_size..(min_size + 100)).prop_map(move |v| {
|
vec(arb_node, min_size..(min_size + 100)).prop_map(move |v| {
|
||||||
let mut tree = CommitmentTree::empty();
|
let mut tree = CommitmentTree::empty();
|
||||||
for node in v.into_iter() {
|
for node in v.into_iter() {
|
||||||
tree.append(node).unwrap();
|
tree.append(node).unwrap();
|
||||||
}
|
}
|
||||||
tree.parents.resize_with((depth - 1).into(), || None);
|
tree.parents.resize_with((DEPTH - 1).into(), || None);
|
||||||
tree
|
tree
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use orchard::tree::MerkleHashOrchard;
|
||||||
use zcash_encoding::{Optional, Vector};
|
use zcash_encoding::{Optional, Vector};
|
||||||
|
|
||||||
use super::{CommitmentTree, HashSer};
|
use super::{CommitmentTree, HashSer};
|
||||||
|
use crate::sapling;
|
||||||
|
|
||||||
pub const SER_V1: u8 = 1;
|
pub const SER_V1: u8 = 1;
|
||||||
pub const SER_V2: u8 = 2;
|
pub const SER_V2: u8 = 2;
|
||||||
|
@ -78,8 +79,8 @@ pub fn read_address<R: Read>(mut reader: R) -> io::Result<Address> {
|
||||||
|
|
||||||
pub fn read_frontier_v0<H: Hashable + HashSer + Clone, R: Read>(
|
pub fn read_frontier_v0<H: Hashable + HashSer + Clone, R: Read>(
|
||||||
mut reader: R,
|
mut reader: R,
|
||||||
) -> io::Result<Frontier<H, 32>> {
|
) -> io::Result<Frontier<H, { sapling::NOTE_COMMITMENT_TREE_DEPTH }>> {
|
||||||
let tree = CommitmentTree::read(&mut reader)?;
|
let tree = CommitmentTree::<H, { sapling::NOTE_COMMITMENT_TREE_DEPTH }>::read(&mut reader)?;
|
||||||
|
|
||||||
Ok(tree.to_frontier())
|
Ok(tree.to_frontier())
|
||||||
}
|
}
|
||||||
|
@ -317,7 +318,7 @@ mod tests {
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
#[test]
|
#[test]
|
||||||
fn frontier_serialization_v0(t in arb_commitment_tree(0, sapling::arb_node(), 32))
|
fn frontier_serialization_v0(t in arb_commitment_tree::<_, _, 32>(0, sapling::arb_node()))
|
||||||
{
|
{
|
||||||
let mut buffer = vec![];
|
let mut buffer = vec![];
|
||||||
t.write(&mut buffer).unwrap();
|
t.write(&mut buffer).unwrap();
|
||||||
|
@ -328,7 +329,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn frontier_serialization_v1(t in arb_commitment_tree(1, sapling::arb_node(), 32))
|
fn frontier_serialization_v1(t in arb_commitment_tree::<_, _, 32>(1, sapling::arb_node()))
|
||||||
{
|
{
|
||||||
let original: Frontier<Node, 32> = t.to_frontier();
|
let original: Frontier<Node, 32> = t.to_frontier();
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub use address::PaymentAddress;
|
||||||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||||
pub use note::{nullifier::Nullifier, Note, Rseed};
|
pub use note::{nullifier::Nullifier, Note, Rseed};
|
||||||
pub use tree::{
|
pub use tree::{
|
||||||
merkle_hash, Node, SAPLING_COMMITMENT_TREE_DEPTH, SAPLING_COMMITMENT_TREE_DEPTH_U8,
|
merkle_hash, CommitmentTree, IncrementalWitness, MerklePath, Node, NOTE_COMMITMENT_TREE_DEPTH,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create the spendAuthSig for a Sapling SpendDescription.
|
/// Create the spendAuthSig for a Sapling SpendDescription.
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
//! Abstractions over the proving system and parameters.
|
//! Abstractions over the proving system and parameters.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
|
self,
|
||||||
redjubjub::{PublicKey, Signature},
|
redjubjub::{PublicKey, Signature},
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
Node,
|
|
||||||
},
|
},
|
||||||
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
||||||
};
|
};
|
||||||
|
@ -35,7 +34,7 @@ pub trait TxProver {
|
||||||
ar: jubjub::Fr,
|
ar: jubjub::Fr,
|
||||||
value: u64,
|
value: u64,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: sapling::MerklePath,
|
||||||
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()>;
|
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()>;
|
||||||
|
|
||||||
/// Create the value commitment and proof for a Sapling [`OutputDescription`],
|
/// Create the value commitment and proof for a Sapling [`OutputDescription`],
|
||||||
|
@ -69,11 +68,11 @@ pub mod mock {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
constants::SPENDING_KEY_GENERATOR,
|
constants::SPENDING_KEY_GENERATOR,
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
|
self,
|
||||||
redjubjub::{PublicKey, Signature},
|
redjubjub::{PublicKey, Signature},
|
||||||
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
||||||
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed,
|
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
|
||||||
},
|
},
|
||||||
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
||||||
};
|
};
|
||||||
|
@ -96,7 +95,7 @@ pub mod mock {
|
||||||
ar: jubjub::Fr,
|
ar: jubjub::Fr,
|
||||||
value: u64,
|
value: u64,
|
||||||
_anchor: bls12_381::Scalar,
|
_anchor: bls12_381::Scalar,
|
||||||
_merkle_path: MerklePath<Node>,
|
_merkle_path: sapling::MerklePath,
|
||||||
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
|
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
|
||||||
let mut rng = OsRng;
|
let mut rng = OsRng;
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,18 @@ use super::{
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
pedersen_hash::{pedersen_hash, Personalization},
|
pedersen_hash::{pedersen_hash, Personalization},
|
||||||
};
|
};
|
||||||
use crate::merkle_tree::HashSer;
|
use crate::merkle_tree::{self, HashSer};
|
||||||
|
|
||||||
pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
|
pub const NOTE_COMMITMENT_TREE_DEPTH: u8 = 32;
|
||||||
pub const SAPLING_COMMITMENT_TREE_DEPTH_U8: u8 = 32;
|
pub type CommitmentTree = merkle_tree::CommitmentTree<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
||||||
|
pub type IncrementalWitness = merkle_tree::IncrementalWitness<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
||||||
|
pub type MerklePath = merkle_tree::MerklePath<Node, NOTE_COMMITMENT_TREE_DEPTH>;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref UNCOMMITTED_SAPLING: bls12_381::Scalar = bls12_381::Scalar::one();
|
static ref UNCOMMITTED_SAPLING: bls12_381::Scalar = bls12_381::Scalar::one();
|
||||||
static ref EMPTY_ROOTS: Vec<Node> = {
|
static ref EMPTY_ROOTS: Vec<Node> = {
|
||||||
let mut v = vec![Node::empty_leaf()];
|
let mut v = vec![Node::empty_leaf()];
|
||||||
for d in 0..SAPLING_COMMITMENT_TREE_DEPTH_U8 {
|
for d in 0..NOTE_COMMITMENT_TREE_DEPTH {
|
||||||
let next = Node::combine(d.into(), &v[usize::from(d)], &v[usize::from(d)]);
|
let next = Node::combine(d.into(), &v[usize::from(d)], &v[usize::from(d)]);
|
||||||
v.push(next);
|
v.push(next);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,6 @@ use std::error;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
|
|
||||||
#[cfg(not(feature = "zfuture"))]
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -15,14 +12,13 @@ use crate::{
|
||||||
keys::OutgoingViewingKey,
|
keys::OutgoingViewingKey,
|
||||||
legacy::TransparentAddress,
|
legacy::TransparentAddress,
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
merkle_tree::MerklePath,
|
sapling::{self, prover::TxProver, value::NoteValue, Diversifier, Note, PaymentAddress},
|
||||||
sapling::{prover::TxProver, value::NoteValue, Diversifier, Node, Note, PaymentAddress},
|
|
||||||
transaction::{
|
transaction::{
|
||||||
components::{
|
components::{
|
||||||
amount::{Amount, BalanceError},
|
amount::{Amount, BalanceError},
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
|
||||||
builder::{SaplingBuilder, SaplingMetadata},
|
fees as sapling_fees,
|
||||||
},
|
},
|
||||||
transparent::{self, builder::TransparentBuilder},
|
transparent::{self, builder::TransparentBuilder},
|
||||||
},
|
},
|
||||||
|
@ -67,7 +63,7 @@ pub enum Error<FeeError> {
|
||||||
/// An error occurred in constructing the transparent parts of a transaction.
|
/// An error occurred in constructing the transparent parts of a transaction.
|
||||||
TransparentBuild(transparent::builder::Error),
|
TransparentBuild(transparent::builder::Error),
|
||||||
/// An error occurred in constructing the Sapling parts of a transaction.
|
/// An error occurred in constructing the Sapling parts of a transaction.
|
||||||
SaplingBuild(sapling::builder::Error),
|
SaplingBuild(sapling_builder::Error),
|
||||||
/// An error occurred in constructing the TZE parts of a transaction.
|
/// An error occurred in constructing the TZE parts of a transaction.
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
TzeBuild(tze::builder::Error),
|
TzeBuild(tze::builder::Error),
|
||||||
|
@ -144,7 +140,7 @@ pub struct Builder<'a, P, R> {
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
|
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
|
||||||
#[cfg(not(feature = "zfuture"))]
|
#[cfg(not(feature = "zfuture"))]
|
||||||
tze_builder: PhantomData<&'a ()>,
|
tze_builder: std::marker::PhantomData<&'a ()>,
|
||||||
progress_notifier: Option<Sender<Progress>>,
|
progress_notifier: Option<Sender<Progress>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,13 +169,13 @@ impl<'a, P, R> Builder<'a, P, R> {
|
||||||
|
|
||||||
/// Returns the set of Sapling inputs currently committed to be consumed
|
/// Returns the set of Sapling inputs currently committed to be consumed
|
||||||
/// by the transaction.
|
/// by the transaction.
|
||||||
pub fn sapling_inputs(&self) -> &[impl sapling::fees::InputView<()>] {
|
pub fn sapling_inputs(&self) -> &[impl sapling_fees::InputView<()>] {
|
||||||
self.sapling_builder.inputs()
|
self.sapling_builder.inputs()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the set of Sapling outputs currently set to be produced by
|
/// Returns the set of Sapling outputs currently set to be produced by
|
||||||
/// the transaction.
|
/// the transaction.
|
||||||
pub fn sapling_outputs(&self) -> &[impl sapling::fees::OutputView] {
|
pub fn sapling_outputs(&self) -> &[impl sapling_fees::OutputView] {
|
||||||
self.sapling_builder.outputs()
|
self.sapling_builder.outputs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,7 +222,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
tze_builder: TzeBuilder::empty(),
|
tze_builder: TzeBuilder::empty(),
|
||||||
#[cfg(not(feature = "zfuture"))]
|
#[cfg(not(feature = "zfuture"))]
|
||||||
tze_builder: PhantomData,
|
tze_builder: std::marker::PhantomData,
|
||||||
progress_notifier: None,
|
progress_notifier: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -240,8 +236,8 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
||||||
extsk: ExtendedSpendingKey,
|
extsk: ExtendedSpendingKey,
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
note: Note,
|
note: Note,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: sapling::MerklePath,
|
||||||
) -> Result<(), sapling::builder::Error> {
|
) -> Result<(), sapling_builder::Error> {
|
||||||
self.sapling_builder
|
self.sapling_builder
|
||||||
.add_spend(&mut self.rng, extsk, diversifier, note, merkle_path)
|
.add_spend(&mut self.rng, extsk, diversifier, note, merkle_path)
|
||||||
}
|
}
|
||||||
|
@ -253,9 +249,9 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
||||||
to: PaymentAddress,
|
to: PaymentAddress,
|
||||||
value: Amount,
|
value: Amount,
|
||||||
memo: MemoBytes,
|
memo: MemoBytes,
|
||||||
) -> Result<(), sapling::builder::Error> {
|
) -> Result<(), sapling_builder::Error> {
|
||||||
if value.is_negative() {
|
if value.is_negative() {
|
||||||
return Err(sapling::builder::Error::InvalidAmount);
|
return Err(sapling_builder::Error::InvalidAmount);
|
||||||
}
|
}
|
||||||
self.sapling_builder.add_output(
|
self.sapling_builder.add_output(
|
||||||
&mut self.rng,
|
&mut self.rng,
|
||||||
|
@ -555,8 +551,8 @@ mod tests {
|
||||||
sapling::{Node, Rseed},
|
sapling::{Node, Rseed},
|
||||||
transaction::components::{
|
transaction::components::{
|
||||||
amount::{Amount, DEFAULT_FEE},
|
amount::{Amount, DEFAULT_FEE},
|
||||||
sapling::builder::{self as build_s},
|
sapling::builder::{self as sapling_builder},
|
||||||
transparent::builder::{self as build_t},
|
transparent::builder::{self as transparent_builder},
|
||||||
},
|
},
|
||||||
zip32::ExtendedSpendingKey,
|
zip32::ExtendedSpendingKey,
|
||||||
};
|
};
|
||||||
|
@ -567,9 +563,6 @@ mod tests {
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use super::TzeBuilder;
|
use super::TzeBuilder;
|
||||||
|
|
||||||
#[cfg(not(feature = "zfuture"))]
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
#[cfg(feature = "transparent-inputs")]
|
#[cfg(feature = "transparent-inputs")]
|
||||||
use crate::{
|
use crate::{
|
||||||
legacy::keys::{AccountPrivKey, IncomingViewingKey},
|
legacy::keys::{AccountPrivKey, IncomingViewingKey},
|
||||||
|
@ -599,7 +592,7 @@ mod tests {
|
||||||
Amount::from_i64(-1).unwrap(),
|
Amount::from_i64(-1).unwrap(),
|
||||||
MemoBytes::empty()
|
MemoBytes::empty()
|
||||||
),
|
),
|
||||||
Err(build_s::Error::InvalidAmount)
|
Err(sapling_builder::Error::InvalidAmount)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -626,7 +619,7 @@ mod tests {
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
tze_builder: TzeBuilder::empty(),
|
tze_builder: TzeBuilder::empty(),
|
||||||
#[cfg(not(feature = "zfuture"))]
|
#[cfg(not(feature = "zfuture"))]
|
||||||
tze_builder: PhantomData,
|
tze_builder: std::marker::PhantomData,
|
||||||
progress_notifier: None,
|
progress_notifier: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -672,9 +665,9 @@ mod tests {
|
||||||
|
|
||||||
let note1 = to.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
|
let note1 = to.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
|
||||||
let cmu1 = Node::from_cmu(¬e1.cmu());
|
let cmu1 = Node::from_cmu(¬e1.cmu());
|
||||||
let mut tree = CommitmentTree::empty();
|
let mut tree = CommitmentTree::<Node, 32>::empty();
|
||||||
tree.append(cmu1).unwrap();
|
tree.append(cmu1).unwrap();
|
||||||
let witness1 = IncrementalWitness::from_tree(&tree);
|
let witness1 = IncrementalWitness::from_tree(tree);
|
||||||
|
|
||||||
let tx_height = TEST_NETWORK
|
let tx_height = TEST_NETWORK
|
||||||
.activation_height(NetworkUpgrade::Sapling)
|
.activation_height(NetworkUpgrade::Sapling)
|
||||||
|
@ -697,7 +690,7 @@ mod tests {
|
||||||
// that a binding signature was attempted
|
// that a binding signature was attempted
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
builder.mock_build(),
|
builder.mock_build(),
|
||||||
Err(Error::SaplingBuild(build_s::Error::BindingSig))
|
Err(Error::SaplingBuild(sapling_builder::Error::BindingSig))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -712,7 +705,7 @@ mod tests {
|
||||||
&TransparentAddress::PublicKey([0; 20]),
|
&TransparentAddress::PublicKey([0; 20]),
|
||||||
Amount::from_i64(-1).unwrap(),
|
Amount::from_i64(-1).unwrap(),
|
||||||
),
|
),
|
||||||
Err(build_t::Error::InvalidAmount)
|
Err(transparent_builder::Error::InvalidAmount)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -780,9 +773,9 @@ mod tests {
|
||||||
|
|
||||||
let note1 = to.create_note(50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
|
let note1 = to.create_note(50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
|
||||||
let cmu1 = Node::from_cmu(¬e1.cmu());
|
let cmu1 = Node::from_cmu(¬e1.cmu());
|
||||||
let mut tree = CommitmentTree::empty();
|
let mut tree = CommitmentTree::<Node, 32>::empty();
|
||||||
tree.append(cmu1).unwrap();
|
tree.append(cmu1).unwrap();
|
||||||
let mut witness1 = IncrementalWitness::from_tree(&tree);
|
let mut witness1 = IncrementalWitness::from_tree(tree.clone());
|
||||||
|
|
||||||
// Fail if there is insufficient input
|
// Fail if there is insufficient input
|
||||||
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.00001 t-ZEC fee, 0.00050999 z-ZEC in
|
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.00001 t-ZEC fee, 0.00050999 z-ZEC in
|
||||||
|
@ -820,7 +813,7 @@ mod tests {
|
||||||
let cmu2 = Node::from_cmu(¬e2.cmu());
|
let cmu2 = Node::from_cmu(¬e2.cmu());
|
||||||
tree.append(cmu2).unwrap();
|
tree.append(cmu2).unwrap();
|
||||||
witness1.append(cmu2).unwrap();
|
witness1.append(cmu2).unwrap();
|
||||||
let witness2 = IncrementalWitness::from_tree(&tree);
|
let witness2 = IncrementalWitness::from_tree(tree);
|
||||||
|
|
||||||
// Succeeds if there is sufficient input
|
// Succeeds if there is sufficient input
|
||||||
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.0006 z-ZEC in
|
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.0006 z-ZEC in
|
||||||
|
@ -856,7 +849,7 @@ mod tests {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
builder.mock_build(),
|
builder.mock_build(),
|
||||||
Err(Error::SaplingBuild(build_s::Error::BindingSig))
|
Err(Error::SaplingBuild(sapling_builder::Error::BindingSig))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@ use crate::{
|
||||||
consensus::{self, BlockHeight},
|
consensus::{self, BlockHeight},
|
||||||
keys::OutgoingViewingKey,
|
keys::OutgoingViewingKey,
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
keys::SaplingIvk,
|
keys::SaplingIvk,
|
||||||
note_encryption::sapling_note_encryption,
|
note_encryption::sapling_note_encryption,
|
||||||
|
@ -19,7 +18,7 @@ use crate::{
|
||||||
spend_sig_internal,
|
spend_sig_internal,
|
||||||
util::generate_random_rseed_internal,
|
util::generate_random_rseed_internal,
|
||||||
value::{NoteValue, ValueSum},
|
value::{NoteValue, ValueSum},
|
||||||
Diversifier, Node, Note, PaymentAddress,
|
Diversifier, MerklePath, Node, Note, PaymentAddress,
|
||||||
},
|
},
|
||||||
transaction::{
|
transaction::{
|
||||||
builder::Progress,
|
builder::Progress,
|
||||||
|
@ -67,7 +66,7 @@ pub struct SpendDescriptionInfo {
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
note: Note,
|
note: Note,
|
||||||
alpha: jubjub::Fr,
|
alpha: jubjub::Fr,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: MerklePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fees::InputView<()> for SpendDescriptionInfo {
|
impl fees::InputView<()> for SpendDescriptionInfo {
|
||||||
|
@ -280,7 +279,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
||||||
extsk: ExtendedSpendingKey,
|
extsk: ExtendedSpendingKey,
|
||||||
diversifier: Diversifier,
|
diversifier: Diversifier,
|
||||||
note: Note,
|
note: Note,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: MerklePath,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// Consistency check: all anchors must equal the first one
|
// Consistency check: all anchors must equal the first one
|
||||||
let node = Node::from_cmu(¬e.cmu());
|
let node = Node::from_cmu(¬e.cmu());
|
||||||
|
@ -392,7 +391,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
||||||
|
|
||||||
let nullifier = spend.note.nf(
|
let nullifier = spend.note.nf(
|
||||||
&proof_generation_key.to_viewing_key().nk,
|
&proof_generation_key.to_viewing_key().nk,
|
||||||
spend.merkle_path.position,
|
spend.merkle_path.position(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let (zkproof, cv, rk) = prover
|
let (zkproof, cv, rk) = prover
|
||||||
|
@ -610,8 +609,8 @@ pub mod testing {
|
||||||
n_notes
|
n_notes
|
||||||
),
|
),
|
||||||
commitment_trees in vec(
|
commitment_trees in vec(
|
||||||
arb_commitment_tree(n_notes, arb_node(), 32).prop_map(
|
arb_commitment_tree::<_, _, 32>(n_notes, arb_node()).prop_map(
|
||||||
|t| IncrementalWitness::from_tree(&t).path().unwrap()
|
|t| IncrementalWitness::from_tree(t).path().unwrap()
|
||||||
),
|
),
|
||||||
n_notes
|
n_notes
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,6 +7,9 @@ and this library adheres to Rust's notion of
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- `circuit::sapling::TREE_DEPTH` use `zcash_primitives::sapling::NOTE_COMMITMENT_TREE_DEPTH` instead
|
||||||
|
|
||||||
## [0.11.0] - 2023-04-15
|
## [0.11.0] - 2023-04-15
|
||||||
### Changed
|
### Changed
|
||||||
- Bumped dependencies to `bls12_381 0.8`, `group 0.13`, `jubjub 0.10`,
|
- Bumped dependencies to `bls12_381 0.8`, `group 0.13`, `jubjub 0.10`,
|
||||||
|
|
|
@ -6,9 +6,7 @@ use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||||
|
|
||||||
use zcash_primitives::constants;
|
use zcash_primitives::constants;
|
||||||
|
|
||||||
use zcash_primitives::sapling::{
|
use zcash_primitives::sapling::{PaymentAddress, ProofGenerationKey};
|
||||||
PaymentAddress, ProofGenerationKey, SAPLING_COMMITMENT_TREE_DEPTH,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::ecc;
|
use super::ecc;
|
||||||
use super::pedersen_hash;
|
use super::pedersen_hash;
|
||||||
|
@ -29,8 +27,6 @@ use group::ff::PrimeFieldBits;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use zcash_primitives::sapling::value::NoteValue;
|
use zcash_primitives::sapling::value::NoteValue;
|
||||||
|
|
||||||
pub const TREE_DEPTH: usize = SAPLING_COMMITMENT_TREE_DEPTH;
|
|
||||||
|
|
||||||
/// The opening (value and randomness) of a Sapling value commitment.
|
/// The opening (value and randomness) of a Sapling value commitment.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ValueCommitmentOpening {
|
pub struct ValueCommitmentOpening {
|
||||||
|
|
|
@ -4,12 +4,11 @@ use bellman::groth16::{Parameters, PreparedVerifyingKey};
|
||||||
use bls12_381::Bls12;
|
use bls12_381::Bls12;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
prover::TxProver,
|
prover::TxProver,
|
||||||
redjubjub::{PublicKey, Signature},
|
redjubjub::{PublicKey, Signature},
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed,
|
Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
|
||||||
},
|
},
|
||||||
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
||||||
};
|
};
|
||||||
|
@ -154,7 +153,7 @@ impl TxProver for LocalTxProver {
|
||||||
ar: jubjub::Fr,
|
ar: jubjub::Fr,
|
||||||
value: u64,
|
value: u64,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: MerklePath,
|
||||||
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
|
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
|
||||||
let (proof, cv, rk) = ctx.spend_proof(
|
let (proof, cv, rk) = ctx.spend_proof(
|
||||||
proof_generation_key,
|
proof_generation_key,
|
||||||
|
|
|
@ -7,11 +7,10 @@ use group::{Curve, GroupEncoding};
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
||||||
merkle_tree::MerklePath,
|
|
||||||
sapling::{
|
sapling::{
|
||||||
redjubjub::{PublicKey, Signature},
|
redjubjub::{PublicKey, Signature},
|
||||||
value::{CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment},
|
value::{CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment},
|
||||||
Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed,
|
Diversifier, MerklePath, Note, PaymentAddress, ProofGenerationKey, Rseed,
|
||||||
},
|
},
|
||||||
transaction::components::Amount,
|
transaction::components::Amount,
|
||||||
};
|
};
|
||||||
|
@ -52,7 +51,7 @@ impl SaplingProvingContext {
|
||||||
ar: jubjub::Fr,
|
ar: jubjub::Fr,
|
||||||
value: u64,
|
value: u64,
|
||||||
anchor: bls12_381::Scalar,
|
anchor: bls12_381::Scalar,
|
||||||
merkle_path: MerklePath<Node>,
|
merkle_path: MerklePath,
|
||||||
proving_key: &Parameters<Bls12>,
|
proving_key: &Parameters<Bls12>,
|
||||||
verifying_key: &PreparedVerifyingKey<Bls12>,
|
verifying_key: &PreparedVerifyingKey<Bls12>,
|
||||||
) -> Result<(Proof<Bls12>, ValueCommitment, PublicKey), ()> {
|
) -> Result<(Proof<Bls12>, ValueCommitment, PublicKey), ()> {
|
||||||
|
@ -84,7 +83,7 @@ impl SaplingProvingContext {
|
||||||
// Let's compute the nullifier while we have the position
|
// Let's compute the nullifier while we have the position
|
||||||
let note = Note::from_parts(payment_address, NoteValue::from_raw(value), rseed);
|
let note = Note::from_parts(payment_address, NoteValue::from_raw(value), rseed);
|
||||||
|
|
||||||
let nullifier = note.nf(&viewing_key.nk, merkle_path.position);
|
let nullifier = note.nf(&viewing_key.nk, merkle_path.position());
|
||||||
|
|
||||||
// We now have the full witness for our circuit
|
// We now have the full witness for our circuit
|
||||||
let instance = Spend {
|
let instance = Spend {
|
||||||
|
@ -94,7 +93,7 @@ impl SaplingProvingContext {
|
||||||
commitment_randomness: Some(note.rcm()),
|
commitment_randomness: Some(note.rcm()),
|
||||||
ar: Some(ar),
|
ar: Some(ar),
|
||||||
auth_path: merkle_path
|
auth_path: merkle_path
|
||||||
.auth_path
|
.auth_path()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(node, b)| Some(((*node).into(), *b)))
|
.map(|(node, b)| Some(((*node).into(), *b)))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
|
Loading…
Reference in New Issue