PHP WebShell

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

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

/* eslint-disable @typescript-eslint/ban-ts-comment */
import * as should from 'should';
import { bitgo } from '@bitgo/utxo-lib';
const { chainCodes, chainCodesP2sh, chainCodesP2shP2wsh, chainCodesP2tr, chainCodesP2trMusig2, chainCodesP2wsh } =
  bitgo;
import { Dimensions, OutputDimensions, VirtualSizes } from '../src';

import { getOutputDimensionsForUnspentType, UnspentTypePubKeyHash, UnspentTypeScript2of3 } from './testutils';

describe('Dimensions Attributes', function () {
  it('has read-only nInputs and nOutputs', function () {
    should.throws(() => ((Dimensions.ZERO as any).nInputs = 1), /read-only/);
    should.throws(() => ((Dimensions.ZERO as any).nOutputs = 1), /read-only/);
  });
});

describe('Output Dimensions', function () {
  it('instantiates', function () {
    const dims = new OutputDimensions({ size: 0, count: 0 });
    should.throws(() => (dims.count += 1));
  });
});

describe('Dimensions Arithmetic', function () {
  it('sums correctly', function () {
    Dimensions.zero()
      .plus({ nP2shInputs: 1 })
      .should.eql(
        new Dimensions({
          nP2shInputs: 1,
          nP2shP2wshInputs: 0,
          nP2wshInputs: 0,
          nP2trKeypathInputs: 0,
          nP2trScriptPathLevel1Inputs: 0,
          nP2trScriptPathLevel2Inputs: 0,
          nP2shP2pkInputs: 0,
          outputs: { size: 0, count: 0 },
        })
      );

    const components = [
      { nP2shInputs: 1 },
      { nP2shP2wshInputs: 2 },
      { nP2wshInputs: 3 },
      { nP2trKeypathInputs: 4 },
      { nP2trScriptPathLevel1Inputs: 5 },
      { nP2trScriptPathLevel2Inputs: 6 },
      { outputs: { size: 23, count: 1 } },
      { outputs: { size: 44, count: 2 } },
      { outputs: { size: 0, count: 0 } },
    ];

    components.forEach((component) => should.doesNotThrow(() => Dimensions.sum(component)));

    const sum = components.reduce((a, b) => a.plus(b), Dimensions.zero());

    sum.should.eql(Dimensions.sum(...components));

    sum.should.eql(
      new Dimensions({
        nP2shInputs: 1,
        nP2shP2wshInputs: 2,
        nP2wshInputs: 3,
        nP2trKeypathInputs: 4,
        nP2trScriptPathLevel1Inputs: 5,
        nP2trScriptPathLevel2Inputs: 6,
        nP2shP2pkInputs: 0,
        outputs: { size: 67, count: 3 },
      })
    );

    sum.nOutputs.should.eql(sum.outputs.count);
  });

  it('provides some typical output sizes', function () {
    (
      [
        [Dimensions.SingleOutput.p2sh, VirtualSizes.txP2shOutputSize],
        [Dimensions.SingleOutput.p2shP2wsh, VirtualSizes.txP2shP2wshOutputSize],
        [Dimensions.SingleOutput.p2wsh, VirtualSizes.txP2wshOutputSize],
        [Dimensions.SingleOutput.p2pkh, VirtualSizes.txP2pkhOutputSize],
        [Dimensions.SingleOutput.p2wpkh, VirtualSizes.txP2wpkhOutputSize],
        [Dimensions.SingleOutput.p2tr, VirtualSizes.txP2trOutputSize],
      ] as [Dimensions, number][]
    ).forEach(([dims, size]) => {
      dims.getOutputsVSize().should.eql(size);
    });
  });

  it('prevents sum of invalid data', function () {
    should.doesNotThrow(() => Dimensions.sum({ outputs: { count: 0, size: 0 } }));
    should.doesNotThrow(() => Dimensions.sum({ outputs: { count: 1, size: 1 } }));
    should.throws(() => Dimensions.sum({ nOutputs: 1 }));
    should.throws(() => Dimensions.sum({ nOutputs: 1, outputs: { count: 2, size: 1 } }));
    // @ts-ignore
    should.throws(() => Dimensions.sum({ nP2shInputs: 1 }, { nP2shInputs: 'foo' }));
    should.throws(() => Dimensions.sum({ outputs: { count: 1, size: 0 } }));
    should.throws(() => Dimensions.sum({ outputs: { count: 0, size: 1 } }));
    should.throws(() => Dimensions.sum({ outputs: { count: 1, size: 1 } }, { outputs: { count: 1, size: 0 } }));
  });

  it('counts inputs correctly', function () {
    Object.entries(Dimensions.SingleInput).forEach(([key, value]) => {
      value.nInputs.should.eql(1, key);
    });
  });

  it('multiplies correctly', function () {
    const d = new Dimensions({
      nP2shInputs: 1,
      nP2shP2wshInputs: 2,
      nP2wshInputs: 3,
      nP2trKeypathInputs: 4,
      nP2trScriptPathLevel1Inputs: 5,
      nP2trScriptPathLevel2Inputs: 6,
      nP2shP2pkInputs: 7,
      outputs: { count: 1, size: 22 },
    }).times(3);

    d.should.eql(
      new Dimensions({
        nP2shInputs: 3,
        nP2shP2wshInputs: 6,
        nP2wshInputs: 9,
        nP2trKeypathInputs: 12,
        nP2trScriptPathLevel1Inputs: 15,
        nP2trScriptPathLevel2Inputs: 18,
        nP2shP2pkInputs: 21,
        outputs: { count: 3, size: 66 },
      })
    );

    d.getNInputs().should.eql(84);
    d.nInputs.should.eql(84);
  });
});

describe('Dimensions from unspent types', function () {
  it('determines unspent size according to chain', function () {
    chainCodesP2sh.forEach((chain) => Dimensions.fromUnspent({ chain }).should.eql(Dimensions.sum({ nP2shInputs: 1 })));

    chainCodesP2shP2wsh.forEach((chain) =>
      Dimensions.fromUnspent({ chain }).should.eql(Dimensions.sum({ nP2shP2wshInputs: 1 }))
    );

    chainCodesP2wsh.forEach((chain) =>
      Dimensions.fromUnspent({ chain }).should.eql(Dimensions.sum({ nP2wshInputs: 1 }))
    );

    chainCodesP2tr.forEach((chain) => {
      Dimensions.fromUnspent({ chain }).should.eql(Dimensions.sum({ nP2trScriptPathLevel2Inputs: 1 }));
      Dimensions.fromUnspent(
        { chain },
        { p2tr: { scriptPathLevel: 1 }, p2trMusig2: { scriptPathLevel: undefined } }
      ).should.eql(Dimensions.sum({ nP2trScriptPathLevel1Inputs: 1 }));
    });

    chainCodesP2trMusig2.forEach((chain) => {
      Dimensions.fromUnspent({ chain }).should.eql(Dimensions.sum({ nP2trScriptPathLevel1Inputs: 1 }));
      Dimensions.fromUnspent(
        { chain },
        { p2tr: { scriptPathLevel: undefined }, p2trMusig2: { scriptPathLevel: undefined } }
      ).should.eql(Dimensions.sum({ nP2trKeypathInputs: 1 }));
    });

    Dimensions.fromUnspents(chainCodes.map((chain) => ({ chain }))).should.eql(
      new Dimensions({
        nP2shP2wshInputs: 2,
        nP2shInputs: 2,
        nP2wshInputs: 2,
        nP2trKeypathInputs: 0,
        nP2trScriptPathLevel1Inputs: 2,
        nP2trScriptPathLevel2Inputs: 2,
        nP2shP2pkInputs: 0,
        outputs: { count: 0, size: 0 },
      })
    );

    Dimensions.fromUnspents(
      chainCodes.map((chain) => ({ chain })),
      { p2tr: { scriptPathLevel: 1 }, p2trMusig2: { scriptPathLevel: undefined } }
    ).should.eql(
      new Dimensions({
        nP2shP2wshInputs: 2,
        nP2shInputs: 2,
        nP2wshInputs: 2,
        nP2trKeypathInputs: 2,
        nP2trScriptPathLevel1Inputs: 2,
        nP2trScriptPathLevel2Inputs: 0,
        nP2shP2pkInputs: 0,
        outputs: { count: 0, size: 0 },
      })
    );
  });

  it('calculates output dimensions dynamically', function () {
    const expectedSizes = new Map([
      [UnspentTypeScript2of3.p2sh, VirtualSizes.txP2shOutputSize],
      [UnspentTypeScript2of3.p2shP2wsh, VirtualSizes.txP2shP2wshOutputSize],
      [UnspentTypeScript2of3.p2wsh, VirtualSizes.txP2wshOutputSize],
      [UnspentTypeScript2of3.p2tr, VirtualSizes.txP2trOutputSize],
      [UnspentTypeScript2of3.p2trMusig2, VirtualSizes.txP2trOutputSize],
      [UnspentTypeScript2of3.taprootKeyPathSpend, VirtualSizes.txP2trOutputSize],
      [UnspentTypePubKeyHash.p2pkh, VirtualSizes.txP2pkhOutputSize],
      [UnspentTypePubKeyHash.p2wpkh, VirtualSizes.txP2wpkhOutputSize],
    ]);

    [...Object.keys(UnspentTypeScript2of3), ...Object.keys(UnspentTypePubKeyHash)].forEach((type) =>
      getOutputDimensionsForUnspentType(type).outputs.size.should.eql(expectedSizes.get(type as any))
    );
  });
});

describe('Dimensions estimates', function () {
  it('calculates vsizes', function () {
    function dim(nP2shInputs: number, nP2shP2wshInputs: number, nP2wshInputs: number, nOutputs: number): Dimensions {
      return Dimensions.sum(
        {
          nP2shInputs,
          nP2shP2wshInputs,
          nP2wshInputs,
        },
        getOutputDimensionsForUnspentType(UnspentTypePubKeyHash.p2pkh).times(nOutputs)
      );
    }

    function dimP2tr(
      nP2trKeypathInputs: number,
      nP2trScriptPathLevel1Inputs: number,
      nP2trScriptPathLevel2Inputs: number,
      nOutputs: number
    ): Dimensions {
      return Dimensions.sum(
        {
          nP2trKeypathInputs,
          nP2trScriptPathLevel1Inputs,
          nP2trScriptPathLevel2Inputs,
        },
        getOutputDimensionsForUnspentType(UnspentTypePubKeyHash.p2pkh).times(nOutputs)
      );
    }

    const vectors: [Dimensions, unknown[]][] = [
      [dim(1, 0, 0, 1), [false, 10, 298, 34, 342]],
      [dim(0, 1, 0, 1), [true, 11, 140, 34, 185]],
      [dim(0, 0, 1, 1), [true, 11, 105, 34, 150]],
      [dim(2, 0, 0, 1), [false, 10, 596, 34, 640]],
      [dim(0, 2, 0, 1), [true, 11, 280, 34, 325]],
      [dim(0, 0, 2, 1), [true, 11, 210, 34, 255]],
      [dim(1, 1, 1, 1), [true, 11, 543, 34, 588]],
      [dim(1, 1, 1, 2), [true, 11, 543, 68, 622]],

      [dimP2tr(1, 0, 0, 1), [true, 11, 58, 34, 103]],
      [dimP2tr(0, 1, 0, 1), [true, 11, 108, 34, 153]],
      [dimP2tr(0, 0, 1, 1), [true, 11, 116, 34, 161]],
    ];

    vectors.forEach(([dimensions, props]) => {
      [
        dimensions.isSegwit(),
        dimensions.getOverheadVSize(),
        dimensions.getInputsVSize(),
        dimensions.getOutputsVSize(),
        dimensions.getVSize(),
      ].should.eql(props);
    });
  });
});

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


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