PHP WebShell
Текущая директория: /opt/BitGoJS/modules/sdk-core/src/bitgo/utils/tss
Просмотр файла: baseTSSUtils.ts
import { IRequestTracer } from '../../../api';
import { Key, readKey, SerializedKeyPair } from 'openpgp';
import { IBaseCoin, KeychainsTriplet } from '../../baseCoin';
import { BitGoBase } from '../../bitgoBase';
import { Keychain, KeyIndices } from '../../keychain';
import { getTxRequest } from '../../tss';
import { IWallet } from '../../wallet';
import { MpcUtils } from '../mpcUtils';
import * as _ from 'lodash';
import {
BitgoGPGPublicKey,
BitgoHeldBackupKeyShare,
CustomGShareGeneratingFunction,
CustomRShareGeneratingFunction,
ITssUtils,
PrebuildTransactionWithIntentOptions,
SignatureShareRecord,
TSSParams,
TxRequest,
TxRequestVersion,
CreateKeychainParamsBase,
IntentOptionsForMessage,
PopulatedIntentForMessageSigning,
IntentOptionsForTypedData,
PopulatedIntentForTypedDataSigning,
CreateBitGoKeychainParamsBase,
CommitmentShareRecord,
EncryptedSignerShareRecord,
CustomCommitmentGeneratingFunction,
TSSParamsForMessage,
RequestType,
CustomPaillierModulusGetterFunction,
CustomKShareGeneratingFunction,
CustomMuDeltaShareGeneratingFunction,
CustomSShareGeneratingFunction,
CustomMPCv2SigningRound1GeneratingFunction,
CustomMPCv2SigningRound2GeneratingFunction,
CustomMPCv2SigningRound3GeneratingFunction,
TSSParamsWithPrv,
} from './baseTypes';
import { GShare, SignShare } from '../../../account-lib/mpc/tss';
import { RequestTracer } from '../util';
import * as openpgp from 'openpgp';
import { envRequiresBitgoPubGpgKeyConfig, getBitgoMpcGpgPubKey } from '../../tss/bitgoPubKeys';
import { getBitgoGpgPubKey } from '../opengpgUtils';
/**
* BaseTssUtil class which different signature schemes have to extend
*/
export default class BaseTssUtils<KeyShare> extends MpcUtils implements ITssUtils<KeyShare> {
private _wallet?: IWallet;
protected bitgoPublicGpgKey: openpgp.Key;
protected bitgoMPCv2PublicGpgKey: openpgp.Key | undefined;
constructor(bitgo: BitGoBase, baseCoin: IBaseCoin, wallet?: IWallet) {
super(bitgo, baseCoin);
this._wallet = wallet;
}
get wallet(): IWallet {
if (_.isNil(this._wallet)) {
throw new Error('Wallet not defined');
}
return this._wallet;
}
protected async setBitgoGpgPubKey(bitgo) {
const { mpcV1, mpcV2 } = await getBitgoGpgPubKey(bitgo);
// Do not unset the MPCv1 key if it is already set. This is to avoid unsetting if extra constants api calls fail.
if (mpcV1 !== undefined) {
this.bitgoPublicGpgKey = mpcV1;
}
// Do not unset the MPCv2 key if it is already set
if (mpcV2 !== undefined) {
this.bitgoMPCv2PublicGpgKey = mpcV2;
}
}
protected async pickBitgoPubGpgKeyForSigning(
isMpcv2: boolean,
reqId?: IRequestTracer,
enterpriseId?: string
): Promise<openpgp.Key> {
let bitgoGpgPubKey;
try {
const bitgoKeyChain = await this.baseCoin.keychains().get({ id: this.wallet.keyIds()[KeyIndices.BITGO], reqId });
if (!bitgoKeyChain || !bitgoKeyChain.hsmType) {
throw new Error('Missing Bitgo GPG Pub Key Type.');
}
bitgoGpgPubKey = await openpgp.readKey({
armoredKey: getBitgoMpcGpgPubKey(
this.bitgo.getEnv(),
bitgoKeyChain.hsmType === 'nitro' ? 'nitro' : 'onprem',
isMpcv2 ? 'mpcv2' : 'mpcv1'
),
});
} catch (e) {
if (!envRequiresBitgoPubGpgKeyConfig(this.bitgo.getEnv())) {
console.warn(
`Unable to get BitGo GPG key based on key data with error: ${e}. Fetching BitGo GPG key based on feature flags.`
);
// First try to get the key based on feature flags, if that fails, fallback to the default key from constants api.
bitgoGpgPubKey = await this.getBitgoGpgPubkeyBasedOnFeatureFlags(enterpriseId, isMpcv2, reqId)
.then(
async (pubKey) =>
pubKey ?? (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey())
)
.catch(async (e) => (isMpcv2 ? await this.getBitgoMpcv2PublicGpgKey() : await this.getBitgoPublicGpgKey()));
} else {
throw new Error(
`Environment "${this.bitgo.getEnv()}" requires a BitGo GPG Pub Key Config in BitGoJS for TSS. Error thrown while getting the key from config: ${e}`
);
}
}
return bitgoGpgPubKey;
}
async getBitgoPublicGpgKey(): Promise<openpgp.Key> {
if (!this.bitgoPublicGpgKey) {
// retry getting bitgo's gpg key
await this.setBitgoGpgPubKey(this.bitgo);
if (!this.bitgoPublicGpgKey) {
throw new Error("Failed to get Bitgo's gpg key");
}
}
return this.bitgoPublicGpgKey;
}
async getBitgoMpcv2PublicGpgKey(): Promise<openpgp.Key> {
if (!this.bitgoMPCv2PublicGpgKey) {
// retry getting bitgo's gpg key
await this.setBitgoGpgPubKey(this.bitgo);
if (!this.bitgoMPCv2PublicGpgKey) {
throw new Error("Failed to get Bitgo's gpg key");
}
}
return this.bitgoMPCv2PublicGpgKey;
}
async createBitgoHeldBackupKeyShare(
userGpgKey: SerializedKeyPair<string>,
enterprise: string | undefined
): Promise<BitgoHeldBackupKeyShare> {
const keyResponse = await this.bitgo
.post(this.baseCoin.url('/krs/backupkeys'))
.send({
enterprise,
userGPGPublicKey: userGpgKey.publicKey,
})
.result();
if (!keyResponse || !keyResponse.keyShares) {
throw new Error('Failed to get backup shares from BitGo.');
}
return {
id: keyResponse.id,
keyShares: keyResponse.keyShares,
};
}
public finalizeBitgoHeldBackupKeyShare(
keyId: string,
commonKeychain: string,
userKeyShare: KeyShare,
bitgoKeychain: Keychain,
userGpgKey: SerializedKeyPair<string>,
backupGpgKey: Key
): Promise<BitgoHeldBackupKeyShare> {
throw new Error('Method not implemented.');
}
createUserKeychain(params: CreateKeychainParamsBase): Promise<Keychain> {
throw new Error('Method not implemented.');
}
createBackupKeychain(params: CreateKeychainParamsBase): Promise<Keychain> {
throw new Error('Method not implemented.');
}
createBitgoKeychain(params: CreateBitGoKeychainParamsBase): Promise<Keychain> {
throw new Error('Method not implemented.');
}
createKeychains(params: {
passphrase: string;
enterprise?: string | undefined;
originalPasscodeEncryptionCode?: string | undefined;
isThirdPartyBackup?: boolean;
}): Promise<KeychainsTriplet> {
throw new Error('Method not implemented.');
}
signTxRequest(params: TSSParamsWithPrv): Promise<TxRequest> {
throw new Error('Method not implemented.');
}
signTxRequestForMessage(params: TSSParamsForMessage): Promise<TxRequest> {
throw new Error('Method not implemented.');
}
/**
* Signs a transaction using TSS for EdDSA and through utilization of custom share generators
*
* @param {string | TxRequest} txRequest - transaction request with unsigned transaction
* @param {CustomRShareGeneratingFunction} externalSignerRShareGenerator a function that creates R shares in the EdDSA TSS flow
* @param {CustomGShareGeneratingFunction} externalSignerGShareGenerator a function that creates G shares in the EdDSA TSS flow
* @returns {Promise<TxRequest>} - a signed tx request
*/
signEddsaTssUsingExternalSigner(
txRequest: string | TxRequest,
externalSignerCommitmentGenerator: CustomCommitmentGeneratingFunction,
externalSignerRShareGenerator: CustomRShareGeneratingFunction,
externalSignerGShareGenerator: CustomGShareGeneratingFunction
): Promise<TxRequest> {
throw new Error('Method not implemented.');
}
/**
* Signs a transaction using TSS for ECDSA and through utilization of custom share generators
*
* @param {params: TSSParams | TSSParamsForMessage} params - params object that represents parameters to sign a transaction or a message.
* @param {RequestType} requestType - the type of the request to sign (transaction or message).
* @param {CustomPaillierModulusGetterFunction} externalSignerPaillierModulusGetter a function that creates Paillier Modulus shares in the ECDSA TSS flow.
* @param {CustomKShareGeneratingFunction} externalSignerKShareGenerator a function that creates K shares in the ECDSA TSS flow.
* @param {CustomMuDeltaShareGeneratingFunction} externalSignerMuDeltaShareGenerator a function that creates Mu and Delta shares in the ECDSA TSS flow.
* @param {CustomSShareGeneratingFunction} externalSignerSShareGenerator a function that creates S shares in the ECDSA TSS flow.
*/
signEcdsaTssUsingExternalSigner(
params: TSSParams | TSSParamsForMessage,
requestType: RequestType,
externalSignerPaillierModulusGetter: CustomPaillierModulusGetterFunction,
externalSignerKShareGenerator: CustomKShareGeneratingFunction,
externalSignerMuDeltaShareGenerator: CustomMuDeltaShareGeneratingFunction,
externalSignerSShareGenerator: CustomSShareGeneratingFunction
): Promise<TxRequest> {
throw new Error('Method not implemented.');
}
/**
* Signs a transaction using TSS MPCv2 for ECDSA and through utilization of custom share generators
*
* @param {TSSParams | TSSParamsForMessage} params - params object that represents parameters to sign a transaction or a message.
* @param {CustomMPCv2SigningRound1GeneratingFunction} externalSignerMPCv2SigningRound1Generator - a function that creates MPCv2 Round 1 shares in the ECDSA TSS MPCv2 flow.
* @param {CustomMPCv2SigningRound2GeneratingFunction} externalSignerMPCv2SigningRound2Generator - a function that creates MPCv2 Round 2 shares in the ECDSA TSS MPCv2 flow.
* @param {CustomMPCv2SigningRound3GeneratingFunction} externalSignerMPCv2SigningRound3Generator - a function that creates MPCv2 Round 3 shares in the ECDSA TSS MPCv2 flow.
* @param {RequestType} requestType - the type of the request to sign (transaction or message).
* @returns {Promise<TxRequest>} - a signed tx request
*/
signEcdsaMPCv2TssUsingExternalSigner(
params: TSSParams | TSSParamsForMessage,
externalSignerMPCv2SigningRound1Generator: CustomMPCv2SigningRound1GeneratingFunction,
externalSignerMPCv2SigningRound2Generator: CustomMPCv2SigningRound2GeneratingFunction,
externalSignerMPCv2SigningRound3Generator: CustomMPCv2SigningRound3GeneratingFunction,
requestType?: RequestType
): Promise<TxRequest> {
throw new Error('Method not implemented.');
}
/**
* Create an Commitment (User to BitGo) share from an unsigned transaction and private user signing material
* EDDSA only
*
* @param {Object} params - params object
* @param {TxRequest} params.txRequest - transaction request with unsigned transaction
* @param {string} params.prv - user signing material
* @param {string} params.walletPassphrase - wallet passphrase
*
* @returns {Promise<{ userToBitgoCommitment: CommitmentShareRecor, encryptedSignerShare: EncryptedSignerShareRecord }>} - Commitment Share and the Encrypted Signer Share to BitGo
*/
createCommitmentShareFromTxRequest(params: {
txRequest: TxRequest;
prv: string;
walletPassphrase: string;
bitgoGpgPubKey: string;
}): Promise<{
userToBitgoCommitment: CommitmentShareRecord;
encryptedSignerShare: EncryptedSignerShareRecord;
encryptedUserToBitgoRShare: EncryptedSignerShareRecord;
}> {
throw new Error('Method not implemented.');
}
/**
* Create an R (User to BitGo) share from an unsigned transaction and private user signing material
*
* @param {Object} params - params object
* @param {TxRequest} params.txRequest - transaction request with unsigned transaction
* @param {string} params.prv - user signing material
* @param {string} [params.walletPassphrase] - wallet passphrase
* @param {EncryptedSignerShareRecord} [params.encryptedUserToBitgoRShare] - encrypted user to bitgo R share generated in the commitment phase
* @returns {Promise<{ rShare: SignShare }>} - R Share to BitGo
*/
createRShareFromTxRequest(params: {
txRequest: TxRequest;
walletPassphrase: string;
encryptedUserToBitgoRShare: EncryptedSignerShareRecord;
}): Promise<{ rShare: SignShare }> {
throw new Error('Method not implemented.');
}
/**
* Create a G (User to BitGo) share from an unsigned transaction and private user signing material
*
* @param {Object} params - params object
* @param {TxRequest} params.txRequest - transaction request with unsigned transaction
* @param {string} params.prv - user signing material
* @param {SignatureShareRecord} params.bitgoToUserRShare - BitGo to User R Share
* @param {SignShare} params.userToBitgoRShare - User to BitGo R Share
* @param {CommitmentShareRecord} params.bitgoToUserCommitment - BitGo to User Commitment
* @returns {Promise<GShare>} - GShare from User to BitGo
*/
createGShareFromTxRequest(params: {
txRequest: TxRequest;
prv: string;
bitgoToUserRShare: SignatureShareRecord;
userToBitgoRShare: SignShare;
bitgoToUserCommitment: CommitmentShareRecord;
}): Promise<GShare> {
throw new Error('Method not implemented.');
}
/**
* Builds a tx request from params and verify it
*
* @param {PrebuildTransactionWithIntentOptions} params - parameters to build the tx
* @param {TxRequestVersion} apiVersion lite or full
* @param {boolean} preview boolean indicating if this is to preview a tx request, which will not initiate policy checks or pending approvals
* @returns {Promise<TxRequest>} - a built tx request
*/
async prebuildTxWithIntent(
params: PrebuildTransactionWithIntentOptions,
apiVersion: TxRequestVersion = 'lite',
preview?: boolean
): Promise<TxRequest> {
const intentOptions = this.populateIntent(this.baseCoin, params);
const whitelistedParams = {
intent: {
...intentOptions,
},
apiVersion: apiVersion,
preview,
};
const reqTracer = params.reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
const unsignedTx = (await this.bitgo
.post(this.bitgo.url('/wallet/' + this.wallet.id() + '/txrequests', 2))
.send(whitelistedParams)
.result()) as TxRequest;
return unsignedTx;
}
/**
* Create a tx request from params for message signing
*
* @param params
* @param apiVersion
* @param preview
*/
async createTxRequestWithIntentForMessageSigning(
params: IntentOptionsForMessage,
apiVersion: TxRequestVersion = 'full',
preview?: boolean
): Promise<TxRequest> {
const intentOptions: PopulatedIntentForMessageSigning = {
custodianMessageId: params.custodianMessageId,
intentType: params.intentType,
sequenceId: params.sequenceId,
comment: params.comment,
memo: params.memo?.value,
isTss: params.isTss,
messageRaw: params.messageRaw,
messageEncoded: params.messageEncoded ?? '',
};
return this.createTxRequestBase(intentOptions, apiVersion, preview, params.reqId);
}
/**
* Create a tx request from params for type data signing
*
* @param params
* @param apiVersion
* @param preview
*/
async createTxRequestWithIntentForTypedDataSigning(
params: IntentOptionsForTypedData,
apiVersion: TxRequestVersion = 'full',
preview?: boolean
): Promise<TxRequest> {
const intentOptions: PopulatedIntentForTypedDataSigning = {
custodianMessageId: params.custodianMessageId,
intentType: params.intentType,
sequenceId: params.sequenceId,
comment: params.comment,
memo: params.memo?.value,
isTss: params.isTss,
messageRaw: params.typedDataRaw,
messageEncoded: params.typedDataEncoded ?? '',
};
return this.createTxRequestBase(intentOptions, apiVersion, preview, params.reqId);
}
/**
* Calls Bitgo API to create tx request.
*
* @private
*/
private async createTxRequestBase(
intentOptions: PopulatedIntentForTypedDataSigning | PopulatedIntentForMessageSigning,
apiVersion: TxRequestVersion,
preview?: boolean,
reqId?: IRequestTracer
): Promise<TxRequest> {
const whitelistedParams = {
intent: {
...intentOptions,
},
apiVersion,
preview,
};
const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
return this.bitgo
.post(this.bitgo.url(`/wallet/${this.wallet.id()}/txrequests`, 2))
.send(whitelistedParams)
.result();
}
/**
* Call delete signature shares for a txRequest, the endpoint delete the signatures and return them
*
* @param {string} txRequestId tx id reference to delete signature shares
* @param {IRequestTracer} reqId - the request tracer request id
* @returns {SignatureShareRecord[]}
*/
async deleteSignatureShares(txRequestId: string, reqId?: IRequestTracer): Promise<SignatureShareRecord[]> {
const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
return this.bitgo
.del(this.bitgo.url(`/wallet/${this.wallet.id()}/txrequests/${txRequestId}/signatureshares`, 2))
.send()
.result();
}
/**
* Initialize the send procedure once Bitgo has the User To Bitgo GShare
*
* @param {String} txRequestId - the txRequest Id
* @param {IRequestTracer} reqId - the request tracer request id
* @returns {Promise<any>}
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
async sendTxRequest(txRequestId: string, reqId?: IRequestTracer): Promise<any> {
const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
return this.bitgo
.post(this.baseCoin.url('/wallet/' + this.wallet.id() + '/tx/send'))
.send({ txRequestId })
.result();
}
/**
* Delete signature shares, get the tx request without them from the db and sign it to finally send it.
*
* Note : This can be performed in order to reach latest network conditions required on pending approval flow.
*
* @param {String} txRequestId - the txRequest Id to make the requests.
* @param {String} decryptedPrv - decrypted prv to sign the tx request.
* @param {RequestTracer} reqId id tracer.
* @returns {Promise<any>}
*/
async recreateTxRequest(txRequestId: string, decryptedPrv: string, reqId: IRequestTracer): Promise<TxRequest> {
await this.deleteSignatureShares(txRequestId, reqId);
// after delete signatures shares get the tx without them
const txRequest = await getTxRequest(this.bitgo, this.wallet.id(), txRequestId, reqId);
return await this.signTxRequest({ txRequest, prv: decryptedPrv, reqId });
}
/**
* Gets the latest Tx Request by id
*
* @param {String} txRequestId - the txRequest Id
* @param {IRequestTracer} reqId - request tracer request id
* @returns {Promise<TxRequest>}
*/
async getTxRequest(txRequestId: string, reqId?: IRequestTracer): Promise<TxRequest> {
return getTxRequest(this.bitgo, this.wallet.id(), txRequestId, reqId);
}
/**
* It gets the appropriate BitGo GPG public key for key creation based on a
* combination of coin and the feature flags on the user and their enterprise if set.
* @param enterpriseId - enterprise under which user wants to create the wallet
* @param isMPCv2 - true to get the MPCv2 GPG public key, defaults to false
* @param reqId - request tracer request id
*/
public async getBitgoGpgPubkeyBasedOnFeatureFlags(
enterpriseId: string | undefined,
isMPCv2 = false,
reqId?: IRequestTracer
): Promise<Key> {
const reqTracer = reqId || new RequestTracer();
this.bitgo.setRequestTracer(reqTracer);
const response: BitgoGPGPublicKey = await this.bitgo
.get(this.baseCoin.url('/tss/pubkey'))
.query({ enterpriseId })
.retry(3)
.result();
const bitgoPublicKeyStr = isMPCv2 ? response.mpcv2PublicKey : response.publicKey;
return readKey({ armoredKey: bitgoPublicKeyStr as string });
}
/**
* Returns supported TxRequest versions for this wallet
* @deprecated Whenever needed, use apiVersion 'full' for TSS wallets
*/
public supportedTxRequestVersions(): TxRequestVersion[] {
if (!this._wallet || this._wallet.type() === 'trading' || this._wallet.multisigType() !== 'tss') {
return [];
} else if (this._wallet.baseCoin.getMPCAlgorithm() === 'ecdsa') {
return ['full'];
} else if (this._wallet.baseCoin.getMPCAlgorithm() === 'eddsa' && this._wallet.type() === 'hot') {
return ['lite', 'full'];
} else {
return ['full'];
}
}
/**
* Returns true if the txRequest is using apiVersion == full and is pending approval
* @param txRequest
* @returns boolean
*/
isPendingApprovalTxRequestFull(txRequest: TxRequest): boolean {
const { apiVersion, state } = txRequest;
return apiVersion === 'full' && 'pendingApproval' === state;
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!