Skip to content
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
113 changes: 112 additions & 1 deletion modules/sdk-core/src/bitgo/utils/tss/eddsa/eddsaMPCv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@ import {
} from '../../../tss/eddsa/eddsaMPCv2';
import { generateGPGKeyPair } from '../../opengpgUtils';
import { MPCv2PartiesEnum } from '../ecdsa/typesMPCv2';
import { RequestType, SignatureShareType, TSSParamsForMessageWithPrv, TSSParamsWithPrv, TxRequest } from '../baseTypes';
import {
CustomEddsaMPCv2SigningRound1GeneratingFunction,
CustomEddsaMPCv2SigningRound2GeneratingFunction,
CustomEddsaMPCv2SigningRound3GeneratingFunction,
RequestType,
SignatureShareType,
TSSParams,
TSSParamsForMessage,
TSSParamsForMessageWithPrv,
TSSParamsWithPrv,
TxRequest,
} from '../baseTypes';
import { BaseEddsaUtils } from './base';
import { EddsaMPCv2KeyGenSendFn, KeyGenSenderForEnterprise } from './eddsaMPCv2KeyGenSender';

Expand Down Expand Up @@ -515,4 +526,104 @@ export class EddsaMPCv2Utils extends BaseEddsaUtils {
}

// #endregion

// #region external signer
/** @inheritdoc */
async signEddsaMPCv2TssUsingExternalSigner(
params: TSSParams | TSSParamsForMessage,
externalSignerEddsaMPCv2SigningRound1Generator: CustomEddsaMPCv2SigningRound1GeneratingFunction,
externalSignerEddsaMPCv2SigningRound2Generator: CustomEddsaMPCv2SigningRound2GeneratingFunction,
externalSignerEddsaMPCv2SigningRound3Generator: CustomEddsaMPCv2SigningRound3GeneratingFunction,
requestType: RequestType = RequestType.tx
): Promise<TxRequest> {
const { txRequest, reqId } = params;

// TODO(WP-2176): Add support for message signing
assert(
requestType === RequestType.tx,
'Only transaction signing is supported for external signer, got: ' + requestType
);

let txRequestResolved: TxRequest;
if (typeof txRequest === 'string') {
txRequestResolved = await getTxRequest(this.bitgo, this.wallet.id(), txRequest, reqId);
} else {
txRequestResolved = txRequest;
}

const bitgoPublicGpgKey = await this.pickBitgoPubGpgKeyForSigning(
true,
reqId,
txRequestResolved.enterpriseId,
true
);

if (!bitgoPublicGpgKey) {
throw new Error('Missing BitGo GPG key for MPCv2');
}

// round 1
const { signatureShareRound1, userGpgPubKey, encryptedRound1Session, encryptedUserGpgPrvKey } =
await externalSignerEddsaMPCv2SigningRound1Generator({ txRequest: txRequestResolved });
const round1TxRequest = await sendSignatureShareV2(
this.bitgo,
txRequestResolved.walletId,
txRequestResolved.txRequestId,
[signatureShareRound1],
requestType,
this.baseCoin.getMPCAlgorithm(),
userGpgPubKey,
undefined,
this.wallet.multisigTypeVersion(),
reqId
);

// round 2
const { signatureShareRound2, encryptedRound2Session } =
await externalSignerEddsaMPCv2SigningRound2Generator({
txRequest: round1TxRequest,
encryptedRound1Session,
encryptedUserGpgPrvKey,
bitgoPublicGpgKey: bitgoPublicGpgKey.armor(),
});
const round2TxRequest = await sendSignatureShareV2(
this.bitgo,
txRequestResolved.walletId,
txRequestResolved.txRequestId,
[signatureShareRound2],
requestType,
this.baseCoin.getMPCAlgorithm(),
userGpgPubKey,
undefined,
this.wallet.multisigTypeVersion(),
reqId
);
assert(
round2TxRequest.transactions && round2TxRequest.transactions[0].signatureShares,
'Missing signature shares in round 2 txRequest'
);

// round 3
const { signatureShareRound3 } = await externalSignerEddsaMPCv2SigningRound3Generator({
txRequest: round2TxRequest,
encryptedRound2Session,
encryptedUserGpgPrvKey,
bitgoPublicGpgKey: bitgoPublicGpgKey.armor(),
});
await sendSignatureShareV2(
this.bitgo,
txRequestResolved.walletId,
txRequestResolved.txRequestId,
[signatureShareRound3],
requestType,
this.baseCoin.getMPCAlgorithm(),
userGpgPubKey,
undefined,
this.wallet.multisigTypeVersion(),
reqId
);

return sendTxRequest(this.bitgo, txRequestResolved.walletId, txRequestResolved.txRequestId, requestType, reqId);
}
// #endregion
}
62 changes: 62 additions & 0 deletions modules/sdk-core/src/bitgo/wallet/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2165,6 +2165,15 @@ export class Wallet implements IWallet {
return this.signTransactionTssExternalSignerECDSA(this.baseCoin, params);
}

if (
_.isFunction(params.customEddsaMPCv2SigningRound1GenerationFunction) &&
_.isFunction(params.customEddsaMPCv2SigningRound2GenerationFunction) &&
_.isFunction(params.customEddsaMPCv2SigningRound3GenerationFunction)
) {
// invoke external signer TSS for EdDSA MPCv2 workflow
return this.signTransactionTssExternalSignerEdDSAMPCv2(this.baseCoin, params);
}

if (
_.isFunction(params.customMPCv2SigningRound1GenerationFunction) &&
_.isFunction(params.customMPCv2SigningRound2GenerationFunction) &&
Expand Down Expand Up @@ -4335,6 +4344,59 @@ export class Wallet implements IWallet {
}
}

/**
* Signs a transaction from a TSS EdDSA MPCv2 wallet using external signer.
*
* @param params signing options
*/
private async signTransactionTssExternalSignerEdDSAMPCv2(
coin: IBaseCoin,
params: WalletSignTransactionOptions = {}
): Promise<TxRequest> {
let txRequestId = '';
if (params.txRequestId) {
txRequestId = params.txRequestId;
} else if (params.txPrebuild && params.txPrebuild.txRequestId) {
txRequestId = params.txPrebuild.txRequestId;
} else {
throw new Error('TxRequestId required to sign TSS transactions with External Signer.');
}

if (!params.customEddsaMPCv2SigningRound1GenerationFunction) {
throw new Error(
'Generator function for EdDSA MPCv2 Round 1 share required to sign transactions with External Signer.'
);
}

if (!params.customEddsaMPCv2SigningRound2GenerationFunction) {
throw new Error(
'Generator function for EdDSA MPCv2 Round 2 share required to sign transactions with External Signer.'
);
}

if (!params.customEddsaMPCv2SigningRound3GenerationFunction) {
throw new Error(
'Generator function for EdDSA MPCv2 Round 3 share required to sign transactions with External Signer.'
);
}

try {
assert(this.tssUtils, 'tssUtils must be defined');
const signedTxRequest = await this.tssUtils.signEddsaMPCv2TssUsingExternalSigner(
{
txRequest: txRequestId,
reqId: params.reqId || new RequestTracer(),
},
params.customEddsaMPCv2SigningRound1GenerationFunction,
params.customEddsaMPCv2SigningRound2GenerationFunction,
params.customEddsaMPCv2SigningRound3GenerationFunction
);
return signedTxRequest;
} catch (e) {
throw new Error('failed to sign transaction ' + e);
}
}

/**
* Signs a transaction from a TSS ECDSA wallet using external signer.
*
Expand Down
Loading
Loading