Skip to content

mobile: send and deploy Braavos accounts as the extension does - #796

Merged
Ryun1 merged 10 commits into
feature/mobile-walletfrom
mobile/braavos-support
Oct 29, 2026
Merged

Ryun1 merged 10 commits into
feature/mobile-walletfrom
mobile/braavos-support

Conversation

@Ryun1

@Ryun1 Ryun1 commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Ports the extension's Braavos support (f20125d and the deploy-first gate) to the mobile core and apps, and fixes the core job's accounts_parity failure (the recovery hint for a funded modern-class Braavos counterfactual).

What changes

  • Deployed Braavos: class at one accepted block must be a signing implementation; get_signers must list this wallet's key first; an active 2FA signer refuses unless its deferred removal expired by the block timestamp. Invokes sign [r, s].
  • Undeployed Braavos: a send is refused at review with BraavosDeployFirst ("Deploy this account first (Settings → Deploy account)."); the base class is BraavosNotDeployable.
  • Deployment (modern class only): extended authorization [r, s, ...11 params, aux_r, aux_s], params signed first. The estimate is signed for real over the query-version hash (the Braavos constructor checks it even when estimated) — the one place a review signs. Krusty only hashes v3, so send/sign.rs composes the query hash; a unit test pins it to Krusty at v3 and the fixture pins the query hash.
  • View-only removed from core, iOS and Android (AccountInfo.viewOnly, SendError.ViewOnlyAccount, sendRefusal, its copy). Braavos keys stay out of Nostr multisig co-signers. Private-balance reads now admit Braavos, as the extension and Ready already do.

Parity

scripts/mobile-transactions-braavos.ts runs the extension's real path into extension-transactions-braavos*.json (6 scenarios, 8 refusals); tests/transactions_braavos_parity.rs requires identical request bodies, signature arrays, signed-hash count and transaction hashes. Docs: mobile/docs/braavos.md.

Verification

  • Passing locally: mobile core suite (except below), cargo fmt/clippy -D warnings, Kotlin lint, TS types/lint, bun run test (3303, incl. fixture drift).
  • Not run locally (no Xcode / Android SDK): SwiftLint, iOS build/tests, Android build/tests — CI is the first check of the app edits.
  • Pre-existing failures, not from this PR: history_parity (4, mobile lacks direction/tokenAddress on activity entries), privacy_parity (tokens-65), check:files (accounts-parity-families.json > 500 lines), one @bramble/terminal swap-route test.

Ryun1 added 2 commits October 28, 2026 15:12
Main taught the extension to deploy funded Braavos counterfactuals
(f20125d); the mobile core still treated every Braavos record as view
only, so the accounts parity test failed on the new recovery hint and
the apps could not act for a Braavos account at all.

The core now follows braavos-compatibility.ts, braavos-deployment-gate.ts,
deployment.ts and account-signer.ts:

- A deployed account is read at one accepted block: its class must be a
  Braavos implementation this wallet signs for, this wallet's key the
  first Stark signer, and any 2FA signer's requested removal expired by
  the block's timestamp. Invokes carry a bare [r, s].
- An undeployed account deploys only through its own reviewed
  deployment (BraavosDeployFirst); the base class never deploys.
- The modern deployment class deploys with the extended authorization
  [r, s, ...params, aux_r, aux_s], parameters signed first. Its estimate
  is signed for real over the query-version hash, since the constructor
  checks the authorization even when estimated. Krusty hashes version 3
  only, so sign.rs composes that hash; a test pins it to Krusty's.

The static view-only flag is gone from the core and both apps: every
record passes the one signing gate and the plan decides on chain. Braavos
keys are still not offered as Nostr multisig co-signers.

scripts/mobile-transactions-braavos.ts captures the extension's own path
(sends, legacy implementation, expired 2FA removal, deployments, and each
refusal); transactions_braavos_parity.rs replays it and requires the same
request bodies, signature arrays, signed-hash count and hashes.
Follow-ups from review: a get_signers length near usize::MAX now reads as malformed instead of overflowing; the query-hash composition test also compares a non-zero nonce against Krusty, since nonce and DA mode were both zero; the unreachable Braavos arm in deploy_probed is gone (submit_probed already refuses an undeployed Braavos send); and comments and verification notes no longer describe the removed view-only gate.
cursoragent and others added 8 commits October 29, 2026 11:44
A base class, an unrecognised signing class, a wrong first key, an active
2FA signer, and a malformed get_signers list used to become a fee-failed
review. Confirm is OutOfOrder while can_confirm is false, so the sentence
never reached the user. The review step now fails with that code, after the
same reads the extension throws on. A modern-class deployment still quotes,
and Ready reviews are unchanged. The parity test requires every review-time
refusal to be that failure.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
ensureAccountDeployed used the Ready refusal when the record has no
deployment. A Braavos base class that reached that arm would have said
the account was an Argent class this wallet does not deploy.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
Krusty only hashes version 3, so the query bit is composed locally. The
existing test checks that composer at version 3. This one checks the
estimate hash is that composer with the query bit set, and not the
broadcast hash.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
A load-balanced node answers JSON-RPC 24, or "block not found", when it has
not seen the accepted block the previous call pinned. The extension retries
that whole decision three times. Argent and Braavos share the pin, so both
do. A third miss still fails closed, and a contract-not-found does not retry.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
The flag is gone. The plan test, the accounts module note, and the account
docs still described it, and the multisig comment still explained the
exclusion as view-only. plan.rs stays under the 500-line cap (493), so it
does not need a split.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
cargo test stops at the first failing target, so accounts_parity hid
history_parity and history_parity hid privacy_parity. --no-fail-fast runs
the rest. A failing binary still fails the job.

Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
Co-authored-by: Ryan <Ryun1@users.noreply.github.com>
Review follow-ups. sign_hash_for_account signs a host-supplied hash with no class or signer check, so it refuses Braavos records again: a Braavos signature is made only inside a send plan, after the account's live class and signers are read (the extension's requireLiveAuthorizableClass refuses the same states). The Braavos deployment estimate's signatures at review no longer renew the idle window (plan_sign takes renew; the confirmed re-estimate and the deployment still renew), so a review stays passive as reads are. block_at gets its own doc comment back from nonce.
@Ryun1
Ryun1 marked this pull request as ready for review October 29, 2026 16:52
@cursor

cursor Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Changes wallet transaction preparation, signing, and deployment authorization for a new account family, with broad removal of prior Braavos send blocks—incorrect probe or signature logic could refuse valid sends or sign unsafe states.

Overview
Adds Braavos account support in the mobile Rust core to match the extension: on-chain probes (pinned block, signing class, get_signers, deferred 2FA removal vs block time), invoke signing as [r, s], and standalone deployment with extended deploy authorization and real signatures on the query-version hash for fee estimates.

Undeployed Braavos no longer auto-deploys inside Send; review/submit return BraavosDeployFirst / BraavosNotDeployable with new user-facing copy. The old view-only model is removed (viewOnly, SendError.ViewOnlyAccount, Android sendRefusal and upfront Send/Deploy toasts); Send and Deploy flows defer to the core plan. Braavos still cannot use raw sign_hash_for_account or Nostr multisig co-signer lists.

Android wires the new failure codes and always opens Send/Deploy; account catalog and generated assets include Braavos class hashes. Tests/fixtures add extension Braavos transaction vectors, shared drive helpers, block-pin retries, and CI runs cargo test --no-fail-fast.

Reviewed by Cursor Bugbot for commit e69a7a5. Bugbot is set up for automated code reviews on this repo. Configure here.

@Ryun1
Ryun1 merged commit 35cc955 into feature/mobile-wallet Oct 29, 2026
2 of 4 checks passed
@Ryun1
Ryun1 deleted the mobile/braavos-support branch October 29, 2026 16:52
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