mirror of https://github.com/vacp2p/zerokit.git
feat(rln): public input computation from witness
This commit is contained in:
parent
7ac5b60cb6
commit
30d820aeb9
|
@ -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"] }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue