Skip to content

Commit

Permalink
groth16-gpu (#263)
Browse files Browse the repository at this point in the history
* chore: add gpu support for groth16

* chore: add gpu support for groth16

* chore: add gpu support for groth16

* chore: add gpu support for groth16

* chore: add gpu support for groth16

* chore: cargo fmt

* chore: remove unused file

* chore: change some content

* chore: change some content
  • Loading branch information
ibmp33 authored Jun 30, 2024
1 parent f8da56b commit 57e81bb
Show file tree
Hide file tree
Showing 27 changed files with 2,121 additions and 61 deletions.
53 changes: 53 additions & 0 deletions algebraic-gpu/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
[package]
name = "algebraic-gpu"
version = "0.0.1"
edition = "2021"
license = "Apache-2.0"
description = "Eigen Algebraic based on R1CS"
documentation = "eigen.market"
homepage = "eigen.market"

[lib]
crate-type = ["cdylib", "rlib"]

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

[dependencies]
hex-literal = "0.2.1"
itertools = "0.8.1"
log = "0.4.11"
num-bigint = "0.3.3"
num-traits = "0.2.8"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["arbitrary_precision"] }
hex = "*"
wasmer = { version = "4.3.2", default-features = false }
thiserror = "1.0"
anyhow = "1.0.79"
fnv = { version = "1.0.3", default-features = false }
num = { version = "0.4.0" }
byteorder = "1"

ff = { version = "0.13.0", features = ["derive"] }
pairing = "0.23.0"
bellperson = { version = "0.26", default-features = false, features = ["groth16"] }

[dev-dependencies]
env_logger = "0.10"

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
rand = "0.4"

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = { version = "0.2.51", features = ["serde-serialize"] }
wasm-bindgen-futures = "0.4.1"
rand = { version="0.6.5", features = ["wasm-bindgen"] }

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3"

[features]
default = ["bellperson/default", "wasmer/singlepass"]
wasm = ["wasmer/js-default"]
cuda = ["bellperson/cuda", "wasmer/singlepass"]
opencl = ["bellperson/opencl", "wasmer/singlepass"]
169 changes: 169 additions & 0 deletions algebraic-gpu/src/circom_circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
// copy from https://github.com/poma/zkutil/blob/master/src/circom_circuit.rs partially
#![allow(clippy::needless_range_loop)]
extern crate rand;

use itertools::Itertools;
use std::collections::BTreeMap;
use std::str;

use crate::bellperson::{
Circuit, ConstraintSystem, Index, LinearCombination, SynthesisError, Variable,
};
use ff::PrimeField;
pub use num_bigint::BigUint;
use num_traits::Num;

pub fn repr_to_big<T: std::fmt::Debug>(r: T) -> String {
let hex_str = format!("{:?}", r);
let trim_quotes = hex_str
.trim_start_matches("Scalar(0x")
.trim_end_matches(')');
let clean_hex = trim_quotes.trim_matches('"').trim_start_matches("0x");
BigUint::from_str_radix(clean_hex, 16)
.map(|bigint: BigUint| bigint.to_str_radix(10))
.unwrap()
}

#[derive(Serialize, Deserialize)]
pub struct CircuitJson {
pub constraints: Vec<Vec<BTreeMap<String, String>>>,
#[serde(rename = "nPubInputs")]
pub num_inputs: usize,
#[serde(rename = "nOutputs")]
pub num_outputs: usize,
#[serde(rename = "nVars")]
pub num_variables: usize,
}

pub type Constraint<E> = (Vec<(usize, E)>, Vec<(usize, E)>, Vec<(usize, E)>);

// R1CSfile's CustomGates
#[derive(Debug, Default, Clone)]
pub struct CustomGates<E: PrimeField> {
pub template_name: String,
pub parameters: Vec<E>,
}

// R1CSfile's CustomGatesUses
#[derive(Debug, Default, Clone)]
pub struct CustomGatesUses {
pub id: u64,
pub signals: Vec<u64>,
}

/// R1CS spec: https://www.sikoba.com/docs/SKOR_GD_R1CS_Format.pdf
#[derive(Clone, Debug)]
pub struct R1CS<E: PrimeField> {
pub num_inputs: usize,
pub num_aux: usize,
pub num_variables: usize,
pub num_outputs: usize,
pub constraints: Vec<Constraint<E>>,
pub custom_gates: Vec<CustomGates<E>>,
pub custom_gates_uses: Vec<CustomGatesUses>,
}

#[derive(Clone, Debug)]
pub struct CircomCircuit<E: PrimeField> {
pub r1cs: R1CS<E>,
pub witness: Option<Vec<E>>,
pub wire_mapping: Option<Vec<usize>>,
pub aux_offset: usize,
// debug symbols
}

impl<E: PrimeField> CircomCircuit<E> {
pub fn get_public_inputs(&self) -> Option<Vec<E>> {
match &self.witness {
None => None,
Some(w) => match &self.wire_mapping {
None => Some(w[1..self.r1cs.num_inputs].to_vec()),
Some(m) => Some(
m[1..self.r1cs.num_inputs]
.iter()
.map(|i| w[*i])
.collect_vec(),
),
},
}
}

pub fn get_public_inputs_json(&self) -> String {
let inputs = self.get_public_inputs();
let inputs = match inputs {
None => return String::from("[]"),
Some(inp) => inp.iter().map(repr_to_big).collect_vec(),
};
serde_json::to_string_pretty(&inputs).unwrap()
}
}

/// Our demo circuit implements this `Circuit` trait which
/// is used during paramgen and proving in order to
/// synthesize the constraint system.
impl<E: PrimeField> Circuit<E> for CircomCircuit<E> {
//noinspection RsBorrowChecker
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
let witness = &self.witness;
let wire_mapping = &self.wire_mapping;
for i in 1..self.r1cs.num_inputs {
cs.alloc_input(
|| format!("variable {}", i),
|| {
Ok(match witness {
None => E::from_str_vartime(&format!("alloc input {} error", i)).unwrap(),
Some(w) => match wire_mapping {
None => w[i],
Some(m) => w[m[i]],
},
})
},
)?;
}
for i in 0..self.r1cs.num_aux {
cs.alloc(
|| format!("aux {}", i + self.aux_offset),
|| {
Ok(match witness {
None => {
E::from_str_vartime(&format!("alloc aux {} error", i + self.aux_offset))
.unwrap()
}
Some(w) => match wire_mapping {
None => w[i + self.r1cs.num_inputs],
Some(m) => w[m[i + self.r1cs.num_inputs]],
},
})
},
)?;
}

let make_index = |index| {
if index < self.r1cs.num_inputs {
Index::Input(index)
} else {
Index::Aux(index - self.r1cs.num_inputs + self.aux_offset)
}
};
let make_lc = |lc_data: Vec<(usize, E)>| {
lc_data.iter().fold(
LinearCombination::<E>::zero(),
|lc: LinearCombination<E>, (index, coeff)| {
lc + (*coeff, Variable::new_unchecked(make_index(*index)))
},
)
};
for (i, constraint) in self.r1cs.constraints.iter().enumerate() {
// 0 * LC = 0 must be ignored
if !((constraint.0.is_empty() || constraint.1.is_empty()) && constraint.2.is_empty()) {
cs.enforce(
|| format!("{}", i),
|_| make_lc(constraint.0.clone()),
|_| make_lc(constraint.1.clone()),
|_| make_lc(constraint.2.clone()),
);
}
}
Ok(())
}
}
58 changes: 58 additions & 0 deletions algebraic-gpu/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#![allow(clippy::unit_arg)]

#[macro_use]
extern crate serde;
#[macro_use]
extern crate hex_literal;
extern crate bellperson;
extern crate byteorder;
extern crate ff;
extern crate itertools;
extern crate num_bigint;
extern crate num_traits;
extern crate pairing;
extern crate rand;

pub mod circom_circuit;
pub mod r1cs_file;
pub mod reader;
pub mod witness;

pub use bellperson::groth16::*;
pub use ff::*;

#[cfg(target_arch = "wasm32")]
extern crate wasm_bindgen;

#[cfg(all(test, target_arch = "wasm32"))]
extern crate wasm_bindgen_test;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;

#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);

// The `console.log` is quite polymorphic, so we can bind it with multiple
// signatures. Note that we need to use `js_name` to ensure we always call
// `log` in JS.
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_u32(a: u32);

// Multiple arguments too!
#[wasm_bindgen(js_namespace = console, js_name = log)]
fn log_many(a: &str, b: &str);
}

#[cfg(target_arch = "wasm32")]
#[macro_export]
macro_rules! console_log {
// Note that this is using the `log` function imported above during
// `bare_bones`
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
Loading

0 comments on commit 57e81bb

Please sign in to comment.