mirror of https://github.com/vacp2p/zerokit.git
fix(rln): switch to field type, add field arithmetic
This commit is contained in:
parent
cf6168bf4e
commit
7ac5b60cb6
|
@ -51,6 +51,7 @@ fn fq_from_str(s: &str) -> Fq {
|
|||
.into()
|
||||
}
|
||||
|
||||
// Extracts the element in G1 corresponding to its JSON serialization
|
||||
fn json_to_g1(json: &Value, key: &str) -> G1Affine {
|
||||
let els: Vec<String> = json
|
||||
.get(key)
|
||||
|
@ -67,6 +68,7 @@ fn json_to_g1(json: &Value, key: &str) -> G1Affine {
|
|||
))
|
||||
}
|
||||
|
||||
// Extracts the vector of G1 elements corresponding to its JSON serialization
|
||||
fn json_to_g1_vec(json: &Value, key: &str) -> Vec<G1Affine> {
|
||||
let els: Vec<Vec<String>> = json
|
||||
.get(key)
|
||||
|
@ -94,6 +96,7 @@ fn json_to_g1_vec(json: &Value, key: &str) -> Vec<G1Affine> {
|
|||
.collect()
|
||||
}
|
||||
|
||||
// Extracts the element in G2 corresponding to its JSON serialization
|
||||
fn json_to_g2(json: &Value, key: &str) -> G2Affine {
|
||||
let els: Vec<Vec<String>> = json
|
||||
.get(key)
|
||||
|
@ -116,6 +119,7 @@ fn json_to_g2(json: &Value, key: &str) -> G2Affine {
|
|||
G2Affine::from(G2Projective::new(x, y, z))
|
||||
}
|
||||
|
||||
// Computes the verification key from its JSON serialization
|
||||
fn vk_from_json(vk_path: &str) -> VerifyingKey<Bn254> {
|
||||
let json = std::fs::read_to_string(vk_path).unwrap();
|
||||
let json: Value = serde_json::from_str(&json).unwrap();
|
||||
|
@ -129,6 +133,7 @@ fn vk_from_json(vk_path: &str) -> VerifyingKey<Bn254> {
|
|||
}
|
||||
}
|
||||
|
||||
// Checks verification key to be correct with respect to proving key
|
||||
pub fn check_vk_from_zkey(verifying_key: VerifyingKey<Bn254>) {
|
||||
assert_eq!(ZKEY().vk, verifying_key);
|
||||
}
|
||||
|
|
113
rln/src/lib.rs
113
rln/src/lib.rs
|
@ -10,8 +10,7 @@ pub mod circuit;
|
|||
pub mod ffi;
|
||||
pub mod protocol;
|
||||
pub mod public;
|
||||
|
||||
pub type Field = Fr;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
@ -55,21 +54,36 @@ mod test {
|
|||
|
||||
// We check correct computation of the path and indexes
|
||||
let expected_path_elements = vec![
|
||||
"0",
|
||||
"14744269619966411208579211824598458697587494354926760081771325075741142829156",
|
||||
"7423237065226347324353380772367382631490014989348495481811164164159255474657",
|
||||
"11286972368698509976183087595462810875513684078608517520839298933882497716792",
|
||||
"3607627140608796879659380071776844901612302623152076817094415224584923813162",
|
||||
"19712377064642672829441595136074946683621277828620209496774504837737984048981",
|
||||
"20775607673010627194014556968476266066927294572720319469184847051418138353016",
|
||||
"3396914609616007258851405644437304192397291162432396347162513310381425243293",
|
||||
"21551820661461729022865262380882070649935529853313286572328683688269863701601",
|
||||
"6573136701248752079028194407151022595060682063033565181951145966236778420039",
|
||||
"12413880268183407374852357075976609371175688755676981206018884971008854919922",
|
||||
"14271763308400718165336499097156975241954733520325982997864342600795471836726",
|
||||
"20066985985293572387227381049700832219069292839614107140851619262827735677018",
|
||||
"9394776414966240069580838672673694685292165040808226440647796406499139370960",
|
||||
"11331146992410411304059858900317123658895005918277453009197229807340014528524",
|
||||
Field::from_str("0x0000000000000000000000000000000000000000000000000000000000000000")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2098f5fb9e239eab3ceac3f27b81e481dc3124d55ffed523a839ee8446b64864")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1069673dcdb12263df301a6ff584a7ec261a44cb9dc68df067a4774460b1f1e1")
|
||||
.unwrap(),
|
||||
Field::from_str("0x18f43331537ee2af2e3d758d50f72106467c6eea50371dd528d57eb2b856d238")
|
||||
.unwrap(),
|
||||
Field::from_str("0x07f9d837cb17b0d36320ffe93ba52345f1b728571a568265caac97559dbc952a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2b94cf5e8746b3f5c9631f4c5df32907a699c58c94b2ad4d7b5cec1639183f55")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2dee93c5a666459646ea7d22cca9e1bcfed71e6951b953611d11dda32ea09d78")
|
||||
.unwrap(),
|
||||
Field::from_str("0x078295e5a22b84e982cf601eb639597b8b0515a88cb5ac7fa8a4aabe3c87349d")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2fa5e5f18f6027a6501bec864564472a616b2e274a41211a444cbe3a99f3cc61")
|
||||
.unwrap(),
|
||||
Field::from_str("0x0e884376d0d8fd21ecb780389e941f66e45e7acce3e228ab3e2156a614fcd747")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1b7201da72494f1e28717ad1a52eb469f95892f957713533de6175e5da190af2")
|
||||
.unwrap(),
|
||||
Field::from_str("0x1f8d8822725e36385200c0b201249819a6e6e1e4650808b5bebc6bface7d7636")
|
||||
.unwrap(),
|
||||
Field::from_str("0x2c5d82f66c914bafb9701589ba8cfcfb6162b0a12acf88a8d0879a0471b5f85a")
|
||||
.unwrap(),
|
||||
Field::from_str("0x14c54148a0940bb820957f5adf3fa1134ef5c4aaa113f4646458f270e0bfbfd0")
|
||||
.unwrap(),
|
||||
Field::from_str("0x190d33b12f986f961e10c0ee44d8b9af11be25588cad89d416118e4bf4ebe80c")
|
||||
.unwrap(),
|
||||
];
|
||||
|
||||
let expected_identity_path_index: Vec<u8> =
|
||||
|
@ -82,6 +96,71 @@ mod test {
|
|||
assert!(tree.verify(id_commitment.into(), &merkle_proof));
|
||||
}
|
||||
|
||||
#[test]
|
||||
// We test a RLN proof generation and verification
|
||||
fn test_witness_from_json() {
|
||||
// From rln JSON witness
|
||||
// Input generated with https://github.com/oskarth/zk-kit/commit/b6a872f7160c7c14e10a0ea40acab99cbb23c9a8
|
||||
let input_json_str = r#"
|
||||
{
|
||||
"identity_secret": "12825549237505733615964533204745049909430608936689388901883576945030025938736",
|
||||
"path_elements": [
|
||||
"18622655742232062119094611065896226799484910997537830749762961454045300666333",
|
||||
"20590447254980891299813706518821659736846425329007960381537122689749540452732",
|
||||
"7423237065226347324353380772367382631490014989348495481811164164159255474657",
|
||||
"11286972368698509976183087595462810875513684078608517520839298933882497716792",
|
||||
"3607627140608796879659380071776844901612302623152076817094415224584923813162",
|
||||
"19712377064642672829441595136074946683621277828620209496774504837737984048981",
|
||||
"20775607673010627194014556968476266066927294572720319469184847051418138353016",
|
||||
"3396914609616007258851405644437304192397291162432396347162513310381425243293",
|
||||
"21551820661461729022865262380882070649935529853313286572328683688269863701601",
|
||||
"6573136701248752079028194407151022595060682063033565181951145966236778420039",
|
||||
"12413880268183407374852357075976609371175688755676981206018884971008854919922",
|
||||
"14271763308400718165336499097156975241954733520325982997864342600795471836726",
|
||||
"20066985985293572387227381049700832219069292839614107140851619262827735677018",
|
||||
"9394776414966240069580838672673694685292165040808226440647796406499139370960",
|
||||
"11331146992410411304059858900317123658895005918277453009197229807340014528524"
|
||||
],
|
||||
"identity_path_index": [
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"x": "8143228284048792769012135629627737459844825626241842423967352803501040982",
|
||||
"epoch": "0x0000005b612540fc986b42322f8cb91c2273afad58ed006fdba0c97b4b16b12f",
|
||||
"rln_identifier": "11412926387081627876309792396682864042420635853496105400039841573530884328439"
|
||||
}
|
||||
"#;
|
||||
|
||||
// We generate all relevant keys
|
||||
let proving_key = &ZKEY();
|
||||
let verification_key = &VK();
|
||||
let builder = CIRCOM();
|
||||
|
||||
// We compute witness from the json input example
|
||||
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's verify the proof
|
||||
let verified = verify_proof(verification_key, proof, inputs);
|
||||
|
||||
assert!(verified.unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
// We test a RLN proof generation and verification
|
||||
fn test_end_to_end() {
|
||||
|
|
|
@ -9,7 +9,7 @@ use ark_groth16::{
|
|||
VerifyingKey,
|
||||
};
|
||||
use ark_relations::r1cs::SynthesisError;
|
||||
use ark_std::{rand::thread_rng, UniformRand};
|
||||
use ark_std::{rand::thread_rng, str::FromStr, UniformRand};
|
||||
use color_eyre::Result;
|
||||
use ethers_core::utils::keccak256;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
|
@ -25,24 +25,52 @@ use serde::{Deserialize, Serialize};
|
|||
use std::time::Instant;
|
||||
use thiserror::Error;
|
||||
|
||||
pub use crate::utils::{str_to_field, vec_to_field, vec_to_fr};
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// RLN Witness data structure and utility functions
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct RLNWitnessInput {
|
||||
identity_secret: String,
|
||||
path_elements: Vec<String>,
|
||||
identity_secret: Field,
|
||||
path_elements: Vec<Field>,
|
||||
identity_path_index: Vec<u8>,
|
||||
x: String,
|
||||
epoch: String,
|
||||
rln_identifier: String,
|
||||
x: Field,
|
||||
epoch: Field,
|
||||
rln_identifier: Field,
|
||||
}
|
||||
|
||||
pub fn rln_witness_from_json(input_json_str: &str) -> RLNWitnessInput {
|
||||
let rln_witness: RLNWitnessInput =
|
||||
let input_json: serde_json::Value =
|
||||
serde_json::from_str(input_json_str).expect("JSON was not well-formatted");
|
||||
rln_witness
|
||||
|
||||
let identity_secret = str_to_field(input_json["identity_secret"].to_string(), 10);
|
||||
|
||||
let mut path_elements: Vec<Field> = vec![];
|
||||
for v in input_json["path_elements"].as_array().unwrap().iter() {
|
||||
path_elements.push(str_to_field(v.to_string(), 10));
|
||||
}
|
||||
|
||||
let mut identity_path_index: Vec<u8> = vec![];
|
||||
for v in input_json["identity_path_index"].as_array().unwrap().iter() {
|
||||
identity_path_index.push(v.as_u64().unwrap() as u8);
|
||||
}
|
||||
|
||||
let x = str_to_field(input_json["x"].to_string(), 10);
|
||||
|
||||
let epoch = str_to_field(input_json["epoch"].to_string(), 16);
|
||||
|
||||
let rln_identifier = str_to_field(input_json["rln_identifier"].to_string(), 10);
|
||||
|
||||
RLNWitnessInput {
|
||||
identity_secret,
|
||||
path_elements,
|
||||
identity_path_index,
|
||||
x,
|
||||
epoch,
|
||||
rln_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rln_witness_from_values(
|
||||
|
@ -55,16 +83,14 @@ pub fn rln_witness_from_values(
|
|||
let path_elements = get_path_elements(merkle_proof);
|
||||
let identity_path_index = get_identity_path_index(merkle_proof);
|
||||
|
||||
let rln_witness = RLNWitnessInput {
|
||||
identity_secret: BigInt::from(identity_secret).to_str_radix(10),
|
||||
RLNWitnessInput {
|
||||
identity_secret,
|
||||
path_elements,
|
||||
identity_path_index,
|
||||
x: BigInt::from(x).to_str_radix(10),
|
||||
epoch: format!("{:#066x}", BigInt::from(epoch)), //We format it as a padded 32 bytes hex with leading 0x for compatibility with zk-kit
|
||||
rln_identifier: BigInt::from(rln_identifier).to_str_radix(10),
|
||||
};
|
||||
|
||||
rln_witness
|
||||
x,
|
||||
epoch,
|
||||
rln_identifier,
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
|
@ -117,12 +143,12 @@ impl From<Proof> for ArkProof<Bn<Parameters>> {
|
|||
|
||||
/// 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<String> {
|
||||
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) => BigInt::from(*value).to_str_radix(10),
|
||||
Branch::Left(value) | Branch::Right(value) => *value,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
@ -180,44 +206,29 @@ pub fn generate_proof(
|
|||
mut builder: CircomBuilder<Bn254>,
|
||||
proving_key: &ProvingKey<Bn254>,
|
||||
rln_witness: RLNWitnessInput,
|
||||
) -> Result<(Proof, Vec<Fr>), ProofError> {
|
||||
) -> Result<(Proof, Vec<Field>), ProofError> {
|
||||
let now = Instant::now();
|
||||
|
||||
builder.push_input(
|
||||
"identity_secret",
|
||||
BigInt::parse_bytes(rln_witness.identity_secret.as_bytes(), 10).unwrap(),
|
||||
);
|
||||
builder.push_input("identity_secret", BigInt::from(rln_witness.identity_secret));
|
||||
|
||||
for v in rln_witness.path_elements.iter() {
|
||||
builder.push_input(
|
||||
"path_elements",
|
||||
BigInt::parse_bytes(v.as_bytes(), 10).unwrap(),
|
||||
);
|
||||
builder.push_input("path_elements", BigInt::from(*v));
|
||||
}
|
||||
|
||||
for v in rln_witness.identity_path_index.iter() {
|
||||
builder.push_input("identity_path_index", BigInt::from(*v));
|
||||
}
|
||||
|
||||
builder.push_input(
|
||||
"x",
|
||||
BigInt::parse_bytes(rln_witness.x.as_bytes(), 10).unwrap(),
|
||||
);
|
||||
builder.push_input("x", BigInt::from(rln_witness.x));
|
||||
|
||||
builder.push_input(
|
||||
"epoch",
|
||||
BigInt::parse_bytes(rln_witness.epoch.strip_prefix("0x").unwrap().as_bytes(), 16).unwrap(),
|
||||
);
|
||||
builder.push_input("epoch", BigInt::from(rln_witness.epoch));
|
||||
|
||||
builder.push_input(
|
||||
"rln_identifier",
|
||||
BigInt::parse_bytes(rln_witness.rln_identifier.as_bytes(), 10).unwrap(),
|
||||
);
|
||||
builder.push_input("rln_identifier", BigInt::from(rln_witness.rln_identifier));
|
||||
|
||||
let circom = builder.build().unwrap();
|
||||
|
||||
// Get the populated instance of the circuit with the witness
|
||||
let inputs = circom.get_public_inputs().unwrap();
|
||||
let inputs = vec_to_field(circom.get_public_inputs().unwrap());
|
||||
|
||||
println!("witness generation took: {:.2?}", now.elapsed());
|
||||
|
||||
|
@ -249,12 +260,12 @@ pub fn generate_proof(
|
|||
pub fn verify_proof(
|
||||
verifying_key: &VerifyingKey<Bn254>,
|
||||
proof: Proof,
|
||||
inputs: Vec<Fr>,
|
||||
inputs: Vec<Field>,
|
||||
) -> Result<bool, ProofError> {
|
||||
// Check that the proof is valid
|
||||
let pvk = prepare_verifying_key(verifying_key);
|
||||
let pr: ArkProof<Bn254> = proof.into();
|
||||
let verified = ark_verify_proof(&pvk, &pr, &inputs)?;
|
||||
let verified = ark_verify_proof(&pvk, &pr, &vec_to_fr(inputs))?;
|
||||
|
||||
Ok(verified)
|
||||
}
|
||||
|
|
|
@ -17,26 +17,18 @@ use serde::Deserialize;
|
|||
use serde_json;
|
||||
use std::io::{self, Read, Write};
|
||||
|
||||
use crate::circuit::{CIRCOM, ZKEY};
|
||||
use crate::protocol;
|
||||
|
||||
// TODO Add Engine here? i.e. <E: Engine> not <Bn254>
|
||||
// TODO Assuming we want to use IncrementalMerkleTree, figure out type/trait conversions
|
||||
// TODO Adopt to new protocol structure
|
||||
pub struct RLN {
|
||||
circom: CircomCircuit<Bn254>,
|
||||
circom: CircomCircuit<Bn254>, //Was CircomCircuit
|
||||
params: ProvingKey<Bn254>,
|
||||
tree: PoseidonTree,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
//#[serde(rename_all = "camelCase")]
|
||||
struct WitnessInput {
|
||||
identity_secret: String,
|
||||
path_elements: Vec<String>,
|
||||
identity_path_index: Vec<i32>,
|
||||
x: String,
|
||||
epoch: String,
|
||||
rln_identifier: String,
|
||||
}
|
||||
|
||||
// TODO Expand API to have better coverage of things needed
|
||||
|
||||
impl RLN {
|
||||
|
@ -47,19 +39,18 @@ impl RLN {
|
|||
|
||||
let builder = CircomBuilder::new(cfg);
|
||||
|
||||
// create an empty instance for setting it up
|
||||
let circom = builder.setup();
|
||||
|
||||
let mut rng = thread_rng();
|
||||
let params = generate_random_parameters::<Bn254, _, _>(circom, &mut rng).unwrap();
|
||||
let params = ZKEY();
|
||||
|
||||
let circom = builder.build().unwrap();
|
||||
|
||||
let inputs = circom.get_public_inputs().unwrap();
|
||||
println!("Public inputs {:#?} ", inputs);
|
||||
|
||||
// We compute a default empty tree
|
||||
// Probably better to pass it as parameter
|
||||
let TREE_HEIGHT = 21;
|
||||
let leaf = Field::from(0);
|
||||
let tree = PoseidonTree::new(21, leaf);
|
||||
let tree = PoseidonTree::new(TREE_HEIGHT, leaf);
|
||||
|
||||
RLN {
|
||||
circom,
|
||||
|
@ -68,6 +59,18 @@ impl RLN {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn set_tree<R: Read>(&self, _input_data: R) -> io::Result<()> {
|
||||
//Implement leaf and deserialization
|
||||
//let leaf = Leaf::deserialize(input_data).unwrap();
|
||||
|
||||
//returns H::Hash, which is a 256 bit hash value
|
||||
//let root = self.tree.root();
|
||||
// TODO Return root as LE here
|
||||
//root.write_le(&mut result_data)?;
|
||||
//println!("NYI: root le write buffer {:#?}", root);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// returns current membership root
|
||||
/// * `root` is a scalar field element in 32 bytes
|
||||
pub fn get_root<W: Write>(&self, _result_data: W) -> io::Result<()> {
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
use ark_bn254::{Bn254, Fr, Parameters};
|
||||
use ark_ff::{Fp256, PrimeField};
|
||||
use ark_std::str::FromStr;
|
||||
use ethers_core::utils::keccak256;
|
||||
use num_bigint::{BigInt, BigUint, ToBigInt};
|
||||
use semaphore::{identity::Identity, Field};
|
||||
|
||||
pub fn to_fr(el: Field) -> Fr {
|
||||
Fr::try_from(el).unwrap()
|
||||
}
|
||||
|
||||
pub fn to_field(el: Fr) -> Field {
|
||||
el.try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn vec_to_fr(v: Vec<Field>) -> Vec<Fr> {
|
||||
let mut result: Vec<Fr> = vec![];
|
||||
for el in v {
|
||||
result.push(to_fr(el));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn vec_to_field(v: Vec<Fr>) -> Vec<Field> {
|
||||
let mut result: Vec<Field> = vec![];
|
||||
for el in v {
|
||||
result.push(to_field(el));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub fn str_to_field(input: String, radix: i32) -> Field {
|
||||
assert!((radix == 10) || (radix == 16));
|
||||
|
||||
// We remove any quote present and we trim
|
||||
let input_clean = input.replace("\"", "");
|
||||
let input_clean = input_clean.trim();
|
||||
|
||||
if radix == 10 {
|
||||
Field::from_str(&format!(
|
||||
"{:01$x}",
|
||||
BigUint::from_str(input_clean).unwrap(),
|
||||
64
|
||||
))
|
||||
.unwrap()
|
||||
} else {
|
||||
let input_clean = input_clean.replace("0x", "");
|
||||
Field::from_str(&format!("{:0>64}", &input_clean)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// Arithmetic over Field elements (wrapped over arkworks algebra crate)
|
||||
|
||||
pub fn add(a: Field, b: Field) -> Field {
|
||||
to_field(to_fr(a) + to_fr(b))
|
||||
}
|
||||
|
||||
pub fn mul(a: Field, b: Field) -> Field {
|
||||
to_field(to_fr(a) * to_fr(b))
|
||||
}
|
||||
|
||||
pub fn div(a: Field, b: Field) -> Field {
|
||||
to_field(to_fr(a) / to_fr(b))
|
||||
}
|
||||
|
||||
pub fn inv(a: Field) -> Field {
|
||||
to_field(Fr::from(1) / to_fr(a))
|
||||
}
|
Loading…
Reference in New Issue