PHP WebShell

Текущая директория: /opt/BitGoJS/modules/unspents/test

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

import * as unspents from '../src';
import * as utxolib from '@bitgo/utxo-lib';
import assert from 'assert';
import { createScriptPubKey } from './signedTx/txGen';

/**
 * makeEnum('a', 'b') returns `{ a: 'a', b: 'b' }`
 *
 * @param args
 * @return map with string keys and symbol values
 */
const makeEnum = (...args: string[]): any => args.reduce((obj, key) => Object.assign(obj, { [key]: key }), {});

export const UnspentTypeP2shP2pk = 'p2shP2pk';

// p2trMusig2 is assumed to be script path only. taprootKeyPathSpend is for p2trMusig2 key path
export const UnspentTypeScript2of3: {
  p2sh: string;
  p2shP2wsh: string;
  p2wsh: string;
  p2tr: string;
  p2trMusig2: string;
  taprootKeyPathSpend: string;
} = makeEnum('p2sh', 'p2shP2wsh', 'p2wsh', 'p2tr', 'p2trMusig2', 'taprootKeyPathSpend');

export const UnspentTypePubKeyHash: {
  p2pkh: 'p2pkh';
  p2wpkh: 'p2wpkh';
} = makeEnum('p2pkh', 'p2wpkh');

export type TestUnspentType = string | UnspentTypeOpReturn;

export class UnspentTypeOpReturn {
  constructor(public size: number) {}

  public toString(): string {
    return `opReturn(${this.size})`;
  }
}

export type InputScriptType = utxolib.bitgo.outputScripts.ScriptType | 'taprootKeyPathSpend';

export function getInputScriptTypes(): InputScriptType[] {
  return [...utxolib.bitgo.outputScripts.scriptTypes2Of3, 'p2shP2pk', 'taprootKeyPathSpend'];
}

/**
 * Return the input dimensions based on unspent type
 * @param unspentType - one of UnspentTypeScript2of3
 * @return Dimensions
 */
export const getInputDimensionsForUnspentType = (unspentType: TestUnspentType): unspents.Dimensions => {
  switch (unspentType) {
    case UnspentTypeScript2of3.p2sh:
      return unspents.Dimensions.sum({ nP2shInputs: 1 });
    case UnspentTypeScript2of3.p2shP2wsh:
      return unspents.Dimensions.sum({ nP2shP2wshInputs: 1 });
    case UnspentTypeScript2of3.p2wsh:
      return unspents.Dimensions.sum({ nP2wshInputs: 1 });
    case UnspentTypeScript2of3.p2tr:
    case UnspentTypeScript2of3.p2trMusig2:
      return unspents.Dimensions.sum({ nP2trScriptPathLevel1Inputs: 1 });
    case UnspentTypeScript2of3.taprootKeyPathSpend:
      return unspents.Dimensions.sum({ nP2trKeypathInputs: 1 });
    case UnspentTypeP2shP2pk:
      return unspents.Dimensions.sum({ nP2shP2pkInputs: 1 });
  }
  throw new Error(`no input dimensions for ${unspentType}`);
};

export const getOutputDimensionsForUnspentType = (unspentType: TestUnspentType): unspents.Dimensions => {
  /* The values here are validated in the test 'calculates output dimensions dynamically' */
  switch (unspentType) {
    case UnspentTypeScript2of3.p2sh:
    case UnspentTypeScript2of3.p2shP2wsh:
    case UnspentTypeP2shP2pk:
      return unspents.Dimensions.fromOutputScriptLength(23);
    case UnspentTypeScript2of3.p2wsh:
      return unspents.Dimensions.fromOutputScriptLength(34);
    case UnspentTypeScript2of3.p2tr:
    case UnspentTypeScript2of3.p2trMusig2:
    case UnspentTypeScript2of3.taprootKeyPathSpend:
      return unspents.Dimensions.fromOutputScriptLength(34);
    case UnspentTypePubKeyHash.p2pkh:
      return unspents.Dimensions.fromOutputScriptLength(25);
    case UnspentTypePubKeyHash.p2wpkh:
      return unspents.Dimensions.fromOutputScriptLength(22);
    default:
      if (unspentType instanceof UnspentTypeOpReturn) {
        return unspents.Dimensions.fromOutputScriptLength(1 + unspentType.size);
      }
      throw new TypeError(`unknown unspentType ${unspentType}`);
  }
};

function getDefaultSignerNames(
  inputType: InputScriptType,
  signers?: { signerName: utxolib.bitgo.KeyName; cosignerName: utxolib.bitgo.KeyName }
): utxolib.bitgo.KeyName[] {
  if (signers) {
    return [signers.signerName, signers.cosignerName];
  }
  if (inputType === 'p2shP2pk') {
    return ['user'];
  }
  if (inputType === 'p2trMusig2') {
    return ['user', 'backup'];
  }
  return ['user', 'bitgo'];
}

export function constructPsbt(
  keys: utxolib.bitgo.RootWalletKeys,
  inputTypes: InputScriptType[],
  outputTypes: TestUnspentType[],
  signatureStatus: 'unsigned' | 'halfsigned' | 'fullysigned',
  signers?: { signerName: utxolib.bitgo.KeyName; cosignerName: utxolib.bitgo.KeyName }
): utxolib.bitgo.UtxoPsbt<utxolib.bitgo.UtxoTransaction<bigint>> {
  const psbt = utxolib.bitgo.createPsbtForNetwork({ network: utxolib.networks.bitcoin });

  inputTypes.forEach((t, i) => {
    if (t === 'p2shP2pk') {
      const signer = keys[getDefaultSignerNames(t, signers)[0]];
      const unspent = utxolib.testutil.mockReplayProtectionUnspent(utxolib.networks.bitcoin, BigInt(10), {
        key: signer,
        vout: i,
      });
      const { redeemScript } = utxolib.bitgo.outputScripts.createOutputScriptP2shP2pk(signer.publicKey);
      assert.ok(redeemScript);
      utxolib.bitgo.addReplayProtectionUnspentToPsbt(psbt, unspent, redeemScript);
    } else {
      const unspent = utxolib.testutil.mockWalletUnspent(utxolib.networks.bitcoin, BigInt(10), {
        keys,
        chain: utxolib.bitgo.getExternalChainCode(t === 'taprootKeyPathSpend' ? 'p2trMusig2' : t),
        vout: i,
        index: i,
      });
      const signerNames = getDefaultSignerNames(t, signers);
      utxolib.bitgo.addWalletUnspentToPsbt(psbt, unspent, keys, signerNames[0], signerNames[1]);
    }
  });

  outputTypes.forEach((t, index) => {
    psbt.addOutput({
      script: createScriptPubKey(keys.triple, t),
      value: BigInt(10),
    });
  });

  if (signatureStatus === 'unsigned') {
    return psbt;
  }

  psbt.setAllInputsMusig2NonceHD(keys['user']);
  psbt.setAllInputsMusig2NonceHD(keys['bitgo']);

  inputTypes.forEach((t, i) => {
    const signerNames = getDefaultSignerNames(t, signers);
    if (t === 'p2shP2pk') {
      if (signatureStatus === 'fullysigned') {
        psbt.signInput(i, keys[signerNames[0]]);
      }
    } else {
      psbt.signInputHD(i, keys[signerNames[0]]);
      if (signatureStatus === 'fullysigned') {
        psbt.signInputHD(i, keys[signerNames[1]]);
      }
    }
  });
  if (signatureStatus === 'fullysigned') {
    assert.ok(psbt.validateSignaturesOfAllInputs());
  }
  return psbt;
}

export function getSignedTransaction(
  keys: utxolib.bitgo.RootWalletKeys,
  signerName: utxolib.bitgo.KeyName,
  cosignerName: utxolib.bitgo.KeyName,
  inputTypes: InputScriptType[],
  outputTypes: TestUnspentType[]
): utxolib.bitgo.UtxoTransaction {
  const psbt = constructPsbt(keys, inputTypes, outputTypes, 'fullysigned', { signerName, cosignerName });
  psbt.finalizeAllInputs();
  return (psbt.extractTransaction() as utxolib.bitgo.UtxoTransaction<bigint>).clone<number>('number');
}

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


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