From d9be906ed7f42b7da8d4cf5c806a4aee357419a6 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 21 Dec 2018 11:24:33 +0800 Subject: [PATCH] demo added, docs updated --- Cargo.toml | 2 +- src/demo/.gitignore | 2 + src/demo/Cargo.toml | 12 +++ src/demo/README.md | 42 ++++++++ src/demo/examples/xor.rs | 112 ++++++++++++++++++++ src/demo/src/lib.rs | 9 ++ src/demo/src/verifier_contract.rs | 164 ++++++++++++++++++++++++++++++ src/lib.rs | 2 +- 8 files changed, 343 insertions(+), 2 deletions(-) create mode 100644 src/demo/.gitignore create mode 100644 src/demo/Cargo.toml create mode 100644 src/demo/README.md create mode 100644 src/demo/examples/xor.rs create mode 100644 src/demo/src/lib.rs create mode 100644 src/demo/src/verifier_contract.rs diff --git a/Cargo.toml b/Cargo.toml index 0022373..99bd4f8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -authors = ["Sean Bowe ", "Alex Vlasov "] +authors = ["Sean Bowe ", "Alex Vlasov ", "Alex Gluchowski 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 +``` \ No newline at end of file diff --git a/src/demo/examples/xor.rs b/src/demo/examples/xor.rs new file mode 100644 index 0000000..b697ac4 --- /dev/null +++ b/src/demo/examples/xor.rs @@ -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 { + fn grab(&self) -> Result; +} + +impl OptionExt for Option { + fn grab(&self) -> Result { + self.ok_or(SynthesisError::AssignmentMissing) + } +} + +struct XorCircuit { + a: Option, + b: Option, + c: Option, +} + +// Implementation of our circuit: +// Given a bit `c`, prove that we know bits `a` and `b` such that `c = a xor b` +impl Circuit for XorCircuit { + fn synthesize>(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:: { + 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, "")); +} diff --git a/src/demo/src/lib.rs b/src/demo/src/lib.rs new file mode 100644 index 0000000..bbdfec2 --- /dev/null +++ b/src/demo/src/lib.rs @@ -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; \ No newline at end of file diff --git a/src/demo/src/verifier_contract.rs b/src/demo/src/verifier_contract.rs new file mode 100644 index 0000000..374b42e --- /dev/null +++ b/src/demo/src/verifier_contract.rs @@ -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(vk: &groth16::VerifyingKey, proof: &groth16::Proof, 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(vk: &groth16::VerifyingKey, proof: &groth16::Proof, 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::(inputs), + inputs_extra = inputs_extra) +} + +fn unpack(t: &T) -> Vec +{ + 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 { + 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(vk: &groth16::VerifyingKey) -> 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> = vk.ic.iter().map(unpack).collect(); + out.push_str(&render_array("gammaABC", true, ic.as_slice())); + + out +} + +fn hardcode_proof(proof: &groth16::Proof) -> String { + let values = &[ + unpack(&proof.a), + unpack(&proof.b), + unpack(&proof.c), + ]; + render_array("proof", false, values) +} + +fn hardcode_inputs(inputs: &[E::Fr]) -> String { + let values: Vec> = inputs.iter().map(|i| {vec!(format!("{}", inputs[0].into_repr()))}).collect(); + render_array("inputs", true, values.as_slice()) +} diff --git a/src/lib.rs b/src/lib.rs index 1b8f62d..53ccdbb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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,