1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

fix conflicts

This commit is contained in:
schaeff 2022-07-04 19:50:12 +02:00
parent 80f4aa4939
commit 8f5e4d9985
47 changed files with 1707 additions and 498 deletions

View file

@ -84,6 +84,7 @@ jobs:
deploy:
docker:
- image: cimg/python:3.8-node
resource_class: large
steps:
- checkout
- setup_remote_docker:

7
.gitignore vendored
View file

@ -14,6 +14,13 @@ proof.json
universal_setup.dat
witness
# ZoKrates source files at the root of the repository
/*.zok
# snarkjs artifacts
*.wtns
*.r1cs
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock
# Cargo.lock

View file

@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
https://github.com/Zokrates/ZoKrates/compare/latest...develop
## [0.7.14] - 2022-05-31
### Release
- https://github.com/Zokrates/ZoKrates/releases/tag/0.7.14 <!-- markdown-link-check-disable-line -->
### Changes
- Add curve and scheme to the verification key and proof, detect these parameters on `verify`, `print-proof`, and `export-verifier` (#1152, @schaeff)
- Fix `one_liner.sh` script not resolving latest github tag (#1146, @dark64)
- Fix instructions to build from source (#1141, @schaeff)
- Fix keccak padding issue, allow arbitrary input size (#1139, @dark64)
- Fix tuple assignment when rhs is a conditional (#1138, @dark64)
## [0.7.13] - 2022-04-18
### Release

540
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@ members = [
"zokrates_bellman",
"zokrates_proof_systems",
"zokrates_js",
"zokrates_circom"
]
exclude = []

View file

@ -26,7 +26,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y \
&& rustup toolchain install $RUST_VERSION --allow-downgrade --profile minimal --component rustfmt clippy \
&& curl -sL https://deb.nodesource.com/setup_14.x | bash - && apt-get install -y nodejs && npm i -g solc \
&& curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs && npm i -g solc \
&& curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh \
&& curl -sL https://raw.githubusercontent.com/Sarcasm/run-clang-format/master/run-clang-format.py > /opt/run-clang-format.py \
&& chmod +x /opt/run-clang-format.py \

View file

@ -1 +0,0 @@
Fix tuple assignment when rhs is a conditional

View file

@ -1 +0,0 @@
Fix keccak padding issue, allow arbitrary input size

View file

@ -1 +0,0 @@
Fix instructions to build from source

View file

@ -0,0 +1 @@
Optionally export snarkjs artifacts

View file

@ -1 +0,0 @@
Fix `one_liner.sh` script not resolving latest github tag

View file

@ -1 +0,0 @@
Add curve and scheme to the verification key and proof, detect these parameters on `verify`, `print-proof`, and `export-verifier`

View file

@ -0,0 +1 @@
Fix constant inlining for tuples

View file

@ -2,6 +2,6 @@
apt-get update -y
apt-get install -y curl gnupg sudo build-essential git
curl -sL https://deb.nodesource.com/setup_12.x | bash -
curl -sL https://deb.nodesource.com/setup_16.x | bash -
apt-get install -y nodejs
npm i -g solc

View file

@ -72,15 +72,15 @@ fn ark_combination<T: Field + ArkFieldExtensions>(
.fold(LinearCombination::zero(), |acc, e| acc + e)
}
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn public_inputs_values(&self) -> Vec<<T::ArkEngine as PairingEngine>::Fr> {
self.program
.public_inputs(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_ark())
.collect()
}
}
// impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
// pub fn public_inputs_values(&self) -> Vec<<T::ArkEngine as PairingEngine>::Fr> {
// self.program
// .public_inputs(self.witness.as_ref().unwrap())
// .iter()
// .map(|v| v.clone().into_ark())
// .collect()
// }
// }
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
ConstraintSynthesizer<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>
@ -153,6 +153,28 @@ impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
}
}
impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>> Computation<T, I> {
pub fn public_inputs_values(&self) -> Vec<<T::ArkEngine as PairingEngine>::Fr> {
self.program
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_ark())
.collect()
}
}
// impl<T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<T>>>
// ConstraintSynthesizer<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>
// for Computation<T, I>
// {
// fn generate_constraints(
// self,
// cs: ConstraintSystemRef<<<T as ArkFieldExtensions>::ArkEngine as PairingEngine>::Fr>,
// ) -> Result<(), SynthesisError> {
// self.program.generate_constraints(cs, self.witness)
// }
// }
mod parse {
use super::*;
use ark_ff::ToBytes;

View file

@ -1,4 +1,5 @@
use crate::common::{Parameter, RuntimeError, Solver, Variable};
use crate::flat::{flat_expression_from_bits, flat_expression_from_variable_summands};
use crate::flat::{FlatDirective, FlatExpression, FlatFunctionIterator, FlatStatement};
use crate::typed::types::{
ConcreteGenericsAssignment, DeclarationConstant, DeclarationSignature, DeclarationType,
@ -308,29 +309,6 @@ impl FlatEmbed {
}
}
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
// we build a binary tree of additions by splitting the vector recursively
#[cfg(any(feature = "ark", feature = "bellman"))]
fn flat_expression_from_vec<T: Field>(v: &[(usize, T)]) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (key, val) = v[0].clone();
FlatExpression::Mult(
box FlatExpression::Number(val),
box FlatExpression::Identifier(Variable::new(key)),
)
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_vec::<T>(u),
box flat_expression_from_vec::<T>(v),
)
}
}
}
/// Returns a flat function which computes a sha256 round
///
/// # Remarks
@ -400,9 +378,9 @@ pub fn sha256_round<T: Field>(
// insert flattened statements to represent constraints
let constraint_statements = r1cs.constraints.into_iter().map(|c| {
let c = from_bellman::<T, Bn256>(c);
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
let rhs_a = flat_expression_from_variable_summands::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_variable_summands::<T>(c.b.as_slice());
let lhs = flat_expression_from_variable_summands::<T>(c.c.as_slice());
FlatStatement::Condition(
lhs,
@ -508,9 +486,9 @@ pub fn snark_verify_bls12_377<T: Field>(
.into_iter()
.map(|c| {
let c = from_ark::<T, Bls12_377>(c);
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
let rhs_a = flat_expression_from_variable_summands::<T>(c.a.as_slice());
let rhs_b = flat_expression_from_variable_summands::<T>(c.b.as_slice());
let lhs = flat_expression_from_variable_summands::<T>(c.c.as_slice());
FlatStatement::Condition(
lhs,
@ -614,17 +592,11 @@ pub fn unpack_to_bitwidth<T: Field>(
.collect();
// sum check: o253 + o252 * 2 + ... + o{253 - (bit_width - 1)} * 2**(bit_width - 1)
let mut lhs_sum = FlatExpression::Number(T::from(0));
for i in 0..bit_width {
lhs_sum = FlatExpression::Add(
box lhs_sum,
box FlatExpression::Mult(
box FlatExpression::Identifier(Variable::new(bit_width - i)),
box FlatExpression::Number(T::from(2).pow(i)),
),
);
}
let lhs_sum = flat_expression_from_bits(
(0..bit_width)
.map(|i| FlatExpression::Identifier(Variable::new(i + 1)))
.collect(),
);
statements.push(FlatStatement::Condition(
lhs_sum,

View file

@ -8,7 +8,7 @@ use std::fmt;
// id < 0 for public outputs
#[derive(Serialize, Deserialize, Clone, PartialEq, Hash, Eq, Ord, PartialOrd, Copy)]
pub struct Variable {
id: isize,
pub id: isize,
}
impl Variable {

View file

@ -6,11 +6,17 @@
//! @date 2017
pub mod folder;
pub mod utils;
pub use crate::common::Parameter;
pub use crate::common::RuntimeError;
pub use crate::common::Variable;
pub use utils::{
flat_expression_from_bits, flat_expression_from_expression_summands,
flat_expression_from_variable_summands,
};
use crate::common::Solver;
use std::collections::HashMap;
use std::fmt;

View file

@ -0,0 +1,53 @@
use crate::flat::{FlatExpression, Variable};
use zokrates_field::Field;
// util to convert a vector of `(coefficient, expression)` to a flat_expression
// we build a binary tree of additions by splitting the vector recursively
pub fn flat_expression_from_expression_summands<T: Field, U: Clone + Into<FlatExpression<T>>>(
v: &[(T, U)],
) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (val, var) = v[0].clone();
FlatExpression::Mult(box FlatExpression::Number(val), box var.into())
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_expression_summands(u),
box flat_expression_from_expression_summands(v),
)
}
}
}
pub fn flat_expression_from_bits<T: Field>(v: Vec<FlatExpression<T>>) -> FlatExpression<T> {
flat_expression_from_expression_summands(
&v.into_iter()
.rev()
.enumerate()
.map(|(index, var)| (T::from(2).pow(index), var))
.collect::<Vec<_>>(),
)
}
pub fn flat_expression_from_variable_summands<T: Field>(v: &[(T, usize)]) -> FlatExpression<T> {
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (val, var) = v[0].clone();
FlatExpression::Mult(
box FlatExpression::Number(val),
box FlatExpression::Identifier(Variable::new(var)),
)
}
n => {
let (u, v) = v.split_at(n / 2);
FlatExpression::Add(
box flat_expression_from_variable_summands(u),
box flat_expression_from_variable_summands(v),
)
}
}
}

View file

@ -1,5 +1,6 @@
//use crate::solvers::Solver;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::fmt;
use std::hash::Hash;
use zokrates_field::Field;
@ -29,6 +30,8 @@ pub enum Statement<T> {
Directive(Directive<T>),
}
pub type PublicInputs = BTreeSet<Variable>;
impl<T: Field> Statement<T> {
pub fn definition<U: Into<QuadComb<T>>>(v: Variable, e: U) -> Self {
Statement::Constraint(e.into(), v.into(), None)
@ -108,10 +111,18 @@ impl<T, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn public_count(&self) -> usize {
self.arguments.iter().filter(|a| !a.private).count() + self.return_count
}
pub fn public_inputs(&self) -> PublicInputs {
self.arguments
.iter()
.filter(|a| !a.private)
.map(|a| a.id)
.collect()
}
}
impl<T: Field, I: IntoIterator<Item = Statement<T>>> ProgIterator<T, I> {
pub fn public_inputs(&self, witness: &Witness<T>) -> Vec<T> {
pub fn public_inputs_values(&self, witness: &Witness<T>) -> Vec<T> {
self.arguments
.iter()
.filter(|p| !p.private)

View file

@ -20,11 +20,11 @@ impl<T: Field> fmt::Display for SMTLib2Display<'_, T> {
}
}
struct FlatVariableCollector {
struct VariableCollector {
variables: BTreeSet<Variable>,
}
impl<T: Field> Visitor<T> for FlatVariableCollector {
impl<T: Field> Visitor<T> for VariableCollector {
fn visit_variable(&mut self, v: &Variable) {
self.variables.insert(*v);
}
@ -32,7 +32,7 @@ impl<T: Field> Visitor<T> for FlatVariableCollector {
impl<T: Field> SMTLib2 for Prog<T> {
fn to_smtlib2(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut collector = FlatVariableCollector {
let mut collector = VariableCollector {
variables: BTreeSet::<Variable>::new(),
};
collector.visit_module(self);

View file

@ -180,7 +180,7 @@ impl<T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<T>>> Co
pub fn public_inputs_values(&self) -> Vec<<T::BellmanEngine as ScalarEngine>::Fr> {
self.program
.public_inputs(self.witness.as_ref().unwrap())
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_bellman())
.collect()

View file

@ -0,0 +1,17 @@
[package]
name = "zokrates_circom"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_field = { version = "0.5.0", path = "../zokrates_field", default-features = false }
byteorder = "1.4.3"
[dev-dependencies]
pretty_assertions = "1.2.1"
zkutil = "0.5.0"
bellman_ce = { version = "^0.3" }

View file

@ -0,0 +1,90 @@
mod r1cs;
mod witness;
pub use r1cs::write_r1cs;
pub use witness::write_witness;
#[cfg(test)]
mod tests {
use std::io::Cursor;
use bellman_ce::pairing::bn256::Bn256;
use zkutil::circom_circuit::{
create_rng, generate_random_parameters, prove, r1cs_from_bin, witness_from_bin,
CircomCircuit,
};
use zokrates_ast::{
flat::{Parameter, Variable},
ir::{LinComb, Prog, PublicInputs, QuadComb, Statement, Witness},
};
use zokrates_field::Bn128Field;
use super::*;
#[test]
fn setup_and_prove() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![
Parameter::private(Variable::new(0)),
Parameter::public(Variable::new(1)),
],
return_count: 1,
statements: vec![
Statement::Constraint(
QuadComb::from_linear_combinations(
LinComb::from(Variable::new(0)),
LinComb::from(Variable::new(0)),
),
LinComb::from(Variable::new(0)),
None,
),
Statement::Constraint(
(LinComb::from(Variable::new(0)) + LinComb::from(Variable::new(1))).into(),
Variable::public(0).into(),
None,
),
],
};
let mut r1cs = vec![];
write_r1cs(&mut r1cs, prog).unwrap();
let public_inputs: PublicInputs = vec![Variable::new(1)].into_iter().collect();
let witness: Witness<Bn128Field> = Witness(
vec![
(Variable::new(0), Bn128Field::from(1u32)),
(Variable::new(1), Bn128Field::from(1u32)),
(Variable::one(), Bn128Field::from(1u32)),
(Variable::public(0), Bn128Field::from(2u32)),
]
.into_iter()
.collect(),
);
let mut wtns = vec![];
write_witness(&mut wtns, witness, public_inputs).unwrap();
let (r1cs, mapping) = r1cs_from_bin(Cursor::new(r1cs)).unwrap();
let wtns = witness_from_bin::<Bn256, _>(Cursor::new(wtns)).unwrap();
let rng = create_rng();
let circuit = CircomCircuit {
r1cs: r1cs.clone(),
witness: None,
wire_mapping: None,
};
let params = generate_random_parameters(circuit, rng).unwrap();
let circuit = CircomCircuit {
r1cs,
witness: Some(wtns),
wire_mapping: Some(mapping),
};
let rng = create_rng();
assert!(prove(circuit.clone(), &params, rng).is_ok());
}
}

416
zokrates_circom/src/r1cs.rs Normal file
View file

@ -0,0 +1,416 @@
use byteorder::{LittleEndian, WriteBytesExt};
use std::collections::HashMap;
use std::io::Result;
use std::{io::Write, ops::Add};
use zokrates_ast::flat::Variable;
use zokrates_ast::ir::{Prog, Statement};
use zokrates_field::Field;
struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub n_wires: u32,
pub n_pub_out: u32,
pub n_pub_in: u32,
pub n_prv_in: u32,
pub n_labels: u64,
pub n_constraints: u32,
}
type LinComb<T> = Vec<(usize, T)>;
type Constraint<T> = (LinComb<T>, LinComb<T>, LinComb<T>);
fn write_header<W: Write>(writer: &mut W, header: Header) -> Result<()> {
writer.write_u32::<LittleEndian>(header.field_size)?;
writer.write_all(&header.prime_size)?;
writer.write_u32::<LittleEndian>(header.n_wires)?;
writer.write_u32::<LittleEndian>(header.n_pub_out)?;
writer.write_u32::<LittleEndian>(header.n_pub_in)?;
writer.write_u32::<LittleEndian>(header.n_prv_in)?;
writer.write_u64::<LittleEndian>(header.n_labels)?;
writer.write_u32::<LittleEndian>(header.n_constraints)?;
Ok(())
}
/// Returns the index of `var` in `variables`, adding `var` with incremented index if it does not yet exists.
///
/// # Arguments
///
/// * `variables` - A mutual map that maps all existing variables to their index.
/// * `var` - Variable to be searched for.
pub fn provide_variable_idx(variables: &mut HashMap<Variable, usize>, var: &Variable) -> usize {
let index = variables.len();
*variables.entry(*var).or_insert(index)
}
/// Calculates one R1CS row representation of a program and returns (V, A, B, C) so that:
/// * `V` contains all used variables and the index in the vector represents the used number in `A`, `B`, `C`
/// * `<A,x>*<B,x> = <C,x>` for a witness `x`
///
/// # Arguments
///
/// * `prog` - The program the representation is calculated for.
pub fn r1cs_program<T: Field>(prog: Prog<T>) -> (Vec<Variable>, usize, Vec<Constraint<T>>) {
let mut variables: HashMap<Variable, usize> = HashMap::new();
provide_variable_idx(&mut variables, &Variable::one());
for i in 0..prog.return_count {
provide_variable_idx(&mut variables, &Variable::public(i));
}
for x in prog.arguments.iter().filter(|p| !p.private) {
provide_variable_idx(&mut variables, &x.id);
}
// position where private part of witness starts
let private_inputs_offset = variables.len();
// first pass through statements to populate `variables`
for (quad, lin) in prog.statements.iter().filter_map(|s| match s {
Statement::Constraint(quad, lin, _) => Some((quad, lin)),
Statement::Directive(..) => None,
}) {
for (k, _) in &quad.left.0 {
provide_variable_idx(&mut variables, k);
}
for (k, _) in &quad.right.0 {
provide_variable_idx(&mut variables, k);
}
for (k, _) in &lin.0 {
provide_variable_idx(&mut variables, k);
}
}
let mut constraints = vec![];
// second pass to convert program to raw sparse vectors
for (quad, lin) in prog.statements.into_iter().filter_map(|s| match s {
Statement::Constraint(quad, lin, _) => Some((quad, lin)),
Statement::Directive(..) => None,
}) {
constraints.push((
quad.left
.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
quad.right
.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
lin.0
.into_iter()
.map(|(k, v)| (*variables.get(&k).unwrap(), v))
.collect(),
));
}
// Convert map back into list ordered by index
let mut variables_list = vec![Variable::new(0); variables.len()];
for (k, v) in variables.drain() {
assert_eq!(variables_list[v], Variable::new(0));
variables_list[v] = k;
}
(variables_list, private_inputs_offset, constraints)
}
pub fn write_r1cs<T: Field, W: Write>(writer: &mut W, p: Prog<T>) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len() as u32;
let n_pub_out = p.return_count as u32;
let n_pub_in = p.arguments.iter().filter(|a| !a.private).count() as u32;
let n_prv_in = p.arguments.iter().filter(|a| a.private).count() as u32;
let (vars, _, constraints) = r1cs_program(p);
let n_wires = vars.len();
let header = Header {
field_size: modulo_byte_count,
prime_size: T::max_value().to_biguint().add(1u32).to_bytes_le(),
n_wires: n_wires as u32,
n_pub_out,
n_pub_in,
n_prv_in,
n_labels: n_wires as u64,
n_constraints: constraints.len() as u32,
};
// magic
writer.write_all(&[0x72, 0x31, 0x63, 0x73])?;
// version
writer.write_u32::<LittleEndian>(1)?;
// section count
writer.write_u32::<LittleEndian>(3)?;
// section type: constraints
// type
writer.write_u32::<LittleEndian>(2)?;
// size: 4 per lc + (32 + 4) per summand
let size = constraints
.iter()
.map(|(a, b, c)| {
(3 * 4 // for each lc, 4 bytes for its size
+ (a.len() + b.len() + c.len()) // for each summand
* (modulo_byte_count as usize + 4)) // 4 bytes for the signal, `modulo_byte_count` bytes for the coefficient
as u64
})
.sum();
writer.write_u64::<LittleEndian>(size)?;
write_constraints(writer, constraints)?;
// section type: header
// type
writer.write_u32::<LittleEndian>(1)?;
// size
writer.write_u64::<LittleEndian>(32 + 32)?;
// header
write_header(writer, header)?;
// section type: wire2label
// type
writer.write_u32::<LittleEndian>(3)?;
// size
writer.write_u64::<LittleEndian>(n_wires as u64 * 8)?;
write_table(writer, &vars)?;
Ok(())
}
fn write_constraints<T: Field, W: Write>(
writer: &mut W,
constraints: Vec<Constraint<T>>,
) -> Result<()> {
for c in constraints {
write_lincomb(writer, c.0)?;
write_lincomb(writer, c.1)?;
write_lincomb(writer, c.2)?;
}
Ok(())
}
fn write_lincomb<T: Field, W: Write>(writer: &mut W, l: LinComb<T>) -> Result<()> {
writer.write_u32::<LittleEndian>(l.len() as u32)?;
for (var, coeff) in l {
writer.write_u32::<LittleEndian>(var as u32)?;
let mut res = vec![0u8; 32];
for (value, padded) in coeff.to_biguint().to_bytes_le().iter().zip(res.iter_mut()) {
*padded = *value;
}
writer.write_all(&res)?;
}
Ok(())
}
// for now we do not write any signal map
fn write_table<W: Write>(w: &mut W, variables: &[Variable]) -> Result<()> {
for (i, _) in variables.iter().enumerate() {
w.write_u64::<LittleEndian>(i as u64)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
use pretty_assertions::assert_eq;
use zkutil::r1cs_reader;
use zokrates_ast::{
flat::{Parameter, Variable},
ir::{LinComb, QuadComb, Statement},
};
use zokrates_field::Bn128Field;
#[test]
fn empty() {
let prog: Prog<Bn128Field> = Prog::default();
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x72, 0x31, 0x63, 0x73,
// version
0x01, 0x00, 0x00, 0x00,
// section count
0x03, 0x00, 0x00, 0x00,
// constraints section (empty)
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// n wires
0x01, 0x00, 0x00, 0x00,
// n pub outputs
0x00, 0x00, 0x00, 0x00,
// n pub inputs
0x00, 0x00, 0x00, 0x00,
// n priv
0x00, 0x00, 0x00, 0x00,
// n labels
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// n constraints
0x00, 0x00, 0x00, 0x00,
// wire map (variable one?)
0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
#[test]
fn return_one() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![],
return_count: 1,
statements: vec![Statement::Constraint(
LinComb::one().into(),
Variable::public(0).into(),
None,
)],
};
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
0x72, 0x31, 0x63, 0x73,
0x01, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // size = 1 constraint = sum(4 /* write term_count_i */ + term_count_i * (4 + 32)) = 120
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x01, 0x00, 0x00, 0x00, // variable 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulo size
0x20, 0x00, 0x00, 0x00,
// modulo
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
0x02, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
// wire map (one, pub0)
0x03, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
#[test]
fn with_inputs() {
let prog: Prog<Bn128Field> = Prog {
arguments: vec![
Parameter::private(Variable::new(0)),
Parameter::public(Variable::new(1)),
],
return_count: 1,
statements: vec![
Statement::Constraint(
QuadComb::from_linear_combinations(
LinComb::from(Variable::new(0)),
LinComb::from(Variable::new(0)),
),
LinComb::from(Variable::new(0)),
None,
),
Statement::Constraint(
(LinComb::from(Variable::new(0)) + LinComb::from(Variable::new(1))).into(),
Variable::public(0).into(),
None,
),
],
};
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
0x72, 0x31, 0x63, 0x73,
0x01, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// first constraint
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// second constraint
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x00, 0x00, 0x00, 0x00, // variable 0
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x02, 0x00, 0x00, 0x00, // 2 element in this lc
0x03, 0x00, 0x00, 0x00, // variable 3
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x02, 0x00, 0x00, 0x00, // variable 2
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
0x01, 0x00, 0x00, 0x00, // 1 element in this lc
0x01, 0x00, 0x00, 0x00, // variable 1
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // coeff 1
// header
0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulo size
0x20, 0x00, 0x00, 0x00,
// modulo
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
0x04, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00,
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00,
// wire map (one, ~out_0, _1, _0)
0x03, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_r1cs(&mut buf, prog).unwrap();
assert_eq!(buf, expected);
let c = Cursor::new(buf);
assert!(r1cs_reader::read(c).is_ok());
}
}

View file

@ -0,0 +1,219 @@
use std::{
io::{Result, Write},
ops::Add,
};
use byteorder::{LittleEndian, WriteBytesExt};
use zokrates_ast::{
flat::Variable,
ir::{PublicInputs, Witness},
};
use zokrates_field::Field;
pub struct Header {
pub field_size: u32,
pub prime_size: Vec<u8>,
pub witness_size: u32,
}
fn write_header<W: Write>(writer: &mut W, header: Header) -> Result<()> {
writer.write_u32::<LittleEndian>(header.field_size)?;
writer.write_all(&header.prime_size)?;
writer.write_u32::<LittleEndian>(header.witness_size)?;
Ok(())
}
pub fn write_witness<T: Field, W: Write>(
writer: &mut W,
w: Witness<T>,
public_inputs: PublicInputs,
) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len() as u32;
let witness_size = w.0.len() as u32;
let header = Header {
field_size: modulo_byte_count,
prime_size: T::max_value().to_biguint().add(1u32).to_bytes_le(),
witness_size,
};
// magic "wtns"
writer.write_all(&[0x77, 0x74, 0x6e, 0x73])?;
// version
writer.write_u32::<LittleEndian>(2)?;
// section count
writer.write_u32::<LittleEndian>(2)?;
// section type: header
// type
writer.write_u32::<LittleEndian>(1)?;
// size
writer.write_u64::<LittleEndian>(8 + modulo_byte_count as u64)?;
// header
write_header(writer, header)?;
// section type: witness
// type
writer.write_u32::<LittleEndian>(2)?;
// size: `modulo_byte_count` per witness value
let size = witness_size as u64 * modulo_byte_count as u64;
writer.write_u64::<LittleEndian>(size)?;
write_witness_values(writer, w, public_inputs)?;
Ok(())
}
fn write_val<T: Field, W: Write>(writer: &mut W, v: &T, modulo_byte_count: usize) -> Result<()> {
let mut res = vec![0u8; modulo_byte_count];
for (value, padded) in v.to_biguint().to_bytes_le().iter().zip(res.iter_mut()) {
*padded = *value;
}
writer.write_all(&res)?;
Ok(())
}
fn write_witness_values<T: Field, W: Write>(
writer: &mut W,
mut w: Witness<T>,
public_inputs: PublicInputs,
) -> Result<()> {
let modulo_byte_count = T::max_value().to_biguint().add(1u32).to_bytes_le().len();
if let Some(value) = w.0.remove(&Variable::one()) {
write_val(writer, &value, modulo_byte_count)?;
}
let output_count = w.0.iter().filter(|(var, _)| var.is_output()).count();
for value in (0..output_count).map(|id| w.0.remove(&Variable::public(id)).unwrap()) {
write_val(writer, &value, modulo_byte_count)?;
}
for value in public_inputs.iter().map(|var| w.0.remove(var).unwrap()) {
write_val(writer, &value, modulo_byte_count)?;
}
for (_, val) in w.0.iter() {
write_val(writer, val, modulo_byte_count)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use zokrates_ast::{flat::Variable, ir::PublicInputs};
use zokrates_field::Bn128Field;
#[test]
fn empty() {
let w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = Default::default();
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x00, 0x00, 0x00, 0x00,
// witness section (empty)
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn one_value() {
let mut w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = Default::default();
w.0.insert(Variable::public(0), 1.into());
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x01, 0x00, 0x00, 0x00,
// witness section
0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// values
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
#[test]
fn one_and_pub_and_priv() {
let mut w: Witness<Bn128Field> = Witness::default();
let public_inputs: PublicInputs = vec![Variable::new(1)].into_iter().collect();
w.0.extend(vec![
(Variable::public(0), 42.into()),
(Variable::one(), 1.into()),
(Variable::new(0), 43.into()),
(Variable::new(1), 44.into()),
]);
let mut buf = Vec::new();
#[rustfmt::skip]
let expected: Vec<u8> = vec![
// magic
0x77, 0x74, 0x6e, 0x73,
// version
0x02, 0x00, 0x00, 0x00,
// section count
0x02, 0x00, 0x00, 0x00,
// header
0x01, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// modulus size in bytes
0x20, 0x00, 0x00, 0x00,
// modulus
0x01, 0x00, 0x00, 0xf0, 0x93, 0xf5, 0xe1, 0x43, 0x91, 0x70, 0xb9, 0x79, 0x48, 0xe8, 0x33, 0x28, 0x5d, 0x58, 0x81, 0x81, 0xb6, 0x45, 0x50, 0xb8, 0x29, 0xa0, 0x31, 0xe1, 0x72, 0x4e, 0x64, 0x30,
// witness size
0x04, 0x00, 0x00, 0x00,
// witness section
0x02, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// values: ordering should be [one, ~out_0, _1, _0] because _1 is public
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
write_witness(&mut buf, w, public_inputs).unwrap();
assert_eq!(buf, expected);
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_cli"
version = "0.7.13"
version = "0.7.14"
authors = ["Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>", "Dennis Kuhnert <mail@kyroy.com>", "Thibaut Schaeffer <thibaut@schaeff.fr>"]
repository = "https://github.com/Zokrates/ZoKrates.git"
edition = "2018"
@ -22,6 +22,7 @@ zokrates_abi = { version = "0.1", path = "../zokrates_abi" }
zokrates_core = { version = "0.6", path = "../zokrates_core", default-features = false }
zokrates_ast = { version = "0.1", path = "../zokrates_ast", default-features = false }
zokrates_interpreter = { version = "0.1", path = "../zokrates_interpreter", default-features = false }
zokrates_circom = { version = "0.1", path = "../zokrates_circom", default-features = false }
typed-arena = "1.4.1"
zokrates_fs_resolver = { version = "0.5", path = "../zokrates_fs_resolver"}
zokrates_common = { version = "0.1", path = "../zokrates_common", default-features = false }

View file

@ -1,6 +1,8 @@
use zokrates_common::constants::*;
pub const FLATTENED_CODE_DEFAULT_PATH: &str = "out";
pub const CIRCOM_R1CS_DEFAULT_PATH: &str = "out.r1cs";
pub const CIRCOM_WITNESS_DEFAULT_PATH: &str = "out.wtns";
pub const ABI_SPEC_DEFAULT_PATH: &str = "abi.json";
pub const VERIFICATION_KEY_DEFAULT_PATH: &str = "verification.key";
pub const PROVING_KEY_DEFAULT_PATH: &str = "proving.key";

View file

@ -6,6 +6,7 @@ use std::fs::File;
use std::io::{BufReader, BufWriter, Read};
use std::path::{Path, PathBuf};
use typed_arena::Arena;
use zokrates_circom::write_r1cs;
use zokrates_common::constants::BN128;
use zokrates_common::helpers::CurveParameter;
use zokrates_core::compile::{compile, CompileConfig, CompileError};
@ -46,7 +47,15 @@ pub fn subcommand() -> App<'static, 'static> {
.takes_value(true)
.required(false)
.default_value(cli_constants::FLATTENED_CODE_DEFAULT_PATH)
).arg(Arg::with_name("curve")
).arg(Arg::with_name("r1cs")
.short("r1cs")
.long("r1cs")
.help("Path of the output r1cs file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::CIRCOM_R1CS_DEFAULT_PATH)
).arg(Arg::with_name("curve")
.short("c")
.long("curve")
.help("Curve to be used in the compilation")
@ -75,6 +84,7 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
println!("Compiling {}\n", sub_matches.value_of("input").unwrap());
let path = PathBuf::from(sub_matches.value_of("input").unwrap());
let bin_output_path = Path::new(sub_matches.value_of("output").unwrap());
let r1cs_output_path = Path::new(sub_matches.value_of("r1cs").unwrap());
let abi_spec_path = Path::new(sub_matches.value_of("abi-spec").unwrap());
log::debug!("Load entry point file {}", path.display());
@ -133,9 +143,17 @@ fn cli_compile<T: Field>(sub_matches: &ArgMatches) -> Result<(), String> {
let bin_output_file = File::create(&bin_output_path)
.map_err(|why| format!("Could not create {}: {}", bin_output_path.display(), why))?;
let mut writer = BufWriter::new(bin_output_file);
let r1cs_output_file = File::create(&r1cs_output_path)
.map_err(|why| format!("Could not create {}: {}", r1cs_output_path.display(), why))?;
match program_flattened.serialize(&mut writer) {
let mut bin_writer = BufWriter::new(bin_output_file);
let mut r1cs_writer = BufWriter::new(r1cs_output_file);
let program_flattened = program_flattened.collect();
write_r1cs(&mut r1cs_writer, program_flattened.clone()).unwrap();
match program_flattened.serialize(&mut bin_writer) {
Ok(constraint_count) => {
// serialize ABI spec and write to JSON file
log::debug!("Serialize ABI");

View file

@ -10,6 +10,7 @@ use zokrates_ast::typed::{
abi::Abi,
types::{ConcreteSignature, ConcreteType, GTupleType},
};
use zokrates_circom::write_witness;
use zokrates_field::Field;
pub fn subcommand() -> App<'static, 'static> {
@ -34,11 +35,18 @@ pub fn subcommand() -> App<'static, 'static> {
).arg(Arg::with_name("output")
.short("o")
.long("output")
.help("Path of the output file")
.help("Path of the output witness file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::WITNESS_DEFAULT_PATH)
).arg(Arg::with_name("circom-witness")
.long("circom-witness")
.help("Path of the output circom witness file")
.value_name("FILE")
.takes_value(true)
.required(false)
.default_value(cli_constants::CIRCOM_WITNESS_DEFAULT_PATH)
).arg(Arg::with_name("arguments")
.short("a")
.long("arguments")
@ -161,6 +169,8 @@ fn cli_compute<T: Field, I: Iterator<Item = ir::Statement<T>>>(
let interpreter = zokrates_interpreter::Interpreter::default();
let public_inputs = ir_prog.public_inputs();
let witness = interpreter
.execute(ir_prog, &arguments.encode())
.map_err(|e| format!("Execution failed: {}", e))?;
@ -185,6 +195,16 @@ fn cli_compute<T: Field, I: Iterator<Item = ir::Statement<T>>>(
.write(writer)
.map_err(|why| format!("Could not save witness: {:?}", why))?;
// write circom witness to file
let wtns_path = Path::new(sub_matches.value_of("circom-witness").unwrap());
let wtns_file = File::create(&wtns_path)
.map_err(|why| format!("Could not create {}: {}", output_path.display(), why))?;
let mut writer = BufWriter::new(wtns_file);
write_witness(&mut writer, witness, public_inputs)
.map_err(|why| format!("Could not save circom witness: {:?}", why))?;
println!("Witness file written to '{}'", output_path.display());
Ok(())
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_common"
version = "0.1.0"
version = "0.1.1"
authors = ["dark64 <darem966@gmail.com>"]
edition = "2018"

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_core"
version = "0.6.12"
version = "0.6.13"
edition = "2018"
authors = ["Jacob Eberhardt <jacob.eberhardt@tu-berlin.de>", "Dennis Kuhnert <mail@kyroy.com>"]
repository = "https://github.com/Zokrates/ZoKrates"

View file

@ -33,6 +33,26 @@ impl<'a, 'ast, T: Field> ConstantsReader<'a, 'ast, T> {
}
impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
fn fold_declaration_constant(
&mut self,
c: DeclarationConstant<'ast, T>,
) -> DeclarationConstant<'ast, T> {
match c {
DeclarationConstant::Constant(c) => {
let c = self.fold_canonical_constant_identifier(c);
match self.constants.get(&c).cloned() {
Some(e) => match UExpression::try_from(e).unwrap().into_inner() {
UExpressionInner::Value(v) => DeclarationConstant::Concrete(v as u32),
_ => unreachable!(),
},
None => DeclarationConstant::Constant(c),
}
}
c => fold_declaration_constant(self, c),
}
}
fn fold_field_expression(
&mut self,
e: FieldElementExpression<'ast, T>,
@ -169,24 +189,4 @@ impl<'a, 'ast, T: Field> Folder<'ast, T> for ConstantsReader<'a, 'ast, T> {
e => fold_struct_expression_inner(self, ty, e),
}
}
fn fold_declaration_constant(
&mut self,
c: DeclarationConstant<'ast, T>,
) -> DeclarationConstant<'ast, T> {
match c {
DeclarationConstant::Constant(c) => {
let c = self.fold_canonical_constant_identifier(c);
match self.constants.get(&c).cloned() {
Some(e) => match UExpression::try_from(e).unwrap().into_inner() {
UExpressionInner::Value(v) => DeclarationConstant::Concrete(v as u32),
_ => unreachable!(),
},
None => DeclarationConstant::Constant(c),
}
}
c => fold_declaration_constant(self, c),
}
}
}

View file

@ -0,0 +1,16 @@
{
"entry_point": "./tests/tests/constants/tuple.zok",
"max_constraint_count": 3,
"tests": [
{
"input": {
"values": []
},
"output": {
"Ok": {
"values": [[["1", "1"], true]]
}
}
}
]
}

View file

@ -0,0 +1,5 @@
const u32 N = 2
const (field[N], bool) A = ([1; N], true)
def main() -> ((field[N], bool)):
return A

View file

@ -165,11 +165,7 @@ pub fn generate_verify_constraints(
.into_iter()
.zip(matrices.b.into_iter())
.zip(matrices.c.into_iter())
.map(|((a, b), c)| Constraint {
a: a.into_iter().map(|(f, index)| (index, f)).collect(),
b: b.into_iter().map(|(f, index)| (index, f)).collect(),
c: c.into_iter().map(|(f, index)| (index, f)).collect(),
})
.map(|((a, b), c)| Constraint { a, b, c })
.collect();
(
@ -309,26 +305,26 @@ pub fn from_ark<T: zokrates_field::Field, E: PairingEngine>(c: Constraint<E::Fq>
Constraint {
a: c.a
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
b: c.b
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
c: c.c
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
}

View file

@ -180,17 +180,17 @@ impl<E: Engine> ConstraintSystem<E> for R1CS<E::Fr> {
let a = a
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.map(|(variable, coefficient)| (*coefficient, var_to_index(*variable)))
.collect();
let b = b
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.map(|(variable, coefficient)| (*coefficient, var_to_index(*variable)))
.collect();
let c = c
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.map(|(variable, coefficient)| (*coefficient, var_to_index(*variable)))
.collect();
self.constraints.push(Constraint { a, b, c });
@ -257,26 +257,26 @@ pub fn from_bellman<T: zokrates_field::Field, E: Engine>(c: Constraint<E::Fr>) -
Constraint {
a: c.a
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
b: c.b
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
c: c.c
.into_iter()
.map(|(index, fq)| {
.map(|(fq, index)| {
let mut res: Vec<u8> = vec![];
fq.into_repr().write_le(&mut res).unwrap();
(index, T::from_byte_vector(res))
(T::from_byte_vector(res), index)
})
.collect(),
}

View file

@ -26,7 +26,7 @@ pub struct Witness<T> {
#[derive(Default, Debug, PartialEq, Clone)]
pub struct Constraint<T> {
pub a: Vec<(usize, T)>,
pub b: Vec<(usize, T)>,
pub c: Vec<(usize, T)>,
pub a: Vec<(T, usize)>,
pub b: Vec<(T, usize)>,
pub c: Vec<(T, usize)>,
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_js"
version = "1.0.43"
version = "1.0.44"
authors = ["Darko Macesic"]
edition = "2018"
@ -21,5 +21,6 @@ zokrates_ast = { path = "../zokrates_ast", default-features = false, features =
zokrates_interpreter = { path = "../zokrates_interpreter", default-features = false, features = ["ark"] }
zokrates_field = { path = "../zokrates_field", default-features = false }
zokrates_abi = { path = "../zokrates_abi", default-features = false, features = ["ark"] }
zokrates_circom = { path = "../zokrates_circom" }
console_error_panic_hook = "0.1.6"
indexmap = "~1.6.2" # see https://github.com/rustwasm/wasm-bindgen/issues/2770#issuecomment-1041102532

View file

@ -19,6 +19,7 @@ declare module "zokrates-js" {
location?: string;
resolveCallback?: ResolveCallback;
config?: CompileConfig;
snarkjs?: boolean;
}
export type Proof = {
@ -31,9 +32,16 @@ declare module "zokrates-js" {
location: string;
}
export interface ComputeOptions {
snarkjs?: boolean;
}
export interface ComputationResult {
witness: string;
output: string;
snarkjs?: {
witness: Uint8Array;
};
}
export interface Abi {
@ -44,6 +52,9 @@ declare module "zokrates-js" {
export interface CompilationArtifacts {
program: Uint8Array;
abi: Abi;
snarkjs?: {
program: Uint8Array;
};
}
export interface SetupKeypair {
@ -64,7 +75,8 @@ declare module "zokrates-js" {
): CompilationArtifacts;
computeWitness(
input: CompilationArtifacts | Uint8Array,
args: any[]
args: any[],
options?: ComputeOptions
): ComputationResult;
setup(program: Uint8Array): SetupKeypair;
universalSetup(size: number): Uint8Array;

View file

@ -1,6 +1,6 @@
{
"name": "zokrates-js",
"version": "1.0.43",
"version": "1.0.44",
"module": "index.js",
"main": "node/index.js",
"description": "JavaScript bindings for ZoKrates",
@ -43,7 +43,7 @@
"clean-node-pkg": "rimraf node/pkg/README.md node/pkg/.gitignore",
"pretest": "npm run setup && npm run build:node:dev",
"test": "npm run run-tests",
"run-tests": "mocha --timeout 30000 --require esm --recursive tests"
"run-tests": "mocha --timeout 100000 --require esm --recursive tests"
},
"devDependencies": {
"dree": "^2.6.1",
@ -53,6 +53,7 @@
"mocha": "^9.2.0",
"rimraf": "^3.0.2",
"serve": "^11.3.2",
"snarkjs": "^0.4.19",
"text-encoding": "^0.7.0",
"toml": "^3.0.0",
"wasm-pack": "^0.10.2"

View file

@ -11,6 +11,7 @@ use zokrates_ast::ir;
use zokrates_ast::ir::ProgEnum;
use zokrates_ast::typed::abi::Abi;
use zokrates_ast::typed::types::{ConcreteSignature, ConcreteType, GTupleType};
use zokrates_circom::{write_r1cs, write_witness};
use zokrates_common::helpers::{CurveParameter, SchemeParameter};
use zokrates_common::Resolver;
use zokrates_core::compile::{
@ -29,6 +30,7 @@ use zokrates_proof_systems::{
pub struct CompilationResult {
program: Vec<u8>,
abi: Abi,
snarkjs_program: Option<Vec<u8>>,
}
#[wasm_bindgen]
@ -41,6 +43,14 @@ impl CompilationResult {
pub fn abi(&self) -> JsValue {
JsValue::from_serde(&self.abi).unwrap()
}
pub fn snarkjs_program(&self) -> Option<js_sys::Uint8Array> {
self.snarkjs_program.as_ref().map(|p| {
let arr = js_sys::Uint8Array::new_with_length(p.len() as u32);
arr.copy_from(p);
arr
})
}
}
#[derive(Serialize, Deserialize)]
@ -49,10 +59,28 @@ pub struct ResolverResult {
location: String,
}
#[derive(Serialize, Deserialize)]
#[wasm_bindgen]
pub struct ComputationResult {
witness: String,
output: String,
snarkjs_witness: Option<Vec<u8>>,
}
#[wasm_bindgen]
impl ComputationResult {
pub fn witness(&self) -> JsValue {
JsValue::from_str(&self.witness)
}
pub fn output(&self) -> JsValue {
JsValue::from_str(&self.output)
}
pub fn snarkjs_witness(&self) -> Option<js_sys::Uint8Array> {
self.snarkjs_witness.as_ref().map(|w| {
let arr = js_sys::Uint8Array::new_with_length(w.len() as u32);
arr.copy_from(w);
arr
})
}
}
pub struct JsResolver<'a> {
@ -107,7 +135,12 @@ mod internal {
config: JsValue,
) -> Result<CompilationResult, JsValue> {
let resolver = JsResolver::new(resolve_callback);
let config: CompileConfig = config.into_serde().unwrap_or_default();
let config: serde_json::Value = config.into_serde().unwrap();
let with_snarkjs_program = config
.get("snarkjs")
.map(|v| *v == serde_json::Value::Bool(true))
.unwrap_or(false);
let config: CompileConfig = serde_json::from_value(config).unwrap_or_default();
let fmt_error = |e: &CompileError| format!("{}:{}", e.file().display(), e.value());
@ -131,13 +164,19 @@ mod internal {
let abi = artifacts.abi().clone();
let program = artifacts.prog();
let program = artifacts.prog().collect();
let snarkjs_program = with_snarkjs_program.then(|| {
let mut buffer = Cursor::new(vec![]);
write_r1cs(&mut buffer, program.clone()).unwrap();
buffer.into_inner()
});
let mut buffer = Cursor::new(vec![]);
let _ = program.serialize(&mut buffer);
Ok(CompilationResult {
abi,
program: buffer.into_inner(),
snarkjs_program,
})
}
@ -145,9 +184,16 @@ mod internal {
program: ir::Prog<T>,
abi: JsValue,
args: JsValue,
) -> Result<JsValue, JsValue> {
config: JsValue,
) -> Result<ComputationResult, JsValue> {
let input = args.as_string().unwrap();
let config: serde_json::Value = config.into_serde().unwrap();
let with_snarkjs_witness = config
.get("snarkjs")
.map(|v| *v == serde_json::Value::Bool(true))
.unwrap_or(false);
let (inputs, signature) = if abi.is_object() {
let abi: Abi = abi.into_serde().map_err(|err| {
JsValue::from_str(&format!("Could not deserialize `abi`: {}", err))
@ -175,6 +221,8 @@ mod internal {
let interpreter = zokrates_interpreter::Interpreter::default();
let public_inputs = program.public_inputs();
let witness = interpreter
.execute(program, &inputs.encode())
.map_err(|err| JsValue::from_str(&format!("Execution failed: {}", err)))?;
@ -183,12 +231,17 @@ mod internal {
zokrates_abi::Value::decode(witness.return_values(), *signature.output)
.into_serde_json();
let result = ComputationResult {
let snarkjs_witness = with_snarkjs_witness.then(|| {
let mut buffer = Cursor::new(vec![]);
write_witness(&mut buffer, witness.clone(), public_inputs).unwrap();
buffer.into_inner()
});
Ok(ComputationResult {
witness: format!("{}", witness),
output: to_string_pretty(&return_values).unwrap(),
};
Ok(JsValue::from_serde(&result).unwrap())
snarkjs_witness,
})
}
pub fn setup_non_universal<
@ -313,15 +366,20 @@ pub fn compile(
}
#[wasm_bindgen]
pub fn compute_witness(program: &[u8], abi: JsValue, args: JsValue) -> Result<JsValue, JsValue> {
pub fn compute_witness(
program: &[u8],
abi: JsValue,
args: JsValue,
config: JsValue,
) -> Result<ComputationResult, JsValue> {
let prog = ir::ProgEnum::deserialize(program)
.map_err(|err| JsValue::from_str(&err))?
.collect();
match prog {
ProgEnum::Bn128Program(p) => internal::compute::<_>(p, abi, args),
ProgEnum::Bls12_381Program(p) => internal::compute::<_>(p, abi, args),
ProgEnum::Bls12_377Program(p) => internal::compute::<_>(p, abi, args),
ProgEnum::Bw6_761Program(p) => internal::compute::<_>(p, abi, args),
ProgEnum::Bn128Program(p) => internal::compute::<_>(p, abi, args, config),
ProgEnum::Bls12_381Program(p) => internal::compute::<_>(p, abi, args, config),
ProgEnum::Bls12_377Program(p) => internal::compute::<_>(p, abi, args, config),
ProgEnum::Bw6_761Program(p) => internal::compute::<_>(p, abi, args, config),
}
}

Binary file not shown.

View file

@ -1,170 +1,235 @@
const assert = require("assert");
const path = require("path");
const fs = require("fs");
const os = require("os");
const dree = require("dree");
const snarkjs = require("snarkjs");
const { initialize } = require("../node/index.js");
describe("tests", function () {
let zokratesProvider;
let zokratesProvider;
let tmpFolder;
describe("tests", () => {
// initialize once before running tests
before((done) => {
initialize().then((defaultProvider) => {
before(() => {
return initialize().then((defaultProvider) => {
zokratesProvider = defaultProvider;
done();
return fs.promises.mkdtemp(path.join(os.tmpdir(), path.sep)).then((folder) => {
tmpFolder = folder;
});
});
});
describe("compilation", () => {
it("should compile", () => {
assert.doesNotThrow(() => {
const artifacts = zokratesProvider.compile(
"def main() -> field { return 42; }"
);
assert.ok(artifacts !== undefined);
});
});
after(() => {
if (globalThis.curve_bn128) globalThis.curve_bn128.terminate();
});
it("should throw on invalid code", () => {
assert.throws(() => zokratesProvider.compile(":-)"));
});
describe("compilation", () => {
it("should compile", () => {
assert.doesNotThrow(() => {
const artifacts = zokratesProvider.compile(
"def main() -> field: return 42"
);
assert.ok(artifacts !== undefined);
assert.ok(artifacts.snarkjs === undefined);
});
});
it("should resolve stdlib module", () => {
const stdlib = require("../stdlib.js");
assert.doesNotThrow(() => {
const code = `import "${
Object.keys(stdlib)[0]
}" as func; def main() { return; }`;
zokratesProvider.compile(code);
});
});
it("should compile with snarkjs output", () => {
assert.doesNotThrow(() => {
const artifacts = zokratesProvider.compile(
"def main() -> field: return 42",
{ snarkjs: true }
);
assert.ok(artifacts !== undefined);
assert.ok(artifacts.snarkjs.program !== undefined);
});
});
it("should resolve user module", () => {
assert.doesNotThrow(() => {
const code =
'import "test" as test; def main() -> field { return test(); }';
const options = {
resolveCallback: (_, path) => {
return {
source: "def main() -> field { return 1; }",
location: path,
};
},
};
zokratesProvider.compile(code, options);
});
});
it("should throw on invalid code", () => {
assert.throws(() => zokratesProvider.compile(":-)"));
});
it("should throw on unresolved module", () => {
assert.throws(() => {
const code =
'import "test" as test; def main() -> field { return test(); }';
zokratesProvider.compile(code);
});
});
});
it("should resolve stdlib module", () => {
const stdlib = require("../stdlib.js");
assert.doesNotThrow(() => {
const code = `import "${
Object.keys(stdlib)[0]
}" as func\ndef main(): return`;
zokratesProvider.compile(code);
});
});
describe("computation", () => {
it("should compute with valid inputs", () => {
assert.doesNotThrow(() => {
const code = "def main(private field a) -> field { return a * a; }";
const artifacts = zokratesProvider.compile(code);
it("should resolve user module", () => {
assert.doesNotThrow(() => {
const code =
'import "test" as test\ndef main() -> field: return test()';
const options = {
resolveCallback: (_, path) => {
return {
source: "def main() -> (field): return 1",
location: path,
};
},
};
zokratesProvider.compile(code, options);
});
});
const result = zokratesProvider.computeWitness(artifacts, ["2"]);
const output = JSON.parse(result.output);
assert.deepEqual(output, "4");
});
});
it("should throw on unresolved module", () => {
assert.throws(() => {
const code =
'import "test" as test\ndef main() -> field: return test()';
zokratesProvider.compile(code);
});
});
});
it("should throw on invalid input count", () => {
assert.throws(() => {
const code = "def main(private field a) -> field { return a * a; }";
const artifacts = zokratesProvider.compile(code);
zokratesProvider.computeWitness(artifacts, ["1", "2"]);
});
});
describe("computation", () => {
it("should compute with valid inputs", () => {
assert.doesNotThrow(() => {
const code = "def main(private field a) -> field: return a * a";
const artifacts = zokratesProvider.compile(code);
const result = zokratesProvider.computeWitness(artifacts, ["2"]);
const output = JSON.parse(result.output);
assert.deepEqual(output, ["4"]);
assert.ok(result.snarkjs === undefined);
});
});
it("should throw on invalid input type", () => {
assert.throws(() => {
const code = "def main(private field a) -> field { return a * a; }";
const artifacts = zokratesProvider.compile(code);
zokratesProvider.computeWitness(artifacts, [true]);
});
});
});
it("should compute with valid inputs with snarkjs output", () => {
assert.doesNotThrow(() => {
const code = "def main(private field a) -> field: return a * a";
const artifacts = zokratesProvider.compile(code);
const runWithOptions = (options) => {
let provider;
let artifacts;
let computationResult;
let keypair;
let proof;
const result = zokratesProvider.computeWitness(artifacts, ["2"], {
snarkjs: true,
});
before((done) => {
provider = zokratesProvider.withOptions(options);
done();
});
const output = JSON.parse(result.output);
assert.deepEqual(output, ["4"]);
assert.ok(result.snarkjs.witness !== undefined);
});
});
it("compile", () => {
assert.doesNotThrow(() => {
const code =
"def main(private field a, field b) -> bool { return a * a == b; }";
artifacts = provider.compile(code);
});
});
it("should throw on invalid input count", () => {
assert.throws(() => {
const code = "def main(private field a) -> field: return a * a";
const artifacts = zokratesProvider.compile(code);
zokratesProvider.computeWitness(artifacts, ["1", "2"]);
});
});
it("compute witness", () => {
assert.doesNotThrow(() => {
computationResult = provider.computeWitness(artifacts, ["2", "4"]);
});
});
it("should throw on invalid input type", () => {
assert.throws(() => {
const code = "def main(private field a) -> field: return a * a";
const artifacts = zokratesProvider.compile(code);
zokratesProvider.computeWitness(artifacts, [true]);
});
});
});
it("setup", () => {
assert.doesNotThrow(() => {
if (options.scheme === "marlin") {
const srs = provider.universalSetup(4);
keypair = provider.setupWithSrs(srs, artifacts.program);
} else {
keypair = provider.setup(artifacts.program);
}
});
});
const runWithOptions = (options) => {
let provider;
let artifacts;
let computationResult;
let keypair;
let proof;
if (options.curve === "bn128" && ["g16", "gm17"].includes(options.scheme)) {
it("export verifier", () => {
assert.doesNotThrow(() => {
let verifier = provider.exportSolidityVerifier(keypair.vk);
assert.ok(verifier.includes("contract"));
});
});
}
before(() => {
provider = zokratesProvider.withOptions(options);
});
it("generate proof", () => {
assert.doesNotThrow(() => {
proof = provider.generateProof(
artifacts.program,
computationResult.witness,
keypair.pk
);
assert.ok(proof !== undefined);
assert.equal(proof.inputs.length, 2);
});
});
it("compile", () => {
assert.doesNotThrow(() => {
const code =
"def main(private field a, field b) -> bool: return a * a == b";
artifacts = provider.compile(code, { snarkjs: true });
});
});
it("verify", () => {
assert.doesNotThrow(() => {
assert(provider.verify(keypair.vk, proof) === true);
});
});
};
it("compute witness", () => {
assert.doesNotThrow(() => {
computationResult = provider.computeWitness(artifacts, ["2", "4"], {
snarkjs: true,
});
});
});
for (const scheme of ["g16", "gm17", "marlin"]) {
describe(scheme, () => {
for (const curve of ["bn128", "bls12_381", "bls12_377", "bw6_761"]) {
describe(curve, () => runWithOptions({ scheme, curve }));
}
});
}
it("setup", () => {
assert.doesNotThrow(() => {
if (options.scheme === "marlin") {
const srs = provider.universalSetup(4);
keypair = provider.setupWithSrs(srs, artifacts.program);
} else {
keypair = provider.setup(artifacts.program);
}
});
});
if (options.scheme === "g16" && options.curve == "bn128") {
it("snarkjs setup", () => {
// write program to fs
let r1csPath = tmpFolder + "/prog.r1cs";
let zkeyPath = tmpFolder + "/key.zkey";
return fs.promises
.writeFile(r1csPath, artifacts.snarkjs.program)
.then(() => {
return snarkjs.zKey
.newZKey(r1csPath, "./tests/powersOfTau5_0000.ptau", zkeyPath)
.then(() => {});
});
});
}
if (options.curve === "bn128" && ["g16", "gm17"].includes(options.scheme)) {
it("export verifier", () => {
assert.doesNotThrow(() => {
let verifier = provider.exportSolidityVerifier(keypair.vk);
assert.ok(verifier.includes("contract"));
});
});
}
it("generate proof", () => {
assert.doesNotThrow(() => {
proof = provider.generateProof(
artifacts.program,
computationResult.witness,
keypair.pk
);
assert.ok(proof !== undefined);
assert.equal(proof.inputs.length, 2);
});
});
if (options.scheme === "g16" && options.curve == "bn128") {
it("generate snarkjs proof", () => {
// write witness to fs
let witnessPath = tmpFolder + "/witness.wtns";
let zkeyPath = tmpFolder + "/key.zkey";
return fs.promises
.writeFile(witnessPath, computationResult.snarkjs.witness)
.then(() => {
return snarkjs.groth16.prove(zkeyPath, witnessPath);
});
});
}
it("verify", () => {
assert.doesNotThrow(() => {
assert(provider.verify(keypair.vk, proof) === true);
});
});
};
for (const scheme of ["g16", "gm17", "marlin"]) {
describe(scheme, () => {
for (const curve of ["bn128", "bls12_381", "bls12_377", "bw6_761"]) {
describe(curve, () => runWithOptions({ scheme, curve }));
}
});
}
const testRunner = (rootPath, testPath, test) => {
let entryPoint;
@ -241,7 +306,7 @@ describe("tests", function () {
const test = require(file.path);
const testName = file.path.substring(testsPath.length + 1);
if (!ignoreList.some(v => testName.startsWith(v)))
if (!ignoreList.some((v) => testName.startsWith(v)))
describe(testName, () => testRunner(rootPath, file.path, test));
});
});

View file

@ -35,12 +35,16 @@ module.exports = (dep) => {
const defaultProvider = {
compile: (source, compileOptions = {}) => {
const {
var {
curve = "bn128",
location = "main.zok",
resolveCallback = () => null,
config = {},
snarkjs = false,
} = compileOptions;
config = { snarkjs, ...config };
const callback = (currentLocation, importLocation) => {
return (
resolveFromStdlib(currentLocation, importLocation) ||
@ -48,15 +52,36 @@ module.exports = (dep) => {
);
};
const ptr = zokrates.compile(source, location, callback, config, curve);
return {
program: ptr.program(),
abi: ptr.abi(),
};
return Object.assign(
{
program: ptr.program(),
abi: ptr.abi(),
},
snarkjs ? { snarkjs: { program: ptr.snarkjs_program() } } : {}
);
},
computeWitness: (input, args) => {
computeWitness: (input, args, computeOptions = {}) => {
const { program, abi } =
input instanceof Uint8Array ? { program: input, abi: null } : input;
return zokrates.compute_witness(program, abi, JSON.stringify(args));
const { snarkjs = false } = computeOptions;
const ptr = zokrates.compute_witness(program, abi, JSON.stringify(args), {
snarkjs: snarkjs,
});
return Object.assign(
{
witness: ptr.witness(),
output: ptr.output(),
},
snarkjs
? {
snarkjs: {
witness: ptr.snarkjs_witness(),
},
}
: {}
);
},
setup: (program, options) => {
return zokrates.setup(program, options);
@ -79,8 +104,8 @@ module.exports = (dep) => {
utils: {
formatProof: (proof) => {
return zokrates.format_proof(proof);
}
}
},
},
};
const withOptions = (options) => {
@ -91,8 +116,8 @@ module.exports = (dep) => {
...compileOptions,
curve: options.curve,
}),
computeWitness: (artifacts, args) =>
defaultProvider.computeWitness(artifacts, args),
computeWitness: (artifacts, args, computeOptions = {}) =>
defaultProvider.computeWitness(artifacts, args, computeOptions),
setup: (program) => defaultProvider.setup(program, options),
universalSetup: (size) =>
defaultProvider.universalSetup(options.curve, size),
@ -105,11 +130,11 @@ module.exports = (dep) => {
defaultProvider.exportSolidityVerifier(vk),
utils: {
formatProof: (proof) => defaultProvider.utils.formatProof(proof),
}
},
};
};
return {
...withOptions({ scheme: "g16", curve: "bn128" }),
};
};
};

View file

@ -1,6 +1,6 @@
use primitive_types::U256;
pub use revm::Return;
use revm::{AccountInfo, InMemoryDB, Log, TransactOut, TransactTo, EVM};
use revm::{AccountInfo, Database, InMemoryDB, Log, TransactOut, TransactTo, EVM};
use crate::{address::Address, Error, EvmTestError};
@ -60,31 +60,18 @@ impl Evm {
contract: Vec<u8>,
deployer: &Address,
) -> Result<CreateContractResult, Error> {
match self
.vm
.db()
.unwrap()
.cache()
.get_key_value(deployer.as_ref())
{
Some(_) => {
self.vm.env.tx.caller = *deployer.as_ref();
self.vm.env.tx.transact_to = TransactTo::create();
self.vm.env.tx.data = contract.into();
let (_, tx_out, gas, _) = self.vm.transact_commit();
let contract_address = match tx_out {
TransactOut::Create(_, Some(addr)) => Ok(Address(addr)),
_ => Err(Box::new(EvmTestError("create contract failed".to_string()))),
}?;
Ok(CreateContractResult {
addr: contract_address,
gas,
})
}
None => Err(Box::new(EvmTestError(
"deployer address not found".to_string(),
))),
}
self.vm.env.tx.caller = *deployer.as_ref();
self.vm.env.tx.transact_to = TransactTo::create();
self.vm.env.tx.data = contract.into();
let (_, tx_out, gas, _) = self.vm.transact_commit();
let contract_address = match tx_out {
TransactOut::Create(_, Some(addr)) => Ok(Address(addr)),
_ => Err(Box::new(EvmTestError("create contract failed".to_string()))),
}?;
Ok(CreateContractResult {
addr: contract_address,
gas,
})
}
pub fn create_account(&mut self, address: &Address, balance: impl Into<U256>) {
@ -97,27 +84,17 @@ impl Evm {
address: &Address,
balance: impl Into<U256>,
) -> Result<(), Error> {
let mut acc = self
.vm
.db()
.unwrap()
.cache()
.get(address.as_ref())
.ok_or_else(|| Box::new(EvmTestError("account address not found".to_string())))?
.clone();
let mut acc = self.vm.db().unwrap().basic(*address.as_ref()).clone();
acc.balance = balance.into();
self.vm.db().unwrap().insert_cache(*address.as_ref(), acc);
Ok(())
}
pub fn balance_of(&mut self, address: &Address) -> U256 {
match self.vm.db().unwrap().cache().get(address.as_ref()) {
Some(acc) => acc.balance,
None => 0.into(),
}
self.vm.db().unwrap().basic(*address.as_ref()).balance
}
pub fn get_account(&mut self, address: &Address) -> Option<AccountInfo> {
self.vm.db().unwrap().cache().get(address.as_ref()).cloned()
pub fn get_account(&mut self, address: &Address) -> AccountInfo {
self.vm.db().unwrap().basic(*address.as_ref())
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_stdlib"
version = "0.2.8"
version = "0.2.9"
authors = ["Stefan Deml <stefandeml@gmail.com>", "schaeff <thibaut@schaeff.fr>"]
edition = "2018"