1
0
Fork 0
mirror of synced 2025-09-23 12:18:44 +00:00

resolve conflicts, add errors for ark

This commit is contained in:
schaeff 2021-07-08 10:42:40 +02:00
commit ef4241e177
18 changed files with 1234 additions and 338 deletions

63
Cargo.lock generated
View file

@ -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]]

View 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`)

View file

@ -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(DeclarationConstant::Generic(GenericIdentifier {
name: "N",
index: 0,
})),
Some(DeclarationConstant::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,44 +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),
RuntimeError::BellmanConstraint,
)
}
}
/// Returns a flat function which computes a sha256 round
///
/// # Remarks
@ -210,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
@ -260,7 +284,19 @@ 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),
RuntimeError::BellmanConstraint,
)
});
// define which subset of the witness is returned
let outputs: Vec<FlatExpression<T>> = output_indices
.map(|o| FlatExpression::Identifier(FlatVariable::new(o)))
@ -290,6 +326,111 @@ 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)),
RuntimeError::ArkOneBinding,
);
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(),
RuntimeError::ArkInputBinding,
)
})
.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),
RuntimeError::ArkConstraint,
)
})
.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,

View file

@ -23,6 +23,9 @@ pub enum RuntimeError {
BellmanConstraint,
BellmanOneBinding,
BellmanInputBinding,
ArkConstraint,
ArkOneBinding,
ArkInputBinding,
Bitness,
Sum,
Equal,
@ -64,6 +67,9 @@ impl fmt::Display for RuntimeError {
BellmanConstraint => "Bellman constraint is unsatisfied",
BellmanOneBinding => "Bellman ~one binding is unsatisfied",
BellmanInputBinding => "Bellman input binding is unsatisfied",
ArkConstraint => "Ark constraint is unsatisfied",
ArkOneBinding => "Ark ~one binding is unsatisfied",
ArkInputBinding => "Ark input binding is unsatisfied",
Bitness => "Bitness check failed",
Sum => "Sum check failed",
Equal => "Equal check failed",

View file

@ -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),

View file

@ -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>;
@ -148,7 +149,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() {
@ -237,6 +238,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);

View file

@ -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),
}
}
}

View file

@ -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 {

View file

@ -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"]
}
}
}
]
}

View file

@ -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

View file

@ -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"]
}
}
}
]
}

View file

@ -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

View file

@ -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"]
}
}
}
]
}

View file

@ -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

View file

@ -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
View 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(),
}
}

View 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, &current_hash).unwrap();
// Extract the 256 bits of output out of the 8 bytes
let output_bits = res
.into_iter()
.flat_map(|u| u.into_bits_be())
.map(|b| b.get_variable().unwrap().clone());
// Return indices of `input`, `current_hash` and `output` in the CS
(
input_bits
.into_iter()
.map(|b| var_to_index(b.get_variable()))
.collect(),
current_hash_bits
.into_iter()
.map(|b| var_to_index(b.get_variable()))
.collect(),
output_bits
.map(|b| var_to_index(b.get_variable()))
.collect(),
)
}
impl<E: Engine> ConstraintSystem<E> for Witness<E::Fr> {
type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.values.len();
let var = Variable::new_unchecked(Index::Aux(index));
self.values.push(f().unwrap());
Ok(var)
}
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
unreachable!("Bellman helpers are not allowed to allocate public variables")
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, _: LA, _: LB, _: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
// do nothing
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// do nothing
}
fn pop_namespace(&mut self) {
// do nothing
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
impl<E: Engine> ConstraintSystem<E> for R1CS<E::Fr> {
type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
// we don't care about the value as we're only generating the CS
let index = self.aux_count;
let var = Variable::new_unchecked(Index::Aux(index));
self.aux_count += 1;
Ok(var)
}
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
unreachable!("Bellman helpers are not allowed to allocate public variables")
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero());
let c = c(LinearCombination::zero());
let a = a
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let b = b
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let c = c
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
self.constraints.push(Constraint { a, b, c });
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// do nothing
}
fn pop_namespace(&mut self) {
// do nothing
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
pub fn generate_sha256_round_constraints<E: Engine>(
) -> (R1CS<E::Fr>, Vec<usize>, Vec<usize>, Vec<usize>) {
let mut cs = R1CS::default();
let (input_bits, current_hash_bits, output_bits) =
sha256_round::<E, _>(&mut cs, &[None; 512], &[None; 256]);
// res is now the allocated bits for `input`, `current_hash` and `sha256_output`
(cs, input_bits, current_hash_bits, output_bits)
}
pub fn generate_sha256_round_witness<E: Engine>(
input: &[E::Fr],
current_hash: &[E::Fr],
) -> Vec<E::Fr> {
assert_eq!(input.len(), 512);
assert_eq!(current_hash.len(), 256);
let mut cs: Witness<E::Fr> = Witness {
values: vec![<E::Fr as Field>::one()],
};
sha256_round::<E, _>(
&mut cs,
&input.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
&current_hash.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
);
cs.values
}
fn var_to_index(v: Variable) -> usize {
match v.get_unchecked() {
Index::Aux(i) => i + 1,
Index::Input(0) => 0,
_ => unreachable!("No public variables should have been allocated"),
}
}
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());
}
}

View file

@ -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, &current_hash).unwrap();
// Extract the 256 bits of output out of the 8 bytes
let output_bits = res
.into_iter()
.flat_map(|u| u.into_bits_be())
.map(|b| b.get_variable().unwrap().clone());
// Return indices of `input`, `current_hash` and `output` in the CS
(
input_bits
.into_iter()
.map(|b| var_to_index(b.get_variable()))
.collect(),
current_hash_bits
.into_iter()
.map(|b| var_to_index(b.get_variable()))
.collect(),
output_bits
.map(|b| var_to_index(b.get_variable()))
.collect(),
)
}
impl<E: Engine> ConstraintSystem<E> for BellmanWitness<E> {
type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, f: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
let index = self.values.len();
let var = Variable::new_unchecked(Index::Aux(index));
self.values.push(f().unwrap());
Ok(var)
}
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
unreachable!("Bellman helpers are not allowed to allocate public variables")
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, _: LA, _: LB, _: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
// do nothing
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// do nothing
}
fn pop_namespace(&mut self) {
// do nothing
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
impl<E: Engine> ConstraintSystem<E> for BellmanR1CS<E> {
type Root = Self;
fn alloc<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
// we don't care about the value as we're only generating the CS
let index = self.aux_count;
let var = Variable::new_unchecked(Index::Aux(index));
self.aux_count += 1;
Ok(var)
}
fn alloc_input<F, A, AR>(&mut self, _: A, _: F) -> Result<Variable, SynthesisError>
where
F: FnOnce() -> Result<E::Fr, SynthesisError>,
A: FnOnce() -> AR,
AR: Into<String>,
{
unreachable!("Bellman helpers are not allowed to allocate public variables")
}
fn enforce<A, AR, LA, LB, LC>(&mut self, _: A, a: LA, b: LB, c: LC)
where
A: FnOnce() -> AR,
AR: Into<String>,
LA: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LB: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
LC: FnOnce(LinearCombination<E>) -> LinearCombination<E>,
{
let a = a(LinearCombination::zero());
let b = b(LinearCombination::zero());
let c = c(LinearCombination::zero());
let a = a
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let b = b
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
let c = c
.as_ref()
.iter()
.map(|(variable, coefficient)| (var_to_index(*variable), *coefficient))
.collect();
self.constraints.push(BellmanConstraint { a, b, c });
}
fn push_namespace<NR, N>(&mut self, _: N)
where
NR: Into<String>,
N: FnOnce() -> NR,
{
// do nothing
}
fn pop_namespace(&mut self) {
// do nothing
}
fn get_root(&mut self) -> &mut Self::Root {
self
}
}
pub fn generate_sha256_round_constraints<E: Engine>(
) -> (BellmanR1CS<E>, Vec<usize>, Vec<usize>, Vec<usize>) {
let mut cs = BellmanR1CS::default();
let (input_bits, current_hash_bits, output_bits) =
sha256_round(&mut cs, &[None; 512], &[None; 256]);
// res is now the allocated bits for `input`, `current_hash` and `sha256_output`
(cs, input_bits, current_hash_bits, output_bits)
}
pub fn generate_sha256_round_witness<E: Engine>(
input: &[E::Fr],
current_hash: &[E::Fr],
) -> Vec<E::Fr> {
assert_eq!(input.len(), 512);
assert_eq!(current_hash.len(), 256);
let mut cs: BellmanWitness<E> = BellmanWitness {
values: vec![<E::Fr as Field>::one()],
};
sha256_round(
&mut cs,
&input.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
&current_hash.iter().map(|x| Some(*x)).collect::<Vec<_>>(),
);
cs.values
}
fn var_to_index(v: Variable) -> usize {
match v.get_unchecked() {
Index::Aux(i) => i + 1,
Index::Input(0) => 0,
_ => unreachable!("No public variables should have been allocated"),
}
}
#[cfg(test)]
mod tests {
use super::*;
use bellman::pairing::bn256::{Bn256, Fr};
#[test]
fn generate_constraints() {
let (_c, input, current_hash, output) = generate_sha256_round_constraints::<Bn256>();
assert_eq!(input.len(), 512);
assert_eq!(current_hash.len(), 256);
assert_eq!(output.len(), 256);
}
#[test]
fn generate_witness() {
let witness =
generate_sha256_round_witness::<Bn256>(&vec![Fr::one(); 512], &vec![Fr::zero(); 256]);
assert_eq!(witness.len(), 26935);
}
#[test]
fn test_cs() {
use sapling_crypto::circuit::test::TestConstraintSystem;
let mut cs: TestConstraintSystem<Bn256> = TestConstraintSystem::new();
let _ = sha256_round(
&mut cs,
&vec![Some(Fr::zero()); 512],
&vec![Some(Fr::one()); 256],
);
assert!(cs.is_satisfied());
}
#[derive(Default, Debug, PartialEq, Clone)]
pub struct Constraint<T> {
pub a: Vec<(usize, T)>,
pub b: Vec<(usize, T)>,
pub c: Vec<(usize, T)>,
}