fold-babyjubjubs/src/fcircuit.rs
src/fcircuit.rsRaw
1use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
2use ark_ec::CurveGroup;
3use ark_ff::{BigInteger, Field, PrimeField};
4use ark_r1cs_std::alloc::{AllocVar, AllocationMode};
5use ark_r1cs_std::prelude::CurveVar;
6use ark_r1cs_std::{
7 boolean::Boolean,
8 eq::EqGadget,
9 fields::{fp::FpVar, FieldVar},
10};
11use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError};
12use ark_std::{marker::PhantomData, Zero};
13use core::borrow::Borrow;
14use std::fmt::Debug;
15
16use arkeddsa::{constraints::verify, signature::Signature, PublicKey};
17use folding_schemes::{frontend::FCircuit, Error};
18
19pub type CF<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
20
21/// Test circuit to be folded
22#[derive(Clone, Debug)]
23pub struct FoldSigsStepCircuit<
24 F: PrimeField,
25 C: CurveGroup,
26 GC: CurveVar<C, F>,
27 const SIGS_PER_STEP: usize,
28> {
29 _c: PhantomData<C>,
30 _gc: PhantomData<GC>,
31 config: PoseidonConfig<F>,
32}
33impl<F: PrimeField, C: CurveGroup, GC: CurveVar<C, F>, const SIGS_PER_STEP: usize> FCircuit<F>
34 for FoldSigsStepCircuit<F, C, GC, SIGS_PER_STEP>
35where
36 F: Absorb,
37 C: CurveGroup<BaseField = F>,
38{
39 type Params = PoseidonConfig<F>;
40 type ExternalInputs = VecExtInp<C, SIGS_PER_STEP>;
41 type ExternalInputsVar = VecExtInpVar<C, GC, SIGS_PER_STEP>;
42
43 fn new(config: Self::Params) -> Result<Self, Error> {
44 Ok(Self {
45 _c: PhantomData,
46 _gc: PhantomData,
47 config,
48 })
49 }
50 fn state_len(&self) -> usize {
51 1
52 }
53 fn generate_step_constraints(
54 &self,
55 cs: ConstraintSystemRef<F>,
56 _i: usize,
57 z_i: Vec<FpVar<F>>,
58 external_inputs: Self::ExternalInputsVar,
59 ) -> Result<Vec<FpVar<F>>, SynthesisError> {
60 let mut count = z_i[0].clone();
61 for i in 0..SIGS_PER_STEP {
62 let e = external_inputs.0[i].clone();
63 let res = verify::<C, GC>(
64 cs.clone(),
65 self.config.clone(),
66 e.pk,
67 (e.sig_r, e.sig_s),
68 e.msg,
69 )?;
70 res.enforce_equal(&Boolean::<F>::TRUE)?;
71 count = count.clone() + FpVar::<F>::one();
72 }
73
74 Ok(vec![count])
75 }
76}
77
78// recall, here C = ed_on_bn254, so C::BaseField = BN254::ScalarField
79#[derive(Clone, Copy, Debug, PartialEq)]
80pub struct ExtInp<C: CurveGroup> {
81 msg: CF<C>,
82 pk: PublicKey<C>,
83 sig: Signature<C>,
84}
85impl<C: CurveGroup> Default for ExtInp<C> {
86 fn default() -> Self {
87 Self {
88 msg: CF::<C>::zero(),
89 pk: PublicKey(C::zero().into_affine()),
90 sig: Signature::new(C::zero().into_affine(), C::ScalarField::zero()),
91 }
92 }
93}
94
95#[derive(Clone, Debug)]
96pub struct VecExtInp<C: CurveGroup, const SIGS_PER_STEP: usize>(Vec<ExtInp<C>>);
97impl<C: CurveGroup, const SIGS_PER_STEP: usize> Default for VecExtInp<C, SIGS_PER_STEP> {
98 fn default() -> Self {
99 VecExtInp(vec![ExtInp::default(); SIGS_PER_STEP])
100 }
101}
102
103#[derive(Clone, Debug)]
104pub struct ExtInpVar<C: CurveGroup, GC: CurveVar<C, CF<C>>> {
105 msg: FpVar<CF<C>>,
106 pk: GC,
107 sig_r: GC,
108 sig_s: Vec<Boolean<CF<C>>>,
109}
110impl<C: CurveGroup, GC: CurveVar<C, CF<C>>> Default for ExtInpVar<C, GC> {
111 fn default() -> Self {
112 Self {
113 msg: FpVar::<CF<C>>::zero(),
114 pk: GC::zero(),
115 sig_r: GC::zero(),
116 sig_s: vec![Boolean::<CF<C>>::FALSE; 253], // TODO 253-> fieldbitsize
117 }
118 }
119}
120
121#[derive(Clone, Debug)]
122pub struct VecExtInpVar<C: CurveGroup, GC: CurveVar<C, CF<C>>, const SIGS_PER_STEP: usize>(
123 Vec<ExtInpVar<C, GC>>,
124);
125impl<C: CurveGroup, GC: CurveVar<C, CF<C>>, const SIGS_PER_STEP: usize> Default
126 for VecExtInpVar<C, GC, SIGS_PER_STEP>
127{
128 fn default() -> Self {
129 VecExtInpVar(vec![ExtInpVar::default(); SIGS_PER_STEP])
130 }
131}
132
133impl<C, GC, const SIGS_PER_STEP: usize> AllocVar<VecExtInp<C, SIGS_PER_STEP>, CF<C>>
134 for VecExtInpVar<C, GC, SIGS_PER_STEP>
135where
136 C: CurveGroup,
137 GC: CurveVar<C, CF<C>>,
138{
139 fn new_variable<T: Borrow<VecExtInp<C, SIGS_PER_STEP>>>(
140 cs: impl Into<Namespace<CF<C>>>,
141 f: impl FnOnce() -> Result<T, SynthesisError>,
142 mode: AllocationMode,
143 ) -> Result<Self, SynthesisError> {
144 f().and_then(|val| {
145 let cs = cs.into();
146
147 let mut v = vec![];
148 for e in val.borrow().0.iter() {
149 let msg = FpVar::<CF<C>>::new_variable(cs.clone(), || Ok(e.msg), mode)?;
150 let pk = GC::new_variable(cs.clone(), || Ok(e.pk.0), mode)?;
151 let sig_r = GC::new_variable(cs.clone(), || Ok(e.sig.r), mode)?;
152 let sig_s = Vec::<Boolean<CF<C>>>::new_variable(
153 cs.clone(),
154 || Ok(e.sig.s.into_bigint().to_bits_le()),
155 mode,
156 )?;
157 v.push(ExtInpVar {
158 msg,
159 pk,
160 sig_r,
161 sig_s,
162 });
163 }
164
165 Ok(Self(v))
166 })
167 }
168}
169
170#[cfg(test)]
171pub mod tests {
172 use super::*;
173 use ark_bn254::Fr;
174 use ark_ec::AdditiveGroup;
175 use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
176 use ark_relations::r1cs::ConstraintSystem;
177 use ark_std::rand::Rng;
178 use rand_core::CryptoRngCore;
179 use std::str::FromStr;
180
181 use folding_schemes::transcript::poseidon::poseidon_canonical_config;
182
183 use arkeddsa::{
184 ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective},
185 SigningKey,
186 };
187
188 pub fn gen_signatures<R: Rng + CryptoRngCore, const SIGS_PER_STEP: usize>(
189 rng: &mut R,
190 poseidon_config: &PoseidonConfig<Fr>,
191 steps: usize,
192 ) -> Vec<VecExtInp<EdwardsProjective, SIGS_PER_STEP>> {
193 let mut r = vec![];
194 for _ in 0..steps {
195 let mut res: Vec<ExtInp<EdwardsProjective>> = Vec::new();
196 for _ in 0..SIGS_PER_STEP {
197 let sk =
198 SigningKey::<EdwardsProjective>::generate::<blake2::Blake2b512>(rng).unwrap();
199 let msg = Fr::from_str("12345").unwrap();
200 let sig = sk
201 .sign::<blake2::Blake2b512>(&poseidon_config, &msg)
202 .unwrap();
203 let pk = sk.public_key();
204 pk.verify(&poseidon_config, &msg, &sig).unwrap();
205 res.push(ExtInp {
206 msg,
207 pk: pk.clone(),
208 sig,
209 });
210 }
211 r.push(VecExtInp(res));
212 }
213 r
214 }
215
216 #[test]
217 fn test_sig() {
218 const SIGS_PER_STEP: usize = 10;
219 let mut rng = rand::rngs::OsRng;
220 let poseidon_config = poseidon_canonical_config::<Fr>();
221
222 const N: usize = 1;
223 let ext_inps =
224 gen_signatures::<rand::rngs::OsRng, SIGS_PER_STEP>(&mut rng, &poseidon_config, 1);
225 let e = ext_inps[0].0[0].clone();
226 e.pk.verify(&poseidon_config, &e.msg, &e.sig).unwrap();
227 }
228
229 fn ensure_fcircuit_trait<FC: FCircuit<Fr>>(params: FC::Params) {
230 let _ = FC::new(params);
231 }
232
233 // test to check that the Sha256FCircuit computes the same values inside and outside the circuit
234 #[test]
235 fn test_fcircuit() {
236 const SIGS_PER_STEP: usize = 10;
237 let mut rng = rand::rngs::OsRng;
238 let poseidon_config = poseidon_canonical_config::<Fr>();
239
240 let ext_inps =
241 gen_signatures::<rand::rngs::OsRng, SIGS_PER_STEP>(&mut rng, &poseidon_config, 1);
242 let ext_inps = ext_inps[0].clone();
243
244 // here `Fr` is the BN254::G1::Fr = ed_on_bn254_twist::EdwardsProjective::Fq
245 let cs = ConstraintSystem::<Fr>::new_ref();
246
247 type FC = FoldSigsStepCircuit<Fr, EdwardsProjective, EdwardsVar, SIGS_PER_STEP>;
248 ensure_fcircuit_trait::<FC>(poseidon_config.clone());
249
250 let circuit = FC::new(poseidon_config).unwrap();
251 let z_i = vec![Fr::ZERO];
252
253 let external_inputs_var =
254 VecExtInpVar::<EdwardsProjective, EdwardsVar, SIGS_PER_STEP>::new_witness(
255 cs.clone(),
256 || Ok(ext_inps),
257 )
258 .unwrap();
259
260 let z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i)).unwrap();
261 let computed_z_i1Var = circuit
262 .generate_step_constraints(cs.clone(), 0, z_iVar.clone(), external_inputs_var)
263 .unwrap();
264 assert_eq!(
265 computed_z_i1Var.value().unwrap(),
266 vec![Fr::from(SIGS_PER_STEP as u32)]
267 );
268 assert!(cs.is_satisfied().unwrap());
269 dbg!(cs.num_constraints());
270 dbg!(&computed_z_i1Var.value().unwrap());
271 }
272}