arnaucube
arnaucube/fold-babyjubjubsPublic Self-hosted repository
add FCircuit of the EdDSA verifications
arnaucube git@arnaucube.com
Wed, 25 Dec 2024 10:06:56 +0100
commit

63f7e825ea7c71f2fc5365787a96a9a5a0ed9df0

parent

cb7330d4b6bf1e1cead669db1cf9d98a03b36af3

3 files changed, 332 insertions(+), 0 deletions(-)

jump to
A Cargo.toml

@@ -0,0 +1,39 @@

+[package] +name = "fold-babyjubjubs" +version = "0.1.0" +edition = "2021" + +[dependencies] +ark-bn254 = { version = "0.5.0", features = ["r1cs"] } +ark-grumpkin = {version="0.5.0", features=["r1cs"]} +ark-ec = "0.5.0" +ark-ff = "0.5.0" +ark-r1cs-std = { version = "0.5.0", default-features = false } +ark-relations = { version = "0.5.0", default-features = false } +ark-crypto-primitives = { version = "^0.5.0", default-features = false, features = [ + "r1cs", + "sponge", + "crh", +] } +ark-std = "0.5.0" +rand = "0.8.5" +rand_core = {version = "0.6", default-features = false} + +# Note: for testing purposes we use the 'light-test' feature when importing +# Sonobe's folding-schemes, but for a real-world usage it must be used without +# this feature (but then the DeciderETH circuit is bigger and takes more time +# to compute). +folding-schemes = { git = "https://github.com/privacy-scaling-explorations/sonobe", package = "folding-schemes", features=["light-test"], rev="964f9b6de91283c2bf12ffdd7578125dc999104a"} +# folding-schemes = { path = "../../sonobe/sonobe_FCIRCUIT-EXTINP/folding-schemes", package = "folding-schemes", features=["light-test"]} +blake2 = "0.10" +arkeddsa = { git = "https://github.com/arnaucube/arkeddsa", features=["r1cs"], rev="67c077e0e564d5f8d193a44f67357dfd1180cb64"} +# arkeddsa = { path = "../arkeddsa_TE-to-C", features=["r1cs"]} + + + + +[dev-dependencies] + +[features] +default = [] +
A LICENSE

@@ -0,0 +1,21 @@

+MIT License + +Copyright (c) 2023 Privacy & Scaling Explorations (formerly known as appliedzkp) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE.
A src/fcircuit.rs

@@ -0,0 +1,272 @@

+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<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField; + +/// Test circuit to be folded +#[derive(Clone, Debug)] +pub struct FoldSigsStepCircuit< + F: PrimeField, + C: CurveGroup, + GC: CurveVar<C, F>, + const SIGS_PER_STEP: usize, +> { + _c: PhantomData<C>, + _gc: PhantomData<GC>, + config: PoseidonConfig<F>, +} +impl<F: PrimeField, C: CurveGroup, GC: CurveVar<C, F>, const SIGS_PER_STEP: usize> FCircuit<F> + for FoldSigsStepCircuit<F, C, GC, SIGS_PER_STEP> +where + F: Absorb, + C: CurveGroup<BaseField = F>, +{ + type Params = PoseidonConfig<F>; + type ExternalInputs = VecExtInp<C, SIGS_PER_STEP>; + type ExternalInputsVar = VecExtInpVar<C, GC, SIGS_PER_STEP>; + + fn new(config: Self::Params) -> Result<Self, Error> { + Ok(Self { + _c: PhantomData, + _gc: PhantomData, + config, + }) + } + fn state_len(&self) -> usize { + 1 + } + fn generate_step_constraints( + &self, + cs: ConstraintSystemRef<F>, + _i: usize, + z_i: Vec<FpVar<F>>, + external_inputs: Self::ExternalInputsVar, + ) -> Result<Vec<FpVar<F>>, 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::<C, GC>( + cs.clone(), + self.config.clone(), + e.pk, + (e.sig_r, e.sig_s), + e.msg, + )?; + res.enforce_equal(&Boolean::<F>::TRUE)?; + count = count.clone() + FpVar::<F>::one(); + } + + Ok(vec![count]) + } +} + +// recall, here C = ed_on_bn254, so C::BaseField = BN254::ScalarField +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ExtInp<C: CurveGroup> { + msg: CF<C>, + pk: PublicKey<C>, + sig: Signature<C>, +} +impl<C: CurveGroup> Default for ExtInp<C> { + fn default() -> Self { + Self { + msg: CF::<C>::zero(), + pk: PublicKey(C::zero().into_affine()), + sig: Signature::new(C::zero().into_affine(), C::ScalarField::zero()), + } + } +} + +#[derive(Clone, Debug)] +pub struct VecExtInp<C: CurveGroup, const SIGS_PER_STEP: usize>(Vec<ExtInp<C>>); +impl<C: CurveGroup, const SIGS_PER_STEP: usize> Default for VecExtInp<C, SIGS_PER_STEP> { + fn default() -> Self { + VecExtInp(vec![ExtInp::default(); SIGS_PER_STEP]) + } +} + +#[derive(Clone, Debug)] +pub struct ExtInpVar<C: CurveGroup, GC: CurveVar<C, CF<C>>> { + msg: FpVar<CF<C>>, + pk: GC, + sig_r: GC, + sig_s: Vec<Boolean<CF<C>>>, +} +impl<C: CurveGroup, GC: CurveVar<C, CF<C>>> Default for ExtInpVar<C, GC> { + fn default() -> Self { + Self { + msg: FpVar::<CF<C>>::zero(), + pk: GC::zero(), + sig_r: GC::zero(), + sig_s: vec![Boolean::<CF<C>>::FALSE; 253], // TODO 253-> fieldbitsize + } + } +} + +#[derive(Clone, Debug)] +pub struct VecExtInpVar<C: CurveGroup, GC: CurveVar<C, CF<C>>, const SIGS_PER_STEP: usize>( + Vec<ExtInpVar<C, GC>>, +); +impl<C: CurveGroup, GC: CurveVar<C, CF<C>>, const SIGS_PER_STEP: usize> Default + for VecExtInpVar<C, GC, SIGS_PER_STEP> +{ + fn default() -> Self { + VecExtInpVar(vec![ExtInpVar::default(); SIGS_PER_STEP]) + } +} + +impl<C, GC, const SIGS_PER_STEP: usize> AllocVar<VecExtInp<C, SIGS_PER_STEP>, CF<C>> + for VecExtInpVar<C, GC, SIGS_PER_STEP> +where + C: CurveGroup, + GC: CurveVar<C, CF<C>>, +{ + fn new_variable<T: Borrow<VecExtInp<C, SIGS_PER_STEP>>>( + cs: impl Into<Namespace<CF<C>>>, + f: impl FnOnce() -> Result<T, SynthesisError>, + mode: AllocationMode, + ) -> Result<Self, SynthesisError> { + f().and_then(|val| { + let cs = cs.into(); + + let mut v = vec![]; + for e in val.borrow().0.iter() { + let msg = FpVar::<CF<C>>::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::<Boolean<CF<C>>>::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<R: Rng + CryptoRngCore, const SIGS_PER_STEP: usize>( + rng: &mut R, + poseidon_config: &PoseidonConfig<Fr>, + steps: usize, + ) -> Vec<VecExtInp<EdwardsProjective, SIGS_PER_STEP>> { + let mut r = vec![]; + for _ in 0..steps { + let mut res: Vec<ExtInp<EdwardsProjective>> = Vec::new(); + for _ in 0..SIGS_PER_STEP { + let sk = + SigningKey::<EdwardsProjective>::generate::<blake2::Blake2b512>(rng).unwrap(); + let msg = Fr::from_str("12345").unwrap(); + let sig = sk + .sign::<blake2::Blake2b512>(&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::<Fr>(); + + const N: usize = 1; + let ext_inps = + gen_signatures::<rand::rngs::OsRng, SIGS_PER_STEP>(&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<FC: FCircuit<Fr>>(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::<Fr>(); + + let ext_inps = + gen_signatures::<rand::rngs::OsRng, SIGS_PER_STEP>(&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::<Fr>::new_ref(); + + type FC = FoldSigsStepCircuit<Fr, EdwardsProjective, EdwardsVar, SIGS_PER_STEP>; + ensure_fcircuit_trait::<FC>(poseidon_config.clone()); + + let circuit = FC::new(poseidon_config).unwrap(); + let z_i = vec![Fr::ZERO]; + + let external_inputs_var = + VecExtInpVar::<EdwardsProjective, EdwardsVar, SIGS_PER_STEP>::new_witness( + cs.clone(), + || Ok(ext_inps), + ) + .unwrap(); + + let z_iVar = Vec::<FpVar<Fr>>::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()); + } +}