use core::{borrow::Borrow, marker::PhantomData};
use crate::{
crh::{
bowe_hopwood::{Parameters, CHUNK_SIZE, CRH},
pedersen::Window,
FixedLengthCRHGadget,
},
Vec,
};
use algebra_core::{
curves::{ModelParameters, TEModelParameters},
Field,
};
use r1cs_core::{Namespace, SynthesisError};
use r1cs_std::{
alloc::AllocVar, groups::curves::twisted_edwards::AffineVar, prelude::*, uint8::UInt8,
};
use r1cs_std::bits::boolean::Boolean;
type ConstraintF
= <
::BaseField as Field>::BasePrimeField;
#[derive(Derivative)]
#[derivative(Clone(bound = "P: TEModelParameters, W: Window"))]
pub struct ParametersVar {
params: Parameters,
#[doc(hidden)]
_window: PhantomData,
}
pub struct CRHGadget>>
where
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
{
#[doc(hidden)]
_params: PhantomData,
#[doc(hidden)]
_base_field: PhantomData,
}
impl FixedLengthCRHGadget, ConstraintF> for CRHGadget
where
for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>,
F: FieldVar>,
F: TwoBitLookupGadget, TableConstant = P::BaseField>
+ ThreeBitCondNegLookupGadget, TableConstant = P::BaseField>,
P: TEModelParameters,
W: Window,
{
type OutputVar = AffineVar;
type ParametersVar = ParametersVar
;
fn evaluate(
parameters: &Self::ParametersVar,
input: &[UInt8>],
) -> Result {
// Pad the input if it is not the current length.
let mut input_in_bits: Vec> = input
.iter()
.flat_map(|byte| byte.to_bits_le().unwrap())
.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).collect::>())
.collect::>();
let result = AffineVar::precomputed_base_3_bit_signed_digit_scalar_mul(
¶meters.params.generators,
&input_in_bits,
)?;
Ok(result)
}
}
impl AllocVar, ConstraintF> for ParametersVar
where
P: TEModelParameters,
W: Window,
{
fn new_variable>>(
_cs: impl Into>>,
f: impl FnOnce() -> Result,
_mode: AllocationMode,
) -> Result {
let params = f()?.borrow().clone();
Ok(ParametersVar {
params,
_window: PhantomData,
})
}
}
#[cfg(test)]
mod test {
use rand::Rng;
use crate::crh::{
bowe_hopwood::{constraints::CRHGadget, CRH},
pedersen::Window as PedersenWindow,
FixedLengthCRH, FixedLengthCRHGadget,
};
use algebra::{
ed_on_bls12_381::{EdwardsParameters, Fq as Fr},
test_rng, ProjectiveCurve,
};
use r1cs_core::{ConstraintSystem, ConstraintSystemRef};
use r1cs_std::{alloc::AllocVar, ed_on_bls12_381::FqVar, uint8::UInt8, R1CSVar};
type TestCRH = CRH;
type TestCRHGadget = CRHGadget;
#[derive(Clone, PartialEq, Eq, Hash)]
pub(super) struct Window;
impl PedersenWindow for Window {
const WINDOW_SIZE: usize = 63;
const NUM_WINDOWS: usize = 8;
}
fn generate_input(
cs: ConstraintSystemRef,
rng: &mut R,
) -> ([u8; 189], Vec>) {
let mut input = [1u8; 189];
rng.fill_bytes(&mut input);
let mut input_bytes = vec![];
for byte in input.iter() {
input_bytes.push(UInt8::new_witness(cs.clone(), || Ok(byte)).unwrap());
}
(input, input_bytes)
}
#[test]
fn test_native_equality() {
let rng = &mut test_rng();
let cs = ConstraintSystem::::new_ref();
let (input, input_var) = generate_input(cs.clone(), 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 parameters_var =
>::ParametersVar::new_witness(
cs.ns("parameters_var"),
|| Ok(¶meters),
)
.unwrap();
println!(
"number of constraints for input + params: {}",
cs.num_constraints()
);
let result_var = TestCRHGadget::evaluate(¶meters_var, &input_var).unwrap();
println!("number of constraints total: {}", cs.num_constraints());
let primitive_result = primitive_result.into_affine();
assert_eq!(primitive_result, result_var.value().unwrap().into_affine());
assert!(cs.is_satisfied().unwrap());
}
}