commit
d87d815037
@ -1,5 +1,5 @@
|
||||
[package]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>", "Alex Vlasov <alex.m.vlasov@gmail.com>"]
|
||||
authors = ["Sean Bowe <ewillbefull@gmail.com>", "Alex Vlasov <alex.m.vlasov@gmail.com>", "Alex Gluchowski <alex@gluchowski.net"]
|
||||
description = "zk-SNARK library"
|
||||
documentation = "https://github.com/matterinc/bellman"
|
||||
homepage = "https://github.com/matterinc/bellman"
|
||||
|
2
src/demo/.gitignore
vendored
Normal file
2
src/demo/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/target
|
||||
**/*.rs.bk
|
12
src/demo/Cargo.toml
Normal file
12
src/demo/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "bellman-demo"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
rand = "0.4"
|
||||
hex = "0.3.2"
|
||||
time = "0.1"
|
||||
num-bigint = "0.2"
|
||||
ff = { version = "0.4", features = ["derive"] }
|
||||
bellman = { path = "../.." }
|
||||
pairing = { git = 'https://github.com/matterinc/pairing' }
|
42
src/demo/README.md
Normal file
42
src/demo/README.md
Normal file
@ -0,0 +1,42 @@
|
||||
# Demo circuits
|
||||
|
||||
This project contains usage demonstration for `bellman` zkSNARK proving framework.
|
||||
We use elliptic curve BN256, for which pairings can be efficiently performed in Ethereum Virtual Machine.
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
.
|
||||
│
|
||||
├── examples
|
||||
│ └── xor.rs: simple XOR circuit demo
|
||||
└── src
|
||||
└── lib.rs: demo contract rendering
|
||||
```
|
||||
|
||||
## Usage:
|
||||
|
||||
```$bash
|
||||
cargo run --example xor
|
||||
cargo run --bin circuit
|
||||
```
|
||||
|
||||
## Verification in EVM contract:
|
||||
|
||||
```$bash
|
||||
cargo run --example xor > demo.sol
|
||||
```
|
||||
|
||||
Now deploy `DemoVerifier` contract from `demo.sol` (e.g. in [remix](https://remix.ethereum.org)) and run method `verify()`.
|
||||
|
||||
## Benchmarking
|
||||
|
||||
```$bash
|
||||
BELLMAN_VERBOSE=1 cargo run --release [num_constraints]
|
||||
```
|
||||
|
||||
`num_constraints` is decimal:
|
||||
|
||||
```$bash
|
||||
BELLMAN_VERBOSE=1 cargo run --release 1000000
|
||||
```
|
112
src/demo/examples/xor.rs
Normal file
112
src/demo/examples/xor.rs
Normal file
@ -0,0 +1,112 @@
|
||||
extern crate bellman;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate ff;
|
||||
extern crate bellman_demo;
|
||||
|
||||
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::{Engine};
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
|
||||
trait OptionExt<T> {
|
||||
fn grab(&self) -> Result<T, SynthesisError>;
|
||||
}
|
||||
|
||||
impl<T: Copy> OptionExt<T> for Option<T> {
|
||||
fn grab(&self) -> Result<T, SynthesisError> {
|
||||
self.ok_or(SynthesisError::AssignmentMissing)
|
||||
}
|
||||
}
|
||||
|
||||
struct XorCircuit<E: Engine> {
|
||||
a: Option<E::Fr>,
|
||||
b: Option<E::Fr>,
|
||||
c: Option<E::Fr>,
|
||||
}
|
||||
|
||||
// Implementation of our circuit:
|
||||
// Given a bit `c`, prove that we know bits `a` and `b` such that `c = a xor b`
|
||||
impl<E: Engine> Circuit<E> for XorCircuit<E> {
|
||||
fn synthesize<CS: ConstraintSystem<E>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
|
||||
|
||||
// public input: c
|
||||
// variables (witness): a, b
|
||||
|
||||
// constraint system:
|
||||
// a * a = a
|
||||
// b * b = b
|
||||
// 2a * b = a + b - c
|
||||
|
||||
let a = cs.alloc(|| "a", || self.a.grab())?;
|
||||
|
||||
// a * a = a
|
||||
cs.enforce(|| "a is a boolean", |lc| lc + a, |lc| lc + a, |lc| lc + a);
|
||||
|
||||
let b = cs.alloc(|| "b", || self.b.grab())?;
|
||||
|
||||
// b * b = b
|
||||
cs.enforce(|| "b is a boolean", |lc| lc + b, |lc| lc + b, |lc| lc + b);
|
||||
|
||||
// c = a xor b
|
||||
let c = cs.alloc_input(|| "c", || self.c.grab())?;
|
||||
|
||||
// 2a * b = a + b - c
|
||||
cs.enforce(
|
||||
|| "xor constraint",
|
||||
|lc| lc + (E::Fr::from_str("2").unwrap(), a),
|
||||
|lc| lc + b,
|
||||
|lc| lc + a + b - c,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Create some parameters, create a proof, and verify the proof.
|
||||
fn main() {
|
||||
use rand::thread_rng;
|
||||
|
||||
use bellman::groth16::{
|
||||
create_random_proof, generate_random_parameters, prepare_verifying_key, verify_proof
|
||||
};
|
||||
|
||||
let rng = &mut thread_rng();
|
||||
|
||||
let params = {
|
||||
let c = XorCircuit::<Bn256> {
|
||||
a: None,
|
||||
b: None,
|
||||
c: None,
|
||||
};
|
||||
generate_random_parameters(c, rng).unwrap()
|
||||
};
|
||||
|
||||
let pvk = prepare_verifying_key(¶ms.vk);
|
||||
|
||||
// here we allocate actual variables
|
||||
let c = XorCircuit {
|
||||
a: Some(Fr::one()),
|
||||
b: Some(Fr::zero()),
|
||||
c: Some(Fr::one()),
|
||||
};
|
||||
|
||||
// Create a groth16 proof with our parameters.
|
||||
let proof = create_random_proof(c, ¶ms, rng).unwrap();
|
||||
|
||||
// `inputs` slice contains public parameters encoded as field elements Fr
|
||||
|
||||
// incorrect input tests
|
||||
let inputs = &[Fr::from_str("5").unwrap()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(!success); // fails, because 5 is not 1 or 0
|
||||
|
||||
let inputs = &[Fr::zero()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(!success); // fails because 0 != 0 xor 1
|
||||
|
||||
// correct input test
|
||||
let inputs = &[Fr::one()];
|
||||
let success = verify_proof(&pvk, &proof, inputs).unwrap();
|
||||
assert!(success);
|
||||
println!("{}", bellman_demo::verifier_contract::generate_demo_contract(¶ms.vk, &proof, inputs, ""));
|
||||
}
|
9
src/demo/src/lib.rs
Normal file
9
src/demo/src/lib.rs
Normal file
@ -0,0 +1,9 @@
|
||||
#![allow(unused_imports)]
|
||||
#![allow(unused_variables)]
|
||||
extern crate bellman;
|
||||
extern crate pairing;
|
||||
extern crate rand;
|
||||
extern crate hex;
|
||||
extern crate ff;
|
||||
|
||||
pub mod verifier_contract;
|
164
src/demo/src/verifier_contract.rs
Normal file
164
src/demo/src/verifier_contract.rs
Normal file
@ -0,0 +1,164 @@
|
||||
// Library to generate a demo EVM verifier contract
|
||||
|
||||
use bellman::{Circuit, ConstraintSystem, SynthesisError};
|
||||
use ff::{Field, PrimeField};
|
||||
use pairing::{Engine, CurveAffine, EncodedPoint};
|
||||
use bellman::groth16;
|
||||
use pairing::bn256::{Bn256, Fr};
|
||||
use std::fmt;
|
||||
|
||||
pub fn generate_demo_contract<E: Engine>(vk: &groth16::VerifyingKey<E>, proof: &groth16::Proof<E>, inputs: &[E::Fr], inputs_extra: &str) -> String {
|
||||
format!("{}{}", STANDARD_VERIFIER, demo_verifier(vk, proof, inputs, inputs_extra))
|
||||
}
|
||||
|
||||
const STANDARD_VERIFIER: &str =
|
||||
r#"pragma solidity ^0.4.24;
|
||||
|
||||
// from https://github.com/HarryR/ethsnarks/blob/master/contracts/Verifier.sol
|
||||
contract Verifier {
|
||||
|
||||
function NegateY( uint256 Y ) internal pure returns (uint256) {
|
||||
uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
|
||||
return q - (Y % q);
|
||||
}
|
||||
|
||||
function Verify ( uint256[14] in_vk, uint256[] vk_gammaABC, uint256[8] in_proof, uint256[] proof_inputs ) internal view returns (bool) {
|
||||
require( ((vk_gammaABC.length / 2) - 1) == proof_inputs.length );
|
||||
|
||||
// Compute the linear combination vk_x
|
||||
uint256[3] memory mul_input;
|
||||
uint256[4] memory add_input;
|
||||
bool success;
|
||||
uint m = 2;
|
||||
|
||||
// First two fields are used as the sum
|
||||
add_input[0] = vk_gammaABC[0];
|
||||
add_input[1] = vk_gammaABC[1];
|
||||
|
||||
// Performs a sum of gammaABC[0] + sum[ gammaABC[i+1]^proof_inputs[i] ]
|
||||
for (uint i = 0; i < proof_inputs.length; i++) {
|
||||
mul_input[0] = vk_gammaABC[m++];
|
||||
mul_input[1] = vk_gammaABC[m++];
|
||||
mul_input[2] = proof_inputs[i];
|
||||
|
||||
assembly {
|
||||
// ECMUL, output to last 2 elements of `add_input`
|
||||
success := staticcall(sub(gas, 2000), 7, mul_input, 0x80, add(add_input, 0x40), 0x60)
|
||||
}
|
||||
require( success );
|
||||
|
||||
assembly {
|
||||
// ECADD
|
||||
success := staticcall(sub(gas, 2000), 6, add_input, 0xc0, add_input, 0x60)
|
||||
}
|
||||
require( success );
|
||||
}
|
||||
|
||||
uint[24] memory input = [
|
||||
// (proof.A, proof.B)
|
||||
in_proof[0], in_proof[1], // proof.A (G1)
|
||||
in_proof[2], in_proof[3], in_proof[4], in_proof[5], // proof.B (G2)
|
||||
|
||||
// (-vk.alpha, vk.beta)
|
||||
in_vk[0], NegateY(in_vk[1]), // -vk.alpha (G1)
|
||||
in_vk[2], in_vk[3], in_vk[4], in_vk[5], // vk.beta (G2)
|
||||
|
||||
// (-vk_x, vk.gamma)
|
||||
add_input[0], NegateY(add_input[1]), // -vk_x (G1)
|
||||
in_vk[6], in_vk[7], in_vk[8], in_vk[9], // vk.gamma (G2)
|
||||
|
||||
// (-proof.C, vk.delta)
|
||||
in_proof[6], NegateY(in_proof[7]), // -proof.C (G1)
|
||||
in_vk[10], in_vk[11], in_vk[12], in_vk[13] // vk.delta (G2)
|
||||
];
|
||||
|
||||
uint[1] memory out;
|
||||
assembly {
|
||||
success := staticcall(sub(gas, 2000), 8, input, 768, out, 0x20)
|
||||
}
|
||||
require(success);
|
||||
return out[0] != 0;
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
fn demo_verifier<E: Engine>(vk: &groth16::VerifyingKey<E>, proof: &groth16::Proof<E>, inputs: &[E::Fr], inputs_extra: &str) -> String {
|
||||
format!(
|
||||
r#"
|
||||
contract DemoVerifier is Verifier {{
|
||||
|
||||
function getVk() internal view returns (uint256[14] memory vk, uint256[] memory gammaABC) {{
|
||||
{vk}
|
||||
}}
|
||||
|
||||
function getProof() internal view returns (uint256[8] memory proof) {{
|
||||
{proof}
|
||||
}}
|
||||
|
||||
function getInputs() internal view returns (uint256[] memory inputs) {{
|
||||
{inputs}
|
||||
{inputs_extra}
|
||||
}}
|
||||
|
||||
function verify( ) public view returns (bool) {{
|
||||
var (vk, gammaABC) = getVk();
|
||||
return Verify(vk, gammaABC, getProof(), getInputs());
|
||||
}}
|
||||
}}
|
||||
"#,
|
||||
vk = hardcode_vk(vk),
|
||||
proof = hardcode_proof(proof),
|
||||
inputs = hardcode_inputs::<E>(inputs),
|
||||
inputs_extra = inputs_extra)
|
||||
}
|
||||
|
||||
fn unpack<T: CurveAffine>(t: &T) -> Vec<String>
|
||||
{
|
||||
t.into_uncompressed().as_ref().chunks(32).map(|c| "0x".to_owned() + &hex::encode(c)).collect()
|
||||
}
|
||||
|
||||
const SHIFT: &str = " ";
|
||||
|
||||
fn render_array(name: &str, allocate: bool, values: &[Vec<String>]) -> String {
|
||||
let mut out = String::new();
|
||||
out.push('\n');
|
||||
let flattened: Vec<&String> = values.into_iter().flatten().collect();
|
||||
if allocate {
|
||||
out.push_str(&format!("{}{} = new uint256[]({});\n", SHIFT, name, flattened.len()));
|
||||
}
|
||||
for (i, s) in flattened.iter().enumerate() {
|
||||
out.push_str(&format!("{}{}[{}] = {};\n", SHIFT, name, i, s));
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
fn hardcode_vk<E: Engine>(vk: &groth16::VerifyingKey<E>) -> String {
|
||||
let mut out = String::new();
|
||||
|
||||
let values = &[
|
||||
unpack(&vk.alpha_g1),
|
||||
unpack(&vk.beta_g2),
|
||||
unpack(&vk.gamma_g2),
|
||||
unpack(&vk.delta_g2),
|
||||
];
|
||||
out.push_str(&render_array("vk", false, values));
|
||||
|
||||
let ic: Vec<Vec<String>> = vk.ic.iter().map(unpack).collect();
|
||||
out.push_str(&render_array("gammaABC", true, ic.as_slice()));
|
||||
|
||||
out
|
||||
}
|
||||
|
||||
fn hardcode_proof<E: Engine>(proof: &groth16::Proof<E>) -> String {
|
||||
let values = &[
|
||||
unpack(&proof.a),
|
||||
unpack(&proof.b),
|
||||
unpack(&proof.c),
|
||||
];
|
||||
render_array("proof", false, values)
|
||||
}
|
||||
|
||||
fn hardcode_inputs<E: Engine>(inputs: &[E::Fr]) -> String {
|
||||
let values: Vec<Vec<String>> = inputs.iter().map(|i| {vec!(format!("{}", inputs[0].into_repr()))}).collect();
|
||||
render_array("inputs", true, values.as_slice())
|
||||
}
|
@ -436,7 +436,7 @@ use std::env;
|
||||
fn verbose_flag() -> bool {
|
||||
unsafe {
|
||||
if VERBOSE_SWITCH < 0 {
|
||||
VERBOSE_SWITCH = FromStr::from_str(&env::var("BELLMAN_VERBOSE").unwrap_or(String::new())).unwrap_or(0);
|
||||
VERBOSE_SWITCH = FromStr::from_str(&env::var("BELLMAN_VERBOSE").unwrap_or(String::new())).unwrap_or(1);
|
||||
}
|
||||
match VERBOSE_SWITCH {
|
||||
1 => true,
|
||||
|
Loading…
Reference in New Issue
Block a user