Skip to content

fix(wallet): make UMP account lookup resilient - #427

Merged
ty-everett merged 4 commits into
mainfrom
agent/resilient-ump-account-lookups
Aug 4, 2026
Merged

fix(wallet): make UMP account lookup resilient#427
ty-everett merged 4 commits into
mainfrom
agent/resilient-ump-account-lookups

Conversation

@ty-everett

@ty-everett ty-everett commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Keep this pull request in draft until local validation is complete. After
every push, wait for all applicable checks on the exact head to finish and
fix every failure before requesting review or calling the work complete.

Program and scope

  • Tracker or issue: Operational report: Metanet users were blocked when one advertised overlay host returned 503 while another host had usable evidence.
  • Program gate(s) advanced: Wallet authentication availability and UMP lookup resilience.
  • Why this change is needed: The previous all-host unanimity rule converted partial overlay failure into an indeterminate account state even when a clean empty response or a verified account record was available. Stale overlay indexes could also return a spent UMP token alongside its current rendition and create a false ambiguity.
  • Explicitly out of scope: SLAP host election/eviction, early-return lookup timing, WAB continuity relaxation, deployment, and workstation npm publication.
  • Exact head SHA reviewed: dcf5a64179c8bff7b4b12ca233c971ef4b2705e4

Impact

  • No public package source or manifest changed
  • Public package source or manifest changed; affected packages are listed below
  • Infrastructure source, dependency, image, or deployment configuration changed
  • Public API, exports, types, runtime targets, or browser/mobile behavior changed
  • Security-sensitive boundary changed
  • Documentation or examples changed

Affected packages/services and intended versions (publication occurs only
through the release workflow after approval):

  • @bsv/wallet-toolbox@2.5.0
  • @bsv/wallet-toolbox-client@2.5.0
  • @bsv/wallet-toolbox-mobile@2.5.0

These versions were already the unreleased candidates on current main after #426; this PR adds the wallet lookup behavior to that release boundary without creating a second version bump.

The shared account lookup now applies this order:

  1. One or more verified matching records establish that the account exists.
  2. When multiple matching records describe a spend lineage, the unique latest unspent rendition wins using transaction ancestry already present in lookup BEEF; no chain scan is performed.
  3. If multiple unrelated or insufficiently evidenced current records remain, lookup stays ambiguous.
  4. With no verified record, at least one clean empty host response establishes a new account; malformed, rejected, or unavailable peers cannot veto it.
  5. With neither usable positive nor negative evidence, lookup remains indeterminate.

The resolver still waits for all advertised hosts so a slower existing record can override an earlier empty response. WAB existing-account continuity remains fail-closed.

Verification

  • Local commands and results:
    • pnpm install --frozen-lockfile under supported Node 24 — passed.
    • Wallet dependency graph build — passed.
    • Wallet authentication diagnostics — 28/28 passed.
    • pnpm --filter @bsv/wallet-toolbox test:coverage — 183 suites passed; 1,621 passed and 1 skipped before the final focused coverage-only regression, which also passed.
    • Repository patch coverage — 95.38% (124/130 changed line/branch points; target 90%); all changed executable lines covered.
    • Wallet lint and typecheck — passed with zero findings.
    • git diff --check — passed.
  • Hosted CI run: CI run 30947653428 and CodeQL run 30947653414 completed successfully on exact head dcf5a64179c8bff7b4b12ca233c971ef4b2705e4; Codecov patch is 91.67% and the merge gate is green.
  • Conformance evidence: No wire-format or protocol-vector changes. Supersession uses BEEF already returned by lookup hosts.
  • Coverage delta: Covers all-empty, partial empty/failure, all-failed, valid+malformed+empty+failure, malformed+empty, mismatched+empty, malformed-only, duplicate records, old-token outpoint lookup, direct and multi-hop spend lineage, missing intermediate records, forked tokens, same-identity continuity, rotated presentation keys, malformed predecessor data, mixed BEEF depth, and a 1,200-update ancestry chain.
  • Lint/typecheck delta: Zero findings locally.
  • Browser/mobile/packed-consumer evidence: Both browser-package matrix jobs and affected-dependent test shards passed; the behavior lives in shared Wallet Toolbox code used by desktop, browser, and mobile distributions.
  • Performance or bundle-size delta: No dependency or public entry-point change. Ancestry traversal is iterative and bounded by BEEF supplied in lookup results.
  • I self-reviewed the complete diff for correctness, security,
    compatibility, public API, artifacts, dependencies, docs, and operations
  • All applicable checks are terminal and successful on the exact head; any
    scope-based skip is expected and validated by the merge gate

Security and dependencies

  • No dependency or lockfile change
  • Changelog, runtime relevance, peer compatibility, transitive graph, and
    audit results were reviewed
  • CodeQL/negative tests cover any changed trust boundary
  • The exact-head CodeQL analysis has no new alert
  • The exact-head repository quality gate reports zero new Sonar findings
    (including accepted or false-positive issue states) and zero unreviewed hotspots;
    Sonar's aggregate Quality Gate passed verdict alone is not merge evidence
  • No new override, advisory dismissal, quality suppression, or skipped test
  • Any temporary exception is registered with owner, evidence, review date,
    and removal condition
  • Workflow permissions and lifecycle-script behavior remain least privilege

Dependency evidence

Complete every field when a dependency manifest, lockfile, container base, code
generator dependency, Dependabot configuration, or pinned workflow action
changes. Use “Not applicable — ” only when the reason is concrete.

  • Release notes and necessity: Not applicable — no dependency or lockfile delta remains after rebasing onto current main.
  • Runtime, build, and peer compatibility: Not applicable — no dependency selection changed.
  • Deduplicated lockfile: Not applicable — pnpm-lock.yaml matches main.
  • Audit and CodeQL: Dependency audit is unchanged from main; exact-head CodeQL and dependency review passed.
  • Package and consumer tests: Wallet local validation, hosted affected-scope tests, browser packages, and all wallet coverage shards passed.
  • Bundle and performance impact: No new dependency, entry point, or wire format.
  • Affected public package versions: Wallet Toolbox, client, and mobile remain the existing unreleased 2.5.0 candidates.

Release and operations

  • No npm publication was performed from a workstation or from this PR
  • Required npm patch bumps are included or intentionally deferred by the
    controlling program
  • Image/SBOM/provenance/deployment/rollback impact is documented
  • Documentation, changelog, migration, and operational guidance are current

No image, deployment, DNS, database, secret, or wire-format change is involved. Rollback is the prior package release. Publication will use the protected release workflow only after merge.

Completion evidence

  • The linked tracker is updated only for work fully proved by merged code,
    passing checks, resolved alerts, measurements, or an approved exception
  • Review conversations are resolved
  • Documentation, changelog, migration notes, release notes, and operator
    guidance are current or concretely not applicable
  • No pending, failed, stale, cancelled, or unexpectedly skipped check is
    being handed to another contributor as “complete”
  • One qualified maintainer approval is sufficient; no last-pusher
    restriction is assumed

@codecov

codecov Bot commented Aug 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.66667% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...wallet/wallet-toolbox/src/CWIStyleWalletManager.ts 91.66% 0 Missing and 6 partials ⚠️

📢 Thoughts on this report? Let us know!

@ty-everett
ty-everett marked this pull request as ready for review August 4, 2026 15:15

@BraydenLangley BraydenLangley left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performed integration testing locally with an account with had competing forked ump tokens, and these changes resolved the issues previously discovered.

ty-everett and others added 3 commits August 4, 2026 13:05
When distinct verified UMP tokens compete for the same account hash,
resolve on on-chain proof instead of always erroring:

- A candidate spent anywhere in another candidate's BEEF ancestry is
  superseded. The walk is iterative (deep update chains cannot exhaust
  the stack) and evidence is merged across hosts serving the same token
  at different BEEF depths, so a shallow copy cannot mask a deeper
  copy's proof.
- Forked candidates with no spend relationship resolve only when
  exactly one provably consumed a same-identity predecessor token
  (matching presentation or recovery hash), which requires the
  account's keys and demonstrates continuity of control. A freshly
  minted competitor is typically the residue of an erroneous historical
  re-onboarding.
- Anything less decisive — true forks, competing proven continuations,
  or candidates without examinable evidence — still raises
  UMPTokenLookupError so a wrong token can never be chosen silently,
  and multiple verified tokens can never be mistaken for a new account.
- Resolved conflicts report a supersededTokens count in the completed
  lookup telemetry event (counts only, consistent with the privacy
  boundary).

Covers the edge-case matrix in CWIAuthenticationDiagnostics.test.ts:
direct and multi-hop supersession, supersession through intermediates
absent from the lookup answer, host-order independence, duplicate
records, merged shallow/deep evidence, 1200-link chains, rotated
presentation keys, twelve-field predecessors, malformed identity
hashes, and the fail-closed fork cases.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ty-everett
ty-everett force-pushed the agent/resilient-ump-account-lookups branch from de08adb to 546af07 Compare August 4, 2026 20:09
@ty-everett
ty-everett force-pushed the agent/resilient-ump-account-lookups branch from 546af07 to dcf5a64 Compare August 4, 2026 20:23
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

@ty-everett
ty-everett merged commit b80b4ad into main Aug 4, 2026
38 checks passed
@ty-everett
ty-everett deleted the agent/resilient-ump-account-lookups branch August 4, 2026 20:37
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