diff --git a/Cargo.toml b/Cargo.toml index 143ce7dd..de07a82a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,8 +28,6 @@ digest = "0.9" ark-r1cs-std = { version = "^0.3.0", optional = true, default-features = false } ark-snark = { version = "^0.3.0", default-features = false } -ark-nonnative-field = { version = "^0.3.0", optional = true, default-features = false } - rayon = { version = "1.0", optional = true } derivative = { version = "2.0", features = ["use_core"] } tracing = { version = "0.1", default-features = false, features = [ "attributes" ], optional = true } @@ -39,10 +37,18 @@ default = ["std"] std = [ "ark-ff/std", "ark-ec/std", "ark-std/std", "ark-relations/std" ] print-trace = [ "ark-std/print-trace" ] parallel = [ "std", "rayon", "ark-ec/parallel", "ark-std/parallel", "ark-ff/parallel" ] -r1cs = [ "ark-r1cs-std", "tracing", "ark-nonnative-field", "ark-sponge/r1cs" ] +r1cs = [ "ark-r1cs-std", "tracing", "ark-sponge/r1cs" ] [dev-dependencies] ark-ed-on-bls12-381 = { version = "^0.3.0", default-features = false, features = [ "r1cs" ] } ark-bls12-377 = { version = "^0.3.0", default-features = false, features = [ "curve", "r1cs" ] } ark-mnt4-298 = { version = "^0.3.0", default-features = false, features = [ "curve", "r1cs" ] } ark-mnt6-298 = { version = "^0.3.0", default-features = false, features = [ "r1cs" ] } + +[patch.crates-io] +ark-r1cs-std = { git = "https://github.com/arkworks-rs/r1cs-std", branch = "reduce-generics", optional = true, default-features = false } +ark-ed-on-bls12-381 = { git = "https://github.com/arkworks-rs/curves", branch = "reduce-generics", default-features = false, features = [ "r1cs" ] } +ark-bls12-377 = { git = "https://github.com/arkworks-rs/curves", branch = "reduce-generics", default-features = false, features = [ "curve", "r1cs" ] } +ark-mnt4-298 = { git = "https://github.com/arkworks-rs/curves", branch = "reduce-generics", default-features = false, features = [ "curve", "r1cs" ] } +ark-mnt6-298 = { git = "https://github.com/arkworks-rs/curves", branch = "reduce-generics", default-features = false, features = [ "curve", "r1cs" ] } +ark-sponge = { git = "https://github.com/arkworks-rs/sponge", branch = "reduce-generics", default-features = false } diff --git a/cp-benches/benches/crypto_primitives/crh.rs b/cp-benches/benches/crypto_primitives/crh.rs index 12635650..46879cc4 100644 --- a/cp-benches/benches/crypto_primitives/crh.rs +++ b/cp-benches/benches/crypto_primitives/crh.rs @@ -3,7 +3,7 @@ extern crate criterion; use ark_crypto_primitives::crh::{ pedersen::{Window, CRH as PedersenCRH}, - CRHScheme, + CRH, }; use ark_ed_on_bls12_377::EdwardsProjective as Edwards; use criterion::Criterion; diff --git a/src/commitment/blake2s/constraints.rs b/src/commitment/blake2s/constraints.rs index 4659faae..a1ce603f 100644 --- a/src/commitment/blake2s/constraints.rs +++ b/src/commitment/blake2s/constraints.rs @@ -1,7 +1,7 @@ use ark_relations::r1cs::{Namespace, SynthesisError}; use crate::{ - commitment::{blake2s, CommitmentGadget}, + commitment::{blake2s, CommitmentWithGadget}, prf::blake2s::constraints::{evaluate_blake2s, OutputVar}, Vec, }; @@ -16,15 +16,13 @@ pub struct ParametersVar; #[derive(Clone)] pub struct RandomnessVar(pub Vec>); -pub struct CommGadget; - -impl CommitmentGadget for CommGadget { +impl CommitmentWithGadget for blake2s::Commitment { type OutputVar = OutputVar; type ParametersVar = ParametersVar; type RandomnessVar = RandomnessVar; #[tracing::instrument(target = "r1cs", skip(input, r))] - fn commit( + fn commit_gadget( _: &Self::ParametersVar, input: &[UInt8], r: &Self::RandomnessVar, @@ -72,12 +70,10 @@ impl AllocVar<[u8; 32], ConstraintF> for RandomnessVar< #[cfg(test)] mod test { use crate::commitment::{ - blake2s::{ - constraints::{CommGadget, RandomnessVar}, - Commitment, - }, + blake2s::{constraints::RandomnessVar, Commitment}, CommitmentGadget, CommitmentScheme, }; + use crate::Gadget; use ark_ed_on_bls12_381::Fq as Fr; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::ConstraintSystem; @@ -92,7 +88,6 @@ mod test { let rng = &mut ark_std::test_rng(); type TestCOMM = Commitment; - type TestCOMMGadget = CommGadget; let mut randomness = [0u8; 32]; rng.fill(&mut randomness); @@ -112,17 +107,13 @@ mod test { let randomness_var = RandomnessVar(randomness_var); let parameters_var = - >::ParametersVar::new_witness( + as CommitmentGadget>::ParametersVar::new_witness( ark_relations::ns!(cs, "gadget_parameters"), || Ok(¶meters), ) .unwrap(); - let result_var = >::commit( - ¶meters_var, - &input_var, - &randomness_var, - ) - .unwrap(); + let result_var = + Gadget::::commit(¶meters_var, &input_var, &randomness_var).unwrap(); for i in 0..32 { assert_eq!(primitive_result[i], result_var.0[i].value().unwrap()); diff --git a/src/commitment/constraints.rs b/src/commitment/constraints.rs index 5124e5a5..5bfe5fb0 100644 --- a/src/commitment/constraints.rs +++ b/src/commitment/constraints.rs @@ -4,20 +4,59 @@ use ark_r1cs_std::prelude::*; use ark_relations::r1cs::SynthesisError; use core::fmt::Debug; -pub trait CommitmentGadget { +pub trait CommitmentWithGadget: CommitmentScheme { type OutputVar: EqGadget + ToBytesGadget - + AllocVar + + AllocVar + R1CSVar + Clone + Sized + Debug; - type ParametersVar: AllocVar + Clone; - type RandomnessVar: AllocVar + Clone; + type ParametersVar: AllocVar + Clone; + type RandomnessVar: AllocVar + Clone; - fn commit( + fn commit_gadget( parameters: &Self::ParametersVar, input: &[UInt8], r: &Self::RandomnessVar, ) -> Result; } + +pub trait CommitmentGadget { + type Native: CommitmentWithGadget< + ConstraintF, + OutputVar = Self::OutputVar, + ParametersVar = Self::ParametersVar, + RandomnessVar = Self::RandomnessVar, + >; + type OutputVar: EqGadget + + ToBytesGadget + + AllocVar<::Output, ConstraintF> + + R1CSVar + + Clone + + Sized + + Debug; + type ParametersVar: AllocVar<::Parameters, ConstraintF> + + Clone; + type RandomnessVar: AllocVar<::Randomness, ConstraintF> + + Clone; + + fn commit( + parameters: &Self::ParametersVar, + input: &[UInt8], + r: &Self::RandomnessVar, + ) -> Result { + Self::Native::commit_gadget(parameters, input, r) + } +} + +impl CommitmentGadget for crate::Gadget +where + C: CommitmentWithGadget, + ConstraintF: Field, +{ + type Native = C; + type OutputVar = C::OutputVar; + type ParametersVar = C::ParametersVar; + type RandomnessVar = C::RandomnessVar; +} diff --git a/src/commitment/injective_map/constraints.rs b/src/commitment/injective_map/constraints.rs index 7a1ce3cb..1ee3cf42 100644 --- a/src/commitment/injective_map/constraints.rs +++ b/src/commitment/injective_map/constraints.rs @@ -1,60 +1,45 @@ -use crate::commitment::{ - injective_map::{InjectiveMap, PedersenCommCompressor}, - pedersen::{ - constraints::{CommGadget, ParametersVar, RandomnessVar}, - Window, +use crate::{ + commitment::{ + injective_map::PedersenCommCompressor, + pedersen::{ + constraints::{ParametersVar, RandomnessVar}, + Commitment, Window, + }, + CommitmentGadget, }, + Gadget, }; pub use crate::crh::injective_map::constraints::InjectiveMapGadget; use ark_ec::ProjectiveCurve; use ark_ff::{Field, PrimeField}; use ark_r1cs_std::{ - groups::{CurveVar, GroupOpsBounds}, + groups::{CurveWithVar, GroupOpsBounds}, uint8::UInt8, }; use ark_relations::r1cs::SynthesisError; -use ark_std::marker::PhantomData; - type ConstraintF = <::BaseField as Field>::BasePrimeField; -pub struct CommitmentCompressorGadget -where - C: ProjectiveCurve, - I: InjectiveMap, - W: Window, - GG: CurveVar>, - IG: InjectiveMapGadget, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - _compressor: PhantomData, - _compressor_gadget: PhantomData, - _comm: PhantomData>, -} - -impl - crate::commitment::CommitmentGadget, ConstraintF> - for CommitmentCompressorGadget +impl crate::commitment::CommitmentWithGadget> + for PedersenCommCompressor where - C: ProjectiveCurve, - I: InjectiveMap, - GG: CurveVar>, + C: CurveWithVar>, + I: InjectiveMapGadget, ConstraintF: PrimeField, - IG: InjectiveMapGadget, W: Window, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, { - type OutputVar = IG::OutputVar; - type ParametersVar = ParametersVar; + type OutputVar = I::OutputVar; + type ParametersVar = ParametersVar; type RandomnessVar = RandomnessVar>; - fn commit( + fn commit_gadget( parameters: &Self::ParametersVar, input: &[UInt8>], r: &Self::RandomnessVar, ) -> Result { - let result = CommGadget::::commit(parameters, input, r)?; - IG::evaluate(&result) + let result = Gadget::>::commit(parameters, input, r)?; + I::evaluate(&result) } } diff --git a/src/commitment/pedersen/constraints.rs b/src/commitment/pedersen/constraints.rs index 83ddcab1..92cdb8d3 100644 --- a/src/commitment/pedersen/constraints.rs +++ b/src/commitment/pedersen/constraints.rs @@ -11,51 +11,32 @@ use ark_ff::{ use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_r1cs_std::prelude::*; -use core::{borrow::Borrow, marker::PhantomData}; +use core::borrow::Borrow; type ConstraintF = <::BaseField as Field>::BasePrimeField; #[derive(Derivative)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct ParametersVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ +#[derivative(Clone(bound = "C: CurveWithVar>"))] +pub struct ParametersVar>> { params: Parameters, - #[doc(hidden)] - _group_var: PhantomData, } #[derive(Clone, Debug)] pub struct RandomnessVar(Vec>); -pub struct CommGadget>, W: Window> +impl crate::commitment::CommitmentWithGadget> for Commitment where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - #[doc(hidden)] - _curve: PhantomData<*const C>, - #[doc(hidden)] - _group_var: PhantomData<*const GG>, - #[doc(hidden)] - _window: PhantomData<*const W>, -} - -impl crate::commitment::CommitmentGadget, ConstraintF> - for CommGadget -where - C: ProjectiveCurve, - GG: CurveVar>, + C: CurveWithVar>, W: Window, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, ConstraintF: PrimeField, { - type OutputVar = GG; - type ParametersVar = ParametersVar; + type OutputVar = C::Var; + type ParametersVar = ParametersVar; type RandomnessVar = RandomnessVar>; #[tracing::instrument(target = "r1cs", skip(parameters, r))] - fn commit( + fn commit_gadget( parameters: &Self::ParametersVar, input: &[UInt8>], r: &Self::RandomnessVar, @@ -80,8 +61,10 @@ where .flat_map(|byte| byte.to_bits_le().unwrap()) .collect(); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); - let mut result = - GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?; + let mut result = C::Var::precomputed_base_multiscalar_mul_le( + ¶meters.params.generators, + input_in_bits, + )?; // Compute h^r let rand_bits: Vec<_> = @@ -98,11 +81,10 @@ where } } -impl AllocVar, ConstraintF> for ParametersVar +impl AllocVar, ConstraintF> for ParametersVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, { fn new_variable>>( _cs: impl Into>>, @@ -110,10 +92,7 @@ where _mode: AllocationMode, ) -> Result { let params = f()?.borrow().clone(); - Ok(ParametersVar { - params, - _group_var: PhantomData, - }) + Ok(ParametersVar { params }) } } @@ -138,15 +117,16 @@ where #[cfg(test)] mod test { - use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq, Fr}; + use ark_ed_on_bls12_381::{EdwardsProjective as JubJub, Fq, Fr}; use ark_std::{test_rng, UniformRand}; use crate::{ commitment::{ - pedersen::{constraints::CommGadget, Commitment, Randomness}, + pedersen::{Commitment, Randomness}, CommitmentGadget, CommitmentScheme, }, crh::pedersen, + Gadget, }; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::ConstraintSystem; @@ -168,7 +148,6 @@ mod test { let rng = &mut test_rng(); type TestCOMM = Commitment; - type TestCOMMGadget = CommGadget; let randomness = Randomness(Fr::rand(rng)); @@ -182,19 +161,19 @@ mod test { } let randomness_var = - >::RandomnessVar::new_witness( + as CommitmentGadget>::RandomnessVar::new_witness( ark_relations::ns!(cs, "gadget_randomness"), || Ok(&randomness), ) .unwrap(); let parameters_var = - >::ParametersVar::new_witness( + as CommitmentGadget>::ParametersVar::new_witness( ark_relations::ns!(cs, "gadget_parameters"), || Ok(¶meters), ) .unwrap(); let result_var = - TestCOMMGadget::commit(¶meters_var, &input_var, &randomness_var).unwrap(); + Gadget::::commit(¶meters_var, &input_var, &randomness_var).unwrap(); let primitive_result = primitive_result; assert_eq!(primitive_result, result_var.value().unwrap()); diff --git a/src/commitment/pedersen/mod.rs b/src/commitment/pedersen/mod.rs index 959d9790..7d216b37 100644 --- a/src/commitment/pedersen/mod.rs +++ b/src/commitment/pedersen/mod.rs @@ -1,4 +1,4 @@ -use crate::{CRHScheme, Error, Vec}; +use crate::{Error, Vec, CRH}; use ark_ec::ProjectiveCurve; use ark_ff::{bytes::ToBytes, BitIteratorLE, Field, FpParameters, PrimeField, ToConstraintField}; use ark_std::io::{Result as IoResult, Write}; diff --git a/src/crh/bowe_hopwood/constraints.rs b/src/crh/bowe_hopwood/constraints.rs index bc0dfed4..3209549b 100644 --- a/src/crh/bowe_hopwood/constraints.rs +++ b/src/crh/bowe_hopwood/constraints.rs @@ -2,22 +2,19 @@ use core::{borrow::Borrow, iter, marker::PhantomData}; use crate::{ crh::{ - bowe_hopwood::{Parameters, CHUNK_SIZE}, - pedersen::{self, Window}, - CRHSchemeGadget, TwoToOneCRHSchemeGadget, + bowe_hopwood::{self, Parameters, TwoToOneCRH, CHUNK_SIZE, CRH}, + pedersen::Window, + CRHWithGadget, TwoToOneCRHWithGadget, }, Vec, }; -use ark_ec::{ - twisted_edwards_extended::GroupProjective as TEProjective, ModelParameters, TEModelParameters, -}; +use ark_ec::{ModelParameters, TEModelParameters}; use ark_ff::Field; use ark_r1cs_std::{ alloc::AllocVar, groups::curves::twisted_edwards::AffineVar, prelude::*, uint8::UInt8, }; use ark_relations::r1cs::{Namespace, SynthesisError}; -use crate::crh::bowe_hopwood::{TwoToOneCRH, CRH}; use ark_r1cs_std::bits::boolean::Boolean; type ConstraintF

= <

::BaseField as Field>::BasePrimeField; @@ -30,32 +27,26 @@ pub struct ParametersVar { _window: PhantomData, } -pub struct CRHGadget>> -where - for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, -{ - #[doc(hidden)] - _params: PhantomData

, - #[doc(hidden)] - _base_field: PhantomData, -} +type BF

=

::BaseField; +type BFVar

= as FieldWithVar>::Var; -impl CRHSchemeGadget, ConstraintF

> for CRHGadget +impl CRHWithGadget> for CRH where - for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, - F: FieldVar>, - F: TwoBitLookupGadget, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget, TableConstant = P::BaseField>, + BF

: FieldWithVar, + BFVar

: TwoBitLookupGadget, TableConstant = BF

> + + ThreeBitCondNegLookupGadget, TableConstant = BF

>, + + for<'a> &'a BFVar

: FieldOpsBounds<'a, BF

, BFVar

>, P: TEModelParameters, W: Window, { type InputVar = [UInt8>]; - type OutputVar = F; + type OutputVar = BFVar

; type ParametersVar = ParametersVar; #[tracing::instrument(target = "r1cs", skip(parameters, input))] - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, input: &Self::InputVar, ) -> Result { @@ -90,60 +81,52 @@ where } } -pub struct TwoToOneCRHGadget>> +impl TwoToOneCRHWithGadget> for TwoToOneCRH where - for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, -{ - #[doc(hidden)] - _params: PhantomData

, - #[doc(hidden)] - _base_field: PhantomData, -} + BF

: FieldWithVar, + BFVar

: TwoBitLookupGadget, TableConstant = BF

> + + ThreeBitCondNegLookupGadget, TableConstant = BF

>, -impl TwoToOneCRHSchemeGadget, ConstraintF

> for TwoToOneCRHGadget -where - for<'a> &'a F: FieldOpsBounds<'a, P::BaseField, F>, - F: FieldVar>, - F: TwoBitLookupGadget, TableConstant = P::BaseField> - + ThreeBitCondNegLookupGadget, TableConstant = P::BaseField>, + for<'a> &'a BFVar

: FieldOpsBounds<'a, BF

, BFVar

>, P: TEModelParameters, W: Window, { type InputVar = [UInt8>]; - type OutputVar = F; + type OutputVar = BFVar

; type ParametersVar = ParametersVar; #[tracing::instrument(target = "r1cs", skip(parameters))] - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, - left_input: &Self::InputVar, - right_input: &Self::InputVar, + left: &Self::InputVar, + right: &Self::InputVar, ) -> Result { - let input_size_bytes = pedersen::CRH::, W>::INPUT_SIZE_BITS / 8; + let input_size_bytes = bowe_hopwood::CRH::::INPUT_SIZE_BITS / 8; // assume equality of left and right length - assert_eq!(left_input.len(), right_input.len()); + assert_eq!(left.len(), right.len()); // assume sum of left and right length is at most the CRH length limit - assert!(left_input.len() + right_input.len() <= input_size_bytes); + assert!(left.len() + right.len() <= input_size_bytes); - let num_trailing_zeros = input_size_bytes - (left_input.len() + right_input.len()); - let chained_input: Vec<_> = left_input - .to_vec() + let num_trailing_zeros = input_size_bytes - (left.len() + right.len()); + let chained: Vec<_> = left .into_iter() - .chain(right_input.to_vec().into_iter()) + .chain(right.into_iter()) + .cloned() .chain(iter::repeat(UInt8::constant(0u8)).take(num_trailing_zeros)) .collect(); - CRHGadget::::evaluate(parameters, &chained_input) + CRH::::evaluate_gadget(parameters, &chained) } - fn compress( + #[tracing::instrument(target = "r1cs", skip(parameters, left, right))] + fn compress_gadget( parameters: &Self::ParametersVar, - left_input: &Self::OutputVar, - right_input: &Self::OutputVar, + left: &Self::OutputVar, + right: &Self::OutputVar, ) -> Result { - let left_input_bytes = left_input.to_bytes()?; - let right_input_bytes = right_input.to_bytes()?; - Self::evaluate(parameters, &left_input_bytes, &right_input_bytes) + let left = left.to_bytes()?; + let right = right.to_bytes()?; + Self::evaluate_gadget(parameters, &left, &right) } } @@ -170,20 +153,17 @@ where mod test { use ark_std::rand::Rng; - use crate::crh::bowe_hopwood; - use crate::crh::{pedersen, TwoToOneCRHScheme, TwoToOneCRHSchemeGadget}; - use crate::{CRHScheme, CRHSchemeGadget}; - use ark_ed_on_bls12_381::{constraints::FqVar, EdwardsParameters, Fq as Fr}; + use crate::crh::{pedersen, TwoToOneCRH, TwoToOneCRHGadget}; + use crate::{crh::bowe_hopwood, Gadget}; + use crate::{CRHGadget, CRH}; + use ark_ed_on_bls12_381::{EdwardsParameters, Fq as Fr}; use ark_r1cs_std::{alloc::AllocVar, uint8::UInt8, R1CSVar}; use ark_relations::r1cs::{ConstraintSystem, ConstraintSystemRef}; use ark_std::test_rng; type TestCRH = bowe_hopwood::CRH; - type TestCRHGadget = bowe_hopwood::constraints::CRHGadget; type TestTwoToOneCRH = bowe_hopwood::TwoToOneCRH; - type TestTwoToOneCRHGadget = - bowe_hopwood::constraints::TwoToOneCRHGadget; #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; @@ -219,18 +199,17 @@ mod test { let parameters = TestCRH::setup(rng).unwrap(); let primitive_result = TestCRH::evaluate(¶meters, input.as_slice()).unwrap(); - let parameters_var = - >::ParametersVar::new_witness( - ark_relations::ns!(cs, "parameters_var"), - || Ok(¶meters), - ) - .unwrap(); + let parameters_var = as CRHGadget>::ParametersVar::new_witness( + ark_relations::ns!(cs, "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(); + let result_var = Gadget::::evaluate(¶meters_var, &input_var).unwrap(); println!("number of constraints total: {}", cs.num_constraints()); @@ -245,25 +224,21 @@ mod test { // Max input size is 63 bytes. That leaves 31 for the left half, 31 for the right, and 1 // byte of padding. - let (left_input, left_input_var) = generate_u8_input(cs.clone(), 31, rng); - let (right_input, right_input_var) = generate_u8_input(cs.clone(), 31, rng); + let (left, left_var) = generate_u8_input(cs.clone(), 31, rng); + let (right, right_var) = generate_u8_input(cs.clone(), 31, rng); let parameters = TestTwoToOneCRH::setup(rng).unwrap(); let primitive_result = - TestTwoToOneCRH::evaluate(¶meters, left_input.as_slice(), right_input.as_slice()) - .unwrap(); + TestTwoToOneCRH::evaluate(¶meters, left.as_slice(), right.as_slice()).unwrap(); - let parameters_var = >::ParametersVar::new_witness( - ark_relations::ns!(cs, "parameters_var"), - || Ok(¶meters), - ) - .unwrap(); + let parameters_var = + as TwoToOneCRHGadget>::ParametersVar::new_witness( + ark_relations::ns!(cs, "parameters_var"), + || Ok(¶meters), + ) + .unwrap(); let result_var = - TestTwoToOneCRHGadget::evaluate(¶meters_var, &left_input_var, &right_input_var) - .unwrap(); + Gadget::::evaluate(¶meters_var, &left_var, &right_var).unwrap(); let primitive_result = primitive_result; assert_eq!(primitive_result, result_var.value().unwrap()); diff --git a/src/crh/bowe_hopwood/mod.rs b/src/crh/bowe_hopwood/mod.rs index 6fac6051..dc33de89 100644 --- a/src/crh/bowe_hopwood/mod.rs +++ b/src/crh/bowe_hopwood/mod.rs @@ -12,7 +12,7 @@ use ark_std::{ use rayon::prelude::*; use super::pedersen; -use crate::crh::{CRHScheme, TwoToOneCRHScheme}; +use crate::crh::{TwoToOneCRH as TwoToOneCRHTrait, CRH as CRHTrait}; use ark_ec::{ twisted_edwards_extended::GroupProjective as TEProjective, ProjectiveCurve, TEModelParameters, }; @@ -39,6 +39,8 @@ pub struct CRH { } impl CRH { + pub(crate) const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS; + pub fn create_generators(rng: &mut R) -> Vec>> { let mut generators = Vec::new(); for _ in 0..W::NUM_WINDOWS { @@ -62,14 +64,15 @@ pub struct TwoToOneCRH { } impl TwoToOneCRH { - const INPUT_SIZE_BITS: usize = pedersen::CRH::, W>::INPUT_SIZE_BITS; - const HALF_INPUT_SIZE_BITS: usize = Self::INPUT_SIZE_BITS / 2; + pub(crate) const INPUT_SIZE_BITS: usize = W::WINDOW_SIZE * W::NUM_WINDOWS; + pub(crate) const HALF_INPUT_SIZE_BITS: usize = Self::INPUT_SIZE_BITS / 2; + pub fn create_generators(rng: &mut R) -> Vec>> { CRH::::create_generators(rng) } } -impl CRHScheme for CRH { +impl CRHTrait for CRH { type Input = [u8]; type Output = P::BaseField; @@ -183,7 +186,7 @@ impl CRHScheme for CRH { } } -impl TwoToOneCRHScheme for TwoToOneCRH { +impl TwoToOneCRHTrait for TwoToOneCRH { type Input = [u8]; type Output = P::BaseField; @@ -195,29 +198,29 @@ impl TwoToOneCRHScheme for TwoToOneCR /// A simple implementation method: just concat the left input and right input together /// - /// `evaluate` requires that `left_input` and `right_input` are of equal length. + /// `evaluate` requires that `left` and `right` are of equal length. fn evaluate>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { - let left_input = left_input.borrow(); - let right_input = right_input.borrow(); + let left = left.borrow(); + let right = right.borrow(); assert_eq!( - left_input.len(), - right_input.len(), + left.len(), + right.len(), "left and right input should be of equal length" ); // check overflow - debug_assert!(left_input.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); - debug_assert!(right_input.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); + debug_assert!(left.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); + debug_assert!(right.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); let mut buffer = vec![0u8; Self::INPUT_SIZE_BITS / 8]; buffer .iter_mut() - .zip(left_input.iter().chain(right_input.iter())) + .zip(left.iter().chain(right.iter())) .for_each(|(b, l_b)| *b = *l_b); CRH::::evaluate(parameters, buffer) @@ -225,13 +228,13 @@ impl TwoToOneCRHScheme for TwoToOneCR fn compress>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { Self::evaluate( parameters, - crate::to_unchecked_bytes!(left_input)?, - crate::to_unchecked_bytes!(right_input)?, + crate::to_unchecked_bytes!(left)?, + crate::to_unchecked_bytes!(right)?, ) } } @@ -250,7 +253,7 @@ impl Debug for Parameters

{ mod test { use crate::{ crh::{bowe_hopwood, pedersen::Window}, - CRHScheme, + CRH, }; use ark_ed_on_bls12_381::EdwardsParameters; use ark_std::test_rng; diff --git a/src/crh/constraints.rs b/src/crh/constraints.rs index 81c0aebd..e64147bf 100644 --- a/src/crh/constraints.rs +++ b/src/crh/constraints.rs @@ -1,51 +1,137 @@ use ark_ff::Field; use core::fmt::Debug; -use crate::crh::{CRHScheme, TwoToOneCRHScheme}; +use crate::{ + crh::{TwoToOneCRH, CRH}, + Gadget, +}; use ark_relations::r1cs::SynthesisError; use ark_r1cs_std::prelude::*; -pub trait CRHSchemeGadget: Sized { +pub trait CRHWithGadget: CRH { type InputVar: ?Sized; type OutputVar: EqGadget + ToBytesGadget + CondSelectGadget - + AllocVar + + AllocVar + R1CSVar + Debug + Clone + Sized; - type ParametersVar: AllocVar + Clone; + type ParametersVar: AllocVar + Clone; - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, input: &Self::InputVar, ) -> Result; } -pub trait TwoToOneCRHSchemeGadget: Sized { +pub trait CRHGadget { + type Native: CRHWithGadget< + ConstraintF, + InputVar = Self::InputVar, + OutputVar = Self::OutputVar, + ParametersVar = Self::ParametersVar, + >; type InputVar: ?Sized; type OutputVar: EqGadget + ToBytesGadget + CondSelectGadget - + AllocVar + + AllocVar<::Output, ConstraintF> + R1CSVar + Debug + Clone + Sized; - - type ParametersVar: AllocVar + Clone; + type ParametersVar: AllocVar<::Parameters, ConstraintF> + Clone; fn evaluate( parameters: &Self::ParametersVar, - left_input: &Self::InputVar, - right_input: &Self::InputVar, + input: &Self::InputVar, + ) -> Result { + Self::Native::evaluate_gadget(parameters, input) + } +} + +pub trait TwoToOneCRHWithGadget: TwoToOneCRH { + type InputVar: ?Sized; + type OutputVar: EqGadget + + ToBytesGadget + + CondSelectGadget + + AllocVar + + R1CSVar + + Debug + + Clone + + Sized; + + type ParametersVar: AllocVar + Clone; + + fn evaluate_gadget( + parameters: &Self::ParametersVar, + left: &Self::InputVar, + right: &Self::InputVar, ) -> Result; - fn compress( + fn compress_gadget( parameters: &Self::ParametersVar, - left_input: &Self::OutputVar, - right_input: &Self::OutputVar, + left: &Self::OutputVar, + right: &Self::OutputVar, ) -> Result; } +pub trait TwoToOneCRHGadget { + type Native: TwoToOneCRHWithGadget< + ConstraintF, + InputVar = Self::InputVar, + OutputVar = Self::OutputVar, + ParametersVar = Self::ParametersVar, + >; + type InputVar: ?Sized; + type OutputVar: EqGadget + + ToBytesGadget + + CondSelectGadget + + AllocVar<::Output, ConstraintF> + + R1CSVar + + Debug + + Clone + + Sized; + + type ParametersVar: AllocVar<::Parameters, ConstraintF> + Clone; + + fn evaluate( + parameters: &Self::ParametersVar, + left: &Self::InputVar, + right: &Self::InputVar, + ) -> Result { + Self::Native::evaluate_gadget(parameters, left, right) + } + + fn compress( + parameters: &Self::ParametersVar, + left: &Self::OutputVar, + right: &Self::OutputVar, + ) -> Result { + Self::Native::compress_gadget(parameters, left, right) + } +} + +impl CRHGadget for Gadget +where + H: CRHWithGadget, + ConstraintF: Field, +{ + type Native = H; + type InputVar = H::InputVar; + type ParametersVar = H::ParametersVar; + type OutputVar = H::OutputVar; +} + +impl TwoToOneCRHGadget for Gadget +where + H: TwoToOneCRHWithGadget, + ConstraintF: Field, +{ + type Native = H; + type InputVar = H::InputVar; + type ParametersVar = H::ParametersVar; + type OutputVar = H::OutputVar; +} diff --git a/src/crh/injective_map/constraints.rs b/src/crh/injective_map/constraints.rs index ea67326c..81712d53 100644 --- a/src/crh/injective_map/constraints.rs +++ b/src/crh/injective_map/constraints.rs @@ -1,166 +1,110 @@ -use crate::crh::{ - constraints, - injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor}, - pedersen::{constraints as ped_constraints, Window}, - TwoToOneCRHSchemeGadget, +use crate::{ + crh::{ + constraints, + injective_map::{InjectiveMap, PedersenCRHCompressor, TECompressor}, + pedersen::{self, constraints as ped_constraints, Window}, + CRHGadget, TwoToOneCRHGadget, + }, + Gadget, }; -use core::{fmt::Debug, marker::PhantomData}; +use core::fmt::Debug; use crate::crh::injective_map::PedersenTwoToOneCRHCompressor; -use crate::CRHSchemeGadget; use ark_ec::{ - models::{ModelParameters, TEModelParameters}, - twisted_edwards_extended::GroupProjective as TEProjective, - ProjectiveCurve, + models::TEModelParameters, twisted_edwards_extended::GroupProjective as TEProjective, + ModelParameters, ProjectiveCurve, }; use ark_ff::fields::{Field, PrimeField, SquareRootField}; -use ark_r1cs_std::{ - fields::fp::FpVar, - groups::{curves::twisted_edwards::AffineVar as TEVar, CurveVar}, - prelude::*, -}; +use ark_r1cs_std::{groups::curves::twisted_edwards::AffineVar as TEVar, prelude::*}; use ark_relations::r1cs::SynthesisError; type ConstraintF = <::BaseField as Field>::BasePrimeField; -pub trait InjectiveMapGadget< - C: ProjectiveCurve, - I: InjectiveMap, - GG: CurveVar>, -> where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ +pub trait InjectiveMapGadget>>: InjectiveMap { type OutputVar: EqGadget> + ToBytesGadget> + CondSelectGadget> - + AllocVar> - + R1CSVar, Value = I::Output> + + AllocVar> + + R1CSVar, Value = Self::Output> + Debug + Clone + Sized; - fn evaluate(ge: &GG) -> Result; + fn evaluate(ge: &C::Var) -> Result; } -pub struct TECompressorGadget; +type BFVar

= <

::BaseField as FieldWithVar>::Var; -impl InjectiveMapGadget, TECompressor, TEVar>> - for TECompressorGadget +impl

InjectiveMapGadget> for TECompressor where - F: PrimeField + SquareRootField, - P: TEModelParameters + ModelParameters, + P: TEModelParameters, + BFVar

: + TwoBitLookupGadget<::BasePrimeField, TableConstant = P::BaseField>, + for<'a> &'a BFVar

: FieldOpsBounds<'a, P::BaseField, BFVar

>, + P::BaseField: FieldWithVar + PrimeField + SquareRootField, { - type OutputVar = FpVar; + type OutputVar = BFVar

; - fn evaluate(ge: &TEVar>) -> Result { + fn evaluate(ge: &TEVar

) -> Result { Ok(ge.x.clone()) } } -pub struct PedersenCRHCompressorGadget -where - C: ProjectiveCurve, - I: InjectiveMap, - W: Window, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, - IG: InjectiveMapGadget, -{ - #[doc(hidden)] - _compressor: PhantomData, - #[doc(hidden)] - _compressor_gadget: PhantomData, - #[doc(hidden)] - _crh: ped_constraints::CRHGadget, -} - -impl constraints::CRHSchemeGadget, ConstraintF> - for PedersenCRHCompressorGadget +impl constraints::CRHWithGadget> for PedersenCRHCompressor where - C: ProjectiveCurve, - I: InjectiveMap, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, - IG: InjectiveMapGadget, + C: CurveWithVar>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, + I: InjectiveMapGadget, W: Window, { type InputVar = [UInt8>]; - type OutputVar = IG::OutputVar; - type ParametersVar = ped_constraints::CRHParametersVar; + type OutputVar = I::OutputVar; + type ParametersVar = ped_constraints::CRHParametersVar; #[tracing::instrument(target = "r1cs", skip(parameters, input))] - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, input: &Self::InputVar, ) -> Result { - let result = as CRHSchemeGadget<_, _>>::evaluate( - parameters, input, - )?; - IG::evaluate(&result) + let result = Gadget::>::evaluate(parameters, input)?; + I::evaluate(&result) } } -pub struct PedersenTwoToOneCRHCompressorGadget +impl constraints::TwoToOneCRHWithGadget> + for PedersenTwoToOneCRHCompressor where - C: ProjectiveCurve, - I: InjectiveMap, - W: Window, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, - IG: InjectiveMapGadget, -{ - #[doc(hidden)] - _compressor: PhantomData, - #[doc(hidden)] - _compressor_gadget: PhantomData, - #[doc(hidden)] - _crh: ped_constraints::CRHGadget, -} - -impl - constraints::TwoToOneCRHSchemeGadget, ConstraintF> - for PedersenTwoToOneCRHCompressorGadget -where - C: ProjectiveCurve, - I: InjectiveMap, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, - IG: InjectiveMapGadget, + C: CurveWithVar>, + I: InjectiveMapGadget, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, W: Window, { type InputVar = [UInt8>]; - type OutputVar = IG::OutputVar; - type ParametersVar = ped_constraints::CRHParametersVar; + type OutputVar = I::OutputVar; + type ParametersVar = ped_constraints::CRHParametersVar; #[tracing::instrument(target = "r1cs", skip(parameters))] - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, - left_input: &Self::InputVar, - right_input: &Self::InputVar, + left: &Self::InputVar, + right: &Self::InputVar, ) -> Result { // assume equality of left and right length - assert_eq!(left_input.len(), right_input.len()); - let result = ped_constraints::TwoToOneCRHGadget::::evaluate( - parameters, - left_input, - right_input, - )?; - IG::evaluate(&result) + assert_eq!(left.len(), right.len()); + let result = Gadget::>::evaluate(parameters, left, right)?; + I::evaluate(&result) } - fn compress( + #[tracing::instrument(target = "r1cs", skip(parameters))] + fn compress_gadget( parameters: &Self::ParametersVar, - left_input: &Self::OutputVar, - right_input: &Self::OutputVar, + left: &Self::OutputVar, + right: &Self::OutputVar, ) -> Result { - let left_input_bytes = left_input.to_non_unique_bytes()?; - let right_input_bytes = right_input.to_non_unique_bytes()?; - >::evaluate( - parameters, - &left_input_bytes, - &right_input_bytes, - ) + let left = left.to_non_unique_bytes()?; + let right = right.to_non_unique_bytes()?; + Gadget::::evaluate(parameters, &left, &right) } } diff --git a/src/crh/injective_map/mod.rs b/src/crh/injective_map/mod.rs index 03c671e5..9d483673 100644 --- a/src/crh/injective_map/mod.rs +++ b/src/crh/injective_map/mod.rs @@ -3,7 +3,7 @@ use ark_ff::bytes::ToBytes; use ark_std::rand::Rng; use ark_std::{fmt::Debug, hash::Hash, marker::PhantomData}; -use super::{pedersen, CRHScheme, TwoToOneCRHScheme}; +use super::{pedersen, TwoToOneCRH, CRH}; use ark_ec::{ models::{ModelParameters, TEModelParameters}, twisted_edwards_extended::{GroupAffine as TEAffine, GroupProjective as TEProjective}, @@ -45,10 +45,10 @@ pub struct PedersenCRHCompressor, W: pede _window: PhantomData, } -impl, W: pedersen::Window> CRHScheme +impl, W: pedersen::Window> CRH for PedersenCRHCompressor { - type Input = as CRHScheme>::Input; + type Input = as CRH>::Input; type Output = I::Output; type Parameters = pedersen::Parameters; @@ -80,10 +80,10 @@ pub struct PedersenTwoToOneCRHCompressor< _window: PhantomData, } -impl, W: pedersen::Window> TwoToOneCRHScheme +impl, W: pedersen::Window> TwoToOneCRH for PedersenTwoToOneCRHCompressor { - type Input = as TwoToOneCRHScheme>::Input; + type Input = as TwoToOneCRH>::Input; type Output = I::Output; type Parameters = pedersen::Parameters; @@ -93,14 +93,12 @@ impl, W: pedersen::Window> TwoToOneCRHSch fn evaluate>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { let eval_time = start_timer!(|| "PedersenCRHCompressor::Eval"); let result = I::injective_map(&pedersen::TwoToOneCRH::::evaluate( - parameters, - left_input, - right_input, + parameters, left, right, )?)?; end_timer!(eval_time); Ok(result) @@ -108,14 +106,14 @@ impl, W: pedersen::Window> TwoToOneCRHSch fn compress>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { // convert output to input Self::evaluate( parameters, - crate::to_unchecked_bytes!(left_input)?, - crate::to_unchecked_bytes!(right_input)?, + crate::to_unchecked_bytes!(left)?, + crate::to_unchecked_bytes!(right)?, ) } } diff --git a/src/crh/mod.rs b/src/crh/mod.rs index 285a4241..63ed3881 100644 --- a/src/crh/mod.rs +++ b/src/crh/mod.rs @@ -20,7 +20,7 @@ pub use constraints::*; /// Interface to CRH. Note that in this release, while all implementations of `CRH` have fixed length, /// variable length CRH may also implement this trait in future. -pub trait CRHScheme { +pub trait CRH { type Input: ?Sized; type Output: ToBytes + Clone @@ -40,7 +40,7 @@ pub trait CRHScheme { } /// CRH used by merkle tree inner hash. Merkle tree will convert leaf output to bytes first. -pub trait TwoToOneCRHScheme { +pub trait TwoToOneCRH { /// Raw Input type of TwoToOneCRH type Input: ?Sized; /// Raw Output type of TwoToOneCRH @@ -58,13 +58,13 @@ pub trait TwoToOneCRHScheme { fn evaluate>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result; fn compress>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result; } diff --git a/src/crh/pedersen/constraints.rs b/src/crh/pedersen/constraints.rs index 1ec6d6c5..f86e63c9 100644 --- a/src/crh/pedersen/constraints.rs +++ b/src/crh/pedersen/constraints.rs @@ -1,7 +1,7 @@ use crate::{ crh::{ - pedersen::{Parameters, Window}, - CRHSchemeGadget as CRHGadgetTrait, + pedersen::{Parameters, TwoToOneCRH, Window, CRH}, + CRHWithGadget, TwoToOneCRHWithGadget, }, Vec, }; @@ -10,47 +10,27 @@ use ark_ff::Field; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::{Namespace, SynthesisError}; -use crate::crh::pedersen::{TwoToOneCRH, CRH}; -use crate::crh::{CRHSchemeGadget, TwoToOneCRHSchemeGadget}; -use core::{borrow::Borrow, marker::PhantomData}; +use core::{borrow::Borrow, iter}; +type ConstraintF = <::BaseField as Field>::BasePrimeField; #[derive(Derivative)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct CRHParametersVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ +#[derivative(Clone(bound = "C: CurveWithVar>"))] +pub struct CRHParametersVar>> { params: Parameters, - #[doc(hidden)] - _group_g: PhantomData, } -type ConstraintF = <::BaseField as Field>::BasePrimeField; -pub struct CRHGadget>, W: Window> +impl CRHWithGadget> for CRH where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - #[doc(hidden)] - _group: PhantomData<*const C>, - #[doc(hidden)] - _group_var: PhantomData<*const GG>, - #[doc(hidden)] - _window: PhantomData<*const W>, -} - -impl CRHSchemeGadget, ConstraintF> for CRHGadget -where - C: ProjectiveCurve, - GG: CurveVar>, + C: CurveWithVar>, W: Window, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, { type InputVar = [UInt8>]; - type OutputVar = GG; - type ParametersVar = CRHParametersVar; + type OutputVar = C::Var; + type ParametersVar = CRHParametersVar; #[tracing::instrument(target = "r1cs", skip(parameters, input))] - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, input: &Self::InputVar, ) -> Result { @@ -71,70 +51,59 @@ where .flat_map(|b| b.to_bits_le().unwrap()) .collect(); let input_in_bits = input_in_bits.chunks(W::WINDOW_SIZE); - let result = - GG::precomputed_base_multiscalar_mul_le(¶meters.params.generators, input_in_bits)?; + let result = C::Var::precomputed_base_multiscalar_mul_le( + ¶meters.params.generators, + input_in_bits, + )?; Ok(result) } } -pub struct TwoToOneCRHGadget>, W: Window> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - #[doc(hidden)] - _group: PhantomData<*const C>, - #[doc(hidden)] - _group_var: PhantomData<*const GG>, - #[doc(hidden)] - _window: PhantomData<*const W>, -} - -impl TwoToOneCRHSchemeGadget, ConstraintF> - for TwoToOneCRHGadget +impl TwoToOneCRHWithGadget> for TwoToOneCRH where - C: ProjectiveCurve, - GG: CurveVar>, + C: CurveWithVar>, W: Window, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + for<'a> &'a C::Var: GroupOpsBounds<'a, C, C::Var>, { type InputVar = [UInt8>]; - type OutputVar = GG; - type ParametersVar = CRHParametersVar; + type OutputVar = C::Var; + type ParametersVar = CRHParametersVar; - #[tracing::instrument(target = "r1cs", skip(parameters))] - fn evaluate( + #[tracing::instrument(target = "r1cs", skip(parameters, left, right))] + fn evaluate_gadget( parameters: &Self::ParametersVar, - left_input: &Self::InputVar, - right_input: &Self::InputVar, + left: &Self::InputVar, + right: &Self::InputVar, ) -> Result { // assume equality of left and right length - assert_eq!(left_input.len(), right_input.len()); - let chained_input: Vec<_> = left_input - .to_vec() - .into_iter() - .chain(right_input.to_vec().into_iter()) + assert_eq!(left.len(), right.len()); + let input_size_bytes = CRH::::INPUT_SIZE_BITS / 8; + let num_trailing_zeros = input_size_bytes - 2 * left.len(); + let chained: Vec<_> = left + .iter() + .chain(right.iter()) + .cloned() + .chain(iter::repeat(UInt8::constant(0u8)).take(num_trailing_zeros)) .collect(); - CRHGadget::::evaluate(parameters, &chained_input) + CRH::::evaluate_gadget(parameters, &chained) } - #[tracing::instrument(target = "r1cs", skip(parameters))] - fn compress( + #[tracing::instrument(target = "r1cs", skip(parameters, left, right))] + fn compress_gadget( parameters: &Self::ParametersVar, - left_input: &Self::OutputVar, - right_input: &Self::OutputVar, + left: &Self::OutputVar, + right: &Self::OutputVar, ) -> Result { // convert output to bytes - let left_input = left_input.to_bytes()?; - let right_input = right_input.to_bytes()?; - Self::evaluate(parameters, &left_input, &right_input) + let left = left.to_bytes()?; + let right = right.to_bytes()?; + Self::evaluate_gadget(parameters, &left, &right) } } -impl AllocVar, ConstraintF> for CRHParametersVar +impl AllocVar, ConstraintF> for CRHParametersVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, { #[tracing::instrument(target = "r1cs", skip(_cs, f))] fn new_variable>>( @@ -143,17 +112,15 @@ where _mode: AllocationMode, ) -> Result { let params = f()?.borrow().clone(); - Ok(CRHParametersVar { - params, - _group_g: PhantomData, - }) + Ok(CRHParametersVar { params }) } } #[cfg(test)] mod test { - use crate::crh::{ - pedersen, CRHScheme, CRHSchemeGadget, TwoToOneCRHScheme, TwoToOneCRHSchemeGadget, + use crate::{ + crh::{pedersen, CRHGadget, TwoToOneCRH, TwoToOneCRHGadget, CRH}, + Gadget, }; use ark_ec::ProjectiveCurve; use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq as Fr}; @@ -163,11 +130,8 @@ mod test { use ark_std::{test_rng, UniformRand}; type TestCRH = pedersen::CRH; - type TestCRHGadget = pedersen::constraints::CRHGadget; type TestTwoToOneCRH = pedersen::TwoToOneCRH; - type TestTwoToOneCRHGadget = - pedersen::constraints::TwoToOneCRHGadget; #[derive(Clone, PartialEq, Eq, Hash)] pub(super) struct Window; @@ -217,7 +181,7 @@ mod test { ) .unwrap(); - let result_var = TestCRHGadget::evaluate(¶meters_var, &input_var).unwrap(); + let result_var = Gadget::::evaluate(¶meters_var, &input_var).unwrap(); let primitive_result = primitive_result; assert_eq!(primitive_result, result_var.value().unwrap()); @@ -229,11 +193,10 @@ mod test { let rng = &mut test_rng(); let cs = ConstraintSystem::::new_ref(); - let (left_input, left_input_var) = generate_affine(cs.clone(), rng); - let (right_input, right_input_var) = generate_affine(cs.clone(), rng); + let (left, left_var) = generate_affine(cs.clone(), rng); + let (right, right_var) = generate_affine(cs.clone(), rng); let parameters = TestTwoToOneCRH::setup(rng).unwrap(); - let primitive_result = - TestTwoToOneCRH::compress(¶meters, left_input, right_input).unwrap(); + let primitive_result = TestTwoToOneCRH::compress(¶meters, left, right).unwrap(); let parameters_var = pedersen::constraints::CRHParametersVar::new_constant( ark_relations::ns!(cs, "CRH Parameters"), @@ -242,8 +205,7 @@ mod test { .unwrap(); let result_var = - TestTwoToOneCRHGadget::compress(¶meters_var, &left_input_var, &right_input_var) - .unwrap(); + Gadget::::compress(¶meters_var, &left_var, &right_var).unwrap(); let primitive_result = primitive_result; assert_eq!(primitive_result, result_var.value().unwrap()); diff --git a/src/crh/pedersen/mod.rs b/src/crh/pedersen/mod.rs index 566831c9..70f1a763 100644 --- a/src/crh/pedersen/mod.rs +++ b/src/crh/pedersen/mod.rs @@ -7,7 +7,7 @@ use ark_std::{ #[cfg(feature = "parallel")] use rayon::prelude::*; -use crate::crh::{CRHScheme, TwoToOneCRHScheme}; +use crate::crh::{TwoToOneCRH as TwoToOneCRHTrait, CRH as CRHTrait}; use ark_ec::ProjectiveCurve; use ark_ff::{Field, ToConstraintField}; use ark_serialize::CanonicalSerialize; @@ -53,7 +53,7 @@ impl CRH { } } -impl CRHScheme for CRH { +impl CRHTrait for CRH { type Input = [u8]; type Output = C::Affine; type Parameters = Parameters; @@ -143,7 +143,7 @@ impl TwoToOneCRH { } } -impl TwoToOneCRHScheme for TwoToOneCRH { +impl TwoToOneCRHTrait for TwoToOneCRH { type Input = [u8]; type Output = C::Affine; type Parameters = Parameters; @@ -154,25 +154,25 @@ impl TwoToOneCRHScheme for TwoToOneCRH { fn evaluate>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { - let left_input = left_input.borrow(); - let right_input = right_input.borrow(); + let left = left.borrow(); + let right = right.borrow(); assert_eq!( - left_input.len(), - right_input.len(), + left.len(), + right.len(), "left and right input should be of equal length" ); // check overflow - debug_assert!(left_input.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); + debug_assert!(left.len() * 8 <= Self::HALF_INPUT_SIZE_BITS); let mut buffer = vec![0u8; (Self::HALF_INPUT_SIZE_BITS + Self::HALF_INPUT_SIZE_BITS) / 8]; buffer .iter_mut() - .zip(left_input.iter().chain(right_input.iter())) + .zip(left.iter().chain(right.iter())) .for_each(|(b, l_b)| *b = *l_b); CRH::::evaluate(parameters, buffer.as_slice()) @@ -180,16 +180,16 @@ impl TwoToOneCRHScheme for TwoToOneCRH { /// A simple implementation method: just concat the left input and right input together /// - /// `evaluate` requires that `left_input` and `right_input` are of equal length. + /// `evaluate` requires that `left` and `right` are of equal length. fn compress>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { Self::evaluate( parameters, - crate::to_unchecked_bytes!(left_input)?, - crate::to_unchecked_bytes!(right_input)?, + crate::to_unchecked_bytes!(left)?, + crate::to_unchecked_bytes!(right)?, ) } } diff --git a/src/crh/poseidon/constraints.rs b/src/crh/poseidon/constraints.rs index 93ae7f1b..2486f0d3 100644 --- a/src/crh/poseidon/constraints.rs +++ b/src/crh/poseidon/constraints.rs @@ -1,8 +1,6 @@ use crate::crh::poseidon::{TwoToOneCRH, CRH}; -use crate::crh::{ - CRHSchemeGadget as CRHGadgetTrait, TwoToOneCRHSchemeGadget as TwoToOneCRHGadgetTrait, -}; -use crate::{CRHScheme, Vec}; +use crate::crh::{CRHWithGadget, TwoToOneCRHWithGadget, CRH as _}; +use crate::Vec; use ark_ff::PrimeField; use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; use ark_r1cs_std::fields::fp::FpVar; @@ -13,35 +11,27 @@ use ark_sponge::poseidon::constraints::PoseidonSpongeVar; use ark_sponge::poseidon::PoseidonParameters; use ark_sponge::Absorb; use ark_std::borrow::Borrow; -use ark_std::marker::PhantomData; #[derive(Clone)] pub struct CRHParametersVar { pub parameters: PoseidonParameters, } -pub struct CRHGadget { - field_phantom: PhantomData, -} - -impl CRHGadgetTrait, F> for CRHGadget { +impl CRHWithGadget for CRH { type InputVar = [FpVar]; type OutputVar = FpVar; type ParametersVar = CRHParametersVar; - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, input: &Self::InputVar, ) -> Result { let cs = input.cs(); if cs.is_none() { - let mut constant_input = Vec::new(); - for var in input.iter() { - constant_input.push(var.value()?); - } + let input = input.iter().map(|f| f.value().unwrap()).collect::>(); Ok(FpVar::Constant( - CRH::::evaluate(¶meters.parameters, constant_input).unwrap(), + CRH::::evaluate(¶meters.parameters, input).unwrap(), )) } else { let mut sponge = PoseidonSpongeVar::new(cs, ¶meters.parameters); @@ -52,42 +42,35 @@ impl CRHGadgetTrait, F> for CRHGadget { } } -pub struct TwoToOneCRHGadget { - field_phantom: PhantomData, -} - -impl TwoToOneCRHGadgetTrait, F> for TwoToOneCRHGadget { +impl TwoToOneCRHWithGadget for TwoToOneCRH { type InputVar = FpVar; type OutputVar = FpVar; type ParametersVar = CRHParametersVar; - fn evaluate( + fn evaluate_gadget( parameters: &Self::ParametersVar, - left_input: &Self::InputVar, - right_input: &Self::InputVar, + left: &Self::InputVar, + right: &Self::InputVar, ) -> Result { - Self::compress(parameters, left_input, right_input) + Self::compress_gadget(parameters, left, right) } - fn compress( + fn compress_gadget( parameters: &Self::ParametersVar, - left_input: &Self::OutputVar, - right_input: &Self::OutputVar, + left: &Self::OutputVar, + right: &Self::OutputVar, ) -> Result { - let cs = left_input.cs().or(right_input.cs()); + let cs = left.cs().or(right.cs()); if cs.is_none() { Ok(FpVar::Constant( - CRH::::evaluate( - ¶meters.parameters, - vec![left_input.value()?, right_input.value()?], - ) - .unwrap(), + CRH::::evaluate(¶meters.parameters, vec![left.value()?, right.value()?]) + .unwrap(), )) } else { let mut sponge = PoseidonSpongeVar::new(cs, ¶meters.parameters); - sponge.absorb(left_input)?; - sponge.absorb(right_input)?; + sponge.absorb(left)?; + sponge.absorb(right)?; let res = sponge.squeeze_field_elements(1)?; Ok(res[0].clone()) } @@ -110,10 +93,9 @@ impl AllocVar, F> for CRHParameter #[cfg(test)] mod test { - use crate::crh::poseidon::constraints::{CRHGadget, CRHParametersVar, TwoToOneCRHGadget}; - use crate::crh::poseidon::{TwoToOneCRH, CRH}; - use crate::crh::{TwoToOneCRHScheme, TwoToOneCRHSchemeGadget}; - use crate::{CRHScheme, CRHSchemeGadget}; + use crate::crh::poseidon::{constraints::CRHParametersVar, TwoToOneCRH, CRH}; + use crate::crh::{CRHGadget, TwoToOneCRH as _, TwoToOneCRHGadget, CRH as _}; + use crate::Gadget; use ark_bls12_377::Fr; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::{ @@ -152,6 +134,7 @@ mod test { test_b.push(Fr::rand(&mut test_rng)); } + // TODO: figure out appropriate rate and capacity let params = PoseidonParameters::::new(8, 24, 31, mds, ark); let crh_a = CRH::::evaluate(¶ms, test_a.clone()).unwrap(); let crh_b = CRH::::evaluate(¶ms, test_b.clone()).unwrap(); @@ -174,9 +157,9 @@ mod test { } let params_g = CRHParametersVar::::new_witness(cs, || Ok(params)).unwrap(); - let crh_a_g = CRHGadget::::evaluate(¶ms_g, &test_a_g).unwrap(); - let crh_b_g = CRHGadget::::evaluate(¶ms_g, &test_b_g).unwrap(); - let crh_g = TwoToOneCRHGadget::::compress(¶ms_g, &crh_a_g, &crh_b_g).unwrap(); + let crh_a_g = Gadget::>::evaluate(¶ms_g, &test_a_g).unwrap(); + let crh_b_g = Gadget::>::evaluate(¶ms_g, &test_b_g).unwrap(); + let crh_g = Gadget::>::compress(¶ms_g, &crh_a_g, &crh_b_g).unwrap(); assert_eq!(crh_a, crh_a_g.value().unwrap()); assert_eq!(crh_b, crh_b_g.value().unwrap()); diff --git a/src/crh/poseidon/mod.rs b/src/crh/poseidon/mod.rs index 3c1b11db..3bc80eb3 100644 --- a/src/crh/poseidon/mod.rs +++ b/src/crh/poseidon/mod.rs @@ -1,5 +1,5 @@ -use crate::crh::TwoToOneCRHScheme; -use crate::{CRHScheme, Error}; +use crate::crh::{TwoToOneCRH as TwoToOneCRHTrait, CRH as CRHTrait}; +use crate::Error; use ark_ff::PrimeField; use ark_sponge::poseidon::{PoseidonParameters, PoseidonSponge}; use ark_sponge::{Absorb, CryptographicSponge}; @@ -14,7 +14,7 @@ pub struct CRH { field_phantom: PhantomData, } -impl CRHScheme for CRH { +impl CRHTrait for CRH { type Input = [F]; type Output = F; type Parameters = PoseidonParameters; @@ -42,7 +42,7 @@ pub struct TwoToOneCRH { field_phantom: PhantomData, } -impl TwoToOneCRHScheme for TwoToOneCRH { +impl TwoToOneCRHTrait for TwoToOneCRH { type Input = F; type Output = F; type Parameters = PoseidonParameters; @@ -55,23 +55,23 @@ impl TwoToOneCRHScheme for TwoToOneCRH { fn evaluate>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { - Self::compress(parameters, left_input, right_input) + Self::compress(parameters, left, right) } fn compress>( parameters: &Self::Parameters, - left_input: T, - right_input: T, + left: T, + right: T, ) -> Result { - let left_input = left_input.borrow(); - let right_input = right_input.borrow(); + let left = left.borrow(); + let right = right.borrow(); let mut sponge = PoseidonSponge::new(parameters); - sponge.absorb(left_input); - sponge.absorb(right_input); + sponge.absorb(left); + sponge.absorb(right); let res = sponge.squeeze_field_elements::(1); Ok(res[0]) } diff --git a/src/encryption/constraints.rs b/src/encryption/constraints.rs index de2a8f91..1cfe963a 100644 --- a/src/encryption/constraints.rs +++ b/src/encryption/constraints.rs @@ -1,4 +1,4 @@ -use crate::encryption::AsymmetricEncryptionScheme; +use crate::{encryption::AsymmetricEnc, Gadget}; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::SynthesisError; @@ -6,21 +6,63 @@ use core::fmt::Debug; use ark_ff::fields::Field; -pub trait AsymmetricEncryptionGadget { - type OutputVar: AllocVar +pub trait AsymmetricEncWithGadget: AsymmetricEnc { + type CiphertextVar: AllocVar + EqGadget + Clone + Sized + Debug; - type ParametersVar: AllocVar + Clone; - type PlaintextVar: AllocVar + Clone; - type PublicKeyVar: AllocVar + Clone; - type RandomnessVar: AllocVar + Clone; + type ParametersVar: AllocVar + Clone; + type PlaintextVar: AllocVar + Clone; + type PublicKeyVar: AllocVar + Clone; + type RandomnessVar: AllocVar + Clone; + + fn encrypt_gadget( + parameters: &Self::ParametersVar, + message: &Self::PlaintextVar, + randomness: &Self::RandomnessVar, + public_key: &Self::PublicKeyVar, + ) -> Result; +} + +pub trait AsymmetricEncGadget { + type Native: AsymmetricEncWithGadget< + ConstraintF, + CiphertextVar = Self::CiphertextVar, + ParametersVar = Self::ParametersVar, + PlaintextVar = Self::PlaintextVar, + PublicKeyVar = Self::PublicKeyVar, + RandomnessVar = Self::RandomnessVar, + >; + type CiphertextVar: AllocVar<::Ciphertext, ConstraintF> + + EqGadget + + Clone + + Sized + + Debug; + type ParametersVar: AllocVar<::Parameters, ConstraintF> + Clone; + type PlaintextVar: AllocVar<::Plaintext, ConstraintF> + Clone; + type PublicKeyVar: AllocVar<::PublicKey, ConstraintF> + Clone; + type RandomnessVar: AllocVar<::Randomness, ConstraintF> + Clone; fn encrypt( parameters: &Self::ParametersVar, message: &Self::PlaintextVar, randomness: &Self::RandomnessVar, public_key: &Self::PublicKeyVar, - ) -> Result; + ) -> Result { + Self::Native::encrypt_gadget(parameters, message, randomness, public_key) + } +} + +impl AsymmetricEncGadget for Gadget +where + Enc: AsymmetricEncWithGadget, + ConstraintF: Field, +{ + type Native = Enc; + type CiphertextVar = Enc::CiphertextVar; + type ParametersVar = Enc::ParametersVar; + type PlaintextVar = Enc::PlaintextVar; + type PublicKeyVar = Enc::PublicKeyVar; + type RandomnessVar = Enc::RandomnessVar; } diff --git a/src/encryption/elgamal/constraints.rs b/src/encryption/elgamal/constraints.rs index 219ec7fe..089fe416 100644 --- a/src/encryption/elgamal/constraints.rs +++ b/src/encryption/elgamal/constraints.rs @@ -4,13 +4,13 @@ use ark_relations::r1cs::{Namespace, SynthesisError}; use crate::encryption::elgamal::{ Ciphertext, ElGamal, Parameters, Plaintext, PublicKey, Randomness, }; -use crate::encryption::AsymmetricEncryptionGadget; +use crate::encryption::AsymmetricEncWithGadget; use ark_ec::ProjectiveCurve; use ark_ff::{ fields::{Field, PrimeField}, to_bytes, Zero, }; -use ark_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; +use ark_std::{borrow::Borrow, vec::Vec}; pub type ConstraintF = <::BaseField as Field>::BasePrimeField; @@ -19,7 +19,7 @@ pub struct RandomnessVar(Vec>); impl AllocVar, F> for RandomnessVar where - C: ProjectiveCurve, + C: CurveWithVar>, F: PrimeField, { fn new_variable>>( @@ -37,112 +37,77 @@ where } #[derive(Derivative)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct ParametersVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - generator: GG, - #[doc(hidden)] - _curve: PhantomData, +#[derivative(Clone(bound = "C::Var: Clone"))] +pub struct ParametersVar>> { + generator: C::Var, } -impl AllocVar, ConstraintF> for ParametersVar +impl AllocVar, ConstraintF> for ParametersVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, - mode: AllocationMode, + _mode: AllocationMode, ) -> Result { - let generator = GG::new_variable(cs, || f().map(|g| g.borrow().generator), mode)?; - Ok(Self { - generator, - _curve: PhantomData, - }) + // Always allocate as constant + let generator = + C::Var::new_constant(cs, f().map(|g| g.borrow().generator.into()).unwrap())?; + Ok(Self { generator }) } } #[derive(Derivative)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct PlaintextVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - pub plaintext: GG, - #[doc(hidden)] - _curve: PhantomData, +#[derivative(Clone(bound = "C::Var: Clone"))] +pub struct PlaintextVar>> { + pub plaintext: C::Var, } -impl AllocVar, ConstraintF> for PlaintextVar +impl AllocVar, ConstraintF> for PlaintextVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { - let plaintext = GG::new_variable(cs, f, mode)?; - Ok(Self { - plaintext, - _curve: PhantomData, - }) + let plaintext = C::Var::new_variable(cs, f, mode)?; + Ok(Self { plaintext }) } } #[derive(Derivative)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct PublicKeyVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - pub pk: GG, - #[doc(hidden)] - _curve: PhantomData, +#[derivative(Clone(bound = "C::Var: Clone"))] +pub struct PublicKeyVar>> { + pub pk: C::Var, } -impl AllocVar, ConstraintF> for PublicKeyVar +impl AllocVar, ConstraintF> for PublicKeyVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { - let pk = GG::new_variable(cs, f, mode)?; - Ok(Self { - pk, - _curve: PhantomData, - }) + let pk = C::Var::new_variable(cs, f, mode)?; + Ok(Self { pk }) } } #[derive(Derivative, Debug)] -#[derivative(Clone(bound = "C: ProjectiveCurve, GG: CurveVar>"))] -pub struct OutputVar>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - pub c1: GG, - pub c2: GG, - #[doc(hidden)] - _curve: PhantomData, +#[derivative(Clone(bound = "C::Var: Clone"))] +pub struct CiphertextVar>> { + pub c1: C::Var, + pub c2: C::Var, } -impl AllocVar, ConstraintF> for OutputVar +impl AllocVar, ConstraintF> for CiphertextVar where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, { fn new_variable>>( cs: impl Into>>, @@ -152,21 +117,15 @@ where let ns = cs.into(); let cs = ns.cs(); let prep = f().map(|g| *g.borrow()); - let c1 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().0), mode)?; - let c2 = GG::new_variable(cs.clone(), || prep.map(|g| g.borrow().1), mode)?; - Ok(Self { - c1, - c2, - _curve: PhantomData, - }) + let c1 = C::Var::new_variable(cs.clone(), || prep.map(|g| g.borrow().0), mode)?; + let c2 = C::Var::new_variable(cs.clone(), || prep.map(|g| g.borrow().1), mode)?; + Ok(Self { c1, c2 }) } } -impl EqGadget> for OutputVar +impl EqGadget> for CiphertextVar where - C: ProjectiveCurve, - GC: CurveVar>, - for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, + C: CurveWithVar>, { #[inline] fn is_eq(&self, other: &Self) -> Result>, SynthesisError> { @@ -174,34 +133,23 @@ where } } -pub struct ElGamalEncGadget>> -where - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, -{ - #[doc(hidden)] - _curve: PhantomData<*const C>, - _group_var: PhantomData<*const GG>, -} - -impl AsymmetricEncryptionGadget, ConstraintF> for ElGamalEncGadget +impl AsymmetricEncWithGadget> for ElGamal where - C: ProjectiveCurve, - GG: CurveVar>, - for<'a> &'a GG: GroupOpsBounds<'a, C, GG>, + C: CurveWithVar>, ConstraintF: PrimeField, { - type OutputVar = OutputVar; - type ParametersVar = ParametersVar; - type PlaintextVar = PlaintextVar; - type PublicKeyVar = PublicKeyVar; + type CiphertextVar = CiphertextVar; + type ParametersVar = ParametersVar; + type PlaintextVar = PlaintextVar; + type PublicKeyVar = PublicKeyVar; type RandomnessVar = RandomnessVar>; - fn encrypt( + fn encrypt_gadget( parameters: &Self::ParametersVar, message: &Self::PlaintextVar, randomness: &Self::RandomnessVar, public_key: &Self::PublicKeyVar, - ) -> Result { + ) -> Result { // flatten randomness to little-endian bit vector let randomness = randomness .0 @@ -210,34 +158,28 @@ where .collect::>(); // compute s = randomness*pk - let s = public_key.pk.clone().scalar_mul_le(randomness.iter())?; + let s = public_key.pk.scalar_mul_le(randomness.iter())?; // compute c1 = randomness*generator - let c1 = parameters - .generator - .clone() - .scalar_mul_le(randomness.iter())?; + let c1 = parameters.generator.scalar_mul_le(randomness.iter())?; // compute c2 = m + s - let c2 = message.plaintext.clone() + s; + let c2 = s + &message.plaintext; - Ok(Self::OutputVar { - c1, - c2, - _curve: PhantomData, - }) + Ok(Self::CiphertextVar { c1, c2 }) } } #[cfg(test)] mod test { - use crate::encryption::constraints::AsymmetricEncryptionGadget; + use crate::encryption::constraints::AsymmetricEncGadget; + use crate::encryption::elgamal::{ElGamal, Randomness}; + use crate::encryption::AsymmetricEnc; + use crate::Gadget; use ark_std::{test_rng, UniformRand}; - use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq}; + use ark_ed_on_bls12_381::{EdwardsProjective as JubJub, Fq}; - use crate::encryption::elgamal::{constraints::ElGamalEncGadget, ElGamal, Randomness}; - use crate::encryption::AsymmetricEncryptionScheme; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::ConstraintSystem; @@ -245,54 +187,49 @@ mod test { fn test_elgamal_gadget() { let rng = &mut test_rng(); - type MyEnc = ElGamal; - type MyGadget = ElGamalEncGadget; + type TestEnc = ElGamal; + type TestGadget = Gadget; // compute primitive result - let parameters = MyEnc::setup(rng).unwrap(); - let (pk, _) = MyEnc::keygen(¶meters, rng).unwrap(); + let parameters = TestEnc::setup(rng).unwrap(); + let (pk, _) = TestEnc::keygen(¶meters, rng).unwrap(); let msg = JubJub::rand(rng).into(); let randomness = Randomness::rand(rng); - let primitive_result = MyEnc::encrypt(¶meters, &pk, &msg, &randomness).unwrap(); + let primitive_result = TestEnc::encrypt(¶meters, &pk, &msg, &randomness).unwrap(); // construct constraint system let cs = ConstraintSystem::::new_ref(); - let randomness_var = - >::RandomnessVar::new_witness( - ark_relations::ns!(cs, "gadget_randomness"), - || Ok(&randomness), - ) - .unwrap(); - let parameters_var = - >::ParametersVar::new_constant( - ark_relations::ns!(cs, "gadget_parameters"), - ¶meters, - ) - .unwrap(); - let msg_var = - >::PlaintextVar::new_witness( - ark_relations::ns!(cs, "gadget_message"), - || Ok(&msg), - ) - .unwrap(); - let pk_var = - >::PublicKeyVar::new_witness( - ark_relations::ns!(cs, "gadget_public_key"), - || Ok(&pk), - ) - .unwrap(); + let randomness_var = >::RandomnessVar::new_witness( + ark_relations::ns!(cs, "gadget_randomness"), + || Ok(&randomness), + ) + .unwrap(); + let parameters_var = >::ParametersVar::new_constant( + ark_relations::ns!(cs, "gadget_parameters"), + ¶meters, + ) + .unwrap(); + let msg_var = >::PlaintextVar::new_witness( + ark_relations::ns!(cs, "gadget_message"), + || Ok(&msg), + ) + .unwrap(); + let pk_var = >::PublicKeyVar::new_witness( + ark_relations::ns!(cs, "gadget_public_key"), + || Ok(&pk), + ) + .unwrap(); // use gadget let result_var = - MyGadget::encrypt(¶meters_var, &msg_var, &randomness_var, &pk_var).unwrap(); + TestGadget::encrypt(¶meters_var, &msg_var, &randomness_var, &pk_var).unwrap(); // check that result equals expected ciphertext in the constraint system - let expected_var = - >::OutputVar::new_input( - ark_relations::ns!(cs, "gadget_expected"), - || Ok(&primitive_result), - ) - .unwrap(); + let expected_var = >::CiphertextVar::new_input( + ark_relations::ns!(cs, "gadget_expected"), + || Ok(&primitive_result), + ) + .unwrap(); expected_var.enforce_equal(&result_var).unwrap(); assert_eq!(primitive_result.0, result_var.c1.value().unwrap()); diff --git a/src/encryption/elgamal/mod.rs b/src/encryption/elgamal/mod.rs index 6e471c78..edd3e4d9 100644 --- a/src/encryption/elgamal/mod.rs +++ b/src/encryption/elgamal/mod.rs @@ -1,7 +1,7 @@ #[cfg(feature = "r1cs")] pub mod constraints; -use crate::encryption::AsymmetricEncryptionScheme; +use crate::encryption::AsymmetricEnc; use crate::Error; use ark_ec::{AffineCurve, ProjectiveCurve}; use ark_ff::{fields::PrimeField, UniformRand}; @@ -36,7 +36,7 @@ pub type Ciphertext = ( ::Affine, ); -impl AsymmetricEncryptionScheme for ElGamal +impl AsymmetricEnc for ElGamal where C::ScalarField: PrimeField, { @@ -111,7 +111,7 @@ mod test { use ark_ed_on_bls12_381::EdwardsProjective as JubJub; use crate::encryption::elgamal::{ElGamal, Randomness}; - use crate::encryption::AsymmetricEncryptionScheme; + use crate::encryption::AsymmetricEnc; #[test] fn test_elgamal_encryption() { diff --git a/src/encryption/mod.rs b/src/encryption/mod.rs index d1213a76..1caafacf 100644 --- a/src/encryption/mod.rs +++ b/src/encryption/mod.rs @@ -8,7 +8,7 @@ pub mod elgamal; use crate::Error; use ark_std::rand::Rng; -pub trait AsymmetricEncryptionScheme { +pub trait AsymmetricEnc { type Parameters; type PublicKey; type SecretKey; diff --git a/src/lib.rs b/src/lib.rs index f1de5d1c..ec96b622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ extern crate ark_std; #[macro_use] extern crate derivative; +use std::marker::PhantomData; + pub(crate) use ark_std::{borrow::ToOwned, boxed::Box, vec::Vec}; mod macros; @@ -28,7 +30,7 @@ pub mod snark; pub use self::{ commitment::CommitmentScheme, - crh::CRHScheme, + crh::CRH, merkle_tree::{MerkleTree, Path}, prf::PRF, signature::SignatureScheme, @@ -37,12 +39,21 @@ pub use self::{ #[cfg(feature = "r1cs")] pub use self::{ - commitment::CommitmentGadget, crh::CRHSchemeGadget, merkle_tree::constraints::PathVar, + commitment::CommitmentGadget, crh::CRHGadget, merkle_tree::constraints::PathVar, prf::PRFGadget, signature::SigRandomizePkGadget, snark::SNARKGadget, }; pub type Error = Box; +#[derive(Derivative)] +#[derivative( + Clone(bound = ""), + Debug(bound = ""), + PartialEq(bound = ""), + Eq(bound = "") +)] +pub struct Gadget(PhantomData); + #[derive(Debug)] pub enum CryptoError { IncorrectInputLength(usize), diff --git a/src/merkle_tree/constraints.rs b/src/merkle_tree/constraints.rs index 50d1194d..2256ec72 100644 --- a/src/merkle_tree/constraints.rs +++ b/src/merkle_tree/constraints.rs @@ -1,6 +1,9 @@ -use crate::crh::TwoToOneCRHSchemeGadget; -use crate::merkle_tree::{Config, IdentityDigestConverter}; -use crate::{CRHSchemeGadget, Path}; +use crate::merkle_tree::{Config, Path}; +use crate::CRHGadget; +use crate::{ + crh::{CRHWithGadget, TwoToOneCRHGadget, TwoToOneCRHWithGadget}, + Gadget, +}; use ark_ff::Field; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::boolean::Boolean; @@ -12,100 +15,79 @@ use ark_std::borrow::Borrow; use ark_std::fmt::Debug; use ark_std::vec::Vec; +use super::{ByteDigestConverter, IdentityDigestConverter}; + pub trait DigestVarConverter { - type TargetType: Borrow; - fn convert(from: From) -> Result; + type Target: Borrow; + fn convert(from: &From) -> Result; } -impl DigestVarConverter for IdentityDigestConverter { - type TargetType = T; +impl, ConstraintF: Field> DigestVarConverter]> + for ByteDigestConverter +{ + type Target = Vec>; - fn convert(from: T) -> Result { - Ok(from) + fn convert(from: &T) -> Result { + from.to_non_unique_bytes() } } -pub struct BytesVarDigestConverter, ConstraintF: Field> { - _prev_layer_digest: T, - _constraint_field: ConstraintF, -} - -impl, ConstraintF: Field> DigestVarConverter]> - for BytesVarDigestConverter -{ - type TargetType = Vec>; +impl DigestVarConverter for IdentityDigestConverter { + type Target = T; - fn convert(from: T) -> Result { - from.to_non_unique_bytes() + fn convert(from: &T) -> Result { + Ok(from.clone()) } } -pub trait ConfigGadget { - type Leaf: Debug + ?Sized; - type LeafDigest: AllocVar - + EqGadget - + ToBytesGadget - + CondSelectGadget - + R1CSVar - + Debug - + Clone - + Sized; - type LeafInnerConverter: DigestVarConverter< - Self::LeafDigest, - >::InputVar, - >; - type InnerDigest: AllocVar - + EqGadget - + ToBytesGadget - + CondSelectGadget - + R1CSVar - + Debug - + Clone - + Sized; - - type LeafHash: CRHSchemeGadget< - P::LeafHash, - ConstraintF, - InputVar = Self::Leaf, - OutputVar = Self::LeafDigest, - >; - type TwoToOneHash: TwoToOneCRHSchemeGadget< - P::TwoToOneHash, - ConstraintF, - OutputVar = Self::InnerDigest, +pub trait ConfigGadget: Config +where + Self::LeafHash: CRHWithGadget, + Self::TwoToOneHash: TwoToOneCRHWithGadget, +{ + type LeafVar: Debug + ?Sized; + + type LeafToInnerVarConverter: DigestVarConverter< + LeafDigestVar, + TwoToOneInputVar, >; } -type LeafParam = - <>::LeafHash as CRHSchemeGadget< -

::LeafHash, - ConstraintF, - >>::ParametersVar; -type TwoToOneParam = - <>::TwoToOneHash as TwoToOneCRHSchemeGadget< -

::TwoToOneHash, - ConstraintF, - >>::ParametersVar; +pub type TwoToOneInputVar = + ::TwoToOneHash> as TwoToOneCRHGadget>::InputVar; +pub type LeafDigestVar = ::LeafHash> as CRHGadget>::OutputVar; +pub type TwoToOneDigestVar = + ::TwoToOneHash> as TwoToOneCRHGadget>::OutputVar; + +pub type LeafParamsVar = + ::LeafHash> as CRHGadget>::ParametersVar; + +pub type TwoToOneParamsVar = + ::TwoToOneHash> as TwoToOneCRHGadget>::ParametersVar; /// Represents a merkle tree path gadget. #[derive(Debug, Derivative)] -#[derivative(Clone(bound = "P: Config, ConstraintF: Field, PG: ConfigGadget"))] -pub struct PathVar> { +#[derivative(Clone(bound = "P: ConfigGadget, ConstraintF: Field"))] +pub struct PathVar, ConstraintF: Field> +where + P::LeafHash: CRHWithGadget, + P::TwoToOneHash: TwoToOneCRHWithGadget, +{ /// `path[i]` is 0 (false) iff ith non-leaf node from top to bottom is left. path: Vec>, /// `auth_path[i]` is the entry of sibling of ith non-leaf node from top to bottom. - auth_path: Vec, + auth_path: Vec>, /// The sibling of leaf. - leaf_sibling: PG::LeafDigest, + leaf_sibling: LeafDigestVar, /// Is this leaf the right child? leaf_is_right_child: Boolean, } -impl> AllocVar, ConstraintF> - for PathVar +impl, ConstraintF: Field> AllocVar, ConstraintF> + for PathVar where - P: Config, - ConstraintF: Field, + P::LeafHash: CRHWithGadget, + P::TwoToOneHash: TwoToOneCRHWithGadget, { #[tracing::instrument(target = "r1cs", skip(cs, f))] fn new_variable>>( @@ -116,7 +98,7 @@ where let ns = cs.into(); let cs = ns.cs(); f().and_then(|val| { - let leaf_sibling = PG::LeafDigest::new_variable( + let leaf_sibling = LeafDigestVar::::new_variable( ark_relations::ns!(cs, "leaf_sibling"), || Ok(val.borrow().leaf_sibling_hash.clone()), mode, @@ -148,7 +130,11 @@ where } } -impl> PathVar { +impl, ConstraintF: Field> PathVar +where + P::LeafHash: CRHWithGadget, + P::TwoToOneHash: TwoToOneCRHWithGadget, +{ /// Set the leaf index of the path to a given value. Verifier can use function before calling `verify` /// to check the correctness leaf position. /// * `leaf_index`: leaf index encoded in little-endian format @@ -190,11 +176,11 @@ impl> PathVar, - two_to_one_params: &TwoToOneParam, - leaf: &PG::Leaf, - ) -> Result { - let claimed_leaf_hash = PG::LeafHash::evaluate(leaf_params, leaf)?; + leaf_params: &LeafParamsVar, + two_to_one_params: &TwoToOneParamsVar, + leaf: &P::LeafVar, + ) -> Result, SynthesisError> { + let claimed_leaf_hash = Gadget::::evaluate(leaf_params, leaf)?; let leaf_sibling_hash = &self.leaf_sibling; // calculate hash for the bottom non_leaf_layer @@ -211,21 +197,26 @@ impl> PathVar::evaluate( + two_to_one_params, + left_hash.borrow(), + &right_hash.borrow(), + )?; // To traverse up a MT, we iterate over the path from bottom to top (i.e. in reverse) // At any given bit, the bit being 0 indicates our currently hashed value is the left, // and the bit being 1 indicates our currently hashed value is on the right. // Thus `left_hash` is the sibling if bit is 1, and it's the computed hash if bit is 0 for (bit, sibling) in self.path.iter().rev().zip(self.auth_path.iter().rev()) { + // TODO: implement this using a conditional swap. let left_hash = bit.select(sibling, &curr_hash)?; let right_hash = bit.select(&curr_hash, sibling)?; - curr_hash = PG::TwoToOneHash::compress(two_to_one_params, &left_hash, &right_hash)?; + curr_hash = + Gadget::::compress(two_to_one_params, &left_hash, &right_hash)?; } Ok(curr_hash) @@ -236,10 +227,10 @@ impl> PathVar, - two_to_one_params: &TwoToOneParam, - root: &PG::InnerDigest, - leaf: &PG::Leaf, + leaf_params: &LeafParamsVar, + two_to_one_params: &TwoToOneParamsVar, + root: &TwoToOneDigestVar, + leaf: &P::LeafVar, ) -> Result, SynthesisError> { let expected_root = self.calculate_root(leaf_params, two_to_one_params, leaf)?; Ok(expected_root.is_eq(root)?) @@ -250,12 +241,12 @@ impl> PathVar, - two_to_one_params: &TwoToOneParam, - old_root: &PG::InnerDigest, - old_leaf: &PG::Leaf, - new_leaf: &PG::Leaf, - ) -> Result { + leaf_params: &LeafParamsVar, + two_to_one_params: &TwoToOneParamsVar, + old_root: &TwoToOneDigestVar, + old_leaf: &P::LeafVar, + new_leaf: &P::LeafVar, + ) -> Result, SynthesisError> { self.verify_membership(leaf_params, two_to_one_params, old_root, old_leaf)? .enforce_equal(&Boolean::TRUE)?; Ok(self.calculate_root(leaf_params, two_to_one_params, new_leaf)?) @@ -267,12 +258,12 @@ impl> PathVar, - two_to_one_params: &TwoToOneParam, - old_root: &PG::InnerDigest, - new_root: &PG::InnerDigest, - old_leaf: &PG::Leaf, - new_leaf: &PG::Leaf, + leaf_params: &LeafParamsVar, + two_to_one_params: &TwoToOneParamsVar, + old_root: &TwoToOneDigestVar, + new_root: &TwoToOneDigestVar, + old_leaf: &P::LeafVar, + new_leaf: &P::LeafVar, ) -> Result, SynthesisError> { let actual_new_root = self.update_leaf(leaf_params, two_to_one_params, old_root, old_leaf, new_leaf)?; diff --git a/src/merkle_tree/mod.rs b/src/merkle_tree/mod.rs index 998dd125..fe4b9eff 100644 --- a/src/merkle_tree/mod.rs +++ b/src/merkle_tree/mod.rs @@ -1,12 +1,11 @@ #![allow(clippy::needless_range_loop)] +use crate::CRH; /// Defines a trait to chain two types of CRHs. -use crate::crh::TwoToOneCRHScheme; -use crate::{CRHScheme, Error}; +use crate::{crh::TwoToOneCRH, Error}; use ark_ff::ToBytes; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::borrow::Borrow; -use ark_std::hash::Hash; use ark_std::vec::Vec; #[cfg(test)] @@ -18,32 +17,28 @@ pub mod constraints; /// Convert the hash digest in different layers by converting previous layer's output to /// `TargetType`, which is a `Borrow` to next layer's input. pub trait DigestConverter { - type TargetType: Borrow; - fn convert(item: From) -> Result; + type Target: Borrow; + fn convert(item: &From) -> Result; } /// A trivial converter where digest of previous layer's hash is the same as next layer's input. -pub struct IdentityDigestConverter { - _prev_layer_digest: T, -} +pub struct IdentityDigestConverter; -impl DigestConverter for IdentityDigestConverter { - type TargetType = T; - fn convert(item: T) -> Result { - Ok(item) +impl DigestConverter for IdentityDigestConverter { + type Target = T; + fn convert(item: &T) -> Result { + Ok(item.clone()) } } /// Convert previous layer's digest to bytes and use bytes as input for next layer's digest. /// TODO: `ToBytes` trait will be deprecated in future versions. -pub struct ByteDigestConverter { - _prev_layer_digest: T, -} +pub struct ByteDigestConverter; -impl DigestConverter for ByteDigestConverter { - type TargetType = Vec; +impl DigestConverter for ByteDigestConverter { + type Target = Vec; - fn convert(item: T) -> Result { + fn convert(item: &T) -> Result { // TODO: In some tests, `serialize` is not consistent with constraints. Try fix those. Ok(crate::to_unchecked_bytes!(item)?) } @@ -51,48 +46,29 @@ impl DigestConverter for ByteDigestCon /// Merkle tree have three types of hashes. /// * `LeafHash`: Convert leaf to leaf digest -/// * `TwoLeavesToOneHash`: Convert two leaf digests to one inner digest. This one can be a wrapped -/// version `TwoHashesToOneHash`, which first converts leaf digest to inner digest. -/// * `TwoHashesToOneHash`: Compress two inner digests to one inner digest +/// * `TwoLeavesToOneHash`: Convert two leaf digests to one inner digest. +/// This can be a wrapped version `TwoToOneHash`, which first converts a leaf digest to an inner digest. pub trait Config { type Leaf: ?Sized; // merkle tree does not store the leaf // leaf layer - type LeafDigest: ToBytes - + Clone - + Eq - + core::fmt::Debug - + Hash - + Default - + CanonicalSerialize - + CanonicalDeserialize; - // transition between leaf layer to inner layer - type LeafInnerDigestConverter: DigestConverter< - Self::LeafDigest, - ::Input, - >; - // inner layer - type InnerDigest: ToBytes - + Clone - + Eq - + core::fmt::Debug - + Hash - + Default - + CanonicalSerialize - + CanonicalDeserialize; - - // Tom's Note: in the future, if we want different hash function, we can simply add more - // types of digest here and specify a digest converter. Same for constraints. - - /// leaf -> leaf digest - /// If leaf hash digest and inner hash digest are different, we can create a new - /// leaf hash which wraps the original leaf hash and convert its output to `Digest`. - type LeafHash: CRHScheme; + // Tom's Note: in the future, if we want different hash function, we can simply add more + // types of digest here and specify a digest converter. Same for constraints. + + type LeafHash: CRH; /// 2 inner digest -> inner digest - type TwoToOneHash: TwoToOneCRHScheme; + type TwoToOneHash: TwoToOneCRH; + + type LeafToInnerConverter: DigestConverter< + LeafDigest, + ::Input, + >; } -pub type TwoToOneParam

= <

::TwoToOneHash as TwoToOneCRHScheme>::Parameters; -pub type LeafParam

= <

::LeafHash as CRHScheme>::Parameters; +pub type LeafDigest = <::LeafHash as CRH>::Output; +pub type TwoToOneDigest = <::TwoToOneHash as TwoToOneCRH>::Output; + +pub type TwoToOneParams

= <

::TwoToOneHash as TwoToOneCRH>::Parameters; +pub type LeafParams

= <

::LeafHash as CRH>::Parameters; /// Stores the hashes of a particular path (in order) from root to leaf. /// For example: @@ -113,9 +89,9 @@ pub type LeafParam

= <

::LeafHash as CRHScheme>::Parameters; Default(bound = "P: Config") )] pub struct Path { - pub leaf_sibling_hash: P::LeafDigest, + pub leaf_sibling_hash: LeafDigest

, /// The sibling of path node ordered from higher layer to lower layer (does not include root node). - pub auth_path: Vec, + pub auth_path: Vec>, /// stores the leaf index of the node pub leaf_index: usize, } @@ -140,9 +116,9 @@ impl Path

{ /// `verify` infers the tree height by setting `tree_height = self.auth_path.len() + 2` pub fn verify>( &self, - leaf_hash_params: &LeafParam

, - two_to_one_params: &TwoToOneParam

, - root_hash: &P::InnerDigest, + leaf_hash_params: &LeafParams

, + two_to_one_params: &TwoToOneParams

, + root_hash: &TwoToOneDigest

, leaf: L, ) -> Result { // calculate leaf hash @@ -152,8 +128,8 @@ impl Path

{ select_left_right_child(self.leaf_index, &claimed_leaf_hash, &self.leaf_sibling_hash)?; // leaf layer to inner layer conversion - let left_child = P::LeafInnerDigestConverter::convert(left_child)?; - let right_child = P::LeafInnerDigestConverter::convert(right_child)?; + let left_child = P::LeafToInnerConverter::convert(&left_child)?; + let right_child = P::LeafToInnerConverter::convert(&right_child)?; let mut curr_path_node = P::TwoToOneHash::evaluate(&two_to_one_params, left_child, right_child)?; @@ -214,13 +190,13 @@ fn select_left_right_child( pub struct MerkleTree { /// stores the non-leaf nodes in level order. The first element is the root node. /// The ith nodes (starting at 1st) children are at indices `2*i`, `2*i+1` - non_leaf_nodes: Vec, + non_leaf_nodes: Vec>, /// store the hash of leaf nodes from left to right - leaf_nodes: Vec, + leaf_nodes: Vec>, /// Store the inner hash parameters - two_to_one_hash_param: TwoToOneParam

, + two_to_one_hash_param: TwoToOneParams

, /// Store the leaf hash parameters - leaf_hash_param: LeafParam

, + leaf_hash_param: LeafParams

, /// Stores the height of the MerkleTree height: usize, } @@ -229,19 +205,19 @@ impl MerkleTree

{ /// Create an empty merkle tree such that all leaves are zero-filled. /// Consider using a sparse merkle tree if you need the tree to be low memory pub fn blank( - leaf_hash_param: &LeafParam

, - two_to_one_hash_param: &TwoToOneParam

, + leaf_hash_param: &LeafParams

, + two_to_one_hash_param: &TwoToOneParams

, height: usize, ) -> Result { // use empty leaf digest - let leaves_digest = vec![P::LeafDigest::default(); 1 << (height - 1)]; + let leaves_digest = vec![LeafDigest::

::default(); 1 << (height - 1)]; Self::new_with_leaf_digest(leaf_hash_param, two_to_one_hash_param, leaves_digest) } /// Returns a new merkle tree. `leaves.len()` should be power of two. pub fn new>( - leaf_hash_param: &LeafParam

, - two_to_one_hash_param: &TwoToOneParam

, + leaf_hash_param: &LeafParams

, + two_to_one_hash_param: &TwoToOneParams

, leaves: impl IntoIterator, ) -> Result { let mut leaves_digests = Vec::new(); @@ -255,9 +231,9 @@ impl MerkleTree

{ } pub fn new_with_leaf_digest( - leaf_hash_param: &LeafParam

, - two_to_one_hash_param: &TwoToOneParam

, - leaves_digest: Vec, + leaf_hash_param: &LeafParams

, + two_to_one_hash_param: &TwoToOneParams

, + leaves_digest: Vec>, ) -> Result { let leaf_nodes_size = leaves_digest.len(); assert!( @@ -268,10 +244,10 @@ impl MerkleTree

{ let tree_height = tree_height(leaf_nodes_size); - let hash_of_empty: P::InnerDigest = P::InnerDigest::default(); + let hash_of_empty = TwoToOneDigest::

::default(); // initialize the merkle tree as array of nodes in level order - let mut non_leaf_nodes: Vec = (0..non_leaf_nodes_size) + let mut non_leaf_nodes: Vec> = (0..non_leaf_nodes_size) .map(|_| hash_of_empty.clone()) .collect(); @@ -296,8 +272,8 @@ impl MerkleTree

{ // compute hash non_leaf_nodes[current_index] = P::TwoToOneHash::evaluate( &two_to_one_hash_param, - P::LeafInnerDigestConverter::convert(leaves_digest[left_leaf_index].clone())?, - P::LeafInnerDigestConverter::convert(leaves_digest[right_leaf_index].clone())?, + P::LeafToInnerConverter::convert(&leaves_digest[left_leaf_index].clone())?, + P::LeafToInnerConverter::convert(&leaves_digest[right_leaf_index].clone())?, )? } } @@ -328,7 +304,7 @@ impl MerkleTree

{ } /// Returns the root of the Merkle tree. - pub fn root(&self) -> P::InnerDigest { + pub fn root(&self) -> TwoToOneDigest

{ self.non_leaf_nodes[0].clone() } @@ -380,12 +356,12 @@ impl MerkleTree

{ &self, index: usize, new_leaf: T, - ) -> Result<(P::LeafDigest, Vec), crate::Error> { + ) -> Result<(LeafDigest

, Vec>), crate::Error> { // calculate the hash of leaf - let new_leaf_hash: P::LeafDigest = P::LeafHash::evaluate(&self.leaf_hash_param, new_leaf)?; + let new_leaf_hash: LeafDigest

= P::LeafHash::evaluate(&self.leaf_hash_param, new_leaf)?; // calculate leaf sibling hash and locate its position (left or right) - let (leaf_left, leaf_right) = if index & 1 == 0 { + let (left_leaf, right_leaf) = if index & 1 == 0 { // leaf on left (&new_leaf_hash, &self.leaf_nodes[index + 1]) } else { @@ -397,8 +373,8 @@ impl MerkleTree

{ { path_bottom_to_top.push(P::TwoToOneHash::evaluate( &self.two_to_one_hash_param, - P::LeafInnerDigestConverter::convert(leaf_left.clone())?, - P::LeafInnerDigestConverter::convert(leaf_right.clone())?, + P::LeafToInnerConverter::convert(&left_leaf)?, + P::LeafToInnerConverter::convert(&right_leaf)?, )?); } @@ -458,7 +434,7 @@ impl MerkleTree

{ &mut self, index: usize, new_leaf: &P::Leaf, - asserted_new_root: &P::InnerDigest, + asserted_new_root: &TwoToOneDigest

, ) -> Result { let new_leaf = new_leaf.borrow(); assert!(index < self.leaf_nodes.len(), "index out of range"); diff --git a/src/merkle_tree/tests/constraints.rs b/src/merkle_tree/tests/constraints.rs index 9e7dd091..c9812987 100644 --- a/src/merkle_tree/tests/constraints.rs +++ b/src/merkle_tree/tests/constraints.rs @@ -1,10 +1,13 @@ mod byte_mt_tests { - use crate::crh::{pedersen, TwoToOneCRHScheme, TwoToOneCRHSchemeGadget}; + use crate::{ + crh::{pedersen, TwoToOneCRH, TwoToOneCRHGadget}, + Gadget, + }; - use crate::merkle_tree::constraints::{BytesVarDigestConverter, ConfigGadget}; + use crate::merkle_tree::constraints::ConfigGadget; use crate::merkle_tree::{ByteDigestConverter, Config}; - use crate::{CRHScheme, CRHSchemeGadget, MerkleTree, PathVar}; - use ark_ed_on_bls12_381::{constraints::EdwardsVar, EdwardsProjective as JubJub, Fq}; + use crate::{CRHGadget, MerkleTree, PathVar, CRH}; + use ark_ed_on_bls12_381::{EdwardsProjective as JubJub, Fq}; #[allow(unused)] use ark_r1cs_std::prelude::*; #[allow(unused)] @@ -18,10 +21,9 @@ mod byte_mt_tests { } type LeafH = pedersen::CRH; - type LeafHG = pedersen::constraints::CRHGadget; type CompressH = pedersen::TwoToOneCRH; - type CompressHG = pedersen::constraints::TwoToOneCRHGadget; + type CompressHG = Gadget; type LeafVar = [UInt8]; @@ -29,24 +31,16 @@ mod byte_mt_tests { impl Config for JubJubMerkleTreeParams { type Leaf = [u8]; - type LeafDigest = ::Output; - type LeafInnerDigestConverter = ByteDigestConverter; + type LeafToInnerConverter = ByteDigestConverter; - type InnerDigest = ::Output; type LeafHash = LeafH; type TwoToOneHash = CompressH; } type ConstraintF = Fq; - struct JubJubMerkleTreeParamsVar; - impl ConfigGadget for JubJubMerkleTreeParamsVar { - type Leaf = LeafVar; - type LeafDigest = >::OutputVar; - type LeafInnerConverter = BytesVarDigestConverter; - type InnerDigest = - >::OutputVar; - type LeafHash = LeafHG; - type TwoToOneHash = CompressHG; + impl ConfigGadget for JubJubMerkleTreeParams { + type LeafVar = LeafVar; + type LeafToInnerVarConverter = ByteDigestConverter; } type JubJubMerkleTree = MerkleTree; @@ -59,8 +53,8 @@ mod byte_mt_tests { ) -> () { let mut rng = ark_std::test_rng(); - let leaf_crh_params = ::setup(&mut rng).unwrap(); - let two_to_one_crh_params = ::setup(&mut rng).unwrap(); + let leaf_crh_params = ::setup(&mut rng).unwrap(); + let two_to_one_crh_params = ::setup(&mut rng).unwrap(); let mut tree = JubJubMerkleTree::new( &leaf_crh_params, &two_to_one_crh_params, @@ -81,11 +75,11 @@ mod byte_mt_tests { .unwrap()); // Allocate Merkle Tree Root - let root = >::OutputVar::new_witness( + let root = as CRHGadget<_>>::OutputVar::new_witness( ark_relations::ns!(cs, "new_digest"), || { if use_bad_root { - Ok(::Output::default()) + Ok(::Output::default()) } else { Ok(root) } @@ -97,14 +91,13 @@ mod byte_mt_tests { println!("constraints from digest: {}", constraints_from_digest); // Allocate Parameters for CRH - let leaf_crh_params_var = - >::ParametersVar::new_constant( - ark_relations::ns!(cs, "leaf_crh_parameter"), - &leaf_crh_params, - ) - .unwrap(); + let leaf_crh_params_var = as CRHGadget<_>>::ParametersVar::new_constant( + ark_relations::ns!(cs, "leaf_crh_parameter"), + &leaf_crh_params, + ) + .unwrap(); let two_to_one_crh_params_var = - >::ParametersVar::new_constant( + as TwoToOneCRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "two_to_one_crh_parameter"), &two_to_one_crh_params, ) @@ -121,7 +114,7 @@ mod byte_mt_tests { println!("constraints from leaf: {}", constraints_from_leaf); // Allocate Merkle Tree Path - let cw: PathVar = + let cw: PathVar = PathVar::new_witness(ark_relations::ns!(cs, "new_witness"), || Ok(&proof)).unwrap(); let constraints_from_path = cs.num_constraints() @@ -160,14 +153,13 @@ mod byte_mt_tests { if let Some(update_query) = update_query { let cs = ConstraintSystem::::new_ref(); // allocate parameters for CRH - let leaf_crh_params_var = - >::ParametersVar::new_constant( - ark_relations::ns!(cs, "leaf_crh_parameter"), - &leaf_crh_params, - ) - .unwrap(); + let leaf_crh_params_var = as CRHGadget<_>>::ParametersVar::new_constant( + ark_relations::ns!(cs, "leaf_crh_parameter"), + &leaf_crh_params, + ) + .unwrap(); let two_to_one_crh_params_var = - >::ParametersVar::new_constant( + as TwoToOneCRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "two_to_one_crh_parameter"), &two_to_one_crh_params, ) @@ -182,19 +174,19 @@ mod byte_mt_tests { // // suppose the verifier already knows old root, new root, old leaf, new leaf, and the original path (so they are public) let old_root = tree.root(); - let old_root_var = >::OutputVar::new_input( + let old_root_var = as CRHGadget<_>>::OutputVar::new_input( ark_relations::ns!(cs, "old_root"), || Ok(old_root), ) .unwrap(); let old_path = tree.generate_proof(update_query.0).unwrap(); - let old_path_var: PathVar = + let old_path_var: PathVar = PathVar::new_input(ark_relations::ns!(cs, "old_path"), || Ok(old_path)).unwrap(); let new_root = { tree.update(update_query.0, &update_query.1).unwrap(); tree.root() }; - let new_root_var = >::OutputVar::new_input( + let new_root_var = as CRHGadget<_>>::OutputVar::new_input( ark_relations::ns!(cs, "new_root"), || Ok(new_root), ) @@ -239,11 +231,14 @@ mod byte_mt_tests { } mod field_mt_tests { - use crate::crh::{poseidon, TwoToOneCRHSchemeGadget}; use crate::merkle_tree::constraints::ConfigGadget; use crate::merkle_tree::tests::test_utils::poseidon_parameters; use crate::merkle_tree::{Config, IdentityDigestConverter}; - use crate::{CRHSchemeGadget, MerkleTree, PathVar}; + use crate::{ + crh::{poseidon, TwoToOneCRHGadget}, + Gadget, + }; + use crate::{CRHGadget, MerkleTree, PathVar}; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::uint32::UInt32; @@ -253,31 +248,22 @@ mod field_mt_tests { type F = ark_ed_on_bls12_381::Fr; type H = poseidon::CRH; - type HG = poseidon::constraints::CRHGadget; type TwoToOneH = poseidon::TwoToOneCRH; - type TwoToOneHG = poseidon::constraints::TwoToOneCRHGadget; type LeafVar = [FpVar]; struct FieldMTConfig; impl Config for FieldMTConfig { type Leaf = [F]; - type LeafDigest = F; - type LeafInnerDigestConverter = IdentityDigestConverter; - type InnerDigest = F; + type LeafToInnerConverter = IdentityDigestConverter; type LeafHash = H; type TwoToOneHash = TwoToOneH; } - struct FieldMTConfigVar; + impl ConfigGadget for FieldMTConfig { + type LeafVar = LeafVar; - impl ConfigGadget for FieldMTConfigVar { - type Leaf = LeafVar; - type LeafDigest = FpVar; - type LeafInnerConverter = IdentityDigestConverter>; - type InnerDigest = FpVar; - type LeafHash = HG; - type TwoToOneHash = TwoToOneHG; + type LeafToInnerVarConverter = IdentityDigestConverter; } type FieldMT = MerkleTree; @@ -315,14 +301,14 @@ mod field_mt_tests { let constraints_from_digest = cs.num_constraints(); println!("constraints from digest: {}", constraints_from_digest); - let leaf_crh_params_var = >::ParametersVar::new_constant( + let leaf_crh_params_var = as CRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "leaf_crh_params"), &leaf_crh_params, ) .unwrap(); let two_to_one_crh_params_var = - >::ParametersVar::new_constant( + as TwoToOneCRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "two_to_one_params"), &leaf_crh_params, ) @@ -342,7 +328,7 @@ mod field_mt_tests { println!("constraints from leaf: {}", constraints_from_leaf); // Allocate MT Path - let mut cw = PathVar::::new_witness( + let mut cw = PathVar::::new_witness( ark_relations::ns!(cs, "new_witness"), || Ok(&proof), ) @@ -399,14 +385,14 @@ mod field_mt_tests { if let Some(update_query) = update_query { let cs = ConstraintSystem::::new_ref(); // allocate parameters for CRH - let leaf_crh_params_var = >::ParametersVar::new_constant( + let leaf_crh_params_var = as CRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "leaf_crh_params"), &leaf_crh_params, ) .unwrap(); let two_to_one_crh_params_var = - >::ParametersVar::new_constant( + as TwoToOneCRHGadget<_>>::ParametersVar::new_constant( ark_relations::ns!(cs, "two_to_one_params"), &leaf_crh_params, ) @@ -426,11 +412,11 @@ mod field_mt_tests { let old_root_var = FpVar::new_input(cs.clone(), || Ok(old_root)).unwrap(); let old_path = tree.generate_proof(update_query.0).unwrap(); - let old_path_var = PathVar::::new_input( - ark_relations::ns!(cs, "old_path"), - || Ok(old_path), - ) - .unwrap(); + let old_path_var = + PathVar::::new_input(ark_relations::ns!(cs, "old_path"), || { + Ok(old_path) + }) + .unwrap(); let new_root = { tree.update(update_query.0, update_query.1.as_slice()) .unwrap(); diff --git a/src/merkle_tree/tests/mod.rs b/src/merkle_tree/tests/mod.rs index 1d1296c0..24a89644 100644 --- a/src/merkle_tree/tests/mod.rs +++ b/src/merkle_tree/tests/mod.rs @@ -27,9 +27,7 @@ mod bytes_mt_tests { impl Config for JubJubMerkleTreeParams { type Leaf = [u8]; - type LeafDigest = ::Output; - type LeafInnerDigestConverter = ByteDigestConverter; - type InnerDigest = ::Output; + type LeafToInnerConverter = ByteDigestConverter; type LeafHash = LeafH; type TwoToOneHash = CompressH; @@ -43,10 +41,8 @@ mod bytes_mt_tests { .iter() .map(|leaf| crate::to_unchecked_bytes!(leaf).unwrap()) .collect(); - let leaf_crh_params = ::setup(&mut rng).unwrap(); - let two_to_one_params = ::setup(&mut rng) - .unwrap() - .clone(); + let leaf_crh_params = ::setup(&mut rng).unwrap(); + let two_to_one_params = ::setup(&mut rng).unwrap().clone(); let mut tree = JubJubMerkleTree::new( &leaf_crh_params.clone(), &two_to_one_params.clone(), @@ -132,9 +128,8 @@ mod field_mt_tests { struct FieldMTConfig; impl Config for FieldMTConfig { type Leaf = [F]; - type LeafDigest = F; - type LeafInnerDigestConverter = IdentityDigestConverter; - type InnerDigest = F; + type LeafToInnerConverter = IdentityDigestConverter; + type LeafHash = H; type TwoToOneHash = TwoToOneH; } diff --git a/src/merkle_tree/tests/test_utils.rs b/src/merkle_tree/tests/test_utils.rs index 1c847c6c..242adfc4 100644 --- a/src/merkle_tree/tests/test_utils.rs +++ b/src/merkle_tree/tests/test_utils.rs @@ -651,8 +651,6 @@ pub(crate) fn poseidon_parameters() -> PoseidonParameters::new(full_rounds, partial_rounds, alpha, mds, ark) } - -#[cfg(feature = "r1cs")] -mod constraints {} diff --git a/src/prf/blake2s/constraints.rs b/src/prf/blake2s/constraints.rs index a7bd093a..b60c5e63 100644 --- a/src/prf/blake2s/constraints.rs +++ b/src/prf/blake2s/constraints.rs @@ -1,7 +1,7 @@ use ark_ff::PrimeField; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; -use crate::{prf::PRFGadget, Vec}; +use crate::{prf::PRFWithGadget, Vec}; use ark_r1cs_std::prelude::*; use core::borrow::Borrow; @@ -289,7 +289,6 @@ pub fn evaluate_blake2s_with_parameters( use crate::prf::Blake2s; -pub struct Blake2sGadget; #[derive(Clone, Debug)] pub struct OutputVar(pub Vec>); @@ -363,18 +362,21 @@ impl R1CSVar for OutputVar { } } -impl PRFGadget for Blake2sGadget { +impl PRFWithGadget for Blake2s { type OutputVar = OutputVar; #[tracing::instrument(target = "r1cs", skip(cs))] - fn new_seed(cs: impl Into>, seed: &[u8; 32]) -> Vec> { + fn new_seed_as_witness(cs: impl Into>, seed: &[u8; 32]) -> Vec> { let ns = cs.into(); let cs = ns.cs(); UInt8::new_witness_vec(ark_relations::ns!(cs, "New Blake2s seed"), seed).unwrap() } #[tracing::instrument(target = "r1cs", skip(seed, input))] - fn evaluate(seed: &[UInt8], input: &[UInt8]) -> Result { + fn evaluate_gadget( + seed: &[UInt8], + input: &[UInt8], + ) -> Result { assert_eq!(seed.len(), 32); let input: Vec<_> = seed .iter() @@ -394,11 +396,16 @@ mod test { use ark_ed_on_bls12_381::Fq as Fr; use ark_std::rand::Rng; - use crate::prf::blake2s::{constraints::evaluate_blake2s, Blake2s as B2SPRF}; + use crate::{ + prf::{ + blake2s::{constraints::evaluate_blake2s, Blake2s}, + PRFWithGadget, PRF, + }, + Gadget, + }; use ark_relations::r1cs::ConstraintSystem; use blake2::VarBlake2s; - use super::Blake2sGadget; use ark_r1cs_std::prelude::*; #[test] @@ -416,7 +423,7 @@ mod test { #[test] fn test_blake2s_prf() { - use crate::prf::{PRFGadget, PRF}; + use crate::prf::PRFGadget; let mut rng = ark_std::test_rng(); let cs = ConstraintSystem::::new_ref(); @@ -427,17 +434,17 @@ mod test { let mut input = [0u8; 32]; rng.fill(&mut input); - let seed_var = Blake2sGadget::new_seed(cs.clone(), &seed); + let seed_var = Blake2s::new_seed_as_witness(cs.clone(), &seed); let input_var = UInt8::new_witness_vec(ark_relations::ns!(cs, "declare_input"), &input).unwrap(); - let out = B2SPRF::evaluate(&seed, &input).unwrap(); - let actual_out_var = >::OutputVar::new_witness( + let out = Blake2s::evaluate(&seed, &input).unwrap(); + let actual_out_var = as PRFGadget>::OutputVar::new_witness( ark_relations::ns!(cs, "declare_output"), || Ok(out), ) .unwrap(); - let output_var = Blake2sGadget::evaluate(&seed_var, &input_var).unwrap(); + let output_var = Gadget::::evaluate(&seed_var, &input_var).unwrap(); output_var.enforce_equal(&actual_out_var).unwrap(); if !cs.is_satisfied().unwrap() { diff --git a/src/prf/constraints.rs b/src/prf/constraints.rs index bbca88c7..1c112f5a 100644 --- a/src/prf/constraints.rs +++ b/src/prf/constraints.rs @@ -1,20 +1,65 @@ use ark_ff::Field; use core::fmt::Debug; -use crate::{prf::PRF, Vec}; +use crate::{prf::PRF, Gadget, Vec}; use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_r1cs_std::prelude::*; -pub trait PRFGadget { - type OutputVar: EqGadget - + ToBytesGadget - + AllocVar - + R1CSVar +pub trait PRFWithGadget: PRF { + type OutputVar: EqGadget + + ToBytesGadget + + AllocVar + + R1CSVar + Clone + Debug; - fn new_seed(cs: impl Into>, seed: &P::Seed) -> Vec>; + fn new_seed_as_witness( + cs: impl Into>, + seed: &Self::Seed, + ) -> Vec>; - fn evaluate(seed: &[UInt8], input: &[UInt8]) -> Result; + fn evaluate_gadget( + seed: &[UInt8], + input: &[UInt8], + ) -> Result; +} + +pub trait PRFGadget { + type Native: PRFWithGadget; + type OutputVar: EqGadget + + ToBytesGadget + + AllocVar<::Output, ConstraintF> + + Clone + + Debug; + + fn new_seed_as_witness( + cs: impl Into>, + seed: &::Seed, + ) -> Vec> { + Self::Native::new_seed_as_witness(cs, seed) + } + + fn evaluate( + seed: &[UInt8], + input: &[UInt8], + ) -> Result { + Self::Native::evaluate_gadget(seed, input) + } +} + +impl PRFGadget for Gadget

+where + P: PRFWithGadget, + ConstraintF: Field, +{ + type Native = P; + type OutputVar = P::OutputVar; + + fn evaluate( + seed: &[UInt8], + input: &[UInt8], + ) -> Result { + P::evaluate_gadget(seed, input) + } } diff --git a/src/signature/constraints.rs b/src/signature/constraints.rs index a669c0ad..093781aa 100644 --- a/src/signature/constraints.rs +++ b/src/signature/constraints.rs @@ -2,16 +2,16 @@ use ark_ff::Field; use ark_r1cs_std::prelude::*; use ark_relations::r1cs::SynthesisError; -use crate::signature::SignatureScheme; +use crate::{signature::SignatureScheme, Gadget}; -pub trait SigVerifyGadget { - type ParametersVar: AllocVar + Clone; +pub trait SigVerifyWithGadget: SignatureScheme { + type ParametersVar: AllocVar + Clone; - type PublicKeyVar: ToBytesGadget + AllocVar + Clone; + type PublicKeyVar: ToBytesGadget + AllocVar + Clone; - type SignatureVar: ToBytesGadget + AllocVar + Clone; + type SignatureVar: ToBytesGadget + AllocVar + Clone; - fn verify( + fn verify_gadget( parameters: &Self::ParametersVar, public_key: &Self::PublicKeyVar, // TODO: Should we make this take in bytes or something different? @@ -20,6 +20,45 @@ pub trait SigVerifyGadget { ) -> Result, SynthesisError>; } +pub trait SigVerifyGadget { + type Native: SigVerifyWithGadget< + ConstraintF, + ParametersVar = Self::ParametersVar, + PublicKeyVar = Self::PublicKeyVar, + SignatureVar = Self::SignatureVar, + >; + type ParametersVar: AllocVar<::Parameters, ConstraintF> + Clone; + + type PublicKeyVar: ToBytesGadget + + AllocVar<::PublicKey, ConstraintF> + + Clone; + + type SignatureVar: ToBytesGadget + + AllocVar<::Signature, ConstraintF> + + Clone; + + fn verify( + parameters: &Self::ParametersVar, + public_key: &Self::PublicKeyVar, + // TODO: Should we make this take in bytes or something different? + message: &[UInt8], + signature: &Self::SignatureVar, + ) -> Result, SynthesisError> { + Self::Native::verify_gadget(parameters, public_key, message, signature) + } +} + +impl SigVerifyGadget for Gadget +where + S: SigVerifyWithGadget, + ConstraintF: Field, +{ + type Native = S; + type ParametersVar = S::ParametersVar; + type PublicKeyVar = S::PublicKeyVar; + type SignatureVar = S::SignatureVar; +} + pub trait SigRandomizePkGadget { type ParametersVar: AllocVar + Clone; diff --git a/src/snark/constraints.rs b/src/snark/constraints.rs index 05a76b2a..d84ec7da 100644 --- a/src/snark/constraints.rs +++ b/src/snark/constraints.rs @@ -1,6 +1,6 @@ use ark_ff::{BigInteger, FpParameters, PrimeField}; -use ark_nonnative_field::params::{get_params, OptimizationType}; -use ark_nonnative_field::{AllocatedNonNativeFieldVar, NonNativeFieldVar}; +use ark_r1cs_std::fields::nonnative::params::{get_params, OptimizationType}; +use ark_r1cs_std::fields::nonnative::{AllocatedNonNativeFieldVar, NonNativeFieldVar}; use ark_r1cs_std::prelude::*; use ark_r1cs_std::{ bits::boolean::Boolean, @@ -22,14 +22,16 @@ use ark_std::{ vec::{IntoIter, Vec}, }; +use crate::Gadget; + /// This implements constraints for SNARK verifiers. -pub trait SNARKGadget> { - type ProcessedVerifyingKeyVar: AllocVar + Clone; - type VerifyingKeyVar: AllocVar +pub trait SNARKWithGadget: SNARK { + type ProcessedVerifyingKeyVar: AllocVar + Clone; + type VerifyingKeyVar: AllocVar + ToBytesGadget + Clone; type InputVar: AllocVar, ConstraintF> + FromFieldElementsGadget + Clone; - type ProofVar: AllocVar + Clone; + type ProofVar: AllocVar + Clone; /// Information about the R1CS constraints required to check proofs relative /// a given verification key. In the context of a LPCP-based pairing-based SNARK @@ -42,7 +44,7 @@ pub trait SNARKGadget> { /// Returns information about the R1CS constraints required to check proofs relative /// to the verification key `circuit_vk`. - fn verifier_size(circuit_vk: &S::VerifyingKey) -> Self::VerifierSize; + fn verifier_size(circuit_vk: &Self::VerifyingKey) -> Self::VerifierSize; /// Optionally allocates `S::Proof` in `cs` without performing /// additional checks, such as subgroup membership checks. Use this *only* @@ -54,7 +56,7 @@ pub trait SNARKGadget> { /// The default implementation does not omit such checks, and just invokes /// `Self::ProofVar::new_variable`. #[tracing::instrument(target = "r1cs", skip(cs, f))] - fn new_proof_unchecked>( + fn new_proof_unchecked>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, @@ -72,7 +74,7 @@ pub trait SNARKGadget> { /// The default implementation does not omit such checks, and just invokes /// `Self::VerifyingKeyVar::new_variable`. #[tracing::instrument(target = "r1cs", skip(cs, f))] - fn new_verification_key_unchecked>( + fn new_verification_key_unchecked>( cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, @@ -80,34 +82,160 @@ pub trait SNARKGadget> { Self::VerifyingKeyVar::new_variable(cs, f, mode) } - fn verify_with_processed_vk( + fn verify_with_processed_vk_gadget( circuit_pvk: &Self::ProcessedVerifyingKeyVar, x: &Self::InputVar, proof: &Self::ProofVar, ) -> Result, SynthesisError>; - fn verify( + fn verify_gadget( circuit_vk: &Self::VerifyingKeyVar, x: &Self::InputVar, proof: &Self::ProofVar, ) -> Result, SynthesisError>; } -pub trait CircuitSpecificSetupSNARKGadget< +/// This implements constraints for SNARK verifiers. +pub trait SNARKGadget { + type Native: SNARKWithGadget< + F, + ConstraintF, + VerifierSize = Self::VerifierSize, + ProcessedVerifyingKeyVar = Self::ProcessedVerifyingKeyVar, + VerifyingKeyVar = Self::VerifyingKeyVar, + InputVar = Self::InputVar, + ProofVar = Self::ProofVar, + >; + type ProcessedVerifyingKeyVar: AllocVar<>::ProcessedVerifyingKey, ConstraintF> + + Clone; + type VerifyingKeyVar: AllocVar<>::VerifyingKey, ConstraintF> + + ToBytesGadget + + Clone; + type InputVar: AllocVar, ConstraintF> + FromFieldElementsGadget + Clone; + type ProofVar: AllocVar<>::Proof, ConstraintF> + Clone; + + /// Information about the R1CS constraints required to check proofs relative + /// a given verification key. In the context of a LPCP-based pairing-based SNARK + /// like that of [[Groth16]](https://eprint.iacr.org/2016/260), + /// this is independent of the R1CS matrices, + /// whereas for more "complex" SNARKs like [[Marlin]](https://eprint.iacr.org/2019/1047), + /// this can encode information about the highest degree of polynomials + /// required to verify proofs. + type VerifierSize: PartialOrd + Clone + fmt::Debug; + + /// Returns information about the R1CS constraints required to check proofs relative + /// to the verification key `circuit_vk`. + fn verifier_size(vk: &>::VerifyingKey) -> Self::VerifierSize { + Self::Native::verifier_size(vk) + } + + /// Optionally allocates `S::Proof` in `cs` without performing + /// additional checks, such as subgroup membership checks. Use this *only* + /// if you know it is safe to do so. Such "safe" scenarios can include + /// the case where `proof` is a public input (`mode == AllocationMode::Input`), + /// and the corresponding checks are performed by the SNARK verifier outside + /// the circuit. Another example is the when `mode == AllocationMode::Constant`. + /// + /// The default implementation does not omit such checks, and just invokes + /// `Self::ProofVar::new_variable`. + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_proof_unchecked>::Proof>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::Native::new_proof_unchecked(cs, f, mode) + } + + /// Optionally allocates `S::VerifyingKey` in `cs` without performing + /// additional checks, such as subgroup membership checks. Use this *only* + /// if you know it is safe to do so. Such "safe" scenarios can include + /// the case where `vk` is a public input (`mode == AllocationMode::Input`), + /// and the corresponding checks are performed by the SNARK verifier outside + /// the circuit. Another example is the when `mode == AllocationMode::Constant`. + /// + /// The default implementation does not omit such checks, and just invokes + /// `Self::VerifyingKeyVar::new_variable`. + #[tracing::instrument(target = "r1cs", skip(cs, f))] + fn new_verification_key_unchecked>::VerifyingKey>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + Self::Native::new_verification_key_unchecked(cs, f, mode) + } + + fn verify_with_processed_vk( + pvk: &Self::ProcessedVerifyingKeyVar, + x: &Self::InputVar, + proof: &Self::ProofVar, + ) -> Result, SynthesisError> { + Self::Native::verify_with_processed_vk_gadget(pvk, x, proof) + } + + fn verify( + vk: &Self::VerifyingKeyVar, + x: &Self::InputVar, + proof: &Self::ProofVar, + ) -> Result, SynthesisError> { + Self::Native::verify_gadget(vk, x, proof) + } +} + +impl SNARKGadget for Gadget +where + S: SNARKWithGadget, F: PrimeField, ConstraintF: PrimeField, - S: CircuitSpecificSetupSNARK, ->: SNARKGadget { + type Native = S; + type VerifierSize = S::VerifierSize; + type ProcessedVerifyingKeyVar = S::ProcessedVerifyingKeyVar; + type VerifyingKeyVar = S::VerifyingKeyVar; + type InputVar = S::InputVar; + type ProofVar = S::ProofVar; } -pub trait UniversalSetupSNARKGadget< +pub trait CircuitSpecificSetupSNARKGadget: + SNARKGadget +where + Self::Native: CircuitSpecificSetupSNARK, +{ +} + +impl CircuitSpecificSetupSNARKGadget for Gadget +where + S: CircuitSpecificSetupSNARK + SNARKWithGadget, F: PrimeField, ConstraintF: PrimeField, - S: UniversalSetupSNARK, ->: SNARKGadget { - type BoundCircuit: From + ConstraintSynthesizer + Clone; +} + +pub trait UniversalSetupSNARKWithGadget: + SNARKWithGadget + UniversalSetupSNARK +{ + type BoundCircuit: From<>::ComputationBound> + + ConstraintSynthesizer + + Clone; +} + +pub trait UniversalSetupSNARKGadget: + SNARKGadget +where + Self::Native: UniversalSetupSNARKWithGadget, +{ + type BoundCircuit: From<>::ComputationBound> + + ConstraintSynthesizer + + Clone; +} + +impl UniversalSetupSNARKGadget for Gadget +where + S: UniversalSetupSNARK + UniversalSetupSNARKWithGadget, + F: PrimeField, + ConstraintF: PrimeField, +{ + type BoundCircuit = S::BoundCircuit; } /// Gadgets to convert elements between different fields for recursive proofs @@ -375,29 +503,17 @@ impl FromFieldElementsGadget for BooleanIn /// Conversion of field elements by allocating them as nonnative field elements /// Used by Marlin -pub struct NonNativeFieldInputVar -where - F: PrimeField, - CF: PrimeField, -{ +pub struct NonNativeFieldInputVar { pub val: Vec>, } -impl NonNativeFieldInputVar -where - F: PrimeField, - CF: PrimeField, -{ +impl NonNativeFieldInputVar { pub fn new(val: Vec>) -> Self { Self { val } } } -impl IntoIterator for NonNativeFieldInputVar -where - F: PrimeField, - CF: PrimeField, -{ +impl IntoIterator for NonNativeFieldInputVar { type Item = NonNativeFieldVar; type IntoIter = IntoIter>; @@ -406,11 +522,7 @@ where } } -impl Clone for NonNativeFieldInputVar -where - F: PrimeField, - CF: PrimeField, -{ +impl Clone for NonNativeFieldInputVar { fn clone(&self) -> Self { Self { val: self.val.clone(), @@ -418,11 +530,7 @@ where } } -impl AllocVar, CF> for NonNativeFieldInputVar -where - F: PrimeField, - CF: PrimeField, -{ +impl AllocVar, CF> for NonNativeFieldInputVar { fn new_variable>>( cs: impl Into>, f: impl FnOnce() -> Result,