1
0
Fork 0
mirror of synced 2025-09-23 04:08:33 +00:00

Merge branch 'develop' into backend-opt

This commit is contained in:
dark64 2023-03-31 12:41:44 +02:00
commit 5257d309c5
49 changed files with 635 additions and 236 deletions

View file

@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
https://github.com/Zokrates/ZoKrates/compare/latest...develop
## [0.8.5] - 2023-03-28
### Release
- https://github.com/Zokrates/ZoKrates/releases/tag/0.8.5 <!-- markdown-link-check-disable-line -->
### Changes
- Reduce memory usage and runtime by refactoring the reducer (ssa, propagation, unrolling and inlining) (#1283, @schaeff)
- Fix `radix-path` help message on `mpc init` subcommand (#1280, @dark64)
- Fix a potential crash in `zokrates-js` due to inefficient serialization of a setup keypair (#1277, @dark64)
- Show help when running `zokrates mpc` (#1275, @dark64)
## [0.8.4] - 2023-01-31
### Release

13
Cargo.lock generated
View file

@ -2906,7 +2906,7 @@ dependencies = [
[[package]]
name = "zokrates_analysis"
version = "0.1.0"
version = "0.1.1"
dependencies = [
"cfg-if 0.1.10",
"csv",
@ -2956,9 +2956,10 @@ dependencies = [
[[package]]
name = "zokrates_ast"
version = "0.1.4"
version = "0.1.5"
dependencies = [
"ark-bls12-377",
"byteorder",
"cfg-if 0.1.10",
"derivative",
"num-bigint 0.2.6",
@ -3003,7 +3004,7 @@ dependencies = [
[[package]]
name = "zokrates_cli"
version = "0.8.4"
version = "0.8.5"
dependencies = [
"assert_cli",
"blake2 0.8.1",
@ -3063,7 +3064,7 @@ dependencies = [
[[package]]
name = "zokrates_core"
version = "0.7.3"
version = "0.7.4"
dependencies = [
"cfg-if 0.1.10",
"csv",
@ -3146,7 +3147,7 @@ dependencies = [
[[package]]
name = "zokrates_interpreter"
version = "0.1.2"
version = "0.1.3"
dependencies = [
"ark-bls12-377",
"num",
@ -3162,7 +3163,7 @@ dependencies = [
[[package]]
name = "zokrates_js"
version = "1.1.5"
version = "1.1.6"
dependencies = [
"console_error_panic_hook",
"getrandom",

View file

@ -0,0 +1 @@
Reduce compiled program size by deduplicating assembly solvers

View file

@ -1 +0,0 @@
Show help when running `zokrates mpc`

View file

@ -1 +0,0 @@
Fix a potential crash in `zokrates-js` due to inefficient serialization of a setup keypair

View file

@ -1 +0,0 @@
Fix `radix-path` help message on `mpc init` subcommand

View file

@ -1 +0,0 @@
Reduce memory usage and runtime by refactoring the reducer (ssa, propagation, unrolling and inlining)

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_analysis"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
[features]

View file

@ -32,7 +32,7 @@ impl<'ast, T: Field> Folder<'ast, T> for Propagator<T> {
match e {
FlatExpression::Number(n) => FlatExpression::Number(n),
FlatExpression::Identifier(id) => match self.constants.get(&id) {
Some(c) => FlatExpression::Number(c.clone()),
Some(c) => FlatExpression::Number(*c),
None => FlatExpression::Identifier(id),
},
FlatExpression::Add(box e1, box e2) => {

View file

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::convert::{TryFrom, TryInto};
use std::marker::PhantomData;
use zokrates_ast::typed::types::{ConcreteArrayType, IntoType, UBitwidth};
@ -481,7 +481,7 @@ impl<'ast, T: Field> Flattener<T> {
// }
#[derive(Default)]
pub struct ArgumentFinder<'ast, T> {
pub identifiers: HashMap<zir::Identifier<'ast>, zir::Type>,
pub identifiers: BTreeMap<zir::Identifier<'ast>, zir::Type>,
_phantom: PhantomData<T>,
}

View file

@ -508,7 +508,7 @@ impl<'ast, T: Field> ResultFolder<'ast, T> for Propagator<'ast, T> {
.unwrap()
{
FieldElementExpression::Number(num) => {
let mut acc = num.clone();
let mut acc = num;
let mut res = vec![];
for i in (0..bit_width as usize).rev() {

View file

@ -170,7 +170,7 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
left_max
.checked_add(&range_max.clone())
.map(|max| (false, true, max))
.unwrap_or_else(|| (true, true, range_max.clone() + range_max))
.unwrap_or_else(|| (true, true, range_max + range_max))
})
});
@ -223,7 +223,7 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
left_max
.checked_add(&target_offset)
.map(|max| (false, true, max))
.unwrap_or_else(|| (true, true, range_max.clone() + target_offset))
.unwrap_or_else(|| (true, true, range_max + target_offset))
} else {
left_max
.checked_add(&offset)
@ -294,7 +294,7 @@ impl<'ast, T: Field> Folder<'ast, T> for UintOptimizer<'ast, T> {
left_max
.checked_mul(&range_max.clone())
.map(|max| (false, true, max))
.unwrap_or_else(|| (true, true, range_max.clone() * range_max))
.unwrap_or_else(|| (true, true, range_max * range_max))
})
});

View file

@ -127,6 +127,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();
@ -155,6 +156,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();

View file

@ -126,6 +126,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();
@ -154,6 +155,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();

View file

@ -135,7 +135,7 @@ impl<'a, T: Field + ArkFieldExtensions, I: IntoIterator<Item = Statement<'a, T>>
self.program
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_ark())
.map(|v| v.into_ark())
.collect()
}
}

View file

@ -410,6 +410,7 @@ mod tests {
),
Statement::constraint(Variable::new(1), Variable::public(0)),
],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();
@ -448,6 +449,7 @@ mod tests {
),
Statement::constraint(Variable::new(1), Variable::public(0)),
],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_ast"
version = "0.1.4"
version = "0.1.5"
edition = "2021"
[features]
@ -9,6 +9,7 @@ bellman = ["zokrates_field/bellman", "pairing_ce", "zokrates_embed/bellman"]
ark = ["ark-bls12-377", "zokrates_embed/ark"]
[dependencies]
byteorder = "1.4.3"
zokrates_pest_ast = { version = "0.3.0", path = "../zokrates_pest_ast" }
cfg-if = "0.1"
zokrates_field = { version = "0.5", path = "../zokrates_field", default-features = false }

View file

@ -10,6 +10,6 @@ pub use self::embed::FlatEmbed;
pub use self::error::RuntimeError;
pub use self::metadata::SourceMetadata;
pub use self::parameter::Parameter;
pub use self::solvers::Solver;
pub use self::solvers::{RefCall, Solver};
pub use self::variable::Variable;
pub use format_string::FormatString;

View file

@ -2,6 +2,12 @@ use crate::zir::ZirFunction;
use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)]
pub struct RefCall {
pub index: usize,
pub argument_count: usize,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, Hash, Eq)]
pub enum Solver<'ast, T> {
ConditionEq,
@ -14,6 +20,7 @@ pub enum Solver<'ast, T> {
EuclideanDiv,
#[serde(borrow)]
Zir(ZirFunction<'ast, T>),
Ref(RefCall),
#[cfg(feature = "bellman")]
Sha256Round,
#[cfg(feature = "ark")]
@ -32,6 +39,7 @@ impl<'ast, T> fmt::Display for Solver<'ast, T> {
Solver::ShaCh => write!(f, "ShaCh"),
Solver::EuclideanDiv => write!(f, "EuclideanDiv"),
Solver::Zir(_) => write!(f, "Zir(..)"),
Solver::Ref(call) => write!(f, "Ref@{}({})", call.index, call.argument_count),
#[cfg(feature = "bellman")]
Solver::Sha256Round => write!(f, "Sha256Round"),
#[cfg(feature = "ark")]
@ -52,6 +60,7 @@ impl<'ast, T> Solver<'ast, T> {
Solver::ShaCh => (3, 1),
Solver::EuclideanDiv => (2, 2),
Solver::Zir(f) => (f.arguments.len(), 1),
Solver::Ref(c) => (c.argument_count, 1),
#[cfg(feature = "bellman")]
Solver::Sha256Round => (768, 26935),
#[cfg(feature = "ark")]

View file

@ -36,7 +36,7 @@ pub fn flat_expression_from_variable_summands<T: Field>(v: &[(T, usize)]) -> Fla
match v.len() {
0 => FlatExpression::Number(T::zero()),
1 => {
let (val, var) = v[0].clone();
let (val, var) = v[0];
FlatExpression::Mult(
box FlatExpression::Number(val),
box FlatExpression::Identifier(Variable::new(var)),

View file

@ -14,6 +14,7 @@ impl<'ast, T: Field, I: IntoIterator<Item = Statement<'ast, T>>> ProgIterator<'a
.statements
.into_iter()
.flat_map(|s| Cleaner::default().fold_statement(s)),
solvers: self.solvers,
}
}
}

View file

@ -165,8 +165,8 @@ impl<T: Field> LinComb<T> {
match acc.entry(val) {
Entry::Occupied(o) => {
// if the new value is non zero, update, else remove the term entirely
if o.get().clone() + coeff.clone() != T::zero() {
*o.into_mut() = o.get().clone() + coeff;
if *o.get() + coeff != T::zero() {
*o.into_mut() = *o.get() + coeff;
} else {
o.remove();
}
@ -258,7 +258,7 @@ impl<T: Field> Mul<&T> for LinComb<T> {
LinComb(
self.0
.into_iter()
.map(|(var, coeff)| (var, coeff * scalar.clone()))
.map(|(var, coeff)| (var, coeff * scalar))
.collect(),
)
}

View file

@ -50,6 +50,7 @@ pub fn fold_program<'ast, T: Field, F: Folder<'ast, T>>(
.flat_map(|s| f.fold_statement(s))
.collect(),
return_count: p.return_count,
solvers: p.solvers,
}
}

View file

@ -24,6 +24,7 @@ pub fn from_flat<'ast, T: Field, I: IntoIterator<Item = FlatStatement<'ast, T>>>
statements: flat_prog_iterator.statements.into_iter().map(Into::into),
arguments: flat_prog_iterator.arguments,
return_count: flat_prog_iterator.return_count,
solvers: vec![],
}
}

View file

@ -14,12 +14,13 @@ pub mod folder;
pub mod from_flat;
mod serialize;
pub mod smtlib2;
mod solver_indexer;
pub mod visitor;
mod witness;
pub use self::expression::QuadComb;
pub use self::expression::{CanonicalLinComb, LinComb};
pub use self::serialize::ProgEnum;
pub use self::serialize::{ProgEnum, ProgHeader};
pub use crate::common::Parameter;
pub use crate::common::RuntimeError;
pub use crate::common::Solver;
@ -130,14 +131,22 @@ pub struct ProgIterator<'ast, T, I: IntoIterator<Item = Statement<'ast, T>>> {
pub arguments: Vec<Parameter>,
pub return_count: usize,
pub statements: I,
#[serde(borrow)]
pub solvers: Vec<Solver<'ast, T>>,
}
impl<'ast, T, I: IntoIterator<Item = Statement<'ast, T>>> ProgIterator<'ast, T, I> {
pub fn new(arguments: Vec<Parameter>, statements: I, return_count: usize) -> Self {
pub fn new(
arguments: Vec<Parameter>,
statements: I,
return_count: usize,
solvers: Vec<Solver<'ast, T>>,
) -> Self {
Self {
arguments,
return_count,
statements,
solvers,
}
}
@ -146,6 +155,7 @@ impl<'ast, T, I: IntoIterator<Item = Statement<'ast, T>>> ProgIterator<'ast, T,
statements: self.statements.into_iter().collect::<Vec<_>>(),
arguments: self.arguments,
return_count: self.return_count,
solvers: self.solvers,
}
}
@ -171,7 +181,7 @@ impl<'ast, T: Field, I: IntoIterator<Item = Statement<'ast, T>>> ProgIterator<'a
self.arguments
.iter()
.filter(|p| !p.private)
.map(|p| witness.0.get(&p.id).unwrap().clone())
.map(|p| *witness.0.get(&p.id).unwrap())
.chain(witness.return_values())
.collect()
}
@ -192,6 +202,7 @@ impl<'ast, T> Prog<'ast, T> {
statements: self.statements.into_iter(),
arguments: self.arguments,
return_count: self.return_count,
solvers: self.solvers,
}
}
}

View file

@ -1,14 +1,16 @@
use crate::ir::check::UnconstrainedVariableDetector;
use crate::ir::{check::UnconstrainedVariableDetector, solver_indexer::SolverIndexer};
use super::{ProgIterator, Statement};
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use serde::Deserialize;
use serde_cbor::{self, StreamDeserializer};
use std::io::{Read, Write};
use std::io::{Read, Seek, Write};
use zokrates_field::*;
type DynamicError = Box<dyn std::error::Error>;
const ZOKRATES_MAGIC: &[u8; 4] = &[0x5a, 0x4f, 0x4b, 0];
const ZOKRATES_VERSION_2: &[u8; 4] = &[0, 0, 0, 2];
const FILE_VERSION: &[u8; 4] = &[3, 0, 0, 0];
#[derive(PartialEq, Eq, Debug)]
pub enum ProgEnum<
@ -58,33 +60,189 @@ impl<
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[repr(u32)]
pub enum SectionType {
Parameters = 1,
Constraints = 2,
Solvers = 3,
}
impl TryFrom<u32> for SectionType {
type Error = String;
fn try_from(value: u32) -> Result<Self, Self::Error> {
match value {
1 => Ok(SectionType::Parameters),
2 => Ok(SectionType::Constraints),
3 => Ok(SectionType::Solvers),
_ => Err("invalid section type".to_string()),
}
}
}
#[derive(Debug, Clone)]
pub struct Section {
pub ty: SectionType,
pub offset: u64,
pub length: u64,
}
impl Section {
pub fn new(ty: SectionType) -> Self {
Self {
ty,
offset: 0,
length: 0,
}
}
pub fn set_offset(&mut self, offset: u64) {
self.offset = offset;
}
pub fn set_length(&mut self, length: u64) {
self.length = length;
}
}
#[derive(Debug, Clone)]
pub struct ProgHeader {
pub magic: [u8; 4],
pub version: [u8; 4],
pub curve_id: [u8; 4],
pub constraint_count: u32,
pub return_count: u32,
pub sections: [Section; 3],
}
impl ProgHeader {
pub fn write<W: Write>(&self, mut w: W) -> std::io::Result<()> {
w.write_all(&self.magic)?;
w.write_all(&self.version)?;
w.write_all(&self.curve_id)?;
w.write_u32::<LittleEndian>(self.constraint_count)?;
w.write_u32::<LittleEndian>(self.return_count)?;
for s in &self.sections {
w.write_u32::<LittleEndian>(s.ty as u32)?;
w.write_u64::<LittleEndian>(s.offset)?;
w.write_u64::<LittleEndian>(s.length)?;
}
Ok(())
}
pub fn read<R: Read>(mut r: R) -> std::io::Result<Self> {
let mut magic = [0; 4];
r.read_exact(&mut magic)?;
let mut version = [0; 4];
r.read_exact(&mut version)?;
let mut curve_id = [0; 4];
r.read_exact(&mut curve_id)?;
let constraint_count = r.read_u32::<LittleEndian>()?;
let return_count = r.read_u32::<LittleEndian>()?;
let parameters = Self::read_section(r.by_ref())?;
let constraints = Self::read_section(r.by_ref())?;
let solvers = Self::read_section(r.by_ref())?;
Ok(ProgHeader {
magic,
version,
curve_id,
constraint_count,
return_count,
sections: [parameters, constraints, solvers],
})
}
fn read_section<R: Read>(mut r: R) -> std::io::Result<Section> {
let id = r.read_u32::<LittleEndian>()?;
let mut section = Section::new(
SectionType::try_from(id)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?,
);
section.set_offset(r.read_u64::<LittleEndian>()?);
section.set_length(r.read_u64::<LittleEndian>()?);
Ok(section)
}
}
impl<'ast, T: Field, I: IntoIterator<Item = Statement<'ast, T>>> ProgIterator<'ast, T, I> {
/// serialize a program iterator, returning the number of constraints serialized
/// Note that we only return constraints, not other statements such as directives
pub fn serialize<W: Write>(self, mut w: W) -> Result<usize, DynamicError> {
pub fn serialize<W: Write + Seek>(self, mut w: W) -> Result<usize, DynamicError> {
use super::folder::Folder;
w.write_all(ZOKRATES_MAGIC)?;
w.write_all(ZOKRATES_VERSION_2)?;
w.write_all(&T::id())?;
// reserve bytes for the header
w.write_all(&[0u8; std::mem::size_of::<ProgHeader>()])?;
serde_cbor::to_writer(&mut w, &self.arguments)?;
serde_cbor::to_writer(&mut w, &self.return_count)?;
// write parameters section
let parameters = {
let mut section = Section::new(SectionType::Parameters);
section.set_offset(w.stream_position()?);
serde_cbor::to_writer(&mut w, &self.arguments)?;
section.set_length(w.stream_position()? - section.offset);
section
};
let mut solver_indexer: SolverIndexer<'ast, T> = SolverIndexer::default();
let mut unconstrained_variable_detector = UnconstrainedVariableDetector::new(&self);
let mut count: usize = 0;
let statements = self.statements.into_iter();
// write constraints section
let constraints = {
let mut section = Section::new(SectionType::Constraints);
section.set_offset(w.stream_position()?);
let mut count = 0;
for s in statements {
if matches!(s, Statement::Constraint(..)) {
count += 1;
let statements = self.statements.into_iter();
for s in statements {
if matches!(s, Statement::Constraint(..)) {
count += 1;
}
let s: Vec<Statement<T>> = solver_indexer
.fold_statement(s)
.into_iter()
.flat_map(|s| unconstrained_variable_detector.fold_statement(s))
.collect();
for s in s {
serde_cbor::to_writer(&mut w, &s)?;
}
}
let s = unconstrained_variable_detector.fold_statement(s);
for s in s {
serde_cbor::to_writer(&mut w, &s)?;
}
}
section.set_length(w.stream_position()? - section.offset);
section
};
// write solvers section
let solvers = {
let mut section = Section::new(SectionType::Solvers);
section.set_offset(w.stream_position()?);
serde_cbor::to_writer(&mut w, &solver_indexer.solvers)?;
section.set_length(w.stream_position()? - section.offset);
section
};
let header = ProgHeader {
magic: *ZOKRATES_MAGIC,
version: *FILE_VERSION,
curve_id: T::id(),
constraint_count: count as u32,
return_count: self.return_count as u32,
sections: [parameters, constraints, solvers],
};
// rewind to write the header
w.rewind()?;
header.write(&mut w)?;
unconstrained_variable_detector
.finalize()
@ -103,11 +261,11 @@ impl<'de, R: serde_cbor::de::Read<'de>, T: serde::Deserialize<'de>> Iterator
type Item = T;
fn next(&mut self) -> Option<T> {
self.s.next().transpose().unwrap()
self.s.next().and_then(|v| v.ok())
}
}
impl<'de, R: Read>
impl<'de, R: Read + Seek>
ProgEnum<
'de,
UnwrappedStreamDeserializer<'de, serde_cbor::de::IoRead<R>, Statement<'de, Bls12_381Field>>,
@ -116,125 +274,75 @@ impl<'de, R: Read>
UnwrappedStreamDeserializer<'de, serde_cbor::de::IoRead<R>, Statement<'de, Bw6_761Field>>,
>
{
fn read<T: Field>(
mut r: R,
header: &ProgHeader,
) -> ProgIterator<
'de,
T,
UnwrappedStreamDeserializer<'de, serde_cbor::de::IoRead<R>, Statement<'de, T>>,
> {
let parameters = {
let section = &header.sections[0];
r.seek(std::io::SeekFrom::Start(section.offset)).unwrap();
let mut p = serde_cbor::Deserializer::from_reader(r.by_ref());
Vec::deserialize(&mut p)
.map_err(|_| String::from("Cannot read parameters"))
.unwrap()
};
let solvers = {
let section = &header.sections[2];
r.seek(std::io::SeekFrom::Start(section.offset)).unwrap();
let mut p = serde_cbor::Deserializer::from_reader(r.by_ref());
Vec::deserialize(&mut p)
.map_err(|_| String::from("Cannot read solvers"))
.unwrap()
};
let statements_deserializer = {
let section = &header.sections[1];
r.seek(std::io::SeekFrom::Start(section.offset)).unwrap();
let p = serde_cbor::Deserializer::from_reader(r);
let s = p.into_iter::<Statement<T>>();
UnwrappedStreamDeserializer { s }
};
ProgIterator::new(
parameters,
statements_deserializer,
header.return_count as usize,
solvers,
)
}
pub fn deserialize(mut r: R) -> Result<Self, String> {
let header = ProgHeader::read(&mut r).map_err(|_| String::from("Invalid header"))?;
// Check the magic number, `ZOK`
let mut magic = [0; 4];
r.read_exact(&mut magic)
.map_err(|_| String::from("Cannot read magic number"))?;
if &header.magic != ZOKRATES_MAGIC {
return Err("Invalid magic number".to_string());
}
if &magic == ZOKRATES_MAGIC {
// Check the version, 2
let mut version = [0; 4];
r.read_exact(&mut version)
.map_err(|_| String::from("Cannot read version"))?;
// Check the file version
if &header.version != FILE_VERSION {
return Err("Invalid file version".to_string());
}
if &version == ZOKRATES_VERSION_2 {
// Check the curve identifier, deserializing accordingly
let mut curve = [0; 4];
r.read_exact(&mut curve)
.map_err(|_| String::from("Cannot read curve identifier"))?;
use serde::de::Deserializer;
let mut p = serde_cbor::Deserializer::from_reader(r);
struct ArgumentsVisitor;
impl<'de> serde::de::Visitor<'de> for ArgumentsVisitor {
type Value = Vec<super::Parameter>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("seq of flat param")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut res = vec![];
while let Some(e) = seq.next_element().unwrap() {
res.push(e);
}
Ok(res)
}
}
let arguments = p.deserialize_seq(ArgumentsVisitor).unwrap();
struct ReturnCountVisitor;
impl<'de> serde::de::Visitor<'de> for ReturnCountVisitor {
type Value = usize;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("usize")
}
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v as usize)
}
fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v as usize)
}
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v as usize)
}
}
let return_count = p.deserialize_u32(ReturnCountVisitor).unwrap();
match curve {
m if m == Bls12_381Field::id() => {
let s = p.into_iter::<Statement<Bls12_381Field>>();
Ok(ProgEnum::Bls12_381Program(ProgIterator::new(
arguments,
UnwrappedStreamDeserializer { s },
return_count,
)))
}
m if m == Bn128Field::id() => {
let s = p.into_iter::<Statement<Bn128Field>>();
Ok(ProgEnum::Bn128Program(ProgIterator::new(
arguments,
UnwrappedStreamDeserializer { s },
return_count,
)))
}
m if m == Bls12_377Field::id() => {
let s = p.into_iter::<Statement<Bls12_377Field>>();
Ok(ProgEnum::Bls12_377Program(ProgIterator::new(
arguments,
UnwrappedStreamDeserializer { s },
return_count,
)))
}
m if m == Bw6_761Field::id() => {
let s = p.into_iter::<Statement<Bw6_761Field>>();
Ok(ProgEnum::Bw6_761Program(ProgIterator::new(
arguments,
UnwrappedStreamDeserializer { s },
return_count,
)))
}
_ => Err(String::from("Unknown curve identifier")),
}
} else {
Err(String::from("Unknown version"))
match header.curve_id {
m if m == Bls12_381Field::id() => {
Ok(ProgEnum::Bls12_381Program(Self::read(r, &header)))
}
} else {
Err(String::from("Wrong magic number"))
m if m == Bn128Field::id() => Ok(ProgEnum::Bn128Program(Self::read(r, &header))),
m if m == Bls12_377Field::id() => {
Ok(ProgEnum::Bls12_377Program(Self::read(r, &header)))
}
m if m == Bw6_761Field::id() => Ok(ProgEnum::Bw6_761Program(Self::read(r, &header))),
_ => Err(String::from("Unknown curve identifier")),
}
}
}

View file

@ -0,0 +1,54 @@
use crate::common::RefCall;
use crate::ir::folder::Folder;
use crate::ir::Directive;
use crate::ir::Solver;
use crate::zir::ZirFunction;
use std::collections::hash_map::DefaultHasher;
use std::collections::hash_map::Entry;
use std::collections::HashMap;
use zokrates_field::Field;
type Hash = u64;
fn hash<T: Field>(f: &ZirFunction<T>) -> Hash {
use std::hash::Hash;
use std::hash::Hasher;
let mut hasher = DefaultHasher::new();
f.hash(&mut hasher);
hasher.finish()
}
#[derive(Debug, Default)]
pub struct SolverIndexer<'ast, T> {
pub solvers: Vec<Solver<'ast, T>>,
pub index_map: HashMap<Hash, usize>,
}
impl<'ast, T: Field> Folder<'ast, T> for SolverIndexer<'ast, T> {
fn fold_directive(&mut self, d: Directive<'ast, T>) -> Directive<'ast, T> {
match d.solver {
Solver::Zir(f) => {
let argument_count = f.arguments.len();
let h = hash(&f);
let index = match self.index_map.entry(h) {
Entry::Occupied(v) => *v.get(),
Entry::Vacant(entry) => {
let index = self.solvers.len();
entry.insert(index);
self.solvers.push(Solver::Zir(f));
index
}
};
Directive {
inputs: d.inputs,
outputs: d.outputs,
solver: Solver::Ref(RefCall {
index,
argument_count,
}),
}
}
_ => d,
}
}
}

View file

@ -0,0 +1,94 @@
use super::{Folder, Identifier, Parameter, Variable, ZirAssignee};
use std::collections::HashMap;
use zokrates_field::Field;
#[derive(Default)]
pub struct ZirCanonicalizer<'ast> {
identifier_map: HashMap<Identifier<'ast>, usize>,
}
impl<'ast, T: Field> Folder<'ast, T> for ZirCanonicalizer<'ast> {
fn fold_parameter(&mut self, p: Parameter<'ast>) -> Parameter<'ast> {
let new_id = self.identifier_map.len();
self.identifier_map.insert(p.id.id.clone(), new_id);
Parameter {
id: Variable::with_id_and_type(Identifier::internal(new_id), p.id._type),
..p
}
}
fn fold_assignee(&mut self, a: ZirAssignee<'ast>) -> ZirAssignee<'ast> {
let new_id = self.identifier_map.len();
self.identifier_map.insert(a.id.clone(), new_id);
ZirAssignee::with_id_and_type(Identifier::internal(new_id), a._type)
}
fn fold_name(&mut self, n: Identifier<'ast>) -> Identifier<'ast> {
match self.identifier_map.get(&n) {
Some(v) => Identifier::internal(*v),
None => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use crate::zir::{
FieldElementExpression, IdentifierExpression, Signature, Type, ZirAssignee, ZirFunction,
ZirStatement,
};
use super::*;
use zokrates_field::Bn128Field;
#[test]
fn canonicalize() {
let func = ZirFunction::<Bn128Field> {
arguments: vec![Parameter {
id: Variable::field_element("a"),
private: true,
}],
statements: vec![
ZirStatement::Definition(
ZirAssignee::field_element("b"),
FieldElementExpression::Identifier(IdentifierExpression::new("a".into()))
.into(),
),
ZirStatement::Return(vec![FieldElementExpression::Identifier(
IdentifierExpression::new("b".into()),
)
.into()]),
],
signature: Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
};
let mut canonicalizer = ZirCanonicalizer::default();
let result = canonicalizer.fold_function(func);
let expected = ZirFunction::<Bn128Field> {
arguments: vec![Parameter {
id: Variable::field_element(Identifier::internal(0usize)),
private: true,
}],
statements: vec![
ZirStatement::Definition(
ZirAssignee::field_element(Identifier::internal(1usize)),
FieldElementExpression::Identifier(IdentifierExpression::new(
Identifier::internal(0usize),
))
.into(),
),
ZirStatement::Return(vec![FieldElementExpression::Identifier(
IdentifierExpression::new(Identifier::internal(1usize)),
)
.into()]),
],
signature: Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
};
assert_eq!(result, expected);
}
}

View file

@ -4,13 +4,14 @@ use std::fmt;
use crate::typed::Identifier as CoreIdentifier;
#[derive(Debug, PartialEq, Clone, Hash, Eq, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Identifier<'ast> {
#[serde(borrow)]
Source(SourceIdentifier<'ast>),
Internal(usize),
}
#[derive(Debug, PartialEq, Clone, Hash, Eq, Serialize, Deserialize)]
#[derive(Debug, PartialEq, Clone, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum SourceIdentifier<'ast> {
#[serde(borrow)]
Basic(CoreIdentifier<'ast>),
@ -30,10 +31,17 @@ impl<'ast> fmt::Display for SourceIdentifier<'ast> {
}
}
impl<'ast> Identifier<'ast> {
pub fn internal<T: Into<usize>>(id: T) -> Self {
Identifier::Internal(id.into())
}
}
impl<'ast> fmt::Display for Identifier<'ast> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Identifier::Source(s) => write!(f, "{}", s),
Identifier::Internal(i) => write!(f, "i{}", i),
}
}
}

View file

@ -43,7 +43,7 @@ impl<'ast, T: Field> std::ops::Sub for LinQuadComb<'ast, T> {
linear: {
let mut l = self.linear;
other.linear.iter_mut().for_each(|(c, _)| {
*c = T::zero() - &*c;
*c = T::zero() - *c;
});
l.append(&mut other.linear);
l
@ -51,7 +51,7 @@ impl<'ast, T: Field> std::ops::Sub for LinQuadComb<'ast, T> {
quadratic: {
let mut q = self.quadratic;
other.quadratic.iter_mut().for_each(|(c, _, _)| {
*c = T::zero() - &*c;
*c = T::zero() - *c;
});
q.append(&mut other.quadratic);
q
@ -68,18 +68,18 @@ impl<'ast, T: Field> LinQuadComb<'ast, T> {
}
Ok(Self {
constant: self.constant.clone() * rhs.constant.clone(),
constant: self.constant * rhs.constant,
linear: {
// lin0 * const1 + lin1 * const0
self.linear
.clone()
.into_iter()
.map(|(c, i)| (c * rhs.constant.clone(), i))
.map(|(c, i)| (c * rhs.constant, i))
.chain(
rhs.linear
.clone()
.into_iter()
.map(|(c, i)| (c * self.constant.clone(), i)),
.map(|(c, i)| (c * self.constant, i)),
)
.collect()
},
@ -87,16 +87,16 @@ impl<'ast, T: Field> LinQuadComb<'ast, T> {
// quad0 * const1 + quad1 * const0 + lin0 * lin1
self.quadratic
.into_iter()
.map(|(c, i0, i1)| (c * rhs.constant.clone(), i0, i1))
.map(|(c, i0, i1)| (c * rhs.constant, i0, i1))
.chain(
rhs.quadratic
.into_iter()
.map(|(c, i0, i1)| (c * self.constant.clone(), i0, i1)),
.map(|(c, i0, i1)| (c * self.constant, i0, i1)),
)
.chain(self.linear.iter().flat_map(|(cl, l)| {
rhs.linear
.iter()
.map(|(cr, r)| (cl.clone() * cr.clone(), l.clone(), r.clone()))
.map(|(cr, r)| (*cl * *cr, l.clone(), r.clone()))
}))
.collect()
},

View file

@ -1,3 +1,4 @@
pub mod canonicalizer;
pub mod folder;
mod from_typed;
mod identifier;

View file

@ -219,6 +219,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let rng = &mut StdRng::from_entropy();

View file

@ -179,7 +179,7 @@ impl<'a, T: BellmanFieldExtensions + Field, I: IntoIterator<Item = Statement<'a,
self.program
.public_inputs_values(self.witness.as_ref().unwrap())
.iter()
.map(|v| v.clone().into_bellman())
.map(|v| v.into_bellman())
.collect()
}
@ -266,6 +266,7 @@ mod tests {
arguments: vec![Parameter::private(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let interpreter = Interpreter::default();
@ -287,6 +288,7 @@ mod tests {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::public(0))],
solvers: vec![],
};
let interpreter = Interpreter::default();
@ -308,6 +310,7 @@ mod tests {
arguments: vec![],
return_count: 1,
statements: vec![Statement::constraint(Variable::one(), Variable::public(0))],
solvers: vec![],
};
let interpreter = Interpreter::default();
@ -340,6 +343,7 @@ mod tests {
Variable::public(1),
),
],
solvers: vec![],
};
let interpreter = Interpreter::default();
@ -363,6 +367,7 @@ mod tests {
LinComb::from(Variable::new(42)) + LinComb::one(),
Variable::public(0),
)],
solvers: vec![],
};
let interpreter = Interpreter::default();
@ -390,6 +395,7 @@ mod tests {
LinComb::from(Variable::new(42)) + LinComb::from(Variable::new(51)),
Variable::public(0),
)],
solvers: vec![],
};
let interpreter = Interpreter::default();

View file

@ -44,6 +44,7 @@ mod tests {
None,
),
],
solvers: vec![],
};
let mut r1cs = vec![];

View file

@ -296,6 +296,7 @@ mod tests {
Variable::public(0).into(),
None,
)],
solvers: vec![],
};
let mut buf = Vec::new();
@ -365,6 +366,7 @@ mod tests {
None,
),
],
solvers: vec![],
};
let mut buf = Vec::new();

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_cli"
version = "0.8.4"
version = "0.8.5"
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"

View file

@ -11,8 +11,8 @@ mod utils;
use self::utils::flat_expression_from_bits;
use zokrates_ast::zir::{
ConditionalExpression, SelectExpression, ShouldReduce, UMetadata, ZirAssemblyStatement,
ZirExpressionList,
canonicalizer::ZirCanonicalizer, ConditionalExpression, Folder, SelectExpression, ShouldReduce,
UMetadata, ZirAssemblyStatement, ZirExpressionList,
};
use zokrates_interpreter::Interpreter;
@ -1885,7 +1885,7 @@ impl<'ast, T: Field> Flattener<'ast, T> {
// constants do not require directives
if let Some(FlatExpression::Number(ref x)) = e.field {
let bits: Vec<_> = Interpreter::execute_solver(&Solver::bits(to), &[x.clone()])
let bits: Vec<_> = Interpreter::execute_solver(&Solver::bits(to), &[*x], &[])
.unwrap()
.into_iter()
.map(FlatExpression::Number)
@ -2237,10 +2237,15 @@ impl<'ast, T: Field> Flattener<'ast, T> {
.cloned()
.map(|p| self.layout.get(&p.id.id).cloned().unwrap().into())
.collect();
let outputs: Vec<Variable> = assignees
.into_iter()
.map(|assignee| self.use_variable(&assignee))
.collect();
let mut canonicalizer = ZirCanonicalizer::default();
let function = canonicalizer.fold_function(function);
let directive = FlatDirective::new(outputs, Solver::Zir(function), inputs);
statements_flattened.push_back(FlatStatement::Directive(directive));
}

View file

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

View file

@ -39,14 +39,18 @@ impl<'ast, T: Field> Folder<'ast, T> for DuplicateOptimizer {
}
fn fold_statement(&mut self, s: Statement<'ast, T>) -> Vec<Statement<'ast, T>> {
let hashed = hash(&s);
let result = match self.seen.get(&hashed) {
Some(_) => vec![],
None => vec![s],
};
self.seen.insert(hashed);
result
match s {
Statement::Block(s) => s.into_iter().flat_map(|s| self.fold_statement(s)).collect(),
s => {
let hashed = hash(&s);
let result = match self.seen.get(&hashed) {
Some(_) => vec![],
None => vec![s],
};
self.seen.insert(hashed);
result
}
}
}
}
@ -77,6 +81,7 @@ mod tests {
],
return_count: 0,
arguments: vec![],
solvers: vec![],
};
let expected = p.clone();
@ -113,6 +118,7 @@ mod tests {
],
return_count: 0,
arguments: vec![],
solvers: vec![],
};
let expected = Prog {
@ -128,6 +134,7 @@ mod tests {
],
return_count: 0,
arguments: vec![],
solvers: vec![],
};
assert_eq!(

View file

@ -54,6 +54,7 @@ pub fn optimize<'ast, T: Field, I: IntoIterator<Item = Statement<'ast, T>>>(
.flat_map(move |s| directive_optimizer.fold_statement(s))
.flat_map(move |s| duplicate_optimizer.fold_statement(s)),
return_count: p.return_count,
solvers: p.solvers,
};
log::debug!("Done");

View file

@ -146,7 +146,7 @@ impl<T: Field> RedefinitionOptimizer<T> {
// unwrap inputs to their constant value
let inputs: Vec<_> = inputs.into_iter().map(|i| i.unwrap()).collect();
// run the solver
let outputs = Interpreter::execute_solver(&d.solver, &inputs).unwrap();
let outputs = Interpreter::execute_solver(&d.solver, &inputs, &[]).unwrap();
assert_eq!(outputs.len(), d.outputs.len());
// insert the results in the substitution
@ -256,12 +256,14 @@ mod tests {
Statement::definition(out, y),
],
return_count: 1,
solvers: vec![],
};
let optimized: Prog<Bn128Field> = Prog {
arguments: vec![x],
statements: vec![Statement::definition(out, x.id)],
return_count: 1,
solvers: vec![],
};
let mut optimizer = RedefinitionOptimizer::init(&p);
@ -280,6 +282,7 @@ mod tests {
arguments: vec![x],
statements: vec![Statement::definition(one, x.id)],
return_count: 1,
solvers: vec![],
};
let optimized = p.clone();
@ -316,6 +319,7 @@ mod tests {
Statement::definition(out, z),
],
return_count: 1,
solvers: vec![],
};
let optimized: Prog<Bn128Field> = Prog {
@ -325,6 +329,7 @@ mod tests {
Statement::definition(out, x.id),
],
return_count: 1,
solvers: vec![],
};
let mut optimizer = RedefinitionOptimizer::init(&p);
@ -365,6 +370,7 @@ mod tests {
Statement::definition(out_1, w),
],
return_count: 2,
solvers: vec![],
};
let optimized: Prog<Bn128Field> = Prog {
@ -374,6 +380,7 @@ mod tests {
Statement::definition(out_1, Bn128Field::from(1)),
],
return_count: 2,
solvers: vec![],
};
let mut optimizer = RedefinitionOptimizer::init(&p);
@ -422,6 +429,7 @@ mod tests {
Statement::definition(r, LinComb::from(a) + LinComb::from(b) + LinComb::from(c)),
],
return_count: 1,
solvers: vec![],
};
let expected: Prog<Bn128Field> = Prog {
@ -442,6 +450,7 @@ mod tests {
),
],
return_count: 1,
solvers: vec![],
};
let mut optimizer = RedefinitionOptimizer::init(&p);
@ -479,6 +488,7 @@ mod tests {
Statement::definition(z, LinComb::from(x.id)),
],
return_count: 0,
solvers: vec![],
};
let optimized = p.clone();
@ -507,6 +517,7 @@ mod tests {
Statement::constraint(x.id, Bn128Field::from(2)),
],
return_count: 1,
solvers: vec![],
};
let optimized = p.clone();

View file

@ -10,7 +10,9 @@ use std::ops::{Add, Div, Mul, Sub};
const _PRIME: u8 = 7;
#[derive(Default, Debug, Hash, Clone, PartialOrd, Ord, Serialize, Deserialize, PartialEq, Eq)]
#[derive(
Default, Debug, Hash, Clone, Copy, PartialOrd, Ord, Serialize, Deserialize, PartialEq, Eq,
)]
pub struct FieldPrime {
v: u8,
}

View file

@ -70,6 +70,7 @@ pub trait Field:
+ Zero
+ One
+ Clone
+ Copy
+ PartialEq
+ Eq
+ Hash
@ -153,7 +154,7 @@ mod prime_field {
type Fr = <$v as ark_ec::PairingEngine>::Fr;
#[derive(PartialEq, PartialOrd, Clone, Eq, Ord, Hash)]
#[derive(PartialEq, PartialOrd, Clone, Copy, Eq, Ord, Hash)]
pub struct FieldPrime {
v: Fr,
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_interpreter"
version = "0.1.2"
version = "0.1.3"
edition = "2021"
[features]

View file

@ -50,7 +50,7 @@ impl Interpreter {
witness.insert(Variable::one(), T::one());
for (arg, value) in program.arguments.iter().zip(inputs.iter()) {
witness.insert(arg.id, value.clone());
witness.insert(arg.id, *value);
}
for statement in program.statements.into_iter() {
@ -83,12 +83,12 @@ impl Interpreter {
inputs.pop().unwrap(),
))
}
_ => Self::execute_solver(&d.solver, &inputs),
_ => Self::execute_solver(&d.solver, &inputs, &program.solvers),
}
.map_err(Error::Solver)?;
for (i, o) in d.outputs.iter().enumerate() {
witness.insert(*o, res[i].clone());
witness.insert(*o, res[i]);
}
}
Statement::Log(l, expressions) => {
@ -164,7 +164,15 @@ impl Interpreter {
pub fn execute_solver<'ast, T: Field>(
solver: &Solver<'ast, T>,
inputs: &[T],
solvers: &[Solver<'ast, T>],
) -> Result<Vec<T>, String> {
let solver = match solver {
Solver::Ref(call) => solvers
.get(call.index)
.ok_or_else(|| format!("Could not get solver at index {}", call.index))?,
s => s,
};
let (expected_input_count, expected_output_count) = solver.get_signature();
assert_eq!(inputs.len(), expected_input_count);
@ -180,7 +188,7 @@ impl Interpreter {
.map(|(a, v)| match &a.id._type {
zir::Type::FieldElement => Ok((
a.id.id.clone(),
zokrates_ast::zir::FieldElementExpression::Number(v.clone()).into(),
zokrates_ast::zir::FieldElementExpression::Number(*v).into(),
)),
zir::Type::Boolean => match v {
v if *v == T::from(0) => Ok((
@ -256,41 +264,38 @@ impl Interpreter {
.collect()
}
Solver::Xor => {
let x = inputs[0].clone();
let y = inputs[1].clone();
let x = inputs[0];
let y = inputs[1];
vec![x.clone() + y.clone() - T::from(2) * x * y]
vec![x + y - T::from(2) * x * y]
}
Solver::Or => {
let x = inputs[0].clone();
let y = inputs[1].clone();
let x = inputs[0];
let y = inputs[1];
vec![x.clone() + y.clone() - x * y]
vec![x + y - x * y]
}
// res = b * c - (2b * c - b - c) * (a)
Solver::ShaAndXorAndXorAnd => {
let a = inputs[0].clone();
let b = inputs[1].clone();
let c = inputs[2].clone();
vec![b.clone() * c.clone() - (T::from(2) * b.clone() * c.clone() - b - c) * a]
let a = inputs[0];
let b = inputs[1];
let c = inputs[2];
vec![b * c - (T::from(2) * b * c - b - c) * a]
}
// res = a(b - c) + c
Solver::ShaCh => {
let a = inputs[0].clone();
let b = inputs[1].clone();
let c = inputs[2].clone();
vec![a * (b - c.clone()) + c]
let a = inputs[0];
let b = inputs[1];
let c = inputs[2];
vec![a * (b - c) + c]
}
Solver::Div => vec![inputs[0]
.clone()
.checked_div(&inputs[1])
.unwrap_or_else(T::one)],
Solver::Div => vec![inputs[0].checked_div(&inputs[1]).unwrap_or_else(T::one)],
Solver::EuclideanDiv => {
use num::CheckedDiv;
let n = inputs[0].clone().to_biguint();
let d = inputs[1].clone().to_biguint();
let n = inputs[0].to_biguint();
let d = inputs[1].to_biguint();
let q = n.checked_div(&d).unwrap_or_else(|| 0u32.into());
let r = n - d * &q;
@ -334,6 +339,7 @@ impl Interpreter {
&inputs[*n + 8usize..],
)
}
_ => unreachable!("unexpected solver"),
};
assert_eq!(res.len(), expected_output_count);
@ -354,14 +360,11 @@ pub enum Error {
}
fn evaluate_lin<T: Field>(w: &Witness<T>, l: &LinComb<T>) -> Result<T, EvaluationError> {
l.0.iter()
.map(|(var, mult)| {
w.0.get(var)
.map(|v| v.clone() * mult)
.ok_or(EvaluationError)
}) // get each term
.collect::<Result<Vec<_>, _>>() // fail if any term isn't found
.map(|v| v.iter().fold(T::from(0), |acc, t| acc + t)) // return the sum
l.0.iter().try_fold(T::from(0), |acc, (var, mult)| {
w.0.get(var)
.map(|v| acc + (*v * mult))
.ok_or(EvaluationError) // fail if any term isn't found
})
}
pub fn evaluate_quad<T: Field>(w: &Witness<T>, q: &QuadComb<T>) -> Result<T, EvaluationError> {
@ -434,6 +437,7 @@ mod tests {
.iter()
.map(|&i| Bn128Field::from(i))
.collect::<Vec<_>>(),
&[],
)
.unwrap();
let res: Vec<Bn128Field> = vec![0, 1].iter().map(|&i| Bn128Field::from(i)).collect();
@ -450,6 +454,7 @@ mod tests {
.iter()
.map(|&i| Bn128Field::from(i))
.collect::<Vec<_>>(),
&[],
)
.unwrap();
let res: Vec<Bn128Field> = vec![1, 1].iter().map(|&i| Bn128Field::from(i)).collect();
@ -460,9 +465,12 @@ mod tests {
#[test]
fn bits_of_one() {
let inputs = vec![Bn128Field::from(1)];
let res =
Interpreter::execute_solver(&Solver::Bits(Bn128Field::get_required_bits()), &inputs)
.unwrap();
let res = Interpreter::execute_solver(
&Solver::Bits(Bn128Field::get_required_bits()),
&inputs,
&[],
)
.unwrap();
assert_eq!(res[253], Bn128Field::from(1));
for r in &res[0..253] {
assert_eq!(*r, Bn128Field::from(0));
@ -472,9 +480,12 @@ mod tests {
#[test]
fn bits_of_42() {
let inputs = vec![Bn128Field::from(42)];
let res =
Interpreter::execute_solver(&Solver::Bits(Bn128Field::get_required_bits()), &inputs)
.unwrap();
let res = Interpreter::execute_solver(
&Solver::Bits(Bn128Field::get_required_bits()),
&inputs,
&[],
)
.unwrap();
assert_eq!(res[253], Bn128Field::from(0));
assert_eq!(res[252], Bn128Field::from(1));
assert_eq!(res[251], Bn128Field::from(0));
@ -487,11 +498,53 @@ mod tests {
#[test]
fn five_hundred_bits_of_1() {
let inputs = vec![Bn128Field::from(1)];
let res = Interpreter::execute_solver(&Solver::Bits(500), &inputs).unwrap();
let res = Interpreter::execute_solver(&Solver::Bits(500), &inputs, &[]).unwrap();
let mut expected = vec![Bn128Field::from(0); 500];
expected[499] = Bn128Field::from(1);
assert_eq!(res, expected);
}
#[test]
fn solver_ref() {
use zir::{
types::{Signature, Type},
FieldElementExpression, Identifier, IdentifierExpression, Parameter, Variable,
ZirFunction, ZirStatement,
};
use zokrates_ast::common::RefCall;
let id = IdentifierExpression::new(Identifier::internal(0usize));
// (field i0) -> i0 * i0
let solvers = vec![Solver::Zir(ZirFunction {
arguments: vec![Parameter {
id: Variable::field_element(id.id.clone()),
private: true,
}],
statements: vec![ZirStatement::Return(vec![FieldElementExpression::Mult(
Box::new(FieldElementExpression::Identifier(id.clone())),
Box::new(FieldElementExpression::Identifier(id.clone())),
)
.into()])],
signature: Signature::new()
.inputs(vec![Type::FieldElement])
.outputs(vec![Type::FieldElement]),
})];
let inputs = vec![Bn128Field::from(2)];
let res = Interpreter::execute_solver(
&Solver::Ref(RefCall {
index: 0,
argument_count: 1,
}),
&inputs,
&solvers,
)
.unwrap();
let expected = vec![Bn128Field::from(4)];
assert_eq!(res, expected);
}
}

View file

@ -1,6 +1,6 @@
[package]
name = "zokrates_js"
version = "1.1.5"
version = "1.1.6"
authors = ["Darko Macesic"]
edition = "2018"

View file

@ -1,6 +1,6 @@
{
"name": "zokrates-js",
"version": "1.1.5",
"version": "1.1.6",
"module": "index.js",
"main": "index-node.js",
"description": "JavaScript bindings for ZoKrates",

View file

@ -520,7 +520,8 @@ pub fn compute_witness(
config: JsValue,
log_callback: &js_sys::Function,
) -> Result<ComputationResult, JsValue> {
let prog = ir::ProgEnum::deserialize(program)
let cursor = Cursor::new(program);
let prog = ir::ProgEnum::deserialize(cursor)
.map_err(|err| JsValue::from_str(&err))?
.collect();
match prog {
@ -582,7 +583,8 @@ pub fn setup(program: &[u8], entropy: JsValue, options: JsValue) -> Result<Keypa
)
.map_err(|e| JsValue::from_str(&e))?;
let prog = ir::ProgEnum::deserialize(program)
let cursor = Cursor::new(program);
let prog = ir::ProgEnum::deserialize(cursor)
.map_err(|err| JsValue::from_str(&err))?
.collect();
@ -644,7 +646,8 @@ pub fn setup_with_srs(srs: &[u8], program: &[u8], options: JsValue) -> Result<Ke
)
.map_err(|e| JsValue::from_str(&e))?;
let prog = ir::ProgEnum::deserialize(program)
let cursor = Cursor::new(program);
let prog = ir::ProgEnum::deserialize(cursor)
.map_err(|err| JsValue::from_str(&err))?
.collect();
@ -721,7 +724,8 @@ pub fn generate_proof(
)
.map_err(|e| JsValue::from_str(&e))?;
let prog = ir::ProgEnum::deserialize(program)
let cursor = Cursor::new(program);
let prog = ir::ProgEnum::deserialize(cursor)
.map_err(|err| JsValue::from_str(&err))?
.collect();

View file

@ -20,6 +20,7 @@ fn generate_proof() {
arguments: vec![Parameter::public(Variable::new(0))],
return_count: 1,
statements: vec![Statement::constraint(Variable::new(0), Variable::new(0))],
solvers: vec![],
};
let interpreter = Interpreter::default();