use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::CurveGroup; use ark_ff::{BigInteger, Field, PrimeField}; use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; use ark_r1cs_std::prelude::CurveVar; use ark_r1cs_std::{ boolean::Boolean, eq::EqGadget, fields::{fp::FpVar, FieldVar}, }; use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use ark_std::{marker::PhantomData, Zero}; use core::borrow::Borrow; use std::fmt::Debug; use arkeddsa::{constraints::verify, signature::Signature, PublicKey}; use folding_schemes::{frontend::FCircuit, Error}; pub type CF = <::BaseField as Field>::BasePrimeField; /// Test circuit to be folded #[derive(Clone, Debug)] pub struct FoldSigsStepCircuit< F: PrimeField, C: CurveGroup, GC: CurveVar, const SIGS_PER_STEP: usize, > { _c: PhantomData, _gc: PhantomData, config: PoseidonConfig, } impl, const SIGS_PER_STEP: usize> FCircuit for FoldSigsStepCircuit where F: Absorb, C: CurveGroup, { type Params = PoseidonConfig; type ExternalInputs = VecExtInp; type ExternalInputsVar = VecExtInpVar; fn new(config: Self::Params) -> Result { Ok(Self { _c: PhantomData, _gc: PhantomData, config, }) } fn state_len(&self) -> usize { 1 } fn generate_step_constraints( &self, cs: ConstraintSystemRef, _i: usize, z_i: Vec>, external_inputs: Self::ExternalInputsVar, ) -> Result>, SynthesisError> { let mut count = z_i[0].clone(); for i in 0..SIGS_PER_STEP { let e = external_inputs.0[i].clone(); let res = verify::( cs.clone(), self.config.clone(), e.pk, (e.sig_r, e.sig_s), e.msg, )?; res.enforce_equal(&Boolean::::TRUE)?; count = count.clone() + FpVar::::one(); } Ok(vec![count]) } } // recall, here C = ed_on_bn254, so C::BaseField = BN254::ScalarField #[derive(Clone, Copy, Debug, PartialEq)] pub struct ExtInp { msg: CF, pk: PublicKey, sig: Signature, } impl Default for ExtInp { fn default() -> Self { Self { msg: CF::::zero(), pk: PublicKey(C::zero().into_affine()), sig: Signature::new(C::zero().into_affine(), C::ScalarField::zero()), } } } #[derive(Clone, Debug)] pub struct VecExtInp(Vec>); impl Default for VecExtInp { fn default() -> Self { VecExtInp(vec![ExtInp::default(); SIGS_PER_STEP]) } } #[derive(Clone, Debug)] pub struct ExtInpVar>> { msg: FpVar>, pk: GC, sig_r: GC, sig_s: Vec>>, } impl>> Default for ExtInpVar { fn default() -> Self { Self { msg: FpVar::>::zero(), pk: GC::zero(), sig_r: GC::zero(), sig_s: vec![Boolean::>::FALSE; 253], // TODO 253-> fieldbitsize } } } #[derive(Clone, Debug)] pub struct VecExtInpVar>, const SIGS_PER_STEP: usize>( Vec>, ); impl>, const SIGS_PER_STEP: usize> Default for VecExtInpVar { fn default() -> Self { VecExtInpVar(vec![ExtInpVar::default(); SIGS_PER_STEP]) } } impl AllocVar, CF> for VecExtInpVar where C: CurveGroup, GC: CurveVar>, { fn new_variable>>( cs: impl Into>>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { f().and_then(|val| { let cs = cs.into(); let mut v = vec![]; for e in val.borrow().0.iter() { let msg = FpVar::>::new_variable(cs.clone(), || Ok(e.msg), mode)?; let pk = GC::new_variable(cs.clone(), || Ok(e.pk.0), mode)?; let sig_r = GC::new_variable(cs.clone(), || Ok(e.sig.r), mode)?; let sig_s = Vec::>>::new_variable( cs.clone(), || Ok(e.sig.s.into_bigint().to_bits_le()), mode, )?; v.push(ExtInpVar { msg, pk, sig_r, sig_s, }); } Ok(Self(v)) }) } } #[cfg(test)] pub mod tests { use super::*; use ark_bn254::Fr; use ark_ec::AdditiveGroup; use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_relations::r1cs::ConstraintSystem; use ark_std::rand::Rng; use rand_core::CryptoRngCore; use std::str::FromStr; use folding_schemes::transcript::poseidon::poseidon_canonical_config; use arkeddsa::{ ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective}, SigningKey, }; pub fn gen_signatures( rng: &mut R, poseidon_config: &PoseidonConfig, steps: usize, ) -> Vec> { let mut r = vec![]; for _ in 0..steps { let mut res: Vec> = Vec::new(); for _ in 0..SIGS_PER_STEP { let sk = SigningKey::::generate::(rng).unwrap(); let msg = Fr::from_str("12345").unwrap(); let sig = sk .sign::(&poseidon_config, &msg) .unwrap(); let pk = sk.public_key(); pk.verify(&poseidon_config, &msg, &sig).unwrap(); res.push(ExtInp { msg, pk: pk.clone(), sig, }); } r.push(VecExtInp(res)); } r } #[test] fn test_sig() { const SIGS_PER_STEP: usize = 10; let mut rng = rand::rngs::OsRng; let poseidon_config = poseidon_canonical_config::(); const N: usize = 1; let ext_inps = gen_signatures::(&mut rng, &poseidon_config, 1); let e = ext_inps[0].0[0].clone(); e.pk.verify(&poseidon_config, &e.msg, &e.sig).unwrap(); } fn ensure_fcircuit_trait>(params: FC::Params) { let _ = FC::new(params); } // test to check that the Sha256FCircuit computes the same values inside and outside the circuit #[test] fn test_fcircuit() { const SIGS_PER_STEP: usize = 10; let mut rng = rand::rngs::OsRng; let poseidon_config = poseidon_canonical_config::(); let ext_inps = gen_signatures::(&mut rng, &poseidon_config, 1); let ext_inps = ext_inps[0].clone(); // here `Fr` is the BN254::G1::Fr = ed_on_bn254_twist::EdwardsProjective::Fq let cs = ConstraintSystem::::new_ref(); type FC = FoldSigsStepCircuit; ensure_fcircuit_trait::(poseidon_config.clone()); let circuit = FC::new(poseidon_config).unwrap(); let z_i = vec![Fr::ZERO]; let external_inputs_var = VecExtInpVar::::new_witness( cs.clone(), || Ok(ext_inps), ) .unwrap(); let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i)).unwrap(); let computed_z_i1Var = circuit .generate_step_constraints(cs.clone(), 0, z_iVar.clone(), external_inputs_var) .unwrap(); assert_eq!( computed_z_i1Var.value().unwrap(), vec![Fr::from(SIGS_PER_STEP as u32)] ); assert!(cs.is_satisfied().unwrap()); dbg!(cs.num_constraints()); dbg!(&computed_z_i1Var.value().unwrap()); } }