wip
This commit is contained in:
parent
5595cf16ca
commit
f68d887038
9 changed files with 552 additions and 161 deletions
69
Cargo.lock
generated
69
Cargo.lock
generated
|
@ -210,6 +210,15 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
dependencies = [
|
||||
"nodrop",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.5.2"
|
||||
|
@ -324,6 +333,17 @@ dependencies = [
|
|||
"opaque-debug",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2-rfc_bellman_edition"
|
||||
version = "0.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdc60350286c7c3db13b98e91dbe5c8b6830a6821bc20af5b0c310ce94d74915"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12",
|
||||
"byteorder",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "0.5.11"
|
||||
|
@ -331,7 +351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"arrayvec 0.5.2",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
|
@ -559,6 +579,12 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-mac"
|
||||
version = "0.7.0"
|
||||
|
@ -1077,6 +1103,12 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
|
||||
[[package]]
|
||||
name = "num"
|
||||
version = "0.1.42"
|
||||
|
@ -1563,6 +1595,23 @@ dependencies = [
|
|||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sapling-crypto_ce"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c4ff5309ec3e4bd800ad4ab3f71e9b76e9ea81c9f0eda6efa16008afbe440b3"
|
||||
dependencies = [
|
||||
"bellman_ce",
|
||||
"blake2-rfc_bellman_edition",
|
||||
"byteorder",
|
||||
"digest",
|
||||
"rand 0.4.6",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"sha2",
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.0"
|
||||
|
@ -1782,6 +1831,15 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiny-keccak"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.1.0"
|
||||
|
@ -2156,6 +2214,7 @@ dependencies = [
|
|||
"typed-arena",
|
||||
"wasm-bindgen-test",
|
||||
"zokrates_common",
|
||||
"zokrates_embed",
|
||||
"zokrates_field",
|
||||
"zokrates_pest_ast",
|
||||
]
|
||||
|
@ -2168,6 +2227,14 @@ dependencies = [
|
|||
"zokrates_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zokrates_embed"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"bellman_ce",
|
||||
"sapling-crypto_ce",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zokrates_field"
|
||||
version = "0.3.7"
|
||||
|
|
|
@ -6,6 +6,7 @@ members = [
|
|||
"zokrates_cli",
|
||||
"zokrates_fs_resolver",
|
||||
"zokrates_stdlib",
|
||||
"zokrates_embed",
|
||||
"zokrates_abi",
|
||||
"zokrates_test",
|
||||
"zokrates_core_test",
|
||||
|
|
|
@ -31,6 +31,7 @@ regex = "0.2"
|
|||
zokrates_field = { version = "0.3.0", path = "../zokrates_field", default-features = false }
|
||||
zokrates_pest_ast = { version = "0.1.0", path = "../zokrates_pest_ast" }
|
||||
zokrates_common = { path = "../zokrates_common" }
|
||||
zokrates_embed = { path = "../zokrates_embed" }
|
||||
rand_0_4 = { version = "0.4", package = "rand" }
|
||||
rand_0_7 = { version = "0.7", package = "rand" }
|
||||
csv = "1"
|
||||
|
|
|
@ -10,8 +10,7 @@ mod utils;
|
|||
use self::utils::flat_expression_from_bits;
|
||||
|
||||
use crate::flat_absy::*;
|
||||
use crate::ir;
|
||||
use crate::solvers::Solver;
|
||||
use crate::solvers::{Executable, Solver};
|
||||
use crate::zir::types::{FunctionIdentifier, FunctionKey, Signature, Type, UBitwidth};
|
||||
use crate::zir::*;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
@ -1652,8 +1651,9 @@ impl<'ast, T: Field> Flattener<'ast, T> {
|
|||
// constants do not require directives
|
||||
match e.field {
|
||||
Some(FlatExpression::Number(ref x)) => {
|
||||
let bits: Vec<_> = ir::Interpreter::default()
|
||||
.execute_solver(&Solver::bits(to), &vec![x.clone()])
|
||||
let solver = Solver::bits(to);
|
||||
let bits: Vec<_> = solver
|
||||
.execute(&vec![x.clone()])
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|x| FlatExpression::Number(x))
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::flat_absy::flat_variable::FlatVariable;
|
||||
use crate::ir::Directive;
|
||||
use crate::ir::{LinComb, Prog, QuadComb, Statement, Witness};
|
||||
use crate::solvers::Solver;
|
||||
use crate::solvers::{Executable, Solver};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
|
@ -76,7 +76,7 @@ impl Interpreter {
|
|||
.iter()
|
||||
.map(|i| i.evaluate(&witness).unwrap())
|
||||
.collect();
|
||||
match self.execute_solver(&d.solver, &inputs) {
|
||||
match d.solver.execute(&inputs) {
|
||||
Ok(res) => {
|
||||
for (i, o) in d.outputs.iter().enumerate() {
|
||||
witness.insert(o.clone(), res[i].clone());
|
||||
|
@ -134,79 +134,6 @@ impl Interpreter {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute_solver<T: Field>(&self, s: &Solver, inputs: &Vec<T>) -> Result<Vec<T>, String> {
|
||||
let (expected_input_count, expected_output_count) = s.get_signature();
|
||||
assert!(inputs.len() == expected_input_count);
|
||||
|
||||
let res = match s {
|
||||
Solver::ConditionEq => match inputs[0].is_zero() {
|
||||
true => vec![T::zero(), T::one()],
|
||||
false => vec![
|
||||
T::one(),
|
||||
T::one().checked_div(&inputs[0]).unwrap_or(T::one()),
|
||||
],
|
||||
},
|
||||
Solver::Bits(bit_width) => {
|
||||
let mut num = inputs[0].clone();
|
||||
let mut res = vec![];
|
||||
|
||||
for i in (0..*bit_width).rev() {
|
||||
if T::from(2).pow(i) <= num {
|
||||
num = num - T::from(2).pow(i);
|
||||
res.push(T::one());
|
||||
} else {
|
||||
res.push(T::zero());
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
Solver::Xor => {
|
||||
let x = inputs[0].clone();
|
||||
let y = inputs[1].clone();
|
||||
|
||||
vec![x.clone() + y.clone() - T::from(2) * x * y]
|
||||
}
|
||||
Solver::Or => {
|
||||
let x = inputs[0].clone();
|
||||
let y = inputs[1].clone();
|
||||
|
||||
vec![x.clone() + y.clone() - 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]
|
||||
}
|
||||
// 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]
|
||||
}
|
||||
Solver::Div => vec![inputs[0]
|
||||
.clone()
|
||||
.checked_div(&inputs[1])
|
||||
.unwrap_or(T::one())],
|
||||
Solver::EuclideanDiv => {
|
||||
use num::CheckedDiv;
|
||||
|
||||
let n = inputs[0].clone().to_biguint();
|
||||
let d = inputs[1].clone().to_biguint();
|
||||
|
||||
let q = n.checked_div(&d).unwrap_or(0u32.into());
|
||||
let r = n - d * &q;
|
||||
vec![T::try_from(q).unwrap(), T::try_from(r).unwrap()]
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(res.len(), expected_output_count);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Field> LinComb<T> {
|
||||
|
@ -265,77 +192,3 @@ impl fmt::Debug for Error {
|
|||
write!(f, "{}", self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use zokrates_field::Bn128Field;
|
||||
|
||||
mod eq_condition {
|
||||
|
||||
// Wanted: (Y = (X != 0) ? 1 : 0)
|
||||
// # Y = if X == 0 then 0 else 1 fi
|
||||
// # M = if X == 0 then 1 else 1/X fi
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let cond_eq = Solver::ConditionEq;
|
||||
let inputs = vec![0];
|
||||
let interpreter = Interpreter::default();
|
||||
let r = interpreter
|
||||
.execute_solver(
|
||||
&cond_eq,
|
||||
&inputs.iter().map(|&i| Bn128Field::from(i)).collect(),
|
||||
)
|
||||
.unwrap();
|
||||
let res: Vec<Bn128Field> = vec![0, 1].iter().map(|&i| Bn128Field::from(i)).collect();
|
||||
assert_eq!(r, &res[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_non_eq() {
|
||||
let cond_eq = Solver::ConditionEq;
|
||||
let inputs = vec![1];
|
||||
let interpreter = Interpreter::default();
|
||||
let r = interpreter
|
||||
.execute_solver(
|
||||
&cond_eq,
|
||||
&inputs.iter().map(|&i| Bn128Field::from(i)).collect(),
|
||||
)
|
||||
.unwrap();
|
||||
let res: Vec<Bn128Field> = vec![1, 1].iter().map(|&i| Bn128Field::from(i)).collect();
|
||||
assert_eq!(r, &res[..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bits_of_one() {
|
||||
let inputs = vec![Bn128Field::from(1)];
|
||||
let interpreter = Interpreter::default();
|
||||
let res = interpreter
|
||||
.execute_solver(&Solver::Bits(Bn128Field::get_required_bits()), &inputs)
|
||||
.unwrap();
|
||||
assert_eq!(res[253], Bn128Field::from(1));
|
||||
for i in 0..253 {
|
||||
assert_eq!(res[i], Bn128Field::from(0));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bits_of_42() {
|
||||
let inputs = vec![Bn128Field::from(42)];
|
||||
let interpreter = Interpreter::default();
|
||||
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));
|
||||
assert_eq!(res[250], Bn128Field::from(1));
|
||||
assert_eq!(res[249], Bn128Field::from(0));
|
||||
assert_eq!(res[248], Bn128Field::from(1));
|
||||
assert_eq!(res[247], Bn128Field::from(0));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,7 @@ use crate::flat_absy::flat_variable::FlatVariable;
|
|||
use crate::ir::folder::{fold_function, Folder};
|
||||
use crate::ir::LinComb;
|
||||
use crate::ir::*;
|
||||
use crate::solvers::Executable;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use zokrates_field::Field;
|
||||
|
||||
|
@ -146,10 +147,8 @@ impl<T: Field> Folder<T> for RedefinitionOptimizer<T> {
|
|||
true => {
|
||||
// unwrap inputs to their constant value
|
||||
let inputs = inputs.into_iter().map(|i| i.unwrap()).collect();
|
||||
// run the interpereter
|
||||
let outputs = Interpreter::default()
|
||||
.execute_solver(&d.solver, &inputs)
|
||||
.unwrap();
|
||||
// run the solver
|
||||
let outputs = d.solver.execute(&inputs).unwrap();
|
||||
|
||||
assert_eq!(outputs.len(), d.outputs.len());
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ pub enum Solver {
|
|||
ShaAndXorAndXorAnd,
|
||||
ShaCh,
|
||||
EuclideanDiv,
|
||||
Sha256Round,
|
||||
}
|
||||
|
||||
impl fmt::Display for Solver {
|
||||
|
@ -31,6 +32,7 @@ impl Solver {
|
|||
Solver::ShaAndXorAndXorAnd => (3, 1),
|
||||
Solver::ShaCh => (3, 1),
|
||||
Solver::EuclideanDiv => (2, 2),
|
||||
Solver::Sha256Round => (768, 26935),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -41,10 +43,145 @@ impl Solver {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Executable<T: Field>: Signed {
|
||||
pub trait Executable<T> {
|
||||
fn execute(&self, inputs: &Vec<T>) -> Result<Vec<T>, String>;
|
||||
}
|
||||
|
||||
pub trait Signed {
|
||||
fn get_signature(&self) -> (usize, usize);
|
||||
impl<T: Field> Executable<T> for Solver {
|
||||
fn execute(&self, inputs: &Vec<T>) -> Result<Vec<T>, String> {
|
||||
let (expected_input_count, expected_output_count) = self.get_signature();
|
||||
assert_eq!(inputs.len(), expected_input_count);
|
||||
|
||||
let res = match self {
|
||||
Solver::ConditionEq => match inputs[0].is_zero() {
|
||||
true => vec![T::zero(), T::one()],
|
||||
false => vec![
|
||||
T::one(),
|
||||
T::one().checked_div(&inputs[0]).unwrap_or(T::one()),
|
||||
],
|
||||
},
|
||||
Solver::Bits(bit_width) => {
|
||||
let mut num = inputs[0].clone();
|
||||
let mut res = vec![];
|
||||
|
||||
for i in (0..*bit_width).rev() {
|
||||
if T::from(2).pow(i) <= num {
|
||||
num = num - T::from(2).pow(i);
|
||||
res.push(T::one());
|
||||
} else {
|
||||
res.push(T::zero());
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
Solver::Xor => {
|
||||
let x = inputs[0].clone();
|
||||
let y = inputs[1].clone();
|
||||
|
||||
vec![x.clone() + y.clone() - T::from(2) * x * y]
|
||||
}
|
||||
Solver::Or => {
|
||||
let x = inputs[0].clone();
|
||||
let y = inputs[1].clone();
|
||||
|
||||
vec![x.clone() + y.clone() - 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]
|
||||
}
|
||||
// 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]
|
||||
}
|
||||
Solver::Div => vec![inputs[0]
|
||||
.clone()
|
||||
.checked_div(&inputs[1])
|
||||
.unwrap_or(T::one())],
|
||||
Solver::EuclideanDiv => {
|
||||
use num::CheckedDiv;
|
||||
|
||||
let n = inputs[0].clone().to_biguint();
|
||||
let d = inputs[1].clone().to_biguint();
|
||||
|
||||
let q = n.checked_div(&d).unwrap_or(0u32.into());
|
||||
let r = n - d * &q;
|
||||
vec![T::try_from(q).unwrap(), T::try_from(r).unwrap()]
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
assert_eq!(res.len(), expected_output_count);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use zokrates_field::Bn128Field;
|
||||
|
||||
mod eq_condition {
|
||||
|
||||
// Wanted: (Y = (X != 0) ? 1 : 0)
|
||||
// # Y = if X == 0 then 0 else 1 fi
|
||||
// # M = if X == 0 then 1 else 1/X fi
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn execute() {
|
||||
let cond_eq = Solver::ConditionEq;
|
||||
let inputs = vec![0];
|
||||
let r = cond_eq
|
||||
.execute(&inputs.iter().map(|&i| Bn128Field::from(i)).collect())
|
||||
.unwrap();
|
||||
let res: Vec<Bn128Field> = vec![0, 1].iter().map(|&i| Bn128Field::from(i)).collect();
|
||||
assert_eq!(r, &res[..]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_non_eq() {
|
||||
let cond_eq = Solver::ConditionEq;
|
||||
let inputs = vec![1];
|
||||
let r = cond_eq
|
||||
.execute(&inputs.iter().map(|&i| Bn128Field::from(i)).collect())
|
||||
.unwrap();
|
||||
let res: Vec<Bn128Field> = vec![1, 1].iter().map(|&i| Bn128Field::from(i)).collect();
|
||||
assert_eq!(r, &res[..]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bits_of_one() {
|
||||
let bits = Solver::Bits(Bn128Field::get_required_bits());
|
||||
let inputs = vec![Bn128Field::from(1)];
|
||||
let res = bits.execute(&inputs).unwrap();
|
||||
assert_eq!(res[253], Bn128Field::from(1));
|
||||
for i in 0..253 {
|
||||
assert_eq!(res[i], Bn128Field::from(0));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bits_of_42() {
|
||||
let bits = Solver::Bits(Bn128Field::get_required_bits());
|
||||
let inputs = vec![Bn128Field::from(42)];
|
||||
let res = bits.execute(&inputs).unwrap();
|
||||
|
||||
assert_eq!(res[253], Bn128Field::from(0));
|
||||
assert_eq!(res[252], Bn128Field::from(1));
|
||||
assert_eq!(res[251], Bn128Field::from(0));
|
||||
assert_eq!(res[250], Bn128Field::from(1));
|
||||
assert_eq!(res[249], Bn128Field::from(0));
|
||||
assert_eq!(res[248], Bn128Field::from(1));
|
||||
assert_eq!(res[247], Bn128Field::from(0));
|
||||
}
|
||||
}
|
||||
|
|
14
zokrates_embed/Cargo.toml
Normal file
14
zokrates_embed/Cargo.toml
Normal file
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "zokrates_embed"
|
||||
version = "0.1.1"
|
||||
authors = ["schaeff <thibaut@schaeff.fr>"]
|
||||
edition = "2018"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
wasm = ["bellman_ce/wasm", "sapling-crypto_ce/wasm"]
|
||||
multicore = ["bellman_ce/multicore", "sapling-crypto_ce/multicore"]
|
||||
|
||||
[dependencies]
|
||||
bellman_ce = { version = "^0.3", default-features = false }
|
||||
sapling-crypto_ce = { version = "^0.1", default-features = false }
|
319
zokrates_embed/src/lib.rs
Normal file
319
zokrates_embed/src/lib.rs
Normal file
|
@ -0,0 +1,319 @@
|
|||
extern crate sapling_crypto_ce as sapling_crypto;
|
||||
use sapling_crypto::bellman;
|
||||
|
||||
use bellman::{
|
||||
pairing::{ff::Field, Engine},
|
||||
ConstraintSystem, Index, LinearCombination, SynthesisError, Variable,
|
||||
};
|
||||
use sapling_crypto::circuit::{
|
||||
boolean::{AllocatedBit, Boolean},
|
||||
sha256::sha256_compression_function,
|
||||
uint32::UInt32,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BellmanR1CS<E: Engine> {
|
||||
pub aux_count: usize,
|
||||
pub constraints: Vec<BellmanConstraint<E>>,
|
||||
}
|
||||
|
||||
impl<E: Engine> BellmanR1CS<E> {
|
||||
pub fn new() -> Self {
|
||||
BellmanR1CS {
|
||||
aux_count: 0,
|
||||
constraints: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BellmanWitness<E: Engine> {
|
||||
pub values: Vec<E::Fr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct BellmanConstraint<E: Engine> {
|
||||
pub a: Vec<(usize, E::Fr)>,
|
||||
pub b: Vec<(usize, E::Fr)>,
|
||||
pub c: Vec<(usize, E::Fr)>,
|
||||
}
|
||||
|
||||
fn sha256_round<E: Engine, CS: ConstraintSystem<E>>(
|
||||
mut cs: CS,
|
||||
input: &Vec<Option<E::Fr>>,
|
||||
current_hash: &Vec<Option<E::Fr>>,
|
||||
) -> Result<(Vec<usize>, Vec<usize>, Vec<usize>), SynthesisError> {
|
||||
// Allocate bits for `input`
|
||||
let input_bits = input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, i)| {
|
||||
AllocatedBit::alloc::<E, _>(
|
||||
&mut cs.namespace(|| format!("input_{}", index)),
|
||||
Some(*i == Some(<E::Fr as Field>::one())),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Define Booleans whose values are the defined bits
|
||||
let input = input_bits
|
||||
.iter()
|
||||
.map(|i| Boolean::Is(i.clone()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Allocate bits for `current_hash`
|
||||
let current_hash_bits = current_hash
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, i)| {
|
||||
AllocatedBit::alloc::<E, _>(
|
||||
&mut cs.namespace(|| format!("current_hash_{}", index)),
|
||||
Some(*i == Some(<E::Fr as Field>::one())),
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Define Booleans whose values are the defined bits
|
||||
let current_hash = current_hash_bits
|
||||
.chunks(32)
|
||||
.map(|chunk| {
|
||||
UInt32::from_bits_be(
|
||||
&chunk
|
||||
.into_iter()
|
||||
.map(|i| Boolean::Is(i.clone()))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Apply the compression function, returning the 8 bytes of outputs
|
||||
let res = sha256_compression_function::<E, _>(&mut cs, &input, ¤t_hash).unwrap();
|
||||
|
||||
// Extract the 256 bits of output out of the 8 bytes
|
||||
let output_bits = res
|
||||
.into_iter()
|
||||
.flat_map(|u| u.into_bits_be())
|
||||
.map(|b| b.get_variable().unwrap().clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Return indices of `input`, `current_hash` and `output` in the CS
|
||||
Ok((
|
||||
input_bits
|
||||
.into_iter()
|
||||
.map(|b| var_to_index(b.get_variable()))
|
||||
.collect(),
|
||||
current_hash_bits
|
||||
.into_iter()
|
||||
.map(|b| var_to_index(b.get_variable()))
|
||||
.collect(),
|
||||
output_bits
|
||||
.into_iter()
|
||||
.map(|b| var_to_index(b.get_variable()))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
impl<E: Engine> ConstraintSystem<E> for BellmanWitness<E> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
let index = self.values.len();
|
||||
let var = Variable::new_unchecked(Index::Aux(index));
|
||||
self.values.push(f().unwrap());
|
||||
Ok(var)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
unreachable!("Bellman helpers are not allowed to allocate public variables")
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, _: LA, _: LB, _: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, _: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Engine> ConstraintSystem<E> for BellmanR1CS<E> {
|
||||
type Root = Self;
|
||||
|
||||
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
// we don't care about the value as we're only generating the CS
|
||||
let index = self.aux_count;
|
||||
let var = Variable::new_unchecked(Index::Aux(index));
|
||||
self.aux_count += 1;
|
||||
Ok(var)
|
||||
}
|
||||
|
||||
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
|
||||
where
|
||||
F: FnOnce() -> Result<E::Fr, SynthesisError>,
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
{
|
||||
unreachable!("Bellman helpers are not allowed to allocate public variables")
|
||||
}
|
||||
|
||||
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
|
||||
where
|
||||
A: FnOnce() -> AR,
|
||||
AR: Into<String>,
|
||||
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
|
||||
{
|
||||
let a = a(LinearCombination::zero());
|
||||
let b = b(LinearCombination::zero());
|
||||
let c = c(LinearCombination::zero());
|
||||
|
||||
let a = a
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
|
||||
.collect();
|
||||
let b = b
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
|
||||
.collect();
|
||||
let c = c
|
||||
.as_ref()
|
||||
.into_iter()
|
||||
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
|
||||
.collect();
|
||||
|
||||
self.constraints.push(BellmanConstraint { a, b, c });
|
||||
}
|
||||
|
||||
fn push_namespace<NR, N>(&mut self, _: N)
|
||||
where
|
||||
NR: Into<String>,
|
||||
N: FnOnce() -> NR,
|
||||
{
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn pop_namespace(&mut self) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
fn get_root(&mut self) -> &mut Self::Root {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn generate_sha256_round_constraints<E: Engine>(
|
||||
) -> (BellmanR1CS<E>, Vec<usize>, Vec<usize>, Vec<usize>) {
|
||||
let mut cs = BellmanR1CS::new();
|
||||
|
||||
let (input_bits, current_hash_bits, output_bits) =
|
||||
sha256_round(&mut cs, &vec![None; 512], &vec![None; 256]).unwrap();
|
||||
|
||||
// res is now the allocated bits for `input`, `current_hash` and `sha256_output`
|
||||
|
||||
(cs, input_bits, current_hash_bits, output_bits)
|
||||
}
|
||||
|
||||
pub fn generate_sha256_round_witness<E: Engine>(
|
||||
input: &[E::Fr],
|
||||
current_hash: &[E::Fr],
|
||||
) -> Vec<E::Fr> {
|
||||
assert_eq!(input.len(), 512);
|
||||
assert_eq!(current_hash.len(), 256);
|
||||
|
||||
let mut cs: BellmanWitness<E> = BellmanWitness {
|
||||
values: vec![<E::Fr as Field>::one()],
|
||||
};
|
||||
|
||||
sha256_round(
|
||||
&mut cs,
|
||||
&input.iter().map(|x| Some(x.clone())).collect(),
|
||||
¤t_hash.iter().map(|x| Some(x.clone())).collect(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
cs.values
|
||||
}
|
||||
|
||||
fn var_to_index(v: Variable) -> usize {
|
||||
match v.get_unchecked() {
|
||||
Index::Aux(i) => i + 1,
|
||||
Index::Input(0) => 0,
|
||||
_ => unreachable!("No public variables should have been allocated"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use bellman::pairing::bn256::{Bn256, Fr};
|
||||
|
||||
#[test]
|
||||
fn generate_constraints() {
|
||||
let (_c, input, current_hash, output) = generate_sha256_round_constraints::<Bn256>();
|
||||
assert_eq!(input.len(), 512);
|
||||
assert_eq!(current_hash.len(), 256);
|
||||
assert_eq!(output.len(), 256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn generate_witness() {
|
||||
let witness =
|
||||
generate_sha256_round_witness::<Bn256>(&vec![Fr::one(); 512], &vec![Fr::zero(); 256]);
|
||||
assert_eq!(witness.len(), 26935);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cs() {
|
||||
use sapling_crypto::circuit::test::TestConstraintSystem;
|
||||
|
||||
let mut cs: TestConstraintSystem<Bn256> = TestConstraintSystem::new();
|
||||
|
||||
let _ = sha256_round(
|
||||
&mut cs,
|
||||
&vec![Some(Fr::zero()); 512],
|
||||
&vec![Some(Fr::one()); 256],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert!(cs.is_satisfied());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue