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"
|
||||
poseidon-rs = "0.0.8"
|
||||
primitive-types = "0.11.1"
|
||||
sha2 = "0.10.1"
|
||||
|
||||
ff = { package="ff_ce", version="0.11"}
|
||||
|
|
|
@ -24,20 +24,20 @@ mod test {
|
|||
use crate::protocol::*;
|
||||
use hex_literal::hex;
|
||||
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]
|
||||
fn test_merkle_proof() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
let leaf = Field::from(0);
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"hello");
|
||||
let id = Identity::from_seed(b"hello");
|
||||
|
||||
// generate merkle tree
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
tree.set(0, id.commitment().into());
|
||||
let mut tree = PoseidonTree::new(21, leaf);
|
||||
tree.set(0, id.commitment());
|
||||
|
||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
let root: Field = tree.root().into();
|
||||
|
@ -48,38 +48,38 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_semaphore() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
let leaf = Field::from(0);
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"hello");
|
||||
let id = Identity::from_seed(b"hello");
|
||||
|
||||
// generate merkle tree
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
tree.set(0, id.commitment().into());
|
||||
let mut tree = PoseidonTree::new(21, leaf);
|
||||
tree.set(0, id.commitment());
|
||||
|
||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
let root = tree.root().into();
|
||||
|
||||
// change signal and external_nullifier here
|
||||
let signal = b"xxx";
|
||||
let external_nullifier = b"appId";
|
||||
// change signal_hash and external_nullifier here
|
||||
let signal_hash = hash_to_field(b"xxx");
|
||||
let external_nullifier_hash = hash_to_field(b"appId");
|
||||
|
||||
let external_nullifier_hash =
|
||||
semaphore::protocol::hash_external_nullifier(external_nullifier);
|
||||
let nullifier_hash =
|
||||
semaphore::protocol::generate_nullifier_hash(&id, external_nullifier_hash);
|
||||
|
||||
let proof =
|
||||
semaphore::protocol::generate_proof(&id, &merkle_proof, external_nullifier, signal)
|
||||
.unwrap();
|
||||
let proof = semaphore::protocol::generate_proof(
|
||||
&id,
|
||||
&merkle_proof,
|
||||
external_nullifier_hash,
|
||||
signal_hash,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let success = semaphore::protocol::verify_proof(
|
||||
root,
|
||||
nullifier_hash,
|
||||
signal,
|
||||
external_nullifier,
|
||||
signal_hash,
|
||||
external_nullifier_hash,
|
||||
&proof,
|
||||
)
|
||||
.unwrap();
|
||||
|
@ -90,15 +90,13 @@ mod test {
|
|||
#[ignore]
|
||||
#[test]
|
||||
fn test_end_to_end() {
|
||||
const LEAF: Hash = Hash::from_bytes_be(hex!(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
));
|
||||
let leaf = Field::from(0);
|
||||
|
||||
// generate identity
|
||||
let id = Identity::new(b"hello");
|
||||
let id = Identity::from_seed(b"hello");
|
||||
|
||||
// generate merkle tree
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let mut tree = PoseidonTree::new(21, leaf);
|
||||
tree.set(0, id.commitment().into());
|
||||
|
||||
let merkle_proof = tree.proof(0).expect("proof should exist");
|
||||
|
@ -107,14 +105,14 @@ mod test {
|
|||
println!("Root: {:#}", root);
|
||||
println!("Merkle proof: {:#?}", merkle_proof);
|
||||
|
||||
// change signal and external_nullifier here
|
||||
let signal = b"xxx";
|
||||
let external_nullifier = b"appId";
|
||||
// change signal_hash and external_nullifier_hash here
|
||||
let signal_hash = hash_to_field(b"xxx");
|
||||
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 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);
|
||||
|
||||
|
@ -130,7 +128,13 @@ mod test {
|
|||
//
|
||||
// Indeed:
|
||||
// if (public_inputs.len() + 1) != pvk.vk.gamma_abc_g1.len() {
|
||||
let success =
|
||||
verify_proof(root, nullifier_hash, signal, external_nullifier, &proof).unwrap();
|
||||
let success = verify_proof(
|
||||
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_ec::bn::Bn;
|
||||
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_std::{rand::thread_rng, UniformRand};
|
||||
use color_eyre::Result;
|
||||
use ethers_core::utils::keccak256;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
use primitive_types::U256;
|
||||
use semaphore::{
|
||||
identity::Identity,
|
||||
merkle_tree::{self, Branch},
|
||||
|
@ -17,11 +20,52 @@ use semaphore::{
|
|||
poseidon_tree::PoseidonHash,
|
||||
Field,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
|
||||
// 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
|
||||
/// TODO: we should create a From trait for this
|
||||
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
|
||||
.iter()
|
||||
.map(|x| match x {
|
||||
Branch::Left(value) | Branch::Right(value) => value.into(),
|
||||
Branch::Left(value) | Branch::Right(value) => *value,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -43,19 +87,6 @@ fn hash_signal(signal: &[u8]) -> Field {
|
|||
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
|
||||
#[must_use]
|
||||
pub fn generate_nullifier_hash(identity: &Identity, external_nullifier: Field) -> Field {
|
||||
|
@ -72,11 +103,6 @@ pub enum ProofError {
|
|||
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:
|
||||
// const witness = RLN.genWitness(secretHash, merkleProof, epoch, signal, rlnIdentifier)
|
||||
// const fullProof = await RLN.genProof(witness, wasmFilePath, finalZkeyPath)
|
||||
|
@ -91,11 +117,9 @@ fn ark_to_bigint(n: Field) -> BigInt {
|
|||
pub fn generate_proof(
|
||||
identity: &Identity,
|
||||
merkle_proof: &merkle_tree::Proof<PoseidonHash>,
|
||||
external_nullifier: &[u8],
|
||||
signal: &[u8],
|
||||
) -> Result<Proof<Bn<Parameters>>, ProofError> {
|
||||
let external_nullifier = hash_external_nullifier(external_nullifier);
|
||||
let signal = hash_signal(signal);
|
||||
external_nullifier_hash: Field,
|
||||
signal_hash: Field,
|
||||
) -> Result<Proof, ProofError> {
|
||||
// TODO Fix inputs
|
||||
// Semaphore genWitness corresponds to these
|
||||
// RLN different, should be:
|
||||
|
@ -111,22 +135,18 @@ pub fn generate_proof(
|
|||
//("identityTrapdoor", vec![identity.trapdoor]),
|
||||
("path_elements", merkle_proof_to_vec(merkle_proof)),
|
||||
("identity_path_index", merkle_proof.path_index()),
|
||||
("externalNullifier", vec![external_nullifier]),
|
||||
("externalNullifier", vec![external_nullifier_hash]),
|
||||
// XXX: Assuming signal is hashed
|
||||
("x", vec![signal]),
|
||||
("x", vec![signal_hash]),
|
||||
// FIXME epoch just hardcoded to random value
|
||||
("epoch", vec![signal]),
|
||||
("epoch", vec![signal_hash]),
|
||||
// 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)| {
|
||||
(
|
||||
name.to_string(),
|
||||
values
|
||||
.iter()
|
||||
.copied()
|
||||
.map(ark_to_bigint)
|
||||
.collect::<Vec<_>>(),
|
||||
values.iter().copied().map(Into::into).collect::<Vec<_>>(),
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -147,7 +167,7 @@ pub fn generate_proof(
|
|||
|
||||
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,
|
||||
r,
|
||||
s,
|
||||
|
@ -156,7 +176,7 @@ pub fn generate_proof(
|
|||
ZKEY.1.num_constraints,
|
||||
full_assignment.as_slice(),
|
||||
)?;
|
||||
|
||||
let proof = ark_proof.into();
|
||||
println!("proof generation took: {:.2?}", now.elapsed());
|
||||
|
||||
Ok(proof)
|
||||
|
@ -173,9 +193,9 @@ pub fn generate_proof(
|
|||
pub fn verify_proof(
|
||||
root: Field,
|
||||
nullifier_hash: Field,
|
||||
signal: &[u8],
|
||||
external_nullifier: &[u8],
|
||||
proof: &Proof<Bn<Parameters>>,
|
||||
signal_hash: Field,
|
||||
external_nullifier_hash: Field,
|
||||
proof: &Proof,
|
||||
) -> Result<bool, ProofError> {
|
||||
// XXX: Why is verification key in zkey but that's not what is used in
|
||||
// verifyProof with verification_key.json? Is there a difference?
|
||||
|
@ -192,11 +212,12 @@ pub fn verify_proof(
|
|||
// epoch
|
||||
// rlnIdentifier
|
||||
let public_inputs = vec![
|
||||
root,
|
||||
nullifier_hash,
|
||||
hash_signal(signal),
|
||||
hash_external_nullifier(external_nullifier),
|
||||
root.into(),
|
||||
nullifier_hash.into(),
|
||||
signal_hash.into(),
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
///
|
||||
use crate::merkle::IncrementalMerkleTree;
|
||||
use crate::poseidon::{Poseidon as PoseidonHasher, PoseidonParams};
|
||||
use semaphore::hash::Hash;
|
||||
use semaphore::poseidon_tree::PoseidonTree;
|
||||
use semaphore::{
|
||||
hash_to_field, identity::Identity, poseidon_tree::PoseidonTree, protocol::*, Field,
|
||||
};
|
||||
|
||||
use ark_circom::{CircomBuilder, CircomCircuit, CircomConfig};
|
||||
use ark_std::rand::thread_rng;
|
||||
|
@ -72,8 +73,8 @@ impl RLN {
|
|||
let inputs = circom.get_public_inputs().unwrap();
|
||||
println!("Public inputs {:#?} ", inputs);
|
||||
|
||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let leaf = Field::from(0);
|
||||
let mut tree = PoseidonTree::new(21, leaf);
|
||||
|
||||
RLN {
|
||||
circom,
|
||||
|
@ -197,8 +198,8 @@ impl RLN {
|
|||
// let hasher = PoseidonHasher::new(poseidon_params.clone());
|
||||
// let tree = IncrementalMerkleTree::empty(hasher, merkle_depth);
|
||||
|
||||
const LEAF: Hash = Hash::from_bytes_be([0u8; 32]);
|
||||
let mut tree = PoseidonTree::new(21, LEAF);
|
||||
let leaf = Field::from(0);
|
||||
let mut tree = PoseidonTree::new(21, leaf);
|
||||
|
||||
RLN {
|
||||
circom,
|
||||
|
|
Loading…
Reference in New Issue