Skip to content

Commit

Permalink
Support R1CS (#52)
Browse files Browse the repository at this point in the history
* fix: avoid the associated type PrettyField that relies on kimchi lib.

* Add R1CS bls12_381 backend

* Apply same set of tests to r1cs backend

* Add SnarkjsExporter to generate .r1cs and .wtns files in snarkjs format

* Update noname run command to generate circuit and witness

* remove the use of option in lc

* refactor witness_var interface

* refactor witness vars in r1cs to vector
  • Loading branch information
katat authored May 7, 2024
1 parent d2ccd03 commit 1c724e1
Show file tree
Hide file tree
Showing 13 changed files with 1,618 additions and 154 deletions.
390 changes: 365 additions & 25 deletions Cargo.lock

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ description = "a programming language for writing zkapps"

[dependencies]
ark-ec = "0.3.0" # elliptic curve library
ark-ff = "0.3.0" # finite field library
ark-ff = "0.3.0"
ark-bls12-381 = "0.3.0"
ark-serialize = "0.3.0" # serialization of arkworks types
ena = "0.14.0" # union-find implementation for the wiring
num-bigint = "0.4.3" # big int library
Expand All @@ -28,3 +29,6 @@ serde_json = "1.0.85" # to (de)serialize JSON
serde = "1.0.144" # to (de)serialize objects
thiserror = "1.0.31" # helpful error traits
toml = "0.8.8" # to parse manifest files
constraint_writers = { git = "https://github.com/iden3/circom.git", tag = "v2.1.8"} # to generate r1cs file
num-bigint-dig = "0.6.0" # to adapt for circom lib
rstest = "0.19.0" # for testing different backend cases
6 changes: 3 additions & 3 deletions src/backends/kimchi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,8 @@ impl Backend for KimchiVesta {
builtin::poseidon
}

fn witness_vars(&self) -> &HashMap<usize, Value<Self>> {
&self.witness_vars
fn witness_vars(&self, var: CellVar) -> &Value<Self> {
self.witness_vars.get(&var.index).unwrap()
}

fn new_internal_var(&mut self, val: Value<KimchiVesta>, span: Span) -> CellVar {
Expand Down Expand Up @@ -370,7 +370,7 @@ impl Backend for KimchiVesta {
for (col, var) in row_of_vars.iter().enumerate() {
let val = if let Some(var) = var {
// if it's a public output, defer it's computation
if matches!(self.witness_vars()[&var.index], Value::PublicOutput(_)) {
if matches!(self.witness_vars(*var), Value::PublicOutput(_)) {
public_outputs_vars
.entry(*var)
.or_default()
Expand Down
40 changes: 27 additions & 13 deletions src/backends/mod.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
use std::collections::HashMap;
use std::{collections::HashMap, str::FromStr};

use ark_ff::{Field, Zero};
use num_bigint::BigUint;

use crate::{
circuit_writer::{DebugInfo, GateKind},
compiler::Sources,
constants::Span,
error::{Error, ErrorKind, Result},
helpers::PrettyField,
imports::FnHandle,
var::{CellVar, Value, Var},
witness::WitnessEnv,
};

use self::{kimchi::KimchiVesta, r1cs::R1csBls12_381};

pub mod kimchi;
pub mod r1cs;

pub enum BackendKind {
KimchiVesta(KimchiVesta),
R1CS(R1csBls12_381),
}

// TODO: should it be cloneable? It is now so because FnInfo needs to be cloneable.
pub trait Backend: Clone {
/// The circuit field / scalar field that the circuit is written on.
type Field: Field + PrettyField;
type Field: Field + FromStr + TryFrom<BigUint> + TryInto<BigUint> + Into<BigUint>;

/// The generated witness type for the backend. Each backend may define its own witness format to be generated.
type GeneratedWitness;

/// This provides a standard way to access to all the internal vars.
/// Different backends should be accessible in the same way by the variable index.
fn witness_vars(&self) -> &HashMap<usize, Value<Self>>;
fn witness_vars(&self, var: CellVar) -> &Value<Self>;

// TODO: as the builtins grows, we might better change this to a crypto struct that holds all the builtin function pointers.
/// poseidon crypto builtin function for different backends
Expand Down Expand Up @@ -76,17 +83,24 @@ pub trait Backend: Clone {
/// - The symbolic variables are stored in the witness_vars.
/// - The computed values are stored in the cached_values.
fn compute_var(&self, env: &mut WitnessEnv<Self::Field>, var: CellVar) -> Result<Self::Field> {
// fetch cache first
// TODO: if self was &mut, then we could use a Value::Cached(Field) to store things instead of that
if let Some(res) = env.cached_values.get(&var) {
self.compute_val(env, self.witness_vars(var), var.index)
}

fn compute_val(
&self,
env: &mut WitnessEnv<Self::Field>,
val: &Value<Self>,
var_index: usize,
) -> Result<Self::Field> {
if let Some(res) = env.cached_values.get(&var_index) {
return Ok(*res);
}

match &self.witness_vars()[&var.index] {
match &val {
Value::Hint(func) => {
let res = func(self, env)
.expect("that function doesn't return a var (type checker error)");
env.cached_values.insert(var, res);
env.cached_values.insert(var_index, res);
Ok(res)
}
Value::Constant(c) => Ok(*c),
Expand All @@ -95,20 +109,20 @@ pub trait Backend: Clone {
for (coeff, var) in lc {
res += self.compute_var(env, *var)? * *coeff;
}
env.cached_values.insert(var, res); // cache
env.cached_values.insert(var_index, res); // cache
Ok(res)
}
Value::Mul(lhs, rhs) => {
let lhs = self.compute_var(env, *lhs)?;
let rhs = self.compute_var(env, *rhs)?;
let res = lhs * rhs;
env.cached_values.insert(var, res); // cache
env.cached_values.insert(var_index, res); // cache
Ok(res)
}
Value::Inverse(v) => {
let v = self.compute_var(env, *v)?;
let res = v.inverse().unwrap_or_else(Self::Field::zero);
env.cached_values.insert(var, res); // cache
env.cached_values.insert(var_index, res); // cache
Ok(res)
}
Value::External(name, idx) => Ok(env.get_external(name)[*idx]),
Expand Down
20 changes: 20 additions & 0 deletions src/backends/r1cs/builtin.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
use ark_bls12_381::Fr;

use crate::{
circuit_writer::{CircuitWriter, VarInfo},
constants::Span,
error::Result,
var::Var,
};

use super::R1csBls12_381;

// todo: impl this
pub fn poseidon(
compiler: &mut CircuitWriter<R1csBls12_381>,
vars: &[VarInfo<Fr>],
span: Span,
) -> Result<Option<Var<Fr>>> {
// dummy for now
unimplemented!()
}
Loading

0 comments on commit 1c724e1

Please sign in to comment.