mirror of https://github.com/zcash/orchard.git
Merge pull request #286 from zcash/merge-non-consensus-changes
Merge non-consensus changes
This commit is contained in:
commit
f1795f8068
20
CHANGELOG.md
20
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<orchard::bundle::BundleCommitment> for [u8; 32]`
|
||||
|
||||
### Changed
|
||||
- MSRV is now 1.54.0.
|
||||
|
@ -14,8 +21,21 @@ 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<i64>`
|
||||
instead of `i64: From<&'a V>`.
|
||||
- `orchard::Bundle::binding_validating_key` now requires the bound
|
||||
`V: Into<i64>` instead of `V: Into<ValueSum>`.
|
||||
|
||||
### 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`
|
||||
- `DiversifierKey` (use the APIs on `FullViewingKey` and `IncomingViewingKey`
|
||||
instead).
|
||||
- `orchard::value::ValueSum::from_raw`
|
||||
|
||||
## [0.1.0-beta.1] - 2021-12-17
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
@ -32,7 +32,7 @@ impl Address {
|
|||
Address { d, pk_d }
|
||||
}
|
||||
|
||||
pub(crate) fn diversifer(&self) -> Diversifier {
|
||||
pub(crate) fn diversifier(&self) -> Diversifier {
|
||||
self.d
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.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);
|
||||
|
@ -731,7 +731,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),
|
||||
|
|
|
@ -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<T: Authorization, V> Bundle<T, V> {
|
|||
&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<V0, E, F: FnOnce(V) -> Result<V0, E>>(
|
||||
|
@ -405,7 +397,13 @@ impl<T: Authorization, V> Bundle<T, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Authorization, V: Copy + Into<ValueSum>> Bundle<T, V> {
|
||||
impl<T: Authorization, V: Copy + Into<i64>> Bundle<T, V> {
|
||||
/// 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<T: Authorization, V: Copy + Into<ValueSum>> Bundle<T, V> {
|
|||
.iter()
|
||||
.map(|a| a.cv_net())
|
||||
.sum::<ValueCommitment>()
|
||||
- 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<V: DynamicUsage> DynamicUsage for Bundle<Authorized, V> {
|
|||
#[derive(Debug)]
|
||||
pub struct BundleCommitment(pub Blake2bHash);
|
||||
|
||||
impl From<BundleCommitment> 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);
|
||||
|
|
|
@ -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<A, V>) -> Blake2bHash
|
||||
where
|
||||
i64: From<&'a V>,
|
||||
{
|
||||
pub(crate) fn hash_bundle_txid_data<A: Authorization, V: Copy + Into<i64>>(
|
||||
bundle: &Bundle<A, V>,
|
||||
) -> 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(&<i64>::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()
|
||||
|
@ -78,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<V>(bundle: &Bundle<Authorized, V>) -> Blake2bHash {
|
||||
pub(crate) fn hash_bundle_auth_data<V>(bundle: &Bundle<Authorized, V>) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_ORCHARD_SIGS_HASH_PERSONALIZATION);
|
||||
h.write_all(bundle.authorization().proof().as_ref())
|
||||
.unwrap();
|
||||
|
|
|
@ -910,7 +910,7 @@ mod tests {
|
|||
.map(|()| {
|
||||
let (_, fvk, spent_note) = Note::dummy(&mut rng, None);
|
||||
|
||||
let sender_address = fvk.default_address();
|
||||
let sender_address = spent_note.recipient();
|
||||
let nk = *fvk.nk();
|
||||
let rivk = *fvk.rivk();
|
||||
let nf_old = spent_note.nullifier(&fvk);
|
||||
|
|
43
src/keys.rs
43
src/keys.rs
|
@ -345,11 +345,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<DiversifierIndex>) -> Address {
|
||||
IncomingViewingKey::from(self).address_at(j)
|
||||
|
@ -427,13 +422,7 @@ impl FullViewingKey {
|
|||
///
|
||||
/// [orchardkeycomponents]: https://zips.z.cash/protocol/nu5.pdf#orchardkeycomponents
|
||||
#[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
|
||||
}
|
||||
}
|
||||
pub(crate) struct DiversifierKey([u8; 32]);
|
||||
|
||||
/// The index for a particular diversifier.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
@ -460,12 +449,14 @@ impl From<[u8; 11]> for DiversifierIndex {
|
|||
}
|
||||
}
|
||||
|
||||
impl DiversifierKey {
|
||||
/// Returns the diversifier at index 0.
|
||||
pub fn default_diversifier(&self) -> Diversifier {
|
||||
self.get(0u32)
|
||||
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 the given index.
|
||||
pub fn get(&self, j: impl Into<DiversifierIndex>) -> Diversifier {
|
||||
let ff = FF1::<Aes256>::new(&self.0, 2).expect("valid radix");
|
||||
|
@ -579,7 +570,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(),
|
||||
}
|
||||
}
|
||||
|
@ -606,9 +597,16 @@ impl IncomingViewingKey {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns the default payment address for this key.
|
||||
pub fn default_address(&self) -> Address {
|
||||
self.address(self.dk.default_diversifier())
|
||||
/// 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<DiversifierIndex> {
|
||||
let j = self.dk.diversifier_index(&addr.diversifier());
|
||||
if &self.address_at(j) == addr {
|
||||
Some(j)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the payment address for this key at the given index.
|
||||
|
@ -860,7 +858,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)
|
||||
|
@ -913,9 +911,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());
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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().as_bytes());
|
||||
np[52..].copy_from_slice(memo);
|
||||
|
|
|
@ -147,6 +147,13 @@ impl VerificationKey<Binding> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: SigType> VerificationKey<T> {
|
||||
/// Verifies a purported `signature` over `msg` made by this verification key.
|
||||
pub fn verify(&self, msg: &[u8], signature: &Signature<T>) -> Result<(), reddsa::Error> {
|
||||
self.0.verify(msg, &signature.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// A RedPallas signature.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Signature<T: SigType>(reddsa::Signature<T>);
|
||||
|
|
12
src/tree.rs
12
src/tree.rs
|
@ -108,11 +108,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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
10
src/value.rs
10
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 {
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use incrementalmerkletree::{bridgetree::BridgeTree, Frontier, Hashable, Tree};
|
||||
use orchard::{
|
||||
builder::Builder,
|
||||
bundle::{Authorized, Flags},
|
||||
circuit::{ProvingKey, VerifyingKey},
|
||||
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<Authorized, i64>, 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, &mut rng).unwrap();
|
||||
proven.apply_signatures(&mut rng, sighash, &[]).unwrap()
|
||||
};
|
||||
|
||||
// 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::<MerkleHashOrchard, 32>::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, &mut rng).unwrap();
|
||||
proven
|
||||
.apply_signatures(&mut rng, sighash, &[SpendAuthorizingKey::from(&sk)])
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Verify the shielded bundle.
|
||||
verify_bundle(&shielded_bundle, &vk);
|
||||
}
|
Loading…
Reference in New Issue