2020-07-15 01:50:05 -07:00
|
|
|
|
#![allow(clippy::unit_arg)]
|
|
|
|
|
#![allow(dead_code)]
|
|
|
|
|
|
2020-08-15 18:02:07 -07:00
|
|
|
|
use super::super::{
|
|
|
|
|
commitment::{pedersen_hashes::mixing_pedersen_hash, NoteCommitment},
|
|
|
|
|
keys::NullifierDerivingKey,
|
|
|
|
|
tree::Position,
|
2020-07-27 23:31:07 -07:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/// Invokes Blake2s-256 as PRF^nfSapling to derive the nullifier for a
|
|
|
|
|
/// Sapling note.
|
|
|
|
|
///
|
|
|
|
|
/// PRF^nfSapling(ρ*) := BLAKE2s-256("Zcash_nf", nk* || ρ*)
|
|
|
|
|
///
|
|
|
|
|
/// https://zips.z.cash/protocol/protocol.pdf#concreteprfs
|
|
|
|
|
fn prf_nf(nk: [u8; 32], rho: [u8; 32]) -> [u8; 32] {
|
|
|
|
|
let hash = blake2s_simd::Params::new()
|
|
|
|
|
.hash_length(32)
|
|
|
|
|
.personal(b"Zcash_nf")
|
|
|
|
|
.to_state()
|
|
|
|
|
.update(&nk[..])
|
|
|
|
|
.update(&rho[..])
|
|
|
|
|
.finalize();
|
|
|
|
|
|
|
|
|
|
*hash.as_array()
|
|
|
|
|
}
|
2020-07-15 01:50:05 -07:00
|
|
|
|
|
|
|
|
|
/// A Nullifier for Sapling transactions
|
2020-09-24 15:46:04 -07:00
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
|
2020-09-23 18:52:52 -07:00
|
|
|
|
#[cfg_attr(
|
|
|
|
|
any(test, feature = "proptest-impl"),
|
|
|
|
|
derive(proptest_derive::Arbitrary)
|
|
|
|
|
)]
|
2021-03-20 10:08:39 -07:00
|
|
|
|
pub struct Nullifier(pub [u8; 32]);
|
2020-07-15 01:50:05 -07:00
|
|
|
|
|
|
|
|
|
impl From<[u8; 32]> for Nullifier {
|
|
|
|
|
fn from(buf: [u8; 32]) -> Self {
|
|
|
|
|
Self(buf)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-07 01:30:57 -07:00
|
|
|
|
impl<'a> From<(NoteCommitment, Position, &'a NullifierDerivingKey)> for Nullifier {
|
|
|
|
|
fn from((cm, pos, nk): (NoteCommitment, Position, &'a NullifierDerivingKey)) -> Self {
|
2020-07-27 23:31:07 -07:00
|
|
|
|
let rho = jubjub::AffinePoint::from(mixing_pedersen_hash(cm.0.into(), pos.0.into()));
|
|
|
|
|
|
|
|
|
|
Nullifier(prf_nf(nk.into(), rho.to_bytes()))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-09 15:42:19 -07:00
|
|
|
|
impl From<Nullifier> for [u8; 32] {
|
|
|
|
|
fn from(n: Nullifier) -> Self {
|
|
|
|
|
n.0
|
2020-07-15 01:50:05 -07:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-24 09:28:25 -07:00
|
|
|
|
|
|
|
|
|
impl From<Nullifier> for [jubjub::Fq; 2] {
|
|
|
|
|
/// Add the nullifier through multiscalar packing
|
|
|
|
|
///
|
|
|
|
|
/// Informed by https://github.com/zkcrypto/bellman/blob/main/src/gadgets/multipack.rs
|
|
|
|
|
fn from(n: Nullifier) -> Self {
|
|
|
|
|
use std::ops::AddAssign;
|
|
|
|
|
|
|
|
|
|
let nullifier_bits_le: Vec<bool> =
|
|
|
|
|
n.0.iter()
|
|
|
|
|
.flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1))
|
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
|
|
// The number of bits needed to represent the modulus, minus 1.
|
|
|
|
|
const CAPACITY: usize = 255 - 1;
|
|
|
|
|
|
|
|
|
|
let mut result = [jubjub::Fq::zero(); 2];
|
|
|
|
|
|
|
|
|
|
// Since we know the max bits of the input (256) and the chunk size
|
|
|
|
|
// (254), this will always result in 2 chunks.
|
|
|
|
|
for (i, bits) in nullifier_bits_le.chunks(CAPACITY).enumerate() {
|
|
|
|
|
let mut cur = jubjub::Fq::zero();
|
|
|
|
|
let mut coeff = jubjub::Fq::one();
|
|
|
|
|
|
|
|
|
|
for bit in bits {
|
|
|
|
|
if *bit {
|
|
|
|
|
cur.add_assign(&coeff);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
coeff = coeff.double();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result[i] = cur
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
}
|