From aae4d026ddb57edb41ca2077d5a8b805b0572bc2 Mon Sep 17 00:00:00 2001 From: joe bebel <55120843+joebebel@users.noreply.github.com> Date: Sun, 20 Aug 2023 22:20:17 -0700 Subject: [PATCH] update note encryption --- masp_note_encryption/src/lib.rs | 46 ++---- masp_primitives/src/sapling.rs | 138 +++++++++++++----- .../src/sapling/note_encryption.rs | 46 +++--- masp_primitives/src/transaction/builder.rs | 10 +- .../transaction/components/sapling/builder.rs | 50 +++---- masp_proofs/src/circuit/sapling.rs | 34 ++--- masp_proofs/src/sapling/prover.rs | 10 +- 7 files changed, 174 insertions(+), 160 deletions(-) diff --git a/masp_note_encryption/src/lib.rs b/masp_note_encryption/src/lib.rs index 182b6897..96aaf3cc 100644 --- a/masp_note_encryption/src/lib.rs +++ b/masp_note_encryption/src/lib.rs @@ -23,14 +23,13 @@ extern crate alloc; #[cfg(feature = "alloc")] use alloc::vec::Vec; -use core::convert::TryInto; - use chacha20::{ cipher::{StreamCipher, StreamCipherSeek}, ChaCha20, }; use chacha20poly1305::{aead::AeadInPlace, ChaCha20Poly1305, KeyInit}; use cipher::KeyIvInit; +use core::convert::TryInto; //use crate::constants::ASSET_IDENTIFIER_LENGTH; pub const ASSET_IDENTIFIER_LENGTH: usize = 32; @@ -176,20 +175,7 @@ pub trait Domain { fn kdf(secret: Self::SharedSecret, ephemeral_key: &EphemeralKeyBytes) -> Self::SymmetricKey; /// Encodes the given `Note` and `Memo` as a note plaintext. - /// - /// # Future breaking changes - /// - /// The `recipient` argument is present as a secondary way to obtain the diversifier; - /// this is due to a historical quirk of how the Sapling `Note` struct was implemented - /// in the `zcash_primitives` crate. `recipient` will be removed from this method in a - /// future crate release, once [`zcash_primitives` has been refactored]. - /// - /// [`zcash_primitives` has been refactored]: https://github.com/zcash/librustzcash/issues/454 - fn note_plaintext_bytes( - note: &Self::Note, - recipient: &Self::Recipient, - memo: &Self::Memo, - ) -> NotePlaintextBytes; + fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> NotePlaintextBytes; /// Derives the [`OutgoingCipherKey`] for an encrypted note, given the note-specific /// public data and an `OutgoingViewingKey`. @@ -247,8 +233,6 @@ pub trait Domain { /// which may be passed via `self`). /// - The note plaintext contains valid encodings of its various fields. /// - Any domain-specific requirements are satisfied. - /// - `ephemeral_key` can be derived from `esk` and the diversifier within the note - /// plaintext. /// /// `&self` is passed here to enable the implementation to enforce contextual checks, /// such as rules like [ZIP 212] that become active at a specific block height. @@ -257,8 +241,6 @@ pub trait Domain { fn parse_note_plaintext_without_memo_ovk( &self, pk_d: &Self::DiversifiedTransmissionKey, - esk: &Self::EphemeralSecretKey, - ephemeral_key: &EphemeralKeyBytes, plaintext: &NotePlaintextBytes, ) -> Option<(Self::Note, Self::Recipient)>; @@ -350,12 +332,10 @@ pub trait ShieldedOutput { /// /// Implements section 4.19 of the /// [Zcash Protocol Specification](https://zips.z.cash/protocol/nu5.pdf#saplingandorchardinband) - pub struct NoteEncryption { epk: D::EphemeralPublicKey, esk: D::EphemeralSecretKey, note: D::Note, - to: D::Recipient, memo: D::Memo, /// `None` represents the `ovk = ⊥` case. ovk: Option, @@ -364,18 +344,12 @@ pub struct NoteEncryption { impl NoteEncryption { /// Construct a new note encryption context for the specified note, /// recipient, and memo. - pub fn new( - ovk: Option, - note: D::Note, - to: D::Recipient, - memo: D::Memo, - ) -> Self { + pub fn new(ovk: Option, note: D::Note, memo: D::Memo) -> Self { let esk = D::derive_esk(¬e).expect("ZIP 212 is active."); NoteEncryption { epk: D::ka_derive_public(¬e, &esk), esk, note, - to, memo, ovk, } @@ -390,14 +364,12 @@ impl NoteEncryption { esk: D::EphemeralSecretKey, ovk: Option, note: D::Note, - to: D::Recipient, memo: D::Memo, ) -> Self { NoteEncryption { epk: D::ka_derive_public(¬e, &esk), esk, note, - to, memo, ovk, } @@ -418,7 +390,7 @@ impl NoteEncryption { let pk_d = D::get_pk_d(&self.note); let shared_secret = D::ka_agree_enc(&self.esk, &pk_d); let key = D::kdf(shared_secret, &D::epk_bytes(&self.epk)); - let input = D::note_plaintext_bytes(&self.note, &self.to, &self.memo); + let input = D::note_plaintext_bytes(&self.note, &self.memo); let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; output[..NOTE_PLAINTEXT_SIZE].copy_from_slice(&input.0); @@ -545,6 +517,8 @@ fn check_note_validity( cmstar_bytes: &D::ExtractedCommitmentBytes, ) -> NoteValidity { if &D::ExtractedCommitmentBytes::from(&D::cmstar(note)) == cmstar_bytes { + // In the case corresponding to specification section 4.19.3, we check that `esk` is equal + // to `D::derive_esk(note)` prior to calling this method. if let Some(derived_esk) = D::derive_esk(note) { if D::epk_bytes(&D::ka_derive_public(note, &derived_esk)) .ct_eq(ephemeral_key) @@ -683,12 +657,12 @@ pub fn try_output_recovery_with_ock Option { - self.g_d().map(|g_d| Note { - asset_type, - value, - rseed, - g_d, - pk_d: self.pk_d, - }) + pub fn create_note(&self, asset_type: AssetType, value: u64, rseed: Rseed) -> Note { + Note::from_parts(asset_type, *self, NoteValue::from_raw(value), rseed) } } @@ -531,9 +525,29 @@ impl ConstantTimeEq for Nullifier { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// The non-negative value of an individual Sapling note. +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] pub struct NoteValue(u64); +impl NoteValue { + /// Returns the raw underlying value. + pub fn inner(&self) -> u64 { + self.0 + } + + /// Creates a note value from its raw numeric value. + /// + /// This only enforces that the value is an unsigned 64-bit integer. Callers should + /// enforce any additional constraints on the value's valid range themselves. + pub fn from_raw(value: u64) -> Self { + NoteValue(value) + } + + pub(crate) fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> { + BitArray::<_, Lsb0>::new(self.0.to_le_bytes()) + } +} + impl TryFrom for NoteValue { type Error = (); @@ -552,36 +566,80 @@ impl From for u64 { } } +impl From for i128 { + fn from(value: NoteValue) -> i128 { + value.0.into() + } +} + +/// A discrete amount of funds received by an address. #[derive(Clone, Debug, Copy)] pub struct Note { /// The asset type that the note represents pub asset_type: AssetType, + /// The recipient of the funds. + recipient: PaymentAddress, /// 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 value: NoteValue, + /// The seed randomness for various note components. pub rseed: Rseed, } impl PartialEq for Note { fn eq(&self, other: &Self) -> bool { - self.value == other.value - && self.asset_type == other.asset_type - && self.g_d == other.g_d - && self.pk_d == other.pk_d - && self.rcm() == other.rcm() + // Notes are canonically defined by their commitments. + self.cmu().eq(&other.cmu()) } } +impl Eq for Note {} + impl Note { pub fn uncommitted() -> bls12_381::Scalar { // The smallest u-coordinate that is not on the curve // is one. bls12_381::Scalar::one() } + /// Creates a note from its component parts. + /// + /// # Caveats + /// + /// This low-level constructor enforces that the provided arguments produce an + /// internally valid `Note`. However, it allows notes to be constructed in a way that + /// violates required security checks for note decryption, as specified in + /// [Section 4.19] of the Zcash Protocol Specification. Users of this constructor + /// should only call it with note components that have been fully validated by + /// decrypting a received note according to [Section 4.19]. + /// + /// [Section 4.19]: https://zips.z.cash/protocol/protocol.pdf#saplingandorchardinband + pub fn from_parts( + asset_type: AssetType, + recipient: PaymentAddress, + value: NoteValue, + rseed: Rseed, + ) -> Self { + Note { + asset_type, + recipient, + value, + rseed, + } + } + + /// Returns the recipient of this note. + pub fn recipient(&self) -> PaymentAddress { + self.recipient + } + + /// Returns the value of this note. + pub fn value(&self) -> NoteValue { + self.value + } + + /// Returns the rseed value of this note. + pub fn rseed(&self) -> &Rseed { + &self.rseed + } /// Computes the note commitment, returning the full point. fn cm_full_point(&self) -> jubjub::SubgroupPoint { @@ -592,13 +650,15 @@ impl Note { note_contents.extend_from_slice(&self.asset_type.asset_generator().to_bytes()); // Writing the value in little endian - note_contents.write_u64::(self.value).unwrap(); + note_contents + .write_u64::(self.value.into()) + .unwrap(); // Write g_d - note_contents.extend_from_slice(&self.g_d.to_bytes()); + note_contents.extend_from_slice(&self.recipient.g_d().unwrap().to_bytes()); // Write pk_d - note_contents.extend_from_slice(&self.pk_d.to_bytes()); + note_contents.extend_from_slice(&self.recipient.pk_d().to_bytes()); assert_eq!(note_contents.len(), 32 + 32 + 32 + 8); @@ -644,6 +704,9 @@ impl Note { .get_u() } + /// Defined in [Zcash Protocol Spec § 4.7.2: Sending Notes (Sapling)][saplingsend]. + /// + /// [saplingsend]: https://zips.z.cash/protocol/protocol.pdf#saplingsend pub fn rcm(&self) -> jubjub::Fr { match self.rseed { Rseed::BeforeZip212(rcm) => rcm, @@ -653,6 +716,8 @@ impl Note { } } + /// Derives `esk` from the internal `Rseed` value, or generates a random value if this + /// note was created with a v1 (i.e. pre-ZIP 212) note plaintext. pub fn generate_or_derive_esk(&self, rng: &mut R) -> jubjub::Fr { self.generate_or_derive_esk_internal(rng) } @@ -688,11 +753,11 @@ impl BorshSerialize for Note { // Write asset type self.asset_type.serialize(writer)?; // Write note value - writer.write_u64::(self.value)?; - // Write diversified base - writer.write_all(&self.g_d.to_bytes())?; + writer.write_u64::(self.value().inner())?; + // Write diversifier + writer.write_all(&self.recipient().diversifier().0)?; // Write diversified transmission key - writer.write_all(&self.pk_d.to_bytes())?; + writer.write_all(&self.recipient().pk_d().to_bytes())?; match self.rseed { Rseed::BeforeZip212(rcm) => { // Write note plaintext lead byte @@ -717,9 +782,10 @@ impl BorshDeserialize for Note { let asset_type = AssetType::deserialize(buf)?; // Read note value let value = buf.read_u64::()?; - // Read diversified base - let g_d_bytes = <[u8; 32]>::deserialize(buf)?; - let g_d = Option::from(jubjub::SubgroupPoint::from_bytes(&g_d_bytes)) + // Read diversifier + let diversifier = Diversifier(<[u8; 11]>::deserialize(buf)?); + diversifier + .g_d() .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "g_d not in field"))?; // Read diversified transmission key let pk_d_bytes = <[u8; 32]>::deserialize(buf)?; @@ -739,9 +805,8 @@ impl BorshDeserialize for Note { // Finally construct note object Ok(Note { asset_type, - value, - g_d, - pk_d, + value: NoteValue::from_raw(value), + recipient: PaymentAddress::from_parts(diversifier, pk_d).unwrap(), rseed, }) } @@ -799,13 +864,12 @@ pub mod testing { prop_compose! { pub fn arb_note(value: NoteValue)( asset_type in crate::asset_type::testing::arb_asset_type(), - addr in arb_payment_address(), + recipient in arb_payment_address(), rseed in prop::array::uniform32(prop::num::u8::ANY).prop_map(Rseed::AfterZip212) ) -> Note { Note { value: value.into(), - g_d: addr.g_d().unwrap(), // this unwrap is safe because arb_payment_address always generates an address with a valid g_d - pk_d: *addr.pk_d(), + recipient, rseed, asset_type } diff --git a/masp_primitives/src/sapling/note_encryption.rs b/masp_primitives/src/sapling/note_encryption.rs index fa3f5fe8..7e67a388 100644 --- a/masp_primitives/src/sapling/note_encryption.rs +++ b/masp_primitives/src/sapling/note_encryption.rs @@ -143,7 +143,7 @@ where let pk_d = get_validated_pk_d(&diversifier)?; let to = PaymentAddress::from_parts(diversifier, pk_d)?; - let note = to.create_note(asset_type, value, rseed)?; + let note = to.create_note(asset_type, value.into(), rseed); Some((note, to)) } @@ -175,9 +175,9 @@ impl SaplingDomain

{ impl Domain for SaplingDomain

{ type EphemeralSecretKey = jubjub::Scalar; - // It is acceptable for this to be a point because we enforce by consensus that - // points must not be small-order, and all points with non-canonical serialization - // are small-order. + // It is acceptable for this to be a point rather than a byte array, because we + // enforce by consensus that 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; @@ -197,7 +197,7 @@ impl Domain for SaplingDomain

{ } fn get_pk_d(note: &Self::Note) -> Self::DiversifiedTransmissionKey { - note.pk_d + *note.recipient().pk_d() } fn prepare_epk(epk: Self::EphemeralPublicKey) -> Self::PreparedEphemeralPublicKey { @@ -213,7 +213,7 @@ impl Domain for SaplingDomain

{ // for efficiency of encryption. The conversion here is fine // because the output of this function is only used for // encoding and the byte encoding is unaffected by the conversion. - (note.g_d * esk).into() + (note.recipient().g_d().unwrap() * esk).into() } fn ka_agree_enc( @@ -237,11 +237,7 @@ impl Domain for SaplingDomain

{ kdf_sapling(dhsecret, epk) } - fn note_plaintext_bytes( - note: &Self::Note, - to: &Self::Recipient, - memo: &Self::Memo, - ) -> NotePlaintextBytes { + fn note_plaintext_bytes(note: &Self::Note, memo: &Self::Memo) -> NotePlaintextBytes { // Note plaintext encoding is defined in section 5.5 of the Zcash Protocol // Specification. let mut input = [0; NOTE_PLAINTEXT_SIZE]; @@ -249,9 +245,9 @@ impl Domain for SaplingDomain

{ Rseed::BeforeZip212(_) => 1, Rseed::AfterZip212(_) => 2, }; - input[1..12].copy_from_slice(&to.diversifier().0); + input[1..12].copy_from_slice(¬e.recipient().diversifier().0); (&mut input[12..20]) - .write_u64::(note.value) + .write_u64::(note.value().inner()) .unwrap(); input[20..52].copy_from_slice(note.asset_type.get_identifier()); @@ -283,7 +279,7 @@ impl Domain for SaplingDomain

{ esk: &Self::EphemeralSecretKey, ) -> OutPlaintextBytes { let mut input = [0u8; OUT_PLAINTEXT_SIZE]; - input[0..32].copy_from_slice(¬e.pk_d.to_bytes()); + input[0..32].copy_from_slice(¬e.recipient().pk_d.to_bytes()); input[32..OUT_PLAINTEXT_SIZE].copy_from_slice(esk.to_repr().as_ref()); OutPlaintextBytes(input) @@ -313,16 +309,10 @@ impl Domain for SaplingDomain

{ fn parse_note_plaintext_without_memo_ovk( &self, pk_d: &Self::DiversifiedTransmissionKey, - esk: &Self::EphemeralSecretKey, - ephemeral_key: &EphemeralKeyBytes, plaintext: &NotePlaintextBytes, ) -> Option<(Self::Note, Self::Recipient)> { sapling_parse_note_plaintext_without_memo(self, &plaintext.0, |diversifier| { - if (diversifier.g_d()? * esk).to_bytes() == ephemeral_key.0 { - Some(*pk_d) - } else { - None - } + diversifier.g_d().map(|_| *pk_d) }) } @@ -440,20 +430,19 @@ impl BatchDomain for SaplingDomain

{ /// /// let height = TEST_NETWORK.activation_height(NetworkUpgrade::MASP).unwrap(); /// let rseed = generate_random_rseed(&TEST_NETWORK, height, &mut rng); -/// let note = to.create_note(asset_type, value, rseed).unwrap(); +/// let note = to.create_note(asset_type, value, rseed); /// let cmu = note.cmu(); /// -/// let mut enc = sapling_note_encryption::(ovk, note, to, MemoBytes::empty()); +/// let mut enc = sapling_note_encryption::(ovk, note, MemoBytes::empty()); /// let encCiphertext = enc.encrypt_note_plaintext(); /// let outCiphertext = enc.encrypt_outgoing_plaintext(&cv.commitment().into(), &cmu, &mut rng); /// ``` pub fn sapling_note_encryption( ovk: Option, note: Note, - to: PaymentAddress, memo: MemoBytes, ) -> NoteEncryption> { - NoteEncryption::new(ovk, note, to, memo) + NoteEncryption::new(ovk, note, memo) } #[allow(clippy::if_same_then_else)] @@ -652,11 +641,11 @@ mod tests { let rseed = generate_random_rseed(&TEST_NETWORK, height, &mut rng); - let note = pa.create_note(asset_type, value, rseed).unwrap(); + let note = pa.create_note(asset_type, value.into(), rseed); let cmu = note.cmu(); let ovk = OutgoingViewingKey([0; 32]); - let ne = sapling_note_encryption::(Some(ovk), note, pa, MemoBytes::empty()); + let ne = sapling_note_encryption::(Some(ovk), note, MemoBytes::empty()); let epk = *ne.epk(); let ock = prf_ock(&ovk, &cv, &cmu.to_repr(), &epk_bytes(&epk)); @@ -1359,7 +1348,7 @@ mod tests { let to = PaymentAddress::from_parts(Diversifier(tv.default_d), pk_d).unwrap(); let note = to .create_note(asset_type, tv.v, Rseed::BeforeZip212(rcm)) - .unwrap(); + ; assert_eq!(note.cmu(), cmu); let output = OutputDescription { @@ -1457,7 +1446,6 @@ mod tests { esk, Some(ovk), note, - to, MemoBytes::from_bytes(&tv.memo).unwrap(), ); diff --git a/masp_primitives/src/transaction/builder.rs b/masp_primitives/src/transaction/builder.rs index fc65dcfd..69678b91 100644 --- a/masp_primitives/src/transaction/builder.rs +++ b/masp_primitives/src/transaction/builder.rs @@ -15,7 +15,7 @@ use crate::{ keys::OutgoingViewingKey, memo::MemoBytes, merkle_tree::MerklePath, - sapling::{prover::TxProver, Diversifier, Node, Note, PaymentAddress}, + sapling::{prover::TxProver, Diversifier, Node,NoteValue, Note, PaymentAddress}, transaction::{ components::{ amount::{BalanceError, I128Sum, U64Sum, ValueSum, MAX_MONEY}, @@ -254,7 +254,7 @@ impl Builder { return Err(sapling::builder::Error::InvalidAmount); } self.sapling_builder - .add_output(&mut self.rng, ovk, to, asset_type, value, memo) + .add_output(&mut self.rng, ovk, to, asset_type, NoteValue::from_raw(value.into()), memo) } /// Adds a transparent coin to be spent in this transaction. @@ -535,7 +535,7 @@ mod tests { 50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)), ) - .unwrap(); + ; let cmu1 = note1.commitment(); let mut tree = CommitmentTree::empty(); tree.append(cmu1).unwrap(); @@ -628,7 +628,7 @@ mod tests { 50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)), ) - .unwrap(); + ; let cmu1 = note1.commitment(); let mut tree = CommitmentTree::empty(); tree.append(cmu1).unwrap(); @@ -657,7 +657,7 @@ mod tests { let note2 = to .create_note(zec(), 1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng))) - .unwrap(); + ; let cmu2 = note2.commitment(); tree.append(cmu2).unwrap(); witness1.append(cmu2).unwrap(); diff --git a/masp_primitives/src/transaction/components/sapling/builder.rs b/masp_primitives/src/transaction/components/sapling/builder.rs index 9d5594ca..8ff66390 100644 --- a/masp_primitives/src/transaction/components/sapling/builder.rs +++ b/masp_primitives/src/transaction/components/sapling/builder.rs @@ -20,7 +20,7 @@ use crate::{ redjubjub::{PrivateKey, Signature}, spend_sig_internal, util::generate_random_rseed_internal, - Diversifier, Node, Note, PaymentAddress, + Diversifier, Node, Note, NoteValue, PaymentAddress, }, transaction::{ builder::Progress, @@ -110,7 +110,7 @@ impl fees::InputView<(), K> for SpendDescriptionInfo { } fn value(&self) -> u64 { - self.note.value + self.note.value().inner() } fn asset_type(&self) -> AssetType { @@ -142,23 +142,16 @@ impl SaplingOutputInfo { ovk: Option, to: PaymentAddress, asset_type: AssetType, - value: u64, + value: NoteValue, memo: MemoBytes, ) -> Result { - let g_d = to.g_d().ok_or(Error::InvalidAddress)?; - if value > MAX_MONEY { + if value.inner() > MAX_MONEY { return Err(Error::InvalidAmount); } let rseed = generate_random_rseed_internal(params, target_height, rng); - let note = Note { - g_d, - pk_d: *to.pk_d(), - value, - rseed, - asset_type, - }; + let note = Note::from_parts(asset_type, to, value, rseed); Ok(SaplingOutputInfo { ovk, @@ -174,7 +167,7 @@ impl SaplingOutputInfo { ctx: &mut Pr::SaplingProvingContext, rng: &mut R, ) -> OutputDescription { - let encryptor = sapling_note_encryption::

(self.ovk, self.note, self.to, self.memo); + let encryptor = sapling_note_encryption::

(self.ovk, self.note.clone(), self.memo); let (zkproof, cv) = prover.output_proof( ctx, @@ -182,7 +175,7 @@ impl SaplingOutputInfo { self.to, self.note.rcm(), self.note.asset_type, - self.note.value, + self.note.value().inner(), ); let cmu = self.note.cmu(); @@ -205,7 +198,7 @@ impl SaplingOutputInfo { impl fees::OutputView for SaplingOutputInfo { fn value(&self) -> u64 { - self.note.value + self.note.value().inner() } fn asset_type(&self) -> AssetType { @@ -457,7 +450,7 @@ impl SaplingBuilder

{ ovk: Option, to: PaymentAddress, asset_type: AssetType, - value: u64, + value: NoteValue, memo: MemoBytes, ) -> Result<(), Error> { let output = SaplingOutputInfo::new_internal( @@ -549,7 +542,7 @@ impl SaplingBuilder

{ spend.note.rseed, spend.alpha, spend.note.asset_type, - spend.note.value, + spend.note.value().inner(), anchor, spend.merkle_path.clone(), ) @@ -652,11 +645,11 @@ impl SaplingBuilder

{ } (diversifier, g_d) }; - let (pk_d, payment_address) = loop { + let payment_address = loop { let dummy_ivk = jubjub::Fr::random(&mut rng); let pk_d = g_d * dummy_ivk; if let Some(addr) = PaymentAddress::from_parts(diversifier, pk_d) { - break (pk_d, addr); + break addr; } }; @@ -665,18 +658,21 @@ impl SaplingBuilder

{ ( payment_address, - Note { - g_d, - pk_d, + Note::from_parts( + AssetType::new(b"dummy").unwrap(), + payment_address, + NoteValue::from_raw(0), rseed, - value: 0, - asset_type: AssetType::new(b"dummy").unwrap(), - }, + ), ) }; let esk = dummy_note.generate_or_derive_esk_internal(&mut rng); - let epk = dummy_note.g_d * esk; + let epk = dummy_note + .recipient() + .g_d() + .expect("checked at construction") + * esk; let (zkproof, cv) = prover.output_proof( ctx, @@ -684,7 +680,7 @@ impl SaplingBuilder

{ dummy_to, dummy_note.rcm(), dummy_note.asset_type, - dummy_note.value, + dummy_note.value().inner(), ); let cmu = dummy_note.cmu(); diff --git a/masp_proofs/src/circuit/sapling.rs b/masp_proofs/src/circuit/sapling.rs index 95e2d669..ef9a7ed2 100644 --- a/masp_proofs/src/circuit/sapling.rs +++ b/masp_proofs/src/circuit/sapling.rs @@ -18,6 +18,9 @@ use crate::constants::{ use bellman::gadgets::{blake2s, boolean, multipack, num, Assignment}; use itertools::multizip; +#[cfg(test)] +use masp_primitives::sapling::NoteValue; + pub const TREE_DEPTH: usize = SAPLING_COMMITMENT_TREE_DEPTH; /// This is an instance of the `Spend` circuit. @@ -647,7 +650,6 @@ fn test_input_circuit_with_bls12_381() { } } - let g_d = payment_address.diversifier().g_d().unwrap(); let commitment_randomness = jubjub::Fr::random(&mut rng); let auth_path = vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth]; @@ -657,13 +659,12 @@ fn test_input_circuit_with_bls12_381() { let rk = jubjub::ExtendedPoint::from(viewing_key.rk(ar)).to_affine(); let expected_value_commitment = jubjub::ExtendedPoint::from(value_commitment.commitment()).to_affine(); - let note = Note { + let note = Note ::from_parts( asset_type, - value: value_commitment.value, - g_d, - pk_d: *payment_address.pk_d(), - rseed: Rseed::BeforeZip212(commitment_randomness), - }; + payment_address, + NoteValue::from_raw(value_commitment.value), + Rseed::BeforeZip212(commitment_randomness), + ); let mut position = 0u64; let cmu = note.cmu(); @@ -763,14 +764,13 @@ fn test_input_circuit_with_bls12_381() { fn test_input_circuit_with_bls12_381_external_test_vectors() { use bellman::gadgets::test::*; use group::{ff::Field, ff::PrimeField, ff::PrimeFieldBits, Group}; + use rand_core::{RngCore, SeedableRng}; + use rand_xorshift::XorShiftRng; use masp_primitives::{ asset_type::AssetType, sapling::pedersen_hash, sapling::{Diversifier, Note, ProofGenerationKey, Rseed}, }; - use rand_core::{RngCore, SeedableRng}; - use rand_xorshift::XorShiftRng; - let mut rng = XorShiftRng::from_seed([ 0x59, 0x62, 0xbe, 0x3d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 0xe5, @@ -836,7 +836,6 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { } } - let g_d = payment_address.diversifier().g_d().unwrap(); let commitment_randomness = jubjub::Fr::random(&mut rng); let auth_path = vec![Some((bls12_381::Scalar::random(&mut rng), rng.next_u32() % 2 != 0)); tree_depth]; @@ -854,13 +853,12 @@ fn test_input_circuit_with_bls12_381_external_test_vectors() { expected_value_commitment.get_v(), bls12_381::Scalar::from_str_vartime(expected_commitment_vs[i as usize]).unwrap() ); - let note = Note { + let note = Note::from_parts( asset_type, - value: value_commitment.value, - g_d, - pk_d: *payment_address.pk_d(), - rseed: Rseed::BeforeZip212(commitment_randomness), - }; + payment_address, + NoteValue::from_raw(value_commitment.value), + Rseed::BeforeZip212(commitment_randomness), + ); let mut position = 0u64; let cmu = note.cmu(); @@ -1030,7 +1028,7 @@ fn test_output_circuit_with_bls12_381() { value_commitment.value, Rseed::BeforeZip212(commitment_randomness), ) - .expect("should be valid") + .cmu(); let expected_value_commitment = diff --git a/masp_proofs/src/sapling/prover.rs b/masp_proofs/src/sapling/prover.rs index 8c13348a..b3020be3 100644 --- a/masp_proofs/src/sapling/prover.rs +++ b/masp_proofs/src/sapling/prover.rs @@ -11,7 +11,7 @@ use masp_primitives::{ merkle_tree::MerklePath, sapling::{ redjubjub::{PrivateKey, PublicKey, Signature}, - Diversifier, Node, Note, PaymentAddress, ProofGenerationKey, Rseed, + Diversifier, Node, NoteValue, Note, PaymentAddress, ProofGenerationKey, Rseed, }, transaction::components::I128Sum, }; @@ -89,13 +89,7 @@ impl SaplingProvingContext { let rk = PublicKey(proof_generation_key.ak.into()).randomize(ar, SPENDING_KEY_GENERATOR); // Let's compute the nullifier while we have the position - let note = Note { - asset_type, - value, - g_d: diversifier.g_d().expect("was a valid diversifier before"), - pk_d: *payment_address.pk_d(), - rseed, - }; + let note = Note::from_parts(asset_type, payment_address, NoteValue::from_raw(value), rseed); let nullifier = note.nf(&viewing_key.nk, merkle_path.position);