Skip to content

Commit 8e65cb9

Browse files
Marzooqaclaude
andcommitted
fix(sdk-lib-mpc): lazy-load wasm-mps to fix browser DSG
Static top-level import compiled to synchronous require() in CJS, causing fs.readFileSync at module init — unavailable in browser. DSG failed with: Cannot read properties of undefined (reading '__wbindgen_add_to_stack_pointer') Mirrors the lazy-load pattern from WCI-244 (DKG fix): - Browser: await import('@bitgo/wasm-mps/web') + webWasm.default() - Node: await import('@bitgo/wasm-mps') - initDsg() and restoreSession() made async - eddsaMPCv2.ts call sites updated to await initDsg() - Tests updated accordingly WCI-866 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0a81a68 commit 8e65cb9

10 files changed

Lines changed: 186 additions & 155 deletions

File tree

modules/bitgo/test/v2/unit/internal/tssUtils/eddsaMPCv2/signTxRequest.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ describe('signTxRequest:', function () {
242242
it('should throw if round 2 response has wrong type', async function () {
243243
const messageBuffer = Buffer.from(signableHex, 'hex');
244244
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
245-
bitgoDsg.initDsg(
245+
await bitgoDsg.initDsg(
246246
bitgoKeyShare,
247247
messageBuffer,
248248
txRequest.transactions![0].unsignedTx.derivationPath,
@@ -433,7 +433,7 @@ describe('signTxRequest:', function () {
433433
: txRequest.transactions![0].unsignedTx.signableHex;
434434
const messageBuffer = Buffer.from(txOrMessageToSign, 'hex');
435435
const bitgoSession = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
436-
bitgoSession.initDsg(
436+
await bitgoSession.initDsg(
437437
bitgoKeyShare,
438438
messageBuffer,
439439
txRequest.transactions?.[0].unsignedTx.derivationPath || 'm/0',

modules/express/test/unit/clientRoutes/externalSign.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ describe('External signer', () => {
11131113
// Initialise BitGo-side DSG session (party 2, co-signing with User party 0)
11141114
const message = Buffer.from(signableHex, 'hex');
11151115
const bitgoDsg = new EddsaMPSDsg.DSG(2 /* BITGO */);
1116-
bitgoDsg.initDsg(bitgoKeyShareBuffer, message, derivationPath, 0 /* USER */);
1116+
await bitgoDsg.initDsg(bitgoKeyShareBuffer, message, derivationPath, 0 /* USER */);
11171117

11181118
const baseTxRequest = {
11191119
txRequestId: 'eddsa-mpcv2-round-trip-test',

modules/sdk-coin-sol/src/sol.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1985,7 +1985,7 @@ export class Sol extends BaseCoin {
19851985
throw new Error('EdDSA MPCv2 recovery: commonKeyChain from keycard does not match bitgoKey');
19861986
}
19871987

1988-
const signature = EDDSAUtils.signRecoveryEddsaMPCv2(
1988+
const signature = await EDDSAUtils.signRecoveryEddsaMPCv2(
19891989
unsignedTransaction.signablePayload,
19901990
currPath,
19911991
userKeyShare,

modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
452452
const partyId = params.mpcv2PartyId ?? MPCv2PartiesEnum.USER;
453453
const signerShareType = partyId === MPCv2PartiesEnum.USER ? SignatureShareType.USER : SignatureShareType.BACKUP;
454454
const userDsg = new EddsaMPSDsg.DSG(partyId);
455-
userDsg.initDsg(userKeyShare, bufferContent, derivationPath, MPCv2PartiesEnum.BITGO);
455+
await userDsg.initDsg(userKeyShare, bufferContent, derivationPath, MPCv2PartiesEnum.BITGO);
456456
const userMsg1 = userDsg.getFirstMessage();
457457

458458
// ── API Round 1 ───────────────────────────────────────────────────────────
@@ -581,7 +581,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
581581
const userGpgPrvKey = await pgp.readPrivateKey({ armoredKey: userGpgKey.privateKey });
582582

583583
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
584-
userDsg.initDsg(userKeyShare, Buffer.from(signableHex, 'hex'), derivationPath, MPCv2PartiesEnum.BITGO);
584+
await userDsg.initDsg(userKeyShare, Buffer.from(signableHex, 'hex'), derivationPath, MPCv2PartiesEnum.BITGO);
585585
const userMsg1 = userDsg.getFirstMessage();
586586
const signatureShareRound1 = await getSignatureShareRoundOne(userMsg1, userGpgPrvKey);
587587
const sessionPayload = JSON.stringify({
@@ -693,7 +693,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
693693
};
694694

695695
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
696-
userDsg.restoreSession(dsgSession);
696+
await userDsg.restoreSession(dsgSession);
697697
const userMsg1: MPSTypes.DeserializedMessage = {
698698
from: MPCv2PartiesEnum.USER,
699699
payload: new Uint8Array(Buffer.from(userMsgPayload, 'base64')),
@@ -800,7 +800,7 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
800800
};
801801

802802
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
803-
userDsg.restoreSession(dsgSession);
803+
await userDsg.restoreSession(dsgSession);
804804
const userMsg2: MPSTypes.DeserializedMessage = {
805805
from: MPCv2PartiesEnum.USER,
806806
payload: new Uint8Array(Buffer.from(userMsgPayload, 'base64')),
@@ -1021,25 +1021,25 @@ export async function getEddsaMpcV2RecoveryKeySharesFromReducedKey(
10211021
* @param commonKeyChain 128-hex-char string: 32-byte pub + 32-byte rootChainCode
10221022
* @returns 64-byte Ed25519 signature Buffer
10231023
*/
1024-
export function signRecoveryEddsaMPCv2(
1024+
export async function signRecoveryEddsaMPCv2(
10251025
message: Buffer,
10261026
derivationPath: string,
10271027
userKeyShare: Buffer,
10281028
backupKeyShare: Buffer,
10291029
commonKeyChain: string
1030-
): Buffer {
1030+
): Promise<Buffer> {
10311031
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
10321032
const backupDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BACKUP);
10331033

1034-
const signature = MPSUtil.executeTillRound(
1034+
const signature = (await MPSUtil.executeTillRound(
10351035
3,
10361036
userDsg,
10371037
backupDsg,
10381038
userKeyShare,
10391039
backupKeyShare,
10401040
message,
10411041
derivationPath
1042-
) as Buffer;
1042+
)) as Buffer;
10431043

10441044
// deriveUnhardenedMps returns 128 hex chars: first 64 are the 32-byte public key
10451045
const derivedKeychain = deriveUnhardenedMps(commonKeyChain, derivationPath);

modules/sdk-core/test/unit/bitgo/utils/tss/eddsa/eddsaMPCv2.ts

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe('EdDSA MPS DSG helper functions', async () => {
7373
it('getSignatureShareRoundOne should build a valid round-1 share', async () => {
7474
const messageBuffer = Buffer.from(signableHex, 'hex');
7575
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
76-
userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
76+
await userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
7777
const userMsg1 = userDsg.getFirstMessage();
7878

7979
const share: SignatureShareRecord = await getSignatureShareRoundOne(userMsg1, userGpgPrivKey);
@@ -94,7 +94,7 @@ describe('EdDSA MPS DSG helper functions', async () => {
9494
it('getSignatureShareRoundOne should build a valid backup round-1 share', async () => {
9595
const messageBuffer = Buffer.from(signableHex, 'hex');
9696
const backupDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BACKUP);
97-
backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
97+
await backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
9898
const backupMsg1 = backupDsg.getFirstMessage();
9999

100100
const share: SignatureShareRecord = await getSignatureShareRoundOne(
@@ -119,7 +119,7 @@ describe('EdDSA MPS DSG helper functions', async () => {
119119
it('verifyPeerMessageRoundOne should verify a valid BitGo round-1 message', async () => {
120120
const messageBuffer = Buffer.from(signableHex, 'hex');
121121
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
122-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
122+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
123123
const bitgoMsg1 = bitgoDsg.getFirstMessage();
124124

125125
const bitgoSignedMsg1 = await MPSComms.detachSignMpsMessage(Buffer.from(bitgoMsg1.payload), bitgoGpgPrivKey);
@@ -153,11 +153,11 @@ describe('EdDSA MPS DSG helper functions', async () => {
153153
it('getSignatureShareRoundTwo should build a valid round-2 share', async () => {
154154
const messageBuffer = Buffer.from(signableHex, 'hex');
155155
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
156-
userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
156+
await userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
157157
const userMsg1 = userDsg.getFirstMessage();
158158

159159
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
160-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
160+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
161161
const bitgoMsg1 = bitgoDsg.getFirstMessage();
162162

163163
const bitgoSignedMsg1 = await MPSComms.detachSignMpsMessage(Buffer.from(bitgoMsg1.payload), bitgoGpgPrivKey);
@@ -185,11 +185,11 @@ describe('EdDSA MPS DSG helper functions', async () => {
185185
it('getSignatureShareRoundTwo should build a valid backup round-2 share', async () => {
186186
const messageBuffer = Buffer.from(signableHex, 'hex');
187187
const backupDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BACKUP);
188-
backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
188+
await backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
189189
const backupMsg1 = backupDsg.getFirstMessage();
190190

191191
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
192-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BACKUP);
192+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BACKUP);
193193
const bitgoMsg1 = bitgoDsg.getFirstMessage();
194194

195195
const bitgoSignedMsg1 = await MPSComms.detachSignMpsMessage(Buffer.from(bitgoMsg1.payload), bitgoGpgPrivKey);
@@ -221,11 +221,11 @@ describe('EdDSA MPS DSG helper functions', async () => {
221221
it('verifyPeerMessageRoundTwo should verify a valid BitGo round-2 message', async () => {
222222
const messageBuffer = Buffer.from(signableHex, 'hex');
223223
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
224-
userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
224+
await userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
225225
const userMsg1 = userDsg.getFirstMessage();
226226

227227
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
228-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
228+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
229229
const bitgoMsg1 = bitgoDsg.getFirstMessage();
230230

231231
const [bitgoMsg2] = bitgoDsg.handleIncomingMessages([bitgoMsg1, userMsg1]);
@@ -261,11 +261,11 @@ describe('EdDSA MPS DSG helper functions', async () => {
261261
it('getSignatureShareRoundThree should build a valid round-3 share', async () => {
262262
const messageBuffer = Buffer.from(signableHex, 'hex');
263263
const userDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.USER);
264-
userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
264+
await userDsg.initDsg(userKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
265265
const userMsg1 = userDsg.getFirstMessage();
266266

267267
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
268-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
268+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
269269
const bitgoMsg1 = bitgoDsg.getFirstMessage();
270270

271271
// Advance to round 2
@@ -302,11 +302,11 @@ describe('EdDSA MPS DSG helper functions', async () => {
302302
it('getSignatureShareRoundThree should build a valid backup round-3 share', async () => {
303303
const messageBuffer = Buffer.from(signableHex, 'hex');
304304
const backupDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BACKUP);
305-
backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
305+
await backupDsg.initDsg(backupKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BITGO);
306306
const backupMsg1 = backupDsg.getFirstMessage();
307307

308308
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
309-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BACKUP);
309+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.BACKUP);
310310
const bitgoMsg1 = bitgoDsg.getFirstMessage();
311311

312312
const bitgoSignedMsg1 = await MPSComms.detachSignMpsMessage(Buffer.from(bitgoMsg1.payload), bitgoGpgPrivKey);
@@ -698,7 +698,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => {
698698

699699
const messageBuffer = Buffer.from(signableHex, 'hex');
700700
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
701-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
701+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
702702

703703
const txRequestRound1 = await signBitgoEddsaRound1(
704704
bitgoDsg,
@@ -756,7 +756,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => {
756756

757757
const messageBuffer = Buffer.from(signableHex, 'hex');
758758
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
759-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
759+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
760760

761761
const txRequestRound1 = await signBitgoEddsaRound1(
762762
bitgoDsg,
@@ -817,7 +817,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => {
817817

818818
const messageBuffer = Buffer.from(signableHex, 'hex');
819819
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
820-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
820+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
821821

822822
const txRequestRound1 = await signBitgoEddsaRound1(
823823
bitgoDsg,
@@ -869,7 +869,7 @@ describe('EddsaMPCv2Utils.createOfflineRound2Share', () => {
869869

870870
const messageBuffer = Buffer.from(signableHex, 'hex');
871871
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
872-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
872+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, derivationPath, MPCv2PartiesEnum.USER);
873873

874874
const txRequestRound1 = await signBitgoEddsaRound1(
875875
bitgoDsg,
@@ -1073,7 +1073,7 @@ describe('EddsaMPCv2Utils.createOfflineRound3Share', () => {
10731073
const transaction = assertSingleTransaction(txRequest);
10741074
const messageBuffer = Buffer.from(transaction.unsignedTx.signableHex, 'hex');
10751075
const bitgoDsg = new EddsaMPSDsg.DSG(MPCv2PartiesEnum.BITGO);
1076-
bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, transaction.unsignedTx.derivationPath, MPCv2PartiesEnum.USER);
1076+
await bitgoDsg.initDsg(bitgoKeyShare, messageBuffer, transaction.unsignedTx.derivationPath, MPCv2PartiesEnum.USER);
10771077

10781078
const txRequestRound1 = await signBitgoEddsaRound1(
10791079
bitgoDsg,
@@ -1782,7 +1782,7 @@ describe('signRecoveryEddsaMPCv2', () => {
17821782
const message = Buffer.from('deadbeef', 'hex');
17831783
const commonKeyChain = userDkg.getCommonKeychain();
17841784

1785-
const signature = EDDSAUtils.signRecoveryEddsaMPCv2(
1785+
const signature = await EDDSAUtils.signRecoveryEddsaMPCv2(
17861786
message,
17871787
derivationPath,
17881788
userDkg.getKeyShare(),
@@ -1803,7 +1803,7 @@ describe('signRecoveryEddsaMPCv2', () => {
18031803
const message = Buffer.from('deadbeef', 'hex');
18041804
const commonKeyChain = userDkg.getCommonKeychain();
18051805

1806-
const signature = EDDSAUtils.signRecoveryEddsaMPCv2(
1806+
const signature = await EDDSAUtils.signRecoveryEddsaMPCv2(
18071807
message,
18081808
derivationPath,
18091809
userDkg.getKeyShare(),
@@ -1827,15 +1827,14 @@ describe('signRecoveryEddsaMPCv2', () => {
18271827
const [wrongDkg] = await MPSUtil.generateEdDsaDKGKeyShares();
18281828
const message = Buffer.from('deadbeef', 'hex');
18291829

1830-
assert.throws(
1831-
() =>
1832-
EDDSAUtils.signRecoveryEddsaMPCv2(
1833-
message,
1834-
derivationPath,
1835-
userDkg.getKeyShare(),
1836-
backupDkg.getKeyShare(),
1837-
wrongDkg.getCommonKeychain() // key chain from a different wallet
1838-
),
1830+
await assert.rejects(
1831+
EDDSAUtils.signRecoveryEddsaMPCv2(
1832+
message,
1833+
derivationPath,
1834+
userDkg.getKeyShare(),
1835+
backupDkg.getKeyShare(),
1836+
wrongDkg.getCommonKeychain() // key chain from a different wallet
1837+
),
18391838
/EdDSA MPCv2 recovery signature verification failed/
18401839
);
18411840
});

0 commit comments

Comments
 (0)