wip
This commit is contained in:
parent
8b02ca03c9
commit
baf18f7e05
7 changed files with 523 additions and 311 deletions
62
Cargo.lock
generated
62
Cargo.lock
generated
|
@ -54,6 +54,7 @@ checksum = "eb89b97424403ec9cc22a1df0db748dd7396c9ba5fb5c71a6f0e10ae1d1a7449"
|
|||
dependencies = [
|
||||
"ark-ec",
|
||||
"ark-ff",
|
||||
"ark-r1cs-std",
|
||||
"ark-std",
|
||||
]
|
||||
|
||||
|
@ -88,12 +89,15 @@ checksum = "74b83a7e125e5c611e4a997123effb2f02e3fbc66531dd77751d3016ee920741"
|
|||
dependencies = [
|
||||
"ark-ec",
|
||||
"ark-ff",
|
||||
"ark-nonnative-field",
|
||||
"ark-r1cs-std",
|
||||
"ark-relations",
|
||||
"ark-snark",
|
||||
"ark-std",
|
||||
"blake2",
|
||||
"derivative",
|
||||
"digest 0.9.0",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -158,9 +162,30 @@ dependencies = [
|
|||
"ark-ec",
|
||||
"ark-ff",
|
||||
"ark-poly",
|
||||
"ark-r1cs-std",
|
||||
"ark-relations",
|
||||
"ark-serialize",
|
||||
"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]]
|
||||
|
@ -176,6 +201,22 @@ dependencies = [
|
|||
"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]]
|
||||
name = "ark-relations"
|
||||
version = "0.2.0"
|
||||
|
@ -1961,9 +2002,21 @@ checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f"
|
|||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"pin-project-lite",
|
||||
"tracing-attributes",
|
||||
"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]]
|
||||
name = "tracing-core"
|
||||
version = "0.1.17"
|
||||
|
@ -2345,6 +2398,15 @@ dependencies = [
|
|||
name = "zokrates_embed"
|
||||
version = "0.1.2"
|
||||
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",
|
||||
"sapling-crypto_ce",
|
||||
]
|
||||
|
|
|
@ -14,7 +14,7 @@ cfg_if::cfg_if! {
|
|||
use pairing_ce::bn256::Bn256;
|
||||
use pairing_ce::ff::{PrimeField, PrimeFieldRepr};
|
||||
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")]
|
||||
impl<T: Field, E: Engine> From<BellmanConstraint<E>> for FlatStatement<T> {
|
||||
fn from(c: BellmanConstraint<E>) -> FlatStatement<T> {
|
||||
let rhs_a = flat_expression_from_vec::<T, E>(&c.a);
|
||||
let rhs_b = flat_expression_from_vec::<T, E>(&c.b);
|
||||
let lhs = flat_expression_from_vec::<T, E>(&c.c);
|
||||
fn flat_statement_from_constraint<T: Field, E: Engine>(c: Constraint<E::Fr>) -> FlatStatement<T> {
|
||||
let rhs_a = flat_expression_from_vec::<T, E>(&c.a);
|
||||
let rhs_b = flat_expression_from_vec::<T, E>(&c.b);
|
||||
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
|
||||
|
@ -251,7 +249,10 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
|
|||
)
|
||||
});
|
||||
// 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
|
||||
let outputs: Vec<FlatExpression<T>> = output_indices
|
||||
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
|
||||
|
|
|
@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize};
|
|||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
#[cfg(feature = "bellman")]
|
||||
use zokrates_embed::generate_sha256_round_witness;
|
||||
use zokrates_embed::bellman::generate_sha256_round_witness;
|
||||
use zokrates_field::Field;
|
||||
|
||||
pub type ExecutionResult<T> = Result<Witness<T>, Error>;
|
||||
|
|
|
@ -11,4 +11,13 @@ 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 }
|
||||
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
137
zokrates_embed/src/ark.rs
Normal 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")
|
||||
}
|
289
zokrates_embed/src/bellman.rs
Normal file
289
zokrates_embed/src/bellman.rs
Normal 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, ¤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());
|
||||
|
||||
// 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<_>>(),
|
||||
¤t_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());
|
||||
}
|
||||
}
|
|
@ -1,26 +1,16 @@
|
|||
extern crate sapling_crypto_ce as sapling_crypto;
|
||||
use sapling_crypto::bellman;
|
||||
pub mod ark;
|
||||
pub mod 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)]
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct BellmanR1CS<E: Engine> {
|
||||
pub struct R1CS<T> {
|
||||
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 {
|
||||
BellmanR1CS {
|
||||
Self {
|
||||
aux_count: 0,
|
||||
constraints: vec![],
|
||||
}
|
||||
|
@ -28,289 +18,13 @@ impl<E: Engine> Default for BellmanR1CS<E> {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BellmanWitness<E: Engine> {
|
||||
pub values: Vec<E::Fr>,
|
||||
pub struct Witness<T> {
|
||||
pub values: Vec<T>,
|
||||
}
|
||||
|
||||
#[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: &[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, ¤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());
|
||||
|
||||
// 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<_>>(),
|
||||
¤t_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());
|
||||
}
|
||||
#[derive(Default, Debug, PartialEq, Clone)]
|
||||
pub struct Constraint<T> {
|
||||
pub a: Vec<(usize, T)>,
|
||||
pub b: Vec<(usize, T)>,
|
||||
pub c: Vec<(usize, T)>,
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue