2021-07-16 01:42:29 -07:00
|
|
|
use ff::PrimeField;
|
|
|
|
use group::{Curve, Group, GroupEncoding};
|
2021-07-11 08:42:52 -07:00
|
|
|
use jubjub::{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! {
|
|
|
|
static ref GENERATORS_EXP: Vec<SubgroupPoint> = read_generators_bin();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn read_generators_bin() -> Vec<SubgroupPoint> {
|
|
|
|
let mut generators_bin = GENERATORS;
|
|
|
|
let mut gens: Vec<SubgroupPoint> = vec![];
|
|
|
|
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];
|
|
|
|
generators_bin.read(&mut bb).unwrap();
|
|
|
|
let p = SubgroupPoint::from_bytes_unchecked(&bb).unwrap();
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
pub fn pedersen_hash(depth: u8, left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
|
|
|
|
let mut result = SubgroupPoint::identity();
|
|
|
|
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 {
|
2021-07-11 08:42:52 -07:00
|
|
|
r = r >> 8;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
jubjub::ExtendedPoint::from(result)
|
|
|
|
.to_affine()
|
|
|
|
.get_u()
|
|
|
|
.to_repr()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn generator_multiplication(acc: &Fr, gens: &[SubgroupPoint], i_generator: u32) -> SubgroupPoint {
|
|
|
|
let acc = acc.to_repr();
|
|
|
|
|
|
|
|
let mut tmp = jubjub::SubgroupPoint::identity();
|
|
|
|
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();
|
|
|
|
|
|
|
|
for _ in 0..100 {
|
|
|
|
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
|
|
|
}
|