From ae3cc78a568002af9b87de066f17efee9e7597ef Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 14 Feb 2022 16:00:04 -0700 Subject: [PATCH 01/12] Add decryption of the diversifier index for an address to the IVK. Also correct a spelling error. --- src/address.rs | 2 +- src/keys.rs | 27 ++++++++++++++++++++------- src/note_encryption.rs | 2 +- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/address.rs b/src/address.rs index 560e3942..a6712051 100644 --- a/src/address.rs +++ b/src/address.rs @@ -32,7 +32,7 @@ impl Address { Address { d, pk_d } } - pub(crate) fn diversifer(&self) -> Diversifier { + pub(crate) fn diversifier(&self) -> Diversifier { self.d } diff --git a/src/keys.rs b/src/keys.rs index 636d0332..eace73b0 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -406,12 +406,6 @@ impl FullViewingKey { #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct DiversifierKey([u8; 32]); -impl From<&FullViewingKey> for DiversifierKey { - fn from(fvk: &FullViewingKey) -> Self { - fvk.derive_dk_ovk().0 - } -} - /// The index for a particular diversifier. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct DiversifierIndex([u8; 11]); @@ -437,6 +431,13 @@ impl From<[u8; 11]> for DiversifierIndex { } } +impl DiversifierIndex { + /// Returns the raw bytes of the diversifier index. + pub fn to_bytes(&self) -> &[u8; 11] { + &self.0 + } +} + impl DiversifierKey { /// Returns the diversifier at index 0. pub fn default_diversifier(&self) -> Diversifier { @@ -556,7 +557,7 @@ pub struct IncomingViewingKey { impl From<&FullViewingKey> for IncomingViewingKey { fn from(fvk: &FullViewingKey) -> Self { IncomingViewingKey { - dk: fvk.into(), + dk: fvk.derive_dk_ovk().0, ivk: fvk.into(), } } @@ -583,6 +584,18 @@ impl IncomingViewingKey { }) } + /// Checks whether the given address was derived from this incoming viewing + /// key, and returns the diversifier index used to derive the address if + /// so. Returns `None` if the address was not derived from this key. + pub fn diversifier_index(&self, addr: &Address) -> Option { + let j = self.dk.diversifier_index(&addr.diversifier()); + if &self.address_at(j) == addr { + Some(j) + } else { + None + } + } + /// Returns the default payment address for this key. pub fn default_address(&self) -> Address { self.address(self.dk.default_diversifier()) diff --git a/src/note_encryption.rs b/src/note_encryption.rs index c0aabfd3..c307625e 100644 --- a/src/note_encryption.rs +++ b/src/note_encryption.rs @@ -149,7 +149,7 @@ impl Domain for OrchardDomain { ) -> NotePlaintextBytes { let mut np = [0; NOTE_PLAINTEXT_SIZE]; np[0] = 0x02; - np[1..12].copy_from_slice(note.recipient().diversifer().as_array()); + np[1..12].copy_from_slice(note.recipient().diversifier().as_array()); np[12..20].copy_from_slice(¬e.value().to_bytes()); np[20..52].copy_from_slice(note.rseed().to_bytes()); np[52..].copy_from_slice(memo); From 4c7ab377fb268d862450ef911eb50d4ea4ac1207 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Mon, 14 Feb 2022 17:02:08 -0700 Subject: [PATCH 02/12] Make the DiversifierKey type crate-private --- src/keys.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/keys.rs b/src/keys.rs index eace73b0..f79924e1 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -404,7 +404,7 @@ impl FullViewingKey { /// /// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub struct DiversifierKey([u8; 32]); +pub(crate) struct DiversifierKey([u8; 32]); /// The index for a particular diversifier. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -850,7 +850,7 @@ pub mod testing { prop_compose! { /// Generate a uniformly distributed Orchard diversifier key. - pub fn arb_diversifier_key()( + pub(crate) fn arb_diversifier_key()( dk_bytes in prop::array::uniform32(prop::num::u8::ANY) ) -> DiversifierKey { DiversifierKey::from_bytes(dk_bytes) From 5d5e28919708f725d3d79726d422a53d14456cc9 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 15 Feb 2022 08:30:09 -0700 Subject: [PATCH 03/12] Remove default_diversifier and default_address methods. --- benches/circuit.rs | 2 +- benches/note_decryption.rs | 2 +- benches/small.rs | 2 +- src/address.rs | 11 +++++++---- src/builder.rs | 6 +++--- src/circuit.rs | 2 +- src/keys.rs | 18 ++---------------- src/note.rs | 2 +- 8 files changed, 17 insertions(+), 28 deletions(-) diff --git a/benches/circuit.rs b/benches/circuit.rs index 8a1f2ce5..85c14de4 100644 --- a/benches/circuit.rs +++ b/benches/circuit.rs @@ -20,7 +20,7 @@ fn criterion_benchmark(c: &mut Criterion) { let rng = OsRng; let sk = SpendingKey::from_bytes([7; 32]).unwrap(); - let recipient = FullViewingKey::from(&sk).default_address(); + let recipient = FullViewingKey::from(&sk).address_at(0u32); let vk = VerifyingKey::build(); let pk = ProvingKey::build(); diff --git a/benches/note_decryption.rs b/benches/note_decryption.rs index 23167fb1..2da15f2b 100644 --- a/benches/note_decryption.rs +++ b/benches/note_decryption.rs @@ -22,7 +22,7 @@ fn bench_note_decryption(c: &mut Criterion) { let fvk = FullViewingKey::from(&SpendingKey::from_bytes([7; 32]).unwrap()); let valid_ivk = IncomingViewingKey::from(&fvk); - let recipient = fvk.default_address(); + let recipient = fvk.address_at(0u32); // Compact actions don't have the full AEAD ciphertext, so ZIP 307 trial-decryption // relies on an invalid ivk resulting in random noise for which the note commitment diff --git a/benches/small.rs b/benches/small.rs index 4e76d30e..2ea43f59 100644 --- a/benches/small.rs +++ b/benches/small.rs @@ -12,7 +12,7 @@ fn key_derivation(c: &mut Criterion) { let fvk = FullViewingKey::from(&sk); c.bench_function("derive_fvk", |b| b.iter(|| FullViewingKey::from(&sk))); - c.bench_function("default_address", |b| b.iter(|| fvk.default_address())); + c.bench_function("default_address", |b| b.iter(|| fvk.address_at(0u32))); } criterion_group!(benches, key_derivation); diff --git a/src/address.rs b/src/address.rs index a6712051..24a41b25 100644 --- a/src/address.rs +++ b/src/address.rs @@ -15,7 +15,7 @@ use crate::{ /// use orchard::keys::{SpendingKey, FullViewingKey}; /// /// let sk = SpendingKey::from_bytes([7; 32]).unwrap(); -/// let address = FullViewingKey::from(&sk).default_address(); +/// let address = FullViewingKey::from(&sk).address_at(0u32); /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Address { @@ -71,15 +71,18 @@ impl Address { pub mod testing { use proptest::prelude::*; - use crate::keys::{testing::arb_spending_key, FullViewingKey}; + use crate::keys::{ + testing::{arb_diversifier_index, arb_spending_key}, + FullViewingKey, + }; use super::Address; prop_compose! { /// Generates an arbitrary payment address. - pub(crate) fn arb_address()(sk in arb_spending_key()) -> Address { + pub(crate) fn arb_address()(sk in arb_spending_key(), j in arb_diversifier_index()) -> Address { let fvk = FullViewingKey::from(&sk); - fvk.default_address() + fvk.address_at(j) } } } diff --git a/src/builder.rs b/src/builder.rs index 54b9a1ef..f4d786be 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -90,7 +90,7 @@ impl RecipientInfo { /// [orcharddummynotes]: https://zips.z.cash/protocol/nu5.pdf#orcharddummynotes fn dummy(rng: &mut impl RngCore) -> Self { let fvk: FullViewingKey = (&SpendingKey::random(rng)).into(); - let recipient = fvk.default_address(); + let recipient = fvk.address_at(0u32); RecipientInfo { ovk: None, @@ -133,7 +133,7 @@ impl ActionInfo { let cv_net = ValueCommitment::derive(v_net, self.rcv.clone()); let nf_old = self.spend.note.nullifier(&self.spend.fvk); - let sender_address = self.spend.fvk.default_address(); + let sender_address = self.spend.fvk.address_at(0u32); let rho_old = self.spend.note.rho(); let psi_old = self.spend.note.rseed().psi(&rho_old); let rcm_old = self.spend.note.rseed().rcm(&rho_old); @@ -724,7 +724,7 @@ mod tests { let sk = SpendingKey::random(&mut rng); let fvk = FullViewingKey::from(&sk); - let recipient = fvk.default_address(); + let recipient = fvk.address_at(0u32); let mut builder = Builder::new( Flags::from_parts(true, true), diff --git a/src/circuit.rs b/src/circuit.rs index 5d201a2f..b45b4dd8 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -961,7 +961,7 @@ mod tests { .map(|()| { let (_, fvk, spent_note) = Note::dummy(&mut rng, None); - let sender_address = fvk.default_address(); + let sender_address = fvk.address_at(0u32); let nk = *fvk.nk(); let rivk = *fvk.rivk(); let nf_old = spent_note.nullifier(&fvk); diff --git a/src/keys.rs b/src/keys.rs index f79924e1..8b028edb 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -333,11 +333,6 @@ impl FullViewingKey { ) } - /// Returns the default payment address for this key. - pub fn default_address(&self) -> Address { - IncomingViewingKey::from(self).default_address() - } - /// Returns the payment address for this key at the given index. pub fn address_at(&self, j: impl Into) -> Address { IncomingViewingKey::from(self).address_at(j) @@ -439,11 +434,6 @@ impl DiversifierIndex { } impl DiversifierKey { - /// Returns the diversifier at index 0. - pub fn default_diversifier(&self) -> Diversifier { - self.get(0u32) - } - /// Returns the diversifier at the given index. pub fn get(&self, j: impl Into) -> Diversifier { let ff = FF1::::new(&self.0, 2).expect("valid radix"); @@ -596,11 +586,6 @@ impl IncomingViewingKey { } } - /// Returns the default payment address for this key. - pub fn default_address(&self) -> Address { - self.address(self.dk.default_diversifier()) - } - /// Returns the payment address for this key at the given index. pub fn address_at(&self, j: impl Into) -> Address { self.address(self.dk.get(j)) @@ -903,9 +888,10 @@ mod tests { fn key_agreement( sk in arb_spending_key(), esk in arb_esk(), + j in arb_diversifier_index(), ) { let ivk = IncomingViewingKey::from(&(&sk).into()); - let addr = ivk.default_address(); + let addr = ivk.address_at(j); let epk = esk.derive_public(addr.g_d()); diff --git a/src/note.rs b/src/note.rs index de318194..40aeb376 100644 --- a/src/note.rs +++ b/src/note.rs @@ -155,7 +155,7 @@ impl Note { ) -> (SpendingKey, FullViewingKey, Self) { let sk = SpendingKey::random(rng); let fvk: FullViewingKey = (&sk).into(); - let recipient = fvk.default_address(); + let recipient = fvk.address_at(0u32); let note = Note::new( recipient, From 00deb330e66ae83dec0c1999b4c978e4b1d5d538 Mon Sep 17 00:00:00 2001 From: Kris Nuttycombe Date: Tue, 15 Feb 2022 08:57:16 -0700 Subject: [PATCH 04/12] Fix pprof version. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 12c30880..ac0265c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ proptest = "1.0.0" zcash_note_encryption = { version = "0.1", features = ["pre-zip-212"] } [target.'cfg(unix)'.dev-dependencies] -pprof = { version = "0.6", features = ["criterion", "flamegraph"] } +pprof = { version = "=0.6.1", features = ["criterion", "flamegraph"] } [lib] bench = false From e92d1167af726aa3031bbf3bfd41e1fdea8d5b91 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Tue, 15 Feb 2022 21:23:15 +0000 Subject: [PATCH 05/12] The address used to derive g_d_old and pk_d_old is the recipient address of the note being spent. --- src/builder.rs | 2 +- src/circuit.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index f4d786be..c5752ed8 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -133,7 +133,7 @@ impl ActionInfo { let cv_net = ValueCommitment::derive(v_net, self.rcv.clone()); let nf_old = self.spend.note.nullifier(&self.spend.fvk); - let sender_address = self.spend.fvk.address_at(0u32); + let sender_address = self.spend.note.recipient(); let rho_old = self.spend.note.rho(); let psi_old = self.spend.note.rseed().psi(&rho_old); let rcm_old = self.spend.note.rseed().rcm(&rho_old); diff --git a/src/circuit.rs b/src/circuit.rs index b45b4dd8..e63917f5 100644 --- a/src/circuit.rs +++ b/src/circuit.rs @@ -961,7 +961,7 @@ mod tests { .map(|()| { let (_, fvk, spent_note) = Note::dummy(&mut rng, None); - let sender_address = fvk.address_at(0u32); + let sender_address = spent_note.recipient(); let nk = *fvk.nk(); let rivk = *fvk.rivk(); let nf_old = spent_note.nullifier(&fvk); From 0b6bd07904b34eee1959224ef846ebf432d2647d Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 19:55:54 +0000 Subject: [PATCH 06/12] Add functional test for creating and verifying a shielding bundle The text exposed some limitations of the current crate API, which have been fixed. --- src/bundle.rs | 30 +++++++++++++-------- src/bundle/commitments.rs | 9 +++---- src/primitives/redpallas.rs | 7 +++++ tests/builder.rs | 54 +++++++++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 16 deletions(-) create mode 100644 tests/builder.rs diff --git a/src/bundle.rs b/src/bundle.rs index 550b42ca..2df9d239 100644 --- a/src/bundle.rs +++ b/src/bundle.rs @@ -2,6 +2,7 @@ pub mod commitments; +use std::convert::TryInto; use std::io; use blake2b_simd::Hash as Blake2bHash; @@ -298,15 +299,6 @@ impl Bundle { &self.authorization } - /// Computes a commitment to the effects of this bundle, suitable for inclusion within - /// a transaction ID. - pub fn commitment<'a>(&'a self) -> BundleCommitment - where - i64: From<&'a V>, - { - BundleCommitment(hash_bundle_txid_data(self)) - } - /// Construct a new bundle by applying a transformation that might fail /// to the value balance. pub fn try_map_value_balance Result>( @@ -405,7 +397,13 @@ impl Bundle { } } -impl> Bundle { +impl> Bundle { + /// Computes a commitment to the effects of this bundle, suitable for inclusion within + /// a transaction ID. + pub fn commitment(&self) -> BundleCommitment { + BundleCommitment(hash_bundle_txid_data(self)) + } + /// Returns the transaction binding validating key for this bundle. /// /// This can be used to validate the [`Authorized::binding_signature`] returned from @@ -416,7 +414,10 @@ impl> Bundle { .iter() .map(|a| a.cv_net()) .sum::() - - ValueCommitment::derive(self.value_balance.into(), ValueCommitTrapdoor::zero())) + - ValueCommitment::derive( + ValueSum::from_raw(self.value_balance.into()), + ValueCommitTrapdoor::zero(), + )) .into_bvk() } } @@ -500,6 +501,13 @@ impl DynamicUsage for Bundle { #[derive(Debug)] pub struct BundleCommitment(pub Blake2bHash); +impl From for [u8; 32] { + fn from(commitment: BundleCommitment) -> Self { + // The commitment uses BLAKE2b-256. + commitment.0.as_bytes().try_into().unwrap() + } +} + /// A commitment to the authorizing data within a bundle of actions. #[derive(Debug)] pub struct BundleAuthorizingCommitment(pub Blake2bHash); diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 2b0334b0..548afda1 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -28,10 +28,9 @@ fn hasher(personal: &[u8; 16]) -> State { /// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION /// /// [zip244]: https://zips.z.cash/zip-0244 -pub fn hash_bundle_txid_data<'a, A: Authorization, V>(bundle: &'a Bundle) -> Blake2bHash -where - i64: From<&'a V>, -{ +pub fn hash_bundle_txid_data>( + bundle: &Bundle, +) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); let mut ch = hasher(ZCASH_ORCHARD_ACTIONS_COMPACT_HASH_PERSONALIZATION); let mut mh = hasher(ZCASH_ORCHARD_ACTIONS_MEMOS_HASH_PERSONALIZATION); @@ -59,7 +58,7 @@ where h.write_all(&mh.finalize().as_bytes()).unwrap(); h.write_all(&nh.finalize().as_bytes()).unwrap(); h.write_all(&[bundle.flags().to_byte()]).unwrap(); - h.write_all(&::from(bundle.value_balance()).to_le_bytes()) + h.write_all(&(*bundle.value_balance()).into().to_le_bytes()) .unwrap(); h.write_all(&bundle.anchor().to_bytes()).unwrap(); h.finalize() diff --git a/src/primitives/redpallas.rs b/src/primitives/redpallas.rs index cbdae803..903de725 100644 --- a/src/primitives/redpallas.rs +++ b/src/primitives/redpallas.rs @@ -147,6 +147,13 @@ impl VerificationKey { } } +impl VerificationKey { + /// Verifies a purported `signature` over `msg` made by this verification key. + pub fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), reddsa::Error> { + self.0.verify(msg, &signature.0) + } +} + /// A RedPallas signature. #[derive(Debug, Clone)] pub struct Signature(reddsa::Signature); diff --git a/tests/builder.rs b/tests/builder.rs new file mode 100644 index 00000000..137db412 --- /dev/null +++ b/tests/builder.rs @@ -0,0 +1,54 @@ +use incrementalmerkletree::Hashable; +use orchard::{ + builder::Builder, + bundle::{Authorized, Flags}, + circuit::{ProvingKey, VerifyingKey}, + keys::{FullViewingKey, SpendingKey}, + tree::MerkleHashOrchard, + value::NoteValue, + Bundle, +}; +use rand::rngs::OsRng; + +fn verify_bundle(bundle: &Bundle, vk: &VerifyingKey) { + assert!(matches!(bundle.verify_proof(&vk), Ok(()))); + let sighash: [u8; 32] = bundle.commitment().into(); + let bvk = bundle.binding_validating_key(); + for action in bundle.actions() { + assert_eq!(action.rk().verify(&sighash, action.authorization()), Ok(())); + } + assert_eq!( + bvk.verify(&sighash, bundle.authorization().binding_signature()), + Ok(()) + ); +} + +#[test] +fn bundle_chain() { + let mut rng = OsRng; + let pk = ProvingKey::build(); + let vk = VerifyingKey::build(); + + let sk = SpendingKey::from_bytes([0; 32]).unwrap(); + let fvk = FullViewingKey::from(&sk); + let recipient = fvk.address_at(0u32); + + // Create a shielding bundle. + let shielding_bundle: Bundle<_, i64> = { + // Use the empty tree. + let anchor = MerkleHashOrchard::empty_root(32.into()).into(); + + let mut builder = Builder::new(Flags::from_parts(false, true), anchor); + assert_eq!( + builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + Ok(()) + ); + let unauthorized = builder.build(&mut rng).unwrap(); + let sighash = unauthorized.commitment().into(); + let proven = unauthorized.create_proof(&pk).unwrap(); + proven.apply_signatures(&mut rng, sighash, &[]).unwrap() + }; + + // Verify the shielding bundle. + verify_bundle(&shielding_bundle, &vk); +} From 62da82bd38bb53e430262e751655d44d05fdefc4 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 21:44:57 +0000 Subject: [PATCH 07/12] Add functional test for creating and verifying a shielded bundle The text exposed some limitations of the current crate API, which have been fixed. --- src/tree.rs | 12 +++++++++-- tests/builder.rs | 54 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/tree.rs b/src/tree.rs index c3b3f08b..754ccf27 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -107,11 +107,19 @@ impl MerklePath { /// Instantiates a new Merkle path given a leaf position and authentication path. pub(crate) fn new(position: u32, auth_path: [pallas::Base; MERKLE_DEPTH_ORCHARD]) -> Self { - Self { + Self::from_parts( position, - auth_path: gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |i| { + gen_const_array_with_default(MerkleHashOrchard::empty_leaf(), |i| { MerkleHashOrchard(auth_path[i]) }), + ) + } + + /// Instantiates a new Merkle path given a leaf position and authentication path. + pub fn from_parts(position: u32, auth_path: [MerkleHashOrchard; MERKLE_DEPTH_ORCHARD]) -> Self { + Self { + position, + auth_path, } } diff --git a/tests/builder.rs b/tests/builder.rs index 137db412..0c375da7 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -1,14 +1,19 @@ -use incrementalmerkletree::Hashable; +use std::convert::TryInto; + +use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Hashable, Tree}; use orchard::{ builder::Builder, bundle::{Authorized, Flags}, circuit::{ProvingKey, VerifyingKey}, - keys::{FullViewingKey, SpendingKey}, - tree::MerkleHashOrchard, + keys::{FullViewingKey, IncomingViewingKey, SpendAuthorizingKey, SpendingKey}, + note::ExtractedNoteCommitment, + note_encryption::OrchardDomain, + tree::{MerkleHashOrchard, MerklePath}, value::NoteValue, Bundle, }; use rand::rngs::OsRng; +use zcash_note_encryption::try_note_decryption; fn verify_bundle(bundle: &Bundle, vk: &VerifyingKey) { assert!(matches!(bundle.verify_proof(&vk), Ok(()))); @@ -51,4 +56,47 @@ fn bundle_chain() { // Verify the shielding bundle. verify_bundle(&shielding_bundle, &vk); + + // Create a shielded bundle spending the previous output. + let shielded_bundle: Bundle<_, i64> = { + let ivk = IncomingViewingKey::from(&fvk); + let (note, _, _) = shielding_bundle + .actions() + .iter() + .find_map(|action| { + let domain = OrchardDomain::for_action(action); + try_note_decryption(&domain, &ivk, action) + }) + .unwrap(); + + // Use the tree with a single leaf. + let cmx: ExtractedNoteCommitment = note.commitment().into(); + let leaf = MerkleHashOrchard::from_cmx(&cmx); + let mut tree = BridgeTree::::new(0); + tree.append(&leaf); + tree.witness(); + let (position, auth_path) = tree.authentication_path(&leaf).unwrap(); + let merkle_path = MerklePath::from_parts( + u64::from(position).try_into().unwrap(), + auth_path[..].try_into().unwrap(), + ); + let anchor = tree.root().into(); + assert_eq!(anchor, merkle_path.root(cmx)); + + let mut builder = Builder::new(Flags::from_parts(true, true), anchor); + assert_eq!(builder.add_spend(fvk, note, merkle_path), Ok(())); + assert_eq!( + builder.add_recipient(None, recipient, NoteValue::from_raw(5000), None), + Ok(()) + ); + let unauthorized = builder.build(&mut rng).unwrap(); + let sighash = unauthorized.commitment().into(); + let proven = unauthorized.create_proof(&pk).unwrap(); + proven + .apply_signatures(&mut rng, sighash, &[SpendAuthorizingKey::from(&sk)]) + .unwrap() + }; + + // Verify the shielded bundle. + verify_bundle(&shielded_bundle, &vk); } From b1d7787ab67f7913f4eb7c1b1ef9f2f5ab87a996 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 22:47:05 +0000 Subject: [PATCH 08/12] Re-introduce `ValueSum::from_raw` as a `pub(crate)` method We removed this in zcash/orchard#267 as it did not need to be part of the public API, but we do still need a way to convert the user-defined valueBalance type into a `ValueSum` when constructing `bvk`, and this method is preferable to exposing the `ValueSum` internals. --- src/value.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/value.rs b/src/value.rs index 1568a5ae..f4ebdeef 100644 --- a/src/value.rs +++ b/src/value.rs @@ -140,6 +140,16 @@ impl ValueSum { // Default for i64 is zero. Default::default() } + + /// Creates a value sum from its raw numeric value. + /// + /// This only enforces that the value is a signed 63-bit integer. We use it internally + /// in `Bundle::binding_validating_key`, where we are converting from the user-defined + /// `valueBalance` type that enforces any additional constraints on the value's valid + /// range. + pub(crate) fn from_raw(value: i64) -> Self { + ValueSum(value as i128) + } } impl Add for ValueSum { From 4ba063a07dcb934be575da39ff0dee9cf239cabe Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 22:48:28 +0000 Subject: [PATCH 09/12] Update non-consensus tests for changes in consensus branch --- tests/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/builder.rs b/tests/builder.rs index 0c375da7..35da5564 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -50,7 +50,7 @@ fn bundle_chain() { ); let unauthorized = builder.build(&mut rng).unwrap(); let sighash = unauthorized.commitment().into(); - let proven = unauthorized.create_proof(&pk).unwrap(); + let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven.apply_signatures(&mut rng, sighash, &[]).unwrap() }; @@ -91,7 +91,7 @@ fn bundle_chain() { ); let unauthorized = builder.build(&mut rng).unwrap(); let sighash = unauthorized.commitment().into(); - let proven = unauthorized.create_proof(&pk).unwrap(); + let proven = unauthorized.create_proof(&pk, &mut rng).unwrap(); proven .apply_signatures(&mut rng, sighash, &[SpendAuthorizingKey::from(&sk)]) .unwrap() From 9283002c13e708f9212b5810982620ffed5b2f58 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 22:56:01 +0000 Subject: [PATCH 10/12] Update changelog --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index aa112d59..b907513a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `orchard::keys`: + - `DiversifierIndex::to_bytes` + - `IncomingViewingKey::diversifier_index` +- `orchard::primitives::redpallas::VerificationKey::verify` +- `orchard::tree::MerklePath::from_parts` +- `impl From for [u8; 32]` ### Changed - MSRV is now 1.54.0. @@ -14,8 +21,17 @@ and this project adheres to Rust's notion of - `orchard::builder::Bundle::create_proof` - `orchard::builder::InProgress::create_proof` - `orchard::circuit::Proof::create` +- `orchard::Bundle::commitment` now requires the bound `V: Copy + Into` + instead of `i64: From<&'a V>`. +- `orchard::Bundle::binding_validating_key` now requires the bound + `V: Into` instead of `V: Into`. ### Removed +- `orchard::keys`: + - `FullViewingKey::default_address` + - `IncomingViewingKey::default_address` +- `DiversifierKey` (use the APIs on `FullViewingKey` and `IncomingViewingKey` + instead). - `orchard::value::ValueSum::from_raw` ## [0.1.0-beta.1] - 2021-12-17 From 28c22718c088c3c3a9bcd1022b721c2988daa2bc Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 23:03:02 +0000 Subject: [PATCH 11/12] Remove `hash_bundle_txid_data, hash_bundle_auth_data` from API --- CHANGELOG.md | 4 ++++ src/bundle/commitments.rs | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b907513a..a8bfecfa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,10 @@ and this project adheres to Rust's notion of `V: Into` instead of `V: Into`. ### Removed +- `orchard::bundle`: + - `commitments::hash_bundle_txid_data` (use `Bundle::commitment` instead). + - `commitments::hash_bundle_auth_data` (use `Bundle::authorizing_commitment` + instead). - `orchard::keys`: - `FullViewingKey::default_address` - `IncomingViewingKey::default_address` diff --git a/src/bundle/commitments.rs b/src/bundle/commitments.rs index 72b57e2d..1d2d5c7d 100644 --- a/src/bundle/commitments.rs +++ b/src/bundle/commitments.rs @@ -28,7 +28,7 @@ fn hasher(personal: &[u8; 16]) -> State { /// personalized with ZCASH_ORCHARD_ACTIONS_HASH_PERSONALIZATION /// /// [zip244]: https://zips.z.cash/zip-0244 -pub fn hash_bundle_txid_data>( +pub(crate) fn hash_bundle_txid_data>( bundle: &Bundle, ) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_HASH_PERSONALIZATION); @@ -77,7 +77,7 @@ pub fn hash_bundle_txid_empty() -> Blake2bHash { /// Identifier Non-Malleability][zip244] /// /// [zip244]: https://zips.z.cash/zip-0244 -pub fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { +pub(crate) fn hash_bundle_auth_data(bundle: &Bundle) -> Blake2bHash { let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION); h.write_all(bundle.authorization().proof().as_ref()) .unwrap(); From 0944cf8800993a7952c9b71d99d07cd3e0e26aed Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Tue, 15 Feb 2022 23:15:44 +0000 Subject: [PATCH 12/12] Fix clippy lint --- tests/builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/builder.rs b/tests/builder.rs index 35da5564..41bce9fc 100644 --- a/tests/builder.rs +++ b/tests/builder.rs @@ -16,7 +16,7 @@ use rand::rngs::OsRng; use zcash_note_encryption::try_note_decryption; fn verify_bundle(bundle: &Bundle, vk: &VerifyingKey) { - assert!(matches!(bundle.verify_proof(&vk), Ok(()))); + assert!(matches!(bundle.verify_proof(vk), Ok(()))); let sighash: [u8; 32] = bundle.commitment().into(); let bvk = bundle.binding_validating_key(); for action in bundle.actions() {