Skip to content

feat(wallet): create message sign request #6324

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 45 additions & 23 deletions modules/bitgo/test/v2/unit/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,34 @@ import * as nock from 'nock';
import * as _ from 'lodash';

import {
BaseTssUtils,
common,
CustomSigningFunction,
Ecdsa,
ECDSAUtils,
EDDSAUtils,
GetUserPrvOptions,
Keychains,
KeyType,
ManageUnspentsOptions,
MessageType,
MessageTypes,
PopulatedIntent,
PrebuildTransactionWithIntentOptions,
RequestTracer,
SendManyOptions,
SignatureShareType,
SignedMessage,
SignTypedDataVersion,
TokenType,
TssUtils,
TxRequest,
Wallet,
SignatureShareType,
Ecdsa,
Keychains,
TxRequestVersion,
TypedData,
TypedMessage,
MessageTypes,
SignTypedDataVersion,
GetUserPrvOptions,
ManageUnspentsOptions,
SignedMessage,
BaseTssUtils,
KeyType,
SendManyOptions,
PopulatedIntent,
TxRequestVersion,
Wallet,
WalletSignMessageOptions,
WalletSignTypedDataOptions,
PrebuildTransactionWithIntentOptions,
} from '@bitgo/sdk-core';

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

it('should throw error for unsupported coins', async function () {
await tssSolWallet
.signMessage({
reqId,
message: { messageRaw },
prv: 'secretKey',
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
describe('should throw error for unsupported coins', function () {
it('sol signMessage', async function () {
await tssSolWallet
.signMessage({
reqId,
message: { messageRaw },
prv: 'secretKey',
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
});

it('sol create signMessage tx request', async function () {
await tssSolWallet
.createSignMessageRequest({
messageRaw,
messageType: MessageType.STRING,
})
.should.be.rejectedWith('Message signing not supported for Testnet Solana');
});
});

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

it('should create tx Request with signMessage intent', async function () {
nock(bgUrl).post(`/api/v2/wallet/${tssEthWallet.id()}/msgrequests`).reply(200, txRequestForMessageSigning);

const txRequest = await tssEthWallet.createSignMessageRequest({
messageRaw,
messageType: MessageType.STRING,
});
txRequest.should.deepEqual(txRequestForMessageSigning);
});

it('should sign message', async function () {
const signMessageTssSpy = sinon.spy(tssEthWallet, 'signMessageTss' as any);
nock(bgUrl)
Expand Down
55 changes: 55 additions & 0 deletions modules/sdk-core/src/bitgo/utils/tss/baseTSSUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { RequestTracer } from '../util';
import * as openpgp from 'openpgp';
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
import { getBitgoGpgPubKey } from '../opengpgUtils';
import assert from 'assert';

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

/**
* Create a sign message request
*
* @param params - the parameters for the sign message request
* @param apiVersion - the API version to use, defaults to 'full'
*/
async createSignMessageRequest(
params: IntentOptionsForMessage,
apiVersion: TxRequestVersion = 'full'
): Promise<TxRequest> {
assert(
params.intentType === 'signMessage',
'Intent type must be signMessage for createMsgRequestWithSignMessageIntent'
);
const intent: PopulatedIntentForMessageSigning = {
custodianMessageId: params.custodianMessageId,
intentType: params.intentType,
sequenceId: params.sequenceId,
comment: params.comment,
memo: params.memo?.value,
isTss: params.isTss,
messageRaw: params.messageRaw,
messageType: params.messageType,
messageEncoded: params.messageEncoded ?? '',
};

return this.createSignMessageRequestBase(intent, apiVersion, params.reqId);
}

/**
* Create a tx request from params for type data signing
*
Expand Down Expand Up @@ -432,6 +462,31 @@ export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtil
.result();
}

/**
* Calls Bitgo API to create msg request.
*
* @private
*/
private async createSignMessageRequestBase(
intent: PopulatedIntentForMessageSigning,
apiVersion: TxRequestVersion,
reqId?: IRequestTracer
): Promise<TxRequest> {
const whitelistedParams = {
intent: {
...intent,
},
apiVersion,
};

const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
return this.bitgo
.post(this.bitgo.url(`/wallet/${this.wallet.id()}/msgrequests`, 2))
.send(whitelistedParams)
.result();
}

/**
* Call delete signature shares for a txRequest, the endpoint delete the signatures and return them
*
Expand Down
8 changes: 8 additions & 0 deletions modules/sdk-core/src/bitgo/utils/tss/baseTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ export enum MPCType {
ECDSA = 'ecdsa',
}

export enum MessageType {
STRING = 'string',
EIP712 = 'eip712',
CIP8 = 'cip8',
}

export interface CustomPaillierModulusGetterFunction {
(params: { txRequest: TxRequest }): Promise<{
userPaillierModulus: string;
Expand Down Expand Up @@ -172,6 +178,7 @@ interface IntentOptionsBase {
export interface IntentOptionsForMessage extends IntentOptionsBase {
messageRaw: string;
messageEncoded?: string;
messageType?: MessageType | string;
}

export interface IntentOptionsForTypedData extends IntentOptionsBase {
Expand Down Expand Up @@ -226,6 +233,7 @@ export interface PopulatedIntentForMessageSigning extends PopulatedIntentBase {
messageRaw: string;
messageEncoded: string;
custodianMessageId?: string;
messageType?: MessageType | string;
}

export interface PopulatedIntentForTypedDataSigning extends PopulatedIntentBase {
Expand Down
46 changes: 46 additions & 0 deletions modules/sdk-core/src/bitgo/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
inferAddressType,
IntentOptionsForMessage,
IntentOptionsForTypedData,
MessageType,
RequestTracer,
RequestType,
TokenTransferRecipientParams,
Expand Down Expand Up @@ -2093,6 +2094,51 @@ export class Wallet implements IWallet {
return this.signMessageTss(presign);
}

/**
* Prepares and creates a sign message request for TSS wallets, that can be used later for signing.
*
* @param params - Parameters for creating the sign message request
* @returns Promise<TxRequest> - The created transaction request for signing a message
*/
async createSignMessageRequest(params: {
messageRaw: string;
messageType?: MessageType | string;
custodianMessageId?: string;
reqId?: RequestTracer;
}): Promise<TxRequest> {
if (this._wallet.multisigType !== 'tss') {
throw new Error('Message signing only supported for TSS wallets');
}

if (!this.baseCoin.supportsMessageSigning()) {
throw new Error(`Message signing not supported for ${this.baseCoin.getFullName()}`);
}

if (!params.messageRaw) {
throw new Error('message required to create message sign request');
}

const reqId = params.reqId || new RequestTracer();

try {
const intentOption: IntentOptionsForMessage = {
custodianMessageId: params.custodianMessageId,
reqId,
intentType: 'signMessage',
isTss: true,
messageRaw: params.messageRaw,
messageType: params.messageType,
};

if (!this.tssUtils) {
throw new Error('TSS utilities not available for this wallet');
}
return await this.tssUtils.createSignMessageRequest(intentOption);
} catch (error) {
throw new Error(`Failed to create message sign request: ${error}`);
}
}

/**
* Get the user private key from either a derivation or an encrypted keychain
* @param [params.keychain / params.key] (object) or params.prv (string)
Expand Down