zcash-sync/src/sapling/hash.rs

166 lines
4.8 KiB
Rust

use ff::PrimeField;
use group::Curve;
use jubjub::{ExtendedPoint, Fr};
use zcash_primitives::constants::PEDERSEN_HASH_CHUNKS_PER_GENERATOR;
use crate::sync::{Hasher, Node};
use super::GENERATORS_EXP;
#[inline(always)]
fn accumulate_scalar(acc: &mut Fr, cur: &mut Fr, x: u8) {
let mut tmp = *cur;
if x & 1 != 0 {
tmp += *cur;
}
*cur = cur.double();
if x & 2 != 0 {
tmp += *cur;
}
if x & 4 != 0 {
tmp = tmp.neg();
}
*acc += tmp;
}
fn accumulate_generator(acc: &Fr, idx_generator: u32) -> ExtendedPoint {
let acc_bytes = acc.to_repr();
let mut tmp = ExtendedPoint::identity();
for (i, &j) in acc_bytes.iter().enumerate() {
let offset = (idx_generator * 32 + i as u32) * 256 + j as u32;
let x = GENERATORS_EXP[offset as usize];
tmp += x;
}
tmp
}
pub fn hash_combine(depth: u8, left: &[u8; 32], right: &[u8; 32]) -> [u8; 32] {
let mut hash = ExtendedPoint::identity();
let mut acc = Fr::zero();
let mut cur = Fr::one();
let a = depth & 7;
let b = depth >> 3;
accumulate_scalar(&mut acc, &mut cur, a);
cur = cur.double().double().double();
accumulate_scalar(&mut acc, &mut cur, b);
cur = cur.double().double().double();
// Shift right by 1 bit and overwrite the 256th bit of left
let mut left = *left;
let mut right = *right;
// move by 1 bit to fill the missing 256th bit of left
let mut carry = 0;
for i in (0..32).rev() {
let c = right[i] & 1;
right[i] = right[i] >> 1 | carry << 7;
carry = c;
}
left[31] &= 0x7F;
left[31] |= carry << 7; // move the first bit of right into 256th of left
// we have 255*2/3 = 170 chunks
let mut bit_offset = 0;
let mut byte_offset = 0;
let mut idx_generator = 0;
for i in 0..170 {
let mut v = if byte_offset < 31 {
left[byte_offset] as u16 | (left[byte_offset + 1] as u16) << 8
} else if byte_offset == 31 {
left[31] as u16 | (right[0] as u16) << 8
} else if byte_offset < 63 {
right[byte_offset - 32] as u16 | (right[byte_offset - 31] as u16) << 8
} else {
right[byte_offset - 32] as u16
};
v = v >> bit_offset & 0x07; // keep 3 bits
accumulate_scalar(&mut acc, &mut cur, v as u8);
if (i+3) % PEDERSEN_HASH_CHUNKS_PER_GENERATOR as u32 == 0 {
hash += accumulate_generator(&acc, idx_generator);
idx_generator += 1;
acc = Fr::zero();
cur = Fr::one();
}
else {
cur = cur.double().double().double(); // 2^4 * cur
}
bit_offset += 3;
if bit_offset >= 8 {
byte_offset += bit_offset / 8;
bit_offset %= 8;
}
}
hash += accumulate_generator(&acc, idx_generator);
let hash = hash
.to_affine()
.get_u()
.to_repr();
hash
}
#[derive(Clone, Default)]
pub struct SaplingHasher {}
impl Hasher for SaplingHasher {
fn uncommited_node() -> Node {
[0u8; 32]
}
fn node_combine(&self, depth: u8, left: &Node, right: &Node) -> Node {
hash_combine(depth, left, right)
}
}
#[cfg(test)]
mod tests {
use std::convert::TryInto;
use rand::RngCore;
use rand::rngs::OsRng;
use crate::pedersen_hash;
use crate::sapling::hash::hash_combine;
#[test]
fn test_hash1() {
let depth = 8;
let sa = "767a9a7e989289efdfa69c4c8e985c31f3c2c0353f20a80f572854206f077f86";
let sb = "944c46945a9e7a0a753850bd90f69d44ac884b60244a9f8eacf3a2aeddd08d6e";
let a: [u8; 32] = hex::decode(sa).unwrap().try_into().unwrap();
let b: [u8; 32] = hex::decode(sb).unwrap().try_into().unwrap();
println!("A: {}", hex::encode(a));
println!("B: {}", hex::encode(b));
let hash = pedersen_hash(depth, &a, &b);
let hash2 = hash_combine(depth, &a, &b);
println!("Reference Hash: {}", hex::encode(hash));
println!("This Hash: {}", hex::encode(hash2));
// need to expose repr for this check
assert_eq!(hash, hash2);
}
#[test]
fn test_hash_random() {
let mut rng = OsRng;
for _ in 0..1000 {
let depth = (rng.next_u32() % 50) as u8;
let mut a = [0u8; 32];
let mut b = [0u8; 32];
rng.fill_bytes(&mut a);
rng.fill_bytes(&mut b);
println!("A: {}", hex::encode(a));
println!("B: {}", hex::encode(b));
let hash = pedersen_hash(depth, &a, &b);
let hash2 = hash_combine(depth, &a, &b);
println!("Reference Hash: {}", hex::encode(hash));
println!("This Hash: {}", hex::encode(hash2));
// need to expose repr for this check
assert_eq!(hash, hash2);
}
}
}