Skip to content

feat(sdk-core): add createOfflineKeyGenRound2Share to EddsaMPCv2Utils#9243

Draft
bitgo-ai-agent-dev[bot] wants to merge 3 commits into
masterfrom
wci-890-add-createofflinekeygenround2share
Draft

feat(sdk-core): add createOfflineKeyGenRound2Share to EddsaMPCv2Utils#9243
bitgo-ai-agent-dev[bot] wants to merge 3 commits into
masterfrom
wci-890-add-createofflinekeygenround2share

Conversation

@bitgo-ai-agent-dev

Copy link
Copy Markdown

What

  • Add createOfflineKeyGenRound2Share to EddsaMPCv2Utils in modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts
  • The method decrypts the persisted round-1 session, restores the DKG state, verifies and deserializes all 3 incoming round-1 messages (own, counterParty, BitGo), calls dkg.handleIncomingMessages to produce ownMsg2, signs it with the GPG key, and returns { signedMsg2, encryptedRound2Session }
  • Fix DKG.restoreSession in modules/sdk-lib-mpc/src/tss/eddsa-mps/dkg.ts to be async and load WASM before restoring state (mirrors DSG.restoreSession), which is required so that handleIncomingMessages works on a restored session
  • Fix createOfflineKeyGenRound1Share (WCI-889) to call this.bitgo.encrypt/decrypt instead of non-existent encryptAsync/decryptAsyncBitGoBase.encrypt/decrypt already return Promise<string>

Why

  • Required by the EdDSA MPCv2 external-signer AKM flow: AWM handlers must be able to drive DKG round 2 without holding decrypted key material in memory across API calls — the round-1 state is encrypted at the end of round 1 and restored here for round 2
  • Completes the second of three signer methods needed for createKeychainsWithExternalSigner to function end-to-end

Test plan

  • Unit tests: Round1 → Round2 produces valid { signedMsg2, encryptedRound2Session }
  • Rejects tampered incoming messages (GPG signature verification fails)
  • Rejects wrong walletPassphrase for session decryption
  • Rejects non-BitGo public key when getEnv() returns 'prod' or 'test' (env requires BitGo pub key config)
  • encryptedRound2Session is only decryptable with the correct walletPassphrase
  • All 65 existing tests in eddsaMPCv2.ts continue to pass

Ticket: WCI-890

@linear-code

linear-code Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

WCI-890

@bitgo-ai-agent-dev bitgo-ai-agent-dev Bot force-pushed the wci-890-add-createofflinekeygenround2share branch 2 times, most recently from ebc2b27 to a95ffdb Compare July 14, 2026 07:00
@bitgo-ai-agent-dev

Copy link
Copy Markdown
Author

The Audit API Spec CI failure is a pre-existing breakage on master (not caused by this PR).

The Generate merge base API spec job checks out master to build a baseline — and master currently fails to compile because WCI-889 merged encryptAsync/decryptAsync calls that don't exist on BitGoBase (lines 667 and 695 of eddsaMPCv2.ts).

This PR actually fixes that bug by replacing encryptAsync/decryptAsync with the correct encrypt/decrypt (which already return Promise<string>).

The BitGo SDK workflow (which tests our branch) passes cleanly on both CI runs — lint ✅, format ✅, unit tests ✅.

@bitgo-ai-agent-dev bitgo-ai-agent-dev Bot force-pushed the wci-890-add-createofflinekeygenround2share branch from f8de6e4 to f697809 Compare July 14, 2026 09:25
Support Bot and others added 3 commits July 14, 2026 10:47
Add createOfflineKeyGenRound2Share to EddsaMPCv2Utils in sdk-core,
enabling external signer AKM flows to complete DKG round 2 without
holding decrypted key material in memory across API calls.

The method:
- Decrypts encryptedUserGpgPrvKey and encryptedRound1Session with the
  wallet passphrase
- Restores the DKG session via dkg.restoreSession
- Verifies and deserializes all 3 incoming round-1 messages (own,
  counterParty, BitGo) via MPSComms.verifyMpsMessage
- Calls dkg.handleIncomingMessages([ownMsg1, counterPartyMsg1, bitgoMsg1])
  to produce ownMsg2 (WASM filters by partyIdx internally)
- Signs ownMsg2 payload with the GPG private key via
  MPSComms.detachSignMpsMessage
- Serializes {dkgSession, ownMsgPayload, ownMsgFrom} and encrypts it
  as encryptedRound2Session

Also fix DKG.restoreSession in sdk-lib-mpc to be async and load WASM
before restoring state (matching DSG.restoreSession behavior), which
is required for handleIncomingMessages to work after a restore.

Also fix createOfflineKeyGenRound1Share (WCI-889) to use
this.bitgo.encrypt/decrypt instead of non-existent encryptAsync/
decryptAsync — BitGoBase.encrypt/decrypt already return Promise<string>.

Ticket: WCI-890
Session-Id: 2b6d8843-9bec-4a05-8725-18d450b7f097
Task-Id: dcea3d67-b592-483a-967b-968cc01f5b76
Fix prettier formatting in createOfflineKeyGenRound2Share (multi-line
decrypt call) and in test helper (function signature and initDkg args).
Remove unused variables from buildCounterPartyAndBitgoMsgs test helper.

Update sdk-lib-mpc DKG session management test to await restoreSession,
which is now async after the fix to load WASM before restoring state.

Ticket: WCI-890
Session-Id: 2b6d8843-9bec-4a05-8725-18d450b7f097
Task-Id: dcea3d67-b592-483a-967b-968cc01f5b76
The assert message incorrectly said "round 1" when it should say "round 2".

Ticket: WCI-890
Session-Id: 2b6d8843-9bec-4a05-8725-18d450b7f097
Task-Id: dcea3d67-b592-483a-967b-968cc01f5b76
@bitgo-ai-agent-dev bitgo-ai-agent-dev Bot force-pushed the wci-890-add-createofflinekeygenround2share branch 2 times, most recently from 8156388 to 9a7d477 Compare July 14, 2026 10:47
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.

1 participant