Repository navigation
Conversation
… pool The STRK20 pool writes an address's viewing key once and never replaces it. Ready (and other wallets) derive viewing keys with their own, unpublished schemes, so an account that shielded there is registered under a key Bramble cannot derive. Imported into Bramble, it can never shield or unshield: the proof needs the registered key, and a new one cannot be registered. Every import path now reads `get_public_key(address)` from the configured pool and compares it with the viewing public key Bramble would register (derived in the vault; only the public x leaves it): - seed import (first-run restore, Settings import, find accounts): discovery labels each deployed candidate and the picker lists a blocked one but never selects or submits it - raw-key import and first-run key vault: refused before any write - Ledger: refused before storing; Bramble has never registered a Ledger account, so any registration is another wallet's The check fails closed: an unreadable pool, a missing reader, or a failed derivation blocks the import until a retry succeeds. An account registered under Bramble's own key still imports. readStrk20Registered now wraps a new readStrk20PublicKey so the key value is available. The public-key probe in deriveImportedAccount is extracted as recoverStarkPublicKey for reuse. Discovery tests share one account stub (tests/support/recovery-accounts.ts) and, like the shared RPC stub, answer the pool read offline.
release/v0.10.8 grew sidepanel-recovery-scan-gate.test.ts, and the two privacy-pool picker tests pushed it past the 500-line cap. The picker fixtures move to tests/support/recovery-picker-harness.ts and the privacy-pool cases to their own file.
PR SummaryMedium Risk Overview Recovery discovery and address import only attach an advisory Vault and RPC plumbing: preview APIs for phrase, stored, and imported accounts ( Dapp private requests that would leak registration state are queued as close-only notices ( Reviewed by Cursor Bugbot for commit 9824f05. Bugbot is set up for automated code reviews on this repo. Configure here. |
|
AI-assisted review (Claude, with an independent Codex gpt-6-astra pass), posted from @derek-flossman's account. Findings were checked against the code; treat them as review input, not approval. Medium — test gap on the viewing-key comparison ( Low (documented trade-off): the seed-import refusal is enforced only in the picker; the write step doesn't re-check the pool. Fine if that's the intended trade-off — just confirming it's deliberate. Low (plausible): in |
|
i need to test it more, so draft for now |
adrienlacombe
left a comment
There was a problem hiding this comment.
Reviewing while it's still a draft. The reasoning about the write-once pool key is clear and well sourced, thanks.
Leaks: I didn't find any. Only the viewing public x-coordinate leaves the vault. The previews store nothing, the pool reads are public state, and nothing new is logged. A deployed recovery candidate costs one extra get_public_key read.
Lock-out / false-positive concerns
- The block covers the whole account, not just privacy. A migrating Ready user with public balances can't import the account to move those funds either. Would "import, but disable shield/unshield for this account with an explanation" serve users better than a hard refusal? Either way, I'd get an explicit product call before this leaves draft.
- Fail-closed reaches further than the threat. The Ledger path (all families) and the raw-key/key-vault paths read the pool even for undeployed addresses, which the PR itself says can't be registered. With only the first RPC endpoint used, a flaky primary now blocks adding a brand-new Ledger account or a first-run key vault. Suggestion: treat a not-deployed address as clear, or skip the read for it, and read the pool through
withRpcFallbackover all endpoints (inline notes below). - Ledger is assumed never registered. The vault already has retained Ledger viewing-key slots (
ledger-strk20-typed-data-v1inaccount-secrets.ts). When Ledger provisioning ships, re-adding a Bramble-registered Ledger account will be refused unless this comparison is updated. Please add a test or TODO that ties the two together. - No background re-check on seed import (acknowledged). The picker is the only enforcement. The risk is low since the message is extension-page-only, but I'd prefer the write boundary to re-check before this un-drafts.
Process / CI
- pr-guards is red (blocking): the linked-issue guard needs an issue reference (
Refs #727or a new issue). - fast-checks pass on 0863a4d.
- The branch is behind base.
Conflicts:
- With #733 and #763 in
onboarding-ops.ts,recovery-account-ui.ts,sidepanel-recovery-scan-gate.test.ts(this PR moves its fixtures intorecovery-picker-harness.ts) andrecovery-discovery.test.ts. - With #741 in
runtime-rpc.ts, the line aftergetDeploymentStatus,increateDefaultRuntimeRpc.
It's probably easiest to rebase this after those land.
Merged with today's release/v0.10.8 (#763 added Xverse tests to the same files), recovery-discovery.test.ts reached 502 lines and sidepanel-recovery-scan-gate.test.ts 510, which check:files refuses. No test changes: - The index-zero listing test moves to recovery-discovery-listing.test.ts. - The picker fixtures move to tests/support/recovery-picker-harness.ts, the same file name and export style #755 uses for them. Refs #718 Refs #727 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
AI-assisted (Claude), posted from @derek-flossman's account. Product decision (Derek): don't refuse the whole account. Import it, and if the address is registered with the privacy pool by another wallet, turn shield/unshield off for that account with a short explanation of why. We don't want to lock anyone out of their public funds; the refusal doesn't protect the shielded funds anyway, since Bramble can't reach them either way. For the rebase (things that changed under this PR):
Open points from the earlier reviews that still apply with the new approach:
New user-facing copy for the "privacy off" explanation needs owner approval — please list it in the PR body. @Ryun1 — could you comment on whether this approach works for you, and on point 6? |
|
@Ryun1 — notes for picking this up tomorrow. This follows the product decision above: import the account, and turn privacy off for it, rather than refusing the import. Proposed rework1. Import always works.
2. The check moves to the point of use, in one place.
3. Nothing hidden or stuck.
4. Ledger. No change. 5. Kept from this PR:
Approved copy
Tests the rework needs
StatusA first pass of this is being built locally on top of your two commits, with Still open:
|
…account Merge release/v0.10.8 and move the pool-key check off import. Every import path succeeds without reading the pool. At the point of use, a non-zero pool public key that differs from the key Bramble would register disables shield, unshield, and other private actions. A failed read or derivation is a retry, not a permanent flag. Ledger accounts stay on the existing ledger blocker. Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
The last push was recorded as an edited event, so fast-checks and release-validation were skipped on d02db1a.
|
Point 6 (pool): the STRK20 docs publish exactly one Mainnet privacy pool, Also done: the PR body is rewritten for the point-of-use approach, the three inline threads on code the rework deleted are resolved as superseded, and an empty commit reran the gates (the last push was recorded as |
|
Pushed
New copy for owner approval (picker row): "Deployed on Starknet · private balances off, set up in another wallet"
|
Import still never blocks on the pool. Seed discovery and find-by-address now read get_public_key for each deployed candidate and, when the key is non-zero and not the one Bramble would register, list the row with "private balances off, set up in another wallet". The row stays selectable: its public funds are reachable here. A zero key, a matching key, an unreadable pool, or a failed derivation list the row unlabelled, and the private-action path re-checks. Undeployed candidates are not read. The pool read and the key comparison are the point-of-use ones (strk20RegisteredElsewhere wraps the same readPoolKey and cache). The discovery suites switch to the offline pool stub so none reaches the live pool now that discovery reads it.
39f932c to
4300d3a
Compare
Summary
Users moving from Ready (and other wallets) with shielded funds import their account into Bramble and then cannot unshield. The pool writes an address's viewing key once (
WriteOnce,NON_ZERO_VALUEon a secondSetViewingKey) and has no rotate path, so Bramble can never register its own key for that account. Every proof needs the registered key, so the account can never shield or unshield here. The account is still worth importing: its public funds are reachable.Import always succeeds. Seed, raw key, key vault, Ledger, Ready key (#777) and address import (#781) never block on the pool. Seed discovery and find-by-address read
get_public_keyfor each deployed candidate only to label the row (below); a failed read lists the row unlabelled. The paths that write an account do not read the pool.The check happens at the point of use. When a private action is attempted, Bramble reads
get_public_keyfor the active account across every configured RPC endpoint (withRpcFallback). The account is flagged only when that key is non-zero and differs from the viewing public key Bramble would register (previewStrk20ViewingPublicKey/previewImportedStrk20ViewingPublicKey; only the public x-coordinate leaves the vault). Zero and a matching key behave as they do today. A failed pool read or key derivation is not a flag: public sends and swaps continue, and the private action asks for a retry.The blocker is added to
resolvePrivacyContextinstrk20-op-context.ts, and every private route that bypasses the chokepoint gets the same check: state read, registration, shield, send, unshield, shadow sweep, and dapp approval staging and execution.Ledger accounts stay on the existing Ledger blocker, which is checked first. A TODO records that the retained Ledger viewing key should be compared once Ledger provisioning ships.
Fee reads stay on the first endpoint. A non-zero pool key may be remembered for the process; a zero key is not, because the address can still be registered.
User-facing copy (for owner approval)
Decisions where the plan was silent
Verified against the pinned sources
core::ec::stark_curve::{GEN_X, GEN_Y}, the standard Stark generator, so Krusty's public key for Bramble's viewing key is directly comparable.compiler.ts:toBigInt(this.userViewingKey)).0x040337b1…ffe812a, which is the address Bramble configures (STRK20_MAINNET_POOL). Ready ships STRK20 through the official stack, so it registers in the same pool. Sources are in the comment thread. A Ready-registered address would settle it on-chain.Other changes
readStrk20Registerednow wraps a newreadStrk20PublicKey, so the key value is available.deriveImportedAccountis extracted asrecoverStarkPublicKey.tests/support/recovery-accounts.ts;makeRuntimeRpcanswers the pool read as "not registered" by default.History
The first two commits refused the import outright. Review changed the product call: import the account and turn privacy off for it with an explanation. The third commit is that rework, with
release/v0.10.8merged in.Refs #727
Roadmap & release
release/v0.10.8Checks
bun run checkpasses locally (3,358 tests;check:lintexits 0 with a Biome schema info, CLI 2.5.14 vs schema 2.5.11)bun run test:e2epasses (CI)Dependencies
none
The claims about pool and contract behaviour come from the vendored Cairo and SDK sources. A reviewer should check them before relying on them in user-facing copy.