script/research/zkvm-metering: tools to generate and verify zk proofs containing single zkvm opcodes for resource usage measurement

This commit is contained in:
oars
2025-11-15 12:13:31 +03:00
committed by skoupidi
parent a08b04efd2
commit 52461e3fd2
34 changed files with 975 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
# ZkVM Metering tool
- The aim of this tool is to analyze resource usage of ZkVM opcodes
using [heaptrack](https://github.com/KDE/heaptrack).
- We have a proof generator and verifier to profile each ZkVM Opcode.
- The `generator` generates a proof, verifying key and public inputs
for all the proofs stored in `generator/proof`.
- Each Zk script file in `generator/proof` contains a single ZkVM
opcode.
- The `generator` saves it's outputs to disk for later use by the
`verifier`.
- The `verifier` loads the proof, verifying key and public inputs and
verifies a single proof at a time identified by the opcode name.
#### Steps to profile a ZkVM Opcode
To generate the proofs go to `generator` directory and run these
commands.
```
% make
% ./generator
```
To verify the proof and profile all the opcodes go to `verifier` and
run these commands. You need to install `heaptrack` before running the
second command.
```
% make
% ./run_heaptrack.sh
```
`run_heaptrack.sh` will generate `heaptrack` report for all the opcodes
in `output` directory. If you prover to analyze a single opcode run
the following.
```
% heaptrack ./verifer [OPCODE_NAME]
```
Once the `heaptrack` report is generated you can view it using `heaptrack_gui`.
#### Analysis Results
| # | Opcode | RAM Usage | Verifying Key Size | Proof Size |
|----|-----------------------|-----------|--------------------|------------|
| 0 | sparse_merkle_root | 17.1 MB | 1.2 MB | 7.7 kB |
| 1 | merkle_root | 8.1 MB | 572.2 kB | 7.7 kB |
| 2 | posedion_hash | 2.4 MB | 145.6 kB | 7.6 kB |
| 3 | base_add | 2.4 MB | 145.6 kB | 7.6 kB |
| 4 | base_mul | 2.4 MB | 145.6 kB | 7.6 kB |
| 5 | base_sub | 2.4 MB | 145.6 kB | 7.6 kB |
| 6 | ec_add | 2.4 MB | 145.6 kB | 7.6 kB |
| 7 | ec_mul | 2.4 MB | 145.6 kB | 7.6 kB |
| 8 | ec_mul_base | 2.4 MB | 145.6 kB | 7.6 kB |
| 9 | ec_mul_short | 2.4 MB | 145.6 kB | 7.6 kB |
| 10 | ec_mul_var_base | 2.4 MB | 145.6 kB | 7.6 kB |
| 11 | ec_get_x | 2.4 MB | 145.6 kB | 7.6 kB |
| 12 | ec_get_y | 2.4 MB | 145.6 kB | 7.6 kB |
| 13 | constrain_instance | 2.4 MB | 145.6 kB | 7.6 kB |
| 14 | constrain_equal_base | 2.4 MB | 145.6 kB | 7.6 kB |
| 15 | constrain_equal_point | 2.4 MB | 145.6 kB | 7.6 kB |
| 16 | bool_check | 2.4 MB | 145.6 kB | 7.6 kB |
| 17 | cond_select | 2.4 MB | 145.6 kB | 7.6 kB |
| 18 | zero_cond | 2.4 MB | 145.6 kB | 7.6 kB |
| 19 | less_than_strict | 2.4 MB | 145.6 kB | 7.6 kB |
| 20 | less_than_loose | 2.4 MB | 145.6 kB | 7.6 kB |
| 21 | range_check | 2.4 MB | 145.6 kB | 7.6 kB |
| 22 | witness_base | 2.4 MB | 145.6 kB | 7.6 kB |
| 23 | debug | 2.4 MB | 145.6 kB | 7.6 kB |

View File

@@ -0,0 +1,5 @@
/target
Cargo.lock
rustfmt.toml
generator
*.bin

View File

@@ -0,0 +1,26 @@
[package]
name = "generator"
version = "0.1.0"
description = "CLI-utility to generate zk proofs for analyzing zkvm opcodes resource usage"
authors = ["Dyne.org foundation <foundation@dyne.org>"]
repository = "https://codeberg.org/darkrenaissance/darkfi"
license = "AGPL-3.0-only"
edition = "2024"
[workspace]
[dependencies]
darkfi-sdk = {path = "../../../../src/sdk"}
darkfi = {path = "../../../../", features = ["zk"]}
halo2_proofs = {version = "0.3.1", features = ["circuit-params"]}
halo2_gadgets = {version = "0.3.1", features = ["circuit-params"]}
rand = "0.8.5"
darkfi-serial = "0.5.0"
[dev-dependencies]
halo2_proofs = {version = "0.3.1", features = ["dev-graph", "sanity-checks"]}
halo2_gadgets = "0.3.1"
[patch.crates-io]
halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v031"}
halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v031"}

View File

@@ -0,0 +1,48 @@
.POSIX:
# Install prefix
PREFIX = $(HOME)/.cargo
# Cargo binary
CARGO = cargo
# Compile target
RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2)
# Uncomment when doing musl static builds
#RUSTFLAGS = -C target-feature=+crt-static -C link-self-contained=yes
# zkas compiler binary
ZKAS = ../../../../zkas
# zkas circuits
PROOFS_SRC = $(shell find proof -type f -name '*.zk')
PROOFS_BIN = $(PROOFS_SRC:=.bin)
SRC = \
Cargo.toml \
$(shell find src -type f -name '*.rs') \
BIN = $(shell grep '^name = ' Cargo.toml | cut -d' ' -f3 | tr -d '"')
all: $(BIN)
$(PROOFS_BIN): $(ZKAS) $(PROOFS_SRC)
$(ZKAS) $(basename $@) -o $@
$(BIN): $(PROOFS_BIN) $(SRC)
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package $@
cp -f target/$(RUST_TARGET)/release/$@ $@
fmt:
$(CARGO) +nightly fmt --all
clippy:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clippy --target=$(RUST_TARGET) \
--release --all-features --workspace --tests
clean:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN)
rm -f $(BIN)
rm -f proof/*.bin
.PHONY: all fmt clippy clean

View File

@@ -0,0 +1,14 @@
k = 11;
field = "pallas";
constant "BaseAdd" {}
witness "BaseAdd" {
Base a,
Base b,
}
circuit "BaseAdd" {
sum = base_add(a, b);
# constrain_instance(sum)
}

View File

@@ -0,0 +1,14 @@
k = 11;
field = "pallas";
constant "BaseMul" {}
witness "BaseMul" {
Base a,
Base b,
}
circuit "BaseMul" {
product = base_mul(a, b);
# constrain_instance(product)
}

View File

@@ -0,0 +1,14 @@
k = 11;
field = "pallas";
constant "BaseSub" {}
witness "BaseSub" {
Base a,
Base b,
}
circuit "BaseSub" {
difference = base_sub(a, b);
# constrain_instance(difference)
}

View File

@@ -0,0 +1,12 @@
k = 11;
field = "pallas";
constant "BoolCheck" {}
witness "BoolCheck" {
Base a,
}
circuit "BoolCheck" {
bool_check(a);
}

View File

@@ -0,0 +1,15 @@
k = 11;
field = "pallas";
constant "CondSelect" {}
witness "CondSelect" {
Base a,
Base b,
Base cond,
}
circuit "CondSelect" {
out = cond_select(cond, a, b);
#constrain_instance(out);
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "ConstrainEqualBase" {}
witness "ConstrainEqualBase" {
Base a,
Base b,
}
circuit "ConstrainEqualBase" {
constrain_equal_base(a, b);
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "ConstrainEqualPoint" {}
witness "ConstrainEqualPoint" {
EcPoint a,
EcPoint b,
}
circuit "ConstrainEqualPoint" {
constrain_equal_point(a, b);
}

View File

@@ -0,0 +1,12 @@
k = 11;
field = "pallas";
constant "ConstrainInstance" {}
witness "ConstrainInstance" {
Base a,
}
circuit "ConstrainInstance" {
constrain_instance(a);
}

View File

@@ -0,0 +1,12 @@
k = 11;
field = "pallas";
constant "Debug" {}
witness "Debug" {
Base a,
}
circuit "Debug" {
debug(a);
}

View File

@@ -0,0 +1,15 @@
k = 11;
field = "pallas";
constant "EcAdd" {}
witness "EcAdd" {
EcPoint a,
EcPoint b,
}
circuit "EcAdd" {
sum = ec_add(a, b);
#constrain_instance(ec_get_x(sum));
#constrain_instance(ec_get_y(sum));
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "EcGetX" {}
witness "EcGetX" {
EcPoint a,
}
circuit "EcGetX" {
a_x = ec_get_x(a);
#constrain_instance(a_x);
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "EcGetY" {}
witness "EcGetY" {
EcPoint a,
}
circuit "EcGetY" {
a_y = ec_get_y(a);
#constrain_instance(a_y);
}

View File

@@ -0,0 +1,16 @@
k = 11;
field = "pallas";
constant "EcMul" {
EcFixedPoint VALUE_COMMIT_RANDOM,
}
witness "EcMul" {
Scalar value_blind,
}
circuit "EcMul" {
vcr = ec_mul(value_blind, VALUE_COMMIT_RANDOM);
#constrain_instance(ec_get_x(vcr));
#constrain_instance(ec_get_y(vcr));
}

View File

@@ -0,0 +1,16 @@
k = 11;
field = "pallas";
constant "EcMulBase" {
EcFixedPointBase NULLIFIER_K,
}
witness "EcMulBase" {
Base secret,
}
circuit "EcMulBase" {
public = ec_mul_base(secret, NULLIFIER_K);
#constrain_instance(ec_get_x(public));
#constrain_instance(ec_get_y(public));
}

View File

@@ -0,0 +1,16 @@
k = 11;
field = "pallas";
constant "EcMulShort" {
EcFixedPointShort VALUE_COMMIT_VALUE,
}
witness "EcMulShort" {
Base value,
}
circuit "EcMulShort" {
vcv = ec_mul_short(value, VALUE_COMMIT_VALUE);
#constrain_instance(ec_get_x(vcv));
#constrain_instance(ec_get_y(vcv));
}

View File

@@ -0,0 +1,16 @@
k = 11;
field = "pallas";
constant "EcMulVarBase" {
}
witness "EcMulVarBase" {
Base ephem_secret,
EcNiPoint pubkey,
}
circuit "EcMulVarBase" {
ephem_public = ec_mul_var_base(ephem_secret, pubkey);
constrain_instance(ec_get_x(ephem_public));
constrain_instance(ec_get_y(ephem_public));
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "LessThanLoose" {}
witness "LessThanLoose" {
Base a,
Base b,
}
circuit "LessThanLoose" {
less_than_loose(a, b);
}

View File

@@ -0,0 +1,13 @@
k = 11;
field = "pallas";
constant "LessThanStrict" {}
witness "LessThanStrict" {
Base a,
Base b,
}
circuit "LessThanStrict" {
less_than_strict(a, b);
}

View File

@@ -0,0 +1,17 @@
k = 13;
field = "pallas";
constant "MerkleRoot" {
}
witness "MerkleRoot" {
Base leaf,
Uint32 leaf_pos,
MerklePath path,
}
circuit "MerkleRoot" {
root = merkle_root(leaf_pos, path, leaf);
# constrain_instance(root)
}

View File

@@ -0,0 +1,14 @@
k = 11;
field = "pallas";
constant "PoseidonHash" {}
witness "PoseidonHash" {
Base a,
Base b,
}
circuit "PoseidonHash" {
hash = poseidon_hash(a, b);
#constrain_instance(hash);
}

View File

@@ -0,0 +1,12 @@
k = 11;
field = "pallas";
constant "RangeCheck" {}
witness "RangeCheck" {
Base a,
}
circuit "RangeCheck" {
range_check(64, a);
}

View File

@@ -0,0 +1,16 @@
k = 14;
field = "pallas";
constant "SparseMerkleRoot" {
}
witness "SparseMerkleRoot" {
SparseMerklePath path,
Base leaf,
}
circuit "SparseMerkleRoot" {
root = sparse_merkle_root(leaf, path, leaf);
# constrain_instance(root)
}

View File

@@ -0,0 +1,12 @@
k = 11;
field = "pallas";
constant "WitnessBase" {}
witness "WitnessBase" {
}
circuit "WitnessBase" {
two = witness_base(2);
#constrain_instance(two);
}

View File

@@ -0,0 +1,14 @@
k = 11;
field = "pallas";
constant "ZeroCond" {}
witness "ZeroCond" {
Base a,
Base b,
}
circuit "ZeroCond" {
out = zero_cond(a, b);
#constrain_instance(out);
}

View File

@@ -0,0 +1,348 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2025 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{
fs::{File, read_dir},
io::{Read, Write},
path::Path,
};
use darkfi::{
zk::{Proof, ProvingKey, VerifyingKey, Witness, ZkCircuit, empty_witnesses, halo2::Field},
zkas::ZkBinary,
};
use darkfi_sdk::{
crypto::{
MerkleNode, MerkleTree,
constants::{
NullifierK,
OrchardFixedBasesFull::ValueCommitR,
fixed_bases::{VALUE_COMMITMENT_PERSONALIZATION, VALUE_COMMITMENT_V_BYTES},
},
pasta_prelude::{Curve, CurveAffine, CurveExt, Group},
smt::{EMPTY_NODES_FP, MemoryStorageFp, PoseidonFp, SmtMemoryFp},
util::{fp_mod_fv, poseidon_hash},
},
pasta::{Ep, Fp, Fq, pallas, pallas::Base},
};
use darkfi_serial::serialize;
use halo2_gadgets::ecc::chip::FixedPoint;
use halo2_proofs::circuit::Value;
use rand::rngs::OsRng;
fn main() {
let entries = read_dir("proof").unwrap();
// Read each compiled zk file bin
for entry in entries.flatten() {
let path = entry.path();
if !(path.is_file() && path.to_str().unwrap().ends_with(".zk.bin")) {
continue
}
let name = path.file_name().unwrap().to_str().unwrap().split(".").next().unwrap();
let proof_file = format!("proof/{name}.proof.bin");
let vk_file = format!("proof/{name}.vks.bin");
let public_inputs_file = format!("proof/{name}.pi.bin");
// Skip if already generated
if Path::new(&proof_file).exists() &&
Path::new(&vk_file).exists() &&
Path::new(&public_inputs_file).exists()
{
println!("{name} is already generated");
continue;
}
println!("Generating {name} ...");
// Open zk bin
let mut file = File::open(&path).unwrap();
let mut buf = vec![];
file.read_to_end(&mut buf).unwrap();
let zkbin = ZkBinary::decode(&buf).unwrap();
// Get witnesses and public inputs for that particular zk file
let (witnesses, public_inputs) = retrieve_proof_inputs(name);
// Generate and save Proof
let circuit = ZkCircuit::new(witnesses, &zkbin);
let proving_key = ProvingKey::build(zkbin.k, &circuit);
let proof = Proof::create(&proving_key, &[circuit], &public_inputs, &mut OsRng).unwrap();
let proof_export = serialize(&proof);
let mut f = File::create(&proof_file).unwrap();
f.write_all(&proof_export).unwrap();
// Generate and save Verifying Key
let verifier_witnesses = empty_witnesses(&zkbin).unwrap();
let circuit = ZkCircuit::new(verifier_witnesses, &zkbin);
let verifying_key = VerifyingKey::build(zkbin.k, &circuit);
let mut vk_export = vec![];
verifying_key.write(&mut vk_export).unwrap();
let mut f = File::create(&vk_file).unwrap();
f.write_all(&vk_export).unwrap();
// Save Public inputs
let public_inputs_export = serialize(&public_inputs);
let mut f = File::create(&public_inputs_file).unwrap();
f.write_all(&public_inputs_export).unwrap();
}
}
fn retrieve_proof_inputs(name: &str) -> (Vec<Witness>, Vec<Base>) {
match name {
"sparse_merkle_root" => {
let hasher = PoseidonFp::new();
let store = MemoryStorageFp::new();
let mut smt = SmtMemoryFp::new(store, hasher.clone(), &EMPTY_NODES_FP);
let leaves =
vec![Fp::random(&mut OsRng), Fp::random(&mut OsRng), Fp::random(&mut OsRng)];
let leaves: Vec<_> = leaves.into_iter().map(|l| (l, l)).collect();
smt.insert_batch(leaves.clone()).unwrap();
let (pos, leaf) = leaves[2];
let root = smt.root();
let path = smt.prove_membership(&pos);
let prover_witnesses = vec![
Witness::SparseMerklePath(Value::known(path.path)),
Witness::Base(Value::known(leaf)),
];
let public_inputs = vec![root];
(prover_witnesses, public_inputs)
}
"merkle_root" => {
let mut tree = MerkleTree::new(u32::MAX as usize);
let node1 = MerkleNode::from(Fp::random(&mut OsRng));
let node2 = MerkleNode::from(Fp::random(&mut OsRng));
let node3 = MerkleNode::from(Fp::random(&mut OsRng));
tree.append(node1);
tree.mark();
tree.append(node2);
let leaf_pos = tree.mark().unwrap();
tree.append(node3);
let root = tree.root(0).unwrap().inner();
let path = tree.witness(leaf_pos, 0).unwrap();
let prover_witnesses = vec![
Witness::Base(Value::known(node2.inner())),
Witness::Uint32(Value::known(u64::from(leaf_pos).try_into().unwrap())),
Witness::MerklePath(Value::known(path.try_into().unwrap())),
];
let public_inputs = vec![root];
(prover_witnesses, public_inputs)
}
"base_add" => {
let b1 = Fp::from(4u64);
let b2 = Fp::from(110u64);
let prover_witnesses =
vec![Witness::Base(Value::known(b1)), Witness::Base(Value::known(b2))];
let public_inputs = vec![b1 + b2];
(prover_witnesses, public_inputs)
}
"base_mul" => {
let b1 = Fp::from(4u64);
let b2 = Fp::from(110u64);
let prover_witnesses =
vec![Witness::Base(Value::known(b1)), Witness::Base(Value::known(b2))];
let public_inputs = vec![b1 * b2];
(prover_witnesses, public_inputs)
}
"base_sub" => {
let b1 = Fp::from(4u64);
let b2 = Fp::from(110u64);
let prover_witnesses =
vec![Witness::Base(Value::known(b1)), Witness::Base(Value::known(b2))];
let public_inputs = vec![b1 - b2];
(prover_witnesses, public_inputs)
}
"ec_add" => {
let p1 = Ep::random(&mut OsRng);
let p2 = Ep::random(&mut OsRng);
let sum = (p1 + p2).to_affine();
let sum_x = *sum.coordinates().unwrap().x();
let sum_y = *sum.coordinates().unwrap().y();
let prover_witnesses =
vec![Witness::EcPoint(Value::known(p1)), Witness::EcPoint(Value::known(p2))];
let public_inputs = vec![sum_x, sum_y];
(prover_witnesses, public_inputs)
}
"ec_mul" => {
let scalar_blind = Fq::random(&mut OsRng);
let vcr = (ValueCommitR.generator() * scalar_blind).to_affine();
let vcr_x = *vcr.coordinates().unwrap().x();
let vcr_y = *vcr.coordinates().unwrap().y();
let prover_witnesses = vec![Witness::Scalar(Value::known(scalar_blind))];
let public_inputs = vec![vcr_x, vcr_y];
(prover_witnesses, public_inputs)
}
"ec_mul_base" => {
let secret_key = Fp::random(&mut OsRng);
let pubkey = (NullifierK.generator() * fp_mod_fv(secret_key)).to_affine();
let pubkey_x = *pubkey.coordinates().unwrap().x();
let pubkey_y = *pubkey.coordinates().unwrap().y();
let prover_witnesses = vec![Witness::Base(Value::known(secret_key))];
let public_inputs = vec![pubkey_x, pubkey_y];
(prover_witnesses, public_inputs)
}
"ec_mul_short" => {
// we can't use Fp::random() since it can be more than u64::MAX and we need value to be u64
let value = Fp::from(42);
let hasher = pallas::Point::hash_to_curve(VALUE_COMMITMENT_PERSONALIZATION);
let val_commit = hasher(&VALUE_COMMITMENT_V_BYTES);
let vcv = (val_commit * fp_mod_fv(value)).to_affine();
let vcv_x = *vcv.coordinates().unwrap().x();
let vcv_y = *vcv.coordinates().unwrap().y();
let prover_witnesses = vec![Witness::Base(Value::known(value))];
let public_inputs = vec![vcv_x, vcv_y];
(prover_witnesses, public_inputs)
}
"ec_mul_var_base" => {
let ephem_secret = Fp::random(&mut OsRng);
let pubkey = NullifierK.generator() * fp_mod_fv(ephem_secret);
let ephem_pub = (pubkey * fp_mod_fv(ephem_secret)).to_affine();
let ephem_pub_x = *ephem_pub.coordinates().unwrap().x();
let ephem_pub_y = *ephem_pub.coordinates().unwrap().y();
let prover_witnesses = vec![
Witness::Base(Value::known(ephem_secret)),
Witness::EcNiPoint(Value::known(pubkey)),
];
let public_inputs = vec![ephem_pub_x, ephem_pub_y];
(prover_witnesses, public_inputs)
}
"ec_get_x" => {
let p = Ep::random(&mut OsRng);
let x = *p.to_affine().coordinates().unwrap().x();
let prover_witnesses = vec![Witness::EcPoint(Value::known(p))];
let public_inputs = vec![x];
(prover_witnesses, public_inputs)
}
"ec_get_y" => {
let p = Ep::random(&mut OsRng);
let y = *p.to_affine().coordinates().unwrap().y();
let prover_witnesses = vec![Witness::EcPoint(Value::known(p))];
let public_inputs = vec![y];
(prover_witnesses, public_inputs)
}
"poseidon_hash" => {
let a = Fp::random(&mut OsRng);
let b = Fp::random(&mut OsRng);
let hash = poseidon_hash([a, b]);
let prover_witnesses =
vec![Witness::Base(Value::known(a)), Witness::Base(Value::known(b))];
let public_inputs = vec![hash];
(prover_witnesses, public_inputs)
}
"constrain_instance" => {
let a = Fp::random(&mut OsRng);
let prover_witnesses = vec![Witness::Base(Value::known(a))];
let public_inputs = vec![a];
(prover_witnesses, public_inputs)
}
"witness_base" => (vec![], vec![Fp::from(2)]),
"constrain_equal_base" => {
let a = Fp::from(23);
let prover_witnesses =
vec![Witness::Base(Value::known(a)), Witness::Base(Value::known(a))];
(prover_witnesses, vec![])
}
"constrain_equal_point" => {
let a = Ep::random(&mut OsRng);
let prover_witnesses =
vec![Witness::EcPoint(Value::known(a)), Witness::EcPoint(Value::known(a))];
(prover_witnesses, vec![])
}
"less_than_strict" => {
let a = Fp::from(23);
let b = Fp::from(42);
let prover_witnesses =
vec![Witness::Base(Value::known(a)), Witness::Base(Value::known(b))];
(prover_witnesses, vec![])
}
"less_than_loose" => {
let a = Fp::from(23);
let prover_witnesses =
vec![Witness::Base(Value::known(a)), Witness::Base(Value::known(a))];
(prover_witnesses, vec![])
}
"bool_check" => {
let a = Fp::from(1);
let prover_witnesses = vec![Witness::Base(Value::known(a))];
(prover_witnesses, vec![])
}
"cond_select" => {
let a = Fp::from(23);
let b = Fp::from(42);
let cond = Fp::from(1);
let prover_witnesses = vec![
Witness::Base(Value::known(a)),
Witness::Base(Value::known(b)),
Witness::Base(Value::known(cond)),
];
let public_inputs = vec![a];
(prover_witnesses, public_inputs)
}
"zero_cond" => {
let a = Fp::from(0);
let b = Fp::from(23);
let prover_witnesses =
vec![Witness::Base(Value::known(a)), Witness::Base(Value::known(b))];
let public_inputs = vec![a];
(prover_witnesses, public_inputs)
}
"range_check" => {
let a = Fp::from(23);
let prover_witnesses = vec![Witness::Base(Value::known(a))];
(prover_witnesses, vec![])
}
"debug" => {
let a = Fp::from(23);
let prover_witnesses = vec![Witness::Base(Value::known(a))];
(prover_witnesses, vec![])
}
_ => panic!("unsupported Zk script"),
}
}

View File

@@ -0,0 +1,5 @@
/target
Cargo.lock
rustfmt.toml
verifier
*.zst

View File

@@ -0,0 +1,22 @@
[package]
name = "verifier"
version = "0.1.0"
description = "CLI-utility to verify zk proofs for analysis"
authors = ["Dyne.org foundation <foundation@dyne.org>"]
repository = "https://codeberg.org/darkrenaissance/darkfi"
license = "AGPL-3.0-only"
edition = "2024"
[workspace]
[dependencies]
darkfi-sdk = {path = "../../../../src/sdk"}
darkfi = {path = "../../../../", features = ["zk"]}
darkfi-serial = "0.5.0"
[patch.crates-io]
halo2_proofs = {git="https://github.com/parazyd/halo2", branch="v031"}
halo2_gadgets = {git="https://github.com/parazyd/halo2", branch="v031"}
[profile.release]
debug = true

View File

@@ -0,0 +1,37 @@
.POSIX:
# Install prefix
PREFIX = $(HOME)/.cargo
# Cargo binary
CARGO = cargo
# Compile target
RUST_TARGET = $(shell rustc -Vv | grep '^host: ' | cut -d' ' -f2)
# Uncomment when doing musl static builds
#RUSTFLAGS = -C target-feature=+crt-static -C link-self-contained=yes
SRC = \
Cargo.toml \
$(shell find src -type f -name '*.rs') \
BIN = $(shell grep '^name = ' Cargo.toml | cut -d' ' -f3 | tr -d '"')
all: $(BIN)
$(BIN): $(SRC)
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) build --target=$(RUST_TARGET) --release --package $@
cp -f target/$(RUST_TARGET)/release/$@ $@
fmt:
$(CARGO) +nightly fmt --all
clippy:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clippy --target=$(RUST_TARGET) \
--release --all-features --workspace --tests
clean:
RUSTFLAGS="$(RUSTFLAGS)" $(CARGO) clean --target=$(RUST_TARGET) --release --package $(BIN)
rm -f $(BIN)
.PHONY: all fmt clippy clean

View File

@@ -0,0 +1,10 @@
#!/bin/bash
SRC_DIR="../generator/proof"
for file in "$SRC_DIR"/*.zk; do
filename=$(basename "$file")
name="${filename%.zk}"
heaptrack --output "output/${name}" ./verifier "$name"
done

View File

@@ -0,0 +1,75 @@
/* This file is part of DarkFi (https://dark.fi)
*
* Copyright (C) 2020-2025 Dyne.org foundation
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::{
env,
fs::File,
io::{Cursor, Read},
};
use darkfi::{
Result,
zk::{Proof, VerifyingKey, ZkCircuit, empty_witnesses},
zkas::ZkBinary,
};
use darkfi_sdk::pasta::pallas::Base;
use darkfi_serial::deserialize;
fn main() -> Result<()> {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
println!("Usage: ./verifier opcode_name");
return Ok(())
}
let file_name = &args[1];
let name = file_name.split(".").next().unwrap();
//Load zkbin, proof, verifying key, public inputs from file
let mut file = File::open(format!("../generator/proof/{name}.zk.bin"))?;
let mut bincode = vec![];
file.read_to_end(&mut bincode)?;
let mut file = File::open(format!("../generator/proof/{name}.proof.bin"))?;
let mut proof_bin = vec![];
file.read_to_end(&mut proof_bin)?;
let mut file = File::open(format!("../generator/proof/{name}.vks.bin"))?;
let mut vkbin = vec![];
file.read_to_end(&mut vkbin)?;
let mut file = File::open(format!("../generator/proof/{name}.pi.bin"))?;
let mut public_inputs_bin = vec![];
file.read_to_end(&mut public_inputs_bin)?;
// Deserialize and Verify
let zkbin = ZkBinary::decode(&bincode)?;
let verifier_witnesses = empty_witnesses(&zkbin)?;
// Create the circuit
let circuit = ZkCircuit::new(verifier_witnesses, &zkbin);
let proof: Proof = deserialize(&proof_bin)?;
let mut vk_buf = Cursor::new(vkbin);
let vk = VerifyingKey::read::<Cursor<Vec<u8>>, ZkCircuit>(&mut vk_buf, circuit)?;
//let vk = VerifyingKey::build(zkbin.k, &circuit);
let public_inputs: Vec<Base> = deserialize(&public_inputs_bin)?;
Ok(proof.verify(&vk, &public_inputs)?)
}