Skip to content

Commit

Permalink
introduce PackedProofs to semaphore-rs for Oxide branch (#85) (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
pdtfh authored Sep 16, 2024
1 parent cb36b23 commit 756d270
Show file tree
Hide file tree
Showing 6 changed files with 285 additions and 30 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ witness = { git = "https://github.com/philsippl/circom-witness-rs" }
# Use the same `ethers-core` version as ark-circom
# TODO: Remove
ethers-core = { git = "https://github.com/gakonst/ethers-rs", default-features = false }
ethabi = "18.0.0"

[dev-dependencies]
bincode = "1.3.3"
Expand Down
45 changes: 23 additions & 22 deletions crates/ark-zkey/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use ark_bn254::{Bn254, Fr};
use ark_circom::read_zkey;
//use ark_ec::pairing::Pairing;
// use ark_ec::pairing::Pairing;
use ark_ff::Field;
use ark_groth16::ProvingKey;
//use ark_groth16::VerifyingKey;
// use ark_groth16::VerifyingKey;
use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use color_eyre::eyre::{Result, WrapErr};
use memmap2::Mmap;
use std::fs::File;
use std::io::Cursor;
//use std::io::{Read,self};
use std::io::BufReader;
use std::path::PathBuf;
use std::time::Instant;
use std::{fs::File, io::Cursor};
// use std::io::{Read,self};
use std::{io::BufReader, path::PathBuf, time::Instant};

#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, PartialEq)]
pub struct SerializableProvingKey(pub ProvingKey<Bn254>);
Expand Down Expand Up @@ -103,7 +100,7 @@ pub fn read_arkzkey(
let arkzkey_file = File::open(arkzkey_file_path).wrap_err("Failed to open arkzkey file")?;
println!("Time to open arkzkey file: {:?}", now.elapsed());

//let mut buf_reader = BufReader::new(arkzkey_file);
// let mut buf_reader = BufReader::new(arkzkey_file);

// Using mmap
let now = std::time::Instant::now();
Expand Down Expand Up @@ -238,11 +235,13 @@ pub fn convert_zkey(
// Ok(())
// }

// fn test_circuit_serialization_deserialization(dir: &str, circuit: &str) -> Result<()> {
// let _zkey_path = format!("{}/target/{}_final.zkey", dir, circuit);
// let arkzkey_path = format!("{}/target/{}_final.arkzkey", dir, circuit);
// fn test_circuit_serialization_deserialization(dir: &str, circuit: &str)
// -> Result<()> { let _zkey_path = format!("{}/target/{}_final.zkey",
// dir, circuit); let arkzkey_path =
// format!("{}/target/{}_final.arkzkey", dir, circuit);

// let (original_proving_key, original_constraint_matrices) = read_proving_key_and_matrices()?;
// let (original_proving_key, original_constraint_matrices) =
// read_proving_key_and_matrices()?;

// println!("[build] Writing arkzkey to: {}", arkzkey_path);
// let now = Instant::now();
Expand All @@ -269,7 +268,8 @@ pub fn convert_zkey(
// "Original and deserialized constraint matrices do not match"
// );

// //flame::dump_html(&mut std::fs::File::create("flame-graph.html").unwrap()).unwrap();
// //flame::dump_html(&mut
// std::fs::File::create("flame-graph.html").unwrap()).unwrap();

// Ok(())
// }
Expand All @@ -292,25 +292,26 @@ pub fn convert_zkey(

// #[test]
// fn test_rsa_serialization_deserialization() -> Result<()> {
// test_circuit_serialization_deserialization("../mopro-core/examples/circom/rsa", "main")
// }
// test_circuit_serialization_deserialization("../mopro-core/examples/
// circom/rsa", "main") }

// // XXX: We do include_bytes for zkey data, so need to manually change this
// #[test]
// // XXX: We do include_bytes for zkey data, so need to manually change
// this #[test]
// fn test_circuit_naive_read() -> Result<()> {
// circuit_naive_read()
// }

// // #[test]
// // fn test_read_arkzkey_from_bytes() -> Result<()> {
// // const ARKZKEY_BYTES: &[u8] = include_bytes!(
// // "../../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.arkzkey"
// // );
// //
// "../../mopro-core/examples/circom/keccak256/target/keccak256_256_test_final.
// arkzkey" // );

// // println!("Reading arkzkey from bytes (keccak)");
// // let now = Instant::now();
// // let (_deserialized_proving_key, _deserialized_constraint_matrices) =
// // read_arkzkey_from_bytes(ARKZKEY_BYTES)?;
// // let (_deserialized_proving_key, _deserialized_constraint_matrices)
// = // read_arkzkey_from_bytes(ARKZKEY_BYTES)?;
// // println!("Time to read arkzkey: {:?}", now.elapsed());

// // Ok(())
Expand Down
4 changes: 2 additions & 2 deletions crates/semaphore-depth-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ impl VisitMut for IdentReplacer {
}
}
syn::Expr::Macro(mcr) => {
let Ok(mut args) = mcr.mac.parse_body::<MacroArgs>() else {
return;
let Ok(mut args) = mcr.mac.parse_body::<MacroArgs>() else {
return;
};
for arg in &mut args.args {
self.visit_expr_mut(arg);
Expand Down
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod field;
pub mod hash;
pub mod identity;
pub mod merkle_tree;
pub mod packed_proof;
pub mod poseidon;
pub mod poseidon_tree;
pub mod protocol;
Expand Down Expand Up @@ -145,8 +146,11 @@ mod test {
#[cfg(feature = "bench")]
pub mod bench {
use crate::{
hash_to_field, identity::Identity, poseidon_tree::LazyPoseidonTree,
protocol::{generate_proof, generate_witness}, Field,
hash_to_field,
identity::Identity,
poseidon_tree::LazyPoseidonTree,
protocol::{generate_proof, generate_witness},
Field,
};
use criterion::Criterion;
use semaphore_depth_config::get_supported_depths;
Expand Down
250 changes: 250 additions & 0 deletions src/packed_proof.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
use std::{
fmt::Display,
str::{from_utf8, FromStr},
};

use crate::protocol::Proof;
use ethabi::{decode, encode, ParamType, Token};
use ethers_core::types::U256;
use serde::{Deserialize, Deserializer, Serialize, Serializer};

use crate::util::{bytes_from_hex, bytes_to_hex, deserialize_bytes, serialize_bytes};

/// A packed proof is a representation of the ZKP in a single attribute (as
/// opposed to array of arrays) which is easier to transport
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct PackedProof(pub [u8; 256]);

impl From<Proof> for PackedProof {
fn from(proof: Proof) -> Self {
let tokens = Token::FixedArray(vec![
Token::Uint(proof.0 .0),
Token::Uint(proof.0 .1),
Token::Uint(proof.1 .0[0]),
Token::Uint(proof.1 .0[1]),
Token::Uint(proof.1 .1[0]),
Token::Uint(proof.1 .1[1]),
Token::Uint(proof.2 .0),
Token::Uint(proof.2 .1),
]);

let bytes = encode(&[tokens]);
let mut encoded = [0u8; 256];
encoded.copy_from_slice(&bytes[..256]);
Self(encoded)
}
}

impl From<PackedProof> for Proof {
fn from(proof: PackedProof) -> Self {
let decoded = decode(&vec![ParamType::Uint(256); 8], &proof.0).unwrap();
let decoded_uint_array = decoded
.into_iter()
.map(|x| x.into_uint().unwrap())
.collect::<Vec<U256>>();

let a = (decoded_uint_array[0], decoded_uint_array[1]);
let b = ([decoded_uint_array[2], decoded_uint_array[3]], [
decoded_uint_array[4],
decoded_uint_array[5],
]);
let c = (decoded_uint_array[6], decoded_uint_array[7]);
Self(a, b, c)
}
}

Check warning on line 54 in src/packed_proof.rs

View workflow job for this annotation

GitHub Actions / clippy

consider implementing `TryFrom` instead

warning: consider implementing `TryFrom` instead --> src/packed_proof.rs:38:1 | 38 | / impl From<PackedProof> for Proof { 39 | | fn from(proof: PackedProof) -> Self { 40 | | let decoded = decode(&vec![ParamType::Uint(256); 8], &proof.0).unwrap(); 41 | | let decoded_uint_array = decoded ... | 53 | | } 54 | | } | |_^ | = help: `From` is intended for infallible conversions only. Use `TryFrom` if there's a possibility for the conversion to fail note: potential failure(s) --> src/packed_proof.rs:40:23 | 40 | let decoded = decode(&vec![ParamType::Uint(256); 8], &proof.0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#fallible_impl_from note: the lint level is defined here --> src/lib.rs:2:55 | 2 | #![warn(clippy::all, clippy::pedantic, clippy::cargo, clippy::nursery)] | ^^^^^^^^^^^^^^^ = note: `#[warn(clippy::fallible_impl_from)]` implied by `#[warn(clippy::nursery)]`

impl Display for PackedProof {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let hex = bytes_to_hex::<256, 514>(&self.0);
write!(
f,
"{}",
from_utf8(&hex).expect("failed to convert to string")
)
}
}

impl FromStr for PackedProof {
type Err = hex::FromHexError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
bytes_from_hex::<256>(s).map(Self)
}
}

impl Serialize for PackedProof {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serialize_bytes::<256, 514, S>(serializer, &self.0)
}
}

impl<'de> Deserialize<'de> for PackedProof {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bytes = deserialize_bytes::<256, _>(deserializer)?;
Ok(Self(bytes))
}
}

#[cfg(test)]
pub mod test {
use super::*;

#[test]
fn test_serializing_proof_into_packed_proof() {
let proof = Proof(
(U256::from(1), U256::from(2)),
([U256::from(3), U256::from(4)], [
U256::from(5),
U256::from(6),
]),
(U256::from(7), U256::from(8)),
);

let packed_proof = PackedProof::from(proof);

assert_eq!(packed_proof.to_string(), "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008");

let proof2 = Proof::from(packed_proof);

assert_eq!(proof, proof2);
}

#[test]
fn test_parse_from_string() {
let packed_proof_str = "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";

let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();

let expected_proof = Proof(
(U256::from(1), U256::from(2)),
([U256::from(3), U256::from(4)], [
U256::from(5),
U256::from(6),
]),
(U256::from(7), U256::from(8)),
);

let proof: Proof = packed_proof.into();

assert_eq!(proof, expected_proof);
}

#[test]
fn test_parse_from_string_without_prefix() {
// note the lack of 0x prefix
let packed_proof_str = "00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";

let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();

let expected_proof = Proof(
(U256::from(5), U256::from(6)),
([U256::from(3), U256::from(4)], [
U256::from(5),
U256::from(6),
]),
(U256::from(7), U256::from(8)),
);

let proof: Proof = packed_proof.into();

assert_eq!(proof, expected_proof);
}

#[test]
fn test_serialize_proof_to_json() {
let packed_proof_str = "0x00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";

let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let proof: Proof = packed_proof.into();

let serialized = serde_json::to_value(proof).unwrap();

assert_eq!(
serialized,
serde_json::json!([["0x1", "0x2"], [["0x3", "0x4"], ["0x5", "0x6"]], [
"0x7", "0x8"
]])
);
}

#[test]
fn test_serialize_proof_to_json_real_numbers() {
let packed_proof_str = "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";

let packed_proof = PackedProof::from_str(packed_proof_str).unwrap();
let proof: Proof = packed_proof.into();

let serialized = serde_json::to_value(proof).unwrap();

assert_eq!(
serialized,
serde_json::json!([
[
"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d",
"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a"
],
[
[
"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6",
"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f"
],
[
"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5",
"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd"
]
],
[
"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487",
"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520"
]
])
);
}

#[test]
fn test_deserialize_proof_from_json() {
let proof_str = "[
[
\"0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d\",
\"0x233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a\"
],
[
[
\"0xc8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6\",
\"0x241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f\"
],
[
\"0xb23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5\",
\"0x91ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd\"
]
],
[
\"0xacdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a0547487\",
\"0x2446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520\"
]
]";

let proof = serde_json::from_str::<Proof>(proof_str).unwrap();

let packed_proof = PackedProof::from(proof);

let expected_proof = "0x15c1fc6907219676890dfe147ee6f10b580c7881dddacb1567b3bcbfc513a54d233afda3efff43a7631990d2e79470abcbae3ccad4b920476e64745bfe97bb0a0c8c7d7434c382d590d601d951c29c8463d555867db70f9e84f7741c81c2e1e6241d2ddf1c9e6670a24109a0e9c915cd6e07d0248a384dd38d3c91e9b0419f5f0b23c5467a06eff56cc2c246ada1e7d5705afc4dc8b43fd5a6972c679a2019c5091ed6522f7924d3674d08966a008f947f9aa016a4100bb12f911326f3e1befd0acdf5a5996e00933206cbec48f3bbdcee2a4ca75f8db911c00001e5a05474872446d6f1c1506837392a30fdc73d66fd89f4e1b1a5d14b93e2ad0c5f7b777520";

assert_eq!(packed_proof.to_string(), expected_proof);
}

#[test]
fn test_invalid_parsing() {
// note this is only 7 numbers
let packed_proof_str = "0x0000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000007";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");

// not a valid number
let packed_proof_str = "0000000000000000p000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000005000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000070000000000000000000000000000000000000000000000000000000000000008";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");

// completely invalid
let packed_proof_str = "0x0";
PackedProof::from_str(packed_proof_str).expect_err("parsing should fail");
}
}
Loading

0 comments on commit 756d270

Please sign in to comment.