mirror of
https://github.com/logos-storage/logos-storage-proofs.git
synced 2026-01-05 23:13:05 +00:00
Completed benchmarking
Basic benchmarking script Added benchmarks for groth16 base of benches.rs Update README.md (#2) * Update README.md * fix tests running example --------- Co-authored-by: Dmitriy Ryajov <dryajov@gmail.com> adding CI build (#4) adding initial CI build for circuits rework with poseidon (#3) * rework with poseidon * adding main template * adding todo * remove mimc Ark circom and rust ffi (#5) * wip rust ffi * proper test component instantiation * adding quick&dirty poseidon implementation * update gitignode * gitignore * adding rust circuit tests * gitignore * rename * add storer tests * move utils under circuit_tests * fix storage proofs * wip: ffi * instantiate storer * enable ark-serialize * delete js tests * update CI to run cargo tests * keep the artifacts dir * update .gitignore * build circuits * remove package json * place built circuits in correct dirs * update gitignore * remove node * fix ci * updating readme * storageproofs.rs to storage_proofs.rs * flatten tests chunks by default * add ffi * fix digest * minor fixes for ffi * fix storer test * use random data for chunks * debug optimizations to speed witness generation * clippy & other lint stuff * add back missing unsafe blocks * release mode disables constraint checks * fix ffi * fix hashes serialization * make naming more consistent * add missing pragma * use correct circuits * add todo * add clarification to readme * silence unused warning * include constants file into exec * remove unused imports extract poseidon to it's own package (#8) * extract poseidon to it's own package * move license to the bottom
This commit is contained in:
parent
a0ba322cfd
commit
afcaf4a600
34
.github/workflows/ci.yml
vendored
Normal file
34
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# This workflow runs the tests for the circuits.
|
||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "master" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "master" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: actions/setup-node@v3
|
||||||
|
with:
|
||||||
|
node-version: 18
|
||||||
|
cache: 'npm'
|
||||||
|
- name: Cache circom
|
||||||
|
id: cache-circom
|
||||||
|
uses: actions/cache@v3
|
||||||
|
with:
|
||||||
|
path: ~/.cargo/bin/circom
|
||||||
|
# Since the version of circom is specified in `scripts/install-circom.sh`,
|
||||||
|
# as long as the file doesn't change we can reuse the circom binary.
|
||||||
|
key: ${{ runner.os }}-circom-${{ hashFiles('./scripts/install-circom.sh') }}
|
||||||
|
- name: Install circom if not cached
|
||||||
|
run: sh ./scripts/install-circom.sh
|
||||||
|
- run: npm ci
|
||||||
|
- name: Build circuits
|
||||||
|
run: sh ./scripts/circuit-prep.sh
|
||||||
|
- name: Run the tests
|
||||||
|
run: RUST_BACKTRACE=full cargo test
|
||||||
19
.gitignore
vendored
19
.gitignore
vendored
@ -1 +1,18 @@
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
|
||||||
|
/target
|
||||||
|
|
||||||
|
|
||||||
|
# Added by cargo
|
||||||
|
#
|
||||||
|
# already existing elements were commented out
|
||||||
|
|
||||||
|
#/target
|
||||||
|
/Cargo.lock
|
||||||
|
.vscode
|
||||||
|
test/circuits/artifacts
|
||||||
|
out.log
|
||||||
|
src/circuit_tests/artifacts/*
|
||||||
|
!src/circuit_tests/artifacts/.keep
|
||||||
|
|||||||
49
Cargo.toml
Normal file
49
Cargo.toml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
[package]
|
||||||
|
name = "codex-storage-proofs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = [
|
||||||
|
"staticlib", # Ensure it gets compiled as a (static) C library
|
||||||
|
# "cdylib", # If you want a shared/dynamic C library (advanced)
|
||||||
|
"lib", # For downstream Rust dependents: `examples/`, `tests/` etc.
|
||||||
|
]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ark-bn254 = { version = "0.3.0" }
|
||||||
|
ark-ec = { version = "0.3.0", default-features = false, features = [
|
||||||
|
"parallel",
|
||||||
|
] }
|
||||||
|
ark-groth16 = { git = "https://github.com/arkworks-rs/groth16", rev = "765817f", features = [
|
||||||
|
"parallel",
|
||||||
|
] }
|
||||||
|
ark-std = { version = "0.3.0", default-features = false, features = [
|
||||||
|
"parallel",
|
||||||
|
] }
|
||||||
|
ark-serialize = { version = "0.3.0", default-features = false }
|
||||||
|
num-bigint = { version = "0.4", default-features = false, features = ["rand"] }
|
||||||
|
ark-circom = { git = "https://github.com/gakonst/ark-circom.git", rev = "35ce5a9", features = [
|
||||||
|
"circom-2",
|
||||||
|
] }
|
||||||
|
ark-ff = { version = "0.3.0", features = ["std"] }
|
||||||
|
ruint = { version = "1.7.0", features = ["serde", "num-bigint", "ark-ff"] }
|
||||||
|
once_cell = "1.17.1"
|
||||||
|
serde = "1.0.156"
|
||||||
|
serde_json = "1.0.94"
|
||||||
|
num-traits = "0.2.15"
|
||||||
|
ark-relations = { version = "0.4.0", features = ["std", "tracing-subscriber"] }
|
||||||
|
<<<<<<< HEAD
|
||||||
|
<<<<<<< HEAD
|
||||||
|
rs-poseidon = {git = "https://github.com/status-im/rs-poseidon" }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
criterion = "0.3"
|
||||||
|
=======
|
||||||
|
>>>>>>> ebef300 (Ark circom and rust ffi (#5))
|
||||||
|
=======
|
||||||
|
rs-poseidon = {git = "https://github.com/status-im/rs-poseidon" }
|
||||||
|
>>>>>>> f8e4b3e (extract poseidon to it's own package (#8))
|
||||||
35
README.md
35
README.md
@ -5,6 +5,29 @@
|
|||||||
|
|
||||||
WIP Zero Knowledge tooling for the Codex project
|
WIP Zero Knowledge tooling for the Codex project
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
First, clone the repo and install the circom components:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone git@github.com:status-im/codex-storage-proofs.git
|
||||||
|
cd codex-storage-proofs
|
||||||
|
npm i
|
||||||
|
cd circuits
|
||||||
|
```
|
||||||
|
|
||||||
|
Nex, compile circuits:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
../scripts/circuit_prep.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Running the tests:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo test # don't run in release more as it dissables circuit assets
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Licensed and distributed under either of
|
Licensed and distributed under either of
|
||||||
@ -16,15 +39,3 @@ or
|
|||||||
* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
|
* Apache License, Version 2.0, ([LICENSE-APACHEv2](LICENSE-APACHEv2) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||||
|
|
||||||
at your option. These files may not be copied, modified, or distributed except according to those terms.
|
at your option. These files may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Preparing test key material (only suitable for testing)
|
|
||||||
```
|
|
||||||
../scripts/circuit_prep.sh storer 13
|
|
||||||
```
|
|
||||||
|
|
||||||
Running part of the code
|
|
||||||
```
|
|
||||||
npm test test/merkletree.js
|
|
||||||
```
|
|
||||||
75
bench/benches.rs
Normal file
75
bench/benches.rs
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use ark_bn254::{Bn254, Fr};
|
||||||
|
use ark_circom::{read_zkey, CircomBuilder, CircomConfig};
|
||||||
|
use ark_groth16::{
|
||||||
|
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof,
|
||||||
|
Proof, ProvingKey,
|
||||||
|
};
|
||||||
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read};
|
||||||
|
use ark_std::rand::rngs::ThreadRng;
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use ruint::aliases::U256;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StorageProofs {
|
||||||
|
builder: CircomBuilder<Bn254>,
|
||||||
|
params: ProvingKey<Bn254>,
|
||||||
|
rng: ThreadRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...StorageProofs implementation here...
|
||||||
|
|
||||||
|
// Functions for benchmarking
|
||||||
|
fn bench_prove(c: &mut Criterion) {
|
||||||
|
// You need to fill in these variables properly
|
||||||
|
let wtns = "...";
|
||||||
|
let r1cs = "...";
|
||||||
|
let zkey = Some("...");
|
||||||
|
let mut sp = StorageProofs::new(wtns, r1cs, zkey);
|
||||||
|
let chunks: &[U256] = &[];
|
||||||
|
let siblings: &[U256] = &[];
|
||||||
|
let hashes: &[U256] = &[];
|
||||||
|
let path: &[i32] = &[];
|
||||||
|
let root = U256::default();
|
||||||
|
let salt = U256::default();
|
||||||
|
let mut proof_bytes = Vec::new();
|
||||||
|
let mut public_inputs_bytes = Vec::new();
|
||||||
|
|
||||||
|
c.bench_function("StorageProofs prove", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(
|
||||||
|
sp.prove(
|
||||||
|
chunks,
|
||||||
|
siblings,
|
||||||
|
hashes,
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
salt,
|
||||||
|
&mut proof_bytes,
|
||||||
|
&mut public_inputs_bytes,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_verify(c: &mut Criterion) {
|
||||||
|
// You need to fill in these variables properly
|
||||||
|
let wtns = "...";
|
||||||
|
let r1cs = "...";
|
||||||
|
let zkey = Some("...");
|
||||||
|
let mut sp = StorageProofs::new(wtns, r1cs, zkey);
|
||||||
|
let proof_bytes: &[u8] = &[];
|
||||||
|
let public_inputs: &[u8] = &[];
|
||||||
|
|
||||||
|
c.bench_function("StorageProofs verify", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(sp.verify(proof_bytes, public_inputs).unwrap());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_prove, bench_verify);
|
||||||
|
criterion_main!(benches);
|
||||||
65
bench/snarkjs_bench.sh
Executable file
65
bench/snarkjs_bench.sh
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Set up the benchmarking parameters
|
||||||
|
ITERATIONS=10
|
||||||
|
CIRCUIT=../test/circuits/storer_test.circom
|
||||||
|
WITNESS=./input.json
|
||||||
|
|
||||||
|
# Define the SnarkJS commands for each system
|
||||||
|
GROTH16_CMD="snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json"
|
||||||
|
PLONK_CMD="snarkjs plonk prove circuit_final.zkey witness.wtns proof.json public.json"
|
||||||
|
|
||||||
|
# Set up the powers of tau ceremony
|
||||||
|
echo "Set up powers of tau ceremony"
|
||||||
|
snarkjs powersoftau new bn128 17 ../scripts/pot17_bn128_0000.ptau -v
|
||||||
|
|
||||||
|
# Generate circuit files
|
||||||
|
circom ${CIRCUIT} --r1cs --wasm --sym
|
||||||
|
snarkjs r1cs export json ./storer_test.r1cs ./storer_test.r1cs.json
|
||||||
|
|
||||||
|
# Generate the proving and verifying keys for Groth16
|
||||||
|
echo "Preparing phase 1"
|
||||||
|
snarkjs powersoftau contribute ../scripts/pot17_bn128_0000.ptau ../scripts/pot17_bn128_0001.ptau >/dev/null 2>&1 </dev/urandom
|
||||||
|
snarkjs powersoftau contribute ../scripts/pot17_bn128_0001.ptau ../scripts/pot17_bn128_0002.ptau >/dev/null 2>&1 </dev/urandom
|
||||||
|
snarkjs powersoftau verify ../scripts/pot17_bn128_0002.ptau
|
||||||
|
snarkjs powersoftau beacon ../scripts/pot17_bn128_0002.ptau ../scripts/pot17_bn128_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon"
|
||||||
|
|
||||||
|
echo "Preparing phase 2"
|
||||||
|
snarkjs powersoftau prepare phase2 ../scripts/pot17_bn128_beacon.ptau ../scripts/pot17_bn128_final.ptau
|
||||||
|
snarkjs powersoftau verify ../scripts/pot17_bn128_final.ptau
|
||||||
|
|
||||||
|
echo "Calculating witness"
|
||||||
|
node ./storer_test_js/generate_witness.js ./storer_test_js/storer_test.wasm ${WITNESS} ./witness.wtns
|
||||||
|
snarkjs wtns check ./storer_test.r1cs ./witness.wtns
|
||||||
|
|
||||||
|
# Benchmark Groth16
|
||||||
|
echo "Benchmarking Groth16..."
|
||||||
|
snarkjs groth16 setup ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_0000.zkey
|
||||||
|
snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st contributor" >/dev/null 2>&1
|
||||||
|
snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="2nd contributor" >/dev/null 2>&1
|
||||||
|
snarkjs zkey verify ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_0002.zkey
|
||||||
|
snarkjs zkey beacon circuit_0002.zkey circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
|
||||||
|
snarkjs zkey verify ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_final.zkey
|
||||||
|
snarkjs zkey export verificationkey circuit_final.zkey verification_key.json
|
||||||
|
for i in $(seq 1 $ITERATIONS); do
|
||||||
|
echo "Proving..."
|
||||||
|
/usr/bin/time -f "%e seconds" $GROTH16_CMD >/dev/null 2>&1
|
||||||
|
echo "Verifying.."
|
||||||
|
/usr/bin/time -f "%e seconds " snarkjs groth16 verify verification_key.json public.json proof.json
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Generate the proving and verifying keys for PLONK
|
||||||
|
echo "Generating PLONK keys..."
|
||||||
|
snarkjs powersoftau contribute ./contributions_2 pot12_0000_final_challenge >/dev/null 2>&1
|
||||||
|
snarkjs powersoftau verify ./contributions_2 >/dev/null 2>&1
|
||||||
|
snarkjs powersoftau prepare phase2 ./contributions_2 pot12_0000_final_challenge --srs_monomial_form ./srs.monomial >/dev/null 2>&1
|
||||||
|
snarkjs plonk setup --srs_monomial_form ./srs.monomial >/dev/null 2>&1
|
||||||
|
|
||||||
|
|
||||||
|
# Benchmark PLONK
|
||||||
|
echo "Benchmarking PLONK..."
|
||||||
|
for i in $(seq 1 $ITERATIONS); do
|
||||||
|
/usr/bin/time -f "%e seconds" $PLONK_CMD >/dev/null 2>&1
|
||||||
|
done
|
||||||
|
|
||||||
66
benches/benches.rs
Normal file
66
benches/benches.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use ark_bn254::{Bn254, Fr};
|
||||||
|
use ark_circom::{read_zkey, CircomBuilder, CircomConfig};
|
||||||
|
use ark_groth16::{
|
||||||
|
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof,
|
||||||
|
Proof, ProvingKey,
|
||||||
|
};
|
||||||
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read};
|
||||||
|
use ark_std::rand::rngs::ThreadRng;
|
||||||
|
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||||
|
use ruint::aliases::U256;
|
||||||
|
use codex_storage_proofs::storage_proofs::{StorageProofs};
|
||||||
|
|
||||||
|
|
||||||
|
// Functions for benchmarking
|
||||||
|
fn bench_prove(c: &mut Criterion) {
|
||||||
|
let wtns = "./witness.wtns";
|
||||||
|
let r1cs = "./storer_test.r1cs";
|
||||||
|
let zkey = Some("./circuit_0001.zkey".to_string());
|
||||||
|
let mut sp = StorageProofs::new(wtns.to_string(), r1cs.to_string(), zkey);
|
||||||
|
let chunks: &[U256] = &[];
|
||||||
|
let siblings: &[U256] = &[];
|
||||||
|
let hashes: &[U256] = &[];
|
||||||
|
let path: &[i32] = &[];
|
||||||
|
let root = U256::default();
|
||||||
|
let salt = U256::default();
|
||||||
|
let mut proof_bytes = Vec::new();
|
||||||
|
let mut public_inputs_bytes = Vec::new();
|
||||||
|
|
||||||
|
c.bench_function("StorageProofs prove", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(
|
||||||
|
sp.prove(
|
||||||
|
chunks,
|
||||||
|
siblings,
|
||||||
|
hashes,
|
||||||
|
path,
|
||||||
|
root,
|
||||||
|
salt,
|
||||||
|
&mut proof_bytes,
|
||||||
|
&mut public_inputs_bytes,
|
||||||
|
)
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bench_verify(c: &mut Criterion) {
|
||||||
|
let wtns = "./witness.wtns";
|
||||||
|
let r1cs = "./storer_test.r1cs";
|
||||||
|
let zkey = Some("./circuit_0001.zkey".to_string());
|
||||||
|
let mut sp = StorageProofs::new(wtns.to_string(), r1cs.to_string(), zkey);
|
||||||
|
let proof_bytes: &[u8] = &[];
|
||||||
|
let public_inputs: &[u8] = &[];
|
||||||
|
|
||||||
|
c.bench_function("StorageProofs verify", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
black_box(sp.verify(proof_bytes, public_inputs).unwrap());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_prove, bench_verify);
|
||||||
|
criterion_main!(benches);
|
||||||
65
benches/snarkjs_bench.sh
Executable file
65
benches/snarkjs_bench.sh
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Set up the benchmarking parameters
|
||||||
|
ITERATIONS=10
|
||||||
|
CIRCUIT=../test/circuits/storer_test.circom
|
||||||
|
WITNESS=./input.json
|
||||||
|
|
||||||
|
# Define the SnarkJS commands for each system
|
||||||
|
GROTH16_CMD="snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json"
|
||||||
|
PLONK_CMD="snarkjs plonk prove circuit_final.zkey witness.wtns proof.json public.json"
|
||||||
|
|
||||||
|
# Set up the powers of tau ceremony
|
||||||
|
echo "Set up powers of tau ceremony"
|
||||||
|
snarkjs powersoftau new bn128 17 ../scripts/pot17_bn128_0000.ptau -v
|
||||||
|
|
||||||
|
# Generate circuit files
|
||||||
|
circom ${CIRCUIT} --r1cs --wasm --sym
|
||||||
|
snarkjs r1cs export json ./storer_test.r1cs ./storer_test.r1cs.json
|
||||||
|
|
||||||
|
# Generate the proving and verifying keys for Groth16
|
||||||
|
echo "Preparing phase 1"
|
||||||
|
snarkjs powersoftau contribute ../scripts/pot17_bn128_0000.ptau ../scripts/pot17_bn128_0001.ptau >/dev/null 2>&1 </dev/urandom
|
||||||
|
snarkjs powersoftau contribute ../scripts/pot17_bn128_0001.ptau ../scripts/pot17_bn128_0002.ptau >/dev/null 2>&1 </dev/urandom
|
||||||
|
snarkjs powersoftau verify ../scripts/pot17_bn128_0002.ptau
|
||||||
|
snarkjs powersoftau beacon ../scripts/pot17_bn128_0002.ptau ../scripts/pot17_bn128_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon"
|
||||||
|
|
||||||
|
echo "Preparing phase 2"
|
||||||
|
snarkjs powersoftau prepare phase2 ../scripts/pot17_bn128_beacon.ptau ../scripts/pot17_bn128_final.ptau
|
||||||
|
snarkjs powersoftau verify ../scripts/pot17_bn128_final.ptau
|
||||||
|
|
||||||
|
echo "Calculating witness"
|
||||||
|
node ./storer_test_js/generate_witness.js ./storer_test_js/storer_test.wasm ${WITNESS} ./witness.wtns
|
||||||
|
snarkjs wtns check ./storer_test.r1cs ./witness.wtns
|
||||||
|
|
||||||
|
# Benchmark Groth16
|
||||||
|
echo "Benchmarking Groth16..."
|
||||||
|
snarkjs groth16 setup ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_0000.zkey
|
||||||
|
snarkjs zkey contribute circuit_0000.zkey circuit_0001.zkey --name="1st contributor" >/dev/null 2>&1
|
||||||
|
snarkjs zkey contribute circuit_0001.zkey circuit_0002.zkey --name="2nd contributor" >/dev/null 2>&1
|
||||||
|
snarkjs zkey verify ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_0002.zkey
|
||||||
|
snarkjs zkey beacon circuit_0002.zkey circuit_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
|
||||||
|
snarkjs zkey verify ./storer_test.r1cs ../scripts/pot17_bn128_final.ptau circuit_final.zkey
|
||||||
|
snarkjs zkey export verificationkey circuit_final.zkey verification_key.json
|
||||||
|
for i in $(seq 1 $ITERATIONS); do
|
||||||
|
echo "Proving..."
|
||||||
|
/usr/bin/time -f "%e seconds" $GROTH16_CMD >/dev/null 2>&1
|
||||||
|
echo "Verifying.."
|
||||||
|
/usr/bin/time -f "%e seconds " snarkjs groth16 verify verification_key.json public.json proof.json
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
# Generate the proving and verifying keys for PLONK
|
||||||
|
echo "Generating PLONK keys..."
|
||||||
|
snarkjs powersoftau contribute ./contributions_2 pot12_0000_final_challenge >/dev/null 2>&1
|
||||||
|
snarkjs powersoftau verify ./contributions_2 >/dev/null 2>&1
|
||||||
|
snarkjs powersoftau prepare phase2 ./contributions_2 pot12_0000_final_challenge --srs_monomial_form ./srs.monomial >/dev/null 2>&1
|
||||||
|
snarkjs plonk setup --srs_monomial_form ./srs.monomial >/dev/null 2>&1
|
||||||
|
|
||||||
|
|
||||||
|
# Benchmark PLONK
|
||||||
|
echo "Benchmarking PLONK..."
|
||||||
|
for i in $(seq 1 $ITERATIONS); do
|
||||||
|
/usr/bin/time -f "%e seconds" $PLONK_CMD >/dev/null 2>&1
|
||||||
|
done
|
||||||
|
|
||||||
55
circuits/poseidon-digest.circom
Normal file
55
circuits/poseidon-digest.circom
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||||
|
|
||||||
|
function roundUpDiv(x, n) {
|
||||||
|
var last = x % n; // get the last digit
|
||||||
|
var div = x \ n; // get the division
|
||||||
|
|
||||||
|
if (last > 0) {
|
||||||
|
return div + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return div;
|
||||||
|
}
|
||||||
|
|
||||||
|
template parallel PoseidonDigest(BLOCK_SIZE, DIGEST_CHUNK) {
|
||||||
|
// BLOCK_SIZE - size of the input block array
|
||||||
|
// DIGEST_CHUNK - number of elements to hash at once
|
||||||
|
signal input block[BLOCK_SIZE]; // Input block array
|
||||||
|
signal output hash; // Output hash
|
||||||
|
|
||||||
|
// Split array into chunks of size DIGEST_CHUNK, usually 2
|
||||||
|
var NUM_CHUNKS = roundUpDiv(BLOCK_SIZE, DIGEST_CHUNK);
|
||||||
|
|
||||||
|
// Initialize an array to store hashes of each block
|
||||||
|
component hashes[NUM_CHUNKS];
|
||||||
|
|
||||||
|
// Loop over chunks and hash them using Poseidon()
|
||||||
|
for (var i = 0; i < NUM_CHUNKS; i++) {
|
||||||
|
hashes[i] = Poseidon(DIGEST_CHUNK);
|
||||||
|
|
||||||
|
var start = i * DIGEST_CHUNK;
|
||||||
|
var end = start + DIGEST_CHUNK;
|
||||||
|
for (var j = start; j < end; j++) {
|
||||||
|
if (j >= BLOCK_SIZE) {
|
||||||
|
hashes[i].inputs[j - start] <== 0;
|
||||||
|
} else {
|
||||||
|
hashes[i].inputs[j - start] <== block[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Concatenate hashes into a single block
|
||||||
|
var concat[NUM_CHUNKS];
|
||||||
|
for (var i = 0; i < NUM_CHUNKS; i++) {
|
||||||
|
concat[i] = hashes[i].out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash concatenated array using Poseidon() again
|
||||||
|
component h = Poseidon(NUM_CHUNKS);
|
||||||
|
h.inputs <== concat;
|
||||||
|
|
||||||
|
// Assign output to hash signal
|
||||||
|
hash <== h.out;
|
||||||
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
pragma circom 2.1.0;
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
// include "../node_modules/circomlib/circuits/poseidon.circom";
|
include "../node_modules/circomlib/circuits/poseidon.circom";
|
||||||
include "../node_modules/circomlib/circuits/mimc.circom";
|
|
||||||
// include "../node_modules/circomlib/circuits/mimcsponge.circom";
|
|
||||||
include "../node_modules/circomlib/circuits/switcher.circom";
|
include "../node_modules/circomlib/circuits/switcher.circom";
|
||||||
include "../node_modules/circomlib/circuits/bitify.circom";
|
include "../node_modules/circomlib/circuits/bitify.circom";
|
||||||
|
|
||||||
|
include "./poseidon-digest.circom";
|
||||||
|
|
||||||
template parallel MerkleProof(LEVELS) {
|
template parallel MerkleProof(LEVELS) {
|
||||||
signal input leaf;
|
signal input leaf;
|
||||||
signal input pathElements[LEVELS];
|
signal input pathElements[LEVELS];
|
||||||
@ -26,31 +26,19 @@ template parallel MerkleProof(LEVELS) {
|
|||||||
switcher[i].R <== pathElements[i];
|
switcher[i].R <== pathElements[i];
|
||||||
switcher[i].sel <== indexBits.out[i];
|
switcher[i].sel <== indexBits.out[i];
|
||||||
|
|
||||||
// hasher[i] = Poseidon(2);
|
hasher[i] = Poseidon(2);
|
||||||
hasher[i] = MultiMiMC7(2, 91);
|
hasher[i].inputs[0] <== switcher[i].outL;
|
||||||
hasher[i].k <== 2;
|
hasher[i].inputs[1] <== switcher[i].outR;
|
||||||
hasher[i].in[0] <== switcher[i].outL;
|
|
||||||
hasher[i].in[1] <== switcher[i].outR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root <== hasher[LEVELS - 1].out;
|
root <== hasher[LEVELS - 1].out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template parallel HashCheck(BLOCK_SIZE) {
|
template StorageProver(BLOCK_SIZE, QUERY_LEN, LEVELS, DIGEST_CHUNK) {
|
||||||
signal input block[BLOCK_SIZE];
|
|
||||||
signal input blockHash;
|
|
||||||
|
|
||||||
component hash = MultiMiMC7(BLOCK_SIZE, 91);
|
|
||||||
hash.in <== block;
|
|
||||||
hash.k <== 2;
|
|
||||||
|
|
||||||
blockHash === hash.out; // assert that block matches hash
|
|
||||||
}
|
|
||||||
|
|
||||||
template StorageProver(BLOCK_SIZE, QUERY_LEN, LEVELS) {
|
|
||||||
// BLOCK_SIZE: size of block in symbols
|
// BLOCK_SIZE: size of block in symbols
|
||||||
// QUERY_LEN: query length, i.e. number if indices to be proven
|
// QUERY_LEN: query length, i.e. number if indices to be proven
|
||||||
// LEVELS: size of Merkle Tree in the manifest
|
// LEVELS: size of Merkle Tree in the manifest
|
||||||
|
// DIGEST_CHUNK: number of symbols to hash in one go
|
||||||
signal input chunks[QUERY_LEN][BLOCK_SIZE]; // chunks to be proven
|
signal input chunks[QUERY_LEN][BLOCK_SIZE]; // chunks to be proven
|
||||||
signal input siblings[QUERY_LEN][LEVELS]; // siblings hashes of chunks to be proven
|
signal input siblings[QUERY_LEN][LEVELS]; // siblings hashes of chunks to be proven
|
||||||
signal input path[QUERY_LEN]; // path of chunks to be proven
|
signal input path[QUERY_LEN]; // path of chunks to be proven
|
||||||
@ -62,9 +50,9 @@ template StorageProver(BLOCK_SIZE, QUERY_LEN, LEVELS) {
|
|||||||
|
|
||||||
component hashers[QUERY_LEN];
|
component hashers[QUERY_LEN];
|
||||||
for (var i = 0; i < QUERY_LEN; i++) {
|
for (var i = 0; i < QUERY_LEN; i++) {
|
||||||
hashers[i] = HashCheck(BLOCK_SIZE);
|
hashers[i] = PoseidonDigest(BLOCK_SIZE, DIGEST_CHUNK);
|
||||||
hashers[i].block <== chunks[i];
|
hashers[i].block <== chunks[i];
|
||||||
hashers[i].blockHash <== hashes[i];
|
hashers[i].hash === hashes[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
component merkelizer[QUERY_LEN];
|
component merkelizer[QUERY_LEN];
|
||||||
|
|||||||
5
circuits/storer_main_256_80_32_16.circom
Normal file
5
circuits/storer_main_256_80_32_16.circom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "./storer.circom";
|
||||||
|
|
||||||
|
component main { public [root, salt] } = StorageProver(256, 80, 32, 16);
|
||||||
5
scripts/circuit-prep.sh
Executable file
5
scripts/circuit-prep.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
circom src/circuit_tests/poseidon-digest-test.circom --r1cs --wasm -o src/circuit_tests/artifacts
|
||||||
|
circom src/circuit_tests/poseidon-hash-test.circom --r1cs --wasm -o src/circuit_tests/artifacts
|
||||||
|
circom src/circuit_tests/storer-test.circom --r1cs --wasm -o src/circuit_tests/artifacts
|
||||||
@ -1,28 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
set -e
|
|
||||||
set -x
|
|
||||||
|
|
||||||
CIRCUIT=`basename $1`
|
|
||||||
POWER="${2:-12}"
|
|
||||||
CURVE="${3:-bn128}"
|
|
||||||
|
|
||||||
POTPREFIX=pot${POWER}_${CURVE}
|
|
||||||
|
|
||||||
if [ ! -f ${POTPREFIX}_final.ptau ]
|
|
||||||
then
|
|
||||||
snarkjs powersoftau new $CURVE $POWER ${POTPREFIX}_0000.ptau -v
|
|
||||||
snarkjs powersoftau contribute ${POTPREFIX}_0000.ptau ${POTPREFIX}_0001.ptau --name="First contribution" -v -e="random text"
|
|
||||||
snarkjs powersoftau verify ${POTPREFIX}_0001.ptau
|
|
||||||
snarkjs powersoftau beacon ${POTPREFIX}_0001.ptau ${POTPREFIX}_beacon.ptau 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon"
|
|
||||||
snarkjs powersoftau prepare phase2 ${POTPREFIX}_beacon.ptau ${POTPREFIX}_final.ptau -v
|
|
||||||
snarkjs powersoftau verify ${POTPREFIX}_final.ptau
|
|
||||||
fi
|
|
||||||
|
|
||||||
# phase 2
|
|
||||||
circom $1.circom --r1cs --wasm
|
|
||||||
|
|
||||||
snarkjs groth16 setup ${CIRCUIT}.r1cs ${POTPREFIX}_final.ptau ${CIRCUIT}_0000.zkey
|
|
||||||
snarkjs zkey contribute ${CIRCUIT}_0000.zkey ${CIRCUIT}_0001.zkey --name="1st Contributor Name" -v -e="another random text"
|
|
||||||
snarkjs zkey verify ${CIRCUIT}.r1cs ${POTPREFIX}_final.ptau ${CIRCUIT}_0001.zkey
|
|
||||||
snarkjs zkey beacon ${CIRCUIT}_0001.zkey ${CIRCUIT}_final.zkey 0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 10 -n="Final Beacon phase2"
|
|
||||||
|
|
||||||
11
scripts/install-circom.sh
Executable file
11
scripts/install-circom.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
circom_version=v2.1.4
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v circom)" ]; then
|
||||||
|
git clone https://github.com/iden3/circom.git
|
||||||
|
cd circom
|
||||||
|
git checkout $circom_version
|
||||||
|
cargo build --release
|
||||||
|
cargo install --path circom
|
||||||
|
fi
|
||||||
0
src/circuit_tests/artifacts/.keep
Normal file
0
src/circuit_tests/artifacts/.keep
Normal file
153
src/circuit_tests/mod.rs
Normal file
153
src/circuit_tests/mod.rs
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use ark_bn254::Bn254;
|
||||||
|
use ark_circom::{CircomBuilder, CircomConfig};
|
||||||
|
use ark_groth16::{
|
||||||
|
create_random_proof as prove, generate_random_parameters, prepare_inputs,
|
||||||
|
prepare_verifying_key, verify_proof_with_prepared_inputs, ProvingKey,
|
||||||
|
};
|
||||||
|
use ark_std::rand::{distributions::Alphanumeric, rngs::ThreadRng, Rng};
|
||||||
|
use rs_poseidon::poseidon::hash;
|
||||||
|
use ruint::aliases::U256;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
circuit_tests::utils::{digest, treehash},
|
||||||
|
storage_proofs::StorageProofs,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct CircuitsTests {
|
||||||
|
builder: CircomBuilder<Bn254>,
|
||||||
|
params: ProvingKey<Bn254>,
|
||||||
|
rng: ThreadRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CircuitsTests {
|
||||||
|
pub fn new(wtns: String, r1cs: String) -> CircuitsTests {
|
||||||
|
let mut rng = ThreadRng::default();
|
||||||
|
let builder = CircomBuilder::new(CircomConfig::<Bn254>::new(wtns, r1cs).unwrap());
|
||||||
|
let params =
|
||||||
|
generate_random_parameters::<Bn254, _, _>(builder.setup(), &mut rng).unwrap();
|
||||||
|
|
||||||
|
CircuitsTests {
|
||||||
|
builder,
|
||||||
|
params,
|
||||||
|
rng,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poseidon_hash(&mut self, elements: &[U256], hash: U256) -> bool {
|
||||||
|
let mut builder = self.builder.clone();
|
||||||
|
|
||||||
|
elements.iter().for_each(|c| builder.push_input("in", *c));
|
||||||
|
builder.push_input("hash", hash);
|
||||||
|
|
||||||
|
let circuit = builder.build().unwrap();
|
||||||
|
let inputs = circuit.get_public_inputs().unwrap();
|
||||||
|
let proof = prove(circuit, &self.params, &mut self.rng).unwrap();
|
||||||
|
let vk = prepare_verifying_key(&self.params.vk);
|
||||||
|
let public_inputs = prepare_inputs(&vk, &inputs).unwrap();
|
||||||
|
verify_proof_with_prepared_inputs(&vk, &proof, &public_inputs).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn poseidon_digest(&mut self, elements: &[U256], hash: U256) -> bool {
|
||||||
|
let mut builder = self.builder.clone();
|
||||||
|
|
||||||
|
elements
|
||||||
|
.iter()
|
||||||
|
.for_each(|c| builder.push_input("block", *c));
|
||||||
|
builder.push_input("hash", hash);
|
||||||
|
|
||||||
|
let circuit = builder.build().unwrap();
|
||||||
|
let inputs = circuit.get_public_inputs().unwrap();
|
||||||
|
|
||||||
|
let proof = prove(circuit, &self.params, &mut self.rng).unwrap();
|
||||||
|
let vk = prepare_verifying_key(&self.params.vk);
|
||||||
|
let public_inputs = prepare_inputs(&vk, &inputs).unwrap();
|
||||||
|
|
||||||
|
verify_proof_with_prepared_inputs(&vk, &proof, &public_inputs).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poseidon_hash() {
|
||||||
|
let r1cs = "./src/circuit_tests/artifacts/poseidon-hash-test.r1cs";
|
||||||
|
let wasm = "./src/circuit_tests/artifacts/poseidon-hash-test_js/poseidon-hash-test.wasm";
|
||||||
|
|
||||||
|
let mut hasher = CircuitsTests::new(wasm.to_string(), r1cs.to_string());
|
||||||
|
assert!(hasher.poseidon_hash(&[U256::from(1)], hash(&[U256::from(1)])));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_poseidon_digest() {
|
||||||
|
let r1cs = "./src/circuit_tests/artifacts/poseidon-digest-test.r1cs";
|
||||||
|
let wasm =
|
||||||
|
"./src/circuit_tests/artifacts/poseidon-digest-test_js/poseidon-digest-test.wasm";
|
||||||
|
|
||||||
|
let mut hasher = CircuitsTests::new(wasm.to_string(), r1cs.to_string());
|
||||||
|
let input: Vec<U256> = (0..256).map(|c| U256::from(c)).collect();
|
||||||
|
assert!(hasher.poseidon_digest(&input, digest(&input, Some(16))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storer() {
|
||||||
|
let r1cs = "./src/circuit_tests/artifacts/storer-test.r1cs";
|
||||||
|
let wasm = "./src/circuit_tests/artifacts/storer-test_js/storer-test.wasm";
|
||||||
|
let mut prover = StorageProofs::new(wasm.to_string(), r1cs.to_string(), None);
|
||||||
|
|
||||||
|
// generate a tuple of (preimages, hash), where preimages is a vector of 256 U256s
|
||||||
|
// and hash is the hash of each vector generated using the digest function
|
||||||
|
let data = (0..4)
|
||||||
|
.map(|_| {
|
||||||
|
let rng = ThreadRng::default();
|
||||||
|
let preimages: Vec<U256> = rng
|
||||||
|
.sample_iter(Alphanumeric)
|
||||||
|
.take(256)
|
||||||
|
.map(|c| U256::from(c))
|
||||||
|
.collect();
|
||||||
|
let hash = digest(&preimages, Some(16));
|
||||||
|
(preimages, hash)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(Vec<U256>, U256)>>();
|
||||||
|
|
||||||
|
let chunks: Vec<U256> = data.iter().flat_map(|c| c.0.to_vec()).collect();
|
||||||
|
let hashes: Vec<U256> = data.iter().map(|c| c.1).collect();
|
||||||
|
let path = [0, 1, 2, 3].to_vec();
|
||||||
|
|
||||||
|
let parent_hash_l = hash(&[hashes[0], hashes[1]]);
|
||||||
|
let parent_hash_r = hash(&[hashes[2], hashes[3]]);
|
||||||
|
|
||||||
|
let siblings = &[
|
||||||
|
hashes[1],
|
||||||
|
parent_hash_r,
|
||||||
|
hashes[0],
|
||||||
|
parent_hash_r,
|
||||||
|
hashes[3],
|
||||||
|
parent_hash_l,
|
||||||
|
hashes[2],
|
||||||
|
parent_hash_l,
|
||||||
|
];
|
||||||
|
|
||||||
|
let root = treehash(hashes.as_slice());
|
||||||
|
let proof_bytes = &mut Vec::new();
|
||||||
|
let public_inputs_bytes = &mut Vec::new();
|
||||||
|
|
||||||
|
prover
|
||||||
|
.prove(
|
||||||
|
chunks.as_slice(),
|
||||||
|
siblings,
|
||||||
|
hashes.as_slice(),
|
||||||
|
path.as_slice(),
|
||||||
|
root,
|
||||||
|
root, // random salt - block hash
|
||||||
|
proof_bytes,
|
||||||
|
public_inputs_bytes,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(prover
|
||||||
|
.verify(proof_bytes.as_slice(), public_inputs_bytes.as_slice())
|
||||||
|
.is_ok());
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/circuit_tests/poseidon-digest-test.circom
Normal file
20
src/circuit_tests/poseidon-digest-test.circom
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "../../circuits/poseidon-digest.circom";
|
||||||
|
|
||||||
|
template PoseidonDigestTest(BLOCK_SIZE, CHUNK_SIZE) {
|
||||||
|
signal input block[BLOCK_SIZE];
|
||||||
|
signal input hash;
|
||||||
|
signal output hash2;
|
||||||
|
|
||||||
|
component digest = PoseidonDigest(BLOCK_SIZE, CHUNK_SIZE);
|
||||||
|
for (var i = 0; i < BLOCK_SIZE; i++) {
|
||||||
|
digest.block[i] <== block[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
digest.hash === hash; // verify that the hash is correct
|
||||||
|
|
||||||
|
hash2 <== digest.hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main { public [hash] } = PoseidonDigestTest(256, 16);
|
||||||
17
src/circuit_tests/poseidon-hash-test.circom
Normal file
17
src/circuit_tests/poseidon-hash-test.circom
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "../../node_modules/circomlib/circuits/poseidon.circom";
|
||||||
|
|
||||||
|
template PoseidonHash(SIZE) {
|
||||||
|
signal input in[SIZE];
|
||||||
|
signal input hash;
|
||||||
|
|
||||||
|
component hasher = Poseidon(SIZE);
|
||||||
|
for(var i = 0; i < SIZE; i++) {
|
||||||
|
hasher.inputs[i] <== in[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher.out === hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
component main { public [hash] } = PoseidonHash(1);
|
||||||
5
src/circuit_tests/storer-test.circom
Normal file
5
src/circuit_tests/storer-test.circom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "../../circuits/storer.circom";
|
||||||
|
|
||||||
|
component main { public [root, salt] } = StorageProver(256, 4, 2, 16);
|
||||||
49
src/circuit_tests/utils.rs
Normal file
49
src/circuit_tests/utils.rs
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
|
||||||
|
use rs_poseidon::poseidon::hash;
|
||||||
|
use ruint::{aliases::U256, uint};
|
||||||
|
|
||||||
|
pub fn digest(input: &[U256], chunk_size: Option<usize>) -> U256 {
|
||||||
|
let chunk_size = chunk_size.unwrap_or(4);
|
||||||
|
let chunks = ((input.len() as f32) / (chunk_size as f32)).ceil() as usize;
|
||||||
|
let mut concat: Vec<U256> = vec![];
|
||||||
|
|
||||||
|
for i in 0..chunks {
|
||||||
|
let range = (i * chunk_size)..std::cmp::min((i + 1) * chunk_size, input.len());
|
||||||
|
let mut chunk = input[range].to_vec();
|
||||||
|
if chunk.len() < chunk_size {
|
||||||
|
chunk.resize(chunk_size, uint!(0_U256));
|
||||||
|
}
|
||||||
|
|
||||||
|
concat.push(hash(chunk.as_slice()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if concat.len() > 1 {
|
||||||
|
return hash(concat.as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
concat[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn treehash(leafs: &[U256]) -> U256 {
|
||||||
|
// simple merkle root (treehash) generator
|
||||||
|
// unbalanced trees will have the last leaf duplicated
|
||||||
|
let mut merkle: Vec<U256> = leafs.to_vec();
|
||||||
|
|
||||||
|
while merkle.len() > 1 {
|
||||||
|
let mut new_merkle = Vec::new();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < merkle.len() {
|
||||||
|
new_merkle.push(hash(&[merkle[i], merkle[i + 1]]));
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if merkle.len() % 2 == 1 {
|
||||||
|
new_merkle.push(hash(&[merkle[merkle.len() - 2], merkle[merkle.len() - 2]]));
|
||||||
|
}
|
||||||
|
|
||||||
|
merkle = new_merkle;
|
||||||
|
}
|
||||||
|
|
||||||
|
merkle[0]
|
||||||
|
}
|
||||||
302
src/ffi.rs
Normal file
302
src/ffi.rs
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
use ruint::aliases::U256;
|
||||||
|
|
||||||
|
use crate::storage_proofs::StorageProofs;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Buffer {
|
||||||
|
pub data: *const u8,
|
||||||
|
pub len: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ProofCtx {
|
||||||
|
pub proof: Buffer,
|
||||||
|
pub public_inputs: Buffer,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProofCtx {
|
||||||
|
pub fn new(proof: &[u8], public_inputs: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
proof: Buffer {
|
||||||
|
data: proof.as_ptr(),
|
||||||
|
len: proof.len(),
|
||||||
|
},
|
||||||
|
public_inputs: Buffer {
|
||||||
|
data: public_inputs.as_ptr(),
|
||||||
|
len: public_inputs.len(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Construct a StorageProofs object
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn init(
|
||||||
|
r1cs: *const &Buffer,
|
||||||
|
wasm: *const &Buffer,
|
||||||
|
zkey: *const &Buffer,
|
||||||
|
) -> *mut StorageProofs {
|
||||||
|
let r1cs = {
|
||||||
|
if r1cs.is_null() {
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let slice = std::slice::from_raw_parts((*r1cs).data, (*r1cs).len);
|
||||||
|
str::from_utf8(slice).unwrap().to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let wasm = {
|
||||||
|
if wasm.is_null() {
|
||||||
|
return std::ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let slice = std::slice::from_raw_parts((*wasm).data, (*wasm).len);
|
||||||
|
str::from_utf8(slice).unwrap().to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
let zkey = {
|
||||||
|
if !zkey.is_null() {
|
||||||
|
let slice = std::slice::from_raw_parts((*zkey).data, (*zkey).len);
|
||||||
|
Some(str::from_utf8(slice).unwrap().to_string())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Box::into_raw(Box::new(StorageProofs::new(wasm, r1cs, zkey)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Use after constructing a StorageProofs object with init
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn prove(
|
||||||
|
prover_ptr: *mut StorageProofs,
|
||||||
|
chunks: *const Buffer,
|
||||||
|
siblings: *const Buffer,
|
||||||
|
hashes: *const Buffer,
|
||||||
|
path: *const i32,
|
||||||
|
path_len: usize,
|
||||||
|
pubkey: *const Buffer,
|
||||||
|
root: *const Buffer,
|
||||||
|
salt: *const Buffer,
|
||||||
|
) -> *mut ProofCtx {
|
||||||
|
let chunks = {
|
||||||
|
let slice = std::slice::from_raw_parts((*chunks).data, (*chunks).len);
|
||||||
|
slice
|
||||||
|
.chunks(U256::BYTES)
|
||||||
|
.map(|c| U256::try_from_le_slice(c).unwrap())
|
||||||
|
.collect::<Vec<U256>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let siblings = {
|
||||||
|
let slice = std::slice::from_raw_parts((*siblings).data, (*siblings).len);
|
||||||
|
slice
|
||||||
|
.chunks(U256::BYTES)
|
||||||
|
.map(|c| U256::try_from_le_slice(c).unwrap())
|
||||||
|
.collect::<Vec<U256>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let hashes = {
|
||||||
|
let slice = std::slice::from_raw_parts((*hashes).data, (*hashes).len);
|
||||||
|
slice
|
||||||
|
.chunks(U256::BYTES)
|
||||||
|
.map(|c| U256::try_from_le_slice(c).unwrap())
|
||||||
|
.collect::<Vec<U256>>()
|
||||||
|
};
|
||||||
|
|
||||||
|
let path = {
|
||||||
|
let slice = std::slice::from_raw_parts(path, path_len);
|
||||||
|
slice.to_vec()
|
||||||
|
};
|
||||||
|
|
||||||
|
let pubkey =
|
||||||
|
U256::try_from_le_slice(std::slice::from_raw_parts((*pubkey).data, (*pubkey).len)).unwrap();
|
||||||
|
|
||||||
|
let root =
|
||||||
|
U256::try_from_le_slice(std::slice::from_raw_parts((*root).data, (*root).len)).unwrap();
|
||||||
|
|
||||||
|
let salt =
|
||||||
|
U256::try_from_le_slice(std::slice::from_raw_parts((*salt).data, (*salt).len)).unwrap();
|
||||||
|
|
||||||
|
let proof_bytes = &mut Vec::new();
|
||||||
|
let public_inputs_bytes = &mut Vec::new();
|
||||||
|
|
||||||
|
let mut _prover = &mut *prover_ptr;
|
||||||
|
_prover
|
||||||
|
.prove(
|
||||||
|
chunks.as_slice(),
|
||||||
|
siblings.as_slice(),
|
||||||
|
hashes.as_slice(),
|
||||||
|
path.as_slice(),
|
||||||
|
root,
|
||||||
|
salt,
|
||||||
|
proof_bytes,
|
||||||
|
public_inputs_bytes,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Box::into_raw(Box::new(ProofCtx::new(proof_bytes, public_inputs_bytes)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Should be called on a valid proof and public inputs previously generated by prove
|
||||||
|
pub unsafe extern "C" fn verify(
|
||||||
|
prover_ptr: *mut StorageProofs,
|
||||||
|
proof: *const Buffer,
|
||||||
|
public_inputs: *const Buffer,
|
||||||
|
) -> bool {
|
||||||
|
let proof = std::slice::from_raw_parts((*proof).data, (*proof).len);
|
||||||
|
let public_inputs = std::slice::from_raw_parts((*public_inputs).data, (*public_inputs).len);
|
||||||
|
let mut _prover = &mut *prover_ptr;
|
||||||
|
_prover.verify(proof, public_inputs).is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Use on a valid pointer to StorageProofs or panics
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn free_prover(prover: *mut StorageProofs) {
|
||||||
|
if prover.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { drop(Box::from_raw(prover)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// Use on a valid pointer to ProofCtx or panics
|
||||||
|
#[no_mangle]
|
||||||
|
pub unsafe extern "C" fn free_proof_ctx(ctx: *mut ProofCtx) {
|
||||||
|
if ctx.is_null() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(Box::from_raw(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use ark_std::rand::{distributions::Alphanumeric, rngs::ThreadRng, Rng};
|
||||||
|
use rs_poseidon::poseidon::hash;
|
||||||
|
use ruint::aliases::U256;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
circuit_tests::utils::{digest, treehash},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{init, prove, Buffer};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_storer_ffi() {
|
||||||
|
// generate a tuple of (preimages, hash), where preimages is a vector of 256 U256s
|
||||||
|
// and hash is the hash of each vector generated using the digest function
|
||||||
|
let data = (0..4)
|
||||||
|
.map(|_| {
|
||||||
|
let rng = ThreadRng::default();
|
||||||
|
let preimages: Vec<U256> = rng
|
||||||
|
.sample_iter(Alphanumeric)
|
||||||
|
.take(256)
|
||||||
|
.map(|c| U256::from(c))
|
||||||
|
.collect();
|
||||||
|
let hash = digest(&preimages, Some(16));
|
||||||
|
(preimages, hash)
|
||||||
|
})
|
||||||
|
.collect::<Vec<(Vec<U256>, U256)>>();
|
||||||
|
|
||||||
|
let chunks: Vec<u8> = data
|
||||||
|
.iter()
|
||||||
|
.map(|c| {
|
||||||
|
c.0.iter()
|
||||||
|
.map(|c| c.to_le_bytes_vec())
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<u8>>()
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let hashes: Vec<U256> = data.iter().map(|c| c.1).collect();
|
||||||
|
let hashes_slice: Vec<u8> = hashes.iter().map(|c| c.to_le_bytes_vec()).flatten().collect();
|
||||||
|
|
||||||
|
let path = [0, 1, 2, 3];
|
||||||
|
let parent_hash_l = hash(&[hashes[0], hashes[1]]);
|
||||||
|
let parent_hash_r = hash(&[hashes[2], hashes[3]]);
|
||||||
|
|
||||||
|
let sibling_hashes = &[
|
||||||
|
hashes[1],
|
||||||
|
parent_hash_r,
|
||||||
|
hashes[0],
|
||||||
|
parent_hash_r,
|
||||||
|
hashes[3],
|
||||||
|
parent_hash_l,
|
||||||
|
hashes[2],
|
||||||
|
parent_hash_l,
|
||||||
|
];
|
||||||
|
|
||||||
|
let siblings: Vec<u8> = sibling_hashes
|
||||||
|
.iter()
|
||||||
|
.map(|c| c.to_le_bytes_vec())
|
||||||
|
.flatten()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let root = treehash(hashes.as_slice());
|
||||||
|
let chunks_buff = Buffer {
|
||||||
|
data: chunks.as_ptr() as *const u8,
|
||||||
|
len: chunks.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let siblings_buff = Buffer {
|
||||||
|
data: siblings.as_ptr() as *const u8,
|
||||||
|
len: siblings.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let hashes_buff = Buffer {
|
||||||
|
data: hashes_slice.as_ptr() as *const u8,
|
||||||
|
len: hashes_slice.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let root_bytes: [u8; U256::BYTES] = root.to_le_bytes();
|
||||||
|
let root_buff = Buffer {
|
||||||
|
data: root_bytes.as_ptr() as *const u8,
|
||||||
|
len: root_bytes.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let r1cs_path = "src/circuit_tests/artifacts/storer-test.r1cs";
|
||||||
|
let wasm_path = "src/circuit_tests/artifacts/storer-test_js/storer-test.wasm";
|
||||||
|
|
||||||
|
let r1cs = &Buffer {
|
||||||
|
data: r1cs_path.as_ptr(),
|
||||||
|
len: r1cs_path.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let wasm = &Buffer {
|
||||||
|
data: wasm_path.as_ptr(),
|
||||||
|
len: wasm_path.len(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let prover_ptr = unsafe { init(&r1cs, &wasm, std::ptr::null()) };
|
||||||
|
let prove_ctx = unsafe {
|
||||||
|
prove(
|
||||||
|
prover_ptr,
|
||||||
|
&chunks_buff as *const Buffer,
|
||||||
|
&siblings_buff as *const Buffer,
|
||||||
|
&hashes_buff as *const Buffer,
|
||||||
|
&path as *const i32,
|
||||||
|
path.len(),
|
||||||
|
&root_buff as *const Buffer, // root
|
||||||
|
&root_buff as *const Buffer, // pubkey
|
||||||
|
&root_buff as *const Buffer, // salt/block hash
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(prove_ctx.is_null() == false);
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub mod ffi;
|
||||||
|
pub mod storage_proofs;
|
||||||
|
mod circuit_tests;
|
||||||
98
src/storage_proofs.rs
Normal file
98
src/storage_proofs.rs
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
|
||||||
|
use ark_bn254::{Bn254, Fr};
|
||||||
|
use ark_circom::{read_zkey, CircomBuilder, CircomConfig};
|
||||||
|
use ark_groth16::{
|
||||||
|
create_random_proof as prove, generate_random_parameters, prepare_verifying_key, verify_proof,
|
||||||
|
Proof, ProvingKey,
|
||||||
|
};
|
||||||
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read};
|
||||||
|
use ark_std::rand::rngs::ThreadRng;
|
||||||
|
use ruint::aliases::U256;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct StorageProofs {
|
||||||
|
builder: CircomBuilder<Bn254>,
|
||||||
|
params: ProvingKey<Bn254>,
|
||||||
|
rng: ThreadRng,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StorageProofs {
|
||||||
|
// TODO: add rng
|
||||||
|
pub fn new(
|
||||||
|
wtns: String,
|
||||||
|
r1cs: String,
|
||||||
|
zkey: Option<String>, /* , rng: Option<ThreadRng> */
|
||||||
|
) -> Self {
|
||||||
|
let mut rng = ThreadRng::default();
|
||||||
|
let builder = CircomBuilder::new(CircomConfig::<Bn254>::new(wtns, r1cs).unwrap());
|
||||||
|
let params: ProvingKey<Bn254> = match zkey {
|
||||||
|
Some(zkey) => {
|
||||||
|
let mut file = File::open(zkey).unwrap();
|
||||||
|
read_zkey(&mut file).unwrap().0
|
||||||
|
}
|
||||||
|
None => generate_random_parameters::<Bn254, _, _>(builder.setup(), &mut rng).unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
builder,
|
||||||
|
params,
|
||||||
|
rng,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prove(
|
||||||
|
&mut self,
|
||||||
|
chunks: &[U256],
|
||||||
|
siblings: &[U256],
|
||||||
|
hashes: &[U256],
|
||||||
|
path: &[i32],
|
||||||
|
root: U256,
|
||||||
|
salt: U256,
|
||||||
|
proof_bytes: &mut Vec<u8>,
|
||||||
|
public_inputs_bytes: &mut Vec<u8>,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let mut builder = self.builder.clone();
|
||||||
|
|
||||||
|
// vec of vecs is flattened, since wasm expects a contiguous array in memory
|
||||||
|
chunks.iter().for_each(|c| builder.push_input("chunks", *c));
|
||||||
|
|
||||||
|
siblings
|
||||||
|
.iter()
|
||||||
|
.for_each(|c| builder.push_input("siblings", *c));
|
||||||
|
|
||||||
|
hashes.iter().for_each(|c| builder.push_input("hashes", *c));
|
||||||
|
path.iter().for_each(|c| builder.push_input("path", *c));
|
||||||
|
|
||||||
|
builder.push_input("root", root);
|
||||||
|
builder.push_input("salt", salt);
|
||||||
|
|
||||||
|
let circuit = builder.build().map_err(|e| e.to_string())?;
|
||||||
|
let inputs = circuit
|
||||||
|
.get_public_inputs()
|
||||||
|
.ok_or("Unable to get public inputs!")?;
|
||||||
|
let proof = prove(circuit, &self.params, &mut self.rng).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
proof.serialize(proof_bytes).map_err(|e| e.to_string())?;
|
||||||
|
inputs
|
||||||
|
.serialize(public_inputs_bytes)
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify<RR: Read>(
|
||||||
|
&mut self,
|
||||||
|
proof_bytes: RR,
|
||||||
|
mut public_inputs: RR,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
let inputs: Vec<Fr> =
|
||||||
|
CanonicalDeserialize::deserialize(&mut public_inputs).map_err(|e| e.to_string())?;
|
||||||
|
let proof = Proof::<Bn254>::deserialize(proof_bytes).map_err(|e| e.to_string())?;
|
||||||
|
let vk = prepare_verifying_key(&self.params.vk);
|
||||||
|
|
||||||
|
verify_proof(&vk, &proof, inputs.as_slice()).map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
5
test/circuits/storer-test.circom
Normal file
5
test/circuits/storer-test.circom
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pragma circom 2.1.0;
|
||||||
|
|
||||||
|
include "../../circuits/storer.circom";
|
||||||
|
|
||||||
|
component main { public [root, salt] } = StorageProver(32, 4, 2, 5);
|
||||||
@ -1,5 +0,0 @@
|
|||||||
pragma circom 2.1.0;
|
|
||||||
|
|
||||||
include "../../circuits/storer.circom";
|
|
||||||
|
|
||||||
component main = StorageProver(32, 4, 2);
|
|
||||||
123
test/storer.js
123
test/storer.js
@ -3,25 +3,39 @@ const path = require("path");
|
|||||||
const crypto = require("crypto");
|
const crypto = require("crypto");
|
||||||
const F1Field = require("ffjavascript").F1Field;
|
const F1Field = require("ffjavascript").F1Field;
|
||||||
const Scalar = require("ffjavascript").Scalar;
|
const Scalar = require("ffjavascript").Scalar;
|
||||||
const mimc7 = require("circomlibjs").mimc7;
|
|
||||||
const mimcsponge = require("circomlibjs").mimcsponge;
|
|
||||||
const { MerkleTree } = require("merkletreejs");
|
|
||||||
const {c} = require("circom_tester");
|
const {c} = require("circom_tester");
|
||||||
const chaiAsPromised = require('chai-as-promised');
|
const chaiAsPromised = require('chai-as-promised');
|
||||||
|
const poseidon = require("circomlibjs/src/poseidon");
|
||||||
|
const wasm_tester = require("circom_tester").wasm;
|
||||||
|
// const snarkjs = require("snarkjs");
|
||||||
|
// const fs = require("fs");
|
||||||
|
|
||||||
chai.use(chaiAsPromised);
|
chai.use(chaiAsPromised);
|
||||||
|
|
||||||
exports.p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
const p = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617");
|
||||||
const Fr = new F1Field(exports.p);
|
const Fr = new F1Field(p);
|
||||||
|
|
||||||
const assert = chai.assert;
|
const assert = chai.assert;
|
||||||
const expect = chai.expect;
|
const expect = chai.expect;
|
||||||
|
|
||||||
const wasm_tester = require("circom_tester").wasm;
|
function digest(input, chunkSize = 5) {
|
||||||
const key = BigInt(2);
|
let chunks = Math.ceil(input.length / chunkSize);
|
||||||
|
let concat = [];
|
||||||
|
|
||||||
const digest = (buf) => mimc7.hash(buf, key);
|
for (let i = 0; i < chunks; i++) {
|
||||||
const digestMulti = (buf) => mimc7.multiHash(buf, key);
|
let chunk = input.slice(i * chunkSize, (i + 1) * chunkSize);
|
||||||
|
if (chunk.length < chunkSize) {
|
||||||
|
chunk = chunk.concat(Array(chunkSize - chunk.length).fill(0));
|
||||||
|
}
|
||||||
|
concat.push(poseidon(chunk));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (concat.length > 1) {
|
||||||
|
return poseidon(concat);
|
||||||
|
}
|
||||||
|
|
||||||
|
return concat[0]
|
||||||
|
}
|
||||||
|
|
||||||
function merkelize(leafs) {
|
function merkelize(leafs) {
|
||||||
// simple merkle root (treehash) generator
|
// simple merkle root (treehash) generator
|
||||||
@ -33,12 +47,12 @@ function merkelize(leafs) {
|
|||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
while (i < merkle.length) {
|
while (i < merkle.length) {
|
||||||
newMerkle.push(digestMulti([merkle[i], merkle[i + 1]]));
|
newMerkle.push(digest([merkle[i], merkle[i + 1]], 2));
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (merkle.length % 2 == 1) {
|
if (merkle.length % 2 == 1) {
|
||||||
newMerkle.add(digestMulti([merkle[merkle.length - 2], merkle[merkle.length - 2]]));
|
newMerkle.add(digest([merkle[merkle.length - 2], merkle[merkle.length - 2]], 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
merkle = newMerkle;
|
merkle = newMerkle;
|
||||||
@ -47,57 +61,71 @@ function merkelize(leafs) {
|
|||||||
return merkle[0];
|
return merkle[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: should be removed at some point, as the rust test should be sufficient, but left here for now to aid debugging
|
||||||
|
|
||||||
describe("Storer test", function () {
|
describe("Storer test", function () {
|
||||||
this.timeout(100000);
|
this.timeout(100000);
|
||||||
|
|
||||||
const a = Array.from(crypto.randomBytes(32).values()).map((v) => BigInt(v));
|
const a = Array.from(crypto.randomBytes(256).values()).map((v) => BigInt(v));
|
||||||
const aHash = digestMulti(a);
|
const aHash = digest(a, 16);
|
||||||
const b = Array.from(crypto.randomBytes(32).values()).map((v) => BigInt(v));
|
const b = Array.from(crypto.randomBytes(256).values()).map((v) => BigInt(v));
|
||||||
const bHash = digestMulti(b);
|
const bHash = digest(b, 16);
|
||||||
const c = Array.from(crypto.randomBytes(32).values()).map((v) => BigInt(v));
|
const c = Array.from(crypto.randomBytes(256).values()).map((v) => BigInt(v));
|
||||||
const cHash = digestMulti(c);
|
const cHash = digest(c, 16);
|
||||||
const d = Array.from(crypto.randomBytes(32).values()).map((v) => BigInt(v));
|
const d = Array.from(crypto.randomBytes(256).values()).map((v) => BigInt(v));
|
||||||
const dHash = digestMulti(d);
|
const dHash = digest(d, 16);
|
||||||
const salt = Array.from(crypto.randomBytes(32).values()).map((v) => BigInt(v));
|
const salt = Array.from(crypto.randomBytes(256).values()).map((v) => BigInt(v));
|
||||||
const saltHash = digestMulti(salt);
|
const saltHash = digest(salt, 16);
|
||||||
|
|
||||||
it("Should merkelize", async () => {
|
it("Should merkelize", async () => {
|
||||||
let root = merkelize([aHash, bHash]);
|
let root = merkelize([aHash, bHash]);
|
||||||
let hash = digestMulti([aHash, bHash]);
|
let hash = digest([aHash, bHash], 2);
|
||||||
|
|
||||||
assert.equal(hash, root);
|
assert.equal(hash, root);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should verify chunk is correct and part of dataset", async () => {
|
it("Should verify chunk is correct and part of dataset", async () => {
|
||||||
const cir = await wasm_tester(path.join(__dirname, "./circuits", "storer_test.circom"));
|
const cir = await wasm_tester("src/circuit_tests/storer-test.circom");
|
||||||
|
|
||||||
const root = merkelize([aHash, bHash, cHash, dHash]);
|
const root = merkelize([aHash, bHash, cHash, dHash]);
|
||||||
|
|
||||||
const parentHashL = digestMulti([aHash, bHash]);
|
const parentHashL = digest([aHash, bHash], 2);
|
||||||
const parentHashR = digestMulti([cHash, dHash]);
|
const parentHashR = digest([cHash, dHash], 2);
|
||||||
|
|
||||||
await cir.calculateWitness({
|
await cir.calculateWitness({
|
||||||
"chunks": [[a], [b], [c], [d]],
|
"chunks": [[a], [b], [c], [d]],
|
||||||
"siblings": [[bHash, parentHashR], [aHash, parentHashR], [dHash, parentHashL], [cHash, parentHashL]],
|
"siblings": [
|
||||||
|
[bHash, parentHashR],
|
||||||
|
[aHash, parentHashR],
|
||||||
|
[dHash, parentHashL],
|
||||||
|
[cHash, parentHashL]],
|
||||||
"hashes": [aHash, bHash, cHash, dHash],
|
"hashes": [aHash, bHash, cHash, dHash],
|
||||||
"path": [0, 1, 2, 3],
|
"path": [0, 1, 2, 3],
|
||||||
"root": root,
|
"root": root,
|
||||||
"salt": saltHash,
|
"salt": saltHash,
|
||||||
}, true);
|
}, true);
|
||||||
}).timeout(100000);
|
});
|
||||||
|
|
||||||
it("Should verify chunk is correct and part of dataset", async () => {
|
it("Should verify chunk is not correct and part of dataset", async () => {
|
||||||
const cir = await wasm_tester(path.join(__dirname, "./circuits", "storer_test.circom"));
|
const cir = await wasm_tester("src/circuit_tests/storer-test.circom");
|
||||||
|
|
||||||
const root = merkelize([aHash, bHash, cHash, dHash]);
|
const root = merkelize([aHash, bHash, cHash, dHash]);
|
||||||
|
|
||||||
const parentHashL = digestMulti([aHash, bHash]);
|
const parentHashL = digest([aHash, bHash], 2);
|
||||||
const parentHashR = digestMulti([cHash, dHash]);
|
const parentHashR = digest([cHash, dHash], 2);
|
||||||
|
|
||||||
const fn = async () => {
|
const fn = async () => {
|
||||||
return await cir.calculateWitness({
|
return await cir.calculateWitness({
|
||||||
"chunks": [[salt], [b], [c], [d]],
|
"chunks": [
|
||||||
"siblings": [[bHash, parentHashR], [aHash, parentHashR], [dHash, parentHashL], [cHash, parentHashL]],
|
[salt], // wrong chunk
|
||||||
|
[b],
|
||||||
|
[c],
|
||||||
|
[d]],
|
||||||
|
"siblings": [
|
||||||
|
[bHash, parentHashR],
|
||||||
|
[aHash, parentHashR],
|
||||||
|
[dHash, parentHashL],
|
||||||
|
[cHash, parentHashL]],
|
||||||
"hashes": [saltHash, bHash, cHash, dHash],
|
"hashes": [saltHash, bHash, cHash, dHash],
|
||||||
"path": [0, 1, 2, 3],
|
"path": [0, 1, 2, 3],
|
||||||
"root": root,
|
"root": root,
|
||||||
@ -108,6 +136,33 @@ describe("Storer test", function () {
|
|||||||
assert.isRejected(
|
assert.isRejected(
|
||||||
fn(), Error,
|
fn(), Error,
|
||||||
/Error: Error: Assert Failed.\nError in template StorageProver_7 line: 75/);
|
/Error: Error: Assert Failed.\nError in template StorageProver_7 line: 75/);
|
||||||
|
});
|
||||||
|
|
||||||
}).timeout(100000);
|
function range(start, end) {
|
||||||
|
return Array(end - start + 1).fill().map((_, idx) => start + idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
it("Should test poseidon digest", async () => {
|
||||||
|
const cir = await wasm_tester("src/circuit_tests/poseidon-digest-test.circom");
|
||||||
|
let input = range(0, 255).map((c) => BigInt(c));
|
||||||
|
await cir.calculateWitness({
|
||||||
|
"block": input,
|
||||||
|
"hash": digest(input, 16),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// it("Should prove digest with zkey file", async () => {
|
||||||
|
// let input = range(0, 255).map((c) => BigInt(c));
|
||||||
|
// const {proof, publicSignals} = await snarkjs.groth16.fullProve(
|
||||||
|
// {
|
||||||
|
// "block": input,
|
||||||
|
// "hash": digest(input, 16),
|
||||||
|
// },
|
||||||
|
// "src/circuit_tests/artifacts/poseidon-digest-test_js/poseidon-digest-test.wasm",
|
||||||
|
// "circuit_0000.zkey");
|
||||||
|
|
||||||
|
// const vKey = JSON.parse(fs.readFileSync("verification_key.json"));
|
||||||
|
// const res = await snarkjs.groth16.verify(vKey, publicSignals, proof);
|
||||||
|
// assert(res);
|
||||||
|
// });
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user