PHP WebShell

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

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

/**
 * Contains helper methods for getting and sorting root nodes from a PSBT.
 */

import * as assert from 'assert';
import * as bs58check from 'bs58check';

import { UtxoPsbt } from '../../UtxoPsbt';
import { isTriple, Triple } from '../../types';
import { BIP32Factory, BIP32Interface } from 'bip32';
import { ecc as eccLib } from '../../../noble_ecc';
import { ParsedScriptType2Of3 } from '../../parseInput';
import { Network } from '../../../networks';
import { createOutputScript2of3 } from '../../outputScripts';
import { PsbtInput } from 'bip174/src/lib/interfaces';
import { createTransactionFromBuffer } from '../../transaction';
import { getPsbtInputScriptType, toScriptType2Of3s } from '../Psbt';

/**
 * Error thrown when no multi-sig input is found in a PSBT.
 * */
export class ErrorNoMultiSigInputFound extends Error {
  constructor(message: string) {
    super(message);
  }
}

/**
 * Retrieves unsorted root BIP32Interface nodes from a PSBT if available.
 * @param psbt - The PSBT from which to extract the global Xpubs.
 * @returns An array of BIP32Interface objects or undefined if not available.
 */
export function getUnsortedRootNodes(psbt: UtxoPsbt): Triple<BIP32Interface> | undefined {
  const bip32s = psbt.data.globalMap.globalXpub?.map((xpub) =>
    BIP32Factory(eccLib).fromBase58(bs58check.encode(xpub.extendedPubkey))
  );
  assert(!bip32s || isTriple(bip32s), `Invalid globalXpubs in PSBT. Expected 3 or none. Got ${bip32s?.length}`);
  return bip32s;
}

/**
 * Determines if the given public keys' permutation matches a specified script.
 * @param params - Object containing public keys, permutation, script public key, script type, and network.
 * @returns A boolean indicating if the permutation matches the script.
 */
function matchesScript({
  pubKeys,
  perm,
  scriptPubKey,
  parsedScriptType,
  network,
}: {
  pubKeys: Buffer[];
  perm: Triple<number>;
  scriptPubKey: Buffer;
  parsedScriptType: ParsedScriptType2Of3;
  network: Network;
}): boolean {
  const pubKeysPerm: Triple<Buffer> = [pubKeys[perm[0]], pubKeys[perm[1]], pubKeys[perm[2]]];
  const scriptTypes = toScriptType2Of3s(parsedScriptType);
  return scriptTypes.some((scriptType) =>
    createOutputScript2of3(pubKeysPerm, scriptType, network).scriptPubKey.equals(scriptPubKey)
  );
}

/**
 * Finds the correct order of public keys to match a given script.
 * @param pubKeys - Array of public keys involved in the script.
 * @param scriptPubKey - The script public key to match against.
 * @param parsedScriptType - The parsed script type.
 * @param network - Bitcoin network.
 * @returns The order of public keys that match the script.
 */
function findSortOrderOfPubKeys(
  pubKeys: Triple<Buffer>,
  scriptPubKey: Buffer,
  parsedScriptType: ParsedScriptType2Of3,
  network: Network
): Triple<number> {
  const permutations: Array<Triple<number>> = [
    [0, 1, 2],
    [0, 2, 1],
    [1, 0, 2],
    [1, 2, 0],
    [2, 0, 1],
    [2, 1, 0],
  ];

  const order = permutations.find((perm) => matchesScript({ pubKeys, perm, scriptPubKey, parsedScriptType, network }));
  assert(order, 'Could not find sort order of multi sig public keys');
  return order;
}

/**
 * Extracts multi-sig input data, including script type, script public key, and derivation path, from the first relevant PSBT input.
 * @param psbt - The PSBT to extract data from.
 * @returns An object containing the parsed script type, script public key, and derivation path.
 */
function getFirstMultiSigInputData(psbt: UtxoPsbt): {
  parsedScriptType: ParsedScriptType2Of3;
  scriptPubKey: Buffer;
  derivationPath: string;
} {
  function getScriptPubKey(input: PsbtInput, prevOutIndex: number) {
    const scriptPubKey =
      input.witnessUtxo?.script ??
      (input.nonWitnessUtxo
        ? createTransactionFromBuffer(input.nonWitnessUtxo, psbt.network, { amountType: 'bigint' }).outs[prevOutIndex]
            .script
        : undefined);
    assert(scriptPubKey, 'Input scriptPubKey can not be found');
    return scriptPubKey;
  }

  function getDerivationPath(input: PsbtInput) {
    const bip32Dv = input?.bip32Derivation ?? input?.tapBip32Derivation;
    assert(bip32Dv?.length, 'Input Bip32Derivation can not be found');
    return bip32Dv[0].path;
  }

  const txInputs = psbt.txInputs;

  for (let i = 0; i < psbt.data.inputs.length; i++) {
    const input = psbt.data.inputs[i];
    const parsedScriptType = getPsbtInputScriptType(input);
    if (parsedScriptType === 'p2shP2pk') {
      continue;
    }
    const scriptPubKey = getScriptPubKey(input, txInputs[i].index);
    const derivationPath = getDerivationPath(input);
    return { parsedScriptType, scriptPubKey, derivationPath };
  }

  throw new ErrorNoMultiSigInputFound('No multi sig input found');
}

/**
 * Sorts given root nodes based on the script compatibility with the PSBT's multi-sig inputs.
 * @param psbt - The PSBT containing multi-sig inputs.
 * @param rootNodes - Array of root nodes to sort.
 * @returns An array of BIP32Interface objects in the order that matches the multi-sig script.
 */
export function sortRootNodes(psbt: UtxoPsbt, rootNodes: Triple<BIP32Interface>): Triple<BIP32Interface> {
  const { parsedScriptType, scriptPubKey, derivationPath } = getFirstMultiSigInputData(psbt);
  const pubKeys = rootNodes.map((rootNode) => rootNode.derivePath(derivationPath).publicKey) as Triple<Buffer>;
  const order = findSortOrderOfPubKeys(pubKeys, scriptPubKey, parsedScriptType, psbt.network);
  return order.map((i) => rootNodes[i]) as Triple<BIP32Interface>;
}

/**
 * Retrieves sorted root nodes from a PSBT, ensuring they are ordered according to script compatibility.
 * @param psbt - The PSBT to extract and sort root nodes from.
 * @returns An array of sorted BIP32Interface root nodes.
 */
export function getSortedRootNodes(psbt: UtxoPsbt): Triple<BIP32Interface> {
  const unsortedRootNodes = getUnsortedRootNodes(psbt);
  assert(unsortedRootNodes, 'Could not find root nodes in PSBT');
  return sortRootNodes(psbt, unsortedRootNodes);
}

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


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