Add mixing_pedersen_hash
Tidy constructors for NoteCommitment
This commit is contained in:
parent
ed888b38ef
commit
65f8f2c613
|
|
@ -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");
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue