Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,298 changes: 517 additions & 781 deletions Cargo.lock

Large diffs are not rendered by default.

31 changes: 16 additions & 15 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ argon2 = "0.5" # Password-based key derivation (quantum-safe)
rand = "0.9"

# Quantus crypto dependencies (aligned with chain)
qp-dilithium-crypto = { version = "0.3.1", features = ["serde"] }
qp-rusty-crystals-dilithium = { version = "2.4.0" }
qp-rusty-crystals-hdwallet = { version = "2.4.0" }
qp-dilithium-crypto = { version = "0.5.0", features = ["serde"] }
qp-rusty-crystals-dilithium = { version = "3.0.1" }
qp-rusty-crystals-hdwallet = { version = "3.0.1" }

# HTTP client for Subsquid queries
blake3 = "1.8"
Expand Down Expand Up @@ -80,7 +80,7 @@ quinn-proto = "0.11.14"
# Force patched version of rustls-webpki (RUSTSEC-2026-0098, RUSTSEC-2026-0099, RUSTSEC-2026-0104)
rustls-webpki = "0.103.13"

# Blockchain deps: align with chain workspace; use chain primitives for qp-dilithium-crypto so sp-* versions match
# Blockchain deps: align with chain workspace
codec = { package = "parity-scale-codec", version = "3.7", features = ["derive"] }
jsonrpsee = { version = "0.24", features = ["client"] }
sp-core = { version = "39.0.0" }
Expand All @@ -93,23 +93,24 @@ subxt-metadata = "0.44"
# ZK proof generation (aligned with chain)
anyhow = "1.0"

qp-plonky2 = { version = "1.5.1", default-features = false, features = ["rand", "std"] }
qp-wormhole-aggregator = { version = "3.0.0", default-features = false, features = ["rayon", "std"] }
qp-wormhole-circuit = { version = "3.0.0", default-features = false, features = ["std"] }
qp-wormhole-circuit-builder = { version = "3.0.0" }
qp-wormhole-inputs = { version = "3.0.0", default-features = false, features = ["std"] }
qp-wormhole-prover = { version = "3.0.0", default-features = false, features = ["std"] }
qp-wormhole-verifier = { version = "3.0.0", default-features = false, features = ["std"] }
qp-zk-circuits-common = { version = "3.0.0", default-features = false, features = ["std"] }
qp-plonky2 = { version = "1.5.4", default-features = false, features = ["rand", "std"] }
qp-plonky2-verifier = { version = "1.5.4", default-features = false }
qp-wormhole-aggregator = { version = "3.1.0", default-features = false, features = ["rayon", "std"] }
qp-wormhole-circuit = { version = "3.1.0", default-features = false, features = ["std"] }
qp-wormhole-circuit-builder = { version = "3.1.0" }
qp-wormhole-inputs = { version = "3.1.0", default-features = false, features = ["std"] }
qp-wormhole-prover = { version = "3.1.0", default-features = false, features = ["std"] }
qp-wormhole-verifier = { version = "3.1.0", default-features = false, features = ["std"] }
qp-zk-circuits-common = { version = "3.1.0", default-features = false, features = ["std"] }


[build-dependencies]
hex = "0.4"
qp-poseidon-core = "2.1.0"
qp-wormhole-circuit-builder = { version = "3.0.0" }
qp-poseidon-core = "3.0.2"
qp-wormhole-circuit-builder = { version = "3.1.0" }

[dev-dependencies]
qp-poseidon-core = "2.1.0"
qp-poseidon-core = "3.0.2"
serial_test = "3.1"
tempfile = "3.8.1"

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,8 @@ quantus developer build-circuits \

**What it does (3 steps):**
1. Clears stale artifacts from the CLI's `generated-bins/` directory.
2. Calls the `qp-wormhole-circuit-builder` library directly to regenerate binary files in `generated-bins/` (`verifier.bin`, `common.bin`, `aggregated_verifier.bin`, `aggregated_common.bin`, `config.json`, plus prover binaries unless `--skip-prover` is set).
3. Copies chain-relevant binaries (`aggregated_common.bin`, `aggregated_verifier.bin`, `config.json`) to `chain/pallets/wormhole/` and touches the pallet source.
2. Calls the `qp-wormhole-circuit-builder` library directly to regenerate binary files in `generated-bins/` (`verifier.bin`, `common.bin`, `private_batch_verifier.bin`, `private_batch_common.bin`, `config.json`, plus prover binaries unless `--skip-prover` is set).
3. Copies chain-relevant binaries (`private_batch_common.bin`, `private_batch_verifier.bin`, `config.json`) to `chain/pallets/wormhole/` and touches the pallet source.

After running, rebuild the chain (`cargo build --release` in the chain directory) so `include_bytes!()` picks up the new binaries.

Expand Down
29 changes: 19 additions & 10 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
//! Build script for quantus-cli.
//!
//! Generates circuit binaries (prover, verifier, aggregator) at build time.
//! This ensures the binaries are always consistent with the circuit crate version
//! and eliminates the need to manually run `quantus developer build-circuits`.
//! Generates circuit binaries (leaf verifier/common + private/public-batch
//! prover/verifier artifacts) at build time. The leaf circuit has no
//! `prover.bin` — proving uses an in-process fresh build. This keeps the
//! binaries consistent with the circuit crate version and eliminates the need
//! to manually run `quantus developer build-circuits`.
//!
//! Outputs are written to `OUT_DIR` (required by cargo) and, during local source
//! builds only, linked/copied to `generated-bins/` in the project root. When the
Expand Down Expand Up @@ -56,6 +58,10 @@ fn main() {
.map(|v| v.parse().expect("QP_NUM_LEAF_PROOFS must be a valid usize"))
.unwrap_or(DEFAULT_NUM_LEAF_PROOFS);

let num_private_batch_proofs: usize = env::var("QP_NUM_PRIVATE_BATCH_PROOFS")
.map(|v| v.parse().expect("QP_NUM_PRIVATE_BATCH_PROOFS must be a valid usize"))
.unwrap_or(DEFAULT_NUM_PRIVATE_BATCH_PROOFS);

// Re-run when QP_NUM_LEAF_PROOFS env var changes. Note: emitting any `rerun-if-*`
// directive opts out of Cargo's default "re-run when any package file changes"
// behavior. However, the important cases still work:
Expand All @@ -66,10 +72,11 @@ fn main() {
// For installed binaries, runtime detection in bins.rs `is_ready()` handles leaf
// count mismatches by regenerating on first use.
println!("cargo:rerun-if-env-changed=QP_NUM_LEAF_PROOFS");
println!("cargo:rerun-if-env-changed=QP_NUM_PRIVATE_BATCH_PROOFS");

println!(
"cargo:warning=[quantus-cli] Generating ZK circuit binaries (num_leaf_proofs={})...",
num_leaf_proofs
"cargo:warning=[quantus-cli] Generating ZK circuit binaries (num_leaf_proofs={}, num_private_batch_proofs={})...",
num_leaf_proofs, num_private_batch_proofs
);

let start = Instant::now();
Expand All @@ -81,7 +88,7 @@ fn main() {
&build_output_dir,
true,
num_leaf_proofs,
None,
Some(num_private_batch_proofs),
)
.expect("Failed to generate circuit binaries");

Expand All @@ -98,11 +105,13 @@ fn main() {
// Print hashes of generated binaries
print_bin_hash(&build_output_dir, "common.bin");
print_bin_hash(&build_output_dir, "verifier.bin");
print_bin_hash(&build_output_dir, "prover.bin");
print_bin_hash(&build_output_dir, "dummy_proof.bin");
print_bin_hash(&build_output_dir, "aggregated_common.bin");
print_bin_hash(&build_output_dir, "aggregated_verifier.bin");
print_bin_hash(&build_output_dir, "aggregated_prover.bin");
print_bin_hash(&build_output_dir, "private_batch_common.bin");
print_bin_hash(&build_output_dir, "private_batch_verifier.bin");
print_bin_hash(&build_output_dir, "private_batch_prover.bin");
print_bin_hash(&build_output_dir, "public_batch_common.bin");
print_bin_hash(&build_output_dir, "public_batch_verifier.bin");
print_bin_hash(&build_output_dir, "public_batch_prover.bin");

// Copy bins to project root for runtime access, but only during local source
// builds — never during `cargo publish` verification (manifest_dir is inside
Expand Down
10 changes: 5 additions & 5 deletions examples/wormhole_sdk_e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
//! 6. compute_merkle_positions -> (siblings, positions)
//! 7. wormhole_lib::generate_proof -> leaf proof bytes
//! 8. aggregate_proofs([leaf]) -> agg.hex
//! 9. verify_aggregated_and_get_events -> minted NativeTransferred
//! 9. verify_private_batch_and_get_events -> minted NativeTransferred
//! ```
//!
//! All helpers are the same ones consumed by `stress-test`. The example is
Expand Down Expand Up @@ -54,7 +54,7 @@ use quantus_cli::{
cli::{address_format::bytes_to_quantus_ss58, common::ExecutionMode, send::parse_amount},
compute_merkle_positions, compute_wormhole_address, decode_full_leaf_data,
error::{QuantusError, Result},
get_zk_merkle_proof, parse_transfer_events, transfer, verify_aggregated_and_get_events,
get_zk_merkle_proof, parse_transfer_events, transfer, verify_private_batch_and_get_events,
wallet::WalletManager,
wormhole_lib::{
self, ProofGenerationInput, NATIVE_ASSET_ID, SCALE_DOWN_FACTOR, VOLUME_FEE_BPS,
Expand Down Expand Up @@ -307,18 +307,18 @@ async fn main() -> Result<()> {

// 9. verify + submit -------------------------------------------------------
println!();
println!("[8/9] verify_aggregated_and_get_events (off-chain verify + on-chain submit)");
println!("[8/9] verify_private_batch_and_get_events (off-chain verify + on-chain submit)");
let verify_start = std::time::Instant::now();
let (mint_block, mint_tx, transfers) =
verify_aggregated_and_get_events(agg_path.to_str().unwrap(), &client).await?;
verify_private_batch_and_get_events(agg_path.to_str().unwrap(), &client).await?;
println!(" verified+included in {:.2}s", verify_start.elapsed().as_secs_f64());
println!(" mint block : {:?}", mint_block);
println!(" mint tx : {:?}", mint_tx);

println!();
println!("[9/9] minted NativeTransferred events:");
if transfers.is_empty() {
println!(" (none — verify_aggregated_proof did not emit any events?)");
println!(" (none — verify_private_batch did not emit any events?)");
}
for (i, ev) in transfers.iter().enumerate() {
let to_ss58 = bytes_to_quantus_ss58(&ev.to.0);
Expand Down
14 changes: 7 additions & 7 deletions examples/wormhole_sdk_usage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
//! submitting anything: `at_best_block`, scans recent blocks for a real `NativeTransferred`
//! event, runs `parse_transfer_events`, fetches the ZK Merkle proof via `get_zk_merkle_proof`,
//! computes positions with `compute_merkle_positions` and decodes the leaf bytes with
//! `decode_full_leaf_data`. The submission path (`submit_unsigned_verify_aggregated_proof`,
//! `verify_aggregated_and_get_events`) is shown only as pseudocode — it requires a funded
//! `decode_full_leaf_data`. The submission path (`submit_unsigned_verify_private_batch`,
//! `verify_private_batch_and_get_events`) is shown only as pseudocode — it requires a funded
//! deposit + ZK proof generation, which [`wormhole_sdk_e2e.rs`](./wormhole_sdk_e2e.rs)
//! demonstrates end-to-end. If the node is unreachable the example exits cleanly with hints; CI
//! without a node still builds and runs it green.
Expand All @@ -34,8 +34,8 @@
//! aggregate_proofs(leaf_files, "agg.hex") → aggregated proof file
//! │
//! ▼
//! verify_aggregated_and_get_events("agg.hex", &client)
//! │ (locally verifies + submits unsigned `verify_aggregated_proof`
//! verify_private_batch_and_get_events("agg.hex", &client)
//! │ (locally verifies + submits unsigned `verify_private_batch`
//! │ + waits for inclusion + collects NativeTransferred events)
//! ▼
//! Vec<NativeTransferred> with the minted amounts at the exit accounts
Expand Down Expand Up @@ -219,7 +219,7 @@ async fn online_demo() -> Result<()> {
println!(
" This is normal on a fresh dev chain. Run examples/wormhole_sdk_e2e.rs first"
);
println!(" (it submits a deposit + verify_aggregated_proof) to populate the chain.");
println!(" (it submits a deposit + verify_private_batch) to populate the chain.");
},
}

Expand Down Expand Up @@ -332,13 +332,13 @@ fn print_online_recipe() {
println!(" let bytes = std::fs::read(\"agg.hex\")?;");
println!(" let bytes = hex::decode(bytes.trim_ascii())?;");
println!(" let (included_at, block_hash, tx_hash) =");
println!(" submit_unsigned_verify_aggregated_proof(&client, bytes).await?;");
println!(" submit_unsigned_verify_private_batch(&client, bytes).await?;");
println!(" println!(\"included @ {{}} block={{:?}} tx={{:?}}\",");
println!(" included_at, block_hash, tx_hash);");
println!();
println!(" // Or, with local verify + event collection:");
println!(" let (block_hash, tx_hash, transfers) =");
println!(" verify_aggregated_and_get_events(\"agg.hex\", &client).await?;");
println!(" verify_private_batch_and_get_events(\"agg.hex\", &client).await?;");
println!(" for ev in transfers {{");
println!(" println!(\" -> {{}} planck to {{}}\", ev.amount, ev.to.to_ss58check());");
println!(" }}");
Expand Down
Loading
Loading