@ -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 = [] |
||||
|
|
@ -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. |
@ -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());
|
||||
|
}
|
||||
|
}
|