Skip to content

Commit 56c12e5

Browse files
committed
feat(sdk-core): create message sign request
TICKET: COIN-4593
1 parent 2ad9e03 commit 56c12e5

File tree

4 files changed

+146
-23
lines changed

4 files changed

+146
-23
lines changed

modules/bitgo/test/v2/unit/wallet.ts

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,33 @@ import * as nock from 'nock';
99
import * as _ from 'lodash';
1010

1111
import {
12+
BaseTssUtils,
1213
common,
1314
CustomSigningFunction,
15+
Ecdsa,
1416
ECDSAUtils,
1517
EDDSAUtils,
18+
GetUserPrvOptions,
19+
Keychains,
20+
KeyType,
21+
ManageUnspentsOptions,
22+
MessageTypes,
23+
PopulatedIntent,
24+
PrebuildTransactionWithIntentOptions,
1625
RequestTracer,
26+
SendManyOptions,
27+
SignatureShareType,
28+
SignedMessage,
29+
SignTypedDataVersion,
1730
TokenType,
1831
TssUtils,
1932
TxRequest,
20-
Wallet,
21-
SignatureShareType,
22-
Ecdsa,
23-
Keychains,
33+
TxRequestVersion,
2434
TypedData,
2535
TypedMessage,
26-
MessageTypes,
27-
SignTypedDataVersion,
28-
GetUserPrvOptions,
29-
ManageUnspentsOptions,
30-
SignedMessage,
31-
BaseTssUtils,
32-
KeyType,
33-
SendManyOptions,
34-
PopulatedIntent,
35-
TxRequestVersion,
36+
Wallet,
3637
WalletSignMessageOptions,
3738
WalletSignTypedDataOptions,
38-
PrebuildTransactionWithIntentOptions,
3939
} from '@bitgo/sdk-core';
4040

4141
import { TestBitGo } from '@bitgo/sdk-test';
@@ -3467,14 +3467,24 @@ describe('V2 Wallet:', function () {
34673467
nock.cleanAll();
34683468
});
34693469

3470-
it('should throw error for unsupported coins', async function () {
3471-
await tssSolWallet
3472-
.signMessage({
3473-
reqId,
3474-
message: { messageRaw },
3475-
prv: 'secretKey',
3476-
})
3477-
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
3470+
describe('should throw error for unsupported coins', function () {
3471+
it('sol signMessage', async function () {
3472+
await tssSolWallet
3473+
.signMessage({
3474+
reqId,
3475+
message: { messageRaw },
3476+
prv: 'secretKey',
3477+
})
3478+
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
3479+
});
3480+
3481+
it('sol create signMessage tx request', async function () {
3482+
await tssSolWallet
3483+
.createSignMessageRequest({
3484+
messageRaw,
3485+
})
3486+
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
3487+
});
34783488
});
34793489

34803490
messageSigningCoins.map((coinName) => {
@@ -3483,6 +3493,15 @@ describe('V2 Wallet:', function () {
34833493
tssEthWallet = new Wallet(bitgo, bitgo.coin(coinName), ethWalletData);
34843494
const txRequestId = txRequestForMessageSigning.txRequestId;
34853495

3496+
it('should create tx Request with signMessage intent', async function () {
3497+
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/msgrequests`).reply(200, txRequestForMessageSigning);
3498+
3499+
const txRequest = await tssEthWallet.createSignMessageRequest({
3500+
messageRaw,
3501+
});
3502+
txRequest.should.deepEqual(txRequestForMessageSigning);
3503+
});
3504+
34863505
it('should sign message', async function () {
34873506
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
34883507
nock(bgUrl)

modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import { RequestTracer } from '../util';
4343
import * as openpgp from 'openpgp';
4444
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
4545
import { getBitgoGpgPubKey } from '../opengpgUtils';
46+
import assert from 'assert';
4647

4748
/**
4849
* BaseTssUtil class which different signature schemes have to extend
@@ -379,6 +380,35 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
379380
return this.createTxRequestBase(intentOptions, apiVersion, preview, params.reqId);
380381
}
381382

383+
/**
384+
* Create a sign message request
385+
*
386+
* @param params - the parameters for the sign message request
387+
* @param apiVersion - the API version to use, defaults to 'full'
388+
*/
389+
async createSignMessageRequest(
390+
params: IntentOptionsForMessage,
391+
apiVersion: TxRequestVersion = 'full'
392+
): Promise<TxRequest> {
393+
assert(
394+
params.intentType === 'signMessage',
395+
'Intent type must be signMessage for createMsgRequestWithSignMessageIntent'
396+
);
397+
const intent: PopulatedIntentForMessageSigning = {
398+
custodianMessageId: params.custodianMessageId,
399+
intentType: params.intentType,
400+
sequenceId: params.sequenceId,
401+
comment: params.comment,
402+
memo: params.memo?.value,
403+
isTss: params.isTss,
404+
messageRaw: params.messageRaw,
405+
messageStandardType: params.messageStandardType,
406+
messageEncoded: params.messageEncoded ?? '',
407+
};
408+
409+
return this.createSignMessageRequestBase(intent, apiVersion, params.reqId);
410+
}
411+
382412
/**
383413
* Create a tx request from params for type data signing
384414
*
@@ -432,6 +462,31 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
432462
.result();
433463
}
434464

465+
/**
466+
* Calls Bitgo API to create msg request.
467+
*
468+
* @private
469+
*/
470+
private async createSignMessageRequestBase(
471+
intent: PopulatedIntentForMessageSigning,
472+
apiVersion: TxRequestVersion,
473+
reqId?: IRequestTracer
474+
): Promise<TxRequest> {
475+
const whitelistedParams = {
476+
intent: {
477+
...intent,
478+
},
479+
apiVersion,
480+
};
481+
482+
const reqTracer = reqId || new RequestTracer();
483+
this.bitgo.setRequestTracer(reqTracer);
484+
return this.bitgo
485+
.post(this.bitgo.url(`/wallet/${this.wallet.id()}/msgrequests`, 2))
486+
.send(whitelistedParams)
487+
.result();
488+
}
489+
435490
/**
436491
* Call delete signature shares for a txRequest, the endpoint delete the signatures and return them
437492
*

modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { KeyShare } from './ecdsa';
99
import { EcdsaTypes } from '@bitgo/sdk-lib-mpc';
1010
import { TssEcdsaStep1ReturnMessage, TssEcdsaStep2ReturnMessage, TxRequestChallengeResponse } from '../../tss/types';
1111
import { AShare, DShare, SShare } from '../../tss/ecdsa/types';
12+
import { MessageStandardType } from '../messageTypes';
1213

1314
export type TxRequestVersion = 'full' | 'lite';
1415
export interface HopParams {
@@ -172,6 +173,7 @@ interface IntentOptionsBase {
172173
export interface IntentOptionsForMessage extends IntentOptionsBase {
173174
messageRaw: string;
174175
messageEncoded?: string;
176+
messageStandardType?: MessageStandardType;
175177
}
176178

177179
export interface IntentOptionsForTypedData extends IntentOptionsBase {
@@ -226,6 +228,7 @@ export interface PopulatedIntentForMessageSigning extends PopulatedIntentBase {
226228
messageRaw: string;
227229
messageEncoded: string;
228230
custodianMessageId?: string;
231+
messageStandardType?: MessageStandardType;
229232
}
230233

231234
export interface PopulatedIntentForTypedDataSigning extends PopulatedIntentBase {

modules/sdk-core/src/bitgo/wallet/wallet.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
inferAddressType,
3535
IntentOptionsForMessage,
3636
IntentOptionsForTypedData,
37+
MessageStandardType,
3738
RequestTracer,
3839
RequestType,
3940
TokenTransferRecipientParams,
@@ -2093,6 +2094,51 @@ export class Wallet implements IWallet {
20932094
return this.signMessageTss(presign);
20942095
}
20952096

2097+
/**
2098+
* Prepares and creates a sign message request for TSS wallets, that can be used later for signing.
2099+
*
2100+
* @param params - Parameters for creating the sign message request
2101+
* @returns Promise<TxRequest> - The created transaction request for signing a message
2102+
*/
2103+
async createSignMessageRequest(params: {
2104+
messageRaw: string;
2105+
messageStandardType?: MessageStandardType;
2106+
custodianMessageId?: string;
2107+
reqId?: RequestTracer;
2108+
}): Promise<TxRequest> {
2109+
if (this._wallet.multisigType !== 'tss') {
2110+
throw new Error('Message signing only supported for TSS wallets');
2111+
}
2112+
2113+
if (!this.baseCoin.supportsMessageSigning()) {
2114+
throw new Error(`Message signing not supported for ${this.baseCoin.getFullName()}`);
2115+
}
2116+
2117+
if (!params.messageRaw) {
2118+
throw new Error('message required to create message sign request');
2119+
}
2120+
2121+
const reqId = params.reqId || new RequestTracer();
2122+
2123+
try {
2124+
const intentOption: IntentOptionsForMessage = {
2125+
custodianMessageId: params.custodianMessageId,
2126+
reqId,
2127+
intentType: 'signMessage',
2128+
isTss: true,
2129+
messageRaw: params.messageRaw,
2130+
messageStandardType: params.messageStandardType,
2131+
};
2132+
2133+
if (!this.tssUtils) {
2134+
throw new Error('TSS utilities not available for this wallet');
2135+
}
2136+
return await this.tssUtils.createSignMessageRequest(intentOption);
2137+
} catch (error) {
2138+
throw new Error(`Failed to create message sign request: ${error}`);
2139+
}
2140+
}
2141+
20962142
/**
20972143
* Get the user private key from either a derivation or an encrypted keychain
20982144
* @param [params.keychain / params.key] (object) or params.prv (string)

0 commit comments

Comments
 (0)