Skip to content

feat: add derivable ed25519 root pub encode/decode helpers - #9592

Open
davidkaplanbitgo wants to merge 1 commit into
masterfrom
davidkaplan/wcn-2485-bitgojs-derivable-ed25519-root-pub-encodedecode-helpers
Open

feat: add derivable ed25519 root pub encode/decode helpers#9592
davidkaplanbitgo wants to merge 1 commit into
masterfrom
davidkaplan/wcn-2485-bitgojs-derivable-ed25519-root-pub-encodedecode-helpers

Conversation

@davidkaplanbitgo

Copy link
Copy Markdown
Contributor

WCN-2485 · TDD Part II-3 §1.3

What

Safe slot-④ (ed25519Multisig) root pubs are Stellar StrKeys — a bare 32-byte ed25519 point with nowhere to carry the chain code that soft-deriving the backup and BitGo co-signer keys requires. Per the TDD, the chain code is concatenated onto pub rather than added as a new field, mirroring the xpub for slot ① and commonKeychain for slots ②③:

pub = <StrKey ed25519 public key>  ||  <chainCode, 64 lowercase hex>
       exactly 56 chars, 'G…'          exactly 64 chars
total length exactly 120

Adds encodeDerivableEd25519Pub / decodeDerivableEd25519Pub to modules/sdk-core under src/bitgo/safe/, exported from the module's public surface so modules/key-card can import them. Also exports isDerivableEd25519Pub, isValidEd25519ChainCode, isValidEd25519StrKeyPublicKey, and the DERIVABLE_ED25519_* constants.

The 56-char split offset is a cross-repo contract shared with wallet-platform, modules/key-card and WRW. slice(56) is not hardcoded anywhere else — every call site goes through these helpers.

Additive only. No existing behaviour changes.

Shared fixture

test/unit/bitgo/safe/fixtures/derivableEd25519Pub.json is copied byte-identical from the wallet-platform implementation (BitGo/bitgo-microservices#62429) and verified as such. Four independent implementations of this split will otherwise drift, and the failure mode is an unrecoverable wallet.

Note on StrKey validation

sdk-core has no stellar-sdk dependency and shouldn't take one on a coin module, so the G… half is validated inline: base32 decode → version byte 0x30 → CRC16-XModem checksum. It agrees with wallet-platform's StrKey.isValidEd25519PublicKey on every fixture vector, including the bad-checksum, secret-seed, and halves-swapped cases. Happy to swap for a real dependency if reviewers prefer.

There is no assertDerivableEd25519Pub here — that signature is wallet-platform-only (it takes an AbstractCoin and throws apiErrors.Invalid, neither of which exists in sdk-core). isDerivableEd25519Pub(composite) covers the equivalent need.

Testing

47 tests, all driven off the shared fixture — round-trip, and rejection of a bare 56-char pub, non-hex suffix, uppercase and mixed-case hex, wrong total length, empty string, bad StrKey checksum, secret seed, and swapped halves. All pass.

tsc --noEmit and eslint are clean for the changed files. One tsconfig.json line lists the fixture JSON, same pattern as sdk-coin-dot.

Heads up: the sdk-core barrel currently throws on import in my checkout (redpallasMPCv2KeyGenSender.ts builds an io-ts union from @bitgo/public-types members the installed version doesn't export) — pre-existing on master, the untouched test/unit/bitgo/safe/safe.ts fails identically. Looks like stale sdk-lib-mpc/public-types dists. To run the suite locally I pointed the import at the module path directly; the committed test imports from '../../../../src' per convention.

Out of scope

createBackup, createMultisigRoot, and the keycard changes — WCN-2488 and WCN-2490, both blocked on this.

Safe slot-4 (ed25519Multisig) root pubs are Stellar StrKeys, which have
nowhere to carry the chain code that soft-deriving co-signer keys needs.
Per TDD Part II-3 1.3 the chain code is concatenated onto pub:

  <56-char StrKey 'G...'> || <64 lowercase hex chain code>

The 56-char split offset is a cross-repo contract shared with
wallet-platform, modules/key-card and WRW, so every call site must go
through these helpers rather than slicing inline.

StrKey validation is implemented inline (base32 + version byte +
CRC16-XModem) because sdk-core must not depend on a coin module.

Tests assert against a fixture copied byte-identical from the
wallet-platform implementation.

TICKET: WCN-2485
@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

WCN-2485

@pranishnepal pranishnepal 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.

lgtm

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