2021-07-16 01:42:29 -07:00
|
|
|
use ff::PrimeField;
|
2021-08-30 00:59:23 -07:00
|
|
|
use group::{Curve, GroupEncoding};
|
2021-11-11 17:39:50 -08:00
|
|
|
use jubjub::{ExtendedNielsPoint, ExtendedPoint, Fr, SubgroupPoint};
|
2021-07-16 01:42:29 -07:00
|
|
|
use lazy_static::lazy_static;
|
|
|
|
use std::io::Read;
|
2021-07-11 08:42:52 -07:00
|
|
|
use std::ops::AddAssign;
|
|
|
|
use zcash_params::GENERATORS;
|
2021-07-16 01:42:29 -07:00
|
|
|
use zcash_primitives::constants::PEDERSEN_HASH_CHUNKS_PER_GENERATOR;
|
2021-07-11 08:42:52 -07:00
|
|
|
|
|
|
|
lazy_static! {
|
2022-08-02 07:34:15 -07:00
|
|
|
pub static ref GENERATORS_EXP: Vec<ExtendedNielsPoint> = read_generators_bin();
|
2021-07-11 08:42:52 -07:00
|
|
|
}
|
|
|
|
|
2021-08-30 00:59:23 -07:00
|
|
|
fn read_generators_bin() -> Vec<ExtendedNielsPoint> {
|
2021-07-11 08:42:52 -07:00
|
|
|
let mut generators_bin = GENERATORS;
|
2021-08-30 00:59:23 -07:00
|
|
|
let mut gens: Vec<ExtendedNielsPoint> = vec![];
|
2021-07-11 08:42:52 -07:00
|
|
|
gens.reserve_exact(3 * 32 * 256);
|
|
|
|
for _i in 0..3 {
|
|
|
|
for _j in 0..32 {
|
|
|
|
for _k in 0..256 {
|
|
|
|
let mut bb = [0u8; 32];
|
2022-06-08 05:48:16 -07:00
|
|
|
generators_bin.read_exact(&mut bb).unwrap();
|
2021-11-11 17:39:50 -08:00
|
|
|
let p = ExtendedPoint::from(SubgroupPoint::from_bytes_unchecked(&bb).unwrap())
|
|
|
|
.to_niels();
|
2021-07-11 08:42:52 -07:00
|
|
|
gens.push(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gens
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! accumulate_scalar {
|
|
|
|
($acc: ident, $cur: ident, $x: expr) => {
|
|
|
|
let mut tmp = $cur;
|
|
|
|
if $x & 1 != 0 {
|
|
|
|
tmp.add_assign(&$cur);
|
|
|
|
}
|
|
|
|
$cur = $cur.double();
|
|
|
|
if $x & 2 != 0 {
|
|
|
|
tmp.add_assign(&$cur);
|
|
|
|
}
|
|
|
|
if $x & 4 != 0 {
|
|
|
|
tmp = tmp.neg();
|
|
|
|
}
|
|
|
|
|
|
|
|
$acc.add_assign(&tmp);
|
2021-07-16 01:42:29 -07:00
|
|
|
};
|
2021-07-11 08:42:52 -07:00
|
|
|
}
|
|
|
|
|
2022-08-02 07:34:15 -07:00
|
|
|
pub type Hash = [u8; 32];
|
2021-08-30 00:59:23 -07:00
|
|
|
|
|
|
|
pub fn pedersen_hash(depth: u8, left: &Hash, right: &Hash) -> Hash {
|
|
|
|
let p = pedersen_hash_inner(depth, left, right);
|
|
|
|
|
2022-06-08 05:48:16 -07:00
|
|
|
p.to_affine().get_u().to_repr()
|
2021-08-30 00:59:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pedersen_hash_inner(depth: u8, left: &Hash, right: &Hash) -> ExtendedPoint {
|
|
|
|
let mut result = ExtendedPoint::identity();
|
2021-07-11 08:42:52 -07:00
|
|
|
let mut bitoffset = 0;
|
|
|
|
let mut byteoffset = 0;
|
|
|
|
let mut r_byteoffset = 0;
|
|
|
|
|
|
|
|
let mut acc = Fr::zero();
|
|
|
|
let mut cur = Fr::one();
|
|
|
|
|
|
|
|
let a = depth & 7;
|
|
|
|
let b = depth >> 3;
|
|
|
|
accumulate_scalar!(acc, cur, a);
|
|
|
|
cur = cur.double().double().double();
|
|
|
|
// println!("{}", hex::encode(acc.to_bytes()));
|
|
|
|
|
|
|
|
accumulate_scalar!(acc, cur, b);
|
|
|
|
cur = cur.double().double().double();
|
|
|
|
// println!("{}", hex::encode(acc.to_bytes()));
|
|
|
|
|
|
|
|
let mut i_generator = 0;
|
|
|
|
let mut chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR - 3;
|
|
|
|
|
|
|
|
let mut r = (left[0] as u16) | (left[1] as u16) << 8;
|
|
|
|
let x = (r >> bitoffset) & 7;
|
|
|
|
accumulate_scalar!(acc, cur, x);
|
|
|
|
cur = cur.double().double().double();
|
|
|
|
|
|
|
|
for _c in 0..169 {
|
|
|
|
bitoffset += 3;
|
|
|
|
let x = (r >> bitoffset) & 7;
|
|
|
|
accumulate_scalar!(acc, cur, x);
|
|
|
|
if bitoffset >= 8 {
|
|
|
|
bitoffset -= 8;
|
|
|
|
byteoffset += 1;
|
|
|
|
if byteoffset < 31 {
|
|
|
|
r = (r >> 8) | (left[byteoffset + 1] as u16) << 8;
|
2021-07-16 01:42:29 -07:00
|
|
|
} else if byteoffset == 31 {
|
2021-07-11 08:42:52 -07:00
|
|
|
r = ((r >> 7) & 0xFF) | (right[0] as u16) << 8;
|
|
|
|
bitoffset += 1;
|
2021-07-16 01:42:29 -07:00
|
|
|
} else if byteoffset < 63 {
|
2021-07-11 08:42:52 -07:00
|
|
|
r = (r >> 8) | (right[r_byteoffset + 1] as u16) << 8;
|
|
|
|
r_byteoffset += 1;
|
2021-07-16 01:42:29 -07:00
|
|
|
} else if byteoffset == 63 {
|
2022-06-08 05:48:16 -07:00
|
|
|
r >>= 8;
|
2021-07-11 08:42:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
chunks_remaining -= 1;
|
|
|
|
if chunks_remaining == 0 {
|
|
|
|
// println!("++ {}", hex::encode(acc.to_bytes()));
|
|
|
|
|
|
|
|
result += generator_multiplication(&acc, &GENERATORS_EXP, i_generator);
|
|
|
|
|
|
|
|
i_generator += 1;
|
|
|
|
acc = Fr::zero();
|
|
|
|
cur = Fr::one();
|
|
|
|
chunks_remaining = PEDERSEN_HASH_CHUNKS_PER_GENERATOR
|
|
|
|
} else {
|
|
|
|
cur = cur.double().double().double(); // 2^4 * cur
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result += generator_multiplication(&acc, &GENERATORS_EXP, i_generator);
|
2021-08-30 00:59:23 -07:00
|
|
|
result
|
2021-07-11 08:42:52 -07:00
|
|
|
}
|
|
|
|
|
2021-11-11 17:39:50 -08:00
|
|
|
fn generator_multiplication(
|
|
|
|
acc: &Fr,
|
|
|
|
gens: &[ExtendedNielsPoint],
|
|
|
|
i_generator: u32,
|
|
|
|
) -> ExtendedPoint {
|
2021-07-11 08:42:52 -07:00
|
|
|
let acc = acc.to_repr();
|
|
|
|
|
2021-08-30 00:59:23 -07:00
|
|
|
let mut tmp = jubjub::ExtendedPoint::identity();
|
2021-07-11 08:42:52 -07:00
|
|
|
for (i, &j) in acc.iter().enumerate() {
|
2021-07-16 01:42:29 -07:00
|
|
|
let offset = (i_generator * 32 + i as u32) * 256 + j as u32;
|
2021-07-11 08:42:52 -07:00
|
|
|
let x = gens[offset as usize];
|
|
|
|
tmp += x;
|
|
|
|
}
|
|
|
|
tmp
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use crate::hash::pedersen_hash;
|
|
|
|
use rand::{thread_rng, RngCore};
|
2021-07-16 01:42:29 -07:00
|
|
|
use zcash_primitives::merkle_tree::Hashable;
|
|
|
|
use zcash_primitives::sapling::Node;
|
2021-07-11 08:42:52 -07:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_hash() {
|
|
|
|
let mut r = thread_rng();
|
|
|
|
|
2021-08-30 00:59:23 -07:00
|
|
|
for _ in 0..1 {
|
2021-07-11 08:42:52 -07:00
|
|
|
let mut a = [0u8; 32];
|
|
|
|
r.fill_bytes(&mut a);
|
|
|
|
let mut b = [0u8; 32];
|
|
|
|
r.fill_bytes(&mut b);
|
|
|
|
let depth = (r.next_u32() % 64) as u8;
|
|
|
|
let depth = depth.min(62);
|
|
|
|
|
|
|
|
// let sa = "767a9a7e989289efdfa69c4c8e985c31f3c2c0353f20a80f572854206f077f86";
|
|
|
|
// let sb = "944c46945a9e7a0a753850bd90f69d44ac884b60244a9f8eacf3a2aeddd08d6e";
|
|
|
|
// a.copy_from_slice(&hex::decode(sa).unwrap());
|
|
|
|
// b.copy_from_slice(&hex::decode(sb).unwrap());
|
|
|
|
// println!("A: {}", hex::encode(a));
|
|
|
|
// println!("B: {}", hex::encode(b));
|
|
|
|
|
|
|
|
let node1 = Node::new(a);
|
|
|
|
let node2 = Node::new(b);
|
2021-07-16 01:42:29 -07:00
|
|
|
let hash = Node::combine(depth as usize, &node1, &node2);
|
2021-07-11 08:42:52 -07:00
|
|
|
let hash2 = pedersen_hash(depth, &a, &b);
|
|
|
|
// println!("Reference Hash: {}", hex::encode(hash.repr));
|
|
|
|
// println!("This Hash: {}", hex::encode(hash2));
|
|
|
|
// need to expose repr for this check
|
|
|
|
assert_eq!(hash.repr, hash2);
|
|
|
|
}
|
|
|
|
}
|
2021-07-16 01:42:29 -07:00
|
|
|
}
|