1
0
Fork 0
mirror of synced 2025-09-24 04:40:05 +00:00

g16 native verifier

This commit is contained in:
dark64 2020-02-18 17:49:14 +01:00
parent 80e8f7de43
commit b55f941831
9 changed files with 208 additions and 203 deletions

7
Cargo.lock generated
View file

@ -92,6 +92,12 @@ dependencies = [
"libc",
]
[[package]]
name = "base64"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7"
[[package]]
name = "bellman_ce"
version = "0.3.1"
@ -1737,6 +1743,7 @@ name = "zokrates_core"
version = "0.4.1"
dependencies = [
"assert_cli",
"base64",
"bellman_ce 0.3.1 (git+https://github.com/matter-labs/bellman?rev=9e35737)",
"bincode",
"cc",

View file

@ -117,10 +117,10 @@ fn cli() -> Result<(), String> {
)
.subcommand(SubCommand::with_name("export-verifier")
.about("Exports a verifier as Solidity smart contract")
.arg(Arg::with_name("input")
.short("i")
.long("input")
.help("Path of the verifier")
.arg(Arg::with_name("verification-key-path")
.short("v")
.long("verification-key-path")
.help("Path of the generated verification key file")
.value_name("FILE")
.takes_value(true)
.required(false)
@ -217,9 +217,9 @@ fn cli() -> Result<(), String> {
.takes_value(true)
.required(false)
.default_value(PROVING_KEY_DEFAULT_PATH)
).arg(Arg::with_name("proofpath")
).arg(Arg::with_name("proof-path")
.short("j")
.long("proofpath")
.long("proof-path")
.help("Path of the JSON proof file")
.value_name("FILE")
.takes_value(true)
@ -245,9 +245,9 @@ fn cli() -> Result<(), String> {
)
.subcommand(SubCommand::with_name("print-proof")
.about("Prints proof in chosen format [remix, json]")
.arg(Arg::with_name("proofpath")
.arg(Arg::with_name("proof-path")
.short("j")
.long("proofpath")
.long("proof-path")
.help("Path of the JSON proof file")
.value_name("FILE")
.takes_value(true)
@ -265,9 +265,9 @@ fn cli() -> Result<(), String> {
)
.subcommand(SubCommand::with_name("verify")
.about("Verifies a given proof with the given constraint system and verification key")
.arg(Arg::with_name("proofpath")
.arg(Arg::with_name("proof-path")
.short("j")
.long("proofpath")
.long("proof-path")
.help("Path of the JSON proof file")
.value_name("FILE")
.takes_value(true)
@ -544,21 +544,21 @@ fn cli() -> Result<(), String> {
{
let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?;
let is_abiv2 = sub_matches.value_of("solidity-abi").unwrap() == "v2";
let is_abi_v2 = sub_matches.value_of("solidity-abi").unwrap() == "v2";
println!("Exporting verifier...");
// read vk file
let input_path = Path::new(sub_matches.value_of("input").unwrap());
let input_file = File::open(&input_path)
.map_err(|why| format!("couldn't open {}: {}", input_path.display(), why))?;
let mut reader = BufReader::new(input_file);
let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap());
let vk_file = File::open(&vk_path)
.map_err(|why| format!("couldn't open {}: {}", vk_path.display(), why))?;
let mut vk = String::new();
let mut reader = BufReader::new(vk_file);
let mut vk = Vec::new();
reader
.read_to_string(&mut vk)
.map_err(|why| format!("couldn't read {}: {}", input_path.display(), why))?;
.read_to_end(&mut vk)
.map_err(|why| format!("couldn't read {}: {}", vk_path.display(), why))?;
let verifier = scheme.export_solidity_verifier(vk, is_abiv2);
let verifier = scheme.export_solidity_verifier(vk, is_abi_v2);
//write output file
let output_path = Path::new(sub_matches.value_of("output").unwrap());
@ -589,7 +589,7 @@ fn cli() -> Result<(), String> {
.map_err(|why| format!("could not load witness: {:?}", why))?;
let pk_path = Path::new(sub_matches.value_of("provingkey").unwrap());
let proof_path = Path::new(sub_matches.value_of("proofpath").unwrap());
let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap());
let program_path = Path::new(sub_matches.value_of("input").unwrap());
let program_file = File::open(&program_path)
@ -621,7 +621,7 @@ fn cli() -> Result<(), String> {
("print-proof", Some(sub_matches)) => {
let format = sub_matches.value_of("format").unwrap();
let path = Path::new(sub_matches.value_of("proofpath").unwrap());
let path = Path::new(sub_matches.value_of("proof-path").unwrap());
let file = File::open(&path)
.map_err(|why| format!("couldn't open {}: {}", path.display(), why))?;
@ -658,12 +658,28 @@ fn cli() -> Result<(), String> {
("verify", Some(sub_matches)) => {
let scheme = get_scheme(sub_matches.value_of("proving-scheme").unwrap())?;
let proof_path = sub_matches.value_of("proofpath").unwrap();
let vk_path = sub_matches.value_of("verification-key-path").unwrap();
let vk_path = Path::new(sub_matches.value_of("verification-key-path").unwrap());
let vk_file = File::open(&vk_path)
.map_err(|why| format!("couldn't open {}: {}", vk_path.display(), why))?;
let mut vk_reader = BufReader::new(vk_file);
let mut vk = Vec::new();
vk_reader
.read_to_end(&mut vk)
.map_err(|why| format!("couldn't read {}: {}", vk_path.display(), why))?;
let proof_path = Path::new(sub_matches.value_of("proof-path").unwrap());
let proof_file = File::open(&proof_path)
.map_err(|why| format!("couldn't open {}: {}", proof_path.display(), why))?;
let mut proof = String::new();
let mut proof_reader = BufReader::new(proof_file);
proof_reader
.read_to_string(&mut proof)
.map_err(|why| format!("couldn't read {}: {}", proof_path.display(), why))?;
println!("Performing verification...");
scheme.verify(String::from(vk_path), String::from(proof_path));
println!("Verified: {}", scheme.verify(vk, proof));
}
_ => unreachable!(),
}

View file

@ -24,6 +24,7 @@ serde_derive = "1.0"
serde_json = "1.0"
serde_bytes = "0.10"
bincode = "0.8.0"
base64 = "0.11.0"
regex = "0.2"
pairing_ce = "0.18"
ff_ce = "0.7"

View file

@ -1,17 +1,20 @@
use std::io::{Cursor, Read};
extern crate base64;
use bellman::groth16::Parameters;
use bellman::groth16::*;
use bellman::pairing::bn256::{Bn256, Fr};
use regex::Regex;
use zokrates_field::field::FieldPrime;
use zokrates_field::field::{Field, FieldPrime};
use crate::ir;
use crate::proof_system::{ProofSystem, SetupKeypair};
use crate::proof_system::bn128::utils::bellman::Computation;
use crate::proof_system::bn128::utils::parser::KeyValueParser;
use crate::proof_system::bn128::utils::bellman::{
parse_fr, parse_g1, parse_g1_hex, parse_g2, parse_g2_hex,
};
use crate::proof_system::bn128::utils::solidity::{
SOLIDITY_G2_ADDITION_LIB, SOLIDITY_PAIRING_LIB, SOLIDITY_PAIRING_LIB_V2,
};
use crate::proof_system::{ProofSystem, SetupKeypair};
const G16_WARNING: &str = "WARNING: You are using the G16 scheme which is subject to malleability. See zokrates.github.io/reference/proving_schemes.html#g16-malleability for implications.";
@ -23,6 +26,35 @@ impl G16 {
}
}
type G1PairingPoint = (String, String);
type G2PairingPoint = (G1PairingPoint, G1PairingPoint);
#[derive(Serialize, Deserialize)]
struct G16ProofPoints {
a: G1PairingPoint,
b: G2PairingPoint,
c: G1PairingPoint,
}
#[derive(Serialize, Deserialize)]
struct G16Proof {
proof: G16ProofPoints,
inputs: Vec<String>,
raw: String,
}
impl G16ProofPoints {
fn new(a: G1PairingPoint, b: G2PairingPoint, c: G1PairingPoint) -> Self {
G16ProofPoints { a, b, c }
}
}
impl G16Proof {
fn new(proof: G16ProofPoints, inputs: Vec<String>, raw: String) -> Self {
G16Proof { proof, inputs, raw }
}
}
impl ProofSystem for G16 {
fn setup(&self, program: ir::Prog<FieldPrime>) -> SetupKeypair {
#[cfg(not(target_arch = "wasm32"))]
@ -30,16 +62,17 @@ impl ProofSystem for G16 {
println!("{}", G16_WARNING);
let parameters = Computation::without_witness(program).setup();
let mut cursor = Cursor::new(Vec::new());
parameters.write(&mut cursor).unwrap();
cursor.set_position(0);
let vk: String = serialize::serialize_vk(parameters.vk);
let mut pk: Vec<u8> = Vec::new();
cursor
.read_to_end(&mut pk)
.expect("Could not read cursor buffer");
parameters
.write(&mut pk)
.expect("Could not write proving key to buffer");
let mut vk: Vec<u8> = Vec::new();
parameters
.vk
.write(&mut vk)
.expect("Could not write verifying key to buffer");
SetupKeypair::from(vk, pk)
}
@ -59,12 +92,30 @@ impl ProofSystem for G16 {
let params = Parameters::read(proving_key.as_slice(), true).unwrap();
let proof = computation.clone().prove(&params);
serialize::serialize_proof(&proof, &computation.public_inputs_values())
let mut raw: Vec<u8> = Vec::new();
proof
.write(&mut raw)
.expect("Could not write proof to buffer");
let proof_points =
G16ProofPoints::new(parse_g1(&proof.a), parse_g2(&proof.b), parse_g1(&proof.c));
let g16_proof = G16Proof::new(
proof_points,
computation
.public_inputs_values()
.iter()
.map(parse_fr)
.collect::<Vec<_>>(),
base64::encode(&raw),
);
format!("{:#}", serde_json::to_string(&g16_proof).unwrap())
}
fn export_solidity_verifier(&self, vk: String, is_abiv2: bool) -> String {
let vk_map = vk.parse_pairs();
let (mut template_text, solidity_pairing_lib) = if is_abiv2 {
fn export_solidity_verifier(&self, vk: Vec<u8>, abi_v2: bool) -> String {
let bellman_vk: VerifyingKey<Bn256> = VerifyingKey::read(vk.as_slice()).unwrap();
let (mut template_text, solidity_pairing_lib) = if abi_v2 {
(
String::from(CONTRACT_TEMPLATE_V2),
String::from(SOLIDITY_PAIRING_LIB_V2),
@ -81,31 +132,55 @@ impl ProofSystem for G16 {
let vk_gamma_abc_repeat_regex = Regex::new(r#"(<%vk_gamma_abc_pts%>)"#).unwrap();
let vk_input_len_regex = Regex::new(r#"(<%vk_input_length%>)"#).unwrap();
template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.alpha").unwrap().as_str()).into_owned();
template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.beta").unwrap().as_str()).into_owned();
template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.gamma").unwrap().as_str()).into_owned();
template_text = vk_regex.replace(template_text.as_str(), vk_map.get("vk.delta").unwrap().as_str()).into_owned();
template_text = vk_regex
.replace(
template_text.as_str(),
parse_g1_hex(&bellman_vk.alpha_g1).as_str(),
)
.into_owned();
template_text = vk_regex
.replace(
template_text.as_str(),
parse_g2_hex(&bellman_vk.beta_g2).as_str(),
)
.into_owned();
template_text = vk_regex
.replace(
template_text.as_str(),
parse_g2_hex(&bellman_vk.gamma_g2).as_str(),
)
.into_owned();
template_text = vk_regex
.replace(
template_text.as_str(),
parse_g2_hex(&bellman_vk.delta_g2).as_str(),
)
.into_owned();
let gamma_abc_count_str = vk_map.get("vk.gamma_abc.len()").unwrap();
let gamma_abc_count: i32 = gamma_abc_count_str.parse::<i32>().unwrap();
let gamma_abc_count: usize = bellman_vk.ic.len();
template_text = vk_gamma_abc_len_regex
.replace(
template_text.as_str(),
format!("{}", gamma_abc_count).as_str(),
)
.into_owned();
template_text = vk_gamma_abc_len_regex.replace(
template_text.as_str(),
format!("{}", gamma_abc_count).as_str()
).into_owned();
template_text = vk_input_len_regex.replace(
template_text.as_str(),
format!("{}", gamma_abc_count - 1).as_str()
).into_owned();
template_text = vk_input_len_regex
.replace(
template_text.as_str(),
format!("{}", gamma_abc_count - 1).as_str(),
)
.into_owned();
let mut gamma_abc_repeat_text = String::new();
for x in 0..gamma_abc_count {
gamma_abc_repeat_text.push_str(
format!(
"vk.gamma_abc[{}] = Pairing.G1Point({});", x,
vk_map.get(format!("vk.gamma_abc[{}]", x).as_str()).unwrap()
).as_str()
"vk.gamma_abc[{}] = Pairing.G1Point({});",
x,
parse_g1_hex(bellman_vk.ic.get(x).unwrap())
)
.as_str(),
);
if x < gamma_abc_count - 1 {
gamma_abc_repeat_text.push_str("\n ");
@ -125,61 +200,27 @@ impl ProofSystem for G16 {
)
}
fn verify(&self, vk: String, proof: String) -> bool {
unimplemented!()
}
}
fn verify(&self, vk: Vec<u8>, proof: String) -> bool {
let vk: VerifyingKey<Bn256> =
VerifyingKey::read(vk.as_slice()).expect("Could not read verifying key");
mod serialize {
use bellman::groth16::{Proof, VerifyingKey};
use pairing::bn256::{Bn256, Fr};
let pvk: PreparedVerifyingKey<Bn256> = prepare_verifying_key(&vk);
let g16_proof: G16Proof = serde_json::from_str(proof.as_str()).unwrap();
use crate::proof_system::bn128::utils::bellman::{
parse_fr_json, parse_g1_hex, parse_g1_json, parse_g2_hex, parse_g2_json,
};
let proof_bytes = base64::decode(g16_proof.raw.as_str()).unwrap();
let bellman_proof: Proof<Bn256> = Proof::read(proof_bytes.as_slice()).unwrap();
pub fn serialize_vk(vk: VerifyingKey<Bn256>) -> String {
format!(
"vk.alpha = {}
vk.beta = {}
vk.gamma = {}
vk.delta = {}
vk.gamma_abc.len() = {}
{}",
parse_g1_hex(&vk.alpha_g1),
parse_g2_hex(&vk.beta_g2),
parse_g2_hex(&vk.gamma_g2),
parse_g2_hex(&vk.delta_g2),
vk.ic.len(),
vk.ic
.iter()
.enumerate()
.map(|(i, x)| format!("vk.gamma_abc[{}] = {}", i, parse_g1_hex(x)))
.collect::<Vec<_>>()
.join("\n")
)
}
let public_inputs: Vec<Fr> = g16_proof
.inputs
.iter()
.map(|s| {
FieldPrime::try_from_hex_str(s.as_str())
.unwrap()
.into_bellman()
})
.collect::<Vec<_>>();
pub fn serialize_proof(p: &Proof<Bn256>, inputs: &Vec<Fr>) -> String {
format!(
"{{
\"proof\": {{
\"a\": {},
\"b\": {},
\"c\": {}
}},
\"inputs\": [{}]
}}",
parse_g1_json(&p.a),
parse_g2_json(&p.b),
parse_g1_json(&p.c),
inputs
.iter()
.map(parse_fr_json)
.collect::<Vec<_>>()
.join(", "),
)
verify_proof(&pvk, &bellman_proof, &public_inputs).unwrap()
}
}

View file

@ -224,69 +224,50 @@ mod parse {
pub fn parse_g1(e: &<Bn256 as bellman::pairing::Engine>::G1Affine) -> (String, String) {
let raw_e = e.to_string();
let captures = G1_REGEX.captures(&raw_e).unwrap();
(
captures.name(&"x").unwrap().as_str().to_string(),
captures.name(&"y").unwrap().as_str().to_string(),
)
}
fn parse_g2(
pub fn parse_g2(
e: &<Bn256 as bellman::pairing::Engine>::G2Affine,
) -> (String, String, String, String) {
) -> ((String, String), (String, String)) {
let raw_e = e.to_string();
let captures = G2_REGEX.captures(&raw_e).unwrap();
(
captures.name(&"x1").unwrap().as_str().to_string(),
captures.name(&"x0").unwrap().as_str().to_string(),
captures.name(&"y1").unwrap().as_str().to_string(),
captures.name(&"y0").unwrap().as_str().to_string(),
(
captures.name(&"x1").unwrap().as_str().to_string(),
captures.name(&"x0").unwrap().as_str().to_string(),
),
(
captures.name(&"y1").unwrap().as_str().to_string(),
captures.name(&"y0").unwrap().as_str().to_string(),
),
)
}
fn parse_fr(e: &Fr) -> String {
pub fn parse_fr(e: &Fr) -> String {
let raw_e = e.to_string();
let captures = FR_REGEX.captures(&raw_e).unwrap();
captures.name(&"x").unwrap().as_str().to_string()
}
pub fn parse_g1_json(e: &<Bn256 as bellman::pairing::Engine>::G1Affine) -> String {
let parsed = parse_g1(e);
format!("[\"{}\", \"{}\"]", parsed.0, parsed.1)
}
pub fn parse_g2_json(e: &<Bn256 as bellman::pairing::Engine>::G2Affine) -> String {
let parsed = parse_g2(e);
format!(
"[[\"{}\", \"{}\"], [\"{}\", \"{}\"]]",
parsed.0, parsed.1, parsed.2, parsed.3,
)
}
pub fn parse_fr_json(e: &Fr) -> String {
let parsed = parse_fr(e);
format!("\"{}\"", parsed)
}
pub fn parse_g1_hex(e: &<Bn256 as bellman::pairing::Engine>::G1Affine) -> String {
let parsed = parse_g1(e);
format!("{}, {}", parsed.0, parsed.1)
}
pub fn parse_g2_hex(e: &<Bn256 as bellman::pairing::Engine>::G2Affine) -> String {
let parsed = parse_g2(e);
format!("[{}, {}], [{}, {}]", parsed.0, parsed.1, parsed.2, parsed.3,)
format!(
"[{}, {}], [{}, {}]",
(parsed.0).0,
(parsed.0).1,
(parsed.1).0,
(parsed.1).1
)
}
}

View file

@ -4,4 +4,3 @@ pub mod ffi;
#[cfg(feature = "libsnark")]
pub mod libsnark;
pub mod solidity;
pub mod parser;

View file

@ -1,48 +0,0 @@
use std::collections::HashMap;
pub trait KeyValueParser {
fn parse_pairs(&self) -> HashMap<String, String>;
}
impl KeyValueParser for String {
fn parse_pairs(&self) -> HashMap<String, String> {
let mut map = HashMap::new();
let mut lines = self.lines();
while let Some(current_line) = lines.next() {
let key_value: Vec<&str> = current_line.split("=").collect();
if key_value.len() < 2 {
continue;
}
map.insert(
String::from(key_value[0].trim()),
String::from(key_value[1].trim_start()));
}
map
}
}
#[cfg(test)]
mod tests {
use super::KeyValueParser;
#[test]
fn parse_example() {
let example: &str = r#"
a = 1
b = 2
"#;
let map = String::from(example).parse_pairs();
assert_eq!("1", map.get("a").unwrap());
assert_eq!("2", map.get("b").unwrap());
}
#[test]
fn parse_empty() {
let map = String::from("").parse_pairs();
assert!(map.is_empty());
}
}

View file

@ -11,15 +11,15 @@ pub use self::bn128::PGHR13;
use crate::ir;
// We only need to serialize this struct, there is no need for deserialization as keys are
// used separetely in other use cases
// used separately in other use cases
#[derive(Serialize)]
pub struct SetupKeypair {
pub vk: String,
pub vk: Vec<u8>,
pub pk: Vec<u8>,
}
impl SetupKeypair {
pub fn from(vk: String, pk: Vec<u8>) -> SetupKeypair {
pub fn from(vk: Vec<u8>, pk: Vec<u8>) -> SetupKeypair {
SetupKeypair { vk, pk }
}
}
@ -34,7 +34,7 @@ pub trait ProofSystem {
proving_key: Vec<u8>,
) -> String;
fn export_solidity_verifier(&self, vk: String, is_abiv2: bool) -> String;
fn export_solidity_verifier(&self, vk: Vec<u8>, abi_v2: bool) -> String;
fn verify(&self, vk: String, proof: String) -> bool;
fn verify(&self, vk: Vec<u8>, proof: String) -> bool;
}

View file

@ -88,6 +88,7 @@ pub trait Field:
fn get_required_bits() -> usize;
/// Tries to parse a string into this representation
fn try_from_dec_str<'a>(s: &'a str) -> Result<Self, ()>;
fn try_from_hex_str<'a>(s: &'a str) -> Result<Self, ()>;
/// Returns a decimal string representing the member of the equivalence class of this `Field` in Z/pZ
/// which lies in [-(p-1)/2, (p-1)/2]
fn to_compact_dec_string(&self) -> String;
@ -139,12 +140,19 @@ impl Field for FieldPrime {
fn get_required_bits() -> usize {
(*P).bits()
}
fn try_from_dec_str<'a>(s: &'a str) -> Result<Self, ()> {
fn try_from_dec_str(s: &str) -> Result<Self, ()> {
let x = BigInt::parse_bytes(s.as_bytes(), 10).ok_or(())?;
Ok(FieldPrime {
value: &x - x.div_floor(&*P) * &*P,
})
}
fn try_from_hex_str(s: &str) -> Result<Self, ()> {
let x = BigInt::parse_bytes(s.trim_start_matches("0x").as_bytes(), 16)
.ok_or(())?;
Ok(FieldPrime {
value: &x - x.div_floor(&*P) * &*P,
})
}
fn to_compact_dec_string(&self) -> String {
// values up to (p-1)/2 included are represented as positive, values between (p+1)/2 and p-1 are represented as negative by subtracting p
if self.value <= FieldPrime::max_value().value / 2 {