Move sapling-specific primitives into the sapling module.

This commit is contained in:
Kris Nuttycombe 2021-03-04 14:26:39 -07:00 committed by Jack Grigg
parent abbf5dfd83
commit 4086df772c
27 changed files with 400 additions and 430 deletions

View File

@ -1,6 +1,6 @@
//! Structs for handling supported address types.
use zcash_primitives::{consensus, legacy::TransparentAddress, primitives::PaymentAddress};
use zcash_primitives::{consensus, legacy::TransparentAddress, sapling::PaymentAddress};
use crate::encoding::{
decode_payment_address, decode_transparent_address, encode_payment_address,

View File

@ -9,8 +9,7 @@ use zcash_primitives::{
consensus::BlockHeight,
memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::{Nullifier, PaymentAddress},
sapling::Node,
sapling::{Node, Nullifier, PaymentAddress},
transaction::{components::Amount, Transaction, TxId},
zip32::ExtendedFullViewingKey,
};
@ -277,8 +276,7 @@ pub mod testing {
consensus::BlockHeight,
memo::Memo,
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::{Nullifier, PaymentAddress},
sapling::Node,
sapling::{Node, Nullifier, PaymentAddress},
transaction::{components::Amount, TxId},
zip32::ExtendedFullViewingKey,
};

View File

@ -95,7 +95,7 @@ use zcash_primitives::{
block::BlockHash,
consensus::{self, BlockHeight, NetworkUpgrade},
merkle_tree::CommitmentTree,
primitives::Nullifier,
sapling::Nullifier,
zip32::ExtendedFullViewingKey,
};

View File

@ -4,7 +4,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight},
memo::MemoBytes,
note_encryption::{try_sapling_note_decryption, try_sapling_output_recovery},
primitives::{Note, PaymentAddress},
sapling::{Note, PaymentAddress},
transaction::Transaction,
zip32::ExtendedFullViewingKey,
};

View File

@ -11,7 +11,7 @@ use std::convert::TryInto;
use std::io::{self, Write};
use zcash_primitives::{
legacy::TransparentAddress,
primitives::PaymentAddress,
sapling::PaymentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};
@ -114,7 +114,7 @@ pub fn decode_extended_full_viewing_key(
/// };
/// use zcash_primitives::{
/// constants::testnet::HRP_SAPLING_PAYMENT_ADDRESS,
/// primitives::{Diversifier, PaymentAddress},
/// sapling::{Diversifier, PaymentAddress},
/// };
///
/// let rng = &mut XorShiftRng::from_seed([
@ -302,7 +302,7 @@ mod tests {
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
constants,
primitives::{Diversifier, PaymentAddress},
sapling::{Diversifier, PaymentAddress},
zip32::ExtendedSpendingKey,
};

View File

@ -7,7 +7,7 @@ use std::convert::TryInto;
use zcash_primitives::{
block::{BlockHash, BlockHeader},
consensus::BlockHeight,
primitives::Nullifier,
sapling::Nullifier,
};
pub mod compact_formats;

View File

@ -6,8 +6,7 @@ use subtle::{Choice, ConditionallySelectable};
use zcash_primitives::{
keys::OutgoingViewingKey,
merkle_tree::IncrementalWitness,
primitives::{Diversifier, Note, Nullifier, PaymentAddress, Rseed},
sapling::Node,
sapling::{Diversifier, Node, Note, Nullifier, PaymentAddress, Rseed},
transaction::{components::Amount, TxId},
};

View File

@ -7,8 +7,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight},
merkle_tree::{CommitmentTree, IncrementalWitness},
note_encryption::try_sapling_compact_note_decryption,
primitives::{Note, Nullifier, PaymentAddress, SaplingIvk},
sapling::Node,
sapling::{Node, Note, Nullifier, PaymentAddress, SaplingIvk},
transaction::TxId,
zip32::ExtendedFullViewingKey,
};
@ -307,8 +306,7 @@ mod tests {
memo::MemoBytes,
merkle_tree::CommitmentTree,
note_encryption::SaplingNoteEncryption,
primitives::{Note, Nullifier, SaplingIvk},
sapling::util::generate_random_rseed,
sapling::{util::generate_random_rseed, Note, Nullifier, SaplingIvk},
transaction::components::Amount,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};

View File

@ -43,8 +43,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight},
memo::Memo,
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::{Nullifier, PaymentAddress},
sapling::Node,
sapling::{Node, Nullifier, PaymentAddress},
transaction::{components::Amount, TxId},
zip32::ExtendedFullViewingKey,
};
@ -565,8 +564,7 @@ mod tests {
consensus::{BlockHeight, Network, NetworkUpgrade, Parameters},
memo::MemoBytes,
note_encryption::SaplingNoteEncryption,
primitives::{Note, Nullifier, PaymentAddress},
sapling::util::generate_random_rseed,
sapling::{util::generate_random_rseed, Note, Nullifier, PaymentAddress},
transaction::components::Amount,
zip32::ExtendedFullViewingKey,
};

View File

@ -17,8 +17,7 @@ use zcash_primitives::{
consensus::{self, BlockHeight, NetworkUpgrade},
memo::{Memo, MemoBytes},
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::{Note, Nullifier, PaymentAddress},
sapling::Node,
sapling::{Node, Note, Nullifier, PaymentAddress},
transaction::{components::Amount, Transaction, TxId},
zip32::ExtendedFullViewingKey,
};

View File

@ -8,7 +8,7 @@ use ff::PrimeField;
use zcash_primitives::{
consensus::BlockHeight,
merkle_tree::IncrementalWitness,
primitives::{Diversifier, Rseed},
sapling::{Diversifier, Rseed},
transaction::components::Amount,
};

View File

@ -471,8 +471,8 @@ mod tests {
extensions::transparent::{self as tze, Extension, FromPayload, ToPayload},
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::Rseed,
sapling::Node,
sapling::Rseed,
transaction::{
builder::Builder,
components::{

View File

@ -5,8 +5,9 @@ use zcash_primitives::{
consensus::{NetworkUpgrade::Canopy, Parameters, TEST_NETWORK},
memo::MemoBytes,
note_encryption::{try_sapling_note_decryption, SaplingNoteEncryption},
primitives::{Diversifier, PaymentAddress, SaplingIvk, ValueCommitment},
sapling::util::generate_random_rseed,
sapling::{
util::generate_random_rseed, Diversifier, PaymentAddress, SaplingIvk, ValueCommitment,
},
transaction::components::{OutputDescription, GROTH_PROOF_SIZE},
};

View File

@ -6,7 +6,7 @@
use crate::{
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
primitives::{ProofGenerationKey, ViewingKey},
sapling::{ProofGenerationKey, ViewingKey},
};
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::PrimeField;
@ -186,7 +186,7 @@ pub mod testing {
use proptest::prelude::{any, prop_compose};
use crate::{
primitives::PaymentAddress,
sapling::PaymentAddress,
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};

View File

@ -15,7 +15,6 @@ pub mod legacy;
pub mod memo;
pub mod merkle_tree;
pub mod note_encryption;
pub mod primitives;
pub mod sapling;
pub mod serialize;
pub mod transaction;

View File

@ -3,7 +3,7 @@
use crate::{
consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD},
memo::MemoBytes,
primitives::{Diversifier, Note, PaymentAddress, Rseed, SaplingIvk},
sapling::{Diversifier, Note, PaymentAddress, Rseed, SaplingIvk},
};
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
@ -115,7 +115,7 @@ pub fn prf_ock(
/// keys::{OutgoingViewingKey, prf_expand},
/// memo::MemoBytes,
/// note_encryption::SaplingNoteEncryption,
/// primitives::{Diversifier, PaymentAddress, Rseed, ValueCommitment},
/// sapling::{Diversifier, PaymentAddress, Rseed, ValueCommitment},
/// };
///
/// let mut rng = OsRng;
@ -582,8 +582,8 @@ mod tests {
},
keys::OutgoingViewingKey,
memo::MemoBytes,
primitives::{Diversifier, PaymentAddress, Rseed, SaplingIvk, ValueCommitment},
sapling::util::generate_random_rseed,
sapling::{Diversifier, PaymentAddress, Rseed, SaplingIvk, ValueCommitment},
};
fn random_enc_ciphertext<R: RngCore + CryptoRng>(

View File

@ -1,357 +0,0 @@
//! Structs for core Zcash primitives.
use ff::PrimeField;
use group::{Curve, Group, GroupEncoding};
use std::array::TryFromSliceError;
use std::convert::TryInto;
use subtle::{Choice, ConstantTimeEq};
use crate::constants;
use crate::sapling::group_hash::group_hash;
use crate::sapling::pedersen_hash::{pedersen_hash, Personalization};
use byteorder::{LittleEndian, WriteBytesExt};
use crate::keys::prf_expand;
use blake2s_simd::Params as Blake2sParams;
use rand_core::{CryptoRng, RngCore};
#[derive(Clone)]
pub struct ValueCommitment {
pub value: u64,
pub randomness: jubjub::Fr,
}
impl ValueCommitment {
pub fn commitment(&self) -> jubjub::SubgroupPoint {
(constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value))
+ (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness)
}
}
#[derive(Clone)]
pub struct ProofGenerationKey {
pub ak: jubjub::SubgroupPoint,
pub nsk: jubjub::Fr,
}
impl ProofGenerationKey {
pub fn to_viewing_key(&self) -> ViewingKey {
ViewingKey {
ak: self.ak,
nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk,
}
}
}
#[derive(Debug, Clone)]
pub struct ViewingKey {
pub ak: jubjub::SubgroupPoint,
pub nk: jubjub::SubgroupPoint,
}
impl ViewingKey {
pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint {
self.ak + constants::SPENDING_KEY_GENERATOR * ar
}
pub fn ivk(&self) -> SaplingIvk {
let mut h = [0; 32];
h.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(constants::CRH_IVK_PERSONALIZATION)
.to_state()
.update(&self.ak.to_bytes())
.update(&self.nk.to_bytes())
.finalize()
.as_bytes(),
);
// Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
SaplingIvk(jubjub::Fr::from_repr(h).expect("should be a valid scalar"))
}
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
self.ivk().to_payment_address(diversifier)
}
}
pub struct SaplingIvk(pub jubjub::Fr);
impl SaplingIvk {
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
diversifier.g_d().and_then(|g_d| {
let pk_d = g_d * self.0;
PaymentAddress::from_parts(diversifier, pk_d)
})
}
pub fn to_repr(&self) -> [u8; 32] {
self.0.to_repr()
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Diversifier(pub [u8; 11]);
impl Diversifier {
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION)
}
}
/// A Sapling payment address.
///
/// # Invariants
///
/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
/// and not the identity).
#[derive(Clone, Debug)]
pub struct PaymentAddress {
pk_d: jubjub::SubgroupPoint,
diversifier: Diversifier,
}
impl PartialEq for PaymentAddress {
fn eq(&self, other: &Self) -> bool {
self.pk_d == other.pk_d && self.diversifier == other.diversifier
}
}
impl PaymentAddress {
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Returns None if `pk_d` is the identity.
pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option<Self> {
if pk_d.is_identity().into() {
None
} else {
Some(PaymentAddress { pk_d, diversifier })
}
}
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Only for test code, as this explicitly bypasses the invariant.
#[cfg(test)]
pub(crate) fn from_parts_unchecked(
diversifier: Diversifier,
pk_d: jubjub::SubgroupPoint,
) -> Self {
PaymentAddress { pk_d, diversifier }
}
/// Parses a PaymentAddress from bytes.
pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
let diversifier = {
let mut tmp = [0; 11];
tmp.copy_from_slice(&bytes[0..11]);
Diversifier(tmp)
};
// Check that the diversifier is valid
diversifier.g_d()?;
let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
if pk_d.is_some().into() {
PaymentAddress::from_parts(diversifier, pk_d.unwrap())
} else {
None
}
}
/// Returns the byte encoding of this `PaymentAddress`.
pub fn to_bytes(&self) -> [u8; 43] {
let mut bytes = [0; 43];
bytes[0..11].copy_from_slice(&self.diversifier.0);
bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
bytes
}
/// Returns the [`Diversifier`] for this `PaymentAddress`.
pub fn diversifier(&self) -> &Diversifier {
&self.diversifier
}
/// Returns `pk_d` for this `PaymentAddress`.
pub fn pk_d(&self) -> &jubjub::SubgroupPoint {
&self.pk_d
}
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
self.diversifier.g_d()
}
pub fn create_note(&self, value: u64, randomness: Rseed) -> Option<Note> {
self.g_d().map(|g_d| Note {
value,
rseed: randomness,
g_d,
pk_d: self.pk_d,
})
}
}
/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212).
///
/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value.
/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
#[derive(Copy, Clone, Debug)]
pub enum Rseed {
BeforeZip212(jubjub::Fr),
AfterZip212([u8; 32]),
}
/// Typesafe wrapper for nullifier values.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Nullifier(pub [u8; 32]);
impl Nullifier {
pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
bytes.try_into().map(Nullifier)
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
}
impl ConstantTimeEq for Nullifier {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
#[derive(Clone, Debug)]
pub struct Note {
/// The value of the note
pub value: u64,
/// The diversified base of the address, GH(d)
pub g_d: jubjub::SubgroupPoint,
/// The public key of the address, g_d^ivk
pub pk_d: jubjub::SubgroupPoint,
/// rseed
pub rseed: Rseed,
}
impl PartialEq for Note {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.g_d == other.g_d
&& self.pk_d == other.pk_d
&& self.rcm() == other.rcm()
}
}
impl Note {
pub fn uncommitted() -> bls12_381::Scalar {
// The smallest u-coordinate that is not on the curve
// is one.
bls12_381::Scalar::one()
}
/// Computes the note commitment, returning the full point.
fn cm_full_point(&self) -> jubjub::SubgroupPoint {
// Calculate the note contents, as bytes
let mut note_contents = vec![];
// Writing the value in little endian
(&mut note_contents)
.write_u64::<LittleEndian>(self.value)
.unwrap();
// Write g_d
note_contents.extend_from_slice(&self.g_d.to_bytes());
// Write pk_d
note_contents.extend_from_slice(&self.pk_d.to_bytes());
assert_eq!(note_contents.len(), 32 + 32 + 8);
// Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment,
note_contents
.into_iter()
.flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
);
// Compute final commitment
(constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
}
/// Computes the nullifier given the viewing key and
/// note position
pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Nullifier {
// Compute rho = cm + position.G
let rho = self.cm_full_point()
+ (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position));
// Compute nf = BLAKE2s(nk | rho)
Nullifier::from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(constants::PRF_NF_PERSONALIZATION)
.to_state()
.update(&viewing_key.nk.to_bytes())
.update(&rho.to_bytes())
.finalize()
.as_bytes(),
)
.unwrap()
}
/// Computes the note commitment
pub fn cmu(&self) -> bls12_381::Scalar {
// The commitment is in the prime order subgroup, so mapping the
// commitment to the u-coordinate is an injective encoding.
jubjub::ExtendedPoint::from(self.cm_full_point())
.to_affine()
.get_u()
}
pub fn rcm(&self) -> jubjub::Fr {
match self.rseed {
Rseed::BeforeZip212(rcm) => rcm,
Rseed::AfterZip212(rseed) => {
jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array())
}
}
}
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
self.generate_or_derive_esk_internal(rng)
}
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> jubjub::Fr {
match self.derive_esk() {
None => {
// create random 64 byte buffer
let mut buffer = [0u8; 64];
rng.fill_bytes(&mut buffer);
// reduce to uniform value
jubjub::Fr::from_bytes_wide(&buffer)
}
Some(esk) => esk,
}
}
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
pub fn derive_esk(&self) -> Option<jubjub::Fr> {
match self.rseed {
Rseed::BeforeZip212(_) => None,
Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide(
prf_expand(&rseed, &[0x05]).as_array(),
)),
}
}
}

View File

@ -7,15 +7,25 @@ pub mod redjubjub;
pub mod util;
use bitvec::{order::Lsb0, view::AsBits};
use blake2s_simd::Params as Blake2sParams;
use byteorder::{LittleEndian, WriteBytesExt};
use ff::PrimeField;
use group::{Curve, GroupEncoding};
use group::{Curve, Group, GroupEncoding};
use lazy_static::lazy_static;
use rand_core::{CryptoRng, RngCore};
use std::array::TryFromSliceError;
use std::convert::TryInto;
use std::io::{self, Read, Write};
use subtle::{Choice, ConstantTimeEq};
use crate::{constants::SPENDING_KEY_GENERATOR, merkle_tree::Hashable, primitives::Note};
use crate::{
constants::{self, SPENDING_KEY_GENERATOR},
keys::prf_expand,
merkle_tree::Hashable,
};
use self::{
group_hash::group_hash,
pedersen_hash::{pedersen_hash, Personalization},
redjubjub::{PrivateKey, PublicKey, Signature},
};
@ -143,3 +153,340 @@ pub(crate) fn spend_sig_internal<R: RngCore>(
// Do the signing
rsk.sign(&data_to_be_signed, rng, SPENDING_KEY_GENERATOR)
}
#[derive(Clone)]
pub struct ValueCommitment {
pub value: u64,
pub randomness: jubjub::Fr,
}
impl ValueCommitment {
pub fn commitment(&self) -> jubjub::SubgroupPoint {
(constants::VALUE_COMMITMENT_VALUE_GENERATOR * jubjub::Fr::from(self.value))
+ (constants::VALUE_COMMITMENT_RANDOMNESS_GENERATOR * self.randomness)
}
}
#[derive(Clone)]
pub struct ProofGenerationKey {
pub ak: jubjub::SubgroupPoint,
pub nsk: jubjub::Fr,
}
impl ProofGenerationKey {
pub fn to_viewing_key(&self) -> ViewingKey {
ViewingKey {
ak: self.ak,
nk: constants::PROOF_GENERATION_KEY_GENERATOR * self.nsk,
}
}
}
#[derive(Debug, Clone)]
pub struct ViewingKey {
pub ak: jubjub::SubgroupPoint,
pub nk: jubjub::SubgroupPoint,
}
impl ViewingKey {
pub fn rk(&self, ar: jubjub::Fr) -> jubjub::SubgroupPoint {
self.ak + constants::SPENDING_KEY_GENERATOR * ar
}
pub fn ivk(&self) -> SaplingIvk {
let mut h = [0; 32];
h.copy_from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(constants::CRH_IVK_PERSONALIZATION)
.to_state()
.update(&self.ak.to_bytes())
.update(&self.nk.to_bytes())
.finalize()
.as_bytes(),
);
// Drop the most significant five bits, so it can be interpreted as a scalar.
h[31] &= 0b0000_0111;
SaplingIvk(jubjub::Fr::from_repr(h).expect("should be a valid scalar"))
}
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
self.ivk().to_payment_address(diversifier)
}
}
#[derive(Debug, Clone)]
pub struct SaplingIvk(pub jubjub::Fr);
impl SaplingIvk {
pub fn to_payment_address(&self, diversifier: Diversifier) -> Option<PaymentAddress> {
diversifier.g_d().and_then(|g_d| {
let pk_d = g_d * self.0;
PaymentAddress::from_parts(diversifier, pk_d)
})
}
pub fn to_repr(&self) -> [u8; 32] {
self.0.to_repr()
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Diversifier(pub [u8; 11]);
impl Diversifier {
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
group_hash(&self.0, constants::KEY_DIVERSIFICATION_PERSONALIZATION)
}
}
/// A Sapling payment address.
///
/// # Invariants
///
/// `pk_d` is guaranteed to be prime-order (i.e. in the prime-order subgroup of Jubjub,
/// and not the identity).
#[derive(Clone, Debug)]
pub struct PaymentAddress {
pk_d: jubjub::SubgroupPoint,
diversifier: Diversifier,
}
impl PartialEq for PaymentAddress {
fn eq(&self, other: &Self) -> bool {
self.pk_d == other.pk_d && self.diversifier == other.diversifier
}
}
impl PaymentAddress {
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Returns None if `pk_d` is the identity.
pub fn from_parts(diversifier: Diversifier, pk_d: jubjub::SubgroupPoint) -> Option<Self> {
if pk_d.is_identity().into() {
None
} else {
Some(PaymentAddress { pk_d, diversifier })
}
}
/// Constructs a PaymentAddress from a diversifier and a Jubjub point.
///
/// Only for test code, as this explicitly bypasses the invariant.
#[cfg(test)]
pub(crate) fn from_parts_unchecked(
diversifier: Diversifier,
pk_d: jubjub::SubgroupPoint,
) -> Self {
PaymentAddress { pk_d, diversifier }
}
/// Parses a PaymentAddress from bytes.
pub fn from_bytes(bytes: &[u8; 43]) -> Option<Self> {
let diversifier = {
let mut tmp = [0; 11];
tmp.copy_from_slice(&bytes[0..11]);
Diversifier(tmp)
};
// Check that the diversifier is valid
diversifier.g_d()?;
let pk_d = jubjub::SubgroupPoint::from_bytes(bytes[11..43].try_into().unwrap());
if pk_d.is_some().into() {
PaymentAddress::from_parts(diversifier, pk_d.unwrap())
} else {
None
}
}
/// Returns the byte encoding of this `PaymentAddress`.
pub fn to_bytes(&self) -> [u8; 43] {
let mut bytes = [0; 43];
bytes[0..11].copy_from_slice(&self.diversifier.0);
bytes[11..].copy_from_slice(&self.pk_d.to_bytes());
bytes
}
/// Returns the [`Diversifier`] for this `PaymentAddress`.
pub fn diversifier(&self) -> &Diversifier {
&self.diversifier
}
/// Returns `pk_d` for this `PaymentAddress`.
pub fn pk_d(&self) -> &jubjub::SubgroupPoint {
&self.pk_d
}
pub fn g_d(&self) -> Option<jubjub::SubgroupPoint> {
self.diversifier.g_d()
}
pub fn create_note(&self, value: u64, randomness: Rseed) -> Option<Note> {
self.g_d().map(|g_d| Note {
value,
rseed: randomness,
g_d,
pk_d: self.pk_d,
})
}
}
/// Enum for note randomness before and after [ZIP 212](https://zips.z.cash/zip-0212).
///
/// Before ZIP 212, the note commitment trapdoor `rcm` must be a scalar value.
/// After ZIP 212, the note randomness `rseed` is a 32-byte sequence, used to derive
/// both the note commitment trapdoor `rcm` and the ephemeral private key `esk`.
#[derive(Copy, Clone, Debug)]
pub enum Rseed {
BeforeZip212(jubjub::Fr),
AfterZip212([u8; 32]),
}
/// Typesafe wrapper for nullifier values.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Nullifier(pub [u8; 32]);
impl Nullifier {
pub fn from_slice(bytes: &[u8]) -> Result<Nullifier, TryFromSliceError> {
bytes.try_into().map(Nullifier)
}
pub fn to_vec(&self) -> Vec<u8> {
self.0.to_vec()
}
}
impl ConstantTimeEq for Nullifier {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
#[derive(Clone, Debug)]
pub struct Note {
/// The value of the note
pub value: u64,
/// The diversified base of the address, GH(d)
pub g_d: jubjub::SubgroupPoint,
/// The public key of the address, g_d^ivk
pub pk_d: jubjub::SubgroupPoint,
/// rseed
pub rseed: Rseed,
}
impl PartialEq for Note {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.g_d == other.g_d
&& self.pk_d == other.pk_d
&& self.rcm() == other.rcm()
}
}
impl Note {
pub fn uncommitted() -> bls12_381::Scalar {
// The smallest u-coordinate that is not on the curve
// is one.
bls12_381::Scalar::one()
}
/// Computes the note commitment, returning the full point.
fn cm_full_point(&self) -> jubjub::SubgroupPoint {
// Calculate the note contents, as bytes
let mut note_contents = vec![];
// Writing the value in little endian
(&mut note_contents)
.write_u64::<LittleEndian>(self.value)
.unwrap();
// Write g_d
note_contents.extend_from_slice(&self.g_d.to_bytes());
// Write pk_d
note_contents.extend_from_slice(&self.pk_d.to_bytes());
assert_eq!(note_contents.len(), 32 + 32 + 8);
// Compute the Pedersen hash of the note contents
let hash_of_contents = pedersen_hash(
Personalization::NoteCommitment,
note_contents
.into_iter()
.flat_map(|byte| (0..8).map(move |i| ((byte >> i) & 1) == 1)),
);
// Compute final commitment
(constants::NOTE_COMMITMENT_RANDOMNESS_GENERATOR * self.rcm()) + hash_of_contents
}
/// Computes the nullifier given the viewing key and
/// note position
pub fn nf(&self, viewing_key: &ViewingKey, position: u64) -> Nullifier {
// Compute rho = cm + position.G
let rho = self.cm_full_point()
+ (constants::NULLIFIER_POSITION_GENERATOR * jubjub::Fr::from(position));
// Compute nf = BLAKE2s(nk | rho)
Nullifier::from_slice(
Blake2sParams::new()
.hash_length(32)
.personal(constants::PRF_NF_PERSONALIZATION)
.to_state()
.update(&viewing_key.nk.to_bytes())
.update(&rho.to_bytes())
.finalize()
.as_bytes(),
)
.unwrap()
}
/// Computes the note commitment
pub fn cmu(&self) -> bls12_381::Scalar {
// The commitment is in the prime order subgroup, so mapping the
// commitment to the u-coordinate is an injective encoding.
jubjub::ExtendedPoint::from(self.cm_full_point())
.to_affine()
.get_u()
}
pub fn rcm(&self) -> jubjub::Fr {
match self.rseed {
Rseed::BeforeZip212(rcm) => rcm,
Rseed::AfterZip212(rseed) => {
jubjub::Fr::from_bytes_wide(prf_expand(&rseed, &[0x04]).as_array())
}
}
}
pub fn generate_or_derive_esk<R: RngCore + CryptoRng>(&self, rng: &mut R) -> jubjub::Fr {
self.generate_or_derive_esk_internal(rng)
}
pub(crate) fn generate_or_derive_esk_internal<R: RngCore>(&self, rng: &mut R) -> jubjub::Fr {
match self.derive_esk() {
None => {
// create random 64 byte buffer
let mut buffer = [0u8; 64];
rng.fill_bytes(&mut buffer);
// reduce to uniform value
jubjub::Fr::from_bytes_wide(&buffer)
}
Some(esk) => esk,
}
}
/// Returns the derived `esk` if this note was created after ZIP 212 activated.
pub fn derive_esk(&self) -> Option<jubjub::Fr> {
match self.rseed {
Rseed::BeforeZip212(_) => None,
Rseed::AfterZip212(rseed) => Some(jubjub::Fr::from_bytes_wide(
prf_expand(&rseed, &[0x05]).as_array(),
)),
}
}
}

View File

@ -1,7 +1,5 @@
//! Abstractions over the proving system and parameters.
use crate::primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed};
use crate::{
merkle_tree::MerklePath,
sapling::{
@ -11,6 +9,8 @@ use crate::{
transaction::components::{Amount, GROTH_PROOF_SIZE},
};
use super::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed};
/// Interface for creating zero-knowledge proofs for shielded transactions.
pub trait TxProver {
/// Type for persisting any necessary context across multiple Sapling proofs.
@ -69,14 +69,10 @@ pub mod mock {
use crate::{
constants::SPENDING_KEY_GENERATOR,
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
};
use crate::{
merkle_tree::MerklePath,
sapling::{
redjubjub::{PublicKey, Signature},
Node,
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};

View File

@ -1,13 +1,11 @@
use blake2b_simd::Params;
use crate::{
consensus::{self, BlockHeight, NetworkUpgrade},
primitives::Rseed,
};
use ff::Field;
use rand_core::{CryptoRng, RngCore};
use crate::consensus::{self, BlockHeight, NetworkUpgrade};
use super::Rseed;
pub fn hash_to_scalar(persona: &[u8], a: &[u8], b: &[u8]) -> jubjub::Fr {
let mut hasher = Params::new().hash_length(64).personal(persona).to_state();
hasher.update(a);

View File

@ -17,10 +17,9 @@ use crate::{
memo::MemoBytes,
merkle_tree::MerklePath,
note_encryption::SaplingNoteEncryption,
primitives::{Diversifier, Note, PaymentAddress},
sapling::{
prover::TxProver, redjubjub::PrivateKey, spend_sig_internal,
util::generate_random_rseed_internal, Node,
util::generate_random_rseed_internal, Diversifier, Node, Note, PaymentAddress,
},
transaction::{
components::{
@ -966,8 +965,7 @@ mod tests {
consensus::{self, Parameters, H0, TEST_NETWORK},
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
primitives::Rseed,
sapling::{prover::mock::MockTxProver, Node},
sapling::{prover::mock::MockTxProver, Node, Rseed},
transaction::components::{amount::Amount, amount::DEFAULT_FEE},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};

View File

@ -3,9 +3,9 @@ use group::GroupEncoding;
use std::io::{self, Read, Write};
use crate::{
primitives::Nullifier,
sapling::redjubjub::{PublicKey, Signature},
use crate::sapling::{
redjubjub::{PublicKey, Signature},
Nullifier,
};
use super::GROTH_PROOF_SIZE;

View File

@ -10,7 +10,7 @@ use std::ops::AddAssign;
use crate::{
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
primitives::{Diversifier, PaymentAddress, ViewingKey},
sapling::{Diversifier, PaymentAddress, ViewingKey},
};
use std::io::{self, Read, Write};

View File

@ -8,7 +8,7 @@ use ff::Field;
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, ValueCommitment};
use zcash_primitives::sapling::{Diversifier, ProofGenerationKey, ValueCommitment};
use zcash_proofs::circuit::sapling::Spend;
const TREE_DEPTH: usize = 32;

View File

@ -7,7 +7,9 @@ use bellman::{Circuit, ConstraintSystem, SynthesisError};
use zcash_primitives::constants;
use zcash_primitives::primitives::{PaymentAddress, ProofGenerationKey, ValueCommitment};
use zcash_primitives::sapling::{
PaymentAddress, ProofGenerationKey, ValueCommitment, SAPLING_COMMITMENT_TREE_DEPTH,
};
use super::ecc;
use super::pedersen_hash;
@ -22,7 +24,7 @@ use bellman::gadgets::multipack;
use bellman::gadgets::num;
use bellman::gadgets::Assignment;
pub const TREE_DEPTH: usize = zcash_primitives::sapling::SAPLING_COMMITMENT_TREE_DEPTH;
pub const TREE_DEPTH: usize = SAPLING_COMMITMENT_TREE_DEPTH;
/// This is an instance of the `Spend` circuit.
pub struct Spend {
@ -516,10 +518,7 @@ fn test_input_circuit_with_bls12_381() {
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
sapling::pedersen_hash,
};
use zcash_primitives::sapling::{pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed};
let mut rng = XorShiftRng::from_seed([
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -659,10 +658,7 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() {
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::{
primitives::{Diversifier, Note, ProofGenerationKey, Rseed},
sapling::pedersen_hash,
};
use zcash_primitives::sapling::{pedersen_hash, Diversifier, Note, ProofGenerationKey, Rseed};
let mut rng = XorShiftRng::from_seed([
0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,
@ -836,7 +832,7 @@ fn test_output_circuit_with_bls12_381() {
use group::Group;
use rand_core::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
use zcash_primitives::primitives::{Diversifier, ProofGenerationKey, Rseed};
use zcash_primitives::sapling::{Diversifier, ProofGenerationKey, Rseed};
let mut rng = XorShiftRng::from_seed([
0x58, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc,

View File

@ -5,11 +5,10 @@ use bls12_381::Bls12;
use std::path::Path;
use zcash_primitives::{
merkle_tree::MerklePath,
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, Rseed},
sapling::{
prover::TxProver,
redjubjub::{PublicKey, Signature},
Node,
Diversifier, Node, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};

View File

@ -10,9 +10,10 @@ use std::ops::{AddAssign, Neg};
use zcash_primitives::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
merkle_tree::MerklePath,
primitives::{Diversifier, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment},
sapling::redjubjub::{PrivateKey, PublicKey, Signature},
sapling::Node,
sapling::{
redjubjub::{PrivateKey, PublicKey, Signature},
Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed, ValueCommitment,
},
transaction::components::Amount,
};