diff --git a/src/fcircuit.rs b/src/fcircuit.rs new file mode 100644 index 0000000..4942d9c --- /dev/null +++ b/src/fcircuit.rs @@ -0,0 +1,155 @@ +use ark_crypto_primitives::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, + Absorb, +}; +use ark_ec::CurveGroup; +use ark_ff::{Field, PrimeField}; +use ark_r1cs_std::prelude::CurveVar; +use ark_r1cs_std::{ + boolean::Boolean, + eq::EqGadget, + fields::{fp::FpVar, FieldVar}, +}; +use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; +use ark_std::marker::PhantomData; +use std::fmt::Debug; + +use arkeddsa::constraints::verify; +use folding_schemes::{frontend::FCircuit, Error}; + +use crate::signature::{SigPk, SigPkVar}; + +pub type CF = <::BaseField as Field>::BasePrimeField; + +/// Test circuit to be folded +#[derive(Clone, Debug)] +pub struct EthDosCircuit> { + _c: PhantomData, + _gc: PhantomData, + config: PoseidonConfig, +} +impl> FCircuit for EthDosCircuit +where + F: Absorb, + C: CurveGroup, +{ + type Params = PoseidonConfig; + type ExternalInputs = SigPk; + type ExternalInputsVar = SigPkVar; + + fn new(config: Self::Params) -> Result { + Ok(Self { + _c: PhantomData, + _gc: PhantomData, + config, + }) + } + fn state_len(&self) -> usize { + 5 + } + fn generate_step_constraints( + &self, + cs: ConstraintSystemRef, + _i: usize, + z_i: Vec>, + external_inputs: Self::ExternalInputsVar, + ) -> Result>, SynthesisError> { + let pk_0_x = z_i[0].clone(); + let pk_0_y = z_i[1].clone(); + let pk_i_x = z_i[2].clone(); + let pk_i_y = z_i[3].clone(); + let mut degree = z_i[4].clone(); + + // get the 'msg' that has been signed, which is the hash of the previous-signer public key + let mut poseidon = PoseidonSpongeVar::new(cs.clone(), &self.config); + poseidon.absorb(&vec![pk_i_x, pk_i_y])?; + let h = poseidon.squeeze_field_elements(1)?; + let msg = h + .first() + .ok_or(ark_relations::r1cs::SynthesisError::Unsatisfiable)?; + + // check that the last signer is signed by the new signer + let ei: SigPkVar = external_inputs.into(); + let res = verify::( + cs.clone(), + self.config.clone(), + ei.pk.clone(), + (ei.sig_r, ei.sig_s), + msg.clone(), + )?; + res.enforce_equal(&Boolean::::TRUE)?; + + // increment the degree + degree = degree.clone() + FpVar::::one(); + + let pk_i1_xy = ei.pk.to_constraint_field()?; + Ok(vec![vec![pk_0_x, pk_0_y], pk_i1_xy, vec![degree]].concat()) + } +} +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bn254::Fr; + use ark_ec::AffineRepr; + use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; + use ark_relations::r1cs::ConstraintSystem; + use ark_std::Zero; + use rand::rngs::OsRng; + + use crate::signature::{gen_signatures, hash_pk}; + use arkeddsa::ed_on_bn254_twist::{constraints::EdwardsVar, EdwardsProjective}; + use folding_schemes::transcript::poseidon::poseidon_canonical_config; + + #[test] + fn test_sig() { + let mut rng = 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].clone(); + + let msg = hash_pk(&poseidon_config, e.pk); + + e.pk.verify(&poseidon_config, &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() { + let mut rng = rand::rngs::OsRng; + let poseidon_config = poseidon_canonical_config::(); + + let pks_sigs = gen_signatures::(&mut rng, &poseidon_config, 1); + + // here `Fr` is the BN254::G1::Fr = ed_on_bn254_twist::EdwardsProjective::Fq + let cs = ConstraintSystem::::new_ref(); + + type FC = EthDosCircuit; + ensure_fcircuit_trait::(poseidon_config.clone()); + + let circuit = FC::new(poseidon_config).unwrap(); + let xy: (Fr, Fr) = pks_sigs[0].pk.0.xy().unwrap(); + let pk0 = vec![xy.0, xy.1]; + let z_i: Vec = vec![pk0.clone(), pk0, vec![Fr::zero()]].concat(); + + let external_inputs_var = + SigPkVar::::new_witness(cs.clone(), || Ok(pks_sigs[0])) + .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(); + // check that the degree (in the last state) is 1, the amount of signatures verified + assert_eq!(computed_z_i1Var.value().unwrap()[4], Fr::from(1_u32)); + assert!(cs.is_satisfied().unwrap()); + dbg!(cs.num_constraints()); + dbg!(&computed_z_i1Var.value().unwrap()); + } +} diff --git a/src/signature.rs b/src/signature.rs new file mode 100644 index 0000000..5e234ca --- /dev/null +++ b/src/signature.rs @@ -0,0 +1,157 @@ +use ark_crypto_primitives::sponge::{ + poseidon::{PoseidonConfig, PoseidonSponge}, + Absorb, CryptographicSponge, +}; +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::alloc::{AllocVar, AllocationMode}; +use ark_r1cs_std::boolean::Boolean; +use ark_r1cs_std::prelude::CurveVar; +use ark_relations::r1cs::{Namespace, SynthesisError}; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; +use ark_std::{rand::Rng, Zero}; +use core::borrow::Borrow; +use rand_core::CryptoRngCore; +use std::fmt::Debug; + +use arkeddsa::{signature::Signature, PublicKey, SigningKey}; + +use crate::fcircuit::CF; + +// recall, here C = ed_on_bn254, so C::BaseField = BN254::ScalarField +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct SigPk { + pub pk: PublicKey, + pub sig: Signature, +} +impl Default for SigPk { + fn default() -> Self { + Self { + pk: PublicKey(C::zero().into_affine()), + sig: Signature::new(C::zero().into_affine(), C::ScalarField::zero()), + } + } +} +impl SigPk { + pub fn to_bytes(&self) -> Vec { + let sig_bytes = self.sig.to_bytes(); + let pk_bytes = self.pk.to_bytes(); + vec![sig_bytes, pk_bytes].concat() + } + pub fn from_bytes(b: Vec) -> Self { + let u_point_size = C::Affine::generator().serialized_size(ark_serialize::Compress::No); + + let sig = Signature::::from_bytes(&b[..32 + u_point_size]).unwrap(); + let pk = PublicKey::::from_bytes(&b[32 + u_point_size..]).unwrap(); + Self { pk, sig } + } +} + +#[derive(Clone, Debug)] +pub struct SigPkVar>> { + pub pk: GC, + pub sig_r: GC, + pub sig_s: Vec>>, +} +impl>> Default for SigPkVar { + fn default() -> Self { + Self { + pk: GC::zero(), + sig_r: GC::zero(), + sig_s: vec![Boolean::>::FALSE; 253], // TODO 253-> fieldbitsize + } + } +} + +impl AllocVar, CF> for SigPkVar +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 e = val.borrow(); + 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, + )?; + let v = Self { pk, sig_r, sig_s }; + + Ok(v) + }) + } +} + +pub fn hash_pk( + poseidon_config: &PoseidonConfig, + pk: PublicKey, +) -> C::BaseField +where + C::BaseField: PrimeField + Absorb, +{ + let mut poseidon = PoseidonSponge::new(poseidon_config); + let (pk_x, pk_y): (C::BaseField, C::BaseField) = pk.xy().unwrap(); + poseidon.absorb(&vec![pk_x, pk_y]); + let k = poseidon.squeeze_field_elements::(1); + *k.first().unwrap() +} + +// returns a vector of signatures & publickeys, where each signature is of the previous public key +pub fn gen_signatures( + rng: &mut R, + poseidon_config: &PoseidonConfig, + steps: usize, +) -> Vec> +where + C::BaseField: PrimeField + Absorb, +{ + let mut prev_pk = None; + let mut res: Vec> = Vec::new(); + for _ in 0..steps { + let extinp = gen_sig(rng, poseidon_config, prev_pk); + res.push(extinp); + prev_pk = Some(extinp.pk); + } + res +} + +// generates a new secret key, and signs the given `prev_pk` with it. If the `prev_pk==None`, it +// will use the newly generated public key as the prev_pk. +pub fn gen_sig( + rng: &mut R, + poseidon_config: &PoseidonConfig, + prev_pk: Option>, +) -> SigPk +where + C::BaseField: PrimeField + Absorb, +{ + let sk = SigningKey::::generate::(rng).unwrap(); + let pk = sk.public_key(); + + // if prev_pk!=None, use it, else, set the new pk to it + let prev_pk = if prev_pk.is_some() { + prev_pk.unwrap() + } else { + *pk + }; + + let msg = hash_pk(poseidon_config, prev_pk); + + let sig = sk + .sign::(&poseidon_config, &msg) + .unwrap(); + pk.verify(&poseidon_config, &msg, &sig).unwrap(); + SigPk { + pk: pk.clone(), + sig, + } +}