Skip to content

Feat/sealed paths and key rules - #161

Merged
nol4lej merged 6 commits into
mainfrom
feat/sealed-paths-and-key-rules
Oct 9, 2026
Merged

nol4lej merged 6 commits into
mainfrom
feat/sealed-paths-and-key-rules

Conversation

@nol4lej

@nol4lej nol4lej commented Oct 9, 2026

Copy link
Copy Markdown
Member

feat: cheaper sealed-tree paths, bounded Merkle proof RPC, and no base-layout spend keys

Follow-up to #160 (spec 18 / tx 6). It fixes two weaknesses in the spend pipeline:

  • a Merkle path in a sealed tree was expensive enough to load a node through public RPC;
  • a spend key that binds no memo could still verify spends.

Both changes land in spec 18, which is not deployed yet.

Sealed-tree paths and the proof RPC

Change Effect
SealedTreePrunedBelowLevel 10 → 6 A sealed path reads 62 leaves instead of 1_022. Measured on a sealed 2^20 tree: ~30 ms → ~2.5 ms (state_call and privacy_getMerkleProof, p50). Costs 32_766 stored nodes per sealed tree instead of 2_046.
Fallback in get_merkle_path A sealed-tree node missing at or above the cut (pruned under an earlier, higher cut) is rebuilt from the leaves, so lowering the cut is safe even after trees were pruned.
privacy_getMerkleProof* (client/rpc-v2) Run on blocking threads, at most 4 at once (ProofGate). The excess is refused as busy (-32009) instead of stalling every other RPC.

pallet-shielded-pool 0.23.0.

No base-layout spend keys, anywhere

A base-layout key (transfer/unshield v1 arity) binds neither the memos nor the full recipient. A copier could swap a spend's memos or alias its recipient.

Path Before After
register_verification_key / batch allowed as v1 refused at every version
set_active_version not checked refused
unretire_version not checked refused: a key retired as unsafe cannot come back
Verification (verify_statement, verify_raw) layout taken from the stored key's arity a base spend key verifies nothing, however it reached storage
Genesis allowed as v1 the chain does not start
  • Exploit reproduced live on the previous binary: with a base key in storage and not retired, a real v1 proof with someone else's memos was accepted and spent the note. With this branch it is refused (ProofVerificationFailed) and the note stays unspent.
  • Testnet is not exposed today: its base v1 keys are retired. The fix makes this independent of retirement state.
  • Encoding: the base encoding of a spend is gone from encoding.rs. encode_transfer and encode_unshield return None for it.
  • Benchmark fix: the verify_proof benchmark seeded its key under TRANSFER. With the new rule, most n skipped the pairing, so regenerated weights would have come out ~40 % low. It now uses an id outside the known table. The committed weights predate this and are unaffected.
  • Dev script: setup-dev.sh example uses version 3.

pallet-zk-verifier 0.16.0, breaking (!).

Docs and comments

  • Comments in shielded-pool/src/merkle and zk-verifier restructured: numbered steps for multi-step functions, one /// per config type, event and field.
  • No behaviour change.
  • RUNTIME_VERSIONS.md §4 updated.

Upgrade notes

Testing

Unit:

  • cargo test for pallet-shielded-pool, pallet-zk-verifier, client/rpc-v2 and orbinum-runtime, also with runtime-benchmarks,skip-proof-verification (the CI test-release set).
  • clippy -D warnings, fmt and taplo clean.
  • New tests:
    • a tree pruned under a higher cut still serves valid paths;
    • ProofGate limits and releases permits;
    • register, batch, activate, unretire, verification and genesis refuse base spend keys.

Live dev node, real proofs:

Script Result
ts-tests/e2e-cross-tree-live.cjs 42/42
ts-tests/e2e-spend-hardening-live.cjs 78/78
ts-tests/e2e-prune-cut-upgrade-live.cjs: cut 10 → 6 via setCodeWithoutChecks on pruned 128-leaf trees 22/22: every path verifies, a note from a tree pruned under the old cut spends
Same script with KEYS_ONLY=1: key rules and the base-key exploit 12/12 (6/12 on the previous binary)
Sealed 2^20 tree, path latency cut 10 vs cut 6, 200/200 paths verified against the sealed root

@nol4lej
nol4lej merged commit 773aca1 into main Oct 9, 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.

1 participant