Merge pull request #918 from Zokrates/recursive-verifier
Recursive verification
This commit is contained in:
commit
ffac53c793
17 changed files with 1218 additions and 334 deletions
63
Cargo.lock
generated
63
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,8 +2398,18 @@ dependencies = [
|
|||
name = "zokrates_embed"
|
||||
version = "0.1.3"
|
||||
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",
|
||||
"zokrates_field",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
1
changelogs/unreleased/918-dark64
Normal file
1
changelogs/unreleased/918-dark64
Normal file
|
@ -0,0 +1 @@
|
|||
Introduce the `snark_verify_bls12_377` embed for one-layer composition of SNARK proofs (over `BLS12-377`/`BW6-761` pair of curves where `BW6-761` is used as an outer curve to `BLS12-377`)
|
|
@ -8,14 +8,19 @@ use crate::typed_absy::types::{
|
|||
GenericIdentifier,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use zokrates_field::{Bn128Field, Field};
|
||||
use zokrates_field::Field;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "bellman")] {
|
||||
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::{from_bellman, generate_sha256_round_constraints}};
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "ark")] {
|
||||
use ark_bls12_377::Bls12_377;
|
||||
use zokrates_embed::ark::{from_ark, generate_verify_constraints};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,8 +29,6 @@ cfg_if::cfg_if! {
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
|
||||
pub enum FlatEmbed {
|
||||
U32ToField,
|
||||
#[cfg(feature = "bellman")]
|
||||
Sha256Round,
|
||||
Unpack,
|
||||
U8ToBits,
|
||||
U16ToBits,
|
||||
|
@ -35,6 +38,10 @@ pub enum FlatEmbed {
|
|||
U16FromBits,
|
||||
U32FromBits,
|
||||
U64FromBits,
|
||||
#[cfg(feature = "bellman")]
|
||||
Sha256Round,
|
||||
#[cfg(feature = "ark")]
|
||||
SnarkVerifyBls12377,
|
||||
}
|
||||
|
||||
impl FlatEmbed {
|
||||
|
@ -116,6 +123,36 @@ impl FlatEmbed {
|
|||
DeclarationType::Boolean,
|
||||
256usize,
|
||||
))]),
|
||||
#[cfg(feature = "ark")]
|
||||
FlatEmbed::SnarkVerifyBls12377 => DeclarationSignature::new()
|
||||
.generics(vec![
|
||||
Some(Constant::Generic(GenericIdentifier {
|
||||
name: "N",
|
||||
index: 0,
|
||||
})),
|
||||
Some(Constant::Generic(GenericIdentifier {
|
||||
name: "V",
|
||||
index: 1,
|
||||
})),
|
||||
])
|
||||
.inputs(vec![
|
||||
DeclarationType::array((
|
||||
DeclarationType::FieldElement,
|
||||
GenericIdentifier {
|
||||
name: "N",
|
||||
index: 0,
|
||||
},
|
||||
)), // inputs
|
||||
DeclarationType::array((DeclarationType::FieldElement, 8usize)), // proof
|
||||
DeclarationType::array((
|
||||
DeclarationType::FieldElement,
|
||||
GenericIdentifier {
|
||||
name: "V",
|
||||
index: 1,
|
||||
},
|
||||
)), // 18 + (2 * n) // vk
|
||||
])
|
||||
.outputs(vec![DeclarationType::Boolean]),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,8 +173,6 @@ impl FlatEmbed {
|
|||
pub fn id(&self) -> &'static str {
|
||||
match self {
|
||||
FlatEmbed::U32ToField => "_U32_TO_FIELD",
|
||||
#[cfg(feature = "bellman")]
|
||||
FlatEmbed::Sha256Round => "_SHA256_ROUND",
|
||||
FlatEmbed::Unpack => "_UNPACK",
|
||||
FlatEmbed::U8ToBits => "_U8_TO_BITS",
|
||||
FlatEmbed::U16ToBits => "_U16_TO_BITS",
|
||||
|
@ -147,15 +182,21 @@ impl FlatEmbed {
|
|||
FlatEmbed::U16FromBits => "_U16_FROM_BITS",
|
||||
FlatEmbed::U32FromBits => "_U32_FROM_BITS",
|
||||
FlatEmbed::U64FromBits => "_U64_FROM_BITS",
|
||||
#[cfg(feature = "bellman")]
|
||||
FlatEmbed::Sha256Round => "_SHA256_ROUND",
|
||||
#[cfg(feature = "ark")]
|
||||
FlatEmbed::SnarkVerifyBls12377 => "_SNARK_VERIFY_BLS12_377",
|
||||
}
|
||||
}
|
||||
|
||||
/// Actually get the `FlatFunction` that this `FlatEmbed` represents
|
||||
pub fn synthetize<T: Field>(&self, generics: &[u32]) -> FlatFunction<T> {
|
||||
match self {
|
||||
FlatEmbed::Unpack => unpack_to_bitwidth(generics[0] as usize),
|
||||
#[cfg(feature = "bellman")]
|
||||
FlatEmbed::Sha256Round => sha256_round(),
|
||||
FlatEmbed::Unpack => unpack_to_bitwidth(generics[0] as usize),
|
||||
#[cfg(feature = "ark")]
|
||||
FlatEmbed::SnarkVerifyBls12377 => snark_verify_bls12_377(generics[0] as usize),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -163,40 +204,26 @@ impl FlatEmbed {
|
|||
|
||||
// util to convert a vector of `(variable_id, coefficient)` to a flat_expression
|
||||
// we build a binary tree of additions by splitting the vector recursively
|
||||
#[cfg(feature = "bellman")]
|
||||
fn flat_expression_from_vec<T: Field, E: Engine>(v: &[(usize, E::Fr)]) -> FlatExpression<T> {
|
||||
fn flat_expression_from_vec<T: Field>(v: &[(usize, T)]) -> FlatExpression<T> {
|
||||
match v.len() {
|
||||
0 => FlatExpression::Number(T::zero()),
|
||||
1 => {
|
||||
let (key, val) = v[0];
|
||||
let mut res: Vec<u8> = vec![];
|
||||
val.into_repr().write_le(&mut res).unwrap();
|
||||
let (key, val) = v[0].clone();
|
||||
FlatExpression::Mult(
|
||||
box FlatExpression::Number(T::from_byte_vector(res)),
|
||||
box FlatExpression::Number(val),
|
||||
box FlatExpression::Identifier(FlatVariable::new(key)),
|
||||
)
|
||||
}
|
||||
n => {
|
||||
let (u, v) = v.split_at(n / 2);
|
||||
FlatExpression::Add(
|
||||
box flat_expression_from_vec::<T, E>(u),
|
||||
box flat_expression_from_vec::<T, E>(v),
|
||||
box flat_expression_from_vec::<T>(u),
|
||||
box flat_expression_from_vec::<T>(v),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[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);
|
||||
|
||||
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a flat function which computes a sha256 round
|
||||
///
|
||||
/// # Remarks
|
||||
|
@ -206,6 +233,7 @@ impl<T: Field, E: Engine> From<BellmanConstraint<E>> for FlatStatement<T> {
|
|||
/// - arguments
|
||||
#[cfg(feature = "bellman")]
|
||||
pub fn sha256_round<T: Field>() -> FlatFunction<T> {
|
||||
use zokrates_field::Bn128Field;
|
||||
assert_eq!(T::id(), Bn128Field::id());
|
||||
|
||||
// Define iterators for all indices at hand
|
||||
|
@ -254,7 +282,15 @@ 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| {
|
||||
let c = from_bellman::<T, Bn256>(c);
|
||||
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
|
||||
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
|
||||
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
|
||||
|
||||
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
|
||||
});
|
||||
|
||||
// define which subset of the witness is returned
|
||||
let outputs: Vec<FlatExpression<T>> = output_indices
|
||||
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
|
||||
|
@ -284,6 +320,105 @@ pub fn sha256_round<T: Field>() -> FlatFunction<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ark")]
|
||||
pub fn snark_verify_bls12_377<T: Field>(n: usize) -> FlatFunction<T> {
|
||||
use zokrates_field::Bw6_761Field;
|
||||
assert_eq!(T::id(), Bw6_761Field::id());
|
||||
|
||||
let (out_index, input_indices, proof_indices, vk_indices, constraints, variable_count) =
|
||||
generate_verify_constraints(n);
|
||||
|
||||
let cs_indices = 0..variable_count;
|
||||
let input_indices = input_indices.into_iter();
|
||||
let proof_indices = proof_indices.into_iter();
|
||||
let vk_indices = vk_indices.into_iter();
|
||||
|
||||
// indices of the arguments to the function
|
||||
let input_argument_indices = input_indices.clone().map(|i| i + variable_count);
|
||||
|
||||
let proof_argument_indices = proof_indices.clone().map(|i| i + variable_count);
|
||||
|
||||
let vk_argument_indices = vk_indices.clone().map(|i| i + variable_count);
|
||||
|
||||
let input_arguments = input_argument_indices
|
||||
.clone()
|
||||
.map(|i| FlatParameter::private(FlatVariable::new(i)));
|
||||
|
||||
let proof_arguments = proof_argument_indices
|
||||
.clone()
|
||||
.map(|i| FlatParameter::private(FlatVariable::new(i)));
|
||||
|
||||
let vk_arguments = vk_argument_indices
|
||||
.clone()
|
||||
.map(|i| FlatParameter::private(FlatVariable::new(i)));
|
||||
|
||||
let arguments = input_arguments
|
||||
.chain(proof_arguments)
|
||||
.chain(vk_arguments)
|
||||
.collect();
|
||||
|
||||
let one_binding_statement = FlatStatement::Condition(
|
||||
FlatExpression::Identifier(FlatVariable::new(0)),
|
||||
FlatExpression::Number(T::from(1)),
|
||||
);
|
||||
|
||||
let input_binding_statements: Vec<_> = input_indices
|
||||
.chain(proof_indices)
|
||||
.chain(vk_indices)
|
||||
.zip(
|
||||
input_argument_indices
|
||||
.clone()
|
||||
.chain(proof_argument_indices.clone())
|
||||
.chain(vk_argument_indices.clone()),
|
||||
)
|
||||
.map(|(cs_index, argument_index)| {
|
||||
FlatStatement::Condition(
|
||||
FlatVariable::new(cs_index).into(),
|
||||
FlatVariable::new(argument_index).into(),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let constraint_statements: Vec<FlatStatement<T>> = constraints
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
let c = from_ark::<T, Bls12_377>(c);
|
||||
let rhs_a = flat_expression_from_vec::<T>(c.a.as_slice());
|
||||
let rhs_b = flat_expression_from_vec::<T>(c.b.as_slice());
|
||||
let lhs = flat_expression_from_vec::<T>(c.c.as_slice());
|
||||
|
||||
FlatStatement::Condition(lhs, FlatExpression::Mult(box rhs_a, box rhs_b))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let return_statement = FlatStatement::Return(FlatExpressionList {
|
||||
expressions: vec![FlatExpression::Identifier(FlatVariable::new(out_index))],
|
||||
});
|
||||
|
||||
// insert a directive to set the witness
|
||||
let directive_statement = FlatStatement::Directive(FlatDirective {
|
||||
outputs: cs_indices.map(FlatVariable::new).collect(),
|
||||
inputs: input_argument_indices
|
||||
.chain(proof_argument_indices)
|
||||
.chain(vk_argument_indices)
|
||||
.map(|i| FlatVariable::new(i).into())
|
||||
.collect(),
|
||||
solver: Solver::SnarkVerifyBls12377(n),
|
||||
});
|
||||
|
||||
let statements: Vec<_> = std::iter::once(directive_statement)
|
||||
.chain(std::iter::once(one_binding_statement))
|
||||
.chain(input_binding_statements)
|
||||
.chain(constraint_statements)
|
||||
.chain(std::iter::once(return_statement))
|
||||
.collect();
|
||||
|
||||
FlatFunction {
|
||||
arguments,
|
||||
statements,
|
||||
}
|
||||
}
|
||||
|
||||
fn use_variable(
|
||||
layout: &mut HashMap<String, FlatVariable>,
|
||||
name: String,
|
||||
|
|
|
@ -17,7 +17,7 @@ use std::path::{Path, PathBuf};
|
|||
use crate::absy::types::UnresolvedType;
|
||||
use typed_arena::Arena;
|
||||
use zokrates_common::Resolver;
|
||||
use zokrates_field::{Bn128Field, Field};
|
||||
use zokrates_field::{Bn128Field, Bw6_761Field, Field};
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Error {
|
||||
|
@ -107,7 +107,8 @@ impl Importer {
|
|||
if T::id() != Bn128Field::id() {
|
||||
return Err(CompileErrorInner::ImportError(
|
||||
Error::new(format!(
|
||||
"Embed sha256round cannot be used with curve {}",
|
||||
"`sha256round` is expected to be compiled over `{}` curve, but found `{}`",
|
||||
Bn128Field::name(),
|
||||
T::name()
|
||||
))
|
||||
.with_pos(Some(pos)),
|
||||
|
@ -121,6 +122,26 @@ impl Importer {
|
|||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ark")]
|
||||
"snark_verify_bls12_377" => {
|
||||
if T::id() != Bw6_761Field::id() {
|
||||
return Err(CompileErrorInner::ImportError(
|
||||
Error::new(format!(
|
||||
"`snark_verify_bls12_377` is expected to be compiled over `{}` curve, but found `{}`",
|
||||
Bw6_761Field::name(),
|
||||
T::name()
|
||||
))
|
||||
.with_pos(Some(pos)),
|
||||
)
|
||||
.in_file(location)
|
||||
.into());
|
||||
} else {
|
||||
SymbolDeclaration {
|
||||
id: symbol.get_alias(),
|
||||
symbol: Symbol::Flat(FlatEmbed::SnarkVerifyBls12377),
|
||||
}
|
||||
}
|
||||
}
|
||||
"unpack" => SymbolDeclaration {
|
||||
id: symbol.get_alias(),
|
||||
symbol: Symbol::Flat(FlatEmbed::Unpack),
|
||||
|
|
|
@ -6,8 +6,9 @@ use pairing_ce::bn256::Bn256;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use zokrates_embed::ark::generate_verify_witness;
|
||||
#[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>;
|
||||
|
@ -144,7 +145,7 @@ impl Interpreter {
|
|||
inputs: &[T],
|
||||
) -> Result<Vec<T>, String> {
|
||||
let (expected_input_count, expected_output_count) = solver.get_signature();
|
||||
assert!(inputs.len() == expected_input_count);
|
||||
assert_eq!(inputs.len(), expected_input_count);
|
||||
|
||||
let res = match solver {
|
||||
Solver::ConditionEq => match inputs[0].is_zero() {
|
||||
|
@ -233,6 +234,17 @@ impl Interpreter {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
#[cfg(feature = "ark")]
|
||||
Solver::SnarkVerifyBls12377(n) => {
|
||||
use zokrates_field::Bw6_761Field;
|
||||
assert_eq!(T::id(), Bw6_761Field::id());
|
||||
|
||||
generate_verify_witness(
|
||||
&inputs[..*n],
|
||||
&inputs[*n..*n + 8usize],
|
||||
&inputs[*n + 8usize..],
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
assert_eq!(res.len(), expected_output_count);
|
||||
|
|
|
@ -13,6 +13,8 @@ pub enum Solver {
|
|||
EuclideanDiv,
|
||||
#[cfg(feature = "bellman")]
|
||||
Sha256Round,
|
||||
#[cfg(feature = "ark")]
|
||||
SnarkVerifyBls12377(usize),
|
||||
}
|
||||
|
||||
impl fmt::Display for Solver {
|
||||
|
@ -34,6 +36,8 @@ impl Solver {
|
|||
Solver::EuclideanDiv => (2, 2),
|
||||
#[cfg(feature = "bellman")]
|
||||
Solver::Sha256Round => (768, 26935),
|
||||
#[cfg(feature = "ark")]
|
||||
Solver::SnarkVerifyBls12377(n) => (26 + 3 * n, 41991 + 4972 * n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -582,7 +582,10 @@ impl<'ast, 'a, T: Field> ResultFolder<'ast, T> for Propagator<'ast, 'a, T> {
|
|||
_ => unreachable!("should be a field value"),
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "bellman")]
|
||||
FlatEmbed::Sha256Round => None,
|
||||
#[cfg(feature = "ark")]
|
||||
FlatEmbed::SnarkVerifyBls12377 => None,
|
||||
};
|
||||
|
||||
match r {
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/snark/snark_verify_bls12_377_1.zok",
|
||||
"curves": ["Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
"14854361253166356985827940126714566475275412573126716429346293669288261212877767002588736768495892518280555332082",
|
||||
"213732210873464696846782550184555137228225514409080200587629835423679101735680889388490505365220726936653232900722",
|
||||
"87538607630632344401950048588801759619324114043687193925268161368645768079512372989046887060116208844098222887523",
|
||||
"5657143080315521889991799092902512094315245718355471372723437558193333983953910948320586493950523700874011063560",
|
||||
"57443068623489368358651325326719858590550354409074784986003051193396111859230778144944186401073595967957696940521",
|
||||
"239017299818740889416561988179003000999440599788233589838386981563280711497257601212385904595560069610856834048609",
|
||||
"210817940648895568697680255986492415724502544301527123629003695092329489846191300997148203752109923795482648905049",
|
||||
"104796720182429147963427519368170838521257629224027565408974396362211239635140389257768036297199752289691646178885",
|
||||
"1",
|
||||
"237849156256827398282019388933972533154056715710612980343582379691235964002811111531637163291964836316287473866944",
|
||||
"121324456153144638357885760921484124420296650752569739652599982435599667566663818850130137668154167962818124679946",
|
||||
"73600332144130508132510040050892177274732799381796146541825372722030832659283233558443467575385522990242420388929",
|
||||
"44732797483932307692113794160403754043679743522093845554740681666841720206796756514002159097899004452746371432672",
|
||||
"11133333007786916806537653763736510041397904969121754556887982143919681840159919608974969747422557814633960596319",
|
||||
"90561577672782365102721874737156537447800052875073945376839636447536979602099666234669817779872333362600029687267",
|
||||
"5450223346768511418345330845468131514992561567665451102957435878264997759483533580796977034429945593412389724558",
|
||||
"235853237950439075722577332685219091953664185148611937130324227335365792837509030624805785387473218289296335533890",
|
||||
"89396333230537847366322364436342481695658547414236326093675863540417141298105682739791578537835191912089484203681",
|
||||
"115830385654423364502343021113073028365721746246232924567075277636234346135515984504152518055968175024342452068593",
|
||||
"11263613907940703510226043272578077114062568830909561875804816268614922948545123959608046723806484765856945366386",
|
||||
"85099371298035679603247495321780481321948685394995318303952199333118698031562067002765732094949837892213467834453",
|
||||
"237849156256827398282019388933972533154056715710612980343582379691235964002811111531637163291964836316287473866944",
|
||||
"121324456153144638357885760921484124420296650752569739652599982435599667566663818850130137668154167962818124679946",
|
||||
"73600332144130508132510040050892177274732799381796146541825372722030832659283233558443467575385522990242420388929",
|
||||
"44732797483932307692113794160403754043679743522093845554740681666841720206796756514002159097899004452746371432672",
|
||||
"147751075268067473595930126919015490789314687953476809410426208666203744311411068892162888393693647317357680788622",
|
||||
"253223744369647051774471619931702227054534749249995484100066505466186263584769989160049762529720081850824722544795",
|
||||
"226753899873357669326157359116609350824063726018578587491538482132599227769745752321252816012800490263881222618536",
|
||||
"176875521207730154886136120529839690202784860066517231969835480145708453592054364059780266900035568058186799176840"
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
from "EMBED" import snark_verify_bls12_377
|
||||
|
||||
// Verifies a proof with 1 public input (0 inputs + 1 output)
|
||||
// Circuit used in this test:
|
||||
//
|
||||
// def main() -> field:
|
||||
// return 1
|
||||
//
|
||||
// Save the circuit as "circuit.zok" and run the following commands (in order):
|
||||
// $ zokrates compile -i ./circuit.zok -c bls12_377
|
||||
// $ zokrates compute-witness
|
||||
// $ zokrates setup -b ark -s gm17
|
||||
// $ zokrates generate-proof -b ark -s gm17
|
||||
//
|
||||
// To get flattened inputs needed for this test, use the following script:
|
||||
// #!/usr/bin/env node
|
||||
//
|
||||
// const path = require("path");
|
||||
// const fs = require("fs");
|
||||
//
|
||||
// let output = []
|
||||
// for (let i = 2; i < process.argv.length; i++) {
|
||||
// const source = fs.readFileSync(path.resolve(process.argv[i]), 'utf8')
|
||||
// const flat = [...source.matchAll(/0x[a-z0-9]+/gm)].map(n => BigInt(n).toString(10));
|
||||
// output.push(...flat)
|
||||
// }
|
||||
//
|
||||
// console.log(JSON.stringify(output));
|
||||
//
|
||||
// Save this script as "flatten.js" and run the following command:
|
||||
// $ node flatten.js proof.json verification.key
|
||||
|
||||
def main(private field[8] proof, private field[1] inputs, private field[20] vk) -> bool:
|
||||
bool result = snark_verify_bls12_377(inputs, proof, vk)
|
||||
return result
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/snark/snark_verify_bls12_377_2.zok",
|
||||
"curves": ["Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
"30886639936493049016175318852868223421962513695924799011862965798142544864756272917016480650319179059444391880142",
|
||||
"210714472424410627451557273311118253425679108293022860127144278352441005505195339659420709617036779682326673533186",
|
||||
"101969549978420613687361685686211943788361418391955806064423246725556175258043096446227634997412743489772585976407",
|
||||
"225606981549539274850150853435430709464645103097569777238240148161962808333007149588258118157237589076622092111900",
|
||||
"94477902787810056005140833707514971680416163466937669331638397632516292559333058429168736236263588445181668773613",
|
||||
"156965166665593649649919836247487186182263277589020558979047044043456286367751723077006781061358137877318135673282",
|
||||
"137741518433065408317198878994358008499493756319076293122811213051853618947340414216838530582726247267590705502194",
|
||||
"126547036337175013106414910386451161000910611736141896590177615068467376299665795605927145787930213987505973766731",
|
||||
"2",
|
||||
"4",
|
||||
"26150522755032959261786285436959898551137848025504557005325333189168466417284586793885098543725273736029879389211",
|
||||
"169754513648720531797744265077389392707096818238531464510797707592538650668826008029773773893361012602508598834793",
|
||||
"172926009578431040673671475398833553033375949638930965254433842547261096474109828672139964685904707258254717562981",
|
||||
"190737508410333459842769941580905855813961948279753848892816073188168697419955701512184037596994386514528425558736",
|
||||
"1619785665530270858283718034422422029553639813181597813279549759777153426792287594479505827096186872882300711765",
|
||||
"63694115876363306907024906479487765094262979049817897093877772048737865300854356915611214233650510384715733840309",
|
||||
"138256625715993632167333368395637908886726696039897946710436000177289042559378071109224721507617736881530800812544",
|
||||
"107857276706363405428900669135705736327281608718185524590709570009027542794964888233568166787710632979062032163927",
|
||||
"117681951719142414345371029336876269027160875021843115377112400246872843732924494507290756295050251515524804614493",
|
||||
"16932482238351125436073535332269385696327441869886865463514408400096260901383164481505901002564992831623879258663",
|
||||
"46308513493241827384377341904914105301671994851198058483103383539450400464257917932036866988024757095122827891763",
|
||||
"12774065758179916688827174319525442607170697024774973507481660009802587305759263737719583503498828398179974682702",
|
||||
"26150522755032959261786285436959898551137848025504557005325333189168466417284586793885098543725273736029879389211",
|
||||
"169754513648720531797744265077389392707096818238531464510797707592538650668826008029773773893361012602508598834793",
|
||||
"172926009578431040673671475398833553033375949638930965254433842547261096474109828672139964685904707258254717562981",
|
||||
"190737508410333459842769941580905855813961948279753848892816073188168697419955701512184037596994386514528425558736",
|
||||
"187872074241198211214687054253180244660204447307195753216396863454451962530721491538379804696965671145239859590846",
|
||||
"172889367615248592096001816975404506869611319851954669628812891122278364377518978073247194031011246327549860771430",
|
||||
"227539811502856876734690781228675876891342950061206768786526280217867721882979938383152839106086209430522325241347",
|
||||
"33214331578997688306993838825659395665609935174693430136691944882187065031251282996759071511854109007793069549563",
|
||||
"231745969633345194328768544928321593376710672347115907704852838281813505601170157293937606734791368236398411854640",
|
||||
"47237328152391646101146711114931457284784793248831449686265996627039097119070481703804420386021717476164037563466"
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
from "EMBED" import snark_verify_bls12_377
|
||||
|
||||
// Verifies a proof with 2 public inputs (1 input + 1 output)
|
||||
// Circuit used in this test:
|
||||
//
|
||||
// def main(field a) -> field:
|
||||
// return a * a
|
||||
//
|
||||
// Save the circuit as "circuit.zok" and run the following commands (in order):
|
||||
// $ zokrates compile -i ./circuit.zok -c bls12_377
|
||||
// $ zokrates compute-witness -a 2
|
||||
// $ zokrates setup -b ark -s gm17
|
||||
// $ zokrates generate-proof -b ark -s gm17
|
||||
//
|
||||
// To get flattened inputs needed for this test, use the following script:
|
||||
// #!/usr/bin/env node
|
||||
//
|
||||
// const path = require("path");
|
||||
// const fs = require("fs");
|
||||
//
|
||||
// let output = []
|
||||
// for (let i = 2; i < process.argv.length; i++) {
|
||||
// const source = fs.readFileSync(path.resolve(process.argv[i]), 'utf8')
|
||||
// const flat = [...source.matchAll(/0x[a-z0-9]+/gm)].map(n => BigInt(n).toString(10));
|
||||
// output.push(...flat)
|
||||
// }
|
||||
//
|
||||
// console.log(JSON.stringify(output));
|
||||
//
|
||||
// Save this script as "flatten.js" and run the following command:
|
||||
// $ node flatten.js proof.json verification.key
|
||||
|
||||
def main(private field[8] proof, private field[2] inputs, private field[22] vk) -> bool:
|
||||
bool result = snark_verify_bls12_377(inputs, proof, vk)
|
||||
return result
|
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"entry_point": "./tests/tests/snark/snark_verify_bls12_377_5.zok",
|
||||
"curves": ["Bw6_761"],
|
||||
"tests": [
|
||||
{
|
||||
"input": {
|
||||
"values": [
|
||||
"60457684924193218954780799695448128402450659922819148866042534731462934804174889585425668379894806827192129355035",
|
||||
"30692976080216123852486339295726836787768525847434467843252380267593056117653493955651719058012291818530914713503",
|
||||
"125357500613234885873309304302314409734224357144836572740733227274842238671614205545693580811504410323545780196986",
|
||||
"247651569074486453849718457544382537302063316144666696072138162914150063434773021124866593709430271032906775848230",
|
||||
"205925817697152648573187530836257591106879527302418969217282393297385329815356611501846344314549412686571096624542",
|
||||
"52408210490520029867007596145203947635265945726032430791270964404506413872638222587937059680769423104224418341783",
|
||||
"245391920863498220927838330312555206148254951255756591670548373412908886220063896460749603543488483619267089689381",
|
||||
"78589112605630898410537770266153128219408270888620016803188045523021815277982064356841045190284469829851165791293",
|
||||
"2",
|
||||
"2",
|
||||
"2",
|
||||
"2",
|
||||
"8",
|
||||
"177010185638377511324501604520563739739317808509681430951164029757345810095174696221494505373089763385490312116358",
|
||||
"153053794329278051178743982884522858761476481700312111393093980133181276780354993379856934110282786295862092443919",
|
||||
"119352601414532465114802089751399530705471140760948692288569726825270783170792459863146836606243083937386903791326",
|
||||
"154395739702801688661668736325735092854144372763349598896957725187031688340840056329057020108410010039817499025290",
|
||||
"219300867221825583189537695275783744511701200221265601874271468574900788700976161865886103325397401233680596662586",
|
||||
"6184162650897786738969218350774078215930829358700672611442020481749290685465136203052430712512726892174302053960",
|
||||
"223615432567648858214064557325028920329759681028000034077399269834838357009569712943919669143358231307616009815434",
|
||||
"175981229674044997402551815995123920812483064905277870260193130162059294237262155034620065210131116619520563506519",
|
||||
"149763821034046861511733819294494872607002147076380551480035933618221202037885306159876853613449195409389418630899",
|
||||
"63613896436066139625271941484202721828670668029032907443649108037543119043920396499152980372905932782160074585332",
|
||||
"83664647128245200609718804963435898883339854035469313658046832013326011731523365594256699720796643575140884572905",
|
||||
"144568623182910160643612162930255558274463299944237682869943691750723939109705466332836308875557772911097578331002",
|
||||
"177010185638377511324501604520563739739317808509681430951164029757345810095174696221494505373089763385490312116358",
|
||||
"153053794329278051178743982884522858761476481700312111393093980133181276780354993379856934110282786295862092443919",
|
||||
"119352601414532465114802089751399530705471140760948692288569726825270783170792459863146836606243083937386903791326",
|
||||
"154395739702801688661668736325735092854144372763349598896957725187031688340840056329057020108410010039817499025290",
|
||||
"149057616125424973478283591473814309565673764317022249169395023211664620308712452144732942791215282170059517214134",
|
||||
"41780114592407788188439225711342125468082786659527520800331438243958377257657588645284569742745602805176661321513",
|
||||
"221720891820590314635918157317325200201212159883016241641294528146177946855934688201173659819769674033460232363042",
|
||||
"50797017918692117248188277962054664678983362179572320962314605303222488912037808946253081883636411158993624881368",
|
||||
"45015881196637283857089803245084152220024891684216432418680197321055655838083895789012460620698622411348666936603",
|
||||
"6614931577154387449192604140382084380965299734550787093003728565193454839715263838958636766466130999302518638149",
|
||||
"245269024464910939632469903406535269950072210280644644046910525955649284275684019199442256400616717695144071175450",
|
||||
"43420687027898212877864397162744483839228857355048382441491263977288496076621257227893835793326940537504242772685",
|
||||
"83272518748948630820579332810320118472860479700707035306680609335770289292207880205558249065217561951629308682324",
|
||||
"6938552589263177251253935997174459628120498877543020100980702178088439767196046212767463370826879237199769600513",
|
||||
"170649759404749298159628447642105098367401676132263627894428382159110486377464596992614660456131317719055604455895",
|
||||
"52917028619173381482821776446008133295138882162362339762583394451153359715914188291769779536313083815565710768404"
|
||||
]
|
||||
},
|
||||
"output": {
|
||||
"Ok": {
|
||||
"values": ["1"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
from "EMBED" import snark_verify_bls12_377
|
||||
|
||||
// Verifies a proof with 5 public inputs (4 inputs + 1 output)
|
||||
// Circuit used in this test:
|
||||
//
|
||||
// def main(field[4] a) -> field:
|
||||
// field out = 0
|
||||
// for u32 i in 0..4 do
|
||||
// out = out + a[i]
|
||||
// endfor
|
||||
// return out
|
||||
//
|
||||
// Save the circuit as "circuit.zok" and run the following commands (in order):
|
||||
// $ zokrates compile -i ./circuit.zok -c bls12_377
|
||||
// $ zokrates compute-witness -a 2 2 2 2
|
||||
// $ zokrates setup -b ark -s gm17
|
||||
// $ zokrates generate-proof -b ark -s gm17
|
||||
//
|
||||
// To get flattened inputs needed for this test, use the following script:
|
||||
// #!/usr/bin/env node
|
||||
//
|
||||
// const path = require("path");
|
||||
// const fs = require("fs");
|
||||
//
|
||||
// let output = []
|
||||
// for (let i = 2; i < process.argv.length; i++) {
|
||||
// const source = fs.readFileSync(path.resolve(process.argv[i]), 'utf8')
|
||||
// const flat = [...source.matchAll(/0x[a-z0-9]+/gm)].map(n => BigInt(n).toString(10));
|
||||
// output.push(...flat)
|
||||
// }
|
||||
//
|
||||
// console.log(JSON.stringify(output));
|
||||
//
|
||||
// Save this script as "flatten.js" and run the following command:
|
||||
// $ node flatten.js proof.json verification.key
|
||||
|
||||
def main(private field[8] proof, private field[5] inputs, private field[28] vk) -> bool:
|
||||
bool result = snark_verify_bls12_377(inputs, proof, vk)
|
||||
return result
|
|
@ -10,5 +10,15 @@ wasm = ["bellman_ce/wasm", "sapling-crypto_ce/wasm"]
|
|||
multicore = ["bellman_ce/multicore", "sapling-crypto_ce/multicore"]
|
||||
|
||||
[dependencies]
|
||||
zokrates_field = { version = "0.4.0", path = "../zokrates_field", 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}
|
340
zokrates_embed/src/ark.rs
Normal file
340
zokrates_embed/src/ark.rs
Normal file
|
@ -0,0 +1,340 @@
|
|||
use ark_bls12_377::{
|
||||
constraints::PairingVar as BLS12PairingVar, Bls12_377 as BLS12PairingEngine, Fq as BLS12Fq,
|
||||
Fq2 as BLS12Fq2,
|
||||
};
|
||||
use ark_bw6_761::Fr as BW6Fr;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_ff::{BigInteger, One, PrimeField};
|
||||
use ark_r1cs_std::bits::boolean::Boolean;
|
||||
use ark_relations::{
|
||||
ns,
|
||||
r1cs::{ConstraintSystem, ConstraintSystemRef},
|
||||
};
|
||||
|
||||
use ark_crypto_primitives::snark::constraints::SNARKGadget;
|
||||
use ark_gm17::{constraints::GM17VerifierGadget, Proof, VerifyingKey, GM17};
|
||||
use ark_r1cs_std::alloc::{AllocVar, AllocationMode};
|
||||
|
||||
use crate::Constraint;
|
||||
use ark_crypto_primitives::SNARK;
|
||||
use ark_r1cs_std::fields::fp::FpVar;
|
||||
use ark_r1cs_std::ToBitsGadget;
|
||||
use ark_relations::r1cs::{ConstraintSynthesizer, SynthesisError};
|
||||
use ark_std::test_rng;
|
||||
use std::str::FromStr;
|
||||
use zokrates_field::Field;
|
||||
|
||||
type GM17Snark = GM17<BLS12PairingEngine>;
|
||||
type VerifierGadget = GM17VerifierGadget<BLS12PairingEngine, BLS12PairingVar>;
|
||||
|
||||
type G1 = <ark_ec::bls12::Bls12<ark_bls12_377::Parameters> as PairingEngine>::G1Affine;
|
||||
type G2 = <ark_ec::bls12::Bls12<ark_bls12_377::Parameters> as PairingEngine>::G2Affine;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct DefaultCircuit {
|
||||
pub public_input_size: usize,
|
||||
}
|
||||
|
||||
impl<F: PrimeField> ConstraintSynthesizer<F> for DefaultCircuit {
|
||||
fn generate_constraints(self, cs: ConstraintSystemRef<F>) -> Result<(), SynthesisError> {
|
||||
for _ in 0..self.public_input_size {
|
||||
let _ = FpVar::<F>::new_input(ns!(cs, "alloc_input"), || Ok(F::one()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn generate_verify_constraints(
|
||||
public_input_size: usize,
|
||||
) -> (
|
||||
usize,
|
||||
Vec<usize>,
|
||||
Vec<usize>,
|
||||
Vec<usize>,
|
||||
Vec<Constraint<BW6Fr>>,
|
||||
usize,
|
||||
) {
|
||||
let cs_sys = ConstraintSystem::<BW6Fr>::new();
|
||||
let cs = ConstraintSystemRef::new(cs_sys);
|
||||
|
||||
let mut rng = test_rng(); // has a fixed seed
|
||||
let circuit = DefaultCircuit { public_input_size };
|
||||
|
||||
let (pk, vk) = GM17Snark::circuit_specific_setup(circuit, &mut rng).unwrap();
|
||||
let proof = GM17Snark::prove(&pk, circuit, &mut rng).unwrap();
|
||||
|
||||
let mut fp_vars = Vec::new();
|
||||
for _ in 0..public_input_size {
|
||||
let fp = FpVar::new_input(ns!(cs, "alloc_input"), || Ok(BLS12Fq::one())).unwrap();
|
||||
fp_vars.push(fp);
|
||||
}
|
||||
|
||||
let input_booleans: Vec<Vec<Boolean<_>>> =
|
||||
fp_vars.iter().map(|i| i.to_bits_le().unwrap()).collect();
|
||||
|
||||
let inputs = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::InputVar::new(input_booleans);
|
||||
|
||||
let proof = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::new_proof_unchecked(
|
||||
ns!(cs, "alloc_proof"),
|
||||
|| Ok(proof),
|
||||
AllocationMode::Witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let vk = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::new_verification_key_unchecked(
|
||||
ns!(cs, "alloc_vk"), || Ok(vk), AllocationMode::Witness
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let res = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::verify(&vk, &inputs, &proof)
|
||||
.unwrap();
|
||||
|
||||
cs.finalize();
|
||||
|
||||
let num_instance_variables = cs.num_instance_variables();
|
||||
let input_indices = fp_vars
|
||||
.iter()
|
||||
.map(|f| var_to_index(&f, 0))
|
||||
.collect::<Vec<usize>>();
|
||||
|
||||
let proof_indices: Vec<usize> = vec![
|
||||
var_to_index(&proof.a.x, num_instance_variables),
|
||||
var_to_index(&proof.a.y, num_instance_variables),
|
||||
var_to_index(&proof.b.x.c0, num_instance_variables),
|
||||
var_to_index(&proof.b.x.c1, num_instance_variables),
|
||||
var_to_index(&proof.b.y.c0, num_instance_variables),
|
||||
var_to_index(&proof.b.y.c1, num_instance_variables),
|
||||
var_to_index(&proof.c.x, num_instance_variables),
|
||||
var_to_index(&proof.c.y, num_instance_variables),
|
||||
];
|
||||
|
||||
let mut vk_indices: Vec<usize> = vec![
|
||||
var_to_index(&vk.h_g2.x.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_g2.x.c1, num_instance_variables),
|
||||
var_to_index(&vk.h_g2.y.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_g2.y.c1, num_instance_variables),
|
||||
var_to_index(&vk.g_alpha_g1.x, num_instance_variables),
|
||||
var_to_index(&vk.g_alpha_g1.y, num_instance_variables),
|
||||
var_to_index(&vk.h_beta_g2.x.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_beta_g2.x.c1, num_instance_variables),
|
||||
var_to_index(&vk.h_beta_g2.y.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_beta_g2.y.c1, num_instance_variables),
|
||||
var_to_index(&vk.g_gamma_g1.x, num_instance_variables),
|
||||
var_to_index(&vk.g_gamma_g1.y, num_instance_variables),
|
||||
var_to_index(&vk.h_gamma_g2.x.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_gamma_g2.x.c1, num_instance_variables),
|
||||
var_to_index(&vk.h_gamma_g2.y.c0, num_instance_variables),
|
||||
var_to_index(&vk.h_gamma_g2.y.c1, num_instance_variables),
|
||||
];
|
||||
|
||||
vk_indices.extend(
|
||||
vk.query
|
||||
.iter()
|
||||
.map(|q| {
|
||||
vec![
|
||||
var_to_index(&q.x, num_instance_variables),
|
||||
var_to_index(&q.y, num_instance_variables),
|
||||
]
|
||||
})
|
||||
.flatten(),
|
||||
);
|
||||
|
||||
let out_index = match &res {
|
||||
Boolean::Is(x) => x
|
||||
.variable()
|
||||
.get_index_unchecked(num_instance_variables)
|
||||
.unwrap(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
(
|
||||
out_index,
|
||||
input_indices,
|
||||
proof_indices,
|
||||
vk_indices,
|
||||
constraints,
|
||||
cs.num_witness_variables() + cs.num_instance_variables(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn generate_verify_witness<T: Field>(inputs: &[T], proof: &[T], vk: &[T]) -> Vec<T> {
|
||||
assert_eq!(proof.len(), 8);
|
||||
assert_eq!(vk.len(), 18 + (2 * inputs.len()));
|
||||
|
||||
let cs_sys = ConstraintSystem::<BW6Fr>::new();
|
||||
let cs = ConstraintSystemRef::new(cs_sys);
|
||||
|
||||
let mut fp_vars = Vec::new();
|
||||
for input in inputs {
|
||||
let input_field: BLS12Fq = BLS12Fq::from_str(input.to_dec_string().as_str()).unwrap();
|
||||
let fp = FpVar::new_input(ns!(cs, "alloc_input"), || Ok(input_field)).unwrap();
|
||||
fp_vars.push(fp);
|
||||
}
|
||||
|
||||
let input_booleans: Vec<Vec<Boolean<_>>> = fp_vars
|
||||
.into_iter()
|
||||
.map(|i| i.to_bits_le().unwrap())
|
||||
.collect();
|
||||
|
||||
let inputs = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::InputVar::new(input_booleans);
|
||||
|
||||
let proof = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::new_proof_unchecked(
|
||||
ns!(cs, "alloc_proof"),
|
||||
|| {
|
||||
Ok(Proof {
|
||||
a: new_g1(&proof[0..2]),
|
||||
b: new_g2(&proof[2..6]),
|
||||
c: new_g1(&proof[6..8]),
|
||||
})
|
||||
},
|
||||
AllocationMode::Witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let vk = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::new_verification_key_unchecked(
|
||||
ns!(cs, "alloc_vk"),
|
||||
|| {
|
||||
Ok(VerifyingKey {
|
||||
h_g2: new_g2(&vk[0..4]),
|
||||
g_alpha_g1: new_g1(&vk[4..6]),
|
||||
h_beta_g2: new_g2(&vk[6..10]),
|
||||
g_gamma_g1: new_g1(&vk[10..12]),
|
||||
h_gamma_g2: new_g2(&vk[12..16]),
|
||||
query: (16..vk.len())
|
||||
.collect::<Vec<_>>()
|
||||
.chunks(2)
|
||||
.map(|c| new_g1(&vk[c[0]..c[1] + 1]))
|
||||
.collect(),
|
||||
})
|
||||
},
|
||||
AllocationMode::Witness,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let _ = <VerifierGadget as SNARKGadget<
|
||||
<BLS12PairingEngine as PairingEngine>::Fr,
|
||||
<BLS12PairingEngine as PairingEngine>::Fq,
|
||||
GM17Snark,
|
||||
>>::verify(&vk, &inputs, &proof)
|
||||
.unwrap();
|
||||
|
||||
cs.finalize();
|
||||
|
||||
let cs = cs.borrow().unwrap();
|
||||
let witness_variables: Vec<BLS12Fq> = cs.witness_assignment.clone();
|
||||
|
||||
cs.instance_assignment
|
||||
.clone()
|
||||
.into_iter()
|
||||
.chain(witness_variables)
|
||||
.map(|fq| T::from_byte_vector(fq.into_repr().to_bytes_le()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn var_to_index<F: ark_ff::PrimeField>(var: &FpVar<F>, offset: usize) -> usize {
|
||||
match var {
|
||||
FpVar::Var(ref fp) => {
|
||||
let var = &fp.variable;
|
||||
var.get_index_unchecked(offset).unwrap()
|
||||
}
|
||||
_ => unreachable!("expected variable, but found a constant"),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_g1<T: Field>(flat: &[T]) -> G1 {
|
||||
assert_eq!(flat.len(), 2);
|
||||
G1::new(
|
||||
BLS12Fq::from_str(&*flat[0].to_dec_string()).unwrap(),
|
||||
BLS12Fq::from_str(&*flat[1].to_dec_string()).unwrap(),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn new_g2<T: Field>(flat: &[T]) -> G2 {
|
||||
assert_eq!(flat.len(), 4);
|
||||
G2::new(
|
||||
BLS12Fq2::new(
|
||||
BLS12Fq::from_str(&*flat[0].to_dec_string()).unwrap(),
|
||||
BLS12Fq::from_str(&*flat[1].to_dec_string()).unwrap(),
|
||||
),
|
||||
BLS12Fq2::new(
|
||||
BLS12Fq::from_str(&*flat[2].to_dec_string()).unwrap(),
|
||||
BLS12Fq::from_str(&*flat[3].to_dec_string()).unwrap(),
|
||||
),
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_ark<T: zokrates_field::Field, E: PairingEngine>(c: Constraint<E::Fq>) -> Constraint<T> {
|
||||
Constraint {
|
||||
a: c.a
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
b: c.b
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
c: c.c
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
319
zokrates_embed/src/bellman.rs
Normal file
319
zokrates_embed/src/bellman.rs
Normal file
|
@ -0,0 +1,319 @@
|
|||
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::bellman::pairing::ff::{PrimeField, PrimeFieldRepr};
|
||||
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;
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_bellman<T: zokrates_field::Field, E: Engine>(c: Constraint<E::Fr>) -> Constraint<T> {
|
||||
Constraint {
|
||||
a: c.a
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
b: c.b
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
c: c.c
|
||||
.into_iter()
|
||||
.map(|(index, fq)| {
|
||||
let mut res: Vec<u8> = vec![];
|
||||
fq.into_repr().write_le(&mut res).unwrap();
|
||||
(index, T::from_byte_vector(res))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
#[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