* feat: draft traits `FoldingScheme` and `Decider` Co-authored-by: arnaucube <root@arnaucube.com> * Add Transcript trait, with PoseidonTranscript impl (#1) Add also the PoseidonTranscriptVar (gadget). * Update FoldingScheme trait to take C1 & C2 as params (#2) * Update FoldingScheme trait to take C1 & C2 as params Update FoldingScheme trait to take C1 & C2 as params which are used by the diverse folding schemes as a cycle of curves. * Add constraint to FoldingScheme C1,C2 fields swap. Co-authored-by: Han <tinghan0110@gmail.com> --------- Co-authored-by: Han <tinghan0110@gmail.com> * move transcript to it's own mod --------- Co-authored-by: han0110 <tinghan0110@gmail.com>main
@ -0,0 +1,2 @@ |
|||||
|
/target |
||||
|
Cargo.lock |
@ -0,0 +1,17 @@ |
|||||
|
[package] |
||||
|
name = "folding-schemes" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
[dependencies] |
||||
|
ark-ec = "0.4.2" |
||||
|
ark-ff = "0.4.2" |
||||
|
ark-std = "0.4.0" |
||||
|
ark-poly = "0.4.0" |
||||
|
ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = [ "r1cs", "sponge" ] } |
||||
|
ark-relations = { version = "^0.4.0", default-features = false } |
||||
|
ark-r1cs-std = { version = "^0.4.0", default-features = false } |
||||
|
thiserror = "1.0" |
||||
|
|
||||
|
[dev-dependencies] |
||||
|
ark-bls12-381 = "0.4.0" |
@ -0,0 +1,85 @@ |
|||||
|
#![allow(non_snake_case)]
|
||||
|
#![allow(non_upper_case_globals)]
|
||||
|
|
||||
|
use ark_ec::CurveGroup;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_std::{fmt::Debug, rand::RngCore};
|
||||
|
use thiserror::Error;
|
||||
|
|
||||
|
pub mod transcript;
|
||||
|
use transcript::Transcript;
|
||||
|
|
||||
|
#[derive(Debug, Error)]
|
||||
|
pub enum Error {
|
||||
|
#[error("Relation not satisfied")]
|
||||
|
NotSatisfied,
|
||||
|
}
|
||||
|
|
||||
|
/// FoldingScheme defines trait that is implemented by the diverse folding schemes. It is defined
|
||||
|
/// over a cycle of curves (C1, C2), where:
|
||||
|
/// - C1 is the main curve, which ScalarField we use as our F for al the field operations
|
||||
|
/// - C2 is the auxiliary curve, which we use for the commitments, whose BaseField (for point
|
||||
|
/// coordinates) are in the C1::ScalarField
|
||||
|
pub trait FoldingScheme<C1: CurveGroup, C2: CurveGroup>: Clone + Debug
|
||||
|
where
|
||||
|
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
|
||||
|
C2::BaseField: PrimeField,
|
||||
|
{
|
||||
|
// type PCS: PolynomialCommitmentScheme<C>; // maybe not needed, just PedersenCommitment
|
||||
|
type PreprocessorParam: Debug;
|
||||
|
type ProverParam: Debug;
|
||||
|
type VerifierParam: Debug;
|
||||
|
type FreshInstance: Debug;
|
||||
|
type PublicInput: Debug;
|
||||
|
type CommittedInstanceWithWitness: Debug;
|
||||
|
type CommittedInstance: Clone + Debug;
|
||||
|
|
||||
|
fn preprocess(
|
||||
|
// pcs_param: &<Self::CS as PolynomialCommitmentScheme<C>>::Param,
|
||||
|
prep_param: &Self::PreprocessorParam,
|
||||
|
) -> Result<(Self::ProverParam, Self::VerifierParam), Error>;
|
||||
|
|
||||
|
fn init_accumulator(
|
||||
|
pp: &Self::ProverParam,
|
||||
|
) -> Result<Self::CommittedInstanceWithWitness, Error>;
|
||||
|
|
||||
|
fn prove(
|
||||
|
pp: &Self::ProverParam,
|
||||
|
running_instance: &mut Self::CommittedInstanceWithWitness,
|
||||
|
incomming_instances: &[Self::FreshInstance],
|
||||
|
transcript: &mut impl Transcript<C1::ScalarField>,
|
||||
|
rng: impl RngCore,
|
||||
|
) -> Result<(), Error>;
|
||||
|
|
||||
|
fn verify(
|
||||
|
vp: &Self::VerifierParam,
|
||||
|
running_instance: &mut Self::CommittedInstance,
|
||||
|
incomming_instances: &[Self::PublicInput],
|
||||
|
transcript: &mut impl Transcript<C1::ScalarField>,
|
||||
|
rng: impl RngCore,
|
||||
|
) -> Result<(), Error>;
|
||||
|
}
|
||||
|
|
||||
|
pub trait Decider<C: CurveGroup>: Clone + Debug {
|
||||
|
type PreprocessorParam: Debug;
|
||||
|
type ProverParam: Debug;
|
||||
|
type VerifierParam: Debug;
|
||||
|
type FreshInstance: Debug;
|
||||
|
type PublicInput: Debug;
|
||||
|
type CommittedInstanceWithWitness: Debug;
|
||||
|
type CommittedInstance: Clone + Debug;
|
||||
|
|
||||
|
fn prove(
|
||||
|
pp: &Self::ProverParam,
|
||||
|
running_instance: &Self::CommittedInstanceWithWitness,
|
||||
|
transcript: &mut impl Transcript<C::ScalarField>,
|
||||
|
rng: impl RngCore,
|
||||
|
) -> Result<(), Error>;
|
||||
|
|
||||
|
fn verify(
|
||||
|
vp: &Self::VerifierParam,
|
||||
|
running_instance: &Self::CommittedInstance,
|
||||
|
transcript: &mut impl Transcript<C::ScalarField>,
|
||||
|
rng: impl RngCore,
|
||||
|
) -> Result<(), Error>;
|
||||
|
}
|
@ -0,0 +1,14 @@ |
|||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_std::fmt::Debug;
|
||||
|
|
||||
|
pub mod poseidon;
|
||||
|
|
||||
|
pub trait Transcript<F: PrimeField> {
|
||||
|
type TranscriptConfig: Debug;
|
||||
|
|
||||
|
fn new(config: &Self::TranscriptConfig) -> Self;
|
||||
|
fn absorb(&mut self, v: &F);
|
||||
|
fn absorb_vec(&mut self, v: &[F]);
|
||||
|
fn get_challenge(&mut self) -> F;
|
||||
|
fn get_challenges(&mut self, n: usize) -> Vec<F>;
|
||||
|
}
|
@ -0,0 +1,122 @@ |
|||||
|
use ark_crypto_primitives::sponge::{
|
||||
|
constraints::CryptographicSpongeVar,
|
||||
|
poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge},
|
||||
|
Absorb, CryptographicSponge,
|
||||
|
};
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_r1cs_std::fields::fp::FpVar;
|
||||
|
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
|
||||
|
|
||||
|
use crate::transcript::Transcript;
|
||||
|
|
||||
|
/// PoseidonTranscript implements the Transcript trait using the Poseidon hash
|
||||
|
pub struct PoseidonTranscript<F: PrimeField + Absorb> {
|
||||
|
sponge: PoseidonSponge<F>,
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField + Absorb> Transcript<F> for PoseidonTranscript<F> {
|
||||
|
type TranscriptConfig = PoseidonConfig<F>;
|
||||
|
|
||||
|
fn new(poseidon_config: &Self::TranscriptConfig) -> Self {
|
||||
|
let sponge = PoseidonSponge::<F>::new(poseidon_config);
|
||||
|
Self { sponge }
|
||||
|
}
|
||||
|
fn absorb(&mut self, v: &F) {
|
||||
|
self.sponge.absorb(&v);
|
||||
|
}
|
||||
|
fn absorb_vec(&mut self, v: &[F]) {
|
||||
|
self.sponge.absorb(&v);
|
||||
|
}
|
||||
|
fn get_challenge(&mut self) -> F {
|
||||
|
let c = self.sponge.squeeze_field_elements(1);
|
||||
|
self.sponge.absorb(&c[0]);
|
||||
|
c[0]
|
||||
|
}
|
||||
|
fn get_challenges(&mut self, n: usize) -> Vec<F> {
|
||||
|
let c = self.sponge.squeeze_field_elements(n);
|
||||
|
self.sponge.absorb(&c);
|
||||
|
c
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
/// PoseidonTranscriptVar implements the gadget compatible with PoseidonTranscript
|
||||
|
pub struct PoseidonTranscriptVar<F: PrimeField> {
|
||||
|
sponge: PoseidonSpongeVar<F>,
|
||||
|
}
|
||||
|
impl<F: PrimeField> PoseidonTranscriptVar<F> {
|
||||
|
pub fn new(cs: ConstraintSystemRef<F>, poseidon_config: &PoseidonConfig<F>) -> Self {
|
||||
|
let sponge = PoseidonSpongeVar::<F>::new(cs, poseidon_config);
|
||||
|
Self { sponge }
|
||||
|
}
|
||||
|
pub fn absorb(&mut self, v: FpVar<F>) -> Result<(), SynthesisError> {
|
||||
|
self.sponge.absorb(&v)
|
||||
|
}
|
||||
|
pub fn absorb_vec(&mut self, v: &[FpVar<F>]) -> Result<(), SynthesisError> {
|
||||
|
self.sponge.absorb(&v)
|
||||
|
}
|
||||
|
pub fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError> {
|
||||
|
let c = self.sponge.squeeze_field_elements(1)?;
|
||||
|
self.sponge.absorb(&c[0])?;
|
||||
|
Ok(c[0].clone())
|
||||
|
}
|
||||
|
pub fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||
|
let c = self.sponge.squeeze_field_elements(n)?;
|
||||
|
self.sponge.absorb(&c)?;
|
||||
|
Ok(c)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod tests {
|
||||
|
use super::*;
|
||||
|
use ark_bls12_381::Fr;
|
||||
|
use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds;
|
||||
|
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
|
||||
|
use ark_relations::r1cs::ConstraintSystem;
|
||||
|
|
||||
|
/// WARNING the method poseidon_test_config is for tests only
|
||||
|
#[cfg(test)]
|
||||
|
pub fn poseidon_test_config<F: PrimeField>() -> PoseidonConfig<F> {
|
||||
|
let full_rounds = 8;
|
||||
|
let partial_rounds = 31;
|
||||
|
let alpha = 5;
|
||||
|
let rate = 2;
|
||||
|
|
||||
|
let (ark, mds) = find_poseidon_ark_and_mds::<F>(
|
||||
|
F::MODULUS_BIT_SIZE as u64,
|
||||
|
rate,
|
||||
|
full_rounds,
|
||||
|
partial_rounds,
|
||||
|
0,
|
||||
|
);
|
||||
|
|
||||
|
PoseidonConfig::new(
|
||||
|
full_rounds as usize,
|
||||
|
partial_rounds as usize,
|
||||
|
alpha,
|
||||
|
mds,
|
||||
|
ark,
|
||||
|
rate,
|
||||
|
1,
|
||||
|
)
|
||||
|
}
|
||||
|
|
||||
|
#[test]
|
||||
|
fn test_transcript_and_transcriptvar() {
|
||||
|
// use 'native' transcript
|
||||
|
let config = poseidon_test_config::<Fr>();
|
||||
|
let mut tr = PoseidonTranscript::<Fr>::new(&config);
|
||||
|
tr.absorb(&Fr::from(42_u32));
|
||||
|
let c = tr.get_challenge();
|
||||
|
|
||||
|
// use 'gadget' transcript
|
||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||
|
let mut tr_var = PoseidonTranscriptVar::<Fr>::new(cs.clone(), &config);
|
||||
|
let v = FpVar::<Fr>::new_witness(cs.clone(), || Ok(Fr::from(42_u32))).unwrap();
|
||||
|
tr_var.absorb(v).unwrap();
|
||||
|
let c_var = tr_var.get_challenge().unwrap();
|
||||
|
|
||||
|
// assert that native & gadget transcripts return the same challenge
|
||||
|
assert_eq!(c, c_var.value().unwrap());
|
||||
|
}
|
||||
|
}
|