Skip to content

fix: show the SubVault configuration before a CREATE_CHILD approval - #164

Merged
graikos merged 9 commits into
mainfrom
fix/create-child-config-display
Oct 9, 2026
Merged

graikos merged 9 commits into
mainfrom
fix/create-child-config-display

Conversation

@graikos

@graikos graikos commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes F-2026-19344.

In short

A CREATE_CHILD proposal commits to the new SubVault's owners and threshold only through a hash in proposal.data. The contract initializes exactly what was approved, but neither approval surface showed approvers what that was: the web page fetched the owners and threshold, checked their hash, then discarded them and let Approve through while the check was still running; the offline bundle never carried them, so the CLI printed Threshold (unknown) and no owner list. A dishonest root owner could propose a SubVault they alone control and have the others approve it blind.

Now both surfaces show the reserved owner list and threshold, verified against proposal.data, and nothing is approved or exported until they are. The offline request bundle becomes version 2 to carry them; the CLI rejects version 1.

Web (ui/app/transactions/[id]/page.tsx)

  • The config check keeps what it verified. The details card shows the SubVault address, SubVault Threshold as "t of n" and the full SubVault Owners list from the reservation whose hash matched the signed data. One loader (lib/child-reservation.ts) serves the page's preflight, its execute path and the offline exporter, and tells four states apart: not indexed yet, invalid (a duplicated or mis-addressed reservation, shown with its reason and blocked, instead of being mistaken for indexer lag), mismatch and match.
  • Approve is enabled only once that check passes. While the reservation is unindexed or still being checked, a disabled "Approve waits for the SubVault config" button and the existing orange banner explain why; a mismatch or an invalid reservation keeps a red block. The placeholder appears only when the configuration is the sole thing holding Approve back. Before, only an explicit mismatch blocked.
  • Offline export of an approval applies the same rule, and buildOfflineApproveBundle refuses to build when the reservation is missing or does not match.

Offline bundle and CLI

  • buildOfflineApproveBundle fetches the child's own createChildConfig and createChildOwner events for a CREATE_CHILD proposal, alongside the account snapshots, checks their hash against proposal.data, refuses a CREATE_CHILD record without a child address, and puts childAddress, childOwners and childThreshold in the bundle. OFFLINE_BUNDLE_VERSION is 2; signed responses stay at 1 (OFFLINE_RESPONSE_VERSION, now beside the request version in contracts/offline-format).
  • OFFLINE_REQUEST_VERSION (2) and STORE_CHECKPOINT_VERSION (1) live in a new pure module, contracts/offline-format, used by the UI's OFFLINE_BUNDLE_VERSION, the CLI's version gate and storesFromOfflineRequest, which used to pin the request version to 1 itself. The checkpoint's own format is unchanged. contracts/src changes are plain TypeScript outside the circuit; the VK jobs run on the PR as always.
  • The CLI rejects any request that is not version 2, then, before the summary and the confirmation prompt, assertCreateChildBundleConfig requires a CREATE_CHILD approve or execute bundle to carry a well-formed owner list and threshold that hash, in reserved slot order, to proposal.data, and a bundled child address equal to the proposal's. Missing, malformed, oversized, reordered or swapped configurations abort with no summary and no signature; a numeric type code gets the same check. The summary then prints the checked owners and threshold; handleApprove and handleExecute repeat the check for direct callers, and the execute path's former inline copy is gone.

Compatibility

Request bundles exported by an older UI are refused with "Unsupported bundle version: 1 (expected 2; export a new request with the current UI)". Pending offline requests must be re-exported; nothing else changes. No contract, proof, schema or signed-response change. A new offline CLI release is needed alongside this UI.

Tests

  • Offline CLI: 42 pass, 10 new or changed. The CLI refuses a version-1 bundle and a CREATE_CHILD approval without its config before rendering a summary; assertCreateChildApprovalConfig accepts the reserved configuration and rejects missing, empty, out-of-range, invalid-address, reordered, truncated and re-thresholded ones; the summary prints the owners and threshold with no (unknown).
  • UI: typecheck and the Next 16 build pass; 41 unit tests pass, 3 new. The approve bundle carries the verified config, refuses a missing or mismatched reservation, and leaves other approvals unchanged.
  • e2e package unit tests: 5 pass; the exporter test now expects the shared request version (CI caught the old 1).
  • test:ui: 40 pass locally before the review fixes, and green on CI at 9567fa2 after them. Two new chainless tests render a CREATE_CHILD approval with a canonical parent and child; with the reservation indexed the owners, threshold and an enabled Approve appear, and with it unindexed Approve is replaced by the disabled waiting button.
  • Offline CLI e2e (offline-cli-e2e.test.ts, SKIP_PROOFS): 14 of 16 passed on the first full run; the two createChild tests failed because the test's hand-built approve bundle predates the config fields. With the bundle carrying childAddress, the owners in reserved slot order and childThreshold, the child-lifecycle block passes (8 of 8). CI runs the full file.
  • Not run: lightnet test:e2e.

Docs

Offline audit guide (CLI flow step 1, bundle format reference for version 2, approve extras, threat model), UI audit guide (reservation check and chainless regression), and every other place that still described version 1 requests: the root, desktop and e2e READMEs, the desktop and security audit guides, and the code comments in the worker, the CLI and ui/lib/offline-format.ts. The signed-response sample in the offline guide said version 2; it is 1.

@graikos
graikos requested a review from mellowcroc October 7, 2026 15:21
@mellowcroc

Copy link
Copy Markdown
Collaborator

Can you ask an agent to create screenshots for the UI changes and link them in the PR?

@graikos

graikos commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator Author

SubVault config does not hash to the signed proposal data: red banner, the owners and threshold that were found, Approve blocked.
createchild-config-mismatch
SubVault reservation not indexed yet: orange banner, owners shown as "Not indexed yet", Approve replaced by a disabled waiting button.
createchild-config-unindexed
Same unindexed state on the Offline tab: the export is refused when the bundle is built, since the owners and threshold are not verified.
createchild-config-unindexed-offline
SubVault reservation indexed and matching the signed data: address, threshold "1 of 2" and the full owner list shown, Approve enabled.
createchild-config-verified
Same verified state on the Offline tab: the approve bundle can be exported and carries the verified owners and threshold.
createchild-config-verified-offline

mellowcroc
mellowcroc previously approved these changes Oct 8, 2026
@graikos
graikos force-pushed the fix/create-child-config-display branch from 27eeb3c to 5c8b5d5 Compare October 9, 2026 09:21
@graikos
graikos requested a review from mellowcroc October 9, 2026 09:23
@graikos
graikos merged commit 0e6bc7d into main Oct 9, 2026
8 of 9 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.

2 participants