Skip to content

feat: sign and share ed25519 multisig safe wallets - #10146

Merged
davidkaplanbitgo merged 1 commit into
masterfrom
WCN-2431-safe-ed25519-sharing
Nov 9, 2026
Merged

davidkaplanbitgo merged 1 commit into
masterfrom
WCN-2431-safe-ed25519-sharing

Conversation

@davidkaplanbitgo

Copy link
Copy Markdown
Contributor

Description

Safe owners of ALGO, XLM and HBAR (ed25519Multisig) wallets could neither sign nor share spend. The shared safe key resolver rejected every ed25519 family, because it only knew secp256k1 BIP32 derivation.

This adds an ed25519 branch to that resolver, used by both owner signing and wallet sharing:

  • SLIP-0010 hardened-derives the user child from the safe root at the registered path.
  • Encodes the derived pub for the coin and requires it to equal the registered child pub, otherwise SafeDerivedPublicKeyMismatchError. A bare G... strkey is not accepted for ALGO or HBAR.
  • Returns only the child seed, encoded the way the coin signs with it (ALGO base32 seed, HBAR DER private key, XLM S...). The root is never returned or placed in a share payload.
  • Moves the per-coin ed25519 encoders out of safe recovery into @bitgo/sdk-lib-safes (ed25519CoinEncoders.ts), so recovery, owner signing and sharing use one registry. Recovery behavior is unchanged.

MPC safe children (ecdsaMpc, eddsaMpc) already carry their own encrypted signing share and share through the ordinary TSS path. No change there, apart from fixtures and wording: a public-only TSS child (an invalid state) still fails closed, and the error text no longer claims a missing derive ceremony.

Issue Number

WCN-2431, WCN-4257

Type of change

  • New feature (non-breaking change which adds functionality)

SafeOwnerSigningNotImplementedError and SafeShareNotImplementedError keep their names and are now thrown only for a public-only TSS child. ResolveSafeChildPrvForSharingParams.mpcAlgorithm is removed; nothing passed it.

How Has This Been Tested?

From the module directories, with sdk-lib-safes rebuilt first:

  • sdk-lib-safes: npx mocha --recursive test - 99 passing (new pinned encoder vectors for ALGO, HBAR, XLM, plus the existing hardened vector)
  • sdk-core: npx mocha test/unit/bitgo/wallet/safeShareWallet.ts test/unit/bitgo/wallet/safeGetUserPrv.ts - 68 passing
  • sdk-core: npx mocha --recursive test/unit/bitgo/safe - 158 passing
  • yarn build in both modules, and yarn lint in both: clean

Covered: pinned SLIP-0010 child seed and coin-encoded prv for txlm, talgo, thbar; registered pub mismatch fails closed; m/0' single-level path fails closed; wrong passphrase; share payload has skipKeychain false, keychain.pub equal to the stored child pub, and no root material; ECDH round trip to the coin-encoded child seed; MPC child with encryptedPrv takes the ordinary path without fetching the root.

Not covered: a live staging send or share-and-sign. That needs the platform side deployed.

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • My code compiles correctly for both Node and Browser environments (Node build verified; browser build not run)
  • I have commented my code, particularly in hard-to-understand areas
  • My commits follow Conventional Commits and I have properly described any BREAKING CHANGES
  • The ticket or github issue was included in the commit message as a reference
  • I have made corresponding changes to the documentation and on any new/updated functions and/or methods - jsdoc
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes (the safe test directories in the two modules; the full suite was not run)

Safe owners of ALGO, XLM and HBAR wallets could neither sign nor share
spend: the shared safe key resolver rejected every ed25519 family
because it only knew secp256k1 BIP32 derivation.

Add an ed25519 branch to the resolver. It SLIP-0010 hardened-derives
the user child from the root at the registered path, encodes the
derived pub for the coin and requires it to equal the registered child
pub, and returns only the child seed encoded the way the coin signs
with it. The root is never returned or placed in a share payload.

Move the per-coin ed25519 encoders out of recovery into sdk-lib-safes
so recovery, owner signing and sharing use one registry.

MPC safe children already carry their own encrypted signing share and
share through the ordinary TSS path. Keep a fail-closed error for a
public-only TSS child and drop the wording that claimed a missing
derive ceremony.

TICKET: WCN-2431
TICKET: WCN-4257
@davidkaplanbitgo

Copy link
Copy Markdown
Contributor Author

@claude

@linear-code

linear-code Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

WCN-2431

WCN-4257

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

⚠️ Compatibility checks are failing on Node.js 27-nightly (experimental, non-blocking). This does not block merge, but it signals an incompatibility to fix before this release becomes supported.

View run

@davidkaplanbitgo
davidkaplanbitgo marked this pull request as ready for review November 9, 2026 21:17
@davidkaplanbitgo
davidkaplanbitgo requested review from a team as code owners November 9, 2026 21:17

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

@davidkaplanbitgo

Copy link
Copy Markdown
Contributor Author

@claude review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review found no issues

No high-confidence issues detected in this change.

@davidkaplanbitgo
davidkaplanbitgo merged commit 98d5c3a into master Nov 9, 2026
28 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