Add sapling::NullifierDerivingKey newtype.
Nullifier computation only requires the nullifier deriving key, not the entire Sapling viewing key. This separation of concerns will be needed for batch decryption when wallet-internal keys will need to be considered.
This commit is contained in:
parent
878a428c3c
commit
f1c2da7b1d
|
@ -127,7 +127,7 @@ impl ScanningKey for DiversifiableFullViewingKey {
|
|||
}
|
||||
|
||||
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf {
|
||||
note.nf(&self.fvk().vk, witness.position() as u64)
|
||||
note.nf(&self.fvk().vk.nk, witness.position() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +151,7 @@ impl ScanningKey for ExtendedFullViewingKey {
|
|||
}
|
||||
|
||||
fn nf(&self, note: &Note, witness: &IncrementalWitness<Node>) -> Self::Nf {
|
||||
note.nf(&self.fvk.vk, witness.position() as u64)
|
||||
note.nf(&self.fvk.vk.nk, witness.position() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -862,7 +862,7 @@ mod tests {
|
|||
rng.fill_bytes(&mut cb.hash);
|
||||
cb.prevHash.extend_from_slice(&prev_hash.0);
|
||||
cb.vtx.push(ctx);
|
||||
(cb, note.nf(&dfvk.fvk().vk, 0))
|
||||
(cb, note.nf(&dfvk.fvk().vk.nk, 0))
|
||||
}
|
||||
|
||||
/// Create a fake CompactBlock at the given height, spending a single note from the
|
||||
|
|
|
@ -9,6 +9,14 @@ and this library adheres to Rust's notion of
|
|||
### Added
|
||||
- `zcash_primitives::sapling::keys::DiversifiableFullViewingKey`
|
||||
- `zcash_primitives::sapling::keys::Scope`
|
||||
- `zcash_primitives::sapling::NullifierDerivingKey`
|
||||
|
||||
### Changed
|
||||
- `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
|
||||
take just a `NullifierDerivingKey` (the only capability it actually required)
|
||||
rather than the full `ViewingKey` as its first argument.
|
||||
|
||||
## [0.7.0] - 2022-06-24
|
||||
### Changed
|
||||
|
|
|
@ -185,15 +185,19 @@ impl ProofGenerationKey {
|
|||
pub fn to_viewing_key(&self) -> ViewingKey {
|
||||
ViewingKey {
|
||||
ak: self.ak,
|
||||
nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk,
|
||||
nk: NullifierDerivingKey(constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A key used to derive the nullifier for a Sapling note.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct NullifierDerivingKey(pub(crate) jubjub::SubgroupPoint);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ViewingKey {
|
||||
pub ak: jubjub::SubgroupPoint,
|
||||
pub nk: jubjub::SubgroupPoint,
|
||||
pub nk: NullifierDerivingKey,
|
||||
}
|
||||
|
||||
impl ViewingKey {
|
||||
|
@ -209,7 +213,7 @@ impl ViewingKey {
|
|||
.personal(constants::CRH_IVK_PERSONALIZATION)
|
||||
.to_state()
|
||||
.update(&self.ak.to_bytes())
|
||||
.update(&self.nk.to_bytes())
|
||||
.update(&self.nk.0.to_bytes())
|
||||
.finalize()
|
||||
.as_bytes(),
|
||||
);
|
||||
|
@ -457,9 +461,9 @@ impl Note {
|
|||
(constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
|
||||
}
|
||||
|
||||
/// Computes the nullifier given the viewing key and
|
||||
/// Computes the nullifier given the nullifier deriving key and
|
||||
/// note position
|
||||
pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Nullifier {
|
||||
pub fn nf(&self, nk: &NullifierDerivingKey, position: u64) -> Nullifier {
|
||||
// Compute rho = cm + position.G
|
||||
let rho = self.cm_full_point()
|
||||
+ (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position));
|
||||
|
@ -470,7 +474,7 @@ impl Note {
|
|||
.hash_length(32)
|
||||
.personal(constants::PRF_NF_PERSONALIZATION)
|
||||
.to_state()
|
||||
.update(&viewing_key.nk.to_bytes())
|
||||
.update(&nk.0.to_bytes())
|
||||
.update(&rho.to_bytes())
|
||||
.finalize()
|
||||
.as_bytes(),
|
||||
|
|
|
@ -16,7 +16,7 @@ use ff::PrimeField;
|
|||
use group::{Group, GroupEncoding};
|
||||
use subtle::CtOption;
|
||||
|
||||
use super::{PaymentAddress, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||
use super::{NullifierDerivingKey, PaymentAddress, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||
|
||||
/// A Sapling expanded spending key
|
||||
#[derive(Clone)]
|
||||
|
@ -104,7 +104,7 @@ impl FullViewingKey {
|
|||
FullViewingKey {
|
||||
vk: ViewingKey {
|
||||
ak: SPENDING_KEY_GENERATOR * expsk.ask,
|
||||
nk: PROOF_GENERATION_KEY_GENERATOR * expsk.nsk,
|
||||
nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
|
||||
},
|
||||
ovk: expsk.ovk,
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ impl FullViewingKey {
|
|||
));
|
||||
}
|
||||
let ak = ak.unwrap();
|
||||
let nk = nk.unwrap();
|
||||
let nk = NullifierDerivingKey(nk.unwrap());
|
||||
|
||||
let mut ovk = [0u8; 32];
|
||||
reader.read_exact(&mut ovk)?;
|
||||
|
@ -147,7 +147,7 @@ impl FullViewingKey {
|
|||
|
||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.vk.ak.to_bytes())?;
|
||||
writer.write_all(&self.vk.nk.to_bytes())?;
|
||||
writer.write_all(&self.vk.nk.0.to_bytes())?;
|
||||
writer.write_all(&self.ovk.0)?;
|
||||
|
||||
Ok(())
|
||||
|
@ -232,11 +232,21 @@ impl DiversifiableFullViewingKey {
|
|||
Self { fvk, dk }
|
||||
}
|
||||
|
||||
/// Exposes the [`FullViewingKey`] component of this diversifiable full viewing key.
|
||||
/// Exposes the external [`FullViewingKey`] component of this diversifiable full viewing key.
|
||||
pub fn fvk(&self) -> &FullViewingKey {
|
||||
&self.fvk
|
||||
}
|
||||
|
||||
/// Derives a nullifier-deriving key for the provided scope.
|
||||
///
|
||||
/// This API is provided so that nullifiers for change notes can be correctly computed.
|
||||
pub fn to_nk(&self, scope: Scope) -> NullifierDerivingKey {
|
||||
match scope {
|
||||
Scope::External => self.fvk.vk.nk,
|
||||
Scope::Internal => self.derive_internal().fvk.vk.nk,
|
||||
}
|
||||
}
|
||||
|
||||
/// Derives an incoming viewing key corresponding to this full viewing key.
|
||||
pub fn to_ivk(&self, scope: Scope) -> SaplingIvk {
|
||||
match scope {
|
||||
|
|
|
@ -356,7 +356,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
let proof_generation_key = spend.extsk.expsk.proof_generation_key();
|
||||
|
||||
let nullifier = spend.note.nf(
|
||||
&proof_generation_key.to_viewing_key(),
|
||||
&proof_generation_key.to_viewing_key().nk,
|
||||
spend.merkle_path.position,
|
||||
);
|
||||
|
||||
|
|
|
@ -18,7 +18,10 @@ use std::io::{self, Read, Write};
|
|||
|
||||
use crate::{
|
||||
keys::{prf_expand, prf_expand_vec, OutgoingViewingKey},
|
||||
sapling::keys::{ExpandedSpendingKey, FullViewingKey},
|
||||
sapling::{
|
||||
keys::{ExpandedSpendingKey, FullViewingKey},
|
||||
NullifierDerivingKey,
|
||||
},
|
||||
};
|
||||
|
||||
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &[u8; 16] = b"ZcashIP32Sapling";
|
||||
|
@ -281,7 +284,7 @@ pub fn sapling_derive_internal_fvk(
|
|||
let r = prf_expand(i.as_bytes(), &[0x18]);
|
||||
let r = r.as_bytes();
|
||||
// PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
|
||||
let nk_internal = PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk;
|
||||
let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk.0);
|
||||
let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
|
||||
let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());
|
||||
|
||||
|
@ -583,7 +586,9 @@ impl ExtendedFullViewingKey {
|
|||
let i_ask = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x13]).as_array());
|
||||
let i_nsk = jubjub::Fr::from_bytes_wide(prf_expand(i_l, &[0x14]).as_array());
|
||||
let ak = (SPENDING_KEY_GENERATOR * i_ask) + self.fvk.vk.ak;
|
||||
let nk = (PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk;
|
||||
let nk = NullifierDerivingKey(
|
||||
(PROOF_GENERATION_KEY_GENERATOR * i_nsk) + self.fvk.vk.nk.0,
|
||||
);
|
||||
|
||||
FullViewingKey {
|
||||
vk: ViewingKey { ak, nk },
|
||||
|
@ -1545,7 +1550,7 @@ mod tests {
|
|||
|
||||
for (xfvk, tv) in xfvks.iter().zip(test_vectors.iter()) {
|
||||
assert_eq!(xfvk.fvk.vk.ak.to_bytes(), tv.ak);
|
||||
assert_eq!(xfvk.fvk.vk.nk.to_bytes(), tv.nk);
|
||||
assert_eq!(xfvk.fvk.vk.nk.0.to_bytes(), tv.nk);
|
||||
|
||||
assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
|
||||
assert_eq!(xfvk.dk.0, tv.dk);
|
||||
|
@ -1589,7 +1594,7 @@ mod tests {
|
|||
|
||||
let internal_xfvk = xfvk.derive_internal();
|
||||
assert_eq!(internal_xfvk.fvk.vk.ak.to_bytes(), tv.ak);
|
||||
assert_eq!(internal_xfvk.fvk.vk.nk.to_bytes(), tv.internal_nk);
|
||||
assert_eq!(internal_xfvk.fvk.vk.nk.0.to_bytes(), tv.internal_nk);
|
||||
|
||||
assert_eq!(internal_xfvk.fvk.ovk.0, tv.internal_ovk);
|
||||
assert_eq!(internal_xfvk.dk.0, tv.internal_dk);
|
||||
|
|
|
@ -611,7 +611,7 @@ fn test_input_circuit_with_bls12_381() {
|
|||
}
|
||||
}
|
||||
|
||||
let expected_nf = note.nf(&viewing_key, position);
|
||||
let expected_nf = note.nf(&viewing_key.nk, position);
|
||||
let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
|
||||
let expected_nf = multipack::compute_multipacking(&expected_nf);
|
||||
assert_eq!(expected_nf.len(), 2);
|
||||
|
@ -789,7 +789,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
|
|||
}
|
||||
}
|
||||
|
||||
let expected_nf = note.nf(&viewing_key, position);
|
||||
let expected_nf = note.nf(&viewing_key.nk, position);
|
||||
let expected_nf = multipack::bytes_to_bits_le(&expected_nf.0);
|
||||
let expected_nf = multipack::compute_multipacking(&expected_nf);
|
||||
assert_eq!(expected_nf.len(), 2);
|
||||
|
|
|
@ -96,7 +96,7 @@ impl SaplingProvingContext {
|
|||
rseed,
|
||||
};
|
||||
|
||||
let nullifier = note.nf(&viewing_key, merkle_path.position);
|
||||
let nullifier = note.nf(&viewing_key.nk, merkle_path.position);
|
||||
|
||||
// We now have the full witness for our circuit
|
||||
let instance = Spend {
|
||||
|
|
Loading…
Reference in New Issue