PHP WebShell

Текущая директория: /opt/BitGoJS/modules/abstract-utxo/test/transaction/descriptor

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

import assert from 'assert';

import * as utxolib from '@bitgo/utxo-lib';
import { Descriptor } from '@bitgo/wasm-miniscript';
import {
  getDefaultXPubs,
  getDescriptor,
  getDescriptorMap,
  mockPsbtDefault,
} from '@bitgo/utxo-core/testutil/descriptor';
import { toPlainObject } from '@bitgo/utxo-core/testutil';
import { createAddressFromDescriptor } from '@bitgo/utxo-core/descriptor';

import { ParsedOutputsBigInt, toBaseParsedTransactionOutputsFromPsbt } from '../../../src/transaction/descriptor/parse';
import {
  AggregateValidationError,
  assertExpectedOutputDifference,
  ErrorImplicitExternalOutputs,
  ErrorMissingOutputs,
} from '../../../src/transaction/descriptor/verifyTransaction';
import { toAmountType } from '../../../src/transaction/descriptor/parseToAmountType';
import { BaseOutput } from '../../../src';

import { getFixtureRoot } from './fixtures.utils';

const { assertEqualFixture } = getFixtureRoot(__dirname + '/fixtures');

type OutputWithValue<T = number | bigint | string> = {
  address?: string;
  value: T;
};

function toBaseOutput<TNumber>(output: OutputWithValue, amountType: 'bigint' | 'string'): BaseOutput<TNumber> {
  assert(output.address);
  return {
    address: output.address,
    amount: toAmountType(output.value, amountType) as TNumber,
  };
}

function toBaseOutputBigInt(output: OutputWithValue): BaseOutput<bigint> {
  return toBaseOutput(output, 'bigint');
}

function toBaseOutputString(output: OutputWithValue): BaseOutput<string> {
  return toBaseOutput(output, 'string');
}

function toMaxOutput(output: OutputWithValue): OutputWithValue<'max'> {
  return {
    ...output,
    value: 'max',
  };
}

describe('parse', function () {
  const descriptorSelf = getDescriptor('Wsh2Of3', getDefaultXPubs('a'));
  const descriptorOther = getDescriptor('Wsh2Of3', getDefaultXPubs('b'));
  const psbt = mockPsbtDefault({ descriptorSelf, descriptorOther });

  function recipient(descriptor: Descriptor, index: number, value = 1000) {
    return { value, address: createAddressFromDescriptor(descriptor, index, utxolib.networks.bitcoin) };
  }

  function internalRecipient(index: number, value?: number): OutputWithValue {
    return recipient(descriptorSelf, index, value);
  }

  function externalRecipient(index: number, value?: number): OutputWithValue {
    return recipient(descriptorOther, index, value);
  }

  function getBaseParsedTransaction(psbt: utxolib.bitgo.UtxoPsbt, recipients: OutputWithValue[]): ParsedOutputsBigInt {
    return toBaseParsedTransactionOutputsFromPsbt(
      psbt,
      getDescriptorMap('Wsh2Of3', getDefaultXPubs('a')),
      recipients.map(toBaseOutputString),
      psbt.network
    );
  }

  describe('toBase', function () {
    it('should return the correct BaseParsedTransactionOutputs', async function () {
      await assertEqualFixture('parseWithoutRecipients.json', toPlainObject(getBaseParsedTransaction(psbt, [])));
      await assertEqualFixture(
        'parseWithExternalRecipient.json',
        toPlainObject(getBaseParsedTransaction(psbt, [psbt.txOutputs[0]]))
      );
      await assertEqualFixture(
        'parseWithInternalRecipient.json',
        toPlainObject(getBaseParsedTransaction(psbt, [psbt.txOutputs[1]]))
      );
      await assertEqualFixture(
        'parseWithExternalRecipient.json',
        // max recipient: ignore actual value
        toPlainObject(getBaseParsedTransaction(psbt, [toMaxOutput(psbt.txOutputs[0])]))
      );
    });

    function assertEqualValidationError(actual: unknown, expected: AggregateValidationError) {
      function normErrors(e: Error[]): Error[] {
        return e.map((e) => ({ ...e, stack: undefined }));
      }
      if (actual instanceof AggregateValidationError) {
        assert.deepStrictEqual(normErrors(actual.errors), normErrors(expected.errors));
      } else {
        throw new Error('unexpected error type: ' + actual);
      }
    }

    function assertValidationError(f: () => void, expected: AggregateValidationError) {
      assert.throws(f, (err) => {
        assertEqualValidationError(err, expected);
        return true;
      });
    }

    function implicitOutputError(output: OutputWithValue, { external = true } = {}): ErrorImplicitExternalOutputs {
      return new ErrorImplicitExternalOutputs([{ ...toBaseOutputBigInt(output), external }]);
    }

    function missingOutputError(output: OutputWithValue, { external = true } = {}): ErrorMissingOutputs {
      return new ErrorMissingOutputs([{ ...toBaseOutputBigInt(output), external }]);
    }

    it('should throw expected error: no recipient requested', function () {
      assertValidationError(
        () => assertExpectedOutputDifference(getBaseParsedTransaction(psbt, [])),
        new AggregateValidationError([implicitOutputError(psbt.txOutputs[0])])
      );
    });

    it('should throw expected error: only internal recipient requested', function () {
      assertValidationError(
        () => assertExpectedOutputDifference(getBaseParsedTransaction(psbt, [psbt.txOutputs[1]])),
        new AggregateValidationError([implicitOutputError(psbt.txOutputs[0])])
      );
    });

    it('should throw expected error: only internal max recipient requested', function () {
      assertValidationError(
        () => assertExpectedOutputDifference(getBaseParsedTransaction(psbt, [toMaxOutput(psbt.txOutputs[1])])),
        new AggregateValidationError([implicitOutputError(psbt.txOutputs[0])])
      );
    });

    it('should throw expected error: swapped recipient', function () {
      const recipient = externalRecipient(99);
      assertValidationError(
        () => assertExpectedOutputDifference(getBaseParsedTransaction(psbt, [recipient])),
        new AggregateValidationError([missingOutputError(recipient), implicitOutputError(psbt.txOutputs[0])])
      );
    });

    it('should throw expected error: missing internal recipient', function () {
      const recipient = internalRecipient(99);
      assertValidationError(
        () => assertExpectedOutputDifference(getBaseParsedTransaction(psbt, [recipient])),
        new AggregateValidationError([missingOutputError(recipient), implicitOutputError(psbt.txOutputs[0])])
      );
    });
  });
});

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


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