Skip to content

perf(bitcoin-wallet-snap): reduce batch account creation RPC and state overhead - #221

Open
hmalik88 wants to merge 20 commits into
mainfrom
hm/bitcoin-perf
Open

perf(bitcoin-wallet-snap): reduce batch account creation RPC and state overhead#221
hmalik88 wants to merge 20 commits into
mainfrom
hm/bitcoin-perf

Conversation

@hmalik88

@hmalik88 hmalik88 commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR improves Bitcoin Snap account creation performance, especially for large keyring_createAccounts ranges used during SRP import.

The main change is that batch creation now fetches entropy once per distinct parent derivation path, then derives hardened account children locally. This replaces the previous shape where each account creation performed its own snap_getBip32Entropy call. The insert path also reuses the state snapshot loaded during the existing-account lookup, avoiding duplicate full-state reads inside the same account mutation.

This PR also removes the 100-account handler chunking so the requested range is processed as one batch, while still preserving idempotency, result ordering, and synchronization behavior.

What Changed

  • Reduced Bitcoin batch account creation entropy RPCs from one per account to one per distinct parent path.
  • Reused the existing-account lookup snapshot during insert, avoiding redundant reads of the encrypted accounts and derivation-path maps.
  • Parallelized the account and derivation-path state writes during insert.
  • Moved range processing into a single createMany batch instead of handler-level chunks of 100.
  • Removed the account creation concurrency throttle, since entropy derivation is now local after the parent node fetch.
  • Coalesced concurrent account synchronization runs so stacked triggers share one in-flight sync.
  • Removed the v1 AccountDeleted event emission from the delete flow, matching keyring v2 behavior.

Why

Before this change, creating N Bitcoin accounts required roughly N entropy RPCs plus repeated full-state reads/writes per 100-account chunk. For large imports, that made account creation scale poorly and increased contention in the extension/Snap RPC queue.

After this change, the dominant repeated cross-process entropy calls are replaced with one parent entropy fetch plus local child derivation. State I/O is also reduced by reusing the mutation snapshot already loaded for idempotency checks.

Notes

The private parent node is held transiently in Snap memory during batch creation. This is the same trust boundary as the previous per-account implementation, which already fetched private entropy before neutering it. The parent node is not persisted or logged, and account descriptors are still built from neutered child nodes.

Test Plan

  • yarn workspace @metamask/snap-networks-utils run build
  • yarn workspace @metamask/bitcoin-wallet-snap run build
  • yarn workspace @metamask/bitcoin-wallet-snap run test
  • Verify keyring_createAccounts for a large range returns accounts in requested index order.
  • Verify idempotent re-creation returns existing accounts without duplicates.
  • Verify a multi-account import performs one parent entropy fetch per distinct parent path.
  • Verify account synchronization still runs after account creation and duplicate concurrent syncs are coalesced.

References

N/A

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've communicated my changes to consumers by updating changelogs for packages I've changed
  • I've introduced breaking changes in this PR and have prepared draft pull requests for clients and consumer packages to resolve them

… path

AccountUseCases.create had no production caller (KeyringHandler routes
keyring_createAccounts through createMany/discover) and emitted the v1
AccountCreated lifecycle event after persisting, with no rollback — a
latent orphaned-account bug against keyring v2 clients. Removes create
and the now-unused emitAccountCreatedEvent/emitAccountDeletedEvent from
SnapClientAdapter and the SnapClient interface.
@hmalik88
hmalik88 marked this pull request as ready for review August 27, 2026 10:06
@hmalik88
hmalik88 requested a review from a team as a code owner August 27, 2026 10:06
@hmalik88
hmalik88 deployed to default-branch August 27, 2026 10:06 — with GitHub Actions Active

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Improves Bitcoin account batch creation performance by reducing entropy RPCs and state operations, while updating synchronization and keyring v2 deletion behavior.

Changes:

  • Derives account children locally from shared parent entropy.
  • Processes account ranges in one batch and reuses state snapshots.
  • Adds synchronization coalescing and removes legacy deletion events.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
AccountUseCases.ts Batches creation and reuses state snapshots.
AccountUseCases.test.ts Updates batch creation and deletion tests.
BdkAccountRepository.ts Adds batched derivation and snapshot persistence.
BdkAccountRepository.test.ts Tests derivation and snapshot merging.
SnapClientAdapter.ts Removes legacy lifecycle event methods.
KeyringHandler.ts Processes ranges as single batches.
KeyringHandler.test.ts Tests unchunked range creation.
CronHandler.ts Coalesces matching synchronization calls.
CronHandler.test.ts Tests synchronization coalescing.
entities/snap.ts Defines account-state snapshots.
entities/account.ts Extends repository contracts for batching.
snap.manifest.json Updates the bundle checksum.
CHANGELOG.md Documents performance and lifecycle changes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +139 to +141
const key = `syncSelectedAccounts:${[...accountIds].sort().join(',')}`;

await this.#syncCoalescer.run(key, async () => {
Comment on lines +254 to +256
// Reuse the lookup's state snapshot: we're inside the account
// mutation, so it cannot have been changed by another creation.
await this.#repository.insertMany(newAccounts, snapshot);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this one should worth to be considered, a sync may have an impact.

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.

4 participants