ZK review remediation — 10 September 2026
All three findings from the staging review have been addressed and committed/pushed to staging at the user's request. No circuits or cryptographic keys were changed, and no transactions, live configuration changes, or deployments were performed as part of this work.
Verified remote staging heads: privacy-frontend ac2dc2a329c4587d55bf58744f1dd9324a792e85; privacy-protocol 4860c121c9ad089df3ee7e3bd755b838d775ca8d; stealthos-relayer 34d35df705e566886435ef07e2d0ce39cbcbefe6. All three Git working trees were clean after pushing. These review reports live outside the Git repositories and remain local.
ZK-01: durable swap-output recovery
The frontend now saves the output secret in the existing encrypted settlement journal before submission. Journal preparation checks the secret against the exact canonical privateSwap calldata, including its proof-bound output NPK/token, source nullifiers, and change commitment.
Settlement completes the output from the confirmed receipt it already holds. It validates the pool's PrivateSwap event against the recorded intent, finds the unique matching insertion, reads the token domain for that actual tree at the receipt block, and recomputes the commitment. It no longer re-fetches a receipt after finalization or depends on the pre-proof current-tree snapshot.
Crash recovery discovers the transaction by its nullifiers even if the browser closed before saving a transaction hash. It uses the same reconstruction routine, requires a canonical successful transaction/receipt and the confirmation-safe chain view, and retains ambiguous records. The journal cannot finalize a new swap without its recovered output. The recovered note and self-delivery metadata are appended in a guarded, idempotent update.
Submission failures retain the journal reference even if no transaction hash was returned. Replaced transactions must match the exact recorded intent. The UI preserves recovery references, correctly marks spent commitments, and routes both recovered swap output and change through the existing durable delivery path.
Primary files:
- swapOutput.ts (
privacy-frontend/src/joinsplit-v2/swapOutput.ts) - pendingJournal.ts (
privacy-frontend/src/joinsplit-v2/pendingJournal.ts) - journalReconciliation.ts (
privacy-frontend/src/joinsplit-v2/journalReconciliation.ts) - settlement.ts (
privacy-frontend/src/joinsplit-v2/settlement.ts)
ZK-02: Flat V2 relayer compatibility
The relayer now supports all seven Flat V2 selectors, with canonical decoding, batch/arity/range/duplicate checks, source/current-tree rules, token-domain checks, and proof-bound positive fees paid to the actual signer for withdrawal-like actions. Transfer and migration remain sponsored because their ABI has no relayer fee field.
An exact whole-transaction simulation is mandatory, including composed batches; SKIP_PREFLIGHT cannot disable that boundary. Value-bearing relay requests fail closed. Legacy behavior remains separate.
Related integration blockers were corrected: the dedicated pool route accepts the frontend's minimal request envelope, and startup can validate the V2 pool/router instead of requiring legacy getters.
Operator configuration remains necessary: select flat-joinsplit-v2 with the appropriate BASE_*_POOL_PROTOCOL, configure the actual pool/router addresses, and ensure the pool fee recipient matches the signer. Existing mainnet Node-relayer restrictions remain in place. See relayer configuration (stealthos-relayer/README.md).
ZK-03: mandatory canonical production release gate
The canonical production deployment script now invokes the existing complete signed-manifest validator afresh before recording broadcasts. This includes its hash-pinned semantic phase-2 lineage and trust-policy verification. The script checks the target chain and all ten signed deployment addresses/runtime code hashes during local script execution before Forge can broadcast them. Saved valid: true receipts are not accepted as authorization.
This implements the deployment-enforcement alternative from the review. It does not introduce a second phase-2 cryptographic verifier inside the exporter. Exporter/token documentation and generated banners now explicitly describe phase-1/IC binding as preliminary, not production approval. A related export/validation mismatch was fixed by using the same canonical production renderer on both sides of the strict byte-for-byte source check.
Production requires Forge --ffi, cached pinned tooling, and the documented release/trust-policy inputs. The exporter marker alone remains insufficient. See production release prerequisites (privacy-protocol/JOINSPLIT_PRODUCTION_MANIFEST.md) and deployment gate (privacy-protocol/contracts/script/JoinSplitProductionReleaseGate.sol).
Verification
- Frontend: 74 tests passed across 10 JoinSplit V2 suites, including 21 new tests. TypeScript
--noEmitpassed. - Swap regressions cover encrypted crash/restart recovery without a saved transaction hash, missing receipts, rollover, substituted events/keys/commitments, repeated finalization/reconciliation, missing recovery material, storage failure before broadcast, no receipt re-fetch, lost submission responses, and replacement-intent mismatch.
- Relayer: full
npm testpassed, including 13 new V2 regression groups; formatting passed. Locked dependencies were restored without changing the lockfile. - Protocol: 26 deployment tests, 38 release-validator tests, and 3 verifier-rendering tests passed. One real-ceremony opt-in validator test remains ignored.
- Both release CLI binaries passed
cargo check. The ceremony dependency-boundary check and finalization shell-script syntax check passed. git diff --checkpassed in all three repositories.
These are local source and regression-test results, not a claim of production readiness. A genuine production ceremony, configured live relayer, and live-chain deployment were not exercised. Existing production manifests must be rebuilt and approved against the final committed sources and artifacts; these changes do not promote development artifacts. Secrets already lost before this fix cannot be reconstructed merely from public transaction data.