mod hash_uncompressed;
use crate::Blake2Xs;
use snarkvm_console_types::prelude::*;
use snarkvm_utilities::BigInteger;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
pub(super) const BHP_CHUNK_SIZE: usize = 3;
pub(super) const BHP_LOOKUP_SIZE: usize = 1 << BHP_CHUNK_SIZE;
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(bound = "E: Serialize + DeserializeOwned")]
pub struct BHPHasher<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> {
bases: Arc<Vec<Vec<Group<E>>>>,
bases_lookup: Arc<Vec<Vec<[Group<E>; BHP_LOOKUP_SIZE]>>>,
random_base: Arc<Vec<Group<E>>>,
}
impl<E: Environment, const NUM_WINDOWS: u8, const WINDOW_SIZE: u8> BHPHasher<E, NUM_WINDOWS, WINDOW_SIZE> {
const MAX_BITS: usize = NUM_WINDOWS as usize * WINDOW_SIZE as usize * BHP_CHUNK_SIZE;
const MIN_BITS: usize = WINDOW_SIZE as usize * BHP_CHUNK_SIZE;
pub fn setup(domain: &str) -> Result<Self> {
let mut maximum_window_size = 0;
let mut range = E::BigInteger::from(2_u64);
while range < E::Scalar::modulus_minus_one_div_two() {
range.muln(4); maximum_window_size += 1;
}
ensure!(WINDOW_SIZE <= maximum_window_size, "The maximum BHP window size is {maximum_window_size}");
let bases = (0..NUM_WINDOWS)
.map(|index| {
let (generator, _, _) = Blake2Xs::hash_to_curve::<E::Affine>(&format!(
"Aleo.BHP.{NUM_WINDOWS}.{WINDOW_SIZE}.{domain}.{index}"
));
let mut base = Group::<E>::new(generator);
let mut powers = Vec::with_capacity(WINDOW_SIZE as usize);
for _ in 0..WINDOW_SIZE {
powers.push(base);
for _ in 0..4 {
base = base.double();
}
}
powers
})
.collect::<Vec<Vec<Group<E>>>>();
ensure!(bases.len() == NUM_WINDOWS as usize, "Incorrect number of BHP windows ({})", bases.len());
for window in &bases {
ensure!(window.len() == WINDOW_SIZE as usize, "Incorrect BHP window size ({})", window.len());
}
let bases_lookup = bases
.iter()
.map(|x| {
x.iter()
.map(|g| {
let mut lookup = [Group::<E>::zero(); BHP_LOOKUP_SIZE];
for (i, element) in lookup.iter_mut().enumerate().take(BHP_LOOKUP_SIZE) {
*element = *g;
if (i & 0x01) != 0 {
*element += g;
}
if (i & 0x02) != 0 {
*element += g.double();
}
if (i & 0x04) != 0 {
*element = element.neg();
}
}
lookup
})
.collect()
})
.collect::<Vec<Vec<[Group<E>; BHP_LOOKUP_SIZE]>>>();
ensure!(bases_lookup.len() == NUM_WINDOWS as usize, "Incorrect number of BHP lookups ({})", bases_lookup.len());
for window in &bases_lookup {
ensure!(window.len() == WINDOW_SIZE as usize, "Incorrect BHP lookup window size ({})", window.len());
}
let (generator, _, _) =
Blake2Xs::hash_to_curve::<E::Affine>(&format!("Aleo.BHP.{NUM_WINDOWS}.{WINDOW_SIZE}.{domain}.Randomizer"));
let mut base_power = Group::<E>::new(generator);
let mut random_base = Vec::with_capacity(Scalar::<E>::size_in_bits());
for _ in 0..Scalar::<E>::size_in_bits() {
random_base.push(base_power);
base_power = base_power.double();
}
ensure!(
random_base.len() == Scalar::<E>::size_in_bits(),
"Incorrect number of BHP random base powers ({})",
random_base.len()
);
Ok(Self { bases: Arc::new(bases), bases_lookup: Arc::new(bases_lookup), random_base: Arc::new(random_base) })
}
pub fn bases(&self) -> &Arc<Vec<Vec<Group<E>>>> {
&self.bases
}
pub fn random_base(&self) -> &Arc<Vec<Group<E>>> {
&self.random_base
}
}