Skip to content

Commit 5fd89e2

Browse files
committed
feat(sdk-core): create message sign request
TICKET: COIN-4593
1 parent 114557f commit 5fd89e2

File tree

4 files changed

+165
-23
lines changed

4 files changed

+165
-23
lines changed

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

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,33 +9,34 @@ 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+
MessageType,
23+
MessageTypes,
24+
PopulatedIntent,
25+
PrebuildTransactionWithIntentOptions,
1626
RequestTracer,
27+
SendManyOptions,
28+
SignatureShareType,
29+
SignedMessage,
30+
SignTypedDataVersion,
1731
TokenType,
1832
TssUtils,
1933
TxRequest,
20-
Wallet,
21-
SignatureShareType,
22-
Ecdsa,
23-
Keychains,
34+
TxRequestVersion,
2435
TypedData,
2536
TypedMessage,
26-
MessageTypes,
27-
SignTypedDataVersion,
28-
GetUserPrvOptions,
29-
ManageUnspentsOptions,
30-
SignedMessage,
31-
BaseTssUtils,
32-
KeyType,
33-
SendManyOptions,
34-
PopulatedIntent,
35-
TxRequestVersion,
37+
Wallet,
3638
WalletSignMessageOptions,
3739
WalletSignTypedDataOptions,
38-
PrebuildTransactionWithIntentOptions,
3940
} from '@bitgo/sdk-core';
4041

4142
import { TestBitGo } from '@bitgo/sdk-test';
@@ -3467,14 +3468,25 @@ describe('V2 Wallet:', function () {
34673468
nock.cleanAll();
34683469
});
34693470

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');
3471+
describe('should throw error for unsupported coins', function () {
3472+
it('sol signMessage', async function () {
3473+
await tssSolWallet
3474+
.signMessage({
3475+
reqId,
3476+
message: { messageRaw },
3477+
prv: 'secretKey',
3478+
})
3479+
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
3480+
});
3481+
3482+
it('sol create signMessage tx request', async function () {
3483+
await tssSolWallet
3484+
.createSignMessageRequest<string>({
3485+
message: messageRaw,
3486+
messageType: MessageType.STRING,
3487+
})
3488+
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
3489+
});
34783490
});
34793491

34803492
messageSigningCoins.map((coinName) => {
@@ -3483,6 +3495,16 @@ describe('V2 Wallet:', function () {
34833495
tssEthWallet = new Wallet(bitgo, bitgo.coin(coinName), ethWalletData);
34843496
const txRequestId = txRequestForMessageSigning.txRequestId;
34853497

3498+
it('should create tx Request with signMessage intent', async function () {
3499+
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/msgrequests`).reply(200, txRequestForMessageSigning);
3500+
3501+
const txRequest = await tssEthWallet.createSignMessageRequest<string>({
3502+
message: messageRaw,
3503+
messageType: MessageType.STRING,
3504+
});
3505+
txRequest.should.deepEqual(txRequestForMessageSigning);
3506+
});
3507+
34863508
it('should sign message', async function () {
34873509
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
34883510
nock(bgUrl)

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

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,15 @@ import {
3737
CustomMPCv2SigningRound2GeneratingFunction,
3838
CustomMPCv2SigningRound3GeneratingFunction,
3939
TSSParamsWithPrv,
40+
MessageIntent,
41+
MessageIntentOptions,
4042
} from './baseTypes';
4143
import { GShare, SignShare } from '../../../account-lib/mpc/tss';
4244
import { RequestTracer } from '../util';
4345
import * as openpgp from 'openpgp';
4446
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
4547
import { getBitgoGpgPubKey } from '../opengpgUtils';
48+
import assert from 'assert';
4649

4750
/**
4851
* BaseTssUtil class which different signature schemes have to extend
@@ -379,6 +382,34 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
379382
return this.createTxRequestBase(intentOptions, apiVersion, preview, params.reqId);
380383
}
381384

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

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

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ export enum MPCType {
5454
ECDSA = 'ecdsa',
5555
}
5656

57+
export enum MessageType {
58+
STRING = 'string',
59+
EIP712 = 'eip712',
60+
CIP8 = 'cip8',
61+
}
62+
5763
export interface CustomPaillierModulusGetterFunction {
5864
(params: { txRequest: TxRequest }): Promise<{
5965
userPaillierModulus: string;
@@ -174,6 +180,11 @@ export interface IntentOptionsForMessage extends IntentOptionsBase {
174180
messageEncoded?: string;
175181
}
176182

183+
export interface MessageIntentOptions<TMessage = string | Record<string, unknown>> extends IntentOptionsBase {
184+
message: TMessage;
185+
messageType: MessageType | string;
186+
}
187+
177188
export interface IntentOptionsForTypedData extends IntentOptionsBase {
178189
typedDataRaw: string;
179190
typedDataEncoded?: string;
@@ -228,6 +239,12 @@ export interface PopulatedIntentForMessageSigning extends PopulatedIntentBase {
228239
custodianMessageId?: string;
229240
}
230241

242+
export interface MessageIntent<TMessage = string | Record<string, unknown>> extends PopulatedIntentBase {
243+
message: TMessage;
244+
messageType: MessageType | string;
245+
custodianMessageId?: string;
246+
}
247+
231248
export interface PopulatedIntentForTypedDataSigning extends PopulatedIntentBase {
232249
messageRaw: string;
233250
messageEncoded: string;

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
inferAddressType,
3535
IntentOptionsForMessage,
3636
IntentOptionsForTypedData,
37+
MessageIntentOptions,
38+
MessageType,
3739
RequestTracer,
3840
RequestType,
3941
TokenTransferRecipientParams,
@@ -2093,6 +2095,51 @@ export class Wallet implements IWallet {
20932095
return this.signMessageTss(presign);
20942096
}
20952097

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

0 commit comments

Comments
 (0)