From d77ed9c6cf021832a3c7ee7bc5eef0c8d0318a0c Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Thu, 15 Sep 2022 02:18:03 +0100 Subject: [PATCH 1/3] Change batch decryption benchmark to cover more cases of (nivks, noutputs). Signed-off-by: Daira Hopwood --- zcash_primitives/benches/note_decryption.rs | 58 +++++++++++---------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/zcash_primitives/benches/note_decryption.rs b/zcash_primitives/benches/note_decryption.rs index a48208dc9..4783a8f28 100644 --- a/zcash_primitives/benches/note_decryption.rs +++ b/zcash_primitives/benches/note_decryption.rs @@ -98,40 +98,42 @@ fn bench_note_decryption(c: &mut Criterion) { } { - let valid_ivks = vec![valid_ivk]; - let invalid_ivks = vec![invalid_ivk]; - - // We benchmark with one IVK so the overall batch size is equal to the number of - // outputs. - let size = 10; - let outputs: Vec<_> = iter::repeat(output) - .take(size) - .map(|output| (SaplingDomain::for_height(TEST_NETWORK, height), output)) - .collect(); - let mut group = c.benchmark_group("sapling-batch-note-decryption"); - group.throughput(Throughput::Elements(size as u64)); - group.bench_function(BenchmarkId::new("valid", size), |b| { - b.iter(|| batch::try_note_decryption(&valid_ivks, &outputs)) - }); + for (nivks, noutputs) in [(1, 10), (10, 1), (10, 10), (50, 50)] { + let invalid_ivks: Vec<_> = iter::repeat(invalid_ivk.clone()).take(nivks).collect(); + let valid_ivks: Vec<_> = iter::repeat(valid_ivk.clone()).take(nivks).collect(); - group.bench_function(BenchmarkId::new("invalid", size), |b| { - b.iter(|| batch::try_note_decryption(&invalid_ivks, &outputs)) - }); + let outputs: Vec<_> = iter::repeat(output.clone()) + .take(noutputs) + .map(|output| (SaplingDomain::for_height(TEST_NETWORK, height), output)) + .collect(); - let compact: Vec<_> = outputs - .into_iter() - .map(|(domain, output)| (domain, CompactOutputDescription::from(output))) - .collect(); + group.bench_function( + BenchmarkId::new(format!("valid-{}", nivks), noutputs), + |b| b.iter(|| batch::try_note_decryption(&valid_ivks, &outputs)), + ); - group.bench_function(BenchmarkId::new("compact-valid", size), |b| { - b.iter(|| batch::try_compact_note_decryption(&valid_ivks, &compact)) - }); + group.bench_function( + BenchmarkId::new(format!("invalid-{}", nivks), noutputs), + |b| b.iter(|| batch::try_note_decryption(&invalid_ivks, &outputs)), + ); - group.bench_function(BenchmarkId::new("compact-invalid", size), |b| { - b.iter(|| batch::try_compact_note_decryption(&invalid_ivks, &compact)) - }); + let compact: Vec<_> = outputs + .into_iter() + .map(|(domain, output)| (domain, CompactOutputDescription::from(output))) + .collect(); + + group.bench_function( + BenchmarkId::new(format!("compact-valid-{}", nivks), noutputs), + |b| b.iter(|| batch::try_compact_note_decryption(&valid_ivks, &compact)), + ); + + group.bench_function( + BenchmarkId::new(format!("compact-invalid-{}", nivks), noutputs), + |b| b.iter(|| batch::try_compact_note_decryption(&invalid_ivks, &compact)), + ); + } } } From 515b0a40ec06eb640fffa65b745166511bf56ecc Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Mon, 12 Sep 2022 22:52:54 +0100 Subject: [PATCH 2/3] zcash_note_encryption: Add API to prepare epk. Co-authored-by: Jack Grigg Signed-off-by: Daira Hopwood --- Cargo.toml | 1 + components/zcash_note_encryption/CHANGELOG.md | 13 ++++++++++--- components/zcash_note_encryption/Cargo.toml | 1 + components/zcash_note_encryption/src/batch.rs | 2 +- components/zcash_note_encryption/src/lib.rs | 19 ++++++++++++++----- 5 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0ddd568e4..0f2939d34 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,3 +21,4 @@ codegen-units = 1 [patch.crates-io] zcash_encoding = { path = "components/zcash_encoding" } zcash_note_encryption = { path = "components/zcash_note_encryption" } +group = { git = "https://github.com/zkcrypto/group.git", rev = "a7f3ceb2373e9fe536996f7b4d55c797f3e667f0" } diff --git a/components/zcash_note_encryption/CHANGELOG.md b/components/zcash_note_encryption/CHANGELOG.md index e3b485e95..4db12f98e 100644 --- a/components/zcash_note_encryption/CHANGELOG.md +++ b/components/zcash_note_encryption/CHANGELOG.md @@ -6,7 +6,17 @@ and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added +- `zcash_note_encryption::Domain`: + - `Domain::PreparedEphemeralPublicKey` associated type. + - `Domain::prepare_epk` method, which produces the above type. +### Changed +- MSRV is now 1.56.1. +- Migrated to `group 0.13`. +- `zcash_note_encryption::Domain` now requires `epk` to be converted to + `Domain::PreparedEphemeralPublicKey` before being passed to + `Domain::ka_agree_dec`. - Changes to batch decryption APIs: - The return types of `batch::try_note_decryption` and `batch::try_compact_note_decryption` have changed. Now, instead of @@ -16,8 +26,5 @@ and this library adheres to Rust's notion of argument to the function. Each successful result includes the index of the entry in `ivks` used to decrypt the value. -### Changed -- MSRV is now 1.56.1. - ## [0.1.0] - 2021-12-17 Initial release. diff --git a/components/zcash_note_encryption/Cargo.toml b/components/zcash_note_encryption/Cargo.toml index de440572c..413522fcb 100644 --- a/components/zcash_note_encryption/Cargo.toml +++ b/components/zcash_note_encryption/Cargo.toml @@ -21,6 +21,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] chacha20 = { version = "0.8", default-features = false } chacha20poly1305 = { version = "0.9", default-features = false } +group = "0.12" rand_core = { version = "0.6", default-features = false } subtle = { version = "2.2.3", default-features = false } diff --git a/components/zcash_note_encryption/src/batch.rs b/components/zcash_note_encryption/src/batch.rs index 9742277a7..ad704167c 100644 --- a/components/zcash_note_encryption/src/batch.rs +++ b/components/zcash_note_encryption/src/batch.rs @@ -51,7 +51,7 @@ where return (0..outputs.len()).map(|_| None).collect(); }; - // Fetch the ephemeral keys for each output and batch-parse them. + // Fetch the ephemeral keys for each output, and batch-parse and prepare them. let ephemeral_keys = D::batch_epk(outputs.iter().map(|(_, output)| output.ephemeral_key())); // Derive the shared secrets for all combinations of (ivk, output). diff --git a/components/zcash_note_encryption/src/lib.rs b/components/zcash_note_encryption/src/lib.rs index 9627d05b2..b16146bb1 100644 --- a/components/zcash_note_encryption/src/lib.rs +++ b/components/zcash_note_encryption/src/lib.rs @@ -113,6 +113,7 @@ enum NoteValidity { pub trait Domain { type EphemeralSecretKey: ConstantTimeEq; type EphemeralPublicKey; + type PreparedEphemeralPublicKey; type SharedSecret; type SymmetricKey: AsRef<[u8]>; type Note; @@ -136,6 +137,9 @@ pub trait Domain { /// Extracts the `DiversifiedTransmissionKey` from the note. fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey; + /// Prepare an ephemeral public key for more efficient scalar multiplication. + fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey; + /// Derives `EphemeralPublicKey` from `esk` and the note's diversifier. fn ka_derive_public( note: &Self::Note, @@ -152,7 +156,7 @@ pub trait Domain { /// decryption. fn ka_agree_dec( ivk: &Self::IncomingViewingKey, - epk: &Self::EphemeralPublicKey, + epk: &Self::PreparedEphemeralPublicKey, ) -> Self::SharedSecret; /// Derives the `SymmetricKey` used to encrypt the note plaintext. @@ -306,10 +310,15 @@ pub trait BatchDomain: Domain { /// them. fn batch_epk( ephemeral_keys: impl Iterator, - ) -> Vec<(Option, EphemeralKeyBytes)> { + ) -> Vec<(Option, EphemeralKeyBytes)> { // Default implementation: do the non-batched thing. ephemeral_keys - .map(|ephemeral_key| (Self::epk(&ephemeral_key), ephemeral_key)) + .map(|ephemeral_key| { + ( + Self::epk(&ephemeral_key).map(Self::prepare_epk), + ephemeral_key, + ) + }) .collect() } } @@ -514,7 +523,7 @@ pub fn try_note_decryption Option<(D::Note, D::Recipient, D::Memo)> { let ephemeral_key = output.ephemeral_key(); - let epk = D::epk(&ephemeral_key)?; + let epk = D::prepare_epk(D::epk(&ephemeral_key)?); let shared_secret = D::ka_agree_dec(ivk, &epk); let key = D::kdf(shared_secret, &ephemeral_key); @@ -611,7 +620,7 @@ pub fn try_compact_note_decryption Option<(D::Note, D::Recipient)> { let ephemeral_key = output.ephemeral_key(); - let epk = D::epk(&ephemeral_key)?; + let epk = D::prepare_epk(D::epk(&ephemeral_key)?); let shared_secret = D::ka_agree_dec(ivk, &epk); let key = D::kdf(shared_secret, &ephemeral_key); From 20e869f501d46dd363ae63017d196ebc97ddc879 Mon Sep 17 00:00:00 2001 From: Daira Hopwood Date: Mon, 12 Sep 2022 22:54:09 +0100 Subject: [PATCH 3/3] zcash_primitives: Use prepared epk and ivk in Sapling note decryption Co-authored-by: Jack Grigg Signed-off-by: Daira Hopwood --- Cargo.toml | 1 + zcash_client_backend/src/data_api/chain.rs | 17 ++-- zcash_client_backend/src/decrypt.rs | 6 +- zcash_client_backend/src/welding_rig.rs | 16 ++-- zcash_primitives/CHANGELOG.md | 13 ++++ zcash_primitives/benches/note_decryption.rs | 5 +- .../src/sapling/note_encryption.rs | 77 ++++++++++++++----- 7 files changed, 99 insertions(+), 36 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0f2939d34..6ce430d9e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,5 @@ codegen-units = 1 [patch.crates-io] zcash_encoding = { path = "components/zcash_encoding" } zcash_note_encryption = { path = "components/zcash_note_encryption" } +orchard = { git = "https://github.com/zcash/orchard.git", rev = "33f1c1141e50adb68715f3359bd75378b4756cca" } group = { git = "https://github.com/zkcrypto/group.git", rev = "a7f3ceb2373e9fe536996f7b4d55c797f3e667f0" } diff --git a/zcash_client_backend/src/data_api/chain.rs b/zcash_client_backend/src/data_api/chain.rs index 7f6822876..784c949f5 100644 --- a/zcash_client_backend/src/data_api/chain.rs +++ b/zcash_client_backend/src/data_api/chain.rs @@ -85,7 +85,7 @@ use zcash_primitives::{ block::BlockHash, consensus::{self, BlockHeight, NetworkUpgrade}, merkle_tree::CommitmentTree, - sapling::{keys::Scope, Nullifier}, + sapling::{keys::Scope, note_encryption::PreparedIncomingViewingKey, Nullifier}, }; use crate::{ @@ -234,12 +234,15 @@ where let mut batch_runner = BatchRunner::new( 100, - dfvks.iter().flat_map(|(account, dfvk)| { - [ - ((**account, Scope::External), dfvk.to_ivk(Scope::External)), - ((**account, Scope::Internal), dfvk.to_ivk(Scope::Internal)), - ] - }), + dfvks + .iter() + .flat_map(|(account, dfvk)| { + [ + ((**account, Scope::External), dfvk.to_ivk(Scope::External)), + ((**account, Scope::Internal), dfvk.to_ivk(Scope::Internal)), + ] + }) + .map(|(tag, ivk)| (tag, PreparedIncomingViewingKey::new(&ivk))), ); cache.with_blocks(last_height, limit, |block: CompactBlock| { diff --git a/zcash_client_backend/src/decrypt.rs b/zcash_client_backend/src/decrypt.rs index 29a666c83..64086c166 100644 --- a/zcash_client_backend/src/decrypt.rs +++ b/zcash_client_backend/src/decrypt.rs @@ -4,7 +4,9 @@ use zcash_primitives::{ consensus::{self, BlockHeight}, memo::MemoBytes, sapling::{ - note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery}, + note_encryption::{ + try_sapling_note_decryption, try_sapling_output_recovery, PreparedIncomingViewingKey, + }, Note, PaymentAddress, }, transaction::Transaction, @@ -47,7 +49,7 @@ pub fn decrypt_transaction( if let Some(bundle) = tx.sapling_bundle() { for (account, ufvk) in ufvks.iter() { if let Some(dfvk) = ufvk.sapling() { - let ivk = dfvk.fvk().vk.ivk(); + let ivk = PreparedIncomingViewingKey::new(&dfvk.fvk().vk.ivk()); let ovk = dfvk.fvk().ovk; for (index, output) in bundle.shielded_outputs.iter().enumerate() { diff --git a/zcash_client_backend/src/welding_rig.rs b/zcash_client_backend/src/welding_rig.rs index f514cd414..d31ef959c 100644 --- a/zcash_client_backend/src/welding_rig.rs +++ b/zcash_client_backend/src/welding_rig.rs @@ -11,7 +11,7 @@ use zcash_primitives::{ sapling::{ self, keys::{DiversifiableFullViewingKey, Scope}, - note_encryption::SaplingDomain, + note_encryption::{PreparedIncomingViewingKey, SaplingDomain}, Node, Note, Nullifier, NullifierDerivingKey, SaplingIvk, }, transaction::components::sapling::CompactOutputDescription, @@ -325,7 +325,8 @@ pub(crate) fn scan_block_with_runner>(); batch::try_compact_note_decryption(&ivks, decoded) @@ -413,8 +414,9 @@ mod tests { memo::MemoBytes, merkle_tree::CommitmentTree, sapling::{ - note_encryption::sapling_note_encryption, util::generate_random_rseed, Note, Nullifier, - SaplingIvk, + note_encryption::{sapling_note_encryption, PreparedIncomingViewingKey}, + util::generate_random_rseed, + Note, Nullifier, SaplingIvk, }, transaction::components::Amount, zip32::{AccountId, ExtendedFullViewingKey, ExtendedSpendingKey}, @@ -557,7 +559,8 @@ mod tests { extfvk .to_sapling_keys() .iter() - .map(|(scope, ivk, _)| ((account, *scope), ivk.clone())), + .map(|(scope, ivk, _)| ((account, *scope), ivk)) + .map(|(tag, ivk)| (tag, PreparedIncomingViewingKey::new(ivk))), ); add_block_to_runner(&Network::TestNetwork, cb.clone(), &mut runner); @@ -620,7 +623,8 @@ mod tests { extfvk .to_sapling_keys() .iter() - .map(|(scope, ivk, _)| ((account, *scope), ivk.clone())), + .map(|(scope, ivk, _)| ((account, *scope), ivk)) + .map(|(tag, ivk)| (tag, PreparedIncomingViewingKey::new(ivk))), ); add_block_to_runner(&Network::TestNetwork, cb.clone(), &mut runner); diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 43bf96ae7..1484b8daf 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -17,6 +17,9 @@ and this library adheres to Rust's notion of - `Scope` - `ExpandedSpendingKey::from_bytes` - `ExtendedSpendingKey::{from_bytes, to_bytes}` +- `zcash_primitives::sapling::note_encryption`: + - `PreparedIncomingViewingKey` + - `PreparedEphemeralPublicKey` - Added in `zcash_primitives::zip32` - `ChainCode::as_bytes` - `DiversifierKey::{from_bytes, as_bytes}` @@ -35,6 +38,7 @@ and this library adheres to Rust's notion of - `Bundle::value_balance` ### Changed +- Migrated to `group 0.13`. - `zcash_primitives::sapling::ViewingKey` now stores `nk` as a `NullifierDerivingKey` instead of as a bare `jubjub::SubgroupPoint`. - The signature of `zcash_primitives::sapling::Note::nf` has changed to @@ -42,6 +46,15 @@ and this library adheres to Rust's notion of rather than the full `ViewingKey` as its first argument. - Made the internals of `zip32::DiversifierKey` private; use `from_bytes` and `as_bytes` on this type instead. +- `zcash_primitives::sapling::note_encryption` APIs now expose precomputations + explicitly (where previously they were performed internally), to enable users + to avoid recomputing incoming viewing key precomputations. Users now need to + call `PreparedIncomingViewingKey::new` to convert their `SaplingIvk`s into + their precomputed forms, and can do so wherever it makes sense in their stack. + - `SaplingDomain::IncomingViewingKey` is now `PreparedIncomingViewingKey` + instead of `SaplingIvk`. + - `try_sapling_note_decryption` and `try_sapling_compact_note_decryption` now + take `&PreparedIncomingViewingKey` instead of `&SaplingIvk`. ## [0.7.0] - 2022-06-24 ### Changed diff --git a/zcash_primitives/benches/note_decryption.rs b/zcash_primitives/benches/note_decryption.rs index 4783a8f28..0e8e5a84b 100644 --- a/zcash_primitives/benches/note_decryption.rs +++ b/zcash_primitives/benches/note_decryption.rs @@ -11,7 +11,7 @@ use zcash_primitives::{ sapling::{ note_encryption::{ sapling_note_encryption, try_sapling_compact_note_decryption, - try_sapling_note_decryption, SaplingDomain, + try_sapling_note_decryption, PreparedIncomingViewingKey, SaplingDomain, }, util::generate_random_rseed, Diversifier, PaymentAddress, SaplingIvk, ValueCommitment, @@ -67,6 +67,9 @@ fn bench_note_decryption(c: &mut Criterion) { } }; + let valid_ivk = PreparedIncomingViewingKey::new(&valid_ivk); + let invalid_ivk = PreparedIncomingViewingKey::new(&invalid_ivk); + { let mut group = c.benchmark_group("sapling-note-decryption"); group.throughput(Throughput::Elements(1)); diff --git a/zcash_primitives/src/sapling/note_encryption.rs b/zcash_primitives/src/sapling/note_encryption.rs index fcb2064f2..27eac5262 100644 --- a/zcash_primitives/src/sapling/note_encryption.rs +++ b/zcash_primitives/src/sapling/note_encryption.rs @@ -2,7 +2,7 @@ use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams}; use byteorder::{LittleEndian, WriteBytesExt}; use ff::PrimeField; -use group::{cofactor::CofactorGroup, GroupEncoding}; +use group::{cofactor::CofactorGroup, GroupEncoding, WnafBase, WnafScalar}; use jubjub::{AffinePoint, ExtendedPoint}; use rand_core::RngCore; @@ -27,16 +27,39 @@ use crate::{ pub const KDF_SAPLING_PERSONALIZATION: &[u8; 16] = b"Zcash_SaplingKDF"; pub const PRF_OCK_PERSONALIZATION: &[u8; 16] = b"Zcash_Derive_ock"; +const PREPARED_WINDOW_SIZE: usize = 4; +type PreparedBase = WnafBase; +type PreparedBaseSubgroup = WnafBase; +type PreparedScalar = WnafScalar; + +/// A Sapling incoming viewing key that has been precomputed for trial decryption. +#[derive(Clone, Debug)] +pub struct PreparedIncomingViewingKey(PreparedScalar); + +impl PreparedIncomingViewingKey { + /// Performs the necessary precomputations to use a `SaplingIvk` for note decryption. + pub fn new(ivk: &SaplingIvk) -> Self { + Self(PreparedScalar::new(&ivk.0)) + } +} + +/// A Sapling ephemeral public key that has been precomputed for trial decryption. +#[derive(Clone, Debug)] +pub struct PreparedEphemeralPublicKey(PreparedBase); + /// Sapling key agreement for note encryption. /// /// Implements section 5.4.4.3 of the Zcash Protocol Specification. pub fn sapling_ka_agree(esk: &jubjub::Fr, pk_d: &jubjub::ExtendedPoint) -> jubjub::SubgroupPoint { + sapling_ka_agree_prepared(&PreparedScalar::new(esk), &PreparedBase::new(*pk_d)) +} + +fn sapling_ka_agree_prepared(esk: &PreparedScalar, pk_d: &PreparedBase) -> jubjub::SubgroupPoint { // [8 esk] pk_d // ::clear_cofactor is implemented using // ExtendedPoint::mul_by_cofactor in the jubjub crate. - let mut wnaf = group::Wnaf::new(); - wnaf.scalar(esk).base(*pk_d).clear_cofactor() + (pk_d * esk).clear_cofactor() } /// Sapling KDF for note encryption. @@ -132,12 +155,13 @@ impl Domain for SaplingDomain

{ // points must not be small-order, and all points with non-canonical serialization // are small-order. type EphemeralPublicKey = jubjub::ExtendedPoint; + type PreparedEphemeralPublicKey = PreparedEphemeralPublicKey; type SharedSecret = jubjub::SubgroupPoint; type SymmetricKey = Blake2bHash; type Note = Note; type Recipient = PaymentAddress; type DiversifiedTransmissionKey = jubjub::SubgroupPoint; - type IncomingViewingKey = SaplingIvk; + type IncomingViewingKey = PreparedIncomingViewingKey; type OutgoingViewingKey = OutgoingViewingKey; type ValueCommitment = jubjub::ExtendedPoint; type ExtractedCommitment = bls12_381::Scalar; @@ -152,6 +176,10 @@ impl Domain for SaplingDomain

{ note.pk_d } + fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { + PreparedEphemeralPublicKey(PreparedBase::new(epk)) + } + fn ka_derive_public( note: &Self::Note, esk: &Self::EphemeralSecretKey, @@ -173,9 +201,9 @@ impl Domain for SaplingDomain

{ fn ka_agree_dec( ivk: &Self::IncomingViewingKey, - epk: &Self::EphemeralPublicKey, + epk: &Self::PreparedEphemeralPublicKey, ) -> Self::SharedSecret { - sapling_ka_agree(&ivk.0, epk) + sapling_ka_agree_prepared(&ivk.0, &epk.0) } /// Sapling KDF for note encryption. @@ -253,7 +281,7 @@ impl Domain for SaplingDomain

{ plaintext: &[u8], ) -> Option<(Self::Note, Self::Recipient)> { sapling_parse_note_plaintext_without_memo(self, plaintext, |diversifier| { - Some(diversifier.g_d()? * ivk.0) + Some(&PreparedBaseSubgroup::new(diversifier.g_d()?) * &ivk.0) }) } @@ -332,13 +360,18 @@ impl BatchDomain for SaplingDomain

{ fn batch_epk( ephemeral_keys: impl Iterator, - ) -> Vec<(Option, EphemeralKeyBytes)> { + ) -> Vec<(Option, EphemeralKeyBytes)> { let ephemeral_keys: Vec<_> = ephemeral_keys.collect(); let epks = jubjub::AffinePoint::batch_from_bytes(ephemeral_keys.iter().map(|b| b.0)); epks.into_iter() .zip(ephemeral_keys.into_iter()) .map(|(epk, ephemeral_key)| { - (epk.map(jubjub::ExtendedPoint::from).into(), ephemeral_key) + ( + epk.map(jubjub::ExtendedPoint::from) + .map(Self::prepare_epk) + .into(), + ephemeral_key, + ) }) .collect() } @@ -391,7 +424,7 @@ pub fn try_sapling_note_decryption< >( params: &P, height: BlockHeight, - ivk: &SaplingIvk, + ivk: &PreparedIncomingViewingKey, output: &Output, ) -> Option<(Note, PaymentAddress, MemoBytes)> { let domain = SaplingDomain { @@ -407,7 +440,7 @@ pub fn try_sapling_compact_note_decryption< >( params: &P, height: BlockHeight, - ivk: &SaplingIvk, + ivk: &PreparedIncomingViewingKey, output: &Output, ) -> Option<(Note, PaymentAddress)> { let domain = SaplingDomain { @@ -493,7 +526,7 @@ mod tests { }, keys::OutgoingViewingKey, memo::MemoBytes, - sapling::util::generate_random_rseed, + sapling::{note_encryption::PreparedIncomingViewingKey, util::generate_random_rseed}, sapling::{Diversifier, PaymentAddress, Rseed, SaplingIvk, ValueCommitment}, transaction::components::{ amount::Amount, @@ -508,18 +541,21 @@ mod tests { ) -> ( OutgoingViewingKey, OutgoingCipherKey, - SaplingIvk, + PreparedIncomingViewingKey, OutputDescription, ) { let ivk = SaplingIvk(jubjub::Fr::random(&mut rng)); + let prepared_ivk = PreparedIncomingViewingKey::new(&ivk); let (ovk, ock, output) = random_enc_ciphertext_with(height, &ivk, rng); - assert!(try_sapling_note_decryption(&TEST_NETWORK, height, &ivk, &output).is_some()); + assert!( + try_sapling_note_decryption(&TEST_NETWORK, height, &prepared_ivk, &output).is_some() + ); assert!(try_sapling_compact_note_decryption( &TEST_NETWORK, height, - &ivk, + &prepared_ivk, &CompactOutputDescription::from(output.clone()), ) .is_some()); @@ -532,7 +568,7 @@ mod tests { assert!(ock_output_recovery.is_some()); assert_eq!(ovk_output_recovery, ock_output_recovery); - (ovk, ock, ivk, output) + (ovk, ock, prepared_ivk, output) } fn random_enc_ciphertext_with( @@ -685,7 +721,7 @@ mod tests { try_sapling_note_decryption( &TEST_NETWORK, height, - &SaplingIvk(jubjub::Fr::random(&mut rng)), + &PreparedIncomingViewingKey::new(&SaplingIvk(jubjub::Fr::random(&mut rng))), &output ), None @@ -851,7 +887,7 @@ mod tests { try_sapling_compact_note_decryption( &TEST_NETWORK, height, - &SaplingIvk(jubjub::Fr::random(&mut rng)), + &PreparedIncomingViewingKey::new(&SaplingIvk(jubjub::Fr::random(&mut rng))), &CompactOutputDescription::from(output) ), None @@ -1309,7 +1345,7 @@ mod tests { // Load the test vector components // - let ivk = SaplingIvk(read_jubjub_scalar!(tv.ivk)); + let ivk = PreparedIncomingViewingKey::new(&SaplingIvk(read_jubjub_scalar!(tv.ivk))); let pk_d = read_point!(tv.default_pk_d).into_subgroup().unwrap(); let rcm = read_jubjub_scalar!(tv.rcm); let cv = read_point!(tv.cv); @@ -1439,7 +1475,7 @@ mod tests { let height = TEST_NETWORK.activation_height(Canopy).unwrap(); // Test batch trial-decryption with multiple IVKs and outputs. - let invalid_ivk = SaplingIvk(jubjub::Fr::random(rng)); + let invalid_ivk = PreparedIncomingViewingKey::new(&SaplingIvk(jubjub::Fr::random(rng))); let valid_ivk = SaplingIvk(jubjub::Fr::random(rng)); let outputs: Vec<_> = (0..10) .map(|_| { @@ -1449,6 +1485,7 @@ mod tests { ) }) .collect(); + let valid_ivk = PreparedIncomingViewingKey::new(&valid_ivk); // Check that batched trial decryptions with invalid_ivk fails. let res = batch::try_note_decryption(&[invalid_ivk.clone()], &outputs);