PHP WebShell

Текущая директория: /opt/BitGoJS/modules/utxo-bin/src/commands/cmdPsbt

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

import * as yargs from 'yargs';
import * as utxolib from '@bitgo/utxo-lib';
import { Dimensions } from '@bitgo/unspents';
import { Buffer } from 'buffer';
import { Descriptor, Miniscript } from '@bitgo/wasm-miniscript';

import { withPsbt, withPsbtOptions, WithPsbtOptions } from './withPsbt';

function toScriptPubKey(
  params: {
    address?: string;
    scriptPubKey?: string;
  },
  network: utxolib.Network
): Buffer {
  if (params.address) {
    return utxolib.addressFormat.toOutputScriptTryFormats(params.address, network);
  }
  if (params.scriptPubKey) {
    return Buffer.from(params.scriptPubKey, 'hex');
  }
  throw new Error('address or scriptPubKey is required');
}

type ArgsAddOutput = WithPsbtOptions & {
  address?: string;
  scriptPubKey?: string;
  amount: string;
  feeRateSatB?: number;
};

function getInputWeight(psbt: utxolib.Psbt, inputIndex?: number): number {
  if (inputIndex === undefined) {
    return psbt.txInputs.reduce((sum, input, inputIndex) => sum + getInputWeight(psbt, inputIndex), 0);
  }
  const { redeemScript, witnessScript } = psbt.data.inputs[inputIndex];
  if (redeemScript) {
    throw new Error('redeemScript is not supported');
  }
  if (!witnessScript) {
    throw new Error('witnessScript is required');
  }
  const witnessMiniscript = Miniscript.fromBitcoinScript(witnessScript, 'segwitv0');
  const descriptor = Descriptor.fromString(`wsh(${witnessMiniscript.toString()})`, 'definite');
  return descriptor.maxWeightToSatisfy();
}

function getOutputVsize(psbt: utxolib.Psbt, outputIndex?: number): number {
  if (outputIndex === undefined) {
    return psbt.txOutputs.reduce((sum, output, outputIndex) => sum + getOutputVsize(psbt, outputIndex), 0);
  }
  const { script } = psbt.txOutputs[outputIndex];
  return Dimensions.getVSizeForOutputWithScriptLength(script.length);
}

function getMaxOutputValue(
  psbt: utxolib.Psbt,
  {
    scriptPubKey,
    feeRateSatB,
  }: {
    scriptPubKey: Buffer;
    feeRateSatB: number;
  }
): bigint {
  const inputSum = psbt.data.inputs.reduce((sum, input) => {
    if (!input.witnessUtxo) {
      throw new Error('witnessUtxo is required');
    }
    return sum + input.witnessUtxo.value;
  }, BigInt(0));
  const outputSum = psbt.txOutputs.reduce((sum, output) => sum + output.value, BigInt(0));
  const inputVsize = Math.ceil(getInputWeight(psbt) / 4);
  const outputVsize = getOutputVsize(psbt) + Dimensions.getVSizeForOutputWithScriptLength(scriptPubKey.length);
  const totalVsize = inputVsize + outputVsize + 11;
  const fee = BigInt(totalVsize * feeRateSatB);
  if (inputSum < outputSum + fee) {
    throw new Error(`insufficient funds: [inputSum=${inputSum}, outputSum=${outputSum}, fee=${fee}]`);
  }
  return inputSum - outputSum - fee;
}

function getOutputValue(
  amount: string,
  {
    scriptPubKey,
    psbt,
    feeRateSatB,
  }: {
    scriptPubKey: Buffer;
    psbt: utxolib.Psbt;
    feeRateSatB?: number;
  }
): bigint {
  if (amount === 'max') {
    if (!feeRateSatB) {
      throw new Error('feeRateSatB is required');
    }
    return getMaxOutputValue(psbt, { scriptPubKey, feeRateSatB });
  }
  return BigInt(parseFloat(amount));
}

export const cmdAddOutput: yargs.CommandModule<unknown, ArgsAddOutput> = {
  command: 'addOutput',
  describe: 'add output to psbt',
  builder(b: yargs.Argv<unknown>) {
    return b
      .options(withPsbtOptions)
      .option('address', { type: 'string' })
      .option('scriptPubKey', { type: 'string' })
      .option('amount', { type: 'string', demandOption: true })
      .option('feeRateSatB', { type: 'number' });
  },
  async handler(argv) {
    await withPsbt(argv, async function (psbt) {
      const scriptPubKey = toScriptPubKey(
        {
          address: argv.address,
          scriptPubKey: argv.scriptPubKey,
        },
        argv.network
      );
      const value = getOutputValue(argv.amount, { scriptPubKey, psbt, feeRateSatB: argv.feeRateSatB });
      psbt.addOutput({ script: scriptPubKey, value });
      return psbt;
    });
  },
};

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


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