PHP WebShell

Текущая директория: /opt/BitGoJS/modules/utxo-lib/test/bitgo/psbt

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

import * as assert from 'assert';

import {
  addXpubsToPsbt,
  getPsbtInputSignatureCount,
  getSignatureValidationArrayPsbt,
  getStrictSignatureCount,
  getStrictSignatureCounts,
  RootWalletKeys,
  Triple,
  UtxoTransaction,
} from '../../../src/bitgo';
import { BIP32Interface } from 'bip32';
import {
  constructPsbt,
  constructTxnBuilder,
  getDefaultWalletKeys,
  Input,
  InputScriptType,
  inputScriptTypes,
  Output,
  outputScriptTypes,
  TxnInput,
  txnInputScriptTypes,
  TxnOutput,
  txnOutputScriptTypes,
} from '../../../src/testutil';
import { getNetworkList, getNetworkName, isMainnet, Network, networks } from '../../../src';
import { isSupportedScriptType } from '../../../src/bitgo/outputScripts';
import { SignatureTargetType } from './Psbt';

const rootWalletKeys = getDefaultWalletKeys();
const signs = ['unsigned', 'halfsigned', 'fullsigned'] as const;

const neutratedRootWalletKeys = new RootWalletKeys(
  rootWalletKeys.triple.map((bip32) => bip32.neutered()) as Triple<BIP32Interface>,
  rootWalletKeys.derivationPrefixes
);

const psbtInputs = inputScriptTypes.map((scriptType) => ({ scriptType, value: BigInt(1000) }));
const psbtOutputs = outputScriptTypes.map((scriptType) => ({ scriptType, value: BigInt(900) }));

const txInputs = txnInputScriptTypes.map((scriptType) => ({ scriptType, value: BigInt(1000) }));
const txOutputs = txnOutputScriptTypes.map((scriptType) => ({ scriptType, value: BigInt(900) }));

function getSigValidArray(scriptType: InputScriptType, sign: SignatureTargetType): Triple<boolean> {
  if (scriptType === 'p2shP2pk' || sign === 'unsigned') {
    return [false, false, false];
  }
  if (sign === 'halfsigned') {
    return [true, false, false];
  }
  return scriptType === 'p2trMusig2' ? [true, true, false] : [true, false, true];
}

function signCount(sign: SignatureTargetType) {
  return sign === 'unsigned' ? 0 : sign === 'halfsigned' ? 1 : 2;
}

function runPsbt(network: Network, sign: SignatureTargetType, inputs: Input[], outputs: Output[]) {
  const coin = getNetworkName(network);
  const signatureCount = signCount(sign);

  describe(`psbt build, sign and verify for ${coin} ${sign}`, function () {
    it(`getSignatureValidationArray with globalXpub ${coin} ${sign}`, function () {
      const psbt = constructPsbt(inputs, outputs, network, rootWalletKeys, sign);
      addXpubsToPsbt(psbt, neutratedRootWalletKeys);
      psbt.data.inputs.forEach((input, inputIndex) => {
        const isP2shP2pk = inputs[inputIndex].scriptType === 'p2shP2pk';
        const expectedSigValid = getSigValidArray(inputs[inputIndex].scriptType, sign);
        psbt.getSignatureValidationArray(inputIndex, { rootNodes: rootWalletKeys.triple }).forEach((sv, i) => {
          if (isP2shP2pk && sign !== 'unsigned' && i === 0) {
            assert.strictEqual(sv, true);
          } else {
            assert.strictEqual(sv, expectedSigValid[i]);
          }
        });
      });
    });

    it(`getSignatureValidationArray with rootNodes ${coin} ${sign}`, function () {
      const psbt = constructPsbt(inputs, outputs, network, rootWalletKeys, sign);
      addXpubsToPsbt(psbt, neutratedRootWalletKeys);
      psbt.data.inputs.forEach((input, inputIndex) => {
        const isP2shP2pk = inputs[inputIndex].scriptType === 'p2shP2pk';
        const expectedSigValid = getSigValidArray(inputs[inputIndex].scriptType, sign);
        psbt.getSignatureValidationArray(inputIndex, { rootNodes: neutratedRootWalletKeys.triple }).forEach((sv, i) => {
          if (isP2shP2pk && sign !== 'unsigned' && i === 0) {
            assert.strictEqual(sv, true);
          } else {
            assert.strictEqual(sv, expectedSigValid[i]);
          }
        });
      });
    });

    it(`getSignatureValidationArrayPsbt  ${coin} ${sign}`, function () {
      const psbt = constructPsbt(inputs, outputs, network, rootWalletKeys, sign);
      const sigValidations = getSignatureValidationArrayPsbt(psbt, neutratedRootWalletKeys);
      psbt.data.inputs.forEach((input, inputIndex) => {
        const expectedSigValid = getSigValidArray(inputs[inputIndex].scriptType, sign);
        const sigValid = sigValidations.find((sv) => sv[0] === inputIndex);
        assert.ok(sigValid);
        sigValid[1].forEach((sv, i) => assert.strictEqual(sv, expectedSigValid[i]));
      });
    });

    it(`psbt signature counts ${coin} ${sign}`, function () {
      const psbt = constructPsbt(inputs, outputs, network, rootWalletKeys, sign);
      const counts = getStrictSignatureCounts(psbt);
      const countsFromInputs = getStrictSignatureCounts(psbt.data.inputs);

      assert.strictEqual(counts.length, psbt.data.inputs.length);
      assert.strictEqual(countsFromInputs.length, psbt.data.inputs.length);
      psbt.data.inputs.forEach((input, inputIndex) => {
        const expectedCount = inputs[inputIndex].scriptType === 'p2shP2pk' && signatureCount > 0 ? 1 : signatureCount;
        assert.strictEqual(getPsbtInputSignatureCount(input), expectedCount);
        assert.strictEqual(getStrictSignatureCount(input), expectedCount);
        assert.strictEqual(counts[inputIndex], expectedCount);
        assert.strictEqual(countsFromInputs[inputIndex], expectedCount);
      });

      if (sign === 'fullsigned') {
        const tx = psbt.finalizeAllInputs().extractTransaction() as UtxoTransaction<bigint>;
        const counts = getStrictSignatureCounts(tx);
        const countsFromIns = getStrictSignatureCounts(tx.ins);

        tx.ins.forEach((input, inputIndex) => {
          const expectedCount = inputs[inputIndex].scriptType === 'p2shP2pk' ? 1 : signatureCount;
          assert.strictEqual(getStrictSignatureCount(input), expectedCount);
          assert.strictEqual(counts[inputIndex], expectedCount);
          assert.strictEqual(countsFromIns[inputIndex], expectedCount);
        });
      }
    });
  });
}

function runTx<TNumber extends number | bigint>(
  network: Network,
  sign: SignatureTargetType,
  inputs: TxnInput<TNumber>[],
  outputs: TxnOutput<TNumber>[]
) {
  const coin = getNetworkName(network);
  const signatureCount = signCount(sign);
  describe(`tx build, sign and verify for ${coin} ${sign}`, function () {
    it(`tx signature counts ${coin} ${sign}`, function () {
      const txb = constructTxnBuilder(inputs, outputs, network, rootWalletKeys, sign);
      const tx = sign === 'fullsigned' ? txb.build() : txb.buildIncomplete();

      const counts = getStrictSignatureCounts(tx);
      const countsFromIns = getStrictSignatureCounts(tx.ins);

      assert.strictEqual(counts.length, tx.ins.length);
      assert.strictEqual(countsFromIns.length, tx.ins.length);
      tx.ins.forEach((input, inputIndex) => {
        const expectedCount = inputs[inputIndex].scriptType === 'p2shP2pk' && signatureCount > 0 ? 1 : signatureCount;
        assert.strictEqual(getStrictSignatureCount(input), expectedCount);
        assert.strictEqual(counts[inputIndex], expectedCount);
        assert.strictEqual(countsFromIns[inputIndex], expectedCount);
      });
    });
  });
}

signs.forEach((sign) => {
  getNetworkList()
    .filter((v) => isMainnet(v) && v !== networks.bitcoinsv)
    .forEach((network) => {
      const supportedPsbtInputs = psbtInputs.filter((input) =>
        isSupportedScriptType(network, input.scriptType === 'taprootKeyPathSpend' ? 'p2trMusig2' : input.scriptType)
      );
      const supportedPsbtOutputs = psbtOutputs.filter((output) => isSupportedScriptType(network, output.scriptType));
      runPsbt(network, sign, supportedPsbtInputs, supportedPsbtOutputs);

      const supportedTxInputs = txInputs.filter((input) => isSupportedScriptType(network, input.scriptType));
      const supportedTxOutputs = txOutputs.filter((output) => isSupportedScriptType(network, output.scriptType));
      runTx(network, sign, supportedTxInputs, supportedTxOutputs);
    });
});

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


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