diff --git a/Cargo.lock b/Cargo.lock index b9fb937c..b66717a2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index 889f4c86..50d4c4ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "zokrates_cli", "zokrates_fs_resolver", "zokrates_stdlib", + "zokrates_embed", "zokrates_abi", "zokrates_test", "zokrates_core_test", diff --git a/zokrates_core/Cargo.toml b/zokrates_core/Cargo.toml index 6a145846..3b9485f3 100644 --- a/zokrates_core/Cargo.toml +++ b/zokrates_core/Cargo.toml @@ -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" diff --git a/zokrates_core/src/flatten/mod.rs b/zokrates_core/src/flatten/mod.rs index e240a826..329ddeec 100644 --- a/zokrates_core/src/flatten/mod.rs +++ b/zokrates_core/src/flatten/mod.rs @@ -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)) diff --git a/zokrates_core/src/ir/interpreter.rs b/zokrates_core/src/ir/interpreter.rs index 3af7faf6..183c30d6 100644 --- a/zokrates_core/src/ir/interpreter.rs +++ b/zokrates_core/src/ir/interpreter.rs @@ -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(&self, s: &Solver, inputs: &Vec) -> Result, 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 LinComb { @@ -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 = 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 = 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)); - } -} diff --git a/zokrates_core/src/optimizer/redefinition.rs b/zokrates_core/src/optimizer/redefinition.rs index e6c3bfd6..c1994eec 100644 --- a/zokrates_core/src/optimizer/redefinition.rs +++ b/zokrates_core/src/optimizer/redefinition.rs @@ -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 Folder for RedefinitionOptimizer { 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()); diff --git a/zokrates_core/src/solvers/mod.rs b/zokrates_core/src/solvers/mod.rs index ed3e1ae5..8e9048dc 100644 --- a/zokrates_core/src/solvers/mod.rs +++ b/zokrates_core/src/solvers/mod.rs @@ -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: Signed { +pub trait Executable { fn execute(&self, inputs: &Vec) -> Result, String>; } -pub trait Signed { - fn get_signature(&self) -> (usize, usize); +impl Executable for Solver { + fn execute(&self, inputs: &Vec) -> Result, 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 = 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 = 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)); + } } diff --git a/zokrates_embed/Cargo.toml b/zokrates_embed/Cargo.toml new file mode 100644 index 00000000..90e9cccb --- /dev/null +++ b/zokrates_embed/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "zokrates_embed" +version = "0.1.1" +authors = ["schaeff "] +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 } \ No newline at end of file diff --git a/zokrates_embed/src/lib.rs b/zokrates_embed/src/lib.rs new file mode 100644 index 00000000..8d689055 --- /dev/null +++ b/zokrates_embed/src/lib.rs @@ -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 { + pub aux_count: usize, + pub constraints: Vec>, +} + +impl BellmanR1CS { + pub fn new() -> Self { + BellmanR1CS { + aux_count: 0, + constraints: vec![], + } + } +} + +#[derive(Debug)] +pub struct BellmanWitness { + pub values: Vec, +} + +#[derive(Debug, PartialEq)] +pub struct BellmanConstraint { + pub a: Vec<(usize, E::Fr)>, + pub b: Vec<(usize, E::Fr)>, + pub c: Vec<(usize, E::Fr)>, +} + +fn sha256_round>( + mut cs: CS, + input: &Vec>, + current_hash: &Vec>, +) -> Result<(Vec, Vec, Vec), SynthesisError> { + // Allocate bits for `input` + let input_bits = input + .iter() + .enumerate() + .map(|(index, i)| { + AllocatedBit::alloc::( + &mut cs.namespace(|| format!("input_{}", index)), + Some(*i == Some(::one())), + ) + .unwrap() + }) + .collect::>(); + + // Define Booleans whose values are the defined bits + let input = input_bits + .iter() + .map(|i| Boolean::Is(i.clone())) + .collect::>(); + + // Allocate bits for `current_hash` + let current_hash_bits = current_hash + .iter() + .enumerate() + .map(|(index, i)| { + AllocatedBit::alloc::( + &mut cs.namespace(|| format!("current_hash_{}", index)), + Some(*i == Some(::one())), + ) + .unwrap() + }) + .collect::>(); + + // 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::>(), + ) + }) + .collect::>(); + + // Apply the compression function, returning the 8 bytes of outputs + let res = sha256_compression_function::(&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::>(); + + // 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 ConstraintSystem for BellmanWitness { + type Root = Self; + + fn alloc(&mut self, _: A, f: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + let index = self.values.len(); + let var = Variable::new_unchecked(Index::Aux(index)); + self.values.push(f().unwrap()); + Ok(var) + } + + fn alloc_input(&mut self, _: A, _: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + unreachable!("Bellman helpers are not allowed to allocate public variables") + } + + fn enforce(&mut self, _: A, _: LA, _: LB, _: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, + { + // do nothing + } + + fn push_namespace(&mut self, _: N) + where + NR: Into, + N: FnOnce() -> NR, + { + // do nothing + } + + fn pop_namespace(&mut self) { + // do nothing + } + + fn get_root(&mut self) -> &mut Self::Root { + self + } +} + +impl ConstraintSystem for BellmanR1CS { + type Root = Self; + + fn alloc(&mut self, _: A, _: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + // 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(&mut self, _: A, _: F) -> Result + where + F: FnOnce() -> Result, + A: FnOnce() -> AR, + AR: Into, + { + unreachable!("Bellman helpers are not allowed to allocate public variables") + } + + fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) + where + A: FnOnce() -> AR, + AR: Into, + LA: FnOnce(LinearCombination) -> LinearCombination, + LB: FnOnce(LinearCombination) -> LinearCombination, + LC: FnOnce(LinearCombination) -> LinearCombination, + { + 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(&mut self, _: N) + where + NR: Into, + 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( +) -> (BellmanR1CS, Vec, Vec, Vec) { + 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( + input: &[E::Fr], + current_hash: &[E::Fr], +) -> Vec { + assert_eq!(input.len(), 512); + assert_eq!(current_hash.len(), 256); + + let mut cs: BellmanWitness = BellmanWitness { + values: vec![::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::(); + 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::(&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 = TestConstraintSystem::new(); + + let _ = sha256_round( + &mut cs, + &vec![Some(Fr::zero()); 512], + &vec![Some(Fr::one()); 256], + ) + .unwrap(); + + assert!(cs.is_satisfied()); + } +}