mirror of https://github.com/vacp2p/zerokit.git
refactor(rln): Upgrade semaphore-rs
This commit is contained in:
parent
1d8408d743
commit
45520e7869
|
@ -48,6 +48,7 @@ serde_json = "1.0.48"
|
||||||
|
|
||||||
once_cell = "1.8"
|
once_cell = "1.8"
|
||||||
poseidon-rs = "0.0.8"
|
poseidon-rs = "0.0.8"
|
||||||
|
primitive-types = "0.11.1"
|
||||||
sha2 = "0.10.1"
|
sha2 = "0.10.1"
|
||||||
|
|
||||||
ff = { package="ff_ce", version="0.11"}
|
ff = { package="ff_ce", version="0.11"}
|
||||||
|
|
|
@ -24,20 +24,20 @@ mod test {
|
||||||
use crate::protocol::*;
|
use crate::protocol::*;
|
||||||
use hex_literal::hex;
|
use hex_literal::hex;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
use semaphore::{hash::Hash, identity::Identity, poseidon_tree::PoseidonTree};
|
use semaphore::{
|
||||||
|
hash::Hash, hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, Field,
|
||||||
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_merkle_proof() {
|
fn test_merkle_proof() {
|
||||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
let leaf = Field::from(0);
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
));
|
|
||||||
|
|
||||||
// generate identity
|
// generate identity
|
||||||
let id = Identity::new(b"hello");
|
let id = Identity::from_seed(b"hello");
|
||||||
|
|
||||||
// generate merkle tree
|
// generate merkle tree
|
||||||
let mut tree = PoseidonTree::new(21, LEAF);
|
let mut tree = PoseidonTree::new(21, leaf);
|
||||||
tree.set(0, id.commitment().into());
|
tree.set(0, id.commitment());
|
||||||
|
|
||||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||||
let root: Field = tree.root().into();
|
let root: Field = tree.root().into();
|
||||||
|
@ -48,38 +48,38 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_semaphore() {
|
fn test_semaphore() {
|
||||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
let leaf = Field::from(0);
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
));
|
|
||||||
|
|
||||||
// generate identity
|
// generate identity
|
||||||
let id = Identity::new(b"hello");
|
let id = Identity::from_seed(b"hello");
|
||||||
|
|
||||||
// generate merkle tree
|
// generate merkle tree
|
||||||
let mut tree = PoseidonTree::new(21, LEAF);
|
let mut tree = PoseidonTree::new(21, leaf);
|
||||||
tree.set(0, id.commitment().into());
|
tree.set(0, id.commitment());
|
||||||
|
|
||||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||||
let root = tree.root().into();
|
let root = tree.root().into();
|
||||||
|
|
||||||
// change signal and external_nullifier here
|
// change signal_hash and external_nullifier here
|
||||||
let signal = b"xxx";
|
let signal_hash = hash_to_field(b"xxx");
|
||||||
let external_nullifier = b"appId";
|
let external_nullifier_hash = hash_to_field(b"appId");
|
||||||
|
|
||||||
let external_nullifier_hash =
|
|
||||||
semaphore::protocol::hash_external_nullifier(external_nullifier);
|
|
||||||
let nullifier_hash =
|
let nullifier_hash =
|
||||||
semaphore::protocol::generate_nullifier_hash(&id, external_nullifier_hash);
|
semaphore::protocol::generate_nullifier_hash(&id, external_nullifier_hash);
|
||||||
|
|
||||||
let proof =
|
let proof = semaphore::protocol::generate_proof(
|
||||||
semaphore::protocol::generate_proof(&id, &merkle_proof, external_nullifier, signal)
|
&id,
|
||||||
|
&merkle_proof,
|
||||||
|
external_nullifier_hash,
|
||||||
|
signal_hash,
|
||||||
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let success = semaphore::protocol::verify_proof(
|
let success = semaphore::protocol::verify_proof(
|
||||||
root,
|
root,
|
||||||
nullifier_hash,
|
nullifier_hash,
|
||||||
signal,
|
signal_hash,
|
||||||
external_nullifier,
|
external_nullifier_hash,
|
||||||
&proof,
|
&proof,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -90,15 +90,13 @@ mod test {
|
||||||
#[ignore]
|
#[ignore]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_end_to_end() {
|
fn test_end_to_end() {
|
||||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
let leaf = Field::from(0);
|
||||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
|
||||||
));
|
|
||||||
|
|
||||||
// generate identity
|
// generate identity
|
||||||
let id = Identity::new(b"hello");
|
let id = Identity::from_seed(b"hello");
|
||||||
|
|
||||||
// generate merkle tree
|
// generate merkle tree
|
||||||
let mut tree = PoseidonTree::new(21, LEAF);
|
let mut tree = PoseidonTree::new(21, leaf);
|
||||||
tree.set(0, id.commitment().into());
|
tree.set(0, id.commitment().into());
|
||||||
|
|
||||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||||
|
@ -107,14 +105,14 @@ mod test {
|
||||||
println!("Root: {:#}", root);
|
println!("Root: {:#}", root);
|
||||||
println!("Merkle proof: {:#?}", merkle_proof);
|
println!("Merkle proof: {:#?}", merkle_proof);
|
||||||
|
|
||||||
// change signal and external_nullifier here
|
// change signal_hash and external_nullifier_hash here
|
||||||
let signal = b"xxx";
|
let signal_hash = hash_to_field(b"xxx");
|
||||||
let external_nullifier = b"appId";
|
let external_nullifier_hash = hash_to_field(b"appId");
|
||||||
|
|
||||||
let external_nullifier_hash = hash_external_nullifier(external_nullifier);
|
|
||||||
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);
|
let nullifier_hash = generate_nullifier_hash(&id, external_nullifier_hash);
|
||||||
|
|
||||||
let proof = generate_proof(&id, &merkle_proof, external_nullifier, signal).unwrap();
|
let proof =
|
||||||
|
generate_proof(&id, &merkle_proof, external_nullifier_hash, signal_hash).unwrap();
|
||||||
|
|
||||||
println!("Proof: {:#?}", proof);
|
println!("Proof: {:#?}", proof);
|
||||||
|
|
||||||
|
@ -130,7 +128,13 @@ mod test {
|
||||||
//
|
//
|
||||||
// Indeed:
|
// Indeed:
|
||||||
// if (public_inputs.len() + 1) != pvk.vk.gamma_abc_g1.len() {
|
// if (public_inputs.len() + 1) != pvk.vk.gamma_abc_g1.len() {
|
||||||
let success =
|
let success = verify_proof(
|
||||||
verify_proof(root, nullifier_hash, signal, external_nullifier, &proof).unwrap();
|
root,
|
||||||
|
nullifier_hash,
|
||||||
|
signal_hash,
|
||||||
|
external_nullifier_hash,
|
||||||
|
&proof,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,15 @@ use ark_bn254::{Bn254, Parameters};
|
||||||
use ark_circom::CircomReduction;
|
use ark_circom::CircomReduction;
|
||||||
use ark_ec::bn::Bn;
|
use ark_ec::bn::Bn;
|
||||||
use ark_ff::{Fp256, PrimeField};
|
use ark_ff::{Fp256, PrimeField};
|
||||||
use ark_groth16::{create_proof_with_reduction_and_matrices, prepare_verifying_key, Proof};
|
use ark_groth16::{
|
||||||
|
create_proof_with_reduction_and_matrices, prepare_verifying_key, Proof as ArkProof,
|
||||||
|
};
|
||||||
use ark_relations::r1cs::SynthesisError;
|
use ark_relations::r1cs::SynthesisError;
|
||||||
use ark_std::{rand::thread_rng, UniformRand};
|
use ark_std::{rand::thread_rng, UniformRand};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use ethers_core::utils::keccak256;
|
use ethers_core::utils::keccak256;
|
||||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||||
|
use primitive_types::U256;
|
||||||
use semaphore::{
|
use semaphore::{
|
||||||
identity::Identity,
|
identity::Identity,
|
||||||
merkle_tree::{self, Branch},
|
merkle_tree::{self, Branch},
|
||||||
|
@ -17,11 +20,52 @@ use semaphore::{
|
||||||
poseidon_tree::PoseidonHash,
|
poseidon_tree::PoseidonHash,
|
||||||
Field,
|
Field,
|
||||||
};
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
// TODO Fields need to be updated to RLN based ones
|
// TODO Fields need to be updated to RLN based ones
|
||||||
|
|
||||||
|
// Matches the private G1Tup type in ark-circom.
|
||||||
|
pub type G1 = (U256, U256);
|
||||||
|
|
||||||
|
// Matches the private G2Tup type in ark-circom.
|
||||||
|
pub type G2 = ([U256; 2], [U256; 2]);
|
||||||
|
|
||||||
|
/// Wrap a proof object so we have serde support
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub struct Proof(G1, G2, G1);
|
||||||
|
|
||||||
|
impl From<ArkProof<Bn<Parameters>>> for Proof {
|
||||||
|
fn from(proof: ArkProof<Bn<Parameters>>) -> Self {
|
||||||
|
let proof = ark_circom::ethereum::Proof::from(proof);
|
||||||
|
let (a, b, c) = proof.as_tuple();
|
||||||
|
Self(a, b, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Proof> for ArkProof<Bn<Parameters>> {
|
||||||
|
fn from(proof: Proof) -> Self {
|
||||||
|
let eth_proof = ark_circom::ethereum::Proof {
|
||||||
|
a: ark_circom::ethereum::G1 {
|
||||||
|
x: proof.0 .0,
|
||||||
|
y: proof.0 .1,
|
||||||
|
},
|
||||||
|
#[rustfmt::skip] // Rustfmt inserts some confusing spaces
|
||||||
|
b: ark_circom::ethereum::G2 {
|
||||||
|
// The order of coefficients is flipped.
|
||||||
|
x: [proof.1.0[1], proof.1.0[0]],
|
||||||
|
y: [proof.1.1[1], proof.1.1[0]],
|
||||||
|
},
|
||||||
|
c: ark_circom::ethereum::G1 {
|
||||||
|
x: proof.2 .0,
|
||||||
|
y: proof.2 .1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
eth_proof.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Helper to merkle proof into a bigint vector
|
/// Helper to merkle proof into a bigint vector
|
||||||
/// TODO: we should create a From trait for this
|
/// TODO: we should create a From trait for this
|
||||||
fn merkle_proof_to_vec(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
|
fn merkle_proof_to_vec(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
|
||||||
|
@ -29,7 +73,7 @@ fn merkle_proof_to_vec(proof: &merkle_tree::Proof<PoseidonHash>) -> Vec<Field> {
|
||||||
.0
|
.0
|
||||||
.iter()
|
.iter()
|
||||||
.map(|x| match x {
|
.map(|x| match x {
|
||||||
Branch::Left(value) | Branch::Right(value) => value.into(),
|
Branch::Left(value) | Branch::Right(value) => *value,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
@ -43,19 +87,6 @@ fn hash_signal(signal: &[u8]) -> Field {
|
||||||
Field::from_be_bytes_mod_order(&bytes)
|
Field::from_be_bytes_mod_order(&bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Internal helper to hash the external nullifier
|
|
||||||
#[must_use]
|
|
||||||
pub fn hash_external_nullifier(nullifier: &[u8]) -> Field {
|
|
||||||
// Hash input to 256 bits.
|
|
||||||
let mut hash = keccak256(nullifier);
|
|
||||||
// Clear first four bytes to make sure the hash is in the field.
|
|
||||||
for byte in &mut hash[0..4] {
|
|
||||||
*byte = 0;
|
|
||||||
}
|
|
||||||
// Convert to field element.
|
|
||||||
Fp256::from_be_bytes_mod_order(&hash)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generates the nullifier hash
|
/// Generates the nullifier hash
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
|
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
|
||||||
|
@ -72,11 +103,6 @@ pub enum ProofError {
|
||||||
SynthesisError(#[from] SynthesisError),
|
SynthesisError(#[from] SynthesisError),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ark_to_bigint(n: Field) -> BigInt {
|
|
||||||
let n: BigUint = n.into();
|
|
||||||
n.to_bigint().expect("conversion always succeeds for uint")
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX This is different from zk-kit API:
|
// XXX This is different from zk-kit API:
|
||||||
// const witness = RLN.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
// const witness = RLN.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||||
// const fullProof = await RLN.genProof(witness, wasmFilePath, finalZkeyPath)
|
// const fullProof = await RLN.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||||
|
@ -91,11 +117,9 @@ fn ark_to_bigint(n: Field) -> BigInt {
|
||||||
pub fn generate_proof(
|
pub fn generate_proof(
|
||||||
identity: &Identity,
|
identity: &Identity,
|
||||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
||||||
external_nullifier: &[u8],
|
external_nullifier_hash: Field,
|
||||||
signal: &[u8],
|
signal_hash: Field,
|
||||||
) -> Result<Proof<Bn<Parameters>>, ProofError> {
|
) -> Result<Proof, ProofError> {
|
||||||
let external_nullifier = hash_external_nullifier(external_nullifier);
|
|
||||||
let signal = hash_signal(signal);
|
|
||||||
// TODO Fix inputs
|
// TODO Fix inputs
|
||||||
// Semaphore genWitness corresponds to these
|
// Semaphore genWitness corresponds to these
|
||||||
// RLN different, should be:
|
// RLN different, should be:
|
||||||
|
@ -111,22 +135,18 @@ pub fn generate_proof(
|
||||||
//("identityTrapdoor", vec![identity.trapdoor]),
|
//("identityTrapdoor", vec![identity.trapdoor]),
|
||||||
("path_elements", merkle_proof_to_vec(merkle_proof)),
|
("path_elements", merkle_proof_to_vec(merkle_proof)),
|
||||||
("identity_path_index", merkle_proof.path_index()),
|
("identity_path_index", merkle_proof.path_index()),
|
||||||
("externalNullifier", vec![external_nullifier]),
|
("externalNullifier", vec![external_nullifier_hash]),
|
||||||
// XXX: Assuming signal is hashed
|
// XXX: Assuming signal is hashed
|
||||||
("x", vec![signal]),
|
("x", vec![signal_hash]),
|
||||||
// FIXME epoch just hardcoded to random value
|
// FIXME epoch just hardcoded to random value
|
||||||
("epoch", vec![signal]),
|
("epoch", vec![signal_hash]),
|
||||||
// FIXME rln_identifier just hardcoded to random value
|
// FIXME rln_identifier just hardcoded to random value
|
||||||
("rln_identifier", vec![signal]),
|
("rln_identifier", vec![signal_hash]),
|
||||||
];
|
];
|
||||||
let inputs = inputs.into_iter().map(|(name, values)| {
|
let inputs = inputs.into_iter().map(|(name, values)| {
|
||||||
(
|
(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
values
|
values.iter().copied().map(Into::into).collect::<Vec<_>>(),
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.map(ark_to_bigint)
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -147,7 +167,7 @@ pub fn generate_proof(
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
|
|
||||||
let proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
|
let ark_proof = create_proof_with_reduction_and_matrices::<_, CircomReduction>(
|
||||||
&ZKEY.0,
|
&ZKEY.0,
|
||||||
r,
|
r,
|
||||||
s,
|
s,
|
||||||
|
@ -156,7 +176,7 @@ pub fn generate_proof(
|
||||||
ZKEY.1.num_constraints,
|
ZKEY.1.num_constraints,
|
||||||
full_assignment.as_slice(),
|
full_assignment.as_slice(),
|
||||||
)?;
|
)?;
|
||||||
|
let proof = ark_proof.into();
|
||||||
println!("proof generation took: {:.2?}", now.elapsed());
|
println!("proof generation took: {:.2?}", now.elapsed());
|
||||||
|
|
||||||
Ok(proof)
|
Ok(proof)
|
||||||
|
@ -173,9 +193,9 @@ pub fn generate_proof(
|
||||||
pub fn verify_proof(
|
pub fn verify_proof(
|
||||||
root: Field,
|
root: Field,
|
||||||
nullifier_hash: Field,
|
nullifier_hash: Field,
|
||||||
signal: &[u8],
|
signal_hash: Field,
|
||||||
external_nullifier: &[u8],
|
external_nullifier_hash: Field,
|
||||||
proof: &Proof<Bn<Parameters>>,
|
proof: &Proof,
|
||||||
) -> Result<bool, ProofError> {
|
) -> Result<bool, ProofError> {
|
||||||
// XXX: Why is verification key in zkey but that's not what is used in
|
// XXX: Why is verification key in zkey but that's not what is used in
|
||||||
// verifyProof with verification_key.json? Is there a difference?
|
// verifyProof with verification_key.json? Is there a difference?
|
||||||
|
@ -192,11 +212,12 @@ pub fn verify_proof(
|
||||||
// epoch
|
// epoch
|
||||||
// rlnIdentifier
|
// rlnIdentifier
|
||||||
let public_inputs = vec![
|
let public_inputs = vec![
|
||||||
root,
|
root.into(),
|
||||||
nullifier_hash,
|
nullifier_hash.into(),
|
||||||
hash_signal(signal),
|
signal_hash.into(),
|
||||||
hash_external_nullifier(external_nullifier),
|
external_nullifier_hash.into(),
|
||||||
];
|
];
|
||||||
let result = ark_groth16::verify_proof(&pvk, proof, &public_inputs)?;
|
let ark_proof = (*proof).into();
|
||||||
|
let result = ark_groth16::verify_proof(&pvk, &ark_proof, &public_inputs[..])?;
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
///
|
///
|
||||||
use crate::merkle::IncrementalMerkleTree;
|
use crate::merkle::IncrementalMerkleTree;
|
||||||
use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams};
|
use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams};
|
||||||
use semaphore::hash::Hash;
|
use semaphore::{
|
||||||
use semaphore::poseidon_tree::PoseidonTree;
|
hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, protocol::*, Field,
|
||||||
|
};
|
||||||
|
|
||||||
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
|
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
|
||||||
use ark_std::rand::thread_rng;
|
use ark_std::rand::thread_rng;
|
||||||
|
@ -72,8 +73,8 @@ impl RLN {
|
||||||
let inputs = circom.get_public_inputs().unwrap();
|
let inputs = circom.get_public_inputs().unwrap();
|
||||||
println!("Public inputs {:#?} ", inputs);
|
println!("Public inputs {:#?} ", inputs);
|
||||||
|
|
||||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
let leaf = Field::from(0);
|
||||||
let mut tree = PoseidonTree::new(21, LEAF);
|
let mut tree = PoseidonTree::new(21, leaf);
|
||||||
|
|
||||||
RLN {
|
RLN {
|
||||||
circom,
|
circom,
|
||||||
|
@ -197,8 +198,8 @@ impl RLN {
|
||||||
// let hasher = PoseidonHasher::new(poseidon_params.clone());
|
// let hasher = PoseidonHasher::new(poseidon_params.clone());
|
||||||
// let tree = IncrementalMerkleTree::empty(hasher, merkle_depth);
|
// let tree = IncrementalMerkleTree::empty(hasher, merkle_depth);
|
||||||
|
|
||||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
let leaf = Field::from(0);
|
||||||
let mut tree = PoseidonTree::new(21, LEAF);
|
let mut tree = PoseidonTree::new(21, leaf);
|
||||||
|
|
||||||
RLN {
|
RLN {
|
||||||
circom,
|
circom,
|
||||||
|
|
Loading…
Reference in New Issue