For the complete documentation index, see llms.txt. This page is also available as Markdown.

89984 bc high missing zk tree registration for miner volume fees permanently freezes credited funds

Submitted on Aug 22nd 2026 at 10:03:42 UTC by @Kalyantr for Audit Comp | Quantus

  • Report ID: #89984

  • Report Type: Blockchain/DLT

  • Report severity: High

  • Target: https://github.com/immunefi-team/audit-comp-quantus-chain

  • Impacts:

    • Permanent freezing of funds

Description

Brief/Intro

Wormhole::process_exit_bundle credits a successful settlement’s miner volume fee to the current QPoW author’s Wormhole-derived address using Unbalanced::increase_balance. Unlike a successful user exit, however, this fee-credit path does not call Wormhole::record_transfer.

record_transfer is the path that increments the recipient’s TransferCount and inserts the corresponding leaf into the ZK tree. Consequently, the miner receives a balance credit but no ZK-tree commitment for that credit. The miner cannot construct the Merkle witness required for a later Wormhole withdrawal, permanently freezing the credited miner fee through all currently available permissionless paths.

Vulnerability Details

A Wormhole address is controlled for withdrawal by knowledge of its Wormhole secret, not by a normal Dilithium account key. To withdraw a balance from such an address, the holder must produce a valid leaf proof proving that a committed ZK-tree leaf contains:

  • The Wormhole recipient address

  • Its per-recipient TransferCount

  • The credited amount

  • A valid Merkle path to a committed ZK-tree root

The normal exit-credit path correctly creates this required state:

record_transfer increments TransferCount and calls T::ZkTree::record_transfer, thereby committing the credit as a ZK-tree leaf. A future Wormhole withdrawal additionally requires knowledge of the Wormhole secret corresponding to the credited recipient.

The miner-fee path only credits the balance and emits an informational event:

There is no corresponding call to:

Therefore, a successful miner-fee payment has this state transition:

The current runtime exposes no permissionless public call that can register a ZK-tree leaf retroactively for an existing credited balance. Sending a later normal transfer to the same Wormhole address creates a leaf only for that later transfer; it cannot create a leaf for the already credited miner fee.

A governance-led runtime migration could potentially repair affected historical credits, but miners have no current permissionless recovery route.

Impact Details

A valid Wormhole settlement with a nonzero fee permanently freezes the miner’s volume-fee credit.

Any holder of a valid Wormhole settlement proof can submit the unsigned settlement transaction with a nonzero fee. When it is included, the current QPoW block author receives a miner-fee credit that is not registered in the ZK tree. No privileged access, invalid proof, or malicious miner action is required.

The affected funds are miner-fee credits, not user deposit principal. They are correctly credited to the intended derived Wormhole address but cannot be withdrawn because the protocol does not commit the required leaf.

This matches the in-scope impact:

Permanent freezing of funds.

Distinction from PR #619

Commit 60ec991 added MinerVolumeFeePaid after the miner-fee balance credit to make the payment visible to indexers. It did not call record_transfer or otherwise register the credit in the ZK tree.

This report concerns the distinct security consequence: without the ZK-tree leaf and TransferCount update, the credited miner fee has no withdrawal witness and is permanently unexitable through permissionless protocol paths.

Record a Wormhole transfer immediately after each successful fee credit. This must happen before moving author into MinerVolumeFeePaid.

Apply the same pattern to the aggregator-rebate branch, but only after its increase_balance(&aggregator_account, aggregator_fee, ...) succeeds.

Add regression tests asserting that:

  • A successful miner-fee credit increases ZkTree::leaf_count() by one additional leaf.

  • Wormhole::transfer_count(&miner) increases by one.

  • The newly recorded leaf belongs to the miner and has amount == miner_fee.

  • The equivalent assertions hold for a successful aggregator rebate to a Wormhole address.

Finally, rebenchmark/update the affected settlement weights, because private settlement can now insert one additional leaf for the miner fee and public settlement can insert up to two additional leaves (miner fee plus aggregator rebate).

This forward fix does not recover already unrecorded historical credits. Those require a separate governance-led migration or compensation procedure based on reconstructable historical fee payments.

Proof of Concept

Add this test inside the existing private_batch_proof_tests module in pallets/wormhole/src/tests.rs.

Run this command:

The test passes with the current code and produces:

Was this helpful?