Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Feb 12, 2024
1 parent 8b23787 commit c75dd13
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 71 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ and this library adheres to Rust's notion of
- `ExpandedSpendingKey.nsk` now has type `ProofAuthorizingKey`.
- `ProofGenerationKey.nsk` now has type `ProofAuthorizingKey`.

### Removed
- `sapling_crypto::keys`:
- `ViewingKey` (use `FullViewingKey` instead).

## [0.1.0] - 2024-01-26
The crate has been completely rewritten. See [`zcash/librustzcash`] for the
history of this rewrite.
Expand Down
92 changes: 48 additions & 44 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,56 +391,38 @@ impl ProofGenerationKey {
}
}

#[derive(Debug, Clone)]
pub struct ViewingKey {
pub ak: SpendValidatingKey,
pub nk: NullifierDerivingKey,
}

impl ViewingKey {
pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
self.ak.randomize(&ar)
}

pub fn ivk(&self) -> SaplingIvk {
SaplingIvk(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
}

pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
self.ivk().to_payment_address(diversifier)
}
}

/// A Sapling key that provides the capability to view incoming and outgoing transactions.
#[derive(Debug)]
/// A key that provides the capability to view incoming and outgoing transactions.
///
/// Modern Zcash wallets use multiple viewing keys scoped to external and internal
/// operations. You should consider using [`DiversifiableFullViewingKey`] instead, which
/// handles these details and ensures that you have a full and consistent view of wallet
/// activity.
///
/// Defined in [Zcash Protocol Spec § 3.1: Payment Addresses and Keys][addressesandkeys].
///
/// [`DiversifiableFullViewingKey`]: crate::zip32::DiversifiableFullViewingKey
/// [addressesandkeys]: https://zips.z.cash/protocol/protocol.pdf#addressesandkeys
// TODO: Rename to `ScopedFullViewingKey` or `ScopedViewingKey` for API clarity.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FullViewingKey {
pub vk: ViewingKey,
pub ovk: OutgoingViewingKey,
}

impl Clone for FullViewingKey {
fn clone(&self) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: self.vk.ak.clone(),
nk: self.vk.nk,
},
ovk: self.ovk,
}
}
ak: SpendValidatingKey,
nk: NullifierDerivingKey,
ovk: OutgoingViewingKey,
}

impl FullViewingKey {
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: (&expsk.ask).into(),
nk: (&expsk.nsk).into(),
},
ak: (&expsk.ask).into(),
nk: (&expsk.nsk).into(),
ovk: expsk.ovk,
}
}

/// Parses a full viewing key from its "raw" encoding as specified in
/// [Zcash Protocol Spec § 5.6.3.3: Sapling Full Viewing Keys][saplingfullviewingkeyencoding].
///
/// [saplingfullviewingkeyencoding]: https://zips.z.cash/protocol/protocol.pdf#saplingfullviewingkeyencoding
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let ak = {
let mut buf = [0u8; 32];
Expand Down Expand Up @@ -471,25 +453,47 @@ impl FullViewingKey {
reader.read_exact(&mut ovk)?;

Ok(FullViewingKey {
vk: ViewingKey { ak, nk },
ak,
nk,
ovk: OutgoingViewingKey(ovk),
})
}

/// Serializes the full viewing key as specified in
/// [Zcash Protocol Spec § 5.6.3.3: Sapling Full Viewing Keys][saplingfullviewingkeyencoding].
///
/// [saplingfullviewingkeyencoding]: https://zips.z.cash/protocol/protocol.pdf#saplingfullviewingkeyencoding
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.0.to_bytes())?;
writer.write_all(&self.ak.to_bytes())?;
writer.write_all(&self.nk.0.to_bytes())?;
writer.write_all(&self.ovk.0)?;

Ok(())
}

/// Serializes the full viewing key as specified in
/// [Zcash Protocol Spec § 5.6.3.3: Sapling Full Viewing Keys][saplingfullviewingkeyencoding].
///
/// [saplingfullviewingkeyencoding]: https://zips.z.cash/protocol/protocol.pdf#saplingfullviewingkeyencoding
pub fn to_bytes(&self) -> [u8; 96] {
let mut result = [0u8; 96];
self.write(&mut result[..])
.expect("should be able to serialize a FullViewingKey");
result
}

pub fn rk(&self, ar: jubjub::Fr) -> redjubjub::VerificationKey<SpendAuth> {
self.ak.randomize(&ar)
}

/// Derives an `IncomingViewingKey` for this full viewing key.
pub fn ivk(&self) -> SaplingIvk {
SaplingIvk(crh_ivk(self.ak.to_bytes(), self.nk.0.to_bytes()))
}

pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
self.ivk().to_payment_address(diversifier)
}
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -750,7 +754,7 @@ pub mod testing {

prop_compose! {
pub fn arb_incoming_viewing_key()(fvk in arb_full_viewing_key()) -> SaplingIvk {
fvk.vk.ivk()
fvk.ivk()
}
}
}
Expand Down
45 changes: 18 additions & 27 deletions src/zip32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use zip32::{ChainCode, ChildIndex, DiversifierIndex, Scope};
use std::io::{self, Read, Write};
use std::ops::AddAssign;

use super::{Diversifier, NullifierDerivingKey, PaymentAddress, ViewingKey};
use super::{Diversifier, NullifierDerivingKey, PaymentAddress};
use crate::keys::ProofAuthorizingKey;
use crate::{
constants::PROOF_GENERATION_KEY_GENERATOR,
Expand All @@ -37,7 +37,7 @@ pub fn sapling_address(
j: DiversifierIndex,
) -> Option<PaymentAddress> {
dk.diversifier(j)
.and_then(|d_j| fvk.vk.to_payment_address(d_j))
.and_then(|d_j| fvk.to_payment_address(d_j))
}

/// Search the diversifier space starting at diversifier index `j` for
Expand All @@ -50,7 +50,7 @@ pub fn sapling_find_address(
j: DiversifierIndex,
) -> Option<(DiversifierIndex, PaymentAddress)> {
let (j, d_j) = dk.find_diversifier(j)?;
fvk.vk.to_payment_address(d_j).map(|addr| (j, addr))
fvk.to_payment_address(d_j).map(|addr| (j, addr))
}

/// Returns the payment address corresponding to the smallest valid diversifier
Expand Down Expand Up @@ -93,16 +93,14 @@ pub fn sapling_derive_internal_fvk(
jubjub::Fr::from_bytes_wide(&PrfExpand::SAPLING_ZIP32_INTERNAL_NSK.with(i.as_bytes()));
let r = PrfExpand::SAPLING_ZIP32_INTERNAL_DK_OVK.with(i.as_bytes());
// PROOF_GENERATION_KEY_GENERATOR = \mathcal{H}^Sapling
let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.vk.nk.0);
let nk_internal = NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * i_nsk + fvk.nk.0);
let dk_internal = DiversifierKey(r[..32].try_into().unwrap());
let ovk_internal = OutgoingViewingKey(r[32..].try_into().unwrap());

(
FullViewingKey {
vk: ViewingKey {
ak: fvk.vk.ak.clone(),
nk: nk_internal,
},
ak: fvk.ak.clone(),
nk: nk_internal,
ovk: ovk_internal,
},
dk_internal,
Expand Down Expand Up @@ -534,9 +532,7 @@ impl std::cmp::PartialEq for ExtendedFullViewingKey {
&& self.parent_fvk_tag == rhs.parent_fvk_tag
&& self.child_index == rhs.child_index
&& self.chain_code == rhs.chain_code
&& self.fvk.vk.ak == rhs.fvk.vk.ak
&& self.fvk.vk.nk == rhs.fvk.vk.nk
&& self.fvk.ovk == rhs.fvk.ovk
&& self.fvk == rhs.fvk
&& self.dk == rhs.dk
}
}
Expand Down Expand Up @@ -709,16 +705,16 @@ impl DiversifiableFullViewingKey {
/// 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,
Scope::External => self.fvk.nk,
Scope::Internal => self.derive_internal().fvk.nk,
}
}

/// Derives an incoming viewing key corresponding to this full viewing key.
pub fn to_ivk(&self, scope: Scope) -> SaplingIvk {
match scope {
Scope::External => self.fvk.vk.ivk(),
Scope::Internal => self.derive_internal().fvk.vk.ivk(),
Scope::External => self.fvk.ivk(),
Scope::Internal => self.derive_internal().fvk.ivk(),
}
}

Expand Down Expand Up @@ -937,9 +933,7 @@ mod tests {
// Check value -> bytes -> parsed round trip.
let dfvk_bytes = dfvk.to_bytes();
let dfvk_parsed = DiversifiableFullViewingKey::from_bytes(&dfvk_bytes).unwrap();
assert_eq!(dfvk_parsed.fvk.vk.ak, dfvk.fvk.vk.ak);
assert_eq!(dfvk_parsed.fvk.vk.nk, dfvk.fvk.vk.nk);
assert_eq!(dfvk_parsed.fvk.ovk, dfvk.fvk.ovk);
assert_eq!(dfvk_parsed.fvk, dfvk.fvk);
assert_eq!(dfvk_parsed.dk, dfvk.dk);

// Check bytes -> parsed -> bytes round trip.
Expand Down Expand Up @@ -1651,14 +1645,14 @@ 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.0.to_bytes(), tv.nk);
assert_eq!(xfvk.fvk.ak.to_bytes(), tv.ak);
assert_eq!(xfvk.fvk.nk.0.to_bytes(), tv.nk);

assert_eq!(xfvk.fvk.ovk.0, tv.ovk);
assert_eq!(xfvk.dk.0, tv.dk);
assert_eq!(xfvk.chain_code.as_bytes(), &tv.c);

assert_eq!(xfvk.fvk.vk.ivk().to_repr().as_ref(), tv.ivk);
assert_eq!(xfvk.fvk.ivk().to_repr().as_ref(), tv.ivk);

let mut ser = vec![];
xfvk.write(&mut ser).unwrap();
Expand Down Expand Up @@ -1695,17 +1689,14 @@ 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.0.to_bytes(), tv.internal_nk);
assert_eq!(internal_xfvk.fvk.ak.to_bytes(), tv.ak);
assert_eq!(internal_xfvk.fvk.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);
assert_eq!(internal_xfvk.chain_code.as_bytes(), &tv.c);

assert_eq!(
internal_xfvk.fvk.vk.ivk().to_repr().as_ref(),
tv.internal_ivk
);
assert_eq!(internal_xfvk.fvk.ivk().to_repr().as_ref(), tv.internal_ivk);

let mut ser = vec![];
internal_xfvk.write(&mut ser).unwrap();
Expand Down

0 comments on commit c75dd13

Please sign in to comment.