diff --git a/zebra-chain/src/commitments/sapling.rs b/zebra-chain/src/commitments/sapling.rs index 0efe5c97..a1d3f1dc 100644 --- a/zebra-chain/src/commitments/sapling.rs +++ b/zebra-chain/src/commitments/sapling.rs @@ -12,15 +12,28 @@ use rand_core::{CryptoRng, RngCore}; use crate::{ keys::sapling::{find_group_hash, Diversifier, TransmissionKey}, - notes::sapling::Note, serde_helpers, serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, types::amount::{Amount, NonNegative}, }; -// TODO: replace with reference to redjubjub or jubjub when merged and -// exported. -type Scalar = jubjub::Fr; +/// Generates a random scalar from the scalar field \mathbb{F}_r_𝕁. +/// +/// The prime order subgroup 𝕁^(r) is the order-r_𝕁 subgroup of 𝕁 +/// after the Edwards cofactor h_𝕁 = 8 is factored out. This function +/// is useful when generating the uniform distribution on +/// \mathbb{F}_r_𝕁 needed for Sapling commitment schemes' trapdoor +/// generators. +/// +/// https://zips.z.cash/protocol/protocol.pdf#jubjub +pub fn generate_trapdoor(csprng: &mut T) -> jubjub::Fr +where + T: RngCore + CryptoRng, +{ + let mut bytes = [0u8; 32]; + csprng.fill_bytes(&mut bytes); + jubjub::Fr::from_bytes(&bytes).unwrap() +} /// "...an algebraic hash function with collision resistance (for /// fixed input length) derived from assumed hardness of the Discrete @@ -40,7 +53,7 @@ pub fn pedersen_hash_to_point(domain: [u8; 8], M: &BitVec) -> jubjub:: } // ⟨Mᵢ⟩ - fn M_i(segment: &BitSlice) -> Scalar { + fn M_i(segment: &BitSlice) -> jubjub::Fr { let mut m_i = [0u8; 32]; for (j, chunk) in segment.chunks(3).enumerate() { @@ -53,7 +66,7 @@ pub fn pedersen_hash_to_point(domain: [u8; 8], M: &BitVec) -> jubjub:: m_i[0] += enc_m_j * (1 << (4 * j)) } - Scalar::from_bytes(&m_i).unwrap() + jubjub::Fr::from_bytes(&m_i).unwrap() } let mut result = jubjub::ExtendedPoint::identity(); @@ -77,6 +90,22 @@ pub fn pedersen_hash(domain: [u8; 8], M: &BitVec) -> jubjub::Fq { jubjub::AffinePoint::from(pedersen_hash_to_point(domain, M)).get_u() } +/// Mixing Pedersen Hash Function +/// +/// Used to compute ρ from a note commitment and its position in the +/// note commitment tree. It takes as input a Pedersen commitment P, +/// and hashes it with another input x. +/// +/// MixingPedersenHash(P, x) := P + [x]FindGroupHash^J^(r)(“Zcash_J_”, “”) +/// +/// https://zips.z.cash/protocol/protocol.pdf#concretemixinghash +#[allow(non_snake_case)] +pub fn mixing_pedersen_hash(P: jubjub::ExtendedPoint, x: jubjub::Fr) -> jubjub::ExtendedPoint { + const J: [u8; 8] = *b"Zcash_J_"; + + P + find_group_hash(J, b"") * x +} + /// Construct a 'windowed' Pedersen commitment by reusing a Perderson /// hash constructon, and adding a randomized point on the Jubjub /// curve. @@ -85,22 +114,18 @@ pub fn pedersen_hash(domain: [u8; 8], M: &BitVec) -> jubjub::Fq { /// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)(“Zcash_PH”, “r”) /// /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit -pub fn windowed_pedersen_commitment_r( - rcm: CommitmentRandomness, - s: &BitVec, -) -> jubjub::ExtendedPoint { +pub fn windowed_pedersen_commitment(r: jubjub::Fr, s: &BitVec) -> jubjub::ExtendedPoint { const D: [u8; 8] = *b"Zcash_PH"; - pedersen_hash_to_point(D, &s) + find_group_hash(D, b"r") * rcm.0 + pedersen_hash_to_point(D, &s) + find_group_hash(D, b"r") * r } /// The randomness used in the Pedersen Hash for note commitment. #[derive(Copy, Clone, Debug, PartialEq)] -pub struct CommitmentRandomness(redjubjub::Randomizer); +pub struct CommitmentRandomness(jubjub::Fr); /// Note commitments for the output notes. #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] -//#[cfg_attr(test, derive(proptest_derive::Arbitrary))] pub struct NoteCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); impl fmt::Debug for NoteCommitment { @@ -130,28 +155,6 @@ impl From for [u8; 32] { } } -impl From for NoteCommitment { - /// Construct a “windowed” Pedersen commitment by reusing a - /// Perderson hash constructon, and adding a randomized point on - /// the Jubjub curve. - /// - /// WindowedPedersenCommit_r (s) := \ - /// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)∗(“Zcash_PH”, “r”) - /// - /// NoteCommit^Sapling_rcm (g*_d , pk*_d , v) := \ - /// WindowedPedersenCommit_rcm([1; 6] || I2LEBSP_64(v) || g*_d || pk*_d) - /// - /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit - fn from(note: Note) -> NoteCommitment { - NoteCommitment::new( - note.rcm, - note.diversifier, - note.transmission_key, - note.value, - ) - } -} - impl Eq for NoteCommitment {} impl ZcashSerialize for NoteCommitment { @@ -170,19 +173,27 @@ impl ZcashDeserialize for NoteCommitment { } impl NoteCommitment { - /// Generate a new _NoteCommitment_. + /// Generate a new _NoteCommitment_ and the randomness used to + /// create it. + /// + /// We return the randomness because it is needed to construct a + /// _Note_, before it is encrypted as part of an _Output + /// Description_. /// /// NoteCommit^Sapling_rcm (g*_d , pk*_d , v) := \ /// WindowedPedersenCommit_rcm([1; 6] || I2LEBSP_64(v) || g*_d || pk*_d) /// /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit #[allow(non_snake_case)] - pub fn new( - rcm: CommitmentRandomness, + pub fn new( + csprng: &mut T, diversifier: Diversifier, transmission_key: TransmissionKey, value: Amount, - ) -> Self { + ) -> (CommitmentRandomness, Self) + where + T: RngCore + CryptoRng, + { // s as in the argument name for WindowedPedersenCommit_r(s) let mut s: BitVec = BitVec::new(); @@ -199,13 +210,18 @@ impl NoteCommitment { s.append(&mut BitVec::::from_slice(&pk_d_bytes[..])); s.append(&mut BitVec::::from_slice(&v_bytes[..])); - Self::from(windowed_pedersen_commitment_r(rcm, &s)) + let rcm = CommitmentRandomness(generate_trapdoor(csprng)); + + ( + rcm, + NoteCommitment::from(windowed_pedersen_commitment(rcm.0, &s)), + ) } /// Hash Extractor for Jubjub (?) /// /// https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub - pub fn extract_u(self) -> jubjub::Fq { + pub fn extract_u(&self) -> jubjub::Fq { self.0.get_u() } } @@ -215,7 +231,6 @@ impl NoteCommitment { /// /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit #[derive(Clone, Deserialize, PartialEq, Serialize)] -//#[cfg_attr(test, derive(proptest_derive::Arbitrary))] pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); impl fmt::Debug for ValueCommitment { @@ -276,11 +291,8 @@ impl ValueCommitment { where T: RngCore + CryptoRng, { - let v = Scalar::from_bytes(&value_bytes).unwrap(); - - let mut rcv_bytes = [0u8; 32]; - csprng.fill_bytes(&mut rcv_bytes); - let rcv = Scalar::from_bytes(&rcv_bytes).unwrap(); + let v = jubjub::Fr::from_bytes(&value_bytes).unwrap(); + let rcv = generate_trapdoor(csprng); let V = find_group_hash(*b"Zcash_cv", b"v"); let R = find_group_hash(*b"Zcash_cv", b"r");