Add mixing_pedersen_hash

Tidy constructors for NoteCommitment
This commit is contained in:
Deirdre Connolly 2020-07-28 01:34:38 -04:00 committed by Deirdre Connolly
parent ed888b38ef
commit 65f8f2c613
1 changed files with 59 additions and 47 deletions

View File

@ -12,15 +12,28 @@ use rand_core::{CryptoRng, RngCore};
use crate::{ use crate::{
keys::sapling::{find_group_hash, Diversifier, TransmissionKey}, keys::sapling::{find_group_hash, Diversifier, TransmissionKey},
notes::sapling::Note,
serde_helpers, serde_helpers,
serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize}, serialization::{ReadZcashExt, SerializationError, ZcashDeserialize, ZcashSerialize},
types::amount::{Amount, NonNegative}, types::amount::{Amount, NonNegative},
}; };
// TODO: replace with reference to redjubjub or jubjub when merged and /// Generates a random scalar from the scalar field \mathbb{F}_r_𝕁.
// exported. ///
type Scalar = jubjub::Fr; /// 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<T>(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 /// "...an algebraic hash function with collision resistance (for
/// fixed input length) derived from assumed hardness of the Discrete /// 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<Lsb0, u8>) -> jubjub::
} }
// ⟨Mᵢ⟩ // ⟨Mᵢ⟩
fn M_i(segment: &BitSlice<Lsb0, u8>) -> Scalar { fn M_i(segment: &BitSlice<Lsb0, u8>) -> jubjub::Fr {
let mut m_i = [0u8; 32]; let mut m_i = [0u8; 32];
for (j, chunk) in segment.chunks(3).enumerate() { for (j, chunk) in segment.chunks(3).enumerate() {
@ -53,7 +66,7 @@ pub fn pedersen_hash_to_point(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> jubjub::
m_i[0] += enc_m_j * (1 << (4 * j)) 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(); let mut result = jubjub::ExtendedPoint::identity();
@ -77,6 +90,22 @@ pub fn pedersen_hash(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> jubjub::Fq {
jubjub::AffinePoint::from(pedersen_hash_to_point(domain, M)).get_u() 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 /// Construct a 'windowed' Pedersen commitment by reusing a Perderson
/// hash constructon, and adding a randomized point on the Jubjub /// hash constructon, and adding a randomized point on the Jubjub
/// curve. /// curve.
@ -85,22 +114,18 @@ pub fn pedersen_hash(domain: [u8; 8], M: &BitVec<Lsb0, u8>) -> jubjub::Fq {
/// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)(“Zcash_PH”, “r”) /// PedersenHashToPoint(“Zcash_PH”, s) + [r]FindGroupHash^J^(r)(“Zcash_PH”, “r”)
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
pub fn windowed_pedersen_commitment_r( pub fn windowed_pedersen_commitment(r: jubjub::Fr, s: &BitVec<Lsb0, u8>) -> jubjub::ExtendedPoint {
rcm: CommitmentRandomness,
s: &BitVec<Lsb0, u8>,
) -> jubjub::ExtendedPoint {
const D: [u8; 8] = *b"Zcash_PH"; 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. /// The randomness used in the Pedersen Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct CommitmentRandomness(redjubjub::Randomizer); pub struct CommitmentRandomness(jubjub::Fr);
/// Note commitments for the output notes. /// Note commitments for the output notes.
#[derive(Clone, Copy, Deserialize, PartialEq, Serialize)] #[derive(Clone, Copy, Deserialize, PartialEq, Serialize)]
//#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct NoteCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); pub struct NoteCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint);
impl fmt::Debug for NoteCommitment { impl fmt::Debug for NoteCommitment {
@ -130,28 +155,6 @@ impl From<NoteCommitment> for [u8; 32] {
} }
} }
impl From<Note> 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 Eq for NoteCommitment {}
impl ZcashSerialize for NoteCommitment { impl ZcashSerialize for NoteCommitment {
@ -170,19 +173,27 @@ impl ZcashDeserialize for NoteCommitment {
} }
impl 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) := \ /// NoteCommit^Sapling_rcm (g*_d , pk*_d , v) := \
/// WindowedPedersenCommit_rcm([1; 6] || I2LEBSP_64(v) || g*_d || pk*_d) /// WindowedPedersenCommit_rcm([1; 6] || I2LEBSP_64(v) || g*_d || pk*_d)
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit /// https://zips.z.cash/protocol/protocol.pdf#concretewindowedcommit
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub fn new( pub fn new<T>(
rcm: CommitmentRandomness, csprng: &mut T,
diversifier: Diversifier, diversifier: Diversifier,
transmission_key: TransmissionKey, transmission_key: TransmissionKey,
value: Amount<NonNegative>, value: Amount<NonNegative>,
) -> Self { ) -> (CommitmentRandomness, Self)
where
T: RngCore + CryptoRng,
{
// s as in the argument name for WindowedPedersenCommit_r(s) // s as in the argument name for WindowedPedersenCommit_r(s)
let mut s: BitVec<Lsb0, u8> = BitVec::new(); let mut s: BitVec<Lsb0, u8> = BitVec::new();
@ -199,13 +210,18 @@ impl NoteCommitment {
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..])); s.append(&mut BitVec::<Lsb0, u8>::from_slice(&pk_d_bytes[..]));
s.append(&mut BitVec::<Lsb0, u8>::from_slice(&v_bytes[..])); s.append(&mut BitVec::<Lsb0, u8>::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 (?) /// Hash Extractor for Jubjub (?)
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concreteextractorjubjub /// 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() self.0.get_u()
} }
} }
@ -215,7 +231,6 @@ impl NoteCommitment {
/// ///
/// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit /// https://zips.z.cash/protocol/protocol.pdf#concretehomomorphiccommit
#[derive(Clone, Deserialize, PartialEq, Serialize)] #[derive(Clone, Deserialize, PartialEq, Serialize)]
//#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint); pub struct ValueCommitment(#[serde(with = "serde_helpers::AffinePoint")] pub jubjub::AffinePoint);
impl fmt::Debug for ValueCommitment { impl fmt::Debug for ValueCommitment {
@ -276,11 +291,8 @@ impl ValueCommitment {
where where
T: RngCore + CryptoRng, T: RngCore + CryptoRng,
{ {
let v = Scalar::from_bytes(&value_bytes).unwrap(); let v = jubjub::Fr::from_bytes(&value_bytes).unwrap();
let rcv = generate_trapdoor(csprng);
let mut rcv_bytes = [0u8; 32];
csprng.fill_bytes(&mut rcv_bytes);
let rcv = Scalar::from_bytes(&rcv_bytes).unwrap();
let V = find_group_hash(*b"Zcash_cv", b"v"); let V = find_group_hash(*b"Zcash_cv", b"v");
let R = find_group_hash(*b"Zcash_cv", b"r"); let R = find_group_hash(*b"Zcash_cv", b"r");