Skip to content

Commit f4288f9

Browse files
committed
chore(mbe): refactoe sign and send for mpc
Ticket: WP-5245
1 parent 16a6dbb commit f4288f9

File tree

7 files changed

+222
-193
lines changed

7 files changed

+222
-193
lines changed

src/__tests__/api/master/ecdsa.test.ts

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import {
1616
TransactionState,
1717
} from '@bitgo/sdk-core';
1818
import { EnclavedExpressClient } from '../../../../src/api/master/clients/enclavedExpressClient';
19-
import { handleEcdsaMPCv2Signing } from '../../../api/master/handlers/ecdsaMPCv2';
19+
import { signAndSendEcdsaMPCv2FromTxRequest } from '../../../api/master/handlers/ecdsaMPCv2';
2020
import { BitGo } from 'bitgo';
2121
import { readKey } from 'openpgp';
2222

@@ -69,7 +69,7 @@ describe('Ecdsa Signing Handler', () => {
6969
it('should successfully sign an ECDSA MPCv2 transaction', async () => {
7070
const txRequest: TxRequest = {
7171
txRequestId: 'test-tx-request-id',
72-
apiVersion: '2.0.0' as TxRequestVersion,
72+
apiVersion: 'full',
7373
enterpriseId: 'test-enterprise-id',
7474
transactions: [],
7575
state: 'pendingUserSignature',
@@ -89,15 +89,6 @@ describe('Ecdsa Signing Handler', () => {
8989
const pgpKey = await readKey({ armoredKey: bitgoGpgKey.publicKey });
9090
sinon.stub(EcdsaMPCv2Utils.prototype, 'getBitgoMpcv2PublicGpgKey').resolves(pgpKey);
9191

92-
// Mock getTxRequest call
93-
const getTxRequestNock = nock(bitgoApiUrl)
94-
.get(`/api/v2/wallet/${walletId}/txrequests`)
95-
.query({ txRequestIds: 'test-tx-request-id', latest: true })
96-
.matchHeader('any', () => true)
97-
.reply(200, {
98-
txRequests: [txRequest],
99-
});
100-
10192
// Mock sendSignatureShareV2 calls for each round
10293
const round1SignatureShare: SignatureShareRecord = {
10394
from: SignatureShareType.USER,
@@ -238,10 +229,10 @@ describe('Ecdsa Signing Handler', () => {
238229
signatureShareRound3: round3SignatureShare,
239230
});
240231

241-
const result = await handleEcdsaMPCv2Signing(
232+
const result = await signAndSendEcdsaMPCv2FromTxRequest(
242233
bitgo,
243234
wallet,
244-
txRequest.txRequestId,
235+
txRequest,
245236
enclavedExpressClient,
246237
'user',
247238
userPubKey,
@@ -253,7 +244,6 @@ describe('Ecdsa Signing Handler', () => {
253244
state: 'signed',
254245
});
255246

256-
getTxRequestNock.done();
257247
sendSignatureShareV2Round1Nock.done();
258248
sendSignatureShareV2Round2Nock.done();
259249
sendSignatureShareV2Round3Nock.done();

src/__tests__/api/master/eddsa.test.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ import { handleEddsaSigning } from '../../../../src/api/master/handlers/eddsa';
1717
import { BitGo } from 'bitgo';
1818
import { readKey } from 'openpgp';
1919

20-
describe('Eddsa Signing Handler', () => {
20+
// TODO: Re-enable once using EDDSA Custom signing fns
21+
xdescribe('Eddsa Signing Handler', () => {
2122
let bitgo: BitGoBase;
2223
let wallet: Wallet;
2324
let enclavedExpressClient: EnclavedExpressClient;
@@ -62,9 +63,30 @@ describe('Eddsa Signing Handler', () => {
6263
it('should successfully sign an Eddsa transaction', async () => {
6364
const txRequest: TxRequest = {
6465
txRequestId: 'test-tx-request-id',
65-
apiVersion: '2.0.0' as TxRequestVersion,
66+
apiVersion: 'full',
6667
enterpriseId: 'test-enterprise-id',
67-
transactions: [],
68+
transactions: [
69+
{
70+
state: 'pendingSignature',
71+
unsignedTx: {
72+
derivationPath: 'm/0',
73+
signableHex: 'testMessage',
74+
serializedTxHex: 'testSerializedTxHex',
75+
},
76+
signatureShares: [
77+
{
78+
share: 'bitgo-to-user-r-share',
79+
from: 'bitgo',
80+
to: 'user',
81+
},
82+
{
83+
share: 'user-to-bitgo-r-share',
84+
from: 'user',
85+
to: 'bitgo',
86+
},
87+
],
88+
},
89+
],
6890
state: 'pendingUserSignature',
6991
walletId: 'test-wallet-id',
7092
walletType: 'hot',
@@ -234,7 +256,7 @@ describe('Eddsa Signing Handler', () => {
234256
const result = await handleEddsaSigning(
235257
bitgo,
236258
wallet,
237-
txRequest.txRequestId,
259+
txRequest,
238260
enclavedExpressClient,
239261
userPubKey,
240262
reqId,

src/__tests__/api/master/sendMany.test.ts

Lines changed: 72 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
1515
let agent: request.SuperAgentTest;
1616
const enclavedExpressUrl = 'http://enclaved.invalid';
1717
const bitgoApiUrl = Environments.test.uri;
18-
const coin = 'tbtc';
1918
const accessToken = 'test-token';
2019
const walletId = 'test-wallet-id';
20+
const coin = 'tbtc';
2121

2222
before(() => {
2323
nock.disableNetConnect();
@@ -49,6 +49,7 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
4949
});
5050

5151
describe('SendMany Multisig:', () => {
52+
const coin = 'tbtc';
5253
it('should send many transactions by calling the enclaved express service', async () => {
5354
// Mock wallet get request
5455
const walletGetNock = nock(bitgoApiUrl)
@@ -226,7 +227,27 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
226227
});
227228

228229
describe('SendMany TSS EDDSA:', () => {
230+
const coin = 'tsol';
229231
it('should send many transactions using EDDSA TSS signing', async () => {
232+
const mockTxRequest = {
233+
txRequestId: 'test-tx-request-id',
234+
state: 'signed',
235+
apiVersion: 'full',
236+
pendingApprovalId: 'test-pending-approval-id',
237+
transactions: [
238+
{
239+
unsignedTx: {
240+
derivationPath: 'm/0',
241+
signableHex: 'testMessage',
242+
},
243+
signedTx: {
244+
id: 'test-tx-id',
245+
tx: 'signed-transaction',
246+
},
247+
},
248+
],
249+
};
250+
230251
// Mock wallet get request for TSS wallet
231252
const walletGetNock = nock(bitgoApiUrl)
232253
.get(`/api/v2/${coin}/wallet/${walletId}`)
@@ -262,34 +283,28 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
262283
walletId,
263284
});
264285

265-
const verifyStub = sinon.stub(Coin.Btc.prototype, 'verifyTransaction').resolves(true);
286+
const verifyStub = sinon.stub(Coin.Tsol.prototype, 'verifyTransaction').resolves(true);
266287

267288
// Mock multisigType to return 'tss'
268289
const multisigTypeStub = sinon.stub(Wallet.prototype, 'multisigType').returns('tss');
269290

270-
// Mock getMPCAlgorithm to return 'eddsa'
271-
const getMPCAlgorithmStub = sinon
272-
.stub(Coin.Btc.prototype, 'getMPCAlgorithm')
273-
.returns('eddsa');
274-
275291
// Mock handleEddsaSigning
276292
const handleEddsaSigningStub = sinon.stub().resolves({
277-
txRequestId: 'test-tx-request-id',
278-
state: 'signed',
279-
apiVersion: 'full',
280-
transactions: [
281-
{
282-
signedTx: {
283-
id: 'test-tx-id',
284-
tx: 'signed-transaction',
285-
},
286-
},
287-
],
293+
...mockTxRequest,
288294
});
289295

290-
// Import and stub the handleEddsaSigning function
296+
// Import and stub the signAndSendTxRequests function
291297
sinon.stub(eddsa, 'handleEddsaSigning').callsFake(handleEddsaSigningStub);
292298

299+
// Mock getTxRequest call
300+
const getTxRequestNock = nock(bitgoApiUrl)
301+
.get(`/api/v2/wallet/${walletId}/txrequests`)
302+
.query({ txRequestIds: 'test-tx-request-id', latest: true })
303+
.matchHeader('any', () => true)
304+
.reply(200, {
305+
txRequests: [mockTxRequest],
306+
});
307+
293308
const response = await agent
294309
.post(`/api/${coin}/wallet/${walletId}/sendMany`)
295310
.set('Authorization', `Bearer ${accessToken}`)
@@ -317,13 +332,14 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
317332
keychainGetNock.done();
318333
sinon.assert.calledOnce(prebuildStub);
319334
sinon.assert.calledOnce(verifyStub);
320-
sinon.assert.calledTwice(multisigTypeStub);
321-
sinon.assert.calledOnce(getMPCAlgorithmStub);
335+
sinon.assert.calledThrice(multisigTypeStub);
322336
sinon.assert.calledOnce(handleEddsaSigningStub);
337+
getTxRequestNock.done();
323338
});
324339
});
325340

326341
describe('SendMany TSS ECDSA:', () => {
342+
const coin = 'hteth';
327343
it('should send many transactions using ECDSA TSS signing', async () => {
328344
// Mock wallet get request for TSS wallet
329345
const walletGetNock = nock(bitgoApiUrl)
@@ -349,45 +365,35 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
349365
type: 'tss',
350366
});
351367

352-
const prebuildStub = sinon.stub(Wallet.prototype, 'prebuildTransaction').resolves({
353-
txRequestId: 'test-tx-request-id',
354-
txHex: 'prebuilt-tx-hex',
355-
txInfo: {
356-
nP2SHInputs: 1,
357-
nSegwitInputs: 0,
358-
nOutputs: 2,
368+
const sendManyStub = sinon.stub(Wallet.prototype, 'sendMany').resolves({
369+
txRequest: {
370+
txRequestId: 'test-tx-request-id',
371+
state: 'signed',
372+
apiVersion: 'full',
373+
pendingApprovalId: 'test-pending-approval-id',
374+
transactions: [
375+
{
376+
state: 'signed',
377+
unsignedTx: {
378+
derivationPath: 'm/0',
379+
signableHex: 'testMessage',
380+
serializedTxHex: 'testSerializedTxHex',
381+
},
382+
signatureShares: [],
383+
signedTx: {
384+
id: 'test-tx-id',
385+
tx: 'signed-transaction',
386+
},
387+
},
388+
],
359389
},
360-
walletId,
390+
txid: 'test-tx-id',
391+
tx: 'signed-transaction',
361392
});
362393

363-
const verifyStub = sinon.stub(Coin.Btc.prototype, 'verifyTransaction').resolves(true);
364-
365394
// Mock multisigType to return 'tss'
366395
const multisigTypeStub = sinon.stub(Wallet.prototype, 'multisigType').returns('tss');
367396

368-
// Mock getMPCAlgorithm to return 'ecdsa'
369-
const getMPCAlgorithmStub = sinon
370-
.stub(Coin.Btc.prototype, 'getMPCAlgorithm')
371-
.returns('ecdsa');
372-
373-
// Mock handleEcdsaSigning
374-
const handleEcdsaMPCv2Signing = sinon.stub().resolves({
375-
txRequestId: 'test-tx-request-id',
376-
state: 'signed',
377-
apiVersion: 'full',
378-
transactions: [
379-
{
380-
signedTx: {
381-
id: 'test-tx-id',
382-
tx: 'signed-transaction',
383-
},
384-
},
385-
],
386-
});
387-
388-
// Import and stub the handleEcdsaSigning function
389-
sinon.stub(ecdsa, 'handleEcdsaMPCv2Signing').callsFake(handleEcdsaMPCv2Signing);
390-
391397
const response = await agent
392398
.post(`/api/${coin}/wallet/${walletId}/sendMany`)
393399
.set('Authorization', `Bearer ${accessToken}`)
@@ -413,11 +419,8 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
413419

414420
walletGetNock.done();
415421
keychainGetNock.done();
416-
sinon.assert.calledOnce(prebuildStub);
417-
sinon.assert.calledOnce(verifyStub);
418-
sinon.assert.calledTwice(multisigTypeStub);
419-
sinon.assert.calledOnce(getMPCAlgorithmStub);
420-
sinon.assert.calledOnce(handleEcdsaMPCv2Signing);
422+
sinon.assert.calledOnce(sendManyStub);
423+
sinon.assert.calledOnce(multisigTypeStub);
421424
});
422425

423426
it('should fail when backup key is used for ECDSA TSS signing', async () => {
@@ -445,19 +448,17 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
445448
type: 'tss',
446449
});
447450

448-
const prebuildStub = sinon.stub(Wallet.prototype, 'prebuildTransaction').resolves({
449-
txRequestId: 'test-tx-request-id',
450-
txHex: 'prebuilt-tx-hex',
451-
txInfo: {
452-
nP2SHInputs: 1,
453-
nSegwitInputs: 0,
454-
nOutputs: 2,
451+
const sendManyStub = sinon.stub(Wallet.prototype, 'sendMany').resolves({
452+
txRequest: {
453+
txRequestId: 'test-tx-request-id',
454+
state: 'signed',
455+
apiVersion: 'full',
456+
pendingApprovalId: 'test-pending-approval-id',
455457
},
456-
walletId,
458+
txid: 'test-tx-id',
459+
tx: 'signed-transaction',
457460
});
458461

459-
const verifyStub = sinon.stub(Coin.Btc.prototype, 'verifyTransaction').resolves(true);
460-
461462
// Mock multisigType to return 'tss'
462463
const multisigTypeStub = sinon.stub(Wallet.prototype, 'multisigType').returns('tss');
463464

@@ -480,9 +481,8 @@ describe('POST /api/:coin/wallet/:walletId/sendmany', () => {
480481

481482
walletGetNock.done();
482483
keychainGetNock.done();
483-
sinon.assert.calledOnce(prebuildStub);
484-
sinon.assert.calledOnce(verifyStub);
485-
sinon.assert.calledTwice(multisigTypeStub);
484+
sinon.assert.notCalled(sendManyStub);
485+
sinon.assert.calledOnce(multisigTypeStub);
486486
});
487487
});
488488

src/api/master/handlers/ecdsaMPCv2.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,23 +79,22 @@ export function createEcdsaMPCv2CustomSigners(
7979
};
8080
}
8181

82-
export async function handleEcdsaMPCv2Signing(
82+
export async function signAndSendEcdsaMPCv2FromTxRequest(
8383
bitgo: BitGoBase,
8484
wallet: Wallet,
85-
txRequestId: string,
85+
txRequest: TxRequest,
8686
enclavedExpressClient: EnclavedExpressClient,
8787
source: 'user' | 'backup',
8888
commonKeychain: string,
8989
reqId: IRequestTracer,
9090
): Promise<TxRequest> {
9191
const ecdsaMPCv2Utils = new EcdsaMPCv2Utils(bitgo, wallet.baseCoin, wallet);
92-
const txRequest = await getTxRequest(bitgo, wallet.id(), txRequestId, reqId);
9392

9493
// Use the shared custom signing functions
9594
const { customMPCv2Round1Generator, customMPCv2Round2Generator, customMPCv2Round3Generator } =
9695
createEcdsaMPCv2CustomSigners(enclavedExpressClient, source, commonKeychain);
9796

98-
// Use the existing signEcdsaMPCv2TssUsingExternalSigner method with our custom signers
97+
// This also sends the TxRequest for broadcast
9998
return await ecdsaMPCv2Utils.signEcdsaMPCv2TssUsingExternalSigner(
10099
{ txRequest, reqId },
101100
customMPCv2Round1Generator,

0 commit comments

Comments
 (0)