Skip to content

chore: support the new wormhole aggregation pipeline#71

Merged
illuzen merged 30 commits intomainfrom
chore/adopts-refactored-aggr
Apr 2, 2026
Merged

chore: support the new wormhole aggregation pipeline#71
illuzen merged 30 commits intomainfrom
chore/adopts-refactored-aggr

Conversation

@ethan-crypto
Copy link
Copy Markdown
Contributor

@ethan-crypto ethan-crypto commented Mar 10, 2026

Supports the new aggregation pipeline defined here. Once that one is approved we can release it and update the wormhole crates here.

@ethan-crypto ethan-crypto requested review from illuzen and n13 March 10, 2026 09:20
@n13
Copy link
Copy Markdown
Contributor

n13 commented Apr 1, 2026

I now have a thorough understanding of the PR. Here is my comprehensive review.


PR #71 Review: Support the new wormhole aggregation pipeline

Summary: This PR upgrades the wormhole ZK circuit crates from v1.0.7 to v1.2.1, adopts the new aggregation pipeline from qp-zk-circuits#96, restructures storage proof keys, adds a build.rs for auto-generating circuit binaries, extracts a reusable wormhole_lib.rs module, and adds a fuzz testing CLI command. 25 commits, +1,810/-771 across 23 files.


1. Dependency Concerns (High Priority)

All wormhole crates are patched to git branches, not released versions:

[patch.crates-io]
qp-zk-circuits-common = { git = "...", branch = "illuzen/back-to-compact" }
qp-wormhole-circuit = { git = "...", branch = "illuzen/back-to-compact" }
# ...7 more patches

And qp-dilithium-crypto is also from a git branch:

qp-dilithium-crypto = { git = "https://github.com/Quantus-Network/chain.git", branch = "illuzen/split-hashers" }

The PR description acknowledges this ("Once that one is approved we can release it and update the wormhole crates here"), but this PR should not be merged until those branches are released to crates.io. Git branch dependencies are fragile -- force-pushes or branch deletions would break the build.


2. Architecture Changes (Good)

wormhole_lib.rs extraction -- This is a solid refactor. It pulls core cryptographic functions (compute_leaf_hash, compute_storage_key, compute_wormhole_address, generate_proof) into a chain-client-free module that external SDKs can consume. The re-exports in lib.rs make the public API clean.

build.rs for circuit generation -- Moving from a manual quantus developer build-circuits command to automatic build.rs generation is a big UX improvement. The profile.dev.build-override with opt-level = 3 is critical and well-documented (prevents 10-minute builds).

Aggregation API migration -- WormholeProofAggregator to Layer0Aggregator is a cleaner API:

  • aggregator.aggregate() returns ProofWithPublicInputs directly (no wrapper)
  • aggregator.verify(proof) replaces result.circuit_data.verify(result.proof)
  • aggregator.batch_size() instead of exposed config fields

3. DRY Violations (Medium Priority)

The following types and constants are defined identically in 3 places:

Definition wormhole_lib.rs wormhole.rs tests/wormhole_integration.rs
TransferProofKey defined re-exported re-defined
TransferProofData defined re-exported re-defined
NATIVE_ASSET_ID defined re-exported re-defined
SCALE_DOWN_FACTOR defined re-exported re-defined
VOLUME_FEE_BPS defined re-exported re-defined
compute_output_amount defined re-exported re-defined

The integration test file should import from quantus_cli instead of redefining everything. It already imports quantus_cli::chain::* and quantus_cli::wallet::*, so it could import the wormhole types too:

use quantus_cli::{
    TransferProofKey, TransferProofData, NATIVE_ASSET_ID,
    SCALE_DOWN_FACTOR, VOLUME_FEE_BPS, compute_output_amount,
};

Additionally, the storage key computation is duplicated between wormhole_lib::compute_storage_key and the run_fuzz_test function (lines ~2592-2600 of the diff). The fuzz test should call wormhole_lib::compute_storage_key.


4. Storage Proof Key Restructure (Important Change)

The TransferProofKey changed from (u32, u64, AccountId32, AccountId32, u128) to (AccountId32, u64) -- just (to, transfer_count). The full transfer data is now stored as a Poseidon2 hash in the value ([u8; 32]) instead of being part of the key.

This is a cleaner design:

  • The key is simpler (find by recipient + counter)
  • The leaf_inputs_hash in the value provides full 256-bit security
  • The ZK circuit verifies the hash matches all transfer details

The auto-generated transfer_proof(_0) method in quantus_subxt.rs now takes Param0 = (AccountId32, u64) but passes () as the storage key (never actually used). The code instead constructs keys manually via compute_storage_key. This works but the unused parameter in the auto-generated code is a bit confusing.


5. Code Quality Issues

try_generate_fuzz_proof has 14 parameters with #[allow(clippy::too_many_arguments)]. This should take a struct:

struct FuzzProofParams<'a> {
    bins_dir: &'a Path,
    read_proof: &'a ReadProof<sp_core::H256>,
    state_root_hex: &'a str,
    // ...
}

Fuzz test is ~840 lines embedded in wormhole.rs, growing the file from 3100 to 3943 lines. The generate_fuzz_cases function alone is ~400 lines. This should either live in a separate src/cli/wormhole_fuzz.rs module or at minimum the fuzz case generation should be in a separate file.

Custom PRNG (SeededRng) uses a simple xorshift64 instead of rand::SeedableRng. While fine for fuzz case generation, using rand_chacha::ChaCha8Rng::seed_from_u64(seed) would be more standard and is already a dependency.

[u8; 110] digest hardcoding in the fuzz test -- if the chain's digest format changes, this will silently truncate. The wormhole_lib.rs handles this more gracefully with dynamic padding.


6. build.rs Concerns

  • Only cargo:rerun-if-changed=build.rs -- If the qp-wormhole-circuit-builder crate changes, the build script won't re-run automatically. Should add:

    println!("cargo:rerun-if-env-changed=QP_NUM_LEAF_PROOFS");
  • Always generates prover bins (include_prover = true) -- For users who only need verification, this adds significant build time. Consider using a cargo feature flag.

  • Default num_leaf_proofs = 16 -- This is only configurable via env var QP_NUM_LEAF_PROOFS. There's no corresponding documentation in README about how to customize this at build time.


7. Test Coverage Assessment

Area Unit Tests Integration Tests Notes
compute_leaf_hash NONE Covered indirectly Should have a unit test with a known vector
compute_wormhole_address NONE Covered indirectly Should have a unit test
compute_nullifier NONE Covered indirectly Marked #[allow(dead_code)]
compute_storage_key length check only Covered Should verify actual bytes against known value
quantize_amount YES (2 cases) -- Good
compute_output_amount YES (2 cases) -- Good
generate_proof NONE Covered (requires chain) Needs prover bins -- hard to unit test
CircuitBinsConfig deser YES (updated) -- Good
DilithiumPair API YES (existing) -- Tests updated for .public() / .secret_bytes()
Layer0Aggregator NONE Covered (requires chain) --
Fuzz test Interactive CLI -- Not automated; requires running chain
build.rs NONE -- Hard to test; verified by successful build

Key gap: compute_leaf_hash and compute_wormhole_address are critical security functions with no unit tests. These should have deterministic test vectors to catch regressions (e.g., if the upstream Poseidon2 hash changes).

The fuzz test is a CLI command, not an automated test. It requires a running chain node and a funded wallet. Consider adding at least a unit-level fuzz test that validates the generate_fuzz_cases function itself (correct expected-pass/fail flags) and a circuit-level test that doesn't require a chain.

The 22 unit tests in wormhole.rs are preserved (same count) and updated for the new APIs. The 2 integration tests in tests/wormhole_integration.rs are updated. The 3 new tests in wormhole_lib.rs are minimal.


8. Minor Issues

  • Treasury Permill display: portion as f64 / 10000.0 -- Permill is parts-per-million (0..1,000,000), so dividing by 10,000 gives a percentage. This is correct.

  • RemoveBuildCircuits from DeveloperCommands -- The BuildCircuits CLI subcommand was removed since build.rs handles it. But the README still references quantus developer build-circuits workflow. The README changes describe it differently but the command no longer exists.

  • No CI checks reported on the branch -- the PR has 0 CI runs and 0 comments. This should be investigated.


Summary Recommendations

  1. Block merge until upstream branches (illuzen/back-to-compact, illuzen/split-hashers) are released and [patch.crates-io] is removed
  2. Fix DRY violations -- integration tests should import from the library instead of redefining types/constants
  3. Add unit tests for compute_leaf_hash and compute_wormhole_address with known test vectors
  4. Refactor the fuzz test -- extract to its own file, use a struct for try_generate_fuzz_proof params, use ChaCha8Rng
  5. Add cargo:rerun-if-env-changed to build.rs
  6. Run CI before merging

@illuzen illuzen merged commit 75a0dea into main Apr 2, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants