Merge remote-tracking branch 'upstream/main' into hotfix/zcash_client_sqlite-0.7.1

This commit is contained in:
Kris Nuttycombe 2023-05-18 14:54:08 -06:00
commit d4073cb2ec
37 changed files with 694 additions and 1398 deletions

View File

@ -121,7 +121,7 @@ jobs:
- name: Generate coverage report
run: cargo tarpaulin --engine llvm --all-features --release --timeout 600 --out Xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3.1.2
uses: codecov/codecov-action@v3.1.3
doc-links:
name: Intra-doc links

View File

@ -21,3 +21,6 @@ codegen-units = 1
[patch.crates-io]
zcash_encoding = { path = "components/zcash_encoding" }
zcash_note_encryption = { path = "components/zcash_note_encryption" }
bridgetree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "ea1686e8f8f6c1e41aa97251a7eb4fadfd33df47" }
incrementalmerkletree = { git = "https://github.com/zcash/incrementalmerkletree.git", rev = "ea1686e8f8f6c1e41aa97251a7eb4fadfd33df47" }
orchard = { git = "https://github.com/zcash/orchard.git", rev = "8bc53ecbde0944f59f2321f06f2f4171975c7288" }

View File

@ -6,6 +6,11 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Bumped dependencies to `hdwallet 0.4`.
- `WalletRead::get_memo` now returns `Result<Option<Memo>, Self::Error>`
instead of `Result<Memo, Self::Error>` in order to make representable
wallet states where the full note plaintext is not available.
## [0.9.0] - 2023-04-28
### Added
@ -17,7 +22,7 @@ and this library adheres to Rust's notion of
to allow reuse of the data structure for non-Sapling contexts.
- `data_api::SentTransactionOutput` must now be constructed using
`SentTransactionOutput::from_parts`. The internal state of `SentTransactionOutput`
is now private, and accessible via methods that have the same names as the
is now private, and accessible via methods that have the same names as the
previously exposed fields.
### Renamed

View File

@ -36,7 +36,7 @@ bech32 = "0.9"
bs58 = { version = "0.4", features = ["check"] }
# - Errors
hdwallet = { version = "0.3.1", optional = true }
hdwallet = { version = "0.4", optional = true }
# - Logging and metrics
memuse = "0.2"

View File

@ -10,8 +10,7 @@ use zcash_primitives::{
consensus::BlockHeight,
legacy::TransparentAddress,
memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{self, Node, Nullifier, PaymentAddress},
sapling::{self, Nullifier, PaymentAddress},
transaction::{
components::{amount::Amount, OutPoint},
Transaction, TxId,
@ -156,8 +155,10 @@ pub trait WalletRead {
/// Returns the memo for a note.
///
/// Implementations of this method must return an error if the note identifier
/// does not appear in the backing data store.
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Memo, Self::Error>;
/// does not appear in the backing data store. Returns `Ok(None)` if the note
/// is known to the wallet but memo data has not yet been populated for that
/// note.
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Option<Memo>, Self::Error>;
/// Returns a transaction.
fn get_transaction(&self, id_tx: Self::TxRef) -> Result<Transaction, Self::Error>;
@ -166,14 +167,14 @@ pub trait WalletRead {
fn get_commitment_tree(
&self,
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.
#[allow(clippy::type_complexity)]
fn get_witnesses(
&self,
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
/// associated account IDs, that are either unspent or have not yet been confirmed as
@ -239,7 +240,7 @@ pub struct PrunedBlock<'a> {
pub block_height: BlockHeight,
pub block_hash: BlockHash,
pub block_time: u32,
pub commitment_tree: &'a CommitmentTree<Node>,
pub commitment_tree: &'a sapling::CommitmentTree,
pub transactions: &'a Vec<WalletTx<Nullifier>>,
}
@ -388,8 +389,8 @@ pub trait WalletWrite: WalletRead {
fn advance_by_block(
&mut self,
block: &PrunedBlock,
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error>;
updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error>;
/// Caches a decrypted transaction in the persistent wallet store.
fn store_decrypted_tx(
@ -433,8 +434,7 @@ pub mod testing {
consensus::{BlockHeight, Network},
legacy::TransparentAddress,
memo::Memo,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Nullifier},
sapling::{self, Nullifier},
transaction::{
components::{Amount, OutPoint},
Transaction, TxId,
@ -514,8 +514,8 @@ pub mod testing {
Ok(Amount::zero())
}
fn get_memo(&self, _id_note: Self::NoteRef) -> Result<Memo, Self::Error> {
Ok(Memo::Empty)
fn get_memo(&self, _id_note: Self::NoteRef) -> Result<Option<Memo>, Self::Error> {
Ok(None)
}
fn get_transaction(&self, _id_tx: Self::TxRef) -> Result<Transaction, Self::Error> {
@ -525,7 +525,7 @@ pub mod testing {
fn get_commitment_tree(
&self,
_block_height: BlockHeight,
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
Ok(None)
}
@ -533,7 +533,7 @@ pub mod testing {
fn get_witnesses(
&self,
_block_height: BlockHeight,
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
Ok(Vec::new())
}
@ -613,8 +613,8 @@ pub mod testing {
fn advance_by_block(
&mut self,
_block: &PrunedBlock,
_updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
_updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
Ok(vec![])
}

View File

@ -89,8 +89,7 @@ use std::convert::Infallible;
use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
merkle_tree::CommitmentTree,
sapling::{note_encryption::PreparedIncomingViewingKey, Nullifier},
sapling::{self, note_encryption::PreparedIncomingViewingKey, Nullifier},
zip32::Scope,
};
@ -237,11 +236,11 @@ where
// Get the most recent CommitmentTree
let mut tree = last_height.map_or_else(
|| Ok(CommitmentTree::empty()),
|| Ok(sapling::CommitmentTree::empty()),
|h| {
data_db
.get_commitment_tree(h)
.map(|t| t.unwrap_or_else(CommitmentTree::empty))
.map(|t| t.unwrap_or_else(sapling::CommitmentTree::empty))
.map_err(Error::Wallet)
},
)?;

View File

@ -4,7 +4,6 @@ use std::fmt::Debug;
use zcash_primitives::{
consensus::{self, NetworkUpgrade},
memo::MemoBytes,
merkle_tree::MerklePath,
sapling::{
self,
note_encryption::{try_sapling_note_decryption, PreparedIncomingViewingKey},
@ -185,7 +184,9 @@ where
/// [`sapling::TxProver`]: zcash_primitives::sapling::prover::TxProver
#[allow(clippy::too_many_arguments)]
#[allow(clippy::type_complexity)]
#[deprecated(note = "Use `spend` instead.")]
#[deprecated(
note = "Use `spend` instead. `create_spend_to_address` uses a fixed fee of 10000 zatoshis, which is not compliant with ZIP 317."
)]
pub fn create_spend_to_address<DbT, ParamsT>(
wallet_db: &mut DbT,
params: &ParamsT,
@ -222,7 +223,9 @@ where
"It should not be possible for this to violate ZIP 321 request construction invariants.",
);
let change_strategy = fees::fixed::SingleOutputChangeStrategy::new(fixed::FeeRule::standard());
#[allow(deprecated)]
let fee_rule = fixed::FeeRule::standard();
let change_strategy = fees::fixed::SingleOutputChangeStrategy::new(fee_rule);
spend(
wallet_db,
params,
@ -701,11 +704,7 @@ fn select_key_for_note<N>(
selected: &SpendableNote<N>,
extsk: &ExtendedSpendingKey,
dfvk: &DiversifiableFullViewingKey,
) -> Option<(
sapling::Note,
ExtendedSpendingKey,
MerklePath<sapling::Node>,
)> {
) -> Option<(sapling::Note, ExtendedSpendingKey, sapling::MerklePath)> {
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

View File

@ -173,7 +173,9 @@ mod tests {
#[test]
fn change_without_dust() {
let change_strategy = SingleOutputChangeStrategy::new(FixedFeeRule::standard());
#[allow(deprecated)]
let fee_rule = FixedFeeRule::standard();
let change_strategy = SingleOutputChangeStrategy::new(fee_rule);
// spend a single Sapling note that is sufficient to pay the fee
let result = change_strategy.compute_balance(
@ -185,7 +187,7 @@ mod tests {
&Vec::<TxOut>::new(),
&[TestSaplingInput {
note_id: 0,
value: Amount::from_u64(45000).unwrap(),
value: Amount::from_u64(60000).unwrap(),
}],
&[SaplingPayment::new(Amount::from_u64(40000).unwrap())],
&DustOutputPolicy::default(),
@ -193,14 +195,16 @@ mod tests {
assert_matches!(
result,
Ok(balance) if balance.proposed_change() == [ChangeValue::Sapling(Amount::from_u64(4000).unwrap())]
&& balance.fee_required() == Amount::from_u64(1000).unwrap()
Ok(balance) if balance.proposed_change() == [ChangeValue::Sapling(Amount::from_u64(10000).unwrap())]
&& balance.fee_required() == Amount::from_u64(10000).unwrap()
);
}
#[test]
fn dust_change() {
let change_strategy = SingleOutputChangeStrategy::new(FixedFeeRule::standard());
#[allow(deprecated)]
let fee_rule = FixedFeeRule::standard();
let change_strategy = SingleOutputChangeStrategy::new(fee_rule);
// spend a single Sapling note that is sufficient to pay the fee
let result = change_strategy.compute_balance(
@ -218,7 +222,7 @@ mod tests {
// enough to pay a fee, plus dust
TestSaplingInput {
note_id: 0,
value: Amount::from_u64(1100).unwrap(),
value: Amount::from_u64(10100).unwrap(),
},
],
&[SaplingPayment::new(Amount::from_u64(40000).unwrap())],
@ -228,7 +232,7 @@ mod tests {
assert_matches!(
result,
Err(ChangeError::InsufficientFunds { available, required })
if available == Amount::from_u64(41100).unwrap() && required == Amount::from_u64(42000).unwrap()
if available == Amount::from_u64(50100).unwrap() && required == Amount::from_u64(60000).unwrap()
);
}
}

View File

@ -6,7 +6,6 @@ use zcash_primitives::{
consensus::BlockHeight,
keys::OutgoingViewingKey,
legacy::TransparentAddress,
merkle_tree::IncrementalWitness,
sapling,
transaction::{
components::{
@ -118,7 +117,7 @@ pub struct WalletSaplingOutput<N> {
account: AccountId,
note: sapling::Note,
is_change: bool,
witness: IncrementalWitness<sapling::Node>,
witness: sapling::IncrementalWitness,
nf: N,
}
@ -132,7 +131,7 @@ impl<N> WalletSaplingOutput<N> {
account: AccountId,
note: sapling::Note,
is_change: bool,
witness: IncrementalWitness<sapling::Node>,
witness: sapling::IncrementalWitness,
nf: N,
) -> Self {
Self {
@ -165,10 +164,10 @@ impl<N> WalletSaplingOutput<N> {
pub fn is_change(&self) -> bool {
self.is_change
}
pub fn witness(&self) -> &IncrementalWitness<sapling::Node> {
pub fn witness(&self) -> &sapling::IncrementalWitness {
&self.witness
}
pub fn witness_mut(&mut self) -> &mut IncrementalWitness<sapling::Node> {
pub fn witness_mut(&mut self) -> &mut sapling::IncrementalWitness {
&mut self.witness
}
pub fn nf(&self) -> &N {
@ -183,7 +182,7 @@ pub struct SpendableNote<NoteRef> {
pub diversifier: sapling::Diversifier,
pub note_value: Amount,
pub rseed: sapling::Rseed,
pub witness: IncrementalWitness<sapling::Node>,
pub witness: sapling::IncrementalWitness,
}
impl<NoteRef> sapling_fees::InputView<NoteRef> for SpendableNote<NoteRef> {

View File

@ -7,7 +7,6 @@ use subtle::{ConditionallySelectable, ConstantTimeEq, CtOption};
use zcash_note_encryption::batch;
use zcash_primitives::{
consensus,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{
self,
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
@ -60,7 +59,7 @@ pub trait ScanningKey {
fn sapling_nf(
key: &Self::SaplingNk,
note: &Note,
witness: &IncrementalWitness<Node>,
witness: &sapling::IncrementalWitness,
) -> Self::Nf;
}
@ -88,9 +87,13 @@ impl ScanningKey for DiversifiableFullViewingKey {
fn sapling_nf(
key: &Self::SaplingNk,
note: &Note,
witness: &IncrementalWitness<Node>,
witness: &sapling::IncrementalWitness,
) -> Self::Nf {
note.nf(key, witness.position() as u64)
note.nf(
key,
u64::try_from(witness.position())
.expect("Sapling note commitment tree position must fit into a u64"),
)
}
}
@ -108,7 +111,7 @@ impl ScanningKey for SaplingIvk {
[((), 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.
@ -130,8 +133,8 @@ impl ScanningKey for SaplingIvk {
/// [`SaplingIvk`]: zcash_primitives::sapling::SaplingIvk
/// [`CompactBlock`]: crate::proto::compact_formats::CompactBlock
/// [`ScanningKey`]: crate::welding_rig::ScanningKey
/// [`CommitmentTree`]: zcash_primitives::merkle_tree::CommitmentTree
/// [`IncrementalWitness`]: zcash_primitives::merkle_tree::IncrementalWitness
/// [`CommitmentTree`]: zcash_primitives::sapling::CommitmentTree
/// [`IncrementalWitness`]: zcash_primitives::sapling::IncrementalWitness
/// [`WalletSaplingOutput`]: crate::wallet::WalletSaplingOutput
/// [`WalletTx`]: crate::wallet::WalletTx
pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
@ -139,8 +142,8 @@ pub fn scan_block<P: consensus::Parameters + Send + 'static, K: ScanningKey>(
block: CompactBlock,
vks: &[(&AccountId, &K)],
nullifiers: &[(AccountId, Nullifier)],
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
tree: &mut sapling::CommitmentTree,
existing_witnesses: &mut [&mut sapling::IncrementalWitness],
) -> Vec<WalletTx<K::Nf>> {
scan_block_with_runner::<_, _, ()>(
params,
@ -200,8 +203,8 @@ pub(crate) fn scan_block_with_runner<
block: CompactBlock,
vks: &[(&AccountId, &K)],
nullifiers: &[(AccountId, Nullifier)],
tree: &mut CommitmentTree<Node>,
existing_witnesses: &mut [&mut IncrementalWitness<Node>],
tree: &mut sapling::CommitmentTree,
existing_witnesses: &mut [&mut sapling::IncrementalWitness],
mut batch_runner: Option<&mut TaggedBatchRunner<P, K::Scope, T>>,
) -> Vec<WalletTx<K::Nf>> {
let mut wtxs: Vec<WalletTx<K::Nf>> = vec![];
@ -350,7 +353,7 @@ pub(crate) fn scan_block_with_runner<
// - Notes created by consolidation transactions.
// - Notes sent from one account to itself.
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, &note, &witness);
shielded_outputs.push(WalletSaplingOutput::from_parts(
@ -392,12 +395,11 @@ mod tests {
consensus::{BlockHeight, Network},
constants::SPENDING_KEY_GENERATOR,
memo::MemoBytes,
merkle_tree::CommitmentTree,
sapling::{
note_encryption::{sapling_note_encryption, PreparedIncomingViewingKey, SaplingDomain},
util::generate_random_rseed,
value::NoteValue,
Note, Nullifier, SaplingIvk,
CommitmentTree, Note, Nullifier, SaplingIvk,
},
transaction::components::Amount,
zip32::{AccountId, DiversifiableFullViewingKey, ExtendedSpendingKey},

View File

@ -6,6 +6,8 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Bumped dependencies to `hdwallet 0.4`.
## [0.7.1] - 2023-05-17

View File

@ -14,6 +14,7 @@ edition = "2021"
rust-version = "1.60"
[dependencies]
incrementalmerkletree = { version = "0.3", features = ["legacy-api"] }
zcash_client_backend = { version = "0.9", path = "../zcash_client_backend" }
zcash_primitives = { version = "0.11", path = "../zcash_primitives", default-features = false }
@ -21,7 +22,7 @@ zcash_primitives = { version = "0.11", path = "../zcash_primitives", default-fea
# (Breaking upgrades to these require a breaking upgrade to this crate.)
# - Errors
bs58 = { version = "0.4", features = ["check"] }
hdwallet = { version = "0.3.1", optional = true }
hdwallet = { version = "0.4", optional = true }
# - Logging and metrics
tracing = "0.1"

View File

@ -43,8 +43,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight},
legacy::TransparentAddress,
memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Nullifier},
sapling::{self, Nullifier},
transaction::{
components::{amount::Amount, OutPoint},
Transaction, TxId,
@ -191,18 +190,17 @@ impl<P: consensus::Parameters> WalletRead for WalletDb<P> {
wallet::get_transaction(self, id_tx)
}
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Memo, Self::Error> {
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Option<Memo>, Self::Error> {
match id_note {
NoteId::SentNoteId(id_note) => wallet::get_sent_memo(self, id_note),
NoteId::ReceivedNoteId(id_note) => wallet::get_received_memo(self, id_note),
}
.map(|m| m.unwrap_or(Memo::Empty))
}
fn get_commitment_tree(
&self,
block_height: BlockHeight,
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
wallet::get_sapling_commitment_tree(self, block_height)
}
@ -210,7 +208,7 @@ impl<P: consensus::Parameters> WalletRead for WalletDb<P> {
fn get_witnesses(
&self,
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)
}
@ -351,14 +349,14 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
self.wallet_db.get_transaction(id_tx)
}
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Memo, Self::Error> {
fn get_memo(&self, id_note: Self::NoteRef) -> Result<Option<Memo>, Self::Error> {
self.wallet_db.get_memo(id_note)
}
fn get_commitment_tree(
&self,
block_height: BlockHeight,
) -> Result<Option<CommitmentTree<Node>>, Self::Error> {
) -> Result<Option<sapling::CommitmentTree>, Self::Error> {
self.wallet_db.get_commitment_tree(block_height)
}
@ -366,7 +364,7 @@ impl<'a, P: consensus::Parameters> WalletRead for DataConnStmtCache<'a, P> {
fn get_witnesses(
&self,
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)
}
@ -516,8 +514,8 @@ impl<'a, P: consensus::Parameters> WalletWrite for DataConnStmtCache<'a, P> {
fn advance_by_block(
&mut self,
block: &PrunedBlock,
updated_witnesses: &[(Self::NoteRef, IncrementalWitness<Node>)],
) -> Result<Vec<(Self::NoteRef, IncrementalWitness<Node>)>, Self::Error> {
updated_witnesses: &[(Self::NoteRef, sapling::IncrementalWitness)],
) -> Result<Vec<(Self::NoteRef, sapling::IncrementalWitness)>, Self::Error> {
// database updates for each block are transactional
self.transactionally(|up| {
// Insert the block into the database.

View File

@ -12,8 +12,8 @@ use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight},
memo::MemoBytes,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Diversifier, Node, Nullifier},
merkle_tree::{write_commitment_tree, write_incremental_witness},
sapling::{self, Diversifier, Nullifier},
transaction::{components::Amount, TxId},
zip32::{AccountId, DiversifierIndex},
};
@ -277,10 +277,10 @@ impl<'a, P> DataConnStmtCache<'a, P> {
block_height: BlockHeight,
block_hash: BlockHash,
block_time: u32,
commitment_tree: &CommitmentTree<Node>,
commitment_tree: &sapling::CommitmentTree,
) -> Result<(), SqliteClientError> {
let mut encoded_tree = Vec::new();
commitment_tree.write(&mut encoded_tree).unwrap();
write_commitment_tree(commitment_tree, &mut encoded_tree).unwrap();
self.stmt_insert_block.execute(params![
u32::from(block_height),
@ -777,7 +777,7 @@ impl<'a, P> DataConnStmtCache<'a, P> {
&mut self,
note_id: NoteId,
height: BlockHeight,
witness: &IncrementalWitness<Node>,
witness: &sapling::IncrementalWitness,
) -> Result<(), SqliteClientError> {
let note_id = match note_id {
NoteId::ReceivedNoteId(note_id) => Ok(note_id),
@ -785,7 +785,7 @@ impl<'a, P> DataConnStmtCache<'a, P> {
}?;
let mut encoded = Vec::new();
witness.write(&mut encoded).unwrap();
write_incremental_witness(witness, &mut encoded).unwrap();
self.stmt_insert_witness
.execute(params![note_id, u32::from(height), encoded])?;

View File

@ -73,8 +73,8 @@ use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight, BranchId, NetworkUpgrade, Parameters},
memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Note, Nullifier},
merkle_tree::{read_commitment_tree, read_incremental_witness},
sapling::{self, Note, Nullifier},
transaction::{components::Amount, Transaction, TxId},
zip32::{
sapling::{DiversifiableFullViewingKey, ExtendedFullViewingKey},
@ -698,14 +698,14 @@ pub(crate) fn truncate_to_height<P: consensus::Parameters>(
pub(crate) fn get_sapling_commitment_tree<P>(
wdb: &WalletDb<P>,
block_height: BlockHeight,
) -> Result<Option<CommitmentTree<Node>>, SqliteClientError> {
) -> Result<Option<sapling::CommitmentTree>, SqliteClientError> {
wdb.conn
.query_row_and_then(
"SELECT sapling_tree FROM blocks WHERE height = ?",
[u32::from(block_height)],
|row| {
let row_data: Vec<u8> = row.get(0)?;
CommitmentTree::read(&row_data[..]).map_err(|e| {
read_commitment_tree(&row_data[..]).map_err(|e| {
rusqlite::Error::FromSqlConversionFailure(
row_data.len(),
rusqlite::types::Type::Blob,
@ -723,7 +723,7 @@ pub(crate) fn get_sapling_commitment_tree<P>(
pub(crate) fn get_sapling_witnesses<P>(
wdb: &WalletDb<P>,
block_height: BlockHeight,
) -> Result<Vec<(NoteId, IncrementalWitness<Node>)>, SqliteClientError> {
) -> Result<Vec<(NoteId, sapling::IncrementalWitness)>, SqliteClientError> {
let mut stmt_fetch_witnesses = wdb
.conn
.prepare("SELECT note, witness FROM sapling_witnesses WHERE block = ?")?;
@ -731,7 +731,7 @@ pub(crate) fn get_sapling_witnesses<P>(
.query_map([u32::from(block_height)], |row| {
let id_note = NoteId::ReceivedNoteId(row.get(0)?);
let wdb: Vec<u8> = row.get(1)?;
Ok(IncrementalWitness::read(&wdb[..]).map(|witness| (id_note, witness)))
Ok(read_incremental_witness(&wdb[..]).map(|witness| (id_note, witness)))
})
.map_err(SqliteClientError::from)?;
@ -895,7 +895,7 @@ pub(crate) fn insert_block<'a, P>(
block_height: BlockHeight,
block_hash: BlockHash,
block_time: u32,
commitment_tree: &CommitmentTree<Node>,
commitment_tree: &sapling::CommitmentTree,
) -> Result<(), SqliteClientError> {
stmts.stmt_insert_block(block_height, block_hash, block_time, commitment_tree)
}
@ -1060,7 +1060,7 @@ pub(crate) fn put_received_note<'a, P, T: ReceivedSaplingOutput>(
pub(crate) fn insert_witness<'a, P>(
stmts: &mut DataConnStmtCache<'a, P>,
note_id: i64,
witness: &IncrementalWitness<Node>,
witness: &sapling::IncrementalWitness,
height: BlockHeight,
) -> Result<(), SqliteClientError> {
stmts.stmt_insert_witness(NoteId::ReceivedNoteId(note_id), height, witness)

View File

@ -7,7 +7,7 @@ use group::ff::PrimeField;
use zcash_primitives::{
consensus::BlockHeight,
merkle_tree::IncrementalWitness,
merkle_tree::read_incremental_witness,
sapling::{Diversifier, Rseed},
transaction::components::Amount,
zip32::AccountId,
@ -50,7 +50,7 @@ fn to_spendable_note(row: &Row) -> Result<SpendableNote<NoteId>, SqliteClientErr
let witness = {
let d: Vec<_> = row.get(4)?;
IncrementalWitness::read(&d[..])?
read_incremental_witness(&d[..])?
};
Ok(SpendableNote {
@ -354,7 +354,7 @@ mod tests {
available,
required
})
if available == Amount::zero() && required == Amount::from_u64(1001).unwrap()
if available == Amount::zero() && required == Amount::from_u64(10001).unwrap()
);
}
@ -437,7 +437,7 @@ mod tests {
required
})
if available == Amount::from_u64(50000).unwrap()
&& required == Amount::from_u64(71000).unwrap()
&& required == Amount::from_u64(80000).unwrap()
);
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 2 to 9 until just before the second
@ -472,7 +472,7 @@ mod tests {
required
})
if available == Amount::from_u64(50000).unwrap()
&& required == Amount::from_u64(71000).unwrap()
&& required == Amount::from_u64(80000).unwrap()
);
// Mine block 11 so that the second note becomes verified
@ -568,12 +568,12 @@ mod tests {
available,
required
})
if available == Amount::zero() && required == Amount::from_u64(3000).unwrap()
if available == Amount::zero() && required == Amount::from_u64(12000).unwrap()
);
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 21 (that don't send us funds)
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 41 (that don't send us funds)
// until just before the first transaction expires
for i in 1..22 {
for i in 1..42 {
let (cb, _) = fake_compact_block(
sapling_activation_height() + i,
cb.hash(),
@ -602,14 +602,14 @@ mod tests {
available,
required
})
if available == Amount::zero() && required == Amount::from_u64(3000).unwrap()
if available == Amount::zero() && required == Amount::from_u64(12000).unwrap()
);
// Mine block SAPLING_ACTIVATION_HEIGHT + 22 so that the first transaction expires
// Mine block SAPLING_ACTIVATION_HEIGHT + 42 so that the first transaction expires
let (cb, _) = fake_compact_block(
sapling_activation_height() + 22,
sapling_activation_height() + 42,
cb.hash(),
&ExtendedSpendingKey::master(&[22]).to_diversifiable_full_viewing_key(),
&ExtendedSpendingKey::master(&[42]).to_diversifiable_full_viewing_key(),
AddressType::DefaultExternal,
value,
);
@ -716,9 +716,9 @@ mod tests {
send_and_recover_with_policy(&mut db_write, OvkPolicy::Sender).unwrap();
assert_eq!(&recovered_to, &addr2);
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 22 (that don't send us funds)
// Mine blocks SAPLING_ACTIVATION_HEIGHT + 1 to 42 (that don't send us funds)
// so that the first transaction expires
for i in 1..=22 {
for i in 1..=42 {
let (cb, _) = fake_compact_block(
sapling_activation_height() + i,
cb.hash(),
@ -752,7 +752,7 @@ mod tests {
let dfvk = usk.sapling().to_diversifiable_full_viewing_key();
// Add funds to the wallet in a single note
let value = Amount::from_u64(51000).unwrap();
let value = Amount::from_u64(60000).unwrap();
let (cb, _) = fake_compact_block(
sapling_activation_height(),
BlockHash([0; 32]),
@ -806,7 +806,7 @@ mod tests {
let dfvk = usk.sapling().to_diversifiable_full_viewing_key();
// Add funds to the wallet in a single note
let value = Amount::from_u64(51000).unwrap();
let value = Amount::from_u64(60000).unwrap();
let (cb, _) = fake_compact_block(
sapling_activation_height(),
BlockHash([0; 32]),

View File

@ -480,15 +480,12 @@ mod tests {
use ff::Field;
use rand_core::OsRng;
use zcash_proofs::prover::LocalTxProver;
use zcash_primitives::{
consensus::{BlockHeight, BranchId, NetworkUpgrade, Parameters},
constants,
extensions::transparent::{self as tze, Extension, FromPayload, ToPayload},
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Rseed},
sapling::{self, Node, Rseed},
transaction::{
builder::Builder,
components::{
@ -500,6 +497,7 @@ mod tests {
},
zip32::ExtendedSpendingKey,
};
use zcash_proofs::prover::LocalTxProver;
use super::{close, hash_1, open, Context, DemoBuilder, Precondition, Program, Witness};
@ -809,18 +807,21 @@ mod tests {
//
let mut rng = OsRng;
// FIXME: implement zcash_primitives::transaction::fees::FutureFeeRule for zip317::FeeRule.
#[allow(deprecated)]
let fee_rule = fixed::FeeRule::standard();
// create some inputs to spend
let extsk = ExtendedSpendingKey::master(&[]);
let to = extsk.default_address().1;
let note1 = to.create_note(101000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
let note1 = to.create_note(110000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
let cm1 = Node::from_cmu(&note1.cmu());
let mut tree = CommitmentTree::empty();
let mut tree = sapling::CommitmentTree::empty();
// fake that the note appears in some previous
// shielded output
tree.append(cm1).unwrap();
let witness1 = IncrementalWitness::from_tree(&tree);
let witness1 = sapling::IncrementalWitness::from_tree(tree);
let mut builder_a = demo_builder(tx_height);
builder_a

View File

@ -8,6 +8,7 @@ and this library adheres to Rust's notion of
## [Unreleased]
### Changed
- MSRV is now 1.60.0.
- Bumped dependencies to `primitive-types 0.12`.
## [0.3.0] - 2022-05-11
### Added

View File

@ -14,7 +14,7 @@ assert_matches = "1.3.0"
proptest = "1.0.0"
[dependencies]
primitive-types = "0.11"
primitive-types = { version = "0.12", default-features = false }
byteorder = "1"
blake2 = { package = "blake2b_simd", version = "1" }
proptest = { version = "1.0.0", optional = true }

View File

@ -6,6 +6,89 @@ and this library adheres to Rust's notion of
[Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Changed
- Bumped dependencies to `secp256k1 0.26`, `hdwallet 0.4`.
### Removed
- `merkle_tree::Hashable` has been removed and its uses have been replaced by
`incrementalmerkletree::Hashable` and `merkle_tree::HashSer`.
- The `Hashable` bound on the `Node` parameter to the `IncrementalWitness`
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::{CommitmentTree, IncrementalWitness, MerklePath}` have been removed in
favor of versions of these types that are now provided by the
`incrementalmerkletree` crate. The replacement types now use const generic
parameters for enforcing the note commitment tree depth. Serialization
methods for these types that do not exist for the `incrementalmerkletree`
replacement types have been replaced by new methods in the `merkle_tree` module.
- `merkle_tree::incremental::write_auth_fragment_v1` has been removed without replacement.
- The `merkle_tree::incremental` module has been removed; its former contents
were either moved to the `merkle_tree` module or were `zcashd`-specific
serialization methods which have been removed entirely and moved into the
[zcashd](https://github.com/zcash/zcash) repository.
- The dependency on the `bridgetree` crate has been removed from
`zcash_primitives` and the following zcashd-specific serialization methods
have been moved to the [zcashd](https://github.com/zcash/zcash) repository:
- `read_auth_fragment_v1`
- `read_bridge_v1`
- `read_bridge_v2`
- `write_bridge_v2`
- `write_bridge`
- `read_checkpoint_v1`
- `read_checkpoint_v2`
- `write_checkpoint_v2`
- `read_tree`
- `write_tree`
- `merkle_tree::{SER_V1, SER_V2}` have been removed as they are now unused.
### Moved
- The following constants and methods have been moved from the
`merkle_tree::incremental` module into the `merkle_tree` module to
consolidate the serialization code for commitment tree frontiers:
- `write_usize_leu64`
- `read_leu64_usize`
- `write_position`
- `read_position`
- `write_address`
- `read_address`
- `read_frontier_v0`
- `write_nonempty_frontier`
- `read_nonempty_frontier_v1`
- `write_frontier_v1`
- `read_frontier_v1`
### Added
- `merkle_tree::incremental::{read_address, write_address}`
- `merkle_tree::incremental::read_bridge_v2`
- `merkle_tree::write_commitment_tree` replaces `merkle_tree::CommitmentTree::write`
- `merkle_tree::read_commitment_tree` replaces `merkle_tree::CommitmentTree::read`
- `merkle_tree::write_incremental_witness` replaces `merkle_tree::IncrementalWitness::write`
- `merkle_tree::read_incremental_witness` replaces `merkle_tree::IncrementalWitness::read`
- `merkle_tree::merkle_path_from_slice` replaces `merkle_tree::MerklePath::from_slice`
- `sapling::{CommitmentTree, IncrementalWitness, MerklePath, NOTE_COMMITMENT_TREE_DEPTH}`
- `transaction::fees::zip317::MINIMUM_FEE`, reflecting the minimum possible
[ZIP 317](https://zips.z.cash/zip-0317) conventional fee.
- `transaction::components::amount::Amount::const_from_i64`, intended for constructing
a constant `Amount`.
### Changed
- The bounds on the `H` parameter to the following methods have changed:
- `merkle_tree::incremental::read_frontier_v0`
- `merkle_tree::incremental::read_auth_fragment_v1`
- The depth of the `merkle_tree::{CommitmentTree, IncrementalWitness, and MerklePath}`
data types are now statically constrained using const generic type parameters.
- `transaction::fees::fixed::FeeRule::standard()` now uses the ZIP 317 minimum fee
(10000 zatoshis rather than 1000 zatoshis) as the fixed fee. To be compliant with
ZIP 317, use `transaction::fees::zip317::FeeRule::standard()` instead.
### Deprecated
- `transaction::components::amount::DEFAULT_FEE` has been deprecated. Depending on
context, you may want to use `transaction::fees::zip317::MINIMUM_FEE`, or calculate
the ZIP 317 conventional fee using `transaction::fees::zip317::FeeRule` instead.
- `transaction::fees::fixed::FeeRule::standard()` has been deprecated.
Use either `transaction::fees::zip317::FeeRule::standard()` or
`transaction::fees::fixed::FeeRule::non_standard`.
## [0.11.0] - 2023-04-15
### Added

View File

@ -42,11 +42,13 @@ subtle = "2.2.3"
bls12_381 = "0.8"
ff = "0.13"
group = { version = "0.13", features = ["wnaf-memuse"] }
incrementalmerkletree = "0.3"
jubjub = "0.10"
nonempty = "0.7"
orchard = { version = "0.4", default-features = false }
# - Note Commitment Trees
incrementalmerkletree = { version = "0.3", features = ["legacy-api"] }
# - Static constants
lazy_static = "1"
@ -55,9 +57,9 @@ proptest = { version = "1.0.0", optional = true }
# - Transparent inputs
# - `Error` type exposed
hdwallet = { version = "0.3.1", optional = true }
hdwallet = { version = "0.4", optional = true }
# - `SecretKey` and `PublicKey` types exposed
secp256k1 = { version = "0.21", optional = true }
secp256k1 = { version = "0.26", optional = true }
# - ZIP 339
bip0039 = { version = "0.10", features = ["std", "all-languages"] }
@ -86,6 +88,7 @@ features = ["pre-zip-212"]
[dev-dependencies]
chacha20poly1305 = "0.10"
criterion = "0.4"
incrementalmerkletree = { version = "0.3", features = ["legacy-api", "test-dependencies"] }
proptest = "1.0.0"
assert_matches = "1.3.0"
rand_xorshift = "0.3"

View File

@ -0,0 +1,7 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc ad8649940f45fef5aad3355d7ca193e7d018285b04f9d976197ff8e1fec8687a # shrinks to ct = CommitmentTree { left: Some(Node { repr: [31, 136, 59, 216, 239, 236, 65, 132, 70, 226, 212, 35, 165, 194, 10, 57, 188, 94, 141, 22, 236, 68, 38, 57, 219, 28, 160, 173, 156, 62, 220, 34] }), right: Some(Node { repr: [117, 7, 10, 72, 207, 40, 193, 79, 126, 156, 158, 193, 63, 190, 49, 179, 227, 188, 144, 85, 30, 143, 186, 203, 154, 76, 5, 113, 42, 16, 60, 41] }), parents: [Some(Node { repr: [92, 253, 34, 29, 13, 202, 250, 201, 54, 210, 88, 207, 183, 117, 166, 126, 63, 101, 34, 44, 119, 88, 122, 240, 51, 77, 53, 148, 190, 141, 226, 103] }), Some(Node { repr: [91, 140, 162, 31, 9, 135, 96, 209, 170, 45, 143, 251, 108, 37, 94, 84, 95, 22, 28, 109, 140, 61, 249, 41, 220, 207, 149, 136, 93, 220, 161, 87] }), Some(Node { repr: [21, 173, 87, 125, 78, 242, 185, 197, 174, 47, 114, 229, 189, 35, 154, 221, 164, 232, 181, 26, 232, 216, 228, 244, 81, 127, 222, 10, 22, 241, 134, 106] }), None, Some(Node { repr: [88, 145, 84, 46, 146, 135, 252, 5, 21, 50, 137, 85, 36, 136, 101, 55, 153, 134, 71, 41, 95, 73, 17, 151, 170, 141, 195, 95, 11, 204, 181, 85] }), None, None] }

File diff suppressed because it is too large Load Diff

View File

@ -1,449 +0,0 @@
//! Implementations of serialization and parsing for Orchard note commitment trees.
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read, Write};
use incrementalmerkletree::{
bridgetree::{AuthFragment, Frontier, Leaf, MerkleBridge, NonEmptyFrontier},
Hashable, Position,
};
use orchard::tree::MerkleHashOrchard;
use zcash_encoding::{Optional, Vector};
use super::{CommitmentTree, HashSer};
pub const SER_V1: u8 = 1;
pub const SER_V2: u8 = 2;
impl HashSer for MerkleHashOrchard {
fn read<R: Read>(mut reader: R) -> io::Result<Self>
where
Self: Sized,
{
let mut repr = [0u8; 32];
reader.read_exact(&mut repr)?;
<Option<_>>::from(Self::from_bytes(&repr)).ok_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"Non-canonical encoding of Pallas base field value.",
)
})
}
fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.to_bytes())
}
}
/// Writes a usize value encoded as a u64 in little-endian order. Since usize
/// is platform-dependent, we consistently represent it as u64 in serialized
/// formats.
pub fn write_usize_leu64<W: Write>(mut writer: W, value: usize) -> io::Result<()> {
// Panic if we get a usize value that can't fit into a u64.
writer.write_u64::<LittleEndian>(value.try_into().unwrap())
}
/// Reads a usize value encoded as a u64 in little-endian order. Since usize
/// is platform-dependent, we consistently represent it as u64 in serialized
/// formats.
pub fn read_leu64_usize<R: Read>(mut reader: R) -> io::Result<usize> {
reader.read_u64::<LittleEndian>()?.try_into().map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!(
"usize could not be decoded from a 64-bit value on this platform: {:?}",
e
),
)
})
}
pub fn write_position<W: Write>(mut writer: W, position: Position) -> io::Result<()> {
write_usize_leu64(&mut writer, position.into())
}
pub fn read_position<R: Read>(mut reader: R) -> io::Result<Position> {
read_leu64_usize(&mut reader).map(Position::from)
}
pub fn read_frontier_v0<H: Hashable + super::Hashable, R: Read>(
mut reader: R,
) -> io::Result<Frontier<H, 32>> {
let tree = CommitmentTree::read(&mut reader)?;
Ok(tree.to_frontier())
}
pub fn write_nonempty_frontier_v1<H: HashSer, W: Write>(
mut writer: W,
frontier: &NonEmptyFrontier<H>,
) -> io::Result<()> {
write_position(&mut writer, frontier.position())?;
match frontier.leaf() {
Leaf::Left(a) => {
a.write(&mut writer)?;
Optional::write(&mut writer, None, |w, n: &H| n.write(w))?;
}
Leaf::Right(a, b) => {
a.write(&mut writer)?;
Optional::write(&mut writer, Some(b), |w, n| n.write(w))?;
}
}
Vector::write(&mut writer, frontier.ommers(), |w, e| e.write(w))?;
Ok(())
}
#[allow(clippy::redundant_closure)]
pub fn read_nonempty_frontier_v1<H: HashSer + Clone, R: Read>(
mut reader: R,
) -> io::Result<NonEmptyFrontier<H>> {
let position = read_position(&mut reader)?;
let left = H::read(&mut reader)?;
let right = Optional::read(&mut reader, H::read)?;
let leaf = right.map_or_else(
|| Leaf::Left(left.clone()),
|r| Leaf::Right(left.clone(), r),
);
let ommers = Vector::read(&mut reader, |r| H::read(r))?;
NonEmptyFrontier::from_parts(position, leaf, ommers).map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("Parsing resulted in an invalid Merkle frontier: {:?}", err),
)
})
}
pub fn write_frontier_v1<H: HashSer, W: Write>(
writer: W,
frontier: &Frontier<H, 32>,
) -> io::Result<()> {
Optional::write(writer, frontier.value(), write_nonempty_frontier_v1)
}
#[allow(clippy::redundant_closure)]
pub fn read_frontier_v1<H: HashSer + Clone, R: Read>(reader: R) -> io::Result<Frontier<H, 32>> {
match Optional::read(reader, read_nonempty_frontier_v1)? {
None => Ok(Frontier::empty()),
Some(f) => Frontier::try_from(f).map_err(|err| {
io::Error::new(
io::ErrorKind::InvalidInput,
format!("Parsing resulted in an invalid Merkle frontier: {:?}", err),
)
}),
}
}
pub fn write_auth_fragment_v1<H: HashSer, W: Write>(
mut writer: W,
fragment: &AuthFragment<H>,
) -> io::Result<()> {
write_position(&mut writer, fragment.position())?;
write_usize_leu64(&mut writer, fragment.altitudes_observed())?;
Vector::write(&mut writer, fragment.values(), |w, a| a.write(w))
}
#[allow(clippy::redundant_closure)]
pub fn read_auth_fragment_v1<H: HashSer, R: Read>(mut reader: R) -> io::Result<AuthFragment<H>> {
let position = read_position(&mut reader)?;
let alts_observed = read_leu64_usize(&mut reader)?;
let values = Vector::read(&mut reader, |r| H::read(r))?;
Ok(AuthFragment::from_parts(position, alts_observed, values))
}
pub fn write_bridge_v1<H: HashSer + Ord, W: Write>(
mut writer: W,
bridge: &MerkleBridge<H>,
) -> io::Result<()> {
Optional::write(&mut writer, bridge.prior_position(), |w, pos| {
write_position(w, pos)
})?;
Vector::write(
&mut writer,
&bridge.auth_fragments().iter().collect::<Vec<_>>(),
|mut w, (pos, a)| {
write_position(&mut w, **pos)?;
write_auth_fragment_v1(w, a)
},
)?;
write_nonempty_frontier_v1(&mut writer, bridge.frontier())?;
Ok(())
}
pub fn read_bridge_v1<H: HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<MerkleBridge<H>> {
let prior_position = Optional::read(&mut reader, read_position)?;
let auth_fragments = Vector::read(&mut reader, |mut r| {
Ok((read_position(&mut r)?, read_auth_fragment_v1(r)?))
})?
.into_iter()
.collect();
let frontier = read_nonempty_frontier_v1(&mut reader)?;
Ok(MerkleBridge::from_parts(
prior_position,
auth_fragments,
frontier,
))
}
pub fn write_bridge<H: HashSer + Ord, W: Write>(
mut writer: W,
bridge: &MerkleBridge<H>,
) -> io::Result<()> {
writer.write_u8(SER_V1)?;
write_bridge_v1(writer, bridge)
}
pub fn read_bridge<H: HashSer + Ord + Clone, R: Read>(
mut reader: R,
) -> io::Result<MerkleBridge<H>> {
match reader.read_u8()? {
SER_V1 => read_bridge_v1(&mut reader),
flag => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("Unrecognized serialization version: {:?}", flag),
)),
}
}
#[cfg(test)]
mod tests {
use hex;
use proptest::prelude::*;
use incrementalmerkletree::{
bridgetree::{BridgeTree, Frontier},
Tree,
};
use super::*;
use crate::{
merkle_tree::testing::{arb_commitment_tree, TestNode},
sapling::{testing as sapling, Node},
};
proptest! {
#[test]
fn frontier_serialization_v0(t in arb_commitment_tree(0, sapling::arb_node(), 32))
{
let mut buffer = vec![];
t.write(&mut buffer).unwrap();
let frontier: Frontier<Node, 32> = read_frontier_v0(&buffer[..]).unwrap();
let expected: Frontier<Node, 32> = t.to_frontier();
assert_eq!(frontier, expected);
}
#[test]
fn frontier_serialization_v1(t in arb_commitment_tree(1, sapling::arb_node(), 32))
{
let original: Frontier<Node, 32> = t.to_frontier();
let mut buffer = vec![];
write_frontier_v1(&mut buffer, &original).unwrap();
let read: Frontier<Node, 32> = read_frontier_v1(&buffer[..]).unwrap();
assert_eq!(read, original);
}
}
#[test]
fn test_bridge_roundtrip() {
let mut t: BridgeTree<TestNode, 8> = BridgeTree::new(10);
let mut to_unwitness = vec![];
let mut has_auth_path = vec![];
for i in 0usize..100 {
assert!(
t.append(&TestNode(i.try_into().unwrap())),
"Append should succeed."
);
if i % 5 == 0 {
t.checkpoint();
}
if i % 7 == 0 {
t.witness();
if i > 0 && i % 2 == 0 {
to_unwitness.push(Position::from(i));
} else {
has_auth_path.push(Position::from(i));
}
}
if i % 11 == 0 && !to_unwitness.is_empty() {
let pos = to_unwitness.remove(0);
t.remove_witness(pos);
}
}
for (i, b) in t
.prior_bridges()
.iter()
.chain(t.current_bridge().iter())
.enumerate()
{
let mut buffer = vec![];
write_bridge(&mut buffer, b).unwrap();
let b0 = read_bridge(&buffer[..]).unwrap();
assert_eq!(b, &b0);
let buffer2 = hex::decode(&BRIDGE_V1_VECTORS[i]).unwrap();
let b2 = read_bridge(&buffer2[..]).unwrap();
assert_eq!(b, &b2);
}
let latest_root = t.root(0).unwrap();
for (pos, witness) in BRIDGE_V1_WITNESSES {
let path = t
.authentication_path(Position::from(*pos), &latest_root)
.unwrap();
assert_eq!(witness.to_vec(), path);
}
}
const BRIDGE_V1_VECTORS: &[&str] = &[
"010000000000000000000000000000000000000000",
"01010000000000000000010000000000000000000000000000000002000000000000000201000000000000000cf29c71c9b7c4a50500000000000000040000000000000001050000000000000001545227c621102b3a",
"010105000000000000000100000000000000000000000000000000030000000000000001948e8c8cb96eed280700000000000000060000000000000001070000000000000002bae878935340beb7545227c621102b3a",
"010107000000000000000200000000000000000000000000000000030000000000000000070000000000000007000000000000000000000000000000000a000000000000000a000000000000000002157a46a02f6b8e34c730d7a51e3a8378",
"01010a000000000000000200000000000000000000000000000000030000000000000000070000000000000007000000000000000000000000000000000e000000000000000e000000000000000003779388f05d9525bb33fac4dcb323ec8bc730d7a51e3a8378",
"01010e000000000000000300000000000000000000000000000000040000000000000001c49580ae5e5e758307000000000000000700000000000000010000000000000001c49580ae5e5e75830e000000000000000e000000000000000100000000000000010f000000000000000f000000000000000e00000000000000010f0000000000000003779388f05d9525bb33fac4dcb323ec8bc730d7a51e3a8378",
"01010f000000000000000300000000000000000000000000000000040000000000000000070000000000000007000000000000000100000000000000000e000000000000000e0000000000000001000000000000000014000000000000001400000000000000000281be7679141b8edda519e57e99af06e2",
"010114000000000000000300000000000000000000000000000000040000000000000000070000000000000007000000000000000100000000000000000e000000000000000e00000000000000010000000000000000150000000000000014000000000000000115000000000000000281be7679141b8edda519e57e99af06e2",
"0101150000000000000003000000000000000000000000000000000400000000000000000700000000000000070000000000000001000000000000000015000000000000001500000000000000010000000000000001fdf772cabf2d5b20190000000000000018000000000000000119000000000000000244365e47c1afcfa1a519e57e99af06e2",
"01011900000000000000030000000000000000000000000000000004000000000000000007000000000000000700000000000000010000000000000000150000000000000015000000000000000100000000000000001c000000000000001c000000000000000003174b903560b67a6744365e47c1afcfa1a519e57e99af06e2",
"01011c00000000000000040000000000000000000000000000000004000000000000000007000000000000000700000000000000010000000000000000150000000000000015000000000000000100000000000000001c000000000000001c000000000000000100000000000000011d000000000000001e000000000000001e000000000000000004cfe7a4133f88607d174b903560b67a6744365e47c1afcfa1a519e57e99af06e2",
"01011e00000000000000030000000000000000000000000000000005000000000000000175938fb118b4555d0700000000000000070000000000000002000000000000000175938fb118b4555d15000000000000001500000000000000020000000000000001c050bf17c144495d2300000000000000220000000000000001230000000000000002f22dbb4a83f3b28318bd1c4cf1fd912e",
"0101230000000000000004000000000000000000000000000000000500000000000000000700000000000000070000000000000002000000000000000015000000000000001500000000000000020000000000000000230000000000000023000000000000000100000000000000017cf828deead5aa3d280000000000000028000000000000000002e28c91f60fbf69d518bd1c4cf1fd912e",
"0101280000000000000004000000000000000000000000000000000500000000000000000700000000000000070000000000000002000000000000000015000000000000001500000000000000020000000000000000230000000000000023000000000000000100000000000000002a000000000000002a000000000000000003e16610644840a918e28c91f60fbf69d518bd1c4cf1fd912e",
"01012a0000000000000004000000000000000000000000000000000500000000000000000700000000000000070000000000000002000000000000000015000000000000001500000000000000020000000000000000230000000000000023000000000000000100000000000000002d000000000000002c00000000000000012d0000000000000003afd0751401aff593e28c91f60fbf69d518bd1c4cf1fd912e",
"01012d000000000000000400000000000000000000000000000000050000000000000000070000000000000007000000000000000200000000000000001500000000000000150000000000000002000000000000000023000000000000002300000000000000020000000000000001dd486e048f1e6c2131000000000000003000000000000000013100000000000000029f16b2397db7491d18bd1c4cf1fd912e",
"0101310000000000000005000000000000000000000000000000000500000000000000000700000000000000070000000000000002000000000000000015000000000000001500000000000000020000000000000000230000000000000023000000000000000200000000000000003100000000000000310000000000000000000000000000000032000000000000003200000000000000000356370c5489a364b29f16b2397db7491d18bd1c4cf1fd912e",
"010132000000000000000500000000000000000000000000000000050000000000000000070000000000000007000000000000000200000000000000001500000000000000150000000000000002000000000000000023000000000000002300000000000000020000000000000000310000000000000031000000000000000200000000000000024f6ce1b1025daa00c2db272cc44c77c6370000000000000036000000000000000137000000000000000491c03add43411617ff8c00dfd0bc84289f16b2397db7491d18bd1c4cf1fd912e",
"0101370000000000000005000000000000000000000000000000000500000000000000000700000000000000070000000000000002000000000000000015000000000000001500000000000000020000000000000000230000000000000023000000000000000200000000000000003100000000000000310000000000000002000000000000000038000000000000003800000000000000000335cff7ed860ba07e9f16b2397db7491d18bd1c4cf1fd912e",
"010138000000000000000600000000000000000000000000000000050000000000000000070000000000000007000000000000000200000000000000001500000000000000150000000000000002000000000000000023000000000000002300000000000000020000000000000000310000000000000031000000000000000200000000000000003800000000000000380000000000000002000000000000000239000000000000001f6951d61f683ea73c000000000000003c0000000000000000047cfc0f73b07bd1ef35cff7ed860ba07e9f16b2397db7491d18bd1c4cf1fd912e",
"01013c000000000000000600000000000000000000000000000000060000000000000001f788446318d5f99907000000000000000700000000000000030000000000000001f788446318d5f99915000000000000001500000000000000030000000000000001f788446318d5f99923000000000000002300000000000000030000000000000001fb8290b35ee7b7dd31000000000000003100000000000000030000000000000001ea30e73bc03ac3c638000000000000003800000000000000030000000000000001852e67bafeb639803f000000000000003e00000000000000013f00000000000000050a984f21271cd02d7cfc0f73b07bd1ef35cff7ed860ba07e9f16b2397db7491d18bd1c4cf1fd912e",
"01013f00000000000000070000000000000000000000000000000006000000000000000007000000000000000700000000000000030000000000000000150000000000000015000000000000000300000000000000002300000000000000230000000000000003000000000000000031000000000000003100000000000000030000000000000000380000000000000038000000000000000300000000000000003f000000000000003f000000000000000000000000000000004100000000000000400000000000000001410000000000000001af35cc5f4335f3a7",
"010141000000000000000600000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f0000000000000000000000000000000046000000000000004600000000000000000331fc52e580f35e5348d31a06aa5a4470af35cc5f4335f3a7",
"010146000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004600000000000000460000000000000001000000000000000147000000000000004b000000000000004a00000000000000014b0000000000000003fe1c911e7cebb841b8b5aeae9a67d003af35cc5f4335f3a7",
"01014b000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f00000000000000000000000000000000460000000000000046000000000000000100000000000000004d000000000000004c00000000000000014d000000000000000347a60f231e2896ceb8b5aeae9a67d003af35cc5f4335f3a7",
"01014d000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000100000000000000011387f5de1c3253dd500000000000000050000000000000000002428877913fee798baf35cc5f4335f3a7",
"010150000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d00000000000000010000000000000000540000000000000054000000000000000003e22700e31d13d582428877913fee798baf35cc5f4335f3a7",
"010154000000000000000800000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000100000000000000005400000000000000540000000000000001000000000000000155000000000000005500000000000000540000000000000001550000000000000003e22700e31d13d582428877913fee798baf35cc5f4335f3a7",
"010155000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000100000000000000005a000000000000005a00000000000000000498e2ef95e82f2d3e901d6a966885931b428877913fee798baf35cc5f4335f3a7",
"01015a000000000000000700000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000100000000000000005b000000000000005a00000000000000015b000000000000000498e2ef95e82f2d3e901d6a966885931b428877913fee798baf35cc5f4335f3a7",
"01015b000000000000000800000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d00000000000000020000000000000001e7e0fcdfe98c29685b000000000000005b00000000000000010000000000000001d8900fcea5e695a55f000000000000005e00000000000000015f0000000000000005c3c19bd287343ee12218736715dbc702901d6a966885931b428877913fee798baf35cc5f4335f3a7",
"01015f000000000000000800000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000200000000000000005b000000000000005b000000000000000100000000000000006200000000000000620000000000000000036aa28f62b4a6071a082530c7c14f49acaf35cc5f4335f3a7",
"010162000000000000000800000000000000000000000000000000060000000000000000070000000000000007000000000000000300000000000000001500000000000000150000000000000003000000000000000023000000000000002300000000000000030000000000000000310000000000000031000000000000000300000000000000003f000000000000003f000000000000000000000000000000004d000000000000004d000000000000000200000000000000005b000000000000005b0000000000000001000000000000000063000000000000006200000000000000016300000000000000036aa28f62b4a6071a082530c7c14f49acaf35cc5f4335f3a7",
];
const BRIDGE_V1_WITNESSES: &[(usize, [TestNode; 8])] = &[
(
0,
[
TestNode(1),
TestNode(11944874187515818508),
TestNode(2949135074203569812),
TestNode(9472581151991305668),
TestNode(6725479636698895221),
TestNode(11095133457725294839),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
7,
[
TestNode(6),
TestNode(13240090682216474810),
TestNode(4191461615442809428),
TestNode(9472581151991305668),
TestNode(6725479636698895221),
TestNode(11095133457725294839),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
21,
[
TestNode(20),
TestNode(2331507533852899325),
TestNode(15964727503826108033),
TestNode(6721979514944966848),
TestNode(16286898176225778085),
TestNode(11095133457725294839),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
35,
[
TestNode(34),
TestNode(9489915110043102706),
TestNode(4443599187080706172),
TestNode(2408333500339865821),
TestNode(15976492597045658363),
TestNode(3355742410173627672),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
49,
[
TestNode(48),
TestNode(47953012196469839),
TestNode(14300983547176410050),
TestNode(14322355837281448170),
TestNode(2110419648866555551),
TestNode(3355742410173627672),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
63,
[
TestNode(62),
TestNode(3301169481250740234),
TestNode(17280729242972191868),
TestNode(9124305519198588725),
TestNode(2110419648866555551),
TestNode(3355742410173627672),
TestNode(14133087432678894597),
TestNode(5741638299150577022),
],
),
(
77,
[
TestNode(76),
TestNode(15948145805030164243),
TestNode(14886129728222111303),
TestNode(274833491322910136),
TestNode(7505685190102802663),
TestNode(2256868868291095598),
TestNode(12102075187160954287),
TestNode(5741638299150577022),
],
),
(
91,
[
TestNode(90),
TestNode(4480289880297955992),
TestNode(11931696387589116120),
TestNode(1987078544847150480),
TestNode(10050326000244852802),
TestNode(2256868868291095598),
TestNode(12102075187160954287),
TestNode(5741638299150577022),
],
),
];
}

View File

@ -24,7 +24,7 @@ pub use address::PaymentAddress;
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
pub use note::{nullifier::Nullifier, Note, Rseed};
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.

View File

@ -1,11 +1,10 @@
//! Abstractions over the proving system and parameters.
use crate::{
merkle_tree::MerklePath,
sapling::{
self,
redjubjub::{PublicKey, Signature},
value::ValueCommitment,
Node,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};
@ -35,7 +34,7 @@ pub trait TxProver {
ar: jubjub::Fr,
value: u64,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
merkle_path: sapling::MerklePath,
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()>;
/// Create the value commitment and proof for a Sapling [`OutputDescription`],
@ -67,19 +66,18 @@ pub trait TxProver {
pub mod mock {
use rand_core::OsRng;
use super::TxProver;
use crate::{
constants::SPENDING_KEY_GENERATOR,
merkle_tree::MerklePath,
sapling::{
self,
redjubjub::{PublicKey, Signature},
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed,
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};
use super::TxProver;
pub struct MockTxProver;
impl TxProver for MockTxProver {
@ -96,7 +94,7 @@ pub mod mock {
ar: jubjub::Fr,
value: u64,
_anchor: bls12_381::Scalar,
_merkle_path: MerklePath<Node>,
_merkle_path: sapling::MerklePath,
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
let mut rng = OsRng;

View File

@ -1,6 +1,6 @@
use bitvec::{order::Lsb0, view::AsBits};
use group::{ff::PrimeField, Curve};
use incrementalmerkletree::Altitude;
use incrementalmerkletree::{Hashable, Level};
use lazy_static::lazy_static;
use std::io::{self, Read, Write};
@ -8,17 +8,21 @@ use super::{
note::ExtractedNoteCommitment,
pedersen_hash::{pedersen_hash, Personalization},
};
use crate::merkle_tree::{HashSer, Hashable};
use crate::merkle_tree::HashSer;
pub const SAPLING_COMMITMENT_TREE_DEPTH: usize = 32;
pub const SAPLING_COMMITMENT_TREE_DEPTH_U8: u8 = 32;
pub const NOTE_COMMITMENT_TREE_DEPTH: u8 = 32;
pub type CommitmentTree =
incrementalmerkletree::frontier::CommitmentTree<Node, NOTE_COMMITMENT_TREE_DEPTH>;
pub type IncrementalWitness =
incrementalmerkletree::witness::IncrementalWitness<Node, NOTE_COMMITMENT_TREE_DEPTH>;
pub type MerklePath = incrementalmerkletree::MerklePath<Node, NOTE_COMMITMENT_TREE_DEPTH>;
lazy_static! {
static ref UNCOMMITTED_SAPLING: bls12_381::Scalar = bls12_381::Scalar::one();
static ref EMPTY_ROOTS: Vec<Node> = {
let mut v = vec![Node::blank()];
for d in 0..SAPLING_COMMITMENT_TREE_DEPTH {
let next = Node::combine(d, &v[d], &v[d]);
let mut v = vec![Node::empty_leaf()];
for d in 0..NOTE_COMMITMENT_TREE_DEPTH {
let next = Node::combine(d.into(), &v[usize::from(d)], &v[usize::from(d)]);
v.push(next);
}
v
@ -93,14 +97,14 @@ impl incrementalmerkletree::Hashable for Node {
}
}
fn combine(altitude: Altitude, lhs: &Self, rhs: &Self) -> Self {
fn combine(level: Level, lhs: &Self, rhs: &Self) -> Self {
Node {
repr: merkle_hash(altitude.into(), &lhs.repr, &rhs.repr),
repr: merkle_hash(level.into(), &lhs.repr, &rhs.repr),
}
}
fn empty_root(altitude: Altitude) -> Self {
EMPTY_ROOTS[<usize>::from(altitude)]
fn empty_root(level: Level) -> Self {
EMPTY_ROOTS[<usize>::from(level)]
}
}

View File

@ -5,9 +5,6 @@ use std::error;
use std::fmt;
use std::sync::mpsc::Sender;
#[cfg(not(feature = "zfuture"))]
use std::marker::PhantomData;
use rand::{rngs::OsRng, CryptoRng, RngCore};
use crate::{
@ -15,14 +12,13 @@ use crate::{
keys::OutgoingViewingKey,
legacy::TransparentAddress,
memo::MemoBytes,
merkle_tree::MerklePath,
sapling::{prover::TxProver, value::NoteValue, Diversifier, Node, Note, PaymentAddress},
sapling::{self, prover::TxProver, value::NoteValue, Diversifier, Note, PaymentAddress},
transaction::{
components::{
amount::{Amount, BalanceError},
sapling::{
self,
builder::{SaplingBuilder, SaplingMetadata},
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
fees as sapling_fees,
},
transparent::{self, builder::TransparentBuilder},
},
@ -49,7 +45,9 @@ use crate::{
},
};
const DEFAULT_TX_EXPIRY_DELTA: u32 = 20;
/// Since Blossom activation, the default transaction expiry delta should be 40 blocks.
/// <https://zips.z.cash/zip-0203#changes-for-blossom>
const DEFAULT_TX_EXPIRY_DELTA: u32 = 40;
/// Errors that can occur during transaction construction.
#[derive(Debug, PartialEq, Eq)]
@ -67,7 +65,7 @@ pub enum Error<FeeError> {
/// An error occurred in constructing the transparent parts of a transaction.
TransparentBuild(transparent::builder::Error),
/// 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.
#[cfg(feature = "zfuture")]
TzeBuild(tze::builder::Error),
@ -144,7 +142,7 @@ pub struct Builder<'a, P, R> {
#[cfg(feature = "zfuture")]
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
#[cfg(not(feature = "zfuture"))]
tze_builder: PhantomData<&'a ()>,
tze_builder: std::marker::PhantomData<&'a ()>,
progress_notifier: Option<Sender<Progress>>,
}
@ -173,13 +171,13 @@ impl<'a, P, R> Builder<'a, P, R> {
/// Returns the set of Sapling inputs currently committed to be consumed
/// 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()
}
/// Returns the set of Sapling outputs currently set to be produced by
/// the transaction.
pub fn sapling_outputs(&self) -> &[impl sapling::fees::OutputView] {
pub fn sapling_outputs(&self) -> &[impl sapling_fees::OutputView] {
self.sapling_builder.outputs()
}
}
@ -204,7 +202,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
/// expiry delta.
pub fn new_with_rng(params: P, target_height: BlockHeight, rng: R) -> Builder<'a, P, R> {
Self::new_internal(params, rng, target_height)
}
@ -226,7 +224,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
#[cfg(feature = "zfuture")]
tze_builder: TzeBuilder::empty(),
#[cfg(not(feature = "zfuture"))]
tze_builder: PhantomData,
tze_builder: std::marker::PhantomData,
progress_notifier: None,
}
}
@ -240,8 +238,8 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note,
merkle_path: MerklePath<Node>,
) -> Result<(), sapling::builder::Error> {
merkle_path: sapling::MerklePath,
) -> Result<(), sapling_builder::Error> {
self.sapling_builder
.add_spend(&mut self.rng, extsk, diversifier, note, merkle_path)
}
@ -253,9 +251,9 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
to: PaymentAddress,
value: Amount,
memo: MemoBytes,
) -> Result<(), sapling::builder::Error> {
) -> Result<(), sapling_builder::Error> {
if value.is_negative() {
return Err(sapling::builder::Error::InvalidAmount);
return Err(sapling_builder::Error::InvalidAmount);
}
self.sapling_builder.add_output(
&mut self.rng,
@ -529,7 +527,7 @@ mod testing {
/// # Default values
///
/// The expiry height will be set to the given height plus the default transaction
/// expiry delta (20 blocks).
/// expiry delta.
///
/// WARNING: DO NOT USE IN PRODUCTION
pub fn test_only_new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> {
@ -537,6 +535,7 @@ mod testing {
}
pub fn mock_build(self) -> Result<(Transaction, SaplingMetadata), Error<Infallible>> {
#[allow(deprecated)]
self.build(&MockTxProver, &fixed::FeeRule::standard())
}
}
@ -545,18 +544,18 @@ mod testing {
#[cfg(test)]
mod tests {
use ff::Field;
use incrementalmerkletree::{frontier::CommitmentTree, witness::IncrementalWitness};
use rand_core::OsRng;
use crate::{
consensus::{NetworkUpgrade, Parameters, TEST_NETWORK},
legacy::TransparentAddress,
memo::MemoBytes,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{Node, Rseed},
transaction::components::{
amount::{Amount, DEFAULT_FEE},
sapling::builder::{self as build_s},
transparent::builder::{self as build_t},
amount::Amount,
sapling::builder::{self as sapling_builder},
transparent::builder::{self as transparent_builder},
},
zip32::ExtendedSpendingKey,
};
@ -567,9 +566,6 @@ mod tests {
#[cfg(feature = "transparent-inputs")]
use super::TzeBuilder;
#[cfg(not(feature = "zfuture"))]
use std::marker::PhantomData;
#[cfg(feature = "transparent-inputs")]
use crate::{
legacy::keys::{AccountPrivKey, IncomingViewingKey},
@ -599,7 +595,7 @@ mod tests {
Amount::from_i64(-1).unwrap(),
MemoBytes::empty()
),
Err(build_s::Error::InvalidAmount)
Err(sapling_builder::Error::InvalidAmount)
);
}
@ -626,7 +622,7 @@ mod tests {
#[cfg(feature = "zfuture")]
tze_builder: TzeBuilder::empty(),
#[cfg(not(feature = "zfuture"))]
tze_builder: PhantomData,
tze_builder: std::marker::PhantomData,
progress_notifier: None,
};
@ -653,7 +649,7 @@ mod tests {
builder
.add_transparent_output(
&TransparentAddress::PublicKey([0; 20]),
Amount::from_u64(49000).unwrap(),
Amount::from_u64(40000).unwrap(),
)
.unwrap();
@ -672,9 +668,9 @@ mod tests {
let note1 = to.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
let cmu1 = Node::from_cmu(&note1.cmu());
let mut tree = CommitmentTree::empty();
let mut tree = CommitmentTree::<Node, 32>::empty();
tree.append(cmu1).unwrap();
let witness1 = IncrementalWitness::from_tree(&tree);
let witness1 = IncrementalWitness::from_tree(tree);
let tx_height = TEST_NETWORK
.activation_height(NetworkUpgrade::Sapling)
@ -689,7 +685,7 @@ mod tests {
builder
.add_transparent_output(
&TransparentAddress::PublicKey([0; 20]),
Amount::from_u64(49000).unwrap(),
Amount::from_u64(40000).unwrap(),
)
.unwrap();
@ -697,7 +693,7 @@ mod tests {
// that a binding signature was attempted
assert_eq!(
builder.mock_build(),
Err(Error::SaplingBuild(build_s::Error::BindingSig))
Err(Error::SaplingBuild(sapling_builder::Error::BindingSig))
);
}
@ -712,12 +708,14 @@ mod tests {
&TransparentAddress::PublicKey([0; 20]),
Amount::from_i64(-1).unwrap(),
),
Err(build_t::Error::InvalidAmount)
Err(transparent_builder::Error::InvalidAmount)
);
}
#[test]
fn fails_on_negative_change() {
use crate::transaction::fees::zip317::MINIMUM_FEE;
let mut rng = OsRng;
// Just use the master key as the ExtendedSpendingKey for this test
@ -732,7 +730,7 @@ mod tests {
let builder = Builder::new(TEST_NETWORK, tx_height);
assert_eq!(
builder.mock_build(),
Err(Error::InsufficientFunds(DEFAULT_FEE))
Err(Error::InsufficientFunds(MINIMUM_FEE))
);
}
@ -741,7 +739,7 @@ mod tests {
let to = dfvk.default_address().1;
// Fail if there is only a Sapling output
// 0.0005 z-ZEC out, 0.00001 t-ZEC fee
// 0.0005 z-ZEC out, 0.0001 t-ZEC fee
{
let mut builder = Builder::new(TEST_NETWORK, tx_height);
builder
@ -755,13 +753,13 @@ mod tests {
assert_eq!(
builder.mock_build(),
Err(Error::InsufficientFunds(
(Amount::from_i64(50000).unwrap() + DEFAULT_FEE).unwrap()
(Amount::from_i64(50000).unwrap() + MINIMUM_FEE).unwrap()
))
);
}
// Fail if there is only a transparent output
// 0.0005 t-ZEC out, 0.00001 t-ZEC fee
// 0.0005 t-ZEC out, 0.0001 t-ZEC fee
{
let mut builder = Builder::new(TEST_NETWORK, tx_height);
builder
@ -773,19 +771,19 @@ mod tests {
assert_eq!(
builder.mock_build(),
Err(Error::InsufficientFunds(
(Amount::from_i64(50000).unwrap() + DEFAULT_FEE).unwrap()
(Amount::from_i64(50000).unwrap() + MINIMUM_FEE).unwrap()
))
);
}
let note1 = to.create_note(50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
let note1 = to.create_note(59999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)));
let cmu1 = Node::from_cmu(&note1.cmu());
let mut tree = CommitmentTree::empty();
let mut tree = CommitmentTree::<Node, 32>::empty();
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
// 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.0001 t-ZEC fee, 0.00059999 z-ZEC in
{
let mut builder = Builder::new(TEST_NETWORK, tx_height);
builder
@ -820,7 +818,7 @@ mod tests {
let cmu2 = Node::from_cmu(&note2.cmu());
tree.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
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.0006 z-ZEC in
@ -856,7 +854,7 @@ mod tests {
.unwrap();
assert_eq!(
builder.mock_build(),
Err(Error::SaplingBuild(build_s::Error::BindingSig))
Err(Error::SaplingBuild(sapling_builder::Error::BindingSig))
)
}
}

View File

@ -8,6 +8,12 @@ use orchard::value as orchard;
pub const COIN: i64 = 1_0000_0000;
pub const MAX_MONEY: i64 = 21_000_000 * COIN;
#[deprecated(
since = "0.12.0",
note = "To calculate the ZIP 317 fee, use `transaction::fees::zip317::FeeRule`.
For a constant representing the minimum ZIP 317 fee, use `transaction::fees::zip317::MINIMUM_FEE`.
For the constant amount 1000 zatoshis, use `Amount::const_from_i64(1000)`."
)]
pub const DEFAULT_FEE: Amount = Amount(1000);
/// A type-safe representation of some quantity of Zcash.
@ -32,6 +38,14 @@ impl Amount {
Amount(0)
}
/// Creates a constant Amount from an i64.
///
/// Panics: if the amount is outside the range `{-MAX_MONEY..MAX_MONEY}`.
pub const fn const_from_i64(amount: i64) -> Self {
assert!(-MAX_MONEY <= amount && amount <= MAX_MONEY); // contains is not const
Amount(amount)
}
/// Creates an Amount from an i64.
///
/// Returns an error if the amount is outside the range `{-MAX_MONEY..MAX_MONEY}`.

View File

@ -10,7 +10,6 @@ use crate::{
consensus::{self, BlockHeight},
keys::OutgoingViewingKey,
memo::MemoBytes,
merkle_tree::MerklePath,
sapling::{
keys::SaplingIvk,
note_encryption::sapling_note_encryption,
@ -19,7 +18,7 @@ use crate::{
spend_sig_internal,
util::generate_random_rseed_internal,
value::{NoteValue, ValueSum},
Diversifier, Node, Note, PaymentAddress,
Diversifier, MerklePath, Node, Note, PaymentAddress,
},
transaction::{
builder::Progress,
@ -67,7 +66,7 @@ pub struct SpendDescriptionInfo {
diversifier: Diversifier,
note: Note,
alpha: jubjub::Fr,
merkle_path: MerklePath<Node>,
merkle_path: MerklePath,
}
impl fees::InputView<()> for SpendDescriptionInfo {
@ -280,7 +279,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
extsk: ExtendedSpendingKey,
diversifier: Diversifier,
note: Note,
merkle_path: MerklePath<Node>,
merkle_path: MerklePath,
) -> Result<(), Error> {
// Consistency check: all anchors must equal the first one
let node = Node::from_cmu(&note.cmu());
@ -392,7 +391,8 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
let nullifier = spend.note.nf(
&proof_generation_key.to_viewing_key().nk,
spend.merkle_path.position,
u64::try_from(spend.merkle_path.position())
.expect("Sapling note commitment tree position must fit into a u64"),
);
let (zkproof, cv, rk) = prover
@ -586,7 +586,6 @@ pub mod testing {
testing::{arb_branch_id, arb_height},
TEST_NETWORK,
},
merkle_tree::{testing::arb_commitment_tree, IncrementalWitness},
sapling::{
prover::mock::MockTxProver,
testing::{arb_node, arb_note},
@ -599,6 +598,9 @@ pub mod testing {
},
zip32::sapling::testing::arb_extended_spending_key,
};
use incrementalmerkletree::{
frontier::testing::arb_commitment_tree, witness::IncrementalWitness,
};
use super::SaplingBuilder;
@ -610,8 +612,8 @@ pub mod testing {
n_notes
),
commitment_trees in vec(
arb_commitment_tree(n_notes, arb_node(), 32).prop_map(
|t| IncrementalWitness::from_tree(&t).path().unwrap()
arb_commitment_tree::<_, _, 32>(n_notes, arb_node()).prop_map(
|t| IncrementalWitness::from_tree(t).path().unwrap()
),
n_notes
),

View File

@ -1,9 +1,7 @@
use crate::{
consensus::{self, BlockHeight},
transaction::components::{
amount::{Amount, DEFAULT_FEE},
transparent::fees as transparent,
},
transaction::components::{amount::Amount, transparent::fees as transparent},
transaction::fees::zip317,
};
#[cfg(feature = "zfuture")]
@ -22,10 +20,22 @@ impl FeeRule {
Self { fixed_fee }
}
/// Creates a new fixed fee rule with the standard default fee.
/// Creates a new fixed fee rule with the minimum possible [ZIP 317] fee,
/// i.e. 10000 zatoshis.
///
/// Note that using a fixed fee is not compliant with [ZIP 317]; consider
/// using [`zcash_primitives::transaction::fees::zip317::FeeRule::standard()`]
/// instead.
///
/// [`zcash_primitives::transaction::fees::zip317::FeeRule::standard()`]: crate::transaction::fees::zip317::FeeRule::standard
/// [ZIP 317]: https://zips.z.cash/zip-0317
#[deprecated(
since = "0.12.0",
note = "To calculate the ZIP 317 fee, use `transaction::fees::zip317::FeeRule::standard()`. For a fixed fee, use the `non_standard` constructor."
)]
pub fn standard() -> Self {
Self {
fixed_fee: DEFAULT_FEE,
fixed_fee: zip317::MINIMUM_FEE,
}
}

View File

@ -13,6 +13,13 @@ use crate::{
},
};
/// The minimum conventional fee using the standard [ZIP 317] constants. Equivalent to
/// `(FeeRule::standard().marginal_fee() * FeeRule::standard().grace_actions()).unwrap()`,
/// but as a constant.
///
/// [ZIP 317]: https//zips.z.cash/zip-0317
pub const MINIMUM_FEE: Amount = Amount::const_from_i64(10_000);
/// A [`FeeRule`] implementation that implements the [ZIP 317] fee rule.
///
/// This fee rule supports only P2pkh transparent inputs; an error will be returned if a coin

View File

@ -7,6 +7,9 @@ and this library adheres to Rust's notion of
## [Unreleased]
### Removed
- `circuit::sapling::TREE_DEPTH` use `zcash_primitives::sapling::NOTE_COMMITMENT_TREE_DEPTH` instead
## [0.11.0] - 2023-04-15
### Changed
- Bumped dependencies to `bls12_381 0.8`, `group 0.13`, `jubjub 0.10`,

View File

@ -24,6 +24,7 @@ zcash_primitives = { version = "0.11", path = "../zcash_primitives", default-fea
bellman = { version = "0.14", default-features = false, features = ["groth16"] }
bls12_381 = "0.8"
group = "0.13"
incrementalmerkletree = { version = "0.3", features = ["legacy-api"] }
jubjub = "0.10"
lazy_static = "1"
minreq = { version = "2", features = ["https"], optional = true }

View File

@ -6,9 +6,7 @@ use bellman::{Circuit, ConstraintSystem, SynthesisError};
use zcash_primitives::constants;
use zcash_primitives::sapling::{
PaymentAddress, ProofGenerationKey, SAPLING_COMMITMENT_TREE_DEPTH,
};
use zcash_primitives::sapling::{PaymentAddress, ProofGenerationKey};
use super::ecc;
use super::pedersen_hash;
@ -29,8 +27,6 @@ use group::ff::PrimeFieldBits;
#[cfg(test)]
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.
#[derive(Clone)]
pub struct ValueCommitmentOpening {

View File

@ -4,12 +4,11 @@ use bellman::groth16::{Parameters, PreparedVerifyingKey};
use bls12_381::Bls12;
use std::path::Path;
use zcash_primitives::{
merkle_tree::MerklePath,
sapling::{
prover::TxProver,
redjubjub::{PublicKey, Signature},
value::ValueCommitment,
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed,
Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};
@ -154,7 +153,7 @@ impl TxProver for LocalTxProver {
ar: jubjub::Fr,
value: u64,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
merkle_path: MerklePath,
) -> Result<([u8; GROTH_PROOF_SIZE], ValueCommitment, PublicKey), ()> {
let (proof, cv, rk) = ctx.spend_proof(
proof_generation_key,

View File

@ -7,11 +7,10 @@ use group::{Curve, GroupEncoding};
use rand_core::OsRng;
use zcash_primitives::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
merkle_tree::MerklePath,
sapling::{
redjubjub::{PublicKey, Signature},
value::{CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment},
Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed,
Diversifier, MerklePath, Note, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::Amount,
};
@ -52,7 +51,7 @@ impl SaplingProvingContext {
ar: jubjub::Fr,
value: u64,
anchor: bls12_381::Scalar,
merkle_path: MerklePath<Node>,
merkle_path: MerklePath,
proving_key: &Parameters<Bls12>,
verifying_key: &PreparedVerifyingKey<Bls12>,
) -> Result<(Proof<Bls12>, ValueCommitment, PublicKey), ()> {
@ -84,9 +83,14 @@ impl SaplingProvingContext {
// Let's compute the nullifier while we have the position
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,
u64::try_from(merkle_path.position())
.expect("Sapling note commitment tree position must fit into a u64"),
);
// We now have the full witness for our circuit
let pos: usize = merkle_path.position().into();
let instance = Spend {
value_commitment_opening: Some(value_commitment_opening),
proof_generation_key: Some(proof_generation_key),
@ -94,9 +98,10 @@ impl SaplingProvingContext {
commitment_randomness: Some(note.rcm()),
ar: Some(ar),
auth_path: merkle_path
.auth_path
.path_elems()
.iter()
.map(|(node, b)| Some(((*node).into(), *b)))
.enumerate()
.map(|(i, node)| Some(((*node).into(), pos >> i & 0x1 == 1)))
.collect(),
anchor: Some(anchor),
};