PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-core/src/bitgo/tss

Просмотр файла: common.ts

import assert from 'assert';
import openpgp from 'openpgp';

import { BitGoBase } from '../bitgoBase';
import { TxRequestChallengeResponse } from './types';
import {
  RequestType,
  TxRequest,
  verifyPrimaryUserWrapper,
  SignatureShareRecord,
  CommitmentShareRecord,
  EncryptedSignerShareRecord,
  ExchangeCommitmentResponse,
  RequestTracer,
} from '../utils';
import { IRequestTracer } from '../../api';

const debug = require('debug')('bitgo:tss:common');

/**
 * Gets the latest Tx Request by id
 *
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id
 * @param {String} txRequestId - the txRequest id
 * @param {IRequestTracer} reqId - the request tracer request id
 * @returns {Promise<TxRequest>}
 */
export async function getTxRequest(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  reqId?: IRequestTracer
): Promise<TxRequest> {
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);
  const txRequestRes = await bitgo
    .get(bitgo.url('/wallet/' + walletId + '/txrequests', 2))
    .query({ txRequestIds: txRequestId, latest: 'true' })
    .retry(3)
    .result();

  if (txRequestRes.txRequests.length <= 0) {
    throw new Error(`Unable to find TxRequest with id ${txRequestId}`);
  }

  return txRequestRes.txRequests[0];
}

/**
 * Sends a Signature Share
 *
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id  *
 * @param {String} txRequestId - the txRequest Id
 * @param {SignatureShareRecord} signatureShare - a Signature Share
 * @param requestType - The type of request being submitted (either tx or message for signing)
 * @param signerShare
 * @param mpcAlgorithm
 * @param apiMode
 * @param {IRequestTracer} reqId - the request tracer request id
 * @returns {Promise<SignatureShareRecord>} - a Signature Share
 */
export async function sendSignatureShare(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  signatureShare: SignatureShareRecord,
  requestType: RequestType,
  signerShare?: string,
  mpcAlgorithm: 'eddsa' | 'ecdsa' = 'eddsa',
  apiMode: 'full' | 'lite' = 'lite',
  userPublicGpgKey?: string,
  reqId?: IRequestTracer
): Promise<SignatureShareRecord> {
  let addendum = '';
  switch (requestType) {
    case RequestType.tx:
      if (mpcAlgorithm === 'ecdsa' || apiMode === 'full') {
        addendum = '/transactions/0';
      }
      break;
    case RequestType.message:
      if (mpcAlgorithm === 'ecdsa' || apiMode === 'full') {
        addendum = '/messages/0';
      }
      break;
  }
  const urlPath = '/wallet/' + walletId + '/txrequests/' + txRequestId + addendum + '/signatureshares';
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);
  return bitgo
    .post(bitgo.url(urlPath, 2))
    .send({
      signatureShare,
      signerShare,
      userPublicGpgKey,
    })
    .result();
}

/**
 * Sends a Signature Share using the sign txRequest route
 *
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id  *
 * @param {String} txRequestId - the txRequest Id
 * @param signatureShares
 * @param requestType - The type of request being submitted (either tx or message for signing)
 * @param signerShare
 * @param mpcAlgorithm
 * @param multisigTypeVersion
 * @param signerGpgPublicKey
 * @param reqId
 * @returns {Promise<SignatureShareRecord>} - a Signature Share
 */
export async function sendSignatureShareV2(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  signatureShares: SignatureShareRecord[],
  requestType: RequestType,
  mpcAlgorithm: 'eddsa' | 'ecdsa',
  signerGpgPublicKey: string,
  signerShare?: string,
  multisigTypeVersion?: 'MPCv2' | undefined,
  reqId?: IRequestTracer
): Promise<TxRequest> {
  const addendum = requestType === RequestType.tx ? '/transactions/0' : '/messages/0';
  const urlPath = '/wallet/' + walletId + '/txrequests/' + txRequestId + addendum + '/sign';
  let type = '';
  if (multisigTypeVersion === 'MPCv2' && mpcAlgorithm === 'ecdsa') {
    type = 'ecdsaMpcV2';
  } else if (multisigTypeVersion === undefined && mpcAlgorithm === 'eddsa') {
    type = 'eddsaMpcV1';
  }
  const requestBody = {
    type,
    signatureShares,
    signerShare,
    signerGpgPublicKey,
  };
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);

  let attempts = 0;
  const maxAttempts = 3;

  while (attempts < maxAttempts) {
    try {
      return await bitgo.post(bitgo.url(urlPath, 2)).send(requestBody).result();
    } catch (err) {
      if (err?.status === 429) {
        const sleepTime = 1000 * (attempts + 1);
        debug(`MPC Signing rate limit error - retrying in ${sleepTime / 1000} seconds`);
        // sleep for a bit before retrying
        await new Promise((resolve) => setTimeout(resolve, sleepTime));
        attempts++;
      } else {
        throw err;
      }
    }
  }
  return await bitgo.post(bitgo.url(urlPath, 2)).send(requestBody).result();
}

/**
 * Sends a Transaction Request for broadcast once signing is complete
 *
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {String} walletId - the wallet id  *
 * @param {String} txRequestId - the txRequest Id
 * @param requestType - The type of request being submitted (either tx or message for signing)
 * @param {IRequestTracer} reqId - request tracer request id
 * @returns {Promise<SignatureShareRecord>} - a Signature Share
 */
export async function sendTxRequest(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  requestType: RequestType,
  reqId?: IRequestTracer
): Promise<TxRequest> {
  const addendum = requestType === RequestType.tx ? '/transactions/0' : '/messages/0';
  const urlPath = '/wallet/' + walletId + '/txrequests/' + txRequestId + addendum + '/send';
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);
  return bitgo.post(bitgo.url(urlPath, 2)).send().result();
}

/**
 * Sends the client commitment and encrypted signer share to the server, getting back the server commitment
 * @param {BitGoBase} bitgo - the bitgo instance
 * @param {string} walletId - the wallet id
 * @param {string} txRequestId - the txRequest Id
 * @param {CommitmentShareRecord} commitmentShare - the client commitment share
 * @param {EncryptedSignerShareRecord} encryptedSignerShare - the client encrypted signer share
 * @param {string} [apiMode] - the txRequest api mode (full or lite) - defaults to lite
 * @param {IRequestTracer} reqId - the request tracer request Id
 * @returns {Promise<ExchangeCommitmentResponse>} - the server commitment share
 */
export async function exchangeEddsaCommitments(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  commitmentShare: CommitmentShareRecord,
  encryptedSignerShare: EncryptedSignerShareRecord,
  apiMode: 'full' | 'lite' = 'lite',
  reqId?: IRequestTracer
): Promise<ExchangeCommitmentResponse> {
  let addendum = '';
  if (apiMode === 'full') {
    addendum = '/transactions/0';
  }
  const urlPath = '/wallet/' + walletId + '/txrequests/' + txRequestId + addendum + '/commit';
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);
  return await bitgo.post(bitgo.url(urlPath, 2)).send({ commitmentShare, encryptedSignerShare }).result();
}

/**
 * Verifies that a TSS wallet signature was produced with the expected key and that the signed data contains the
 * expected common keychain as well as the expected user and backup key ids
 */
export async function commonVerifyWalletSignature(params: {
  walletSignature: openpgp.Key;
  bitgoPub: openpgp.Key;
  commonKeychain: string;
  userKeyId: string;
  backupKeyId: string;
}): Promise<{ value: ArrayBuffer }[]> {
  const { walletSignature, bitgoPub, commonKeychain, userKeyId, backupKeyId } = params;

  // By ensuring that the fingerprints of the walletSignature and the bitgoPub are different and that any of the results
  // from calling verifyPrimaryUser is valid we know that the signature was actually produced by the private key
  // belonging to the bitgoPub.
  if (walletSignature.keyPacket.getFingerprint() === bitgoPub.keyPacket.getFingerprint()) {
    throw new Error('Invalid HSM GPG signature');
  }

  const verificationResult = await verifyPrimaryUserWrapper(walletSignature, bitgoPub, false);
  const isValid = verificationResult.some((result) => result.valid);
  if (!isValid) {
    throw new Error('Invalid HSM GPG signature');
  }
  const primaryUser = await walletSignature.getPrimaryUser();

  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
  // @ts-ignore the rawNotations property is missing from the type but it actually exists
  const rawNotations: { value: Uint8Array }[] = primaryUser.user.otherCertifications[0].rawNotations;

  assert(rawNotations.length === 5, 'invalid wallet signatures');

  assert(
    commonKeychain === Buffer.from(rawNotations[0].value).toString(),
    'wallet signature does not match common keychain'
  );
  assert(userKeyId === Buffer.from(rawNotations[1].value).toString(), `wallet signature does not match user key id`);
  assert(
    backupKeyId === Buffer.from(rawNotations[2].value).toString(),
    'wallet signature does not match backup key id'
  );

  return rawNotations;
}

/**
 * Gets challenge for a tx request from BitGo
 * supports Message and regular Transaction
 * @param bitgo
 * @param walletId
 * @param txRequestId
 * @param index
 * @param requestType
 * @param paillierModulus
 * @param reqId
 */
export async function getTxRequestChallenge(
  bitgo: BitGoBase,
  walletId: string,
  txRequestId: string,
  index: string,
  requestType: RequestType,
  paillierModulus: string,
  reqId?: IRequestTracer
): Promise<TxRequestChallengeResponse> {
  let addendum = '';
  switch (requestType) {
    case RequestType.tx:
      addendum = '/transactions/' + index;
      break;
    case RequestType.message:
      addendum = '/messages/' + index;
      break;
  }
  const urlPath = '/wallet/' + walletId + '/txrequests/' + txRequestId + addendum + '/challenge';
  const reqTracer = reqId || new RequestTracer();
  bitgo.setRequestTracer(reqTracer);
  return await bitgo.post(bitgo.url(urlPath, 2)).send({ paillierModulus }).result();
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!