mirror of
https://github.com/logos-storage/circom-compat.git
synced 2026-01-08 16:03:10 +00:00
Release 0.5 (#68)
Co-authored-by: jakehemmerle <jakehemmerle@protonmail.com> Co-authored-by: Carlos Pérez <37264926+CPerezz@users.noreply.github.com>
This commit is contained in:
parent
936e9de410
commit
03939d583e
24
.github/workflows/ci.yml
vendored
24
.github/workflows/ci.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
|||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: 1.75.0
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
|
|
||||||
# Install for Anvil
|
# Install for Anvil
|
||||||
@ -34,11 +34,6 @@ jobs:
|
|||||||
export PATH=$HOME/bin:$PATH
|
export PATH=$HOME/bin:$PATH
|
||||||
cargo test
|
cargo test
|
||||||
|
|
||||||
- name: cargo test circom 2 feature flag
|
|
||||||
run: |
|
|
||||||
export PATH=$HOME/bin:$PATH
|
|
||||||
cargo test --features circom-2
|
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
@ -48,10 +43,25 @@ jobs:
|
|||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
profile: minimal
|
profile: minimal
|
||||||
toolchain: 1.75.0
|
toolchain: stable
|
||||||
override: true
|
override: true
|
||||||
components: rustfmt, clippy
|
components: rustfmt, clippy
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
- name: cargo clippy
|
- name: cargo clippy
|
||||||
run: cargo clippy -- -D warnings
|
run: cargo clippy -- -D warnings
|
||||||
|
|
||||||
|
wasm32-wasip1-check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
- name: Install stable toolchain
|
||||||
|
uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
target: wasm32-wasip1
|
||||||
|
- name: Check wasm32-wasip compilation
|
||||||
|
run: cargo check --target wasm32-wasip1 --no-default-features --features="wasm"
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
*.ptau
|
||||||
32
Cargo.toml
32
Cargo.toml
@ -1,32 +1,35 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ark-circom"
|
name = "ark-circom"
|
||||||
version = "0.1.0"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "Arkworks bindings to Circom's R1CS, for Groth16 Proof and Witness generation in Rust"
|
description = "Arkworks bindings to Circom's R1CS, for Groth16 Proof and Witness generation in Rust"
|
||||||
|
homepage = "https://arkworks.rs"
|
||||||
|
repository = "https://github.com/arkworks-rs/circom-compat"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
resolver = "2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# WASM operations
|
# WASM operations
|
||||||
wasmer = "4.4.0"
|
wasmer = { version = "4.4.0", default-features = false }
|
||||||
wasmer-wasix = { version = "0.28.0" }
|
wasmer-wasix = { version = "0.28.0", default-features = false }
|
||||||
fnv = { version = "1.0.7", default-features = false }
|
fnv = { version = "1.0.7", default-features = false }
|
||||||
num = { version = "0.4.3" }
|
num = { version = "0.4.3" }
|
||||||
num-traits = { version = "0.2.16", default-features = false }
|
num-traits = { version = "0.2.16", default-features = false }
|
||||||
num-bigint = { version = "0.4.3", default-features = false, features = ["rand"] }
|
num-bigint = { version = "0.4.3", default-features = false, features = ["rand"] }
|
||||||
|
|
||||||
# ZKP Generation
|
# ZKP Generation
|
||||||
ark-crypto-primitives = { version = "0.4.0" }
|
ark-crypto-primitives = { version = "0.5.0" }
|
||||||
ark-ec = { version = "0.4.2", default-features = false, features = ["parallel"] }
|
ark-ff = { version = "0.5.0", default-features = false, features = ["parallel", "asm"] }
|
||||||
ark-ff = { version = "0.4.2", default-features = false, features = ["parallel", "asm"] }
|
ark-ec = { version = "0.5.0", default-features = false, features = ["parallel"] }
|
||||||
ark-std = { version = "0.4.0", default-features = false, features = ["parallel"] }
|
ark-std = { version = "0.5.0", default-features = false, features = ["parallel"] }
|
||||||
ark-bn254 = { version = "0.4.0" }
|
ark-bn254 = { version = "0.5.0" }
|
||||||
ark-groth16 = { version = "0.4.0", default-features = false, features = ["parallel"] }
|
ark-groth16 = { version = "0.5.0", default-features = false, features = ["parallel"] }
|
||||||
ark-poly = { version = "0.4.2", default-features = false, features = ["parallel"] }
|
ark-poly = { version = "0.5.0", default-features = false, features = ["parallel"] }
|
||||||
ark-relations = { version = "0.4.0", default-features = false }
|
ark-relations = { version = "0.5.0", default-features = false }
|
||||||
ark-serialize = { version = "0.4.2", default-features = false }
|
ark-serialize = { version = "0.5.0", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
# decoding of data
|
# decoding of data
|
||||||
@ -54,8 +57,7 @@ name = "groth16"
|
|||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["wasmer/default", "circom-2", "ethereum"]
|
default = ["wasmer/default", "ethereum", "wasmer-wasix/default"]
|
||||||
wasm = ["wasmer/js-default"]
|
wasm = ["wasmer/js-default", "wasmer-wasix/js"]
|
||||||
bench-complex-all = []
|
bench-complex-all = []
|
||||||
circom-2 = []
|
|
||||||
ethereum = ["ethers-core"]
|
ethereum = ["ethers-core"]
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Clone the repository and run `cd ark-circom/ && cargo doc --open`
|
|||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
ark-circom = { git = "https://github.com/gakonst/ark-circom.git" }
|
ark-circom = "0.5.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
@ -66,6 +66,13 @@ Tests require the following installed:
|
|||||||
- [x] Proof generations and verification using Arkworks
|
- [x] Proof generations and verification using Arkworks
|
||||||
- [ ] CLI for common operations
|
- [ ] CLI for common operations
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
The prover key generated by circom differs from the one generated by arkworks' groth16 library. While the format is the same, it represents different values.
|
||||||
|
Circom 'prepares' the powers of tau by converting them to Lagrange base, i.e. from `s^i.G` -> `L_i(s).G`. This affects the witness generation process, and the caller needs to ensure the correct `R1CSToQAP` implementer is used:
|
||||||
|
- use [`CircomReduction`](https://github.com/arkworks-rs/circom-compat/blob/b892c62597687c23341cda1e8e89d58bb6428f36/src/circom/qap.rs#L12) for working with circom-generated files,
|
||||||
|
- use [`LibsnarkReduction`](https://github.com/arkworks-rs/groth16/blob/5272c935bda290a24cd18d0a3f994b0af70d5f27/src/r1cs_to_qap.rs#L101) for setup produced using the arkworks backend.
|
||||||
|
|
||||||
## Acknowledgements
|
## Acknowledgements
|
||||||
|
|
||||||
This library would not have been possibly without the great work done in:
|
This library would not have been possibly without the great work done in:
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "stable"
|
channel = "stable"
|
||||||
version = "1.75.0"
|
|
||||||
@ -92,7 +92,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn satisfied() {
|
async fn satisfied() {
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/mycircuit.wasm",
|
"./test-vectors/mycircuit_js/mycircuit.wasm",
|
||||||
"./test-vectors/mycircuit.r1cs",
|
"./test-vectors/mycircuit.r1cs",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -7,164 +7,57 @@ pub struct Wasm {
|
|||||||
pub memory: Memory,
|
pub memory: Memory,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CircomBase {
|
impl Wasm {
|
||||||
fn init(&self, store: &mut Store, sanity_check: bool) -> Result<()>;
|
pub(crate) fn get_field_num_len32(&self, store: &mut Store) -> Result<u32> {
|
||||||
fn func(&self, name: &str) -> &Function;
|
|
||||||
fn get_n_vars(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
fn get_u32(&self, store: &mut Store, name: &str) -> Result<u32>;
|
|
||||||
// Only exists natively in Circom2, hardcoded for Circom
|
|
||||||
fn get_version(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Circom1 {
|
|
||||||
fn get_ptr_witness(&self, store: &mut Store, w: u32) -> Result<u32>;
|
|
||||||
fn get_fr_len(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
fn get_signal_offset32(
|
|
||||||
&self,
|
|
||||||
store: &mut Store,
|
|
||||||
p_sig_offset: u32,
|
|
||||||
component: u32,
|
|
||||||
hash_msb: u32,
|
|
||||||
hash_lsb: u32,
|
|
||||||
) -> Result<()>;
|
|
||||||
fn set_signal(
|
|
||||||
&self,
|
|
||||||
store: &mut Store,
|
|
||||||
c_idx: u32,
|
|
||||||
component: u32,
|
|
||||||
signal: u32,
|
|
||||||
p_val: u32,
|
|
||||||
) -> Result<()>;
|
|
||||||
fn get_ptr_raw_prime(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Circom2 {
|
|
||||||
fn get_field_num_len32(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
fn get_raw_prime(&self, store: &mut Store) -> Result<()>;
|
|
||||||
fn read_shared_rw_memory(&self, store: &mut Store, i: u32) -> Result<u32>;
|
|
||||||
fn write_shared_rw_memory(&self, store: &mut Store, i: u32, v: u32) -> Result<()>;
|
|
||||||
fn set_input_signal(&self, store: &mut Store, hmsb: u32, hlsb: u32, pos: u32) -> Result<()>;
|
|
||||||
fn get_witness(&self, store: &mut Store, i: u32) -> Result<()>;
|
|
||||||
fn get_witness_size(&self, store: &mut Store) -> Result<u32>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Circom1 for Wasm {
|
|
||||||
fn get_fr_len(&self, store: &mut Store) -> Result<u32> {
|
|
||||||
self.get_u32(store, "getFrLen")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ptr_raw_prime(&self, store: &mut Store) -> Result<u32> {
|
|
||||||
self.get_u32(store, "getPRawPrime")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ptr_witness(&self, store: &mut Store, w: u32) -> Result<u32> {
|
|
||||||
let func = self.func("getPWitness");
|
|
||||||
|
|
||||||
let res = func.call(store, &[w.into()])?;
|
|
||||||
|
|
||||||
Ok(res[0].unwrap_i32() as u32)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_signal_offset32(
|
|
||||||
&self,
|
|
||||||
store: &mut Store,
|
|
||||||
p_sig_offset: u32,
|
|
||||||
component: u32,
|
|
||||||
hash_msb: u32,
|
|
||||||
hash_lsb: u32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let func = self.func("getSignalOffset32");
|
|
||||||
func.call(
|
|
||||||
store,
|
|
||||||
&[
|
|
||||||
p_sig_offset.into(),
|
|
||||||
component.into(),
|
|
||||||
hash_msb.into(),
|
|
||||||
hash_lsb.into(),
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_signal(
|
|
||||||
&self,
|
|
||||||
store: &mut Store,
|
|
||||||
c_idx: u32,
|
|
||||||
component: u32,
|
|
||||||
signal: u32,
|
|
||||||
p_val: u32,
|
|
||||||
) -> Result<()> {
|
|
||||||
let func = self.func("setSignal");
|
|
||||||
func.call(
|
|
||||||
store,
|
|
||||||
&[c_idx.into(), component.into(), signal.into(), p_val.into()],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
impl Circom2 for Wasm {
|
|
||||||
fn get_field_num_len32(&self, store: &mut Store) -> Result<u32> {
|
|
||||||
self.get_u32(store, "getFieldNumLen32")
|
self.get_u32(store, "getFieldNumLen32")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_raw_prime(&self, store: &mut Store) -> Result<()> {
|
pub(crate) fn get_raw_prime(&self, store: &mut Store) -> Result<()> {
|
||||||
let func = self.func("getRawPrime");
|
let func = self.func("getRawPrime");
|
||||||
func.call(store, &[])?;
|
func.call(store, &[])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_shared_rw_memory(&self, store: &mut Store, i: u32) -> Result<u32> {
|
pub(crate) fn read_shared_rw_memory(&self, store: &mut Store, i: u32) -> Result<u32> {
|
||||||
let func = self.func("readSharedRWMemory");
|
let func = self.func("readSharedRWMemory");
|
||||||
let result = func.call(store, &[i.into()])?;
|
let result = func.call(store, &[i.into()])?;
|
||||||
Ok(result[0].unwrap_i32() as u32)
|
Ok(result[0].unwrap_i32() as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_shared_rw_memory(&self, store: &mut Store, i: u32, v: u32) -> Result<()> {
|
pub(crate) fn write_shared_rw_memory(&self, store: &mut Store, i: u32, v: u32) -> Result<()> {
|
||||||
let func = self.func("writeSharedRWMemory");
|
let func = self.func("writeSharedRWMemory");
|
||||||
func.call(store, &[i.into(), v.into()])?;
|
func.call(store, &[i.into(), v.into()])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_input_signal(&self, store: &mut Store, hmsb: u32, hlsb: u32, pos: u32) -> Result<()> {
|
pub(crate) fn set_input_signal(
|
||||||
|
&self,
|
||||||
|
store: &mut Store,
|
||||||
|
hmsb: u32,
|
||||||
|
hlsb: u32,
|
||||||
|
pos: u32,
|
||||||
|
) -> Result<()> {
|
||||||
let func = self.func("setInputSignal");
|
let func = self.func("setInputSignal");
|
||||||
func.call(store, &[hmsb.into(), hlsb.into(), pos.into()])?;
|
func.call(store, &[hmsb.into(), hlsb.into(), pos.into()])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_witness(&self, store: &mut Store, i: u32) -> Result<()> {
|
pub(crate) fn get_witness(&self, store: &mut Store, i: u32) -> Result<()> {
|
||||||
let func = self.func("getWitness");
|
let func = self.func("getWitness");
|
||||||
func.call(store, &[i.into()])?;
|
func.call(store, &[i.into()])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_witness_size(&self, store: &mut Store) -> Result<u32> {
|
pub(crate) fn get_witness_size(&self, store: &mut Store) -> Result<u32> {
|
||||||
self.get_u32(store, "getWitnessSize")
|
self.get_u32(store, "getWitnessSize")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl CircomBase for Wasm {
|
pub(crate) fn init(&self, store: &mut Store, sanity_check: bool) -> Result<()> {
|
||||||
fn init(&self, store: &mut Store, sanity_check: bool) -> Result<()> {
|
|
||||||
let func = self.func("init");
|
let func = self.func("init");
|
||||||
func.call(store, &[Value::I32(sanity_check as i32)])?;
|
func.call(store, &[Value::I32(sanity_check as i32)])?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_n_vars(&self, store: &mut Store) -> Result<u32> {
|
|
||||||
self.get_u32(store, "getNVars")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to version 1 if it isn't explicitly defined
|
|
||||||
fn get_version(&self, store: &mut Store) -> Result<u32> {
|
|
||||||
match self.exports.get_function("getVersion") {
|
|
||||||
Ok(func) => Ok(func.call(store, &[])?[0].unwrap_i32() as u32),
|
|
||||||
Err(_) => Ok(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_u32(&self, store: &mut Store, name: &str) -> Result<u32> {
|
fn get_u32(&self, store: &mut Store, name: &str) -> Result<u32> {
|
||||||
let func = &self.func(name);
|
let func = &self.func(name);
|
||||||
let result = func.call(store, &[])?;
|
let result = func.call(store, &[])?;
|
||||||
|
|||||||
@ -5,14 +5,8 @@ mod memory;
|
|||||||
pub(super) use memory::SafeMemory;
|
pub(super) use memory::SafeMemory;
|
||||||
|
|
||||||
mod circom;
|
mod circom;
|
||||||
pub(super) use circom::CircomBase;
|
|
||||||
pub use circom::Wasm;
|
pub use circom::Wasm;
|
||||||
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
pub(super) use circom::Circom2;
|
|
||||||
|
|
||||||
pub(super) use circom::Circom1;
|
|
||||||
|
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use std::hash::Hasher;
|
use std::hash::Hasher;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use super::{fnv, CircomBase, SafeMemory, Wasm};
|
use super::{fnv, SafeMemory, Wasm};
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use num_bigint::BigInt;
|
use num_bigint::BigInt;
|
||||||
@ -6,19 +6,13 @@ use num_traits::Zero;
|
|||||||
use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, RuntimeError, Store};
|
use wasmer::{imports, Function, Instance, Memory, MemoryType, Module, RuntimeError, Store};
|
||||||
use wasmer_wasix::WasiEnv;
|
use wasmer_wasix::WasiEnv;
|
||||||
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
use num::ToPrimitive;
|
use num::ToPrimitive;
|
||||||
|
|
||||||
use super::Circom1;
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
use super::Circom2;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WitnessCalculator {
|
pub struct WitnessCalculator {
|
||||||
pub instance: Wasm,
|
pub instance: Wasm,
|
||||||
pub memory: Option<SafeMemory>,
|
pub memory: Option<SafeMemory>,
|
||||||
pub n64: u32,
|
pub n64: u32,
|
||||||
pub circom_version: u32,
|
|
||||||
pub prime: BigInt,
|
pub prime: BigInt,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +22,6 @@ pub struct WitnessCalculator {
|
|||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
struct ExitCode(u32);
|
struct ExitCode(u32);
|
||||||
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
fn from_array32(arr: Vec<u32>) -> BigInt {
|
fn from_array32(arr: Vec<u32>) -> BigInt {
|
||||||
let mut res = BigInt::zero();
|
let mut res = BigInt::zero();
|
||||||
let radix = BigInt::from(0x100000000u64);
|
let radix = BigInt::from(0x100000000u64);
|
||||||
@ -38,7 +31,6 @@ fn from_array32(arr: Vec<u32>) -> BigInt {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
fn to_array32(s: &BigInt, size: usize) -> Vec<u32> {
|
fn to_array32(s: &BigInt, size: usize) -> Vec<u32> {
|
||||||
let mut res = vec![0; size];
|
let mut res = vec![0; size];
|
||||||
let mut rem = s.clone();
|
let mut rem = s.clone();
|
||||||
@ -101,78 +93,24 @@ impl WitnessCalculator {
|
|||||||
Ok(wasm)
|
Ok(wasm)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_from_wasm(store: &mut Store, wasm: Wasm) -> Result<Self> {
|
pub fn new_from_wasm(store: &mut Store, instance: Wasm) -> Result<Self> {
|
||||||
let version = wasm.get_version(store).unwrap_or(1);
|
let n32 = instance.get_field_num_len32(store)?;
|
||||||
// Circom 2 feature flag with version 2
|
instance.get_raw_prime(store)?;
|
||||||
#[cfg(feature = "circom-2")]
|
let mut arr = vec![0; n32 as usize];
|
||||||
fn new_circom2(
|
for i in 0..n32 {
|
||||||
instance: Wasm,
|
let res = instance.read_shared_rw_memory(store, i)?;
|
||||||
store: &mut Store,
|
arr[(n32 as usize) - (i as usize) - 1] = res;
|
||||||
version: u32,
|
|
||||||
) -> Result<WitnessCalculator> {
|
|
||||||
let n32 = instance.get_field_num_len32(store)?;
|
|
||||||
instance.get_raw_prime(store)?;
|
|
||||||
let mut arr = vec![0; n32 as usize];
|
|
||||||
for i in 0..n32 {
|
|
||||||
let res = instance.read_shared_rw_memory(store, i)?;
|
|
||||||
arr[(n32 as usize) - (i as usize) - 1] = res;
|
|
||||||
}
|
|
||||||
let prime = from_array32(arr);
|
|
||||||
|
|
||||||
let n64 = ((prime.bits() - 1) / 64 + 1) as u32;
|
|
||||||
|
|
||||||
Ok(WitnessCalculator {
|
|
||||||
instance,
|
|
||||||
memory: None,
|
|
||||||
n64,
|
|
||||||
circom_version: version,
|
|
||||||
prime,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
let prime = from_array32(arr);
|
||||||
|
|
||||||
fn new_circom1(
|
let n64 = ((prime.bits() - 1) / 64 + 1) as u32;
|
||||||
instance: Wasm,
|
|
||||||
store: &mut Store,
|
|
||||||
version: u32,
|
|
||||||
) -> Result<WitnessCalculator> {
|
|
||||||
// Fallback to Circom 1 behavior
|
|
||||||
let n32 = (instance.get_fr_len(store)? >> 2) - 2;
|
|
||||||
let mut safe_memory =
|
|
||||||
SafeMemory::new(instance.memory.clone(), n32 as usize, BigInt::zero());
|
|
||||||
let ptr = instance.get_ptr_raw_prime(store)?;
|
|
||||||
let prime = safe_memory.read_big(store, ptr as usize, n32 as usize)?;
|
|
||||||
|
|
||||||
let n64 = ((prime.bits() - 1) / 64 + 1) as u32;
|
Ok(WitnessCalculator {
|
||||||
safe_memory.prime = prime.clone();
|
instance,
|
||||||
|
memory: None,
|
||||||
Ok(WitnessCalculator {
|
n64,
|
||||||
instance,
|
prime,
|
||||||
memory: Some(safe_memory),
|
})
|
||||||
n64,
|
|
||||||
circom_version: version,
|
|
||||||
prime,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Three possibilities:
|
|
||||||
// a) Circom 2 feature flag enabled, WASM runtime version 2
|
|
||||||
// b) Circom 2 feature flag enabled, WASM runtime version 1
|
|
||||||
// c) Circom 1 default behavior
|
|
||||||
//
|
|
||||||
// Once Circom 2 support is more stable, feature flag can be removed
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "circom-2")] {
|
|
||||||
match version {
|
|
||||||
2 => new_circom2(wasm, store, version),
|
|
||||||
1 => new_circom1(wasm, store, version),
|
|
||||||
|
|
||||||
_ => panic!("Unknown Circom version")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
new_circom1(instance, memory, version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn calculate_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
|
pub fn calculate_witness<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
|
||||||
@ -183,77 +121,6 @@ impl WitnessCalculator {
|
|||||||
) -> Result<Vec<BigInt>> {
|
) -> Result<Vec<BigInt>> {
|
||||||
self.instance.init(store, sanity_check)?;
|
self.instance.init(store, sanity_check)?;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "circom-2")] {
|
|
||||||
match self.circom_version {
|
|
||||||
2 => self.calculate_witness_circom2(store, inputs),
|
|
||||||
1 => self.calculate_witness_circom1(store, inputs),
|
|
||||||
_ => panic!("Unknown Circom version")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.calculate_witness_circom1(inputs, sanity_check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Circom 1 default behavior
|
|
||||||
fn calculate_witness_circom1<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
|
|
||||||
&mut self,
|
|
||||||
store: &mut Store,
|
|
||||||
inputs: I,
|
|
||||||
) -> Result<Vec<BigInt>> {
|
|
||||||
let old_mem_free_pos = self.memory.as_ref().unwrap().free_pos(store)?;
|
|
||||||
let p_sig_offset = self.memory.as_mut().unwrap().alloc_u32(store)?;
|
|
||||||
let p_fr = self.memory.as_mut().unwrap().alloc_fr(store)?;
|
|
||||||
|
|
||||||
// allocate the inputs
|
|
||||||
for (name, values) in inputs.into_iter() {
|
|
||||||
let (msb, lsb) = fnv(&name);
|
|
||||||
|
|
||||||
self.instance
|
|
||||||
.get_signal_offset32(store, p_sig_offset, 0, msb, lsb)?;
|
|
||||||
|
|
||||||
let sig_offset = self
|
|
||||||
.memory
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.read_u32(store, p_sig_offset as usize)
|
|
||||||
.unwrap() as usize;
|
|
||||||
|
|
||||||
for (i, value) in values.into_iter().enumerate() {
|
|
||||||
self.memory
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.write_fr(store, p_fr as usize, &value)?;
|
|
||||||
self.instance
|
|
||||||
.set_signal(store, 0, 0, (sig_offset + i) as u32, p_fr)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut w = Vec::new();
|
|
||||||
|
|
||||||
let n_vars = self.instance.get_n_vars(store)?;
|
|
||||||
for i in 0..n_vars {
|
|
||||||
let ptr = self.instance.get_ptr_witness(store, i)? as usize;
|
|
||||||
let el = self.memory.as_ref().unwrap().read_fr(store, ptr)?;
|
|
||||||
w.push(el);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.memory
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.set_free_pos(store, old_mem_free_pos)?;
|
|
||||||
|
|
||||||
Ok(w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Circom 2 feature flag with version 2
|
|
||||||
#[cfg(feature = "circom-2")]
|
|
||||||
fn calculate_witness_circom2<I: IntoIterator<Item = (String, Vec<BigInt>)>>(
|
|
||||||
&mut self,
|
|
||||||
store: &mut Store,
|
|
||||||
inputs: I,
|
|
||||||
) -> Result<Vec<BigInt>> {
|
|
||||||
let n32 = self.instance.get_field_num_len32(store)?;
|
let n32 = self.instance.get_field_num_len32(store)?;
|
||||||
|
|
||||||
// allocate the inputs
|
// allocate the inputs
|
||||||
@ -385,7 +252,6 @@ mod tests {
|
|||||||
struct TestCase<'a> {
|
struct TestCase<'a> {
|
||||||
circuit_path: &'a str,
|
circuit_path: &'a str,
|
||||||
inputs_path: &'a str,
|
inputs_path: &'a str,
|
||||||
n_vars: u32,
|
|
||||||
n64: u32,
|
n64: u32,
|
||||||
witness: &'a [&'a str],
|
witness: &'a [&'a str],
|
||||||
}
|
}
|
||||||
@ -399,9 +265,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn multiplier_1() {
|
async fn multiplier_1() {
|
||||||
run_test(TestCase {
|
run_test(TestCase {
|
||||||
circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
|
circuit_path: root_path("test-vectors/mycircuit_js/mycircuit.wasm").as_str(),
|
||||||
inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
|
inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
|
||||||
n_vars: 4,
|
|
||||||
n64: 4,
|
n64: 4,
|
||||||
witness: &["1", "33", "3", "11"],
|
witness: &["1", "33", "3", "11"],
|
||||||
});
|
});
|
||||||
@ -410,9 +275,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn multiplier_2() {
|
async fn multiplier_2() {
|
||||||
run_test(TestCase {
|
run_test(TestCase {
|
||||||
circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
|
circuit_path: root_path("test-vectors/mycircuit_js/mycircuit.wasm").as_str(),
|
||||||
inputs_path: root_path("test-vectors/mycircuit-input2.json").as_str(),
|
inputs_path: root_path("test-vectors/mycircuit-input2.json").as_str(),
|
||||||
n_vars: 4,
|
|
||||||
n64: 4,
|
n64: 4,
|
||||||
witness: &[
|
witness: &[
|
||||||
"1",
|
"1",
|
||||||
@ -426,9 +290,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn multiplier_3() {
|
async fn multiplier_3() {
|
||||||
run_test(TestCase {
|
run_test(TestCase {
|
||||||
circuit_path: root_path("test-vectors/mycircuit.wasm").as_str(),
|
circuit_path: root_path("test-vectors/mycircuit_js/mycircuit.wasm").as_str(),
|
||||||
inputs_path: root_path("test-vectors/mycircuit-input3.json").as_str(),
|
inputs_path: root_path("test-vectors/mycircuit-input3.json").as_str(),
|
||||||
n_vars: 4,
|
|
||||||
n64: 4,
|
n64: 4,
|
||||||
witness: &[
|
witness: &[
|
||||||
"1",
|
"1",
|
||||||
@ -446,25 +309,8 @@ mod tests {
|
|||||||
let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
|
let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
|
||||||
let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
|
let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
|
||||||
run_test(TestCase {
|
run_test(TestCase {
|
||||||
circuit_path: root_path("test-vectors/circuit2.wasm").as_str(),
|
circuit_path: root_path("test-vectors/circuit2_js/circuit2.wasm").as_str(),
|
||||||
inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
|
inputs_path: root_path("test-vectors/mycircuit-input1.json").as_str(),
|
||||||
n_vars: 132, // 128 + 4
|
|
||||||
n64: 4,
|
|
||||||
witness,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn smt_verifier() {
|
|
||||||
let witness =
|
|
||||||
std::fs::read_to_string(root_path("test-vectors/smtverifier10-witness.json")).unwrap();
|
|
||||||
let witness: Vec<String> = serde_json::from_str(&witness).unwrap();
|
|
||||||
let witness = &witness.iter().map(|x| x.as_ref()).collect::<Vec<_>>();
|
|
||||||
|
|
||||||
run_test(TestCase {
|
|
||||||
circuit_path: root_path("test-vectors/smtverifier10.wasm").as_str(),
|
|
||||||
inputs_path: root_path("test-vectors/smtverifier10-input.json").as_str(),
|
|
||||||
n_vars: 4794,
|
|
||||||
n64: 4,
|
n64: 4,
|
||||||
witness,
|
witness,
|
||||||
});
|
});
|
||||||
@ -488,10 +334,6 @@ mod tests {
|
|||||||
wtns.prime.to_str_radix(16),
|
wtns.prime.to_str_radix(16),
|
||||||
"30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase()
|
"30644E72E131A029B85045B68181585D2833E84879B9709143E1F593F0000001".to_lowercase()
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
{ wtns.instance.get_n_vars(&mut store).unwrap() },
|
|
||||||
case.n_vars
|
|
||||||
);
|
|
||||||
assert_eq!({ wtns.n64 }, case.n64);
|
assert_eq!({ wtns.n64 }, case.n64);
|
||||||
|
|
||||||
let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap();
|
let inputs_str = std::fs::read_to_string(case.inputs_path).unwrap();
|
||||||
|
|||||||
@ -850,7 +850,7 @@ mod tests {
|
|||||||
let (params, _matrices) = read_zkey(&mut file).unwrap(); // binfile.proving_key().unwrap();
|
let (params, _matrices) = read_zkey(&mut file).unwrap(); // binfile.proving_key().unwrap();
|
||||||
|
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/mycircuit.wasm",
|
"./test-vectors/mycircuit_js/mycircuit.wasm",
|
||||||
"./test-vectors/mycircuit.r1cs",
|
"./test-vectors/mycircuit.r1cs",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -878,7 +878,9 @@ mod tests {
|
|||||||
let mut file = File::open(path).unwrap();
|
let mut file = File::open(path).unwrap();
|
||||||
let (params, matrices) = read_zkey(&mut file).unwrap();
|
let (params, matrices) = read_zkey(&mut file).unwrap();
|
||||||
let mut store = Store::default();
|
let mut store = Store::default();
|
||||||
let mut wtns = WitnessCalculator::new(&mut store, "./test-vectors/mycircuit.wasm").unwrap();
|
let mut wtns =
|
||||||
|
WitnessCalculator::new(&mut store, "./test-vectors/mycircuit_js/mycircuit.wasm")
|
||||||
|
.unwrap();
|
||||||
let mut inputs: HashMap<String, Vec<num_bigint::BigInt>> = HashMap::new();
|
let mut inputs: HashMap<String, Vec<num_bigint::BigInt>> = HashMap::new();
|
||||||
let values = inputs.entry("a".to_string()).or_insert_with(Vec::new);
|
let values = inputs.entry("a".to_string()).or_insert_with(Vec::new);
|
||||||
values.push(3.into());
|
values.push(3.into());
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const fs = require("fs");
|
|
||||||
const {stringifyBigInts, unstringifyBigInts} = require("snarkjs");
|
|
||||||
const WitnessCalculatorBuilder = require("./witness_calculator.js");
|
|
||||||
|
|
||||||
// const wasmName = "smtverifier10.wasm"
|
|
||||||
// const inputName = "smtverifier10-input.json"
|
|
||||||
|
|
||||||
const wasmName = "nconstraints.wasm"
|
|
||||||
const inputName = "nconstraints-input.json"
|
|
||||||
|
|
||||||
async function run () {
|
|
||||||
const wasm = await fs.promises.readFile(wasmName);
|
|
||||||
const input = unstringifyBigInts(JSON.parse(await fs.promises.readFile(inputName, "utf8")));
|
|
||||||
|
|
||||||
console.log("input:", input);
|
|
||||||
let options;
|
|
||||||
const wc = await WitnessCalculatorBuilder(wasm, options);
|
|
||||||
|
|
||||||
const w = await wc.calculateWitness(input);
|
|
||||||
|
|
||||||
console.log("witness:\n", JSON.stringify(stringifyBigInts(w)));
|
|
||||||
|
|
||||||
// const wb = await wc.calculateBinWitness(input);
|
|
||||||
|
|
||||||
// console.log("witnessBin:", Buffer.from(wb).toString('hex'));
|
|
||||||
|
|
||||||
// await fs.promises.writeFile(witnessName, JSON.stringify(stringifyBigInts(w), null, 1));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
run().then(() => {
|
|
||||||
process.exit();
|
|
||||||
});
|
|
||||||
Binary file not shown.
@ -1,3 +1,5 @@
|
|||||||
|
pragma circom 2.0.0;
|
||||||
|
|
||||||
template CheckBits(n) {
|
template CheckBits(n) {
|
||||||
signal input in;
|
signal input in;
|
||||||
signal bits[n];
|
signal bits[n];
|
||||||
@ -15,8 +17,8 @@ template CheckBits(n) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template Multiplier(n) {
|
template Multiplier(n) {
|
||||||
signal private input a;
|
signal input a;
|
||||||
signal private input b;
|
signal input b;
|
||||||
signal output c;
|
signal output c;
|
||||||
signal inva;
|
signal inva;
|
||||||
signal invb;
|
signal invb;
|
||||||
|
|||||||
BIN
test-vectors/circuit2.r1cs
Normal file
BIN
test-vectors/circuit2.r1cs
Normal file
Binary file not shown.
Binary file not shown.
BIN
test-vectors/circuit2_js/circuit2.wasm
Normal file
BIN
test-vectors/circuit2_js/circuit2.wasm
Normal file
Binary file not shown.
20
test-vectors/circuit2_js/generate_witness.js
Normal file
20
test-vectors/circuit2_js/generate_witness.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const wc = require("./witness_calculator.js");
|
||||||
|
const { readFileSync, writeFile } = require("fs");
|
||||||
|
|
||||||
|
if (process.argv.length != 5) {
|
||||||
|
console.log("Usage: node generate_witness.js <file.wasm> <input.json> <output.wtns>");
|
||||||
|
} else {
|
||||||
|
const input = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
||||||
|
|
||||||
|
const buffer = readFileSync(process.argv[2]);
|
||||||
|
wc(buffer).then(async witnessCalculator => {
|
||||||
|
// const w= await witnessCalculator.calculateWitness(input,0);
|
||||||
|
// for (let i=0; i< w.length; i++){
|
||||||
|
// console.log(w[i]);
|
||||||
|
// }
|
||||||
|
const buff= await witnessCalculator.calculateWTNSBin(input,0);
|
||||||
|
writeFile(process.argv[4], buff, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
BIN
test-vectors/circuit2_js/witness.wtns
Normal file
BIN
test-vectors/circuit2_js/witness.wtns
Normal file
Binary file not shown.
337
test-vectors/circuit2_js/witness_calculator.js
Normal file
337
test-vectors/circuit2_js/witness_calculator.js
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
module.exports = async function builder(code, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
let wasmModule;
|
||||||
|
try {
|
||||||
|
wasmModule = await WebAssembly.compile(code);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
console.log("\nTry to run circom --c in order to generate c++ code instead\n");
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wc;
|
||||||
|
|
||||||
|
let errStr = "";
|
||||||
|
let msgStr = "";
|
||||||
|
|
||||||
|
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||||
|
runtime: {
|
||||||
|
exceptionHandler : function(code) {
|
||||||
|
let err;
|
||||||
|
if (code == 1) {
|
||||||
|
err = "Signal not found.\n";
|
||||||
|
} else if (code == 2) {
|
||||||
|
err = "Too many signals set.\n";
|
||||||
|
} else if (code == 3) {
|
||||||
|
err = "Signal already set.\n";
|
||||||
|
} else if (code == 4) {
|
||||||
|
err = "Assert Failed.\n";
|
||||||
|
} else if (code == 5) {
|
||||||
|
err = "Not enough memory.\n";
|
||||||
|
} else if (code == 6) {
|
||||||
|
err = "Input signal array access exceeds the size.\n";
|
||||||
|
} else {
|
||||||
|
err = "Unknown error.\n";
|
||||||
|
}
|
||||||
|
throw new Error(err + errStr);
|
||||||
|
},
|
||||||
|
printErrorMessage : function() {
|
||||||
|
errStr += getMessage() + "\n";
|
||||||
|
// console.error(getMessage());
|
||||||
|
},
|
||||||
|
writeBufferMessage : function() {
|
||||||
|
const msg = getMessage();
|
||||||
|
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||||
|
if (msg === "\n") {
|
||||||
|
console.log(msgStr);
|
||||||
|
msgStr = "";
|
||||||
|
} else {
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " "
|
||||||
|
}
|
||||||
|
// Then append the message to the message we are creating
|
||||||
|
msgStr += msg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showSharedRWMemory : function() {
|
||||||
|
printSharedRWMemory ();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sanityCheck =
|
||||||
|
options
|
||||||
|
// options &&
|
||||||
|
// (
|
||||||
|
// options.sanityCheck ||
|
||||||
|
// options.logGetSignal ||
|
||||||
|
// options.logSetSignal ||
|
||||||
|
// options.logStartComponent ||
|
||||||
|
// options.logFinishComponent
|
||||||
|
// );
|
||||||
|
|
||||||
|
|
||||||
|
wc = new WitnessCalculator(instance, sanityCheck);
|
||||||
|
return wc;
|
||||||
|
|
||||||
|
function getMessage() {
|
||||||
|
var message = "";
|
||||||
|
var c = instance.exports.getMessageChar();
|
||||||
|
while ( c != 0 ) {
|
||||||
|
message += String.fromCharCode(c);
|
||||||
|
c = instance.exports.getMessageChar();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printSharedRWMemory () {
|
||||||
|
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
|
||||||
|
const arr = new Uint32Array(shared_rw_memory_size);
|
||||||
|
for (let j=0; j<shared_rw_memory_size; j++) {
|
||||||
|
arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " "
|
||||||
|
}
|
||||||
|
// Then append the value to the message we are creating
|
||||||
|
msgStr += (fromArray32(arr).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class WitnessCalculator {
|
||||||
|
constructor(instance, sanityCheck) {
|
||||||
|
this.instance = instance;
|
||||||
|
|
||||||
|
this.version = this.instance.exports.getVersion();
|
||||||
|
this.n32 = this.instance.exports.getFieldNumLen32();
|
||||||
|
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let i=0; i<this.n32; i++) {
|
||||||
|
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
||||||
|
}
|
||||||
|
this.prime = fromArray32(arr);
|
||||||
|
|
||||||
|
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||||
|
|
||||||
|
this.sanityCheck = sanityCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
circom_version() {
|
||||||
|
return this.instance.exports.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _doCalculateWitness(input, sanityCheck) {
|
||||||
|
//input is assumed to be a map from signals to arrays of bigints
|
||||||
|
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
||||||
|
const keys = Object.keys(input);
|
||||||
|
var input_counter = 0;
|
||||||
|
keys.forEach( (k) => {
|
||||||
|
const h = fnvHash(k);
|
||||||
|
const hMSB = parseInt(h.slice(0,8), 16);
|
||||||
|
const hLSB = parseInt(h.slice(8,16), 16);
|
||||||
|
const fArr = flatArray(input[k]);
|
||||||
|
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||||
|
if (signalSize < 0){
|
||||||
|
throw new Error(`Signal ${k} not found\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length < signalSize) {
|
||||||
|
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length > signalSize) {
|
||||||
|
throw new Error(`Too many values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
for (let i=0; i<fArr.length; i++) {
|
||||||
|
const arrFr = toArray32(normalize(fArr[i],this.prime),this.n32)
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.instance.exports.setInputSignal(hMSB, hLSB,i);
|
||||||
|
input_counter++;
|
||||||
|
} catch (err) {
|
||||||
|
// console.log(`After adding signal ${i} of ${k}`)
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
if (input_counter < this.instance.exports.getInputSize()) {
|
||||||
|
throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateWitness(input, sanityCheck) {
|
||||||
|
|
||||||
|
const w = [];
|
||||||
|
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
w.push(fromArray32(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async calculateBinWitness(input, sanityCheck) {
|
||||||
|
|
||||||
|
const buff32 = new Uint32Array(this.witnessSize*this.n32);
|
||||||
|
const buff = new Uint8Array( buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const pos = i*this.n32;
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async calculateWTNSBin(input, sanityCheck) {
|
||||||
|
|
||||||
|
const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
|
||||||
|
const buff = new Uint8Array( buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
//"wtns"
|
||||||
|
buff[0] = "w".charCodeAt(0)
|
||||||
|
buff[1] = "t".charCodeAt(0)
|
||||||
|
buff[2] = "n".charCodeAt(0)
|
||||||
|
buff[3] = "s".charCodeAt(0)
|
||||||
|
|
||||||
|
//version 2
|
||||||
|
buff32[1] = 2;
|
||||||
|
|
||||||
|
//number of sections: 2
|
||||||
|
buff32[2] = 2;
|
||||||
|
|
||||||
|
//id section 1
|
||||||
|
buff32[3] = 1;
|
||||||
|
|
||||||
|
const n8 = this.n32*4;
|
||||||
|
//id section 1 length in 64bytes
|
||||||
|
const idSection1length = 8 + n8;
|
||||||
|
const idSection1lengthHex = idSection1length.toString(16);
|
||||||
|
buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
|
||||||
|
buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
|
||||||
|
|
||||||
|
//this.n32
|
||||||
|
buff32[6] = n8;
|
||||||
|
|
||||||
|
//prime number
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
|
||||||
|
var pos = 7;
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
|
||||||
|
// witness size
|
||||||
|
buff32[pos] = this.witnessSize;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
//id section 2
|
||||||
|
buff32[pos] = 2;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// section 2 length
|
||||||
|
const idSection2length = n8*this.witnessSize;
|
||||||
|
const idSection2lengthHex = idSection2length.toString(16);
|
||||||
|
buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
|
||||||
|
buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
|
||||||
|
|
||||||
|
pos += 2;
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toArray32(rem,size) {
|
||||||
|
const res = []; //new Uint32Array(size); //has no unshift
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
while (rem) {
|
||||||
|
res.unshift( Number(rem % radix));
|
||||||
|
rem = rem / radix;
|
||||||
|
}
|
||||||
|
if (size) {
|
||||||
|
var i = size - res.length;
|
||||||
|
while (i>0) {
|
||||||
|
res.unshift(0);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromArray32(arr) { //returns a BigInt
|
||||||
|
var res = BigInt(0);
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
for (let i = 0; i<arr.length; i++) {
|
||||||
|
res = res*radix + BigInt(arr[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatArray(a) {
|
||||||
|
var res = [];
|
||||||
|
fillArray(res, a);
|
||||||
|
return res;
|
||||||
|
|
||||||
|
function fillArray(res, a) {
|
||||||
|
if (Array.isArray(a)) {
|
||||||
|
for (let i=0; i<a.length; i++) {
|
||||||
|
fillArray(res, a[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(n, prime) {
|
||||||
|
let res = BigInt(n) % prime
|
||||||
|
if (res < 0) res += prime
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnvHash(str) {
|
||||||
|
const uint64_max = BigInt(2) ** BigInt(64);
|
||||||
|
let hash = BigInt("0xCBF29CE484222325");
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
hash ^= BigInt(str[i].charCodeAt());
|
||||||
|
hash *= BigInt(0x100000001B3);
|
||||||
|
hash %= uint64_max;
|
||||||
|
}
|
||||||
|
let shash = hash.toString(16);
|
||||||
|
let n = 16 - shash.length;
|
||||||
|
shash = '0'.repeat(n).concat(shash);
|
||||||
|
return shash;
|
||||||
|
}
|
||||||
@ -1 +0,0 @@
|
|||||||
{ "a": 3, "b": 11 }
|
|
||||||
@ -1,6 +1,8 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
template Multiplier() {
|
template Multiplier() {
|
||||||
signal private input a;
|
signal input a;
|
||||||
signal private input b;
|
signal input b;
|
||||||
signal output c;
|
signal output c;
|
||||||
|
|
||||||
c <== a*b;
|
c <== a*b;
|
||||||
|
|||||||
Binary file not shown.
23
test-vectors/mycircuit.sh
Executable file
23
test-vectors/mycircuit.sh
Executable file
@ -0,0 +1,23 @@
|
|||||||
|
# run from within test-vectors dir
|
||||||
|
DIR="test-vectors"
|
||||||
|
if [ ! -d "$DIR" ]; then
|
||||||
|
echo "Directory $DIR does not exist. Please ensure you are running this script from the correct location."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
|
||||||
|
echo "compiling"
|
||||||
|
circom mycircuit.circom --r1cs --wasm
|
||||||
|
|
||||||
|
FILE="powersOfTau28_hez_final_17.ptau"
|
||||||
|
if [ ! -f "$FILE" ]; then
|
||||||
|
echo "getting powers of tau"
|
||||||
|
curl -O https://storage.googleapis.com/zkevm/ptau/powersOfTau28_hez_final_17.ptau
|
||||||
|
else
|
||||||
|
echo "$FILE already exists"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
echo "writing zkey"
|
||||||
|
snarkjs zkey new mycircuit.r1cs powersOfTau28_hez_final_17.ptau test.zkey
|
||||||
@ -1,3 +0,0 @@
|
|||||||
1,2,0,main.a
|
|
||||||
2,3,0,main.b
|
|
||||||
3,1,0,main.c
|
|
||||||
Binary file not shown.
20
test-vectors/mycircuit_js/generate_witness.js
Normal file
20
test-vectors/mycircuit_js/generate_witness.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
const wc = require("./witness_calculator.js");
|
||||||
|
const { readFileSync, writeFile } = require("fs");
|
||||||
|
|
||||||
|
if (process.argv.length != 5) {
|
||||||
|
console.log("Usage: node generate_witness.js <file.wasm> <input.json> <output.wtns>");
|
||||||
|
} else {
|
||||||
|
const input = JSON.parse(readFileSync(process.argv[3], "utf8"));
|
||||||
|
|
||||||
|
const buffer = readFileSync(process.argv[2]);
|
||||||
|
wc(buffer).then(async witnessCalculator => {
|
||||||
|
// const w= await witnessCalculator.calculateWitness(input,0);
|
||||||
|
// for (let i=0; i< w.length; i++){
|
||||||
|
// console.log(w[i]);
|
||||||
|
// }
|
||||||
|
const buff= await witnessCalculator.calculateWTNSBin(input,0);
|
||||||
|
writeFile(process.argv[4], buff, function(err) {
|
||||||
|
if (err) throw err;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Binary file not shown.
337
test-vectors/mycircuit_js/witness_calculator.js
Normal file
337
test-vectors/mycircuit_js/witness_calculator.js
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
module.exports = async function builder(code, options) {
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
let wasmModule;
|
||||||
|
try {
|
||||||
|
wasmModule = await WebAssembly.compile(code);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
console.log("\nTry to run circom --c in order to generate c++ code instead\n");
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wc;
|
||||||
|
|
||||||
|
let errStr = "";
|
||||||
|
let msgStr = "";
|
||||||
|
|
||||||
|
const instance = await WebAssembly.instantiate(wasmModule, {
|
||||||
|
runtime: {
|
||||||
|
exceptionHandler : function(code) {
|
||||||
|
let err;
|
||||||
|
if (code == 1) {
|
||||||
|
err = "Signal not found.\n";
|
||||||
|
} else if (code == 2) {
|
||||||
|
err = "Too many signals set.\n";
|
||||||
|
} else if (code == 3) {
|
||||||
|
err = "Signal already set.\n";
|
||||||
|
} else if (code == 4) {
|
||||||
|
err = "Assert Failed.\n";
|
||||||
|
} else if (code == 5) {
|
||||||
|
err = "Not enough memory.\n";
|
||||||
|
} else if (code == 6) {
|
||||||
|
err = "Input signal array access exceeds the size.\n";
|
||||||
|
} else {
|
||||||
|
err = "Unknown error.\n";
|
||||||
|
}
|
||||||
|
throw new Error(err + errStr);
|
||||||
|
},
|
||||||
|
printErrorMessage : function() {
|
||||||
|
errStr += getMessage() + "\n";
|
||||||
|
// console.error(getMessage());
|
||||||
|
},
|
||||||
|
writeBufferMessage : function() {
|
||||||
|
const msg = getMessage();
|
||||||
|
// Any calls to `log()` will always end with a `\n`, so that's when we print and reset
|
||||||
|
if (msg === "\n") {
|
||||||
|
console.log(msgStr);
|
||||||
|
msgStr = "";
|
||||||
|
} else {
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " "
|
||||||
|
}
|
||||||
|
// Then append the message to the message we are creating
|
||||||
|
msgStr += msg;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showSharedRWMemory : function() {
|
||||||
|
printSharedRWMemory ();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sanityCheck =
|
||||||
|
options
|
||||||
|
// options &&
|
||||||
|
// (
|
||||||
|
// options.sanityCheck ||
|
||||||
|
// options.logGetSignal ||
|
||||||
|
// options.logSetSignal ||
|
||||||
|
// options.logStartComponent ||
|
||||||
|
// options.logFinishComponent
|
||||||
|
// );
|
||||||
|
|
||||||
|
|
||||||
|
wc = new WitnessCalculator(instance, sanityCheck);
|
||||||
|
return wc;
|
||||||
|
|
||||||
|
function getMessage() {
|
||||||
|
var message = "";
|
||||||
|
var c = instance.exports.getMessageChar();
|
||||||
|
while ( c != 0 ) {
|
||||||
|
message += String.fromCharCode(c);
|
||||||
|
c = instance.exports.getMessageChar();
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
function printSharedRWMemory () {
|
||||||
|
const shared_rw_memory_size = instance.exports.getFieldNumLen32();
|
||||||
|
const arr = new Uint32Array(shared_rw_memory_size);
|
||||||
|
for (let j=0; j<shared_rw_memory_size; j++) {
|
||||||
|
arr[shared_rw_memory_size-1-j] = instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we've buffered other content, put a space in between the items
|
||||||
|
if (msgStr !== "") {
|
||||||
|
msgStr += " "
|
||||||
|
}
|
||||||
|
// Then append the value to the message we are creating
|
||||||
|
msgStr += (fromArray32(arr).toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
class WitnessCalculator {
|
||||||
|
constructor(instance, sanityCheck) {
|
||||||
|
this.instance = instance;
|
||||||
|
|
||||||
|
this.version = this.instance.exports.getVersion();
|
||||||
|
this.n32 = this.instance.exports.getFieldNumLen32();
|
||||||
|
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let i=0; i<this.n32; i++) {
|
||||||
|
arr[this.n32-1-i] = this.instance.exports.readSharedRWMemory(i);
|
||||||
|
}
|
||||||
|
this.prime = fromArray32(arr);
|
||||||
|
|
||||||
|
this.witnessSize = this.instance.exports.getWitnessSize();
|
||||||
|
|
||||||
|
this.sanityCheck = sanityCheck;
|
||||||
|
}
|
||||||
|
|
||||||
|
circom_version() {
|
||||||
|
return this.instance.exports.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _doCalculateWitness(input, sanityCheck) {
|
||||||
|
//input is assumed to be a map from signals to arrays of bigints
|
||||||
|
this.instance.exports.init((this.sanityCheck || sanityCheck) ? 1 : 0);
|
||||||
|
const keys = Object.keys(input);
|
||||||
|
var input_counter = 0;
|
||||||
|
keys.forEach( (k) => {
|
||||||
|
const h = fnvHash(k);
|
||||||
|
const hMSB = parseInt(h.slice(0,8), 16);
|
||||||
|
const hLSB = parseInt(h.slice(8,16), 16);
|
||||||
|
const fArr = flatArray(input[k]);
|
||||||
|
let signalSize = this.instance.exports.getInputSignalSize(hMSB, hLSB);
|
||||||
|
if (signalSize < 0){
|
||||||
|
throw new Error(`Signal ${k} not found\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length < signalSize) {
|
||||||
|
throw new Error(`Not enough values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
if (fArr.length > signalSize) {
|
||||||
|
throw new Error(`Too many values for input signal ${k}\n`);
|
||||||
|
}
|
||||||
|
for (let i=0; i<fArr.length; i++) {
|
||||||
|
const arrFr = toArray32(normalize(fArr[i],this.prime),this.n32)
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
this.instance.exports.writeSharedRWMemory(j,arrFr[this.n32-1-j]);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
this.instance.exports.setInputSignal(hMSB, hLSB,i);
|
||||||
|
input_counter++;
|
||||||
|
} catch (err) {
|
||||||
|
// console.log(`After adding signal ${i} of ${k}`)
|
||||||
|
throw new Error(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
if (input_counter < this.instance.exports.getInputSize()) {
|
||||||
|
throw new Error(`Not all inputs have been set. Only ${input_counter} out of ${this.instance.exports.getInputSize()}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculateWitness(input, sanityCheck) {
|
||||||
|
|
||||||
|
const w = [];
|
||||||
|
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const arr = new Uint32Array(this.n32);
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
arr[this.n32-1-j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
w.push(fromArray32(arr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async calculateBinWitness(input, sanityCheck) {
|
||||||
|
|
||||||
|
const buff32 = new Uint32Array(this.witnessSize*this.n32);
|
||||||
|
const buff = new Uint8Array( buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
const pos = i*this.n32;
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async calculateWTNSBin(input, sanityCheck) {
|
||||||
|
|
||||||
|
const buff32 = new Uint32Array(this.witnessSize*this.n32+this.n32+11);
|
||||||
|
const buff = new Uint8Array( buff32.buffer);
|
||||||
|
await this._doCalculateWitness(input, sanityCheck);
|
||||||
|
|
||||||
|
//"wtns"
|
||||||
|
buff[0] = "w".charCodeAt(0)
|
||||||
|
buff[1] = "t".charCodeAt(0)
|
||||||
|
buff[2] = "n".charCodeAt(0)
|
||||||
|
buff[3] = "s".charCodeAt(0)
|
||||||
|
|
||||||
|
//version 2
|
||||||
|
buff32[1] = 2;
|
||||||
|
|
||||||
|
//number of sections: 2
|
||||||
|
buff32[2] = 2;
|
||||||
|
|
||||||
|
//id section 1
|
||||||
|
buff32[3] = 1;
|
||||||
|
|
||||||
|
const n8 = this.n32*4;
|
||||||
|
//id section 1 length in 64bytes
|
||||||
|
const idSection1length = 8 + n8;
|
||||||
|
const idSection1lengthHex = idSection1length.toString(16);
|
||||||
|
buff32[4] = parseInt(idSection1lengthHex.slice(0,8), 16);
|
||||||
|
buff32[5] = parseInt(idSection1lengthHex.slice(8,16), 16);
|
||||||
|
|
||||||
|
//this.n32
|
||||||
|
buff32[6] = n8;
|
||||||
|
|
||||||
|
//prime number
|
||||||
|
this.instance.exports.getRawPrime();
|
||||||
|
|
||||||
|
var pos = 7;
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
|
||||||
|
// witness size
|
||||||
|
buff32[pos] = this.witnessSize;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
//id section 2
|
||||||
|
buff32[pos] = 2;
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
// section 2 length
|
||||||
|
const idSection2length = n8*this.witnessSize;
|
||||||
|
const idSection2lengthHex = idSection2length.toString(16);
|
||||||
|
buff32[pos] = parseInt(idSection2lengthHex.slice(0,8), 16);
|
||||||
|
buff32[pos+1] = parseInt(idSection2lengthHex.slice(8,16), 16);
|
||||||
|
|
||||||
|
pos += 2;
|
||||||
|
for (let i=0; i<this.witnessSize; i++) {
|
||||||
|
this.instance.exports.getWitness(i);
|
||||||
|
for (let j=0; j<this.n32; j++) {
|
||||||
|
buff32[pos+j] = this.instance.exports.readSharedRWMemory(j);
|
||||||
|
}
|
||||||
|
pos += this.n32;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function toArray32(rem,size) {
|
||||||
|
const res = []; //new Uint32Array(size); //has no unshift
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
while (rem) {
|
||||||
|
res.unshift( Number(rem % radix));
|
||||||
|
rem = rem / radix;
|
||||||
|
}
|
||||||
|
if (size) {
|
||||||
|
var i = size - res.length;
|
||||||
|
while (i>0) {
|
||||||
|
res.unshift(0);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fromArray32(arr) { //returns a BigInt
|
||||||
|
var res = BigInt(0);
|
||||||
|
const radix = BigInt(0x100000000);
|
||||||
|
for (let i = 0; i<arr.length; i++) {
|
||||||
|
res = res*radix + BigInt(arr[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
function flatArray(a) {
|
||||||
|
var res = [];
|
||||||
|
fillArray(res, a);
|
||||||
|
return res;
|
||||||
|
|
||||||
|
function fillArray(res, a) {
|
||||||
|
if (Array.isArray(a)) {
|
||||||
|
for (let i=0; i<a.length; i++) {
|
||||||
|
fillArray(res, a[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
res.push(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalize(n, prime) {
|
||||||
|
let res = BigInt(n) % prime
|
||||||
|
if (res < 0) res += prime
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
function fnvHash(str) {
|
||||||
|
const uint64_max = BigInt(2) ** BigInt(64);
|
||||||
|
let hash = BigInt("0xCBF29CE484222325");
|
||||||
|
for (var i = 0; i < str.length; i++) {
|
||||||
|
hash ^= BigInt(str[i].charCodeAt());
|
||||||
|
hash *= BigInt(0x100000001B3);
|
||||||
|
hash %= uint64_max;
|
||||||
|
}
|
||||||
|
let shash = hash.toString(16);
|
||||||
|
let n = 16 - shash.length;
|
||||||
|
shash = '0'.repeat(n).concat(shash);
|
||||||
|
return shash;
|
||||||
|
}
|
||||||
@ -1,14 +0,0 @@
|
|||||||
template A(n) {
|
|
||||||
signal input in;
|
|
||||||
signal output out;
|
|
||||||
|
|
||||||
signal intermediate[n];
|
|
||||||
|
|
||||||
intermediate[0] <== in;
|
|
||||||
for (var i=1; i<n; i++) {
|
|
||||||
intermediate[i] <== intermediate[i-1] * intermediate[i-1] + i;
|
|
||||||
}
|
|
||||||
out <== intermediate[n-1];
|
|
||||||
}
|
|
||||||
|
|
||||||
component main = A({{N}});
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"pi_a": [
|
|
||||||
"6235746210673106891683313862449025023627555115162992763714613342703079170148",
|
|
||||||
"12399711040178466467332477784020211067450597599035925008336813728555144120335",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
"pi_b": [
|
|
||||||
[
|
|
||||||
"3878778368997395576585378205610237973645840367459368364069435490380759882761",
|
|
||||||
"6372446288114997398874714591076628465205914797783709795970421045876437438845"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"1848758415443668505660007055104065117685874818267116126442518003813688485119",
|
|
||||||
"13151381207181352787620244234186261096498266850828100859417113864178798435289"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
"1",
|
|
||||||
"0"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
"pi_c": [
|
|
||||||
"20702909955866523755177574141774962608204777398014771961964213040023134853917",
|
|
||||||
"11380617408700662148925472638480792573061992967930438232337416544412668869948",
|
|
||||||
"1"
|
|
||||||
],
|
|
||||||
"protocol": "groth16",
|
|
||||||
"curve": "bn128"
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
[
|
|
||||||
"20227169454906525228014700210166866282343639252280745415680311389428188660505"
|
|
||||||
]
|
|
||||||
@ -1 +1,134 @@
|
|||||||
["1","33","3","11","10944121435919637611123202872628637544274182200208017171849102093287904247809","15321770010287492655572484021680092561983855080291224040588742930603065946932","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","1","0","1","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]
|
[
|
||||||
|
"1",
|
||||||
|
"33",
|
||||||
|
"3",
|
||||||
|
"11",
|
||||||
|
"10944121435919637611123202872628637544274182200208017171849102093287904247809",
|
||||||
|
"15321770010287492655572484021680092561983855080291224040588742930603065946932",
|
||||||
|
"1",
|
||||||
|
"1",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"1",
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0",
|
||||||
|
"0"
|
||||||
|
]
|
||||||
15
test-vectors/safe-circuit.sh
Executable file
15
test-vectors/safe-circuit.sh
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
# run from within test-vectors dir
|
||||||
|
DIR="test-vectors"
|
||||||
|
if [ ! -d "$DIR" ]; then
|
||||||
|
echo "Directory $DIR does not exist. Please ensure you are running this script from the correct location."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$DIR"
|
||||||
|
|
||||||
|
echo "compiling"
|
||||||
|
circom circuit2.circom --wasm --r1cs
|
||||||
|
|
||||||
|
node circuit2_js/generate_witness.js circuit2_js/circuit2.wasm mycircuit-input1.json circuit2_js/witness.wtns
|
||||||
|
|
||||||
|
snarkjs wej circuit2_js/witness.wtns safe-circuit-witness.json
|
||||||
@ -1 +0,0 @@
|
|||||||
{"enabled":1,"fnc":0,"root":"4677130581325536491486966387607462164138332022971476080171400451642918512081","siblings":["3663166078965935940798554689567237216195612079341396621785946741270885707796","0","0","15268343501033916092396853374199187988748455820543796633535012025134089057292","0","0","0","0","0","0"],"oldKey":0,"oldValue":0,"isOld0":0,"key":8,"value":"88"}
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -11,7 +11,7 @@ type GrothBn = Groth16<Bn254>;
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn groth16_proof() -> Result<()> {
|
async fn groth16_proof() -> Result<()> {
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/mycircuit.wasm",
|
"./test-vectors/mycircuit_js/mycircuit.wasm",
|
||||||
"./test-vectors/mycircuit.r1cs",
|
"./test-vectors/mycircuit.r1cs",
|
||||||
)?;
|
)?;
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
let mut builder = CircomBuilder::new(cfg);
|
||||||
@ -40,32 +40,43 @@ async fn groth16_proof() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn groth16_proof_wrong_input() {
|
async fn groth16_proof_wrong_input() -> Result<()> {
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/mycircuit.wasm",
|
"./test-vectors/mycircuit_js/mycircuit.wasm",
|
||||||
"./test-vectors/mycircuit.r1cs",
|
"./test-vectors/mycircuit.r1cs",
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
let mut builder = CircomBuilder::new(cfg);
|
||||||
builder.push_input("a", 3);
|
builder.push_input("a", 3);
|
||||||
// This isn't a public input to the circuit, should fail
|
// This isn't a public input to the circuit, should fail verification
|
||||||
builder.push_input("foo", 11);
|
builder.push_input("foo", 11);
|
||||||
|
|
||||||
// create an empty instance for setting it up
|
// create an empty instance for setting it up
|
||||||
let circom = builder.setup();
|
let circom = builder.setup();
|
||||||
|
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
let _params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng).unwrap();
|
let params = GrothBn::generate_random_parameters_with_reduction(circom, &mut rng).unwrap();
|
||||||
|
|
||||||
let _ = builder.build().unwrap_err();
|
let circom = builder.build().unwrap();
|
||||||
|
|
||||||
|
// we need to manually specify the public input, else the circuit builder will take the default for b = 0, and set public input to 0 (=11*0).
|
||||||
|
let inputs = vec![Fr::from(33u64)];
|
||||||
|
|
||||||
|
let proof = GrothBn::prove(¶ms, circom, &mut rng).unwrap();
|
||||||
|
|
||||||
|
let pvk = GrothBn::process_vk(¶ms.vk).unwrap();
|
||||||
|
|
||||||
|
let verified = GrothBn::verify_with_processed_vk(&pvk, &inputs, &proof).unwrap();
|
||||||
|
assert!(!verified);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(feature = "circom-2")]
|
async fn groth16_proof_circom() -> Result<()> {
|
||||||
async fn groth16_proof_circom2() -> Result<()> {
|
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/circom2_multiplier2.wasm",
|
"test-vectors/circuit2_js/circuit2.wasm",
|
||||||
"./test-vectors/circom2_multiplier2.r1cs",
|
"test-vectors/circuit2.r1cs",
|
||||||
)?;
|
)?;
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
let mut builder = CircomBuilder::new(cfg);
|
||||||
builder.push_input("a", 3);
|
builder.push_input("a", 3);
|
||||||
@ -93,11 +104,10 @@ async fn groth16_proof_circom2() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[cfg(feature = "circom-2")]
|
async fn witness_generation_circom() -> Result<()> {
|
||||||
async fn witness_generation_circom2() -> Result<()> {
|
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/circom2_multiplier2.wasm",
|
"test-vectors/circuit2_js/circuit2.wasm",
|
||||||
"./test-vectors/circom2_multiplier2.r1cs",
|
"test-vectors/circuit2.r1cs",
|
||||||
)?;
|
)?;
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
let mut builder = CircomBuilder::new(cfg);
|
||||||
builder.push_input("a", 3);
|
builder.push_input("a", 3);
|
||||||
|
|||||||
@ -17,7 +17,7 @@ use std::{convert::TryFrom, sync::Arc};
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn solidity_verifier() -> Result<()> {
|
async fn solidity_verifier() -> Result<()> {
|
||||||
let cfg = CircomConfig::<Fr>::new(
|
let cfg = CircomConfig::<Fr>::new(
|
||||||
"./test-vectors/mycircuit.wasm",
|
"./test-vectors/mycircuit_js/mycircuit.wasm",
|
||||||
"./test-vectors/mycircuit.r1cs",
|
"./test-vectors/mycircuit.r1cs",
|
||||||
)?;
|
)?;
|
||||||
let mut builder = CircomBuilder::new(cfg);
|
let mut builder = CircomBuilder::new(cfg);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user