diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..580b368 --- /dev/null +++ b/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 = [] + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..245499e --- /dev/null +++ b/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. diff --git a/src/fcircuit.rs b/src/fcircuit.rs new file mode 100644 index 0000000..fb7e58c --- /dev/null +++ b/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 = <::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()); + } +}