Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace the ark-zkey witness calculator with the one of iden3 #273

Merged
merged 18 commits into from
Dec 25, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
324 changes: 278 additions & 46 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions rln-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ color-eyre = "=0.6.2"
# serialization
serde_json = "1.0.48"
serde = { version = "1.0.130", features = ["derive"] }

[features]
arkzkey = []
5 changes: 2 additions & 3 deletions rln-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@ fn main() -> Result<()> {
}) => {
let mut resources: Vec<Vec<u8>> = Vec::new();
#[cfg(feature = "arkzkey")]
let filenames = ["rln.wasm", "rln_final.arkzkey", "verification_key.arkvkey"];
let filenames = ["rln_final.arkzkey", "verification_key.arkvkey"];
#[cfg(not(feature = "arkzkey"))]
let filenames = ["rln.wasm", "rln_final.zkey", "verification_key.arkvkey"];
let filenames = ["rln_final.zkey", "verification_key.arkvkey"];
for filename in filenames {
let fullpath = config.join(Path::new(filename));
let mut file = File::open(&fullpath)?;
Expand All @@ -54,7 +54,6 @@ fn main() -> Result<()> {
*tree_height,
resources[0].clone(),
resources[1].clone(),
resources[2].clone(),
tree_config_input_file,
)?);
Ok(())
Expand Down
3 changes: 3 additions & 0 deletions rln/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ color-eyre = "=0.6.2"
thiserror = "=1.0.39"

# utilities
byteorder = "1.4.3"
cfg-if = "=1.0"
num-bigint = { version = "=0.4.3", default-features = false, features = [
"rand",
Expand All @@ -51,11 +52,13 @@ once_cell = "=1.17.1"
lazy_static = "=1.4.0"
rand = "=0.8.5"
rand_chacha = "=0.3.1"
ruint = { version = "1.10.0", features = ["rand", "serde", "ark-ff-04", "num-bigint"] }
tiny-keccak = { version = "=2.0.2", features = ["keccak"] }
utils = { package = "zerokit_utils", version = "=0.5.1", path = "../utils/", default-features = false }


# serialization
prost = "0.13.1"
serde_json = "=1.0.96"
serde = { version = "=1.0.163", features = ["derive"] }

Expand Down
2 changes: 1 addition & 1 deletion rln/Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ args = ["build", "--release"]

[tasks.test_default]
command = "cargo"
args = ["test", "--release"]
args = ["test", "--release", "--", "--nocapture"]

[tasks.test_stateless]
command = "cargo"
Expand Down
14 changes: 9 additions & 5 deletions rln/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ git clone https://github.com/vacp2p/zerokit.git
cd zerokit/rln
```

### Build and Test
### ~~Build and Test~~

To build and test, run the following commands within the module folder
~~To build and test, run the following commands within the module folder~~

```bash
cargo make build
cargo make test
``` bash
cargo make build
cargo make test
```
### Currently the tests are run as follows:
``` bash
cargo make test_default
seemenkina marked this conversation as resolved.
Show resolved Hide resolved
```

### Compile ZK circuits
Expand Down
Binary file added rln/resources/tree_height_20/graph.bin
Binary file not shown.
Binary file removed rln/resources/tree_height_20/rln.wasm
Binary file not shown.
34 changes: 8 additions & 26 deletions rln/src/circuit.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This crate provides interfaces for the zero-knowledge circuit and keys

use crate::iden3calc::calc_witness;
use ark_bn254::{
Bn254, Fq as ArkFq, Fq2 as ArkFq2, Fr as ArkFr, G1Affine as ArkG1Affine,
G1Projective as ArkG1Projective, G2Affine as ArkG2Affine, G2Projective as ArkG2Projective,
Expand All @@ -9,14 +10,10 @@ use ark_relations::r1cs::ConstraintMatrices;
use ark_serialize::CanonicalDeserialize;
use cfg_if::cfg_if;
use color_eyre::{Report, Result};
use num_bigint::BigInt;

#[cfg(not(target_arch = "wasm32"))]
use {
ark_circom::WitnessCalculator,
lazy_static::lazy_static,
std::sync::{Arc, Mutex},
wasmer::{Module, Store},
};
use ::lazy_static::lazy_static;

#[cfg(feature = "arkzkey")]
use {
Expand All @@ -35,7 +32,7 @@ pub const ARKZKEY_BYTES_UNCOMPR: &[u8] =

pub const ZKEY_BYTES: &[u8] = include_bytes!("../resources/tree_height_20/rln_final.zkey");
pub const VK_BYTES: &[u8] = include_bytes!("../resources/tree_height_20/verification_key.arkvkey");
const WASM_BYTES: &[u8] = include_bytes!("../resources/tree_height_20/rln.wasm");
const GRAPH_BYTES: &[u8] = include_bytes!("../resources/tree_height_20/graph.bin");

#[cfg(not(target_arch = "wasm32"))]
lazy_static! {
Expand All @@ -53,11 +50,6 @@ lazy_static! {

#[cfg(not(target_arch = "wasm32"))]
static ref VK: VerifyingKey<Curve> = vk_from_ark_serialized(VK_BYTES).expect("Failed to read vk");

#[cfg(not(target_arch = "wasm32"))]
static ref WITNESS_CALCULATOR: Arc<Mutex<WitnessCalculator>> = {
circom_from_raw(WASM_BYTES).expect("Failed to create witness calculator")
};
}

pub const TEST_TREE_HEIGHT: usize = 20;
Expand Down Expand Up @@ -92,6 +84,10 @@ pub fn zkey_from_raw(zkey_data: &[u8]) -> Result<(ProvingKey<Curve>, ConstraintM
Ok(proving_key_and_matrices)
}

pub fn calculate_rln_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(inputs: I) -> Vec<Fr> {
calc_witness(inputs, GRAPH_BYTES)
}

// Loads the proving key
#[cfg(not(target_arch = "wasm32"))]
pub fn zkey_from_folder() -> &'static (ProvingKey<Curve>, ConstraintMatrices<Fr>) {
Expand All @@ -118,20 +114,6 @@ pub fn vk_from_folder() -> &'static VerifyingKey<Curve> {
&VK
}

// Initializes the witness calculator using a bytes vector
#[cfg(not(target_arch = "wasm32"))]
pub fn circom_from_raw(wasm_buffer: &[u8]) -> Result<Arc<Mutex<WitnessCalculator>>> {
let module = Module::new(&Store::default(), wasm_buffer)?;
let result = WitnessCalculator::from_module(module)?;
Ok(Arc::new(Mutex::new(result)))
}

// Initializes the witness calculator
#[cfg(not(target_arch = "wasm32"))]
pub fn circom_from_folder() -> &'static Arc<Mutex<WitnessCalculator>> {
&WITNESS_CALCULATOR
}

// Computes the verification key from a bytes vector containing pre-processed ark-serialized verification key
// uncompressed, unchecked
pub fn vk_from_ark_serialized(data: &[u8]) -> Result<VerifyingKey<Curve>> {
Expand Down
4 changes: 0 additions & 4 deletions rln/src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,15 +228,13 @@ pub extern "C" fn new(ctx: *mut *mut RLN) -> bool {
#[no_mangle]
pub extern "C" fn new_with_params(
tree_height: usize,
circom_buffer: *const Buffer,
zkey_buffer: *const Buffer,
vk_buffer: *const Buffer,
tree_config: *const Buffer,
ctx: *mut *mut RLN,
) -> bool {
match RLN::new_with_params(
tree_height,
circom_buffer.process().to_vec(),
zkey_buffer.process().to_vec(),
vk_buffer.process().to_vec(),
tree_config.process(),
Expand All @@ -256,13 +254,11 @@ pub extern "C" fn new_with_params(
#[cfg(feature = "stateless")]
#[no_mangle]
pub extern "C" fn new_with_params(
circom_buffer: *const Buffer,
zkey_buffer: *const Buffer,
vk_buffer: *const Buffer,
ctx: *mut *mut RLN,
) -> bool {
match RLN::new_with_params(
circom_buffer.process().to_vec(),
zkey_buffer.process().to_vec(),
vk_buffer.process().to_vec(),
) {
Expand Down
73 changes: 73 additions & 0 deletions rln/src/iden3calc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
seemenkina marked this conversation as resolved.
Show resolved Hide resolved
pub mod graph;
pub mod proto;
pub mod storage;

use ark_bn254::Fr;
use graph::Node;
use num_bigint::BigInt;
use ruint::aliases::U256;
use std::collections::HashMap;
use storage::deserialize_witnesscalc_graph;

pub type InputSignalsInfo = HashMap<String, (usize, usize)>;

pub fn calc_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
inputs: I,
graph_data: &[u8],
) -> Vec<Fr> {
let inputs: HashMap<String, Vec<U256>> = inputs
.into_iter()
.map(|(key, value)| (key, value.iter().map(|v| U256::from(v)).collect()))
.collect();

let (nodes, signals, input_mapping): (Vec<Node>, Vec<usize>, InputSignalsInfo) =
deserialize_witnesscalc_graph(std::io::Cursor::new(graph_data)).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error return, not unwrap

Copy link
Contributor Author

@AlekseiVambol AlekseiVambol Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

See #273 (comment)


let mut inputs_buffer = get_inputs_buffer(get_inputs_size(&nodes));
populate_inputs(&inputs, &input_mapping, &mut inputs_buffer);

graph::evaluate(&nodes, inputs_buffer.as_slice(), &signals)
}

fn get_inputs_size(nodes: &[Node]) -> usize {
let mut start = false;
let mut max_index = 0usize;
for &node in nodes.iter() {
if let Node::Input(i) = node {
if i > max_index {
max_index = i;
}
start = true
} else if start {
break;
}
}
max_index + 1
}

fn populate_inputs(
input_list: &HashMap<String, Vec<U256>>,
inputs_info: &InputSignalsInfo,
input_buffer: &mut [U256],
) {
for (key, value) in input_list {
let (offset, len) = inputs_info[key];
if len != value.len() {
panic!("Invalid input length for {}", key);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the justification for a panic?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, we need to get the results back, not panic

Copy link
Contributor Author

@AlekseiVambol AlekseiVambol Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree.

UPDATE: I have studied all 3 places, where panic! is used, and I think that these case are justified. It seems that these pieces of code panic in situations, which cannot happen with correctly written code and correctly generated execution graph regardless of the user's input. So the condition checks leading to these "panics" can be considered as some fundamental sanity checks.

The same is true regarding those unwraps that I have studied. By the way, the project contains 3 iden3 files with unwraps and 20 previous files with them. Thus, using these unwraps seems to be consistent with the code style.

In most cases the iden3 code uses returning Results, which is the evidence of their understanding the principles of using panic!.

Thus, there is no need in rewriting these panic! and unwrap fragments.

}

for (i, v) in value.iter().enumerate() {
input_buffer[offset + i] = *v;
}
}
}

/// Allocates inputs vec with position 0 set to 1
fn get_inputs_buffer(size: usize) -> Vec<U256> {
let mut inputs = vec![U256::ZERO; size];
inputs[0] = U256::from(1);
inputs
}
Loading
Loading