Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/Improve frontend (rust-interface) of sonobe #96

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
members = [
"folding-schemes",
"solidity-verifiers",
"cli"
"cli",
"frontend-macro"
]
resolver = "2"

Expand Down
50 changes: 36 additions & 14 deletions examples/multi_inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::fp::FpVar;
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use core::marker::PhantomData;
use frontend_macro::Flatten;
use std::time::Instant;

use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
Expand All @@ -25,6 +26,17 @@ use utils::init_nova_ivc_params;
/// we get by applying the step.
/// In this example we set z_i and z_{i+1} to have five elements, and at each step we do different
/// operations on each of them.
///

#[derive(Flatten)]
pub struct State<F: PrimeField> {
pub a: F,
pub b: F,
pub c: F,
pub d: F,
pub e: F,
}

#[derive(Clone, Copy, Debug)]
pub struct MultiInputsFCircuit<F: PrimeField> {
_f: PhantomData<F>,
Expand All @@ -35,9 +47,11 @@ impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
fn new(_params: Self::Params) -> Result<Self, Error> {
Ok(Self { _f: PhantomData })
}

fn state_len(&self) -> usize {
5
State::<F>::state_number()
}

fn external_inputs_len(&self) -> usize {
0
}
Expand All @@ -50,13 +64,16 @@ impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
z_i: Vec<F>,
_external_inputs: Vec<F>,
) -> Result<Vec<F>, Error> {
let a = z_i[0] + F::from(4_u32);
let b = z_i[1] + F::from(40_u32);
let c = z_i[2] * F::from(4_u32);
let d = z_i[3] * F::from(40_u32);
let e = z_i[4] + F::from(100_u32);

Ok(vec![a, b, c, d, e])
let state = State::from(z_i);

let next_state = State {
a: state.a + F::from(4_u32),
b: state.b + F::from(40_u32),
c: state.c * F::from(4_u32),
d: state.d * F::from(40_u32),
e: state.e + F::from(100_u32),
};
Ok(Vec::from(next_state))
}

/// generates the constraints for the step of F for the given z_i
Expand All @@ -67,16 +84,21 @@ impl<F: PrimeField> FCircuit<F> for MultiInputsFCircuit<F> {
z_i: Vec<FpVar<F>>,
_external_inputs: Vec<FpVar<F>>,
) -> Result<Vec<FpVar<F>>, SynthesisError> {
let cs_state = State::cs_state(z_i.clone());

let four = FpVar::<F>::new_constant(cs.clone(), F::from(4u32))?;
let forty = FpVar::<F>::new_constant(cs.clone(), F::from(40u32))?;
let onehundred = FpVar::<F>::new_constant(cs.clone(), F::from(100u32))?;
let a = z_i[0].clone() + four.clone();
let b = z_i[1].clone() + forty.clone();
let c = z_i[2].clone() * four;
let d = z_i[3].clone() * forty;
let e = z_i[4].clone() + onehundred;

Ok(vec![a, b, c, d, e])
let next_cs_state = StateConstraint {
a: cs_state.a.clone() + four.clone(),
b: cs_state.b.clone() + forty.clone(),
c: cs_state.c.clone() * four,
d: cs_state.d.clone() * forty,
e: cs_state.e.clone() + onehundred,
};

Ok(Vec::from(next_cs_state))
}
}

Expand Down
1 change: 1 addition & 0 deletions folding-schemes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ ark-grumpkin = {version="0.4.0", features=["r1cs"]}
rand = "0.8.5"
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] }
tracing-subscriber = { version = "0.2" }
frontend-macro = { path = "../frontend-macro/"}

[features]
default = ["parallel"]
Expand Down
5 changes: 4 additions & 1 deletion folding-schemes/src/frontend/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ pub trait FCircuit<F: PrimeField>: Clone + Debug {

/// returns the number of elements in the external inputs used by the FCircuit. External inputs
/// are optional, and in case no external inputs are used, this method should return 0.
fn external_inputs_len(&self) -> usize;
/// default is zero
fn external_inputs_len(&self) -> usize {
0
}

/// computes the next state values in place, assigning z_{i+1} into z_i, and computing the new
/// z_{i+1}
Expand Down
21 changes: 21 additions & 0 deletions frontend-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
[package]
name = "frontend-macro"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
syn = { version = "0.15", features = ["extra-traits"] }
quote = "0.6"
proc-macro2 = "0.4"

[dev-dependencies]
trybuild = "1.0"
ark-ff = "^0.4.0"
ark-bn254 = {version="0.4.0", features=["r1cs"]}
vuvoth marked this conversation as resolved.
Show resolved Hide resolved
ark-r1cs-std = { version = "0.4.0", default-features = false } # this is patched at the workspace level


[lib]
proc-macro = true
109 changes: 109 additions & 0 deletions frontend-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};

#[proc_macro_derive(Flatten)]
pub fn derive(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let iden = &ast.ident;
let cs_iden_name = format!("{}Constraint", iden);
let cs_iden = syn::Ident::new(&cs_iden_name, iden.span());

let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();

let fields = if let syn::Data::Struct(syn::DataStruct {
fields: syn::Fields::Named(syn::FieldsNamed { ref named, .. }),
..
}) = ast.data
{
named
} else {
unimplemented!();
};

let cs_fields_to_vec = fields.iter().map(|f| {
let name = &f.ident;
quote! { value.#name }
});

let cs_fields = fields.iter().map(|f| {
let name = &f.ident;
quote! {pub #name: ark_r1cs_std::fields::fp::FpVar<F> }
});

let builder_fields = fields.iter().map(|f| {
let name = &f.ident;
quote! { value.#name }
});

let cs_vec_to_fields = fields.iter().enumerate().map(|(id, f)| {
let name = &f.ident;
quote! {#name: vec[#id].clone()}
});

let vec_to_fields = fields.iter().enumerate().map(|(id, f)| {
let name = &f.ident;
quote! {#name: vec[#id]}
});

let state_number = fields.len();

let state_macro = quote! {
impl #impl_generics #iden #ty_generics #where_clause {
pub fn state_number() -> usize {
#state_number
}
}

impl #impl_generics From<#iden #ty_generics> for Vec #ty_generics #where_clause {
fn from(value: #iden #ty_generics) -> Vec #ty_generics {
vec![#(#builder_fields,)*]
}
}

impl #impl_generics From<Vec #ty_generics> for #iden #ty_generics {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are parts of the code that seem to work with vectors, but when I try to use for example

struct State<F: PrimeField> {
    a: F,
    b: F,
    c: Vec<F>, // vector
    d: (F, F), // tuple
}

it fails with an error for both cases (vector & tuple). Is it expected?

To put an example of usage, this is the Grapevine circuit (Circom, but taking it as a real-world sample), where they use mostly vectors for the external_inputs: https://github.com/Mach-34/grapevine-sonobe/blob/556d570e807d08557d7a2a233e4637b00ab4eafa/circom/grapevine.circom#L21

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check it :D and update it soon

fn from(vec: Vec #ty_generics) -> #iden #ty_generics {
assert!(vec.len() == #iden::#ty_generics::state_number());
#iden {
#(#vec_to_fields,)*
}
}
}
};

let constraint_macro = quote! {
pub struct #cs_iden<F: ark_ff::PrimeField> {
#(#cs_fields,)*
}


impl<F: PrimeField> From<Vec<ark_r1cs_std::fields::fp::FpVar<F>>> for #cs_iden<F> {
fn from(vec: Vec<ark_r1cs_std::fields::fp::FpVar<F>>) -> #cs_iden<F>{
#cs_iden {
#(#cs_vec_to_fields,)*
}
}
}

impl<F: PrimeField> From<#cs_iden<F>> for Vec<ark_r1cs_std::fields::fp::FpVar<F>> {
fn from(value: #cs_iden<F>) -> Vec<ark_r1cs_std::fields::fp::FpVar<F>> {
vec![#(#cs_fields_to_vec,)*]
}
}


impl<F: ark_ff::PrimeField> #iden<F>{
pub fn cs_state(v: Vec< ark_r1cs_std::fields::fp::FpVar<F>>) -> #cs_iden<F> {
#cs_iden::from(v)
}
}
};

let expanded = quote! {
#state_macro

#constraint_macro
};

expanded.into()
}
21 changes: 21 additions & 0 deletions frontend-macro/tests/parse.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use ark_bn254::Fr;
use ark_ff::PrimeField;
use frontend_macro::Flatten;
#[derive(Flatten, Debug)]
struct State<F: PrimeField> {
a: F,
b: F,
}

fn main() {
let s = State::<Fr> {
a: Fr::from(1u32),
b: Fr::from(1u32),
};

let v: Vec<Fr> = Vec::from(s);

println!("{:?}", State::<Fr>::state_number());
println!("{:?}", v);
println!("{:?}", State::from(v));
Comment on lines +18 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of prints, this test can do assert_eq with the expected values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This only for compile macro. I added runable test for macro.

}
32 changes: 32 additions & 0 deletions frontend-macro/tests/runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#[cfg(test)]
mod test {

#[test]
fn try_compile() {
let t = trybuild::TestCases::new();
t.pass("tests/parse.rs");
}

#[test]
fn try_run_test() {
use ark_bn254::Fr;
use ark_ff::PrimeField;
use frontend_macro::Flatten;
#[derive(Flatten, Debug, PartialEq, Clone)]
struct State<F: PrimeField> {
a: F,
b: F,
}

let s = State::<Fr> {
a: Fr::from(1u32),
b: Fr::from(1u32),
};

let v: Vec<Fr> = Vec::from(s.clone());

assert_eq!(2, State::<Fr>::state_number());
assert_eq!(vec![s.a, s.b], v);
assert_eq!(s, State::from(v));
}
}