PHP WebShell

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

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

import { BIP32Interface } from '@bitgo/utxo-lib';
import * as utxolib from '@bitgo/utxo-lib';
import { Descriptor } from '@bitgo/wasm-miniscript';

import { DescriptorBuilder, getDescriptorFromBuilder } from './builder';

type NodeUnary<Key extends string> = { [k in Key]: unknown };

function isUnaryNode<TKey extends string>(node: unknown, key: TKey): node is NodeUnary<TKey> {
  if (typeof node !== 'object' || node === null) {
    return false;
  }
  const keys = Object.keys(node);
  return keys.length === 1 && keys[0] === key;
}

function unwrapNode(node: unknown, path: string[]): unknown {
  let current = node;
  for (const key of path) {
    if (!isUnaryNode(current, key)) {
      return undefined;
    }
    current = current[key];
  }
  return current;
}

function parseMulti(node: unknown): {
  threshold: number;
  keys: BIP32Interface[];
  path: string;
} {
  if (!Array.isArray(node)) {
    throw new Error('Unexpected node');
  }
  const [threshold, ...keyNodes] = node;
  if (typeof threshold !== 'number') {
    throw new Error('Expected threshold number');
  }
  const keyWithPath = keyNodes.map((keyNode) => {
    if (!isUnaryNode(keyNode, 'XPub')) {
      throw new Error('Expected XPub node');
    }
    if (typeof keyNode.XPub !== 'string') {
      throw new Error('Expected XPub string');
    }
    const parts = keyNode.XPub.split('/');
    return { xpub: parts[0], path: parts.slice(1).join('/') };
  });
  const paths = keyWithPath.map((k) => k.path);
  paths.forEach((path, i) => {
    if (path !== paths[0]) {
      throw new Error(`Expected all paths to be the same: ${path} !== ${paths[0]}`);
    }
  });
  return {
    threshold,
    keys: keyWithPath.map((k) => utxolib.bip32.fromBase58(k.xpub)),
    path: paths[0],
  };
}

function parseWshMulti(node: unknown): DescriptorBuilder | undefined {
  const wshMsMulti = unwrapNode(node, ['Wsh', 'Ms', 'Multi']);
  if (wshMsMulti) {
    const { threshold, keys, path } = parseMulti(wshMsMulti);
    let name;
    if (threshold === 2 && keys.length === 2) {
      name = 'Wsh2Of2';
    } else if (threshold === 2 && keys.length === 3) {
      name = 'Wsh2Of3';
    } else {
      throw new Error('Unexpected multisig');
    }
    return {
      name,
      keys,
      path,
    };
  }
}

function parseCltvDrop(
  node: unknown,
  name: 'Wsh2Of3CltvDrop' | 'ShWsh2Of3CltvDrop',
  wrapping: string[]
): DescriptorBuilder | undefined {
  const unwrapped = unwrapNode(node, wrapping);
  if (!unwrapped) {
    return;
  }
  if (Array.isArray(unwrapped) && unwrapped.length === 2) {
    const [a, b] = unwrapped;
    const dropAfterAbsLocktime = unwrapNode(a, ['Drop', 'After', 'absLockTime']);
    if (typeof dropAfterAbsLocktime !== 'number') {
      throw new Error('Expected absLockTime number');
    }
    if (!isUnaryNode(b, 'Multi')) {
      throw new Error('Expected Multi node');
    }
    const multi = parseMulti(b.Multi);
    if (multi.threshold === 2 && multi.keys.length === 3) {
      return {
        name,
        locktime: dropAfterAbsLocktime,
        keys: multi.keys,
        path: multi.path,
      };
    }
  }
}

export function parseDescriptorNode(node: unknown): DescriptorBuilder {
  const parsed =
    parseWshMulti(node) ??
    parseCltvDrop(node, 'ShWsh2Of3CltvDrop', ['Sh', 'Wsh', 'Ms', 'AndV']) ??
    parseCltvDrop(node, 'Wsh2Of3CltvDrop', ['Wsh', 'Ms', 'AndV']);
  if (!parsed) {
    throw new Error('Failed to parse descriptor node');
  }
  return parsed;
}

export function parseDescriptor(descriptor: Descriptor): DescriptorBuilder {
  const builder = parseDescriptorNode(descriptor.node());
  if (getDescriptorFromBuilder(builder).toString() !== descriptor.toString()) {
    throw new Error('Failed to parse descriptor');
  }
  return builder;
}

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


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