feat(rln): public input computation from witness

This commit is contained in:
s1fr0 2022-06-09 11:45:27 +02:00
parent 7ac5b60cb6
commit 30d820aeb9
No known key found for this signature in database
GPG Key ID: 2C041D60117BFF46
7 changed files with 157 additions and 64 deletions

View File

@ -17,6 +17,7 @@ num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
# ZKP Generation
ark-ec = { version = "0.3.0", default-features = false, features = ["parallel"] }
ark-ff = { version = "0.3.0", default-features = false, features = ["parallel", "asm"] }
#ark-ff = { git = "https://github.com/arkworks-rs/algebra/"}
ark-std = { version = "0.3.0", default-features = false, features = ["parallel"] }
ark-bn254 = { version = "0.3.0" }
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = ["parallel"] }

View File

@ -46,9 +46,7 @@ pub fn CIRCOM() -> CircomBuilder<Bn254> {
// Utilities to convert a json verification key in a groth16::VerificationKey
fn fq_from_str(s: &str) -> Fq {
BigInteger256::try_from(BigUint::from_str(s).unwrap())
.unwrap()
.into()
Fq::try_from(BigUint::from_str(s).unwrap()).unwrap() //was BigInteger256:: and .into()
}
// Extracts the element in G1 corresponding to its JSON serialization

View File

@ -153,10 +153,12 @@ mod test {
let rln_witness = rln_witness_from_json(input_json_str);
// Let's generate a zkSNARK proof
let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap();
let proof = generate_proof(builder, proving_key, &rln_witness).unwrap();
let proof_values = proof_values_from_witness(&rln_witness);
// Let's verify the proof
let verified = verify_proof(verification_key, proof, inputs);
let verified = verify_proof(verification_key, proof, &proof_values);
assert!(verified.unwrap());
}
@ -196,10 +198,12 @@ mod test {
let builder = CIRCOM();
// Let's generate a zkSNARK proof
let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap();
let proof = generate_proof(builder, proving_key, &rln_witness).unwrap();
let proof_values = proof_values_from_witness(&rln_witness);
// Let's verify the proof
let success = verify_proof(verification_key, proof, inputs).unwrap();
let success = verify_proof(verification_key, proof, &proof_values).unwrap();
assert!(success);
}

View File

@ -7,7 +7,9 @@ use tracing_subscriber::layer::SubscriberExt;
// JSON
use rln::circuit::{CIRCOM, VK, ZKEY};
use rln::protocol::{generate_proof, rln_witness_from_json, verify_proof};
use rln::protocol::{
generate_proof, proof_values_from_witness, rln_witness_from_json, verify_proof,
};
// RLN
fn groth16_proof_example() -> Result<()> {
@ -74,10 +76,12 @@ fn groth16_proof_example() -> Result<()> {
let rln_witness = rln_witness_from_json(input_json_str);
// Let's generate a zkSNARK proof
let (proof, inputs) = generate_proof(builder, proving_key, rln_witness).unwrap();
let proof = generate_proof(builder, proving_key, &rln_witness).unwrap();
let proof_values = proof_values_from_witness(&rln_witness);
// Let's verify the proof
let verified = verify_proof(verification_key, proof, inputs);
let verified = verify_proof(verification_key, proof, &proof_values);
assert!(verified.unwrap());

View File

@ -25,13 +25,13 @@ use serde::{Deserialize, Serialize};
use std::time::Instant;
use thiserror::Error;
pub use crate::utils::{str_to_field, vec_to_field, vec_to_fr};
pub use crate::utils::{add, bytes_to_field, mul, str_to_field, vec_to_field, vec_to_fr};
///////////////////////////////////////////////////////
// RLN Witness data structure and utility functions
///////////////////////////////////////////////////////
#[derive(Debug, Deserialize)]
#[derive(Debug, Deserialize, Clone)]
pub struct RLNWitnessInput {
identity_secret: Field,
path_elements: Vec<Field>,
@ -41,6 +41,18 @@ pub struct RLNWitnessInput {
rln_identifier: Field,
}
#[derive(Debug, Deserialize, Clone)]
pub struct RLNProofValues {
// Public outputs:
y: Field,
nullifier: Field,
root: Field,
// Public Inputs:
x: Field,
epoch: Field,
rln_identifier: Field,
}
pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput {
let input_json: serde_json::Value =
serde_json::from_str(input_json_str).expect("JSON was not well-formatted");
@ -93,6 +105,107 @@ pub fn rln_witness_from_values(
}
}
pub fn proof_values_from_witness(rln_witness: &RLNWitnessInput) -> RLNProofValues {
// y share
let a_0 = rln_witness.identity_secret;
let a_1 = poseidon_hash(&[a_0, rln_witness.epoch]);
let y = mul(rln_witness.x, a_1);
let y = add(y, a_0);
// Nullifier
let nullifier = poseidon_hash(&[a_1, rln_witness.rln_identifier]);
// Merkle tree root computations
let mut root = poseidon_hash(&[rln_witness.identity_secret]);
for i in 0..rln_witness.identity_path_index.len() {
if rln_witness.identity_path_index[i] == 0 {
root = poseidon_hash(&[root, rln_witness.path_elements[i]]);
} else {
root = poseidon_hash(&[rln_witness.path_elements[i], root]);
}
}
let root = get_tree_root(
rln_witness.identity_secret,
&rln_witness.path_elements,
&rln_witness.identity_path_index,
true,
);
RLNProofValues {
y,
nullifier,
root,
x: rln_witness.x,
epoch: rln_witness.epoch,
rln_identifier: rln_witness.rln_identifier,
}
}
///////////////////////////////////////////////////////
// Merkle tree utility functions
///////////////////////////////////////////////////////
/// Helper to merkle proof into a bigint vector
/// TODO: we should create a From trait for this
pub fn get_path_elements(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
proof
.0
.iter()
.map(|x| match x {
Branch::Left(value) | Branch::Right(value) => *value,
})
.collect()
}
pub fn get_identity_path_index(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<u8> {
proof
.0
.iter()
.map(|branch| match branch {
Branch::Left(_) => 0,
Branch::Right(_) => 1,
})
.collect()
}
pub fn get_tree_root(
leaf: Field,
path_elements: &[Field],
identity_path_index: &[u8],
hash_leaf: bool,
) -> Field {
let mut root = leaf;
if hash_leaf {
root = poseidon_hash(&[root]);
}
for i in 0..identity_path_index.len() {
if identity_path_index[i] == 0 {
root = poseidon_hash(&[root, path_elements[i]]);
} else {
root = poseidon_hash(&[path_elements[i], root]);
}
}
root
}
///////////////////////////////////////////////////////
// Signal/nullifier utility functions
///////////////////////////////////////////////////////
fn hash_signal(signal: &[u8]) -> Field {
let hash = keccak256(signal);
bytes_to_field(&hash)
}
/// Generates the nullifier hash
#[must_use]
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
poseidon_hash(&[external_nullifier, identity.nullifier])
}
///////////////////////////////////////////////////////
// Proof data structure and utility functions
///////////////////////////////////////////////////////
@ -137,52 +250,6 @@ impl From<Proof> for ArkProof<Bn<Parameters>> {
}
}
///////////////////////////////////////////////////////
// Merkle tree utility functions
///////////////////////////////////////////////////////
/// Helper to merkle proof into a bigint vector
/// TODO: we should create a From trait for this
pub fn get_path_elements(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
proof
.0
.iter()
.map(|x| match x {
Branch::Left(value) | Branch::Right(value) => *value,
})
.collect()
}
pub fn get_identity_path_index(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<u8> {
proof
.0
.iter()
.map(|branch| match branch {
Branch::Left(_) => 0,
Branch::Right(_) => 1,
})
.collect()
}
///////////////////////////////////////////////////////
// Signal/nullifier utility functions
///////////////////////////////////////////////////////
/// Internal helper to hash the signal to make sure it's in the field
fn hash_signal(signal: &[u8]) -> Field {
let hash = keccak256(signal);
// Shift right one byte to make it fit in the field
let mut bytes = [0_u8; 32];
bytes[1..].copy_from_slice(&hash[..31]);
Field::from_be_bytes_mod_order(&bytes)
}
/// Generates the nullifier hash
#[must_use]
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
poseidon_hash(&[external_nullifier, identity.nullifier])
}
///////////////////////////////////////////////////////
// zkSNARK utility functions
///////////////////////////////////////////////////////
@ -205,8 +272,8 @@ pub enum ProofError {
pub fn generate_proof(
mut builder: CircomBuilder<Bn254>,
proving_key: &ProvingKey<Bn254>,
rln_witness: RLNWitnessInput,
) -> Result<(Proof, Vec<Field>), ProofError> {
rln_witness: &RLNWitnessInput,
) -> Result<Proof, ProofError> {
let now = Instant::now();
builder.push_input("identity_secret", BigInt::from(rln_witness.identity_secret));
@ -227,8 +294,9 @@ pub fn generate_proof(
let circom = builder.build().unwrap();
// This can be checked against proof_values_from_witness
// Get the populated instance of the circuit with the witness
let inputs = vec_to_field(circom.get_public_inputs().unwrap());
//let inputs = vec_to_field(circom.get_public_inputs().unwrap());
println!("witness generation took: {:.2?}", now.elapsed());
@ -248,7 +316,7 @@ pub fn generate_proof(
println!("proof generation took: {:.2?}", now.elapsed());
Ok((proof, inputs))
Ok(proof)
}
/// Verifies a given RLN proof
@ -260,8 +328,18 @@ pub fn generate_proof(
pub fn verify_proof(
verifying_key: &VerifyingKey<Bn254>,
proof: Proof,
inputs: Vec<Field>,
proof_values: &RLNProofValues,
) -> Result<bool, ProofError> {
// We re-arrange proof-values according to the circuit specification
let inputs = vec![
proof_values.y,
proof_values.root,
proof_values.nullifier,
proof_values.x,
proof_values.epoch,
proof_values.rln_identifier,
];
// Check that the proof is valid
let pvk = prepare_verifying_key(verifying_key);
let pr: ArkProof<Bn254> = proof.into();

View File

@ -24,7 +24,7 @@ use crate::protocol;
// TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions
// TODO Adopt to new protocol structure
pub struct RLN {
circom: CircomCircuit<Bn254>, //Was CircomCircuit
circom: CircomCircuit<Bn254>,
params: ProvingKey<Bn254>,
tree: PoseidonTree,
}

View File

@ -49,6 +49,14 @@ pub fn str_to_field(input: String, radix: i32) -> Field {
}
}
pub fn bytes_to_fr(input: &[u8]) -> Fr {
Fr::from(BigUint::from_bytes_le(input))
}
pub fn bytes_to_field(input: &[u8]) -> Field {
to_field(bytes_to_fr(input))
}
// Arithmetic over Field elements (wrapped over arkworks algebra crate)
pub fn add(a: Field, b: Field) -> Field {