mirror of
https://github.com/arnaucube/ark-r1cs-std.git
synced 2026-01-10 16:01:28 +01:00
Adds Bowe-Hopwood hash
This commit is contained in:
committed by
Pratyush Mishra
parent
19856bd9b6
commit
581f3df55f
206
crypto-primitives/src/crh/bowe_hopwood/constraints.rs
Normal file
206
crypto-primitives/src/crh/bowe_hopwood/constraints.rs
Normal file
@@ -0,0 +1,206 @@
|
||||
use algebra::Field;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::crh::{
|
||||
bowe_hopwood::{BoweHopwoodPedersenCRH, BoweHopwoodPedersenParameters, CHUNK_SIZE},
|
||||
pedersen::PedersenWindow,
|
||||
FixedLengthCRHGadget,
|
||||
};
|
||||
use algebra::groups::Group;
|
||||
use r1cs_core::{ConstraintSystem, SynthesisError};
|
||||
use r1cs_std::{alloc::AllocGadget, groups::GroupGadget, uint8::UInt8};
|
||||
|
||||
use r1cs_std::bits::boolean::Boolean;
|
||||
use std::{borrow::Borrow, marker::PhantomData};
|
||||
|
||||
#[derive(Derivative)]
|
||||
#[derivative(Clone(
|
||||
bound = "G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>"
|
||||
))]
|
||||
pub struct BoweHopwoodPedersenCRHGadgetParameters<
|
||||
G: Group,
|
||||
W: PedersenWindow,
|
||||
ConstraintF: Field,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
> {
|
||||
params: BoweHopwoodPedersenParameters<G>,
|
||||
_group_g: PhantomData<GG>,
|
||||
_engine: PhantomData<ConstraintF>,
|
||||
_window: PhantomData<W>,
|
||||
}
|
||||
|
||||
pub struct BoweHopwoodPedersenCRHGadget<
|
||||
G: Group,
|
||||
ConstraintF: Field,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
> {
|
||||
_group: PhantomData<*const G>,
|
||||
_group_gadget: PhantomData<*const GG>,
|
||||
_engine: PhantomData<ConstraintF>,
|
||||
}
|
||||
|
||||
impl<ConstraintF, G, GG, W> FixedLengthCRHGadget<BoweHopwoodPedersenCRH<G, W>, ConstraintF>
|
||||
for BoweHopwoodPedersenCRHGadget<G, ConstraintF, GG>
|
||||
where
|
||||
ConstraintF: Field,
|
||||
G: Group + Hash,
|
||||
GG: GroupGadget<G, ConstraintF>,
|
||||
W: PedersenWindow,
|
||||
{
|
||||
type OutputGadget = GG;
|
||||
type ParametersGadget = BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>;
|
||||
|
||||
fn check_evaluation_gadget<CS: ConstraintSystem<ConstraintF>>(
|
||||
cs: CS,
|
||||
parameters: &Self::ParametersGadget,
|
||||
input: &[UInt8],
|
||||
) -> Result<Self::OutputGadget, SynthesisError> {
|
||||
// Pad the input if it is not the current length.
|
||||
let mut input_in_bits: Vec<_> = input.iter().flat_map(|byte| byte.into_bits_le()).collect();
|
||||
if (input_in_bits.len()) % CHUNK_SIZE != 0 {
|
||||
let current_length = input_in_bits.len();
|
||||
for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
|
||||
input_in_bits.push(Boolean::constant(false));
|
||||
}
|
||||
}
|
||||
assert!(input_in_bits.len() % CHUNK_SIZE == 0);
|
||||
assert_eq!(parameters.params.generators.len(), W::NUM_WINDOWS);
|
||||
for generators in parameters.params.generators.iter() {
|
||||
assert_eq!(generators.len(), W::WINDOW_SIZE);
|
||||
}
|
||||
|
||||
// Allocate new variable for the result.
|
||||
let input_in_bits = input_in_bits
|
||||
.chunks(W::WINDOW_SIZE * CHUNK_SIZE)
|
||||
.map(|x| x.chunks(CHUNK_SIZE).into_iter().collect::<Vec<_>>())
|
||||
.collect::<Vec<_>>();
|
||||
let result = GG::precomputed_base_3_bit_signed_digit_scalar_mul(
|
||||
cs,
|
||||
¶meters.params.generators,
|
||||
&input_in_bits,
|
||||
)?;
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, W: PedersenWindow, ConstraintF: Field, GG: GroupGadget<G, ConstraintF>>
|
||||
AllocGadget<BoweHopwoodPedersenParameters<G>, ConstraintF>
|
||||
for BoweHopwoodPedersenCRHGadgetParameters<G, W, ConstraintF, GG>
|
||||
{
|
||||
fn alloc<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
_cs: CS,
|
||||
value_gen: F,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
T: Borrow<BoweHopwoodPedersenParameters<G>>,
|
||||
{
|
||||
let params = value_gen()?.borrow().clone();
|
||||
Ok(BoweHopwoodPedersenCRHGadgetParameters {
|
||||
params,
|
||||
_group_g: PhantomData,
|
||||
_engine: PhantomData,
|
||||
_window: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn alloc_input<F, T, CS: ConstraintSystem<ConstraintF>>(
|
||||
_cs: CS,
|
||||
value_gen: F,
|
||||
) -> Result<Self, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<T, SynthesisError>,
|
||||
T: Borrow<BoweHopwoodPedersenParameters<G>>,
|
||||
{
|
||||
let params = value_gen()?.borrow().clone();
|
||||
Ok(BoweHopwoodPedersenCRHGadgetParameters {
|
||||
params,
|
||||
_group_g: PhantomData,
|
||||
_engine: PhantomData,
|
||||
_window: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use algebra::fields::sw6::fr::Fr;
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::crh::{
|
||||
bowe_hopwood::{constraints::BoweHopwoodPedersenCRHGadget, BoweHopwoodPedersenCRH},
|
||||
pedersen::PedersenWindow,
|
||||
FixedLengthCRH, FixedLengthCRHGadget,
|
||||
};
|
||||
use algebra::{curves::edwards_sw6::EdwardsProjective as Edwards, ProjectiveCurve};
|
||||
use r1cs_core::ConstraintSystem;
|
||||
use r1cs_std::{
|
||||
alloc::AllocGadget, groups::curves::twisted_edwards::edwards_sw6::EdwardsSWGadget,
|
||||
test_constraint_system::TestConstraintSystem, uint8::UInt8,
|
||||
};
|
||||
|
||||
type TestCRH = BoweHopwoodPedersenCRH<Edwards, Window>;
|
||||
type TestCRHGadget = BoweHopwoodPedersenCRHGadget<Edwards, Fr, EdwardsSWGadget>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub(super) struct Window;
|
||||
|
||||
impl PedersenWindow for Window {
|
||||
const WINDOW_SIZE: usize = 90;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
|
||||
fn generate_input<CS: ConstraintSystem<Fr>, R: Rng>(
|
||||
mut cs: CS,
|
||||
rng: &mut R,
|
||||
) -> ([u8; 270], Vec<UInt8>) {
|
||||
let mut input = [1u8; 270];
|
||||
rng.fill_bytes(&mut input);
|
||||
|
||||
let mut input_bytes = vec![];
|
||||
for (byte_i, input_byte) in input.into_iter().enumerate() {
|
||||
let cs = cs.ns(|| format!("input_byte_gadget_{}", byte_i));
|
||||
input_bytes.push(UInt8::alloc(cs, || Ok(*input_byte)).unwrap());
|
||||
}
|
||||
(input, input_bytes)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn crh_primitive_gadget_test() {
|
||||
let rng = &mut thread_rng();
|
||||
let mut cs = TestConstraintSystem::<Fr>::new();
|
||||
|
||||
let (input, input_bytes) = generate_input(&mut cs, rng);
|
||||
println!("number of constraints for input: {}", cs.num_constraints());
|
||||
|
||||
let parameters = TestCRH::setup(rng).unwrap();
|
||||
let primitive_result = TestCRH::evaluate(¶meters, &input).unwrap();
|
||||
|
||||
let gadget_parameters =
|
||||
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::ParametersGadget::alloc(
|
||||
&mut cs.ns(|| "gadget_parameters"),
|
||||
|| Ok(¶meters),
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
"number of constraints for input + params: {}",
|
||||
cs.num_constraints()
|
||||
);
|
||||
|
||||
let gadget_result =
|
||||
<TestCRHGadget as FixedLengthCRHGadget<TestCRH, Fr>>::check_evaluation_gadget(
|
||||
&mut cs.ns(|| "gadget_evaluation"),
|
||||
&gadget_parameters,
|
||||
&input_bytes,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
println!("number of constraints total: {}", cs.num_constraints());
|
||||
|
||||
let primitive_result = primitive_result.into_affine();
|
||||
assert_eq!(primitive_result.x, gadget_result.x.value.unwrap());
|
||||
assert_eq!(primitive_result.y, gadget_result.y.value.unwrap());
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
}
|
||||
194
crypto-primitives/src/crh/bowe_hopwood/mod.rs
Normal file
194
crypto-primitives/src/crh/bowe_hopwood/mod.rs
Normal file
@@ -0,0 +1,194 @@
|
||||
use crate::Error;
|
||||
use rand::Rng;
|
||||
use rayon::prelude::*;
|
||||
use std::{
|
||||
fmt::{Debug, Formatter, Result as FmtResult},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use super::pedersen::{bytes_to_bits, PedersenCRH, PedersenWindow};
|
||||
use crate::crh::FixedLengthCRH;
|
||||
use algebra::{biginteger::BigInteger, fields::PrimeField, groups::Group};
|
||||
|
||||
#[cfg(feature = "r1cs")]
|
||||
pub mod constraints;
|
||||
|
||||
pub const CHUNK_SIZE: usize = 3;
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BoweHopwoodPedersenParameters<G: Group> {
|
||||
pub generators: Vec<Vec<G>>,
|
||||
}
|
||||
|
||||
pub struct BoweHopwoodPedersenCRH<G: Group, W: PedersenWindow> {
|
||||
group: PhantomData<G>,
|
||||
window: PhantomData<W>,
|
||||
}
|
||||
|
||||
impl<G: Group, W: PedersenWindow> BoweHopwoodPedersenCRH<G, W> {
|
||||
pub fn create_generators<R: Rng>(rng: &mut R) -> Vec<Vec<G>> {
|
||||
let mut generators = Vec::new();
|
||||
for _ in 0..W::NUM_WINDOWS {
|
||||
let mut generators_for_segment = Vec::new();
|
||||
let mut base = G::rand(rng);
|
||||
for _ in 0..W::WINDOW_SIZE {
|
||||
generators_for_segment.push(base);
|
||||
for _ in 0..4 {
|
||||
base.double_in_place();
|
||||
}
|
||||
}
|
||||
generators.push(generators_for_segment);
|
||||
}
|
||||
generators
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group, W: PedersenWindow> FixedLengthCRH for BoweHopwoodPedersenCRH<G, W> {
|
||||
const INPUT_SIZE_BITS: usize = PedersenCRH::<G, W>::INPUT_SIZE_BITS;
|
||||
type Output = G;
|
||||
type Parameters = BoweHopwoodPedersenParameters<G>;
|
||||
|
||||
fn setup<R: Rng>(rng: &mut R) -> Result<Self::Parameters, Error> {
|
||||
fn calculate_num_chunks_in_segment<F: PrimeField>() -> usize {
|
||||
let upper_limit = F::modulus_minus_one_div_two();
|
||||
let mut c = 0;
|
||||
let mut range = F::BigInt::from(2_u64);
|
||||
while range < upper_limit {
|
||||
range.muln(4);
|
||||
c += 1;
|
||||
}
|
||||
|
||||
c
|
||||
}
|
||||
|
||||
let maximum_num_chunks_in_segment = calculate_num_chunks_in_segment::<G::ScalarField>();
|
||||
if W::WINDOW_SIZE > maximum_num_chunks_in_segment {
|
||||
return Err(format!(
|
||||
"Bowe-Hopwood hash must have a window size resulting in scalars < (p-1)/2, \
|
||||
maximum segment size is {}",
|
||||
maximum_num_chunks_in_segment
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
let time = start_timer!(|| format!(
|
||||
"BoweHopwoodPedersenCRH::Setup: {} segments of {} 3-bit chunks; {{0,1}}^{{{}}} -> G",
|
||||
W::NUM_WINDOWS,
|
||||
W::WINDOW_SIZE,
|
||||
W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE
|
||||
));
|
||||
let generators = Self::create_generators(rng);
|
||||
end_timer!(time);
|
||||
Ok(Self::Parameters { generators })
|
||||
}
|
||||
|
||||
fn evaluate(parameters: &Self::Parameters, input: &[u8]) -> Result<Self::Output, Error> {
|
||||
let eval_time = start_timer!(|| "BoweHopwoodPedersenCRH::Eval");
|
||||
|
||||
if (input.len() * 8) > W::WINDOW_SIZE * W::NUM_WINDOWS * CHUNK_SIZE {
|
||||
panic!(
|
||||
"incorrect input length {:?} for window params {:?}x{:?}x{}",
|
||||
input.len(),
|
||||
W::WINDOW_SIZE,
|
||||
W::NUM_WINDOWS,
|
||||
CHUNK_SIZE,
|
||||
);
|
||||
}
|
||||
|
||||
let mut padded_input = Vec::with_capacity(input.len());
|
||||
let input = bytes_to_bits(input);
|
||||
// Pad the input if it is not the current length.
|
||||
padded_input.extend_from_slice(&input);
|
||||
if input.len() % CHUNK_SIZE != 0 {
|
||||
let current_length = input.len();
|
||||
for _ in 0..(CHUNK_SIZE - current_length % CHUNK_SIZE) {
|
||||
padded_input.push(false);
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(padded_input.len() % CHUNK_SIZE, 0);
|
||||
|
||||
assert_eq!(
|
||||
parameters.generators.len(),
|
||||
W::NUM_WINDOWS,
|
||||
"Incorrect pp of size {:?} for window params {:?}x{:?}x{}",
|
||||
parameters.generators.len(),
|
||||
W::WINDOW_SIZE,
|
||||
W::NUM_WINDOWS,
|
||||
CHUNK_SIZE,
|
||||
);
|
||||
for generators in parameters.generators.iter() {
|
||||
assert_eq!(generators.len(), W::WINDOW_SIZE);
|
||||
}
|
||||
assert_eq!(CHUNK_SIZE, 3);
|
||||
|
||||
// Compute sum of h_i^{sum of (1-2*c_{i,j,2})*(1+c_{i,j,0}+2*c_{i,j,1})*2^{4*(j-1)} for all j in segment} for all i.
|
||||
// Described in section 5.4.1.7 in the Zcash protocol specification.
|
||||
let result = padded_input
|
||||
.par_chunks(W::WINDOW_SIZE * CHUNK_SIZE)
|
||||
.zip(¶meters.generators)
|
||||
.map(|(segment_bits, segment_generators)| {
|
||||
segment_bits
|
||||
.par_chunks(CHUNK_SIZE)
|
||||
.zip(segment_generators)
|
||||
.map(|(chunk_bits, generator)| {
|
||||
let mut encoded = generator.clone();
|
||||
if chunk_bits[0] {
|
||||
encoded = encoded + &generator;
|
||||
}
|
||||
if chunk_bits[1] {
|
||||
encoded = encoded + &generator.double();
|
||||
}
|
||||
if chunk_bits[2] {
|
||||
encoded = encoded.neg();
|
||||
}
|
||||
encoded
|
||||
})
|
||||
.reduce(|| G::zero(), |a, b| a + &b)
|
||||
})
|
||||
.reduce(|| G::zero(), |a, b| a + &b);
|
||||
end_timer!(eval_time);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl<G: Group> Debug for BoweHopwoodPedersenParameters<G> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "Bowe-Hopwood Pedersen Hash Parameters {{\n")?;
|
||||
for (i, g) in self.generators.iter().enumerate() {
|
||||
write!(f, "\t Generator {}: {:?}\n", i, g)?;
|
||||
}
|
||||
write!(f, "}}\n")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
crh::{bowe_hopwood::BoweHopwoodPedersenCRH, pedersen::PedersenWindow},
|
||||
FixedLengthCRH,
|
||||
};
|
||||
use algebra::curves::edwards_sw6::EdwardsProjective;
|
||||
use rand::thread_rng;
|
||||
|
||||
#[test]
|
||||
fn test_simple_bh() {
|
||||
#[derive(Clone)]
|
||||
struct TestWindow {}
|
||||
impl PedersenWindow for TestWindow {
|
||||
const WINDOW_SIZE: usize = 90;
|
||||
const NUM_WINDOWS: usize = 8;
|
||||
}
|
||||
|
||||
let rng = &mut thread_rng();
|
||||
let params =
|
||||
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::setup(rng)
|
||||
.unwrap();
|
||||
<BoweHopwoodPedersenCRH<EdwardsProjective, TestWindow> as FixedLengthCRH>::evaluate(
|
||||
¶ms,
|
||||
&[1, 2, 3],
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ use algebra::bytes::ToBytes;
|
||||
use rand::Rng;
|
||||
use std::hash::Hash;
|
||||
|
||||
pub mod bowe_hopwood;
|
||||
pub mod injective_map;
|
||||
pub mod pedersen;
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ impl<PairingE: PairingEngine, ConstraintF: Field, P: PairingGadget<PairingE, Con
|
||||
alpha_g1_beta_g2,
|
||||
gamma_g2_neg_pc,
|
||||
delta_g2_neg_pc,
|
||||
gamma_abc_g1: self.gamma_abc_g1.clone(),
|
||||
gamma_abc_g1: self.gamma_abc_g1.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user