Browse Source

add FCircuit of the EdDSA verifications

main
arnaucube 3 months ago
parent
commit
63f7e825ea
3 changed files with 332 additions and 0 deletions
  1. +39
    -0
      Cargo.toml
  2. +21
    -0
      LICENSE
  3. +272
    -0
      src/fcircuit.rs

+ 39
- 0
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 = []

+ 21
- 0
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.

+ 272
- 0
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());
}
}

Loading…
Cancel
Save