diff --git a/script/research/zkvm-metering/README.md b/script/research/zkvm-metering/README.md new file mode 100644 index 000000000..84a4b6afb --- /dev/null +++ b/script/research/zkvm-metering/README.md @@ -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 | diff --git a/script/research/zkvm-metering/generator/.gitignore b/script/research/zkvm-metering/generator/.gitignore new file mode 100644 index 000000000..cf1c2241f --- /dev/null +++ b/script/research/zkvm-metering/generator/.gitignore @@ -0,0 +1,5 @@ +/target +Cargo.lock +rustfmt.toml +generator +*.bin diff --git a/script/research/zkvm-metering/generator/Cargo.toml b/script/research/zkvm-metering/generator/Cargo.toml new file mode 100644 index 000000000..4c2341adf --- /dev/null +++ b/script/research/zkvm-metering/generator/Cargo.toml @@ -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 "] +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"} \ No newline at end of file diff --git a/script/research/zkvm-metering/generator/Makefile b/script/research/zkvm-metering/generator/Makefile new file mode 100644 index 000000000..34d70e240 --- /dev/null +++ b/script/research/zkvm-metering/generator/Makefile @@ -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 diff --git a/script/research/zkvm-metering/generator/proof/base_add.zk b/script/research/zkvm-metering/generator/proof/base_add.zk new file mode 100644 index 000000000..2321d5107 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/base_add.zk @@ -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) +} diff --git a/script/research/zkvm-metering/generator/proof/base_mul.zk b/script/research/zkvm-metering/generator/proof/base_mul.zk new file mode 100644 index 000000000..15be1bad0 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/base_mul.zk @@ -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) +} diff --git a/script/research/zkvm-metering/generator/proof/base_sub.zk b/script/research/zkvm-metering/generator/proof/base_sub.zk new file mode 100644 index 000000000..3412d6819 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/base_sub.zk @@ -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) +} diff --git a/script/research/zkvm-metering/generator/proof/bool_check.zk b/script/research/zkvm-metering/generator/proof/bool_check.zk new file mode 100644 index 000000000..447fe0297 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/bool_check.zk @@ -0,0 +1,12 @@ +k = 11; +field = "pallas"; + +constant "BoolCheck" {} + +witness "BoolCheck" { + Base a, +} + +circuit "BoolCheck" { + bool_check(a); +} diff --git a/script/research/zkvm-metering/generator/proof/cond_select.zk b/script/research/zkvm-metering/generator/proof/cond_select.zk new file mode 100644 index 000000000..e563699c1 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/cond_select.zk @@ -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); +} diff --git a/script/research/zkvm-metering/generator/proof/constrain_equal_base.zk b/script/research/zkvm-metering/generator/proof/constrain_equal_base.zk new file mode 100644 index 000000000..bbf94b996 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/constrain_equal_base.zk @@ -0,0 +1,13 @@ +k = 11; +field = "pallas"; + +constant "ConstrainEqualBase" {} + +witness "ConstrainEqualBase" { + Base a, + Base b, +} + +circuit "ConstrainEqualBase" { + constrain_equal_base(a, b); +} diff --git a/script/research/zkvm-metering/generator/proof/constrain_equal_point.zk b/script/research/zkvm-metering/generator/proof/constrain_equal_point.zk new file mode 100644 index 000000000..575770c8a --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/constrain_equal_point.zk @@ -0,0 +1,13 @@ +k = 11; +field = "pallas"; + +constant "ConstrainEqualPoint" {} + +witness "ConstrainEqualPoint" { + EcPoint a, + EcPoint b, +} + +circuit "ConstrainEqualPoint" { + constrain_equal_point(a, b); +} diff --git a/script/research/zkvm-metering/generator/proof/constrain_instance.zk b/script/research/zkvm-metering/generator/proof/constrain_instance.zk new file mode 100644 index 000000000..9de58aeae --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/constrain_instance.zk @@ -0,0 +1,12 @@ +k = 11; +field = "pallas"; + +constant "ConstrainInstance" {} + +witness "ConstrainInstance" { + Base a, +} + +circuit "ConstrainInstance" { + constrain_instance(a); +} diff --git a/script/research/zkvm-metering/generator/proof/debug.zk b/script/research/zkvm-metering/generator/proof/debug.zk new file mode 100644 index 000000000..3fe89f8e0 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/debug.zk @@ -0,0 +1,12 @@ +k = 11; +field = "pallas"; + +constant "Debug" {} + +witness "Debug" { + Base a, +} + +circuit "Debug" { + debug(a); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_add.zk b/script/research/zkvm-metering/generator/proof/ec_add.zk new file mode 100644 index 000000000..b7048adce --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_add.zk @@ -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)); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_get_x.zk b/script/research/zkvm-metering/generator/proof/ec_get_x.zk new file mode 100644 index 000000000..7d95e6314 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_get_x.zk @@ -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); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_get_y.zk b/script/research/zkvm-metering/generator/proof/ec_get_y.zk new file mode 100644 index 000000000..840da3731 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_get_y.zk @@ -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); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_mul.zk b/script/research/zkvm-metering/generator/proof/ec_mul.zk new file mode 100644 index 000000000..ab4819f29 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_mul.zk @@ -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)); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_mul_base.zk b/script/research/zkvm-metering/generator/proof/ec_mul_base.zk new file mode 100644 index 000000000..7f8123f7d --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_mul_base.zk @@ -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)); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_mul_short.zk b/script/research/zkvm-metering/generator/proof/ec_mul_short.zk new file mode 100644 index 000000000..e911559c4 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_mul_short.zk @@ -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)); +} diff --git a/script/research/zkvm-metering/generator/proof/ec_mul_var_base.zk b/script/research/zkvm-metering/generator/proof/ec_mul_var_base.zk new file mode 100644 index 000000000..69b5d9fd1 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/ec_mul_var_base.zk @@ -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)); +} diff --git a/script/research/zkvm-metering/generator/proof/less_than_loose.zk b/script/research/zkvm-metering/generator/proof/less_than_loose.zk new file mode 100644 index 000000000..7afc125ee --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/less_than_loose.zk @@ -0,0 +1,13 @@ +k = 11; +field = "pallas"; + +constant "LessThanLoose" {} + +witness "LessThanLoose" { + Base a, + Base b, +} + +circuit "LessThanLoose" { + less_than_loose(a, b); +} diff --git a/script/research/zkvm-metering/generator/proof/less_than_strict.zk b/script/research/zkvm-metering/generator/proof/less_than_strict.zk new file mode 100644 index 000000000..e48d8f7c7 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/less_than_strict.zk @@ -0,0 +1,13 @@ +k = 11; +field = "pallas"; + +constant "LessThanStrict" {} + +witness "LessThanStrict" { + Base a, + Base b, +} + +circuit "LessThanStrict" { + less_than_strict(a, b); +} diff --git a/script/research/zkvm-metering/generator/proof/merkle_root.zk b/script/research/zkvm-metering/generator/proof/merkle_root.zk new file mode 100644 index 000000000..50c1fc2d4 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/merkle_root.zk @@ -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) +} + diff --git a/script/research/zkvm-metering/generator/proof/poseidon_hash.zk b/script/research/zkvm-metering/generator/proof/poseidon_hash.zk new file mode 100644 index 000000000..a318bd2dd --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/poseidon_hash.zk @@ -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); +} diff --git a/script/research/zkvm-metering/generator/proof/range_check.zk b/script/research/zkvm-metering/generator/proof/range_check.zk new file mode 100644 index 000000000..f026bcd0e --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/range_check.zk @@ -0,0 +1,12 @@ +k = 11; +field = "pallas"; + +constant "RangeCheck" {} + +witness "RangeCheck" { + Base a, +} + +circuit "RangeCheck" { + range_check(64, a); +} diff --git a/script/research/zkvm-metering/generator/proof/sparse_merkle_root.zk b/script/research/zkvm-metering/generator/proof/sparse_merkle_root.zk new file mode 100644 index 000000000..e3a43a65d --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/sparse_merkle_root.zk @@ -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) +} + diff --git a/script/research/zkvm-metering/generator/proof/witness_base.zk b/script/research/zkvm-metering/generator/proof/witness_base.zk new file mode 100644 index 000000000..fe8d2e131 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/witness_base.zk @@ -0,0 +1,12 @@ +k = 11; +field = "pallas"; + +constant "WitnessBase" {} + +witness "WitnessBase" { +} + +circuit "WitnessBase" { + two = witness_base(2); + #constrain_instance(two); +} diff --git a/script/research/zkvm-metering/generator/proof/zero_cond.zk b/script/research/zkvm-metering/generator/proof/zero_cond.zk new file mode 100644 index 000000000..f2fe03418 --- /dev/null +++ b/script/research/zkvm-metering/generator/proof/zero_cond.zk @@ -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); +} diff --git a/script/research/zkvm-metering/generator/src/main.rs b/script/research/zkvm-metering/generator/src/main.rs new file mode 100644 index 000000000..383bb755b --- /dev/null +++ b/script/research/zkvm-metering/generator/src/main.rs @@ -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 . + */ + +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, Vec) { + 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"), + } +} diff --git a/script/research/zkvm-metering/verifier/.gitignore b/script/research/zkvm-metering/verifier/.gitignore new file mode 100644 index 000000000..5470a3d75 --- /dev/null +++ b/script/research/zkvm-metering/verifier/.gitignore @@ -0,0 +1,5 @@ +/target +Cargo.lock +rustfmt.toml +verifier +*.zst diff --git a/script/research/zkvm-metering/verifier/Cargo.toml b/script/research/zkvm-metering/verifier/Cargo.toml new file mode 100644 index 000000000..9ea3d0bca --- /dev/null +++ b/script/research/zkvm-metering/verifier/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "verifier" +version = "0.1.0" +description = "CLI-utility to verify zk proofs for analysis" +authors = ["Dyne.org foundation "] +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 \ No newline at end of file diff --git a/script/research/zkvm-metering/verifier/Makefile b/script/research/zkvm-metering/verifier/Makefile new file mode 100644 index 000000000..521fff826 --- /dev/null +++ b/script/research/zkvm-metering/verifier/Makefile @@ -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 diff --git a/script/research/zkvm-metering/verifier/run_heaptrack.sh b/script/research/zkvm-metering/verifier/run_heaptrack.sh new file mode 100755 index 000000000..a7bc2903a --- /dev/null +++ b/script/research/zkvm-metering/verifier/run_heaptrack.sh @@ -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 diff --git a/script/research/zkvm-metering/verifier/src/main.rs b/script/research/zkvm-metering/verifier/src/main.rs new file mode 100644 index 000000000..e537bacd4 --- /dev/null +++ b/script/research/zkvm-metering/verifier/src/main.rs @@ -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 . + */ + +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 = 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::>, ZkCircuit>(&mut vk_buf, circuit)?; + //let vk = VerifyingKey::build(zkbin.k, &circuit); + + let public_inputs: Vec = deserialize(&public_inputs_bin)?; + + Ok(proof.verify(&vk, &public_inputs)?) +}