1
0
Fork 0
mirror of synced 2025-09-23 20:28:36 +00:00
This commit is contained in:
dark64 2021-05-05 19:18:10 +02:00
parent 8b02ca03c9
commit baf18f7e05
7 changed files with 523 additions and 311 deletions

62
Cargo.lock generated
View file

@ -54,6 +54,7 @@ checksum = "eb89b97424403ec9cc22a1df0db748dd7396c9ba5fb5c71a6f0e10ae1d1a7449"
dependencies = [ dependencies = [
"ark-ec", "ark-ec",
"ark-ff", "ark-ff",
"ark-r1cs-std",
"ark-std", "ark-std",
] ]
@ -88,12 +89,15 @@ checksum = "74b83a7e125e5c611e4a997123effb2f02e3fbc66531dd77751d3016ee920741"
dependencies = [ dependencies = [
"ark-ec", "ark-ec",
"ark-ff", "ark-ff",
"ark-nonnative-field",
"ark-r1cs-std",
"ark-relations", "ark-relations",
"ark-snark", "ark-snark",
"ark-std", "ark-std",
"blake2", "blake2",
"derivative", "derivative",
"digest 0.9.0", "digest 0.9.0",
"tracing",
] ]
[[package]] [[package]]
@ -158,9 +162,30 @@ dependencies = [
"ark-ec", "ark-ec",
"ark-ff", "ark-ff",
"ark-poly", "ark-poly",
"ark-r1cs-std",
"ark-relations", "ark-relations",
"ark-serialize", "ark-serialize",
"ark-std", "ark-std",
"derivative",
"tracing",
]
[[package]]
name = "ark-nonnative-field"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17887af156e9911d1dba5b30d49256d508f82f6a4f765a6fad8b5c637b700353"
dependencies = [
"ark-ec",
"ark-ff",
"ark-r1cs-std",
"ark-relations",
"ark-std",
"derivative",
"num-bigint 0.4.0",
"num-integer",
"num-traits 0.2.14",
"tracing",
] ]
[[package]] [[package]]
@ -176,6 +201,22 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "ark-r1cs-std"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a90fea2b84ae4443983d56540360ea004cab952292b7a6535798b6b9dcb7f41"
dependencies = [
"ark-ec",
"ark-ff",
"ark-relations",
"ark-std",
"derivative",
"num-bigint 0.4.0",
"num-traits 0.2.14",
"tracing",
]
[[package]] [[package]]
name = "ark-relations" name = "ark-relations"
version = "0.2.0" version = "0.2.0"
@ -1961,9 +2002,21 @@ checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"pin-project-lite", "pin-project-lite",
"tracing-attributes",
"tracing-core", "tracing-core",
] ]
[[package]]
name = "tracing-attributes"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c42e6fa53307c8a17e4ccd4dc81cf5ec38db9209f59b222210375b54ee40d1e2"
dependencies = [
"proc-macro2 1.0.24",
"quote 1.0.9",
"syn 1.0.67",
]
[[package]] [[package]]
name = "tracing-core" name = "tracing-core"
version = "0.1.17" version = "0.1.17"
@ -2345,6 +2398,15 @@ dependencies = [
name = "zokrates_embed" name = "zokrates_embed"
version = "0.1.2" version = "0.1.2"
dependencies = [ dependencies = [
"ark-bls12-377",
"ark-bw6-761",
"ark-crypto-primitives",
"ark-ec",
"ark-ff",
"ark-gm17",
"ark-r1cs-std",
"ark-relations",
"ark-std",
"bellman_ce", "bellman_ce",
"sapling-crypto_ce", "sapling-crypto_ce",
] ]

View file

@ -14,7 +14,7 @@ cfg_if::cfg_if! {
use pairing_ce::bn256::Bn256; use pairing_ce::bn256::Bn256;
use pairing_ce::ff::{PrimeField, PrimeFieldRepr}; use pairing_ce::ff::{PrimeField, PrimeFieldRepr};
use pairing_ce::Engine; use pairing_ce::Engine;
use zokrates_embed::{generate_sha256_round_constraints, BellmanConstraint}; use zokrates_embed::{bellman::generate_sha256_round_constraints, Constraint};
} }
} }
@ -184,14 +184,12 @@ fn flat_expression_from_vec<T: Field, E: Engine>(v: &[(usize, E::Fr)]) -> FlatEx
} }
#[cfg(feature = "bellman")] #[cfg(feature = "bellman")]
impl<T: Field, E: Engine> From<BellmanConstraint<E>> for FlatStatement<T> { fn flat_statement_from_constraint<T: Field, E: Engine>(c: Constraint<E::Fr>) -> FlatStatement<T> {
fn from(c: BellmanConstraint<E>) -> FlatStatement<T> { let rhs_a = flat_expression_from_vec::<T, E>(&c.a);
let rhs_a = flat_expression_from_vec::<T, E>(&c.a); let rhs_b = flat_expression_from_vec::<T, E>(&c.b);
let rhs_b = flat_expression_from_vec::<T, E>(&c.b); let lhs = flat_expression_from_vec::<T, E>(&c.c);
let lhs = flat_expression_from_vec::<T, E>(&c.c);
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b)) FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
}
} }
/// Returns a flat function which computes a sha256 round /// Returns a flat function which computes a sha256 round
@ -251,7 +249,10 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
) )
}); });
// insert flattened statements to represent constraints // insert flattened statements to represent constraints
let constraint_statements = r1cs.constraints.into_iter().map(|c| c.into()); let constraint_statements = r1cs
.constraints
.into_iter()
.map(|c| flat_statement_from_constraint::<T, Bn256>(c));
// define which subset of the witness is returned // define which subset of the witness is returned
let outputs: Vec<FlatExpression<T>> = output_indices let outputs: Vec<FlatExpression<T>> = output_indices
.map(|o| FlatExpression::Identifier(FlatVariable::new(o))) .map(|o| FlatExpression::Identifier(FlatVariable::new(o)))

View file

@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
#[cfg(feature = "bellman")] #[cfg(feature = "bellman")]
use zokrates_embed::generate_sha256_round_witness; use zokrates_embed::bellman::generate_sha256_round_witness;
use zokrates_field::Field; use zokrates_field::Field;
pub type ExecutionResult<T> = Result<Witness<T>, Error>; pub type ExecutionResult<T> = Result<Witness<T>, Error>;

View file

@ -12,3 +12,12 @@ multicore = ["bellman_ce/multicore", "sapling-crypto_ce/multicore"]
[dependencies] [dependencies]
bellman_ce = { version = "^0.3", default-features = false } bellman_ce = { version = "^0.3", default-features = false }
sapling-crypto_ce = { version = "^0.1", default-features = false } sapling-crypto_ce = { version = "^0.1", default-features = false }
ark-bls12-377 = { version = "^0.2.0", features = ["curve", "r1cs"], default-features = false }
ark-bw6-761 = { version = "^0.2.0", default-features = false }
ark-gm17 = { version = "^0.2.0", default-features = false, features = ["r1cs"] }
ark-relations = { version = "^0.2.0", default-features = false }
ark-crypto-primitives = { version = "^0.2.0", default-features = false, features = ["r1cs"] }
ark-r1cs-std = { version = "^0.2.0", default-features = false }
ark-std = { version = "^0.2.0", default-features = false }
ark-ec = { version = "^0.2.0", default-features = false }
ark-ff = { version = "^0.2.0", default-features = false}

137
zokrates_embed/src/ark.rs Normal file
View file

@ -0,0 +1,137 @@
use ark_bls12_377::{
constraints::PairingVar as BLS12PairingVar, Bls12_377 as BLS12PairingEngine, Fr as BLS12Fr,
};
use ark_bw6_761::Fr as BW6Fr;
use ark_ec::PairingEngine;
use ark_ff::{Field, One, UniformRand};
use ark_r1cs_std::bits::boolean::Boolean;
use ark_relations::{
lc, ns,
r1cs::{ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError},
};
use ark_crypto_primitives::snark::constraints::SNARKGadget;
use ark_crypto_primitives::snark::{CircuitSpecificSetupSNARK, SNARK};
use ark_gm17::{constraints::GM17VerifierGadget, GM17};
use ark_r1cs_std::alloc::AllocVar;
use ark_std::test_rng;
use crate::Constraint;
use ark_r1cs_std::eq::EqGadget;
use ark_relations::r1cs::{OptimizationGoal, Variable};
use ark_std::ops::{Mul, MulAssign};
#[derive(Copy, Clone)]
struct DummyCircuit<F: Field> {
a: Option<F>,
b: Option<F>,
input_size: usize,
}
impl<ConstraintF: Field> ConstraintSynthesizer<ConstraintF> for DummyCircuit<ConstraintF> {
fn generate_constraints(
self,
cs: ConstraintSystemRef<ConstraintF>,
) -> Result<(), SynthesisError> {
let a = cs.new_witness_variable(|| self.a.ok_or(SynthesisError::AssignmentMissing))?;
let b = cs.new_witness_variable(|| self.b.ok_or(SynthesisError::AssignmentMissing))?;
for _ in 0..self.input_size {
let c = cs.new_input_variable(|| {
let mut a = self.a.ok_or(SynthesisError::AssignmentMissing)?;
let b = self.b.ok_or(SynthesisError::AssignmentMissing)?;
a.mul_assign(&b);
Ok(a)
})?;
cs.enforce_constraint(lc!() + a, lc!() + b, lc!() + c)?;
}
Ok(())
}
}
type GM17Snark = GM17<BLS12PairingEngine>;
type VerifierGadget = GM17VerifierGadget<BLS12PairingEngine, BLS12PairingVar>;
pub fn generate_verify_constraints<T>(input_size: usize) -> (usize, Vec<Constraint<T>>) {
let mut rng = test_rng();
let a = BLS12Fr::rand(&mut rng);
let b = BLS12Fr::rand(&mut rng);
let mut c = a.clone();
c.mul_assign(&b);
let circuit = DummyCircuit {
a: Some(a),
b: Some(b),
input_size,
};
let (pk, vk) = GM17Snark::setup(circuit.clone(), &mut rng).unwrap();
let proof = GM17Snark::prove(&pk, circuit, &mut rng).unwrap();
let cs_sys = ConstraintSystem::<BW6Fr>::new();
let cs = ConstraintSystemRef::new(cs_sys);
let input_gadget =
<VerifierGadget as SNARKGadget<
<BLS12PairingEngine as PairingEngine>::Fr,
<BLS12PairingEngine as PairingEngine>::Fq,
GM17Snark,
>>::InputVar::new_input(ns!(cs, "alloc_inputs"), || Ok(vec![c; input_size]))
.unwrap();
let proof_gadget = <VerifierGadget as SNARKGadget<
<BLS12PairingEngine as PairingEngine>::Fr,
<BLS12PairingEngine as PairingEngine>::Fq,
GM17Snark,
>>::ProofVar::new_witness(ns!(cs, "alloc_proof"), || Ok(proof))
.unwrap();
let vk_gadget = <VerifierGadget as SNARKGadget<
<BLS12PairingEngine as PairingEngine>::Fr,
<BLS12PairingEngine as PairingEngine>::Fq,
GM17Snark,
>>::VerifyingKeyVar::new_constant(ns!(cs, "alloc_vk"), vk.clone())
.unwrap();
let res = <VerifierGadget as SNARKGadget<
<BLS12PairingEngine as PairingEngine>::Fr,
<BLS12PairingEngine as PairingEngine>::Fq,
GM17Snark,
>>::verify(&vk_gadget, &input_gadget, &proof_gadget)
.unwrap();
let out_index = match &res {
Boolean::Is(x) => var_to_index(x.variable()),
Boolean::Not(x) => var_to_index(x.variable()),
_ => unreachable!(),
};
res.conditional_enforce_equal(&Boolean::constant(true), &Boolean::constant(true))
.unwrap();
cs.finalize();
let matrices = cs.to_matrices().unwrap();
let constraints: Vec<Constraint<_>> = matrices
.a
.into_iter()
.zip(matrices.b.into_iter())
.zip(matrices.c.into_iter())
.map(|((a, b), c)| Constraint {
a: a.into_iter().map(|(f, index)| (index, f)).collect(),
b: b.into_iter().map(|(f, index)| (index, f)).collect(),
c: c.into_iter().map(|(f, index)| (index, f)).collect(),
})
.collect();
assert!(cs.is_satisfied().unwrap());
(out_index, constraints)
}
fn var_to_index(v: Variable) -> usize {
v.get_index_unchecked(0)
.expect("Could not get variable index")
}

View file

@ -0,0 +1,289 @@
extern crate sapling_crypto_ce as sapling_crypto;
use sapling_crypto::bellman;
use crate::{Constraint, Witness, R1CS};
use bellman::{
pairing::{ff::Field, Engine},
ConstraintSystem, Index, LinearCombination, SynthesisError, Variable,
};
use sapling_crypto::circuit::{
boolean::{AllocatedBit, Boolean},
sha256::sha256_compression_function,
uint32::UInt32,
};
fn sha256_round<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Option<E::Fr>],
current_hash: &[Option<E::Fr>],
) -> (Vec<usize>, Vec<usize>, Vec<usize>) {
// 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
.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, &current_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());
// Return indices of `input`, `current_hash` and `output` in the CS
(
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
.map(|b| var_to_index(b.get_variable()))
.collect(),
)
}
impl<E: Engine> ConstraintSystem<E> for Witness<E::Fr> {
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 R1CS<E::Fr> {
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.clone();
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()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let b = b
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let c = c
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
self.constraints.push(Constraint { 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>(
) -> (R1CS<E::Fr>, Vec<usize>, Vec<usize>, Vec<usize>) {
let mut cs = R1CS::default();
let (input_bits, current_hash_bits, output_bits) =
sha256_round::<E, _>(&mut cs, &[None; 512], &[None; 256]);
// 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: Witness<E::Fr> = Witness {
values: vec![<E::Fr as Field>::one()],
};
sha256_round::<E, _>(
&mut cs,
&input.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
&current_hash.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
);
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],
);
assert!(cs.is_satisfied());
}
}

View file

@ -1,26 +1,16 @@
extern crate sapling_crypto_ce as sapling_crypto; pub mod ark;
use sapling_crypto::bellman; pub mod bellman;
use bellman::{ #[derive(Debug, Clone)]
pairing::{ff::Field, Engine},
ConstraintSystem, Index, LinearCombination, SynthesisError, Variable,
};
use sapling_crypto::circuit::{
boolean::{AllocatedBit, Boolean},
sha256::sha256_compression_function,
uint32::UInt32,
};
#[derive(Debug)]
#[allow(clippy::upper_case_acronyms)] #[allow(clippy::upper_case_acronyms)]
pub struct BellmanR1CS<E: Engine> { pub struct R1CS<T> {
pub aux_count: usize, pub aux_count: usize,
pub constraints: Vec<BellmanConstraint<E>>, pub constraints: Vec<Constraint<T>>,
} }
impl<E: Engine> Default for BellmanR1CS<E> { impl<T> Default for R1CS<T> {
fn default() -> Self { fn default() -> Self {
BellmanR1CS { Self {
aux_count: 0, aux_count: 0,
constraints: vec![], constraints: vec![],
} }
@ -28,289 +18,13 @@ impl<E: Engine> Default for BellmanR1CS<E> {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct BellmanWitness<E: Engine> { pub struct Witness<T> {
pub values: Vec<E::Fr>, pub values: Vec<T>,
} }
#[derive(Debug, PartialEq)] #[derive(Default, Debug, PartialEq, Clone)]
pub struct BellmanConstraint<E: Engine> { pub struct Constraint<T> {
pub a: Vec<(usize, E::Fr)>, pub a: Vec<(usize, T)>,
pub b: Vec<(usize, E::Fr)>, pub b: Vec<(usize, T)>,
pub c: Vec<(usize, E::Fr)>, pub c: Vec<(usize, T)>,
}
fn sha256_round<E: Engine, CS: ConstraintSystem<E>>(
mut cs: CS,
input: &[Option<E::Fr>],
current_hash: &[Option<E::Fr>],
) -> (Vec<usize>, Vec<usize>, Vec<usize>) {
// 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
.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, &current_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());
// Return indices of `input`, `current_hash` and `output` in the CS
(
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
.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()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let b = b
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let c = c
.as_ref()
.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::default();
let (input_bits, current_hash_bits, output_bits) =
sha256_round(&mut cs, &[None; 512], &[None; 256]);
// 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)).collect::<Vec<_>>(),
&current_hash.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
);
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],
);
assert!(cs.is_satisfied());
}
} }