PHP WebShell

Текущая директория: /opt/BitGoJS/modules/utxo-bin/node_modules/bitcoinjs-lib/src/payments

Просмотр файла: p2tr.js

'use strict';
// SegWit version 1 P2TR output type for Taproot defined in
// https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki
Object.defineProperty(exports, '__esModule', { value: true });
exports.p2tr = void 0;
const networks_1 = require('../networks');
const bscript = require('../script');
const taproot = require('../taproot');
const lazy = require('./lazy');
const typef = require('typeforce');
const OPS = bscript.OPS;
const { bech32m } = require('bech32');
/**
 * A secp256k1 x coordinate with unknown discrete logarithm used for eliminating
 * keypath spends, equal to SHA256(uncompressedDER(SECP256K1_GENERATOR_POINT)).
 */
const H = Buffer.from(
  '50929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0',
  'hex',
);
const EMPTY_BUFFER = Buffer.alloc(0);
// output: OP_1 {witnessProgram}
function p2tr(a, opts) {
  if (
    !a.address &&
    !a.pubkey &&
    !a.pubkeys &&
    !(a.redeems && a.redeems.length) &&
    !a.output &&
    !a.witness
  )
    throw new TypeError('Not enough data');
  opts = Object.assign({ validate: true }, opts || {});
  if (!opts.eccLib) throw new Error('ECC Library is required for p2tr.');
  const ecc = opts.eccLib;
  typef(
    {
      network: typef.maybe(typef.Object),
      address: typef.maybe(typef.String),
      // the output script should be a fixed 34 bytes.
      // 1 byte for OP_1 indicating segwit version 1, one byte for 0x20 to push
      // the next 32 bytes, followed by the 32 byte witness program
      output: typef.maybe(typef.BufferN(34)),
      // a single pubkey
      pubkey: typef.maybe(ecc.isXOnlyPoint),
      // the pub key(s) used for keypath signing.
      // aggregated with MuSig2* if > 1
      pubkeys: typef.maybe(typef.arrayOf(ecc.isXOnlyPoint)),
      redeems: typef.maybe(
        typef.arrayOf({
          network: typef.maybe(typef.Object),
          output: typef.maybe(typef.Buffer),
          weight: typef.maybe(typef.Number),
          depth: typef.maybe(typef.Number),
          witness: typef.maybe(typef.arrayOf(typef.Buffer)),
        }),
      ),
      redeemIndex: typef.maybe(typef.Number),
      signature: typef.maybe(bscript.isCanonicalSchnorrSignature),
      controlBlock: typef.maybe(typef.Buffer),
      annex: typef.maybe(typef.Buffer),
    },
    a,
  );
  const _address = lazy.value(() => {
    if (!a.address) return undefined;
    const result = bech32m.decode(a.address);
    const version = result.words.shift();
    const data = bech32m.fromWords(result.words);
    return {
      version,
      prefix: result.prefix,
      data: Buffer.from(data),
    };
  });
  const _outputPubkey = lazy.value(() => {
    // we remove the first two bytes (OP_1 0x20) from the output script to
    // extract the 32 byte taproot pubkey (aka witness program)
    return a.output && a.output.slice(2);
  });
  const network = a.network || networks_1.bitcoin;
  const o = { network };
  const _taprootPaths = lazy.value(() => {
    if (!a.redeems) return;
    if (o.tapTree) {
      return taproot.getDepthFirstTaptree(o.tapTree);
    }
    const outputs = a.redeems.map(({ output }) => output);
    if (!outputs.every(output => output)) return;
    return taproot.getHuffmanTaptree(
      outputs,
      a.redeems.map(({ weight }) => weight),
    );
  });
  const _parsedWitness = lazy.value(() => {
    if (!a.witness) return;
    return taproot.parseTaprootWitness(a.witness);
  });
  const _parsedControlBlock = lazy.value(() => {
    // Can't use o.controlBlock, because it could be circular
    if (a.controlBlock) return taproot.parseControlBlock(ecc, a.controlBlock);
    const parsedWitness = _parsedWitness();
    if (parsedWitness && parsedWitness.spendType === 'Script')
      return taproot.parseControlBlock(ecc, parsedWitness.controlBlock);
  });
  lazy.prop(o, 'internalPubkey', () => {
    if (a.pubkey) {
      // single pubkey
      return a.pubkey;
    } else if (a.pubkeys && a.pubkeys.length === 1) {
      return a.pubkeys[0];
    } else if (a.pubkeys && a.pubkeys.length > 1) {
      // multiple pubkeys
      return Buffer.from(taproot.aggregateMuSigPubkeys(ecc, a.pubkeys));
    } else if (_parsedControlBlock()) {
      return _parsedControlBlock().internalPubkey;
    } else {
      // If there is no key path spending condition, we use an internal key with unknown secret key.
      // TODO: In order to avoid leaking the information that key path spending is not possible it
      // is recommended to pick a fresh integer r in the range 0...n-1 uniformly at random and use
      // H + rG as internal key. It is possible to prove that this internal key does not have a
      // known discrete logarithm with respect to G by revealing r to a verifier who can then
      // reconstruct how the internal key was created.
      return H;
    }
  });
  const _taprootPubkey = lazy.value(() => {
    const parsedControlBlock = _parsedControlBlock();
    const parsedWitness = _parsedWitness();
    // Refuse to create an unspendable key
    if (
      !a.pubkey &&
      !(a.pubkeys && a.pubkeys.length) &&
      !a.redeems &&
      !parsedControlBlock
    )
      return;
    let taptreeRoot;
    // Prefer to get the root via the control block because not all redeems may
    // be available
    if (parsedControlBlock) {
      let tapscript;
      if (parsedWitness && parsedWitness.spendType === 'Script') {
        tapscript = parsedWitness.tapscript;
      } else if (o.redeem && o.redeem.output) {
        tapscript = o.redeem.output;
      }
      if (tapscript)
        taptreeRoot = taproot.getTaptreeRoot(
          ecc,
          parsedControlBlock,
          tapscript,
        );
    }
    if (!taptreeRoot && _taprootPaths()) taptreeRoot = _taprootPaths().root;
    return taproot.tapTweakPubkey(ecc, o.internalPubkey, taptreeRoot);
  });
  lazy.prop(o, 'tapTree', () => {
    if (!a.redeems) return;
    if (a.redeems.find(({ depth }) => depth === undefined)) {
      console.warn(
        'Deprecation Warning: Weight-based tap tree construction will be removed in the future. ' +
          'Please use depth-first coding as specified in BIP-0371.',
      );
      return;
    }
    if (!a.redeems.every(({ output }) => output)) return;
    return {
      leaves: a.redeems.map(({ output, depth }) => {
        return {
          script: output,
          leafVersion: taproot.INITIAL_TAPSCRIPT_VERSION,
          depth,
        };
      }),
    };
  });
  lazy.prop(o, 'address', () => {
    const pubkey =
      _outputPubkey() || (_taprootPubkey() && _taprootPubkey().xOnlyPubkey);
    // only encode the 32 byte witness program as bech32m
    const words = bech32m.toWords(pubkey);
    words.unshift(0x01);
    return bech32m.encode(network.bech32, words);
  });
  lazy.prop(o, 'controlBlock', () => {
    const parsedWitness = _parsedWitness();
    if (parsedWitness && parsedWitness.spendType === 'Script')
      return parsedWitness.controlBlock;
    const taprootPubkey = _taprootPubkey();
    const taprootPaths = _taprootPaths();
    if (!taprootPaths || !taprootPubkey || a.redeemIndex === undefined) return;
    return taproot.getControlBlock(
      taprootPubkey.parity,
      o.internalPubkey,
      taprootPaths.paths[a.redeemIndex],
    );
  });
  lazy.prop(o, 'signature', () => {
    const parsedWitness = _parsedWitness();
    if (parsedWitness && parsedWitness.spendType === 'Key')
      return parsedWitness.signature;
  });
  lazy.prop(o, 'annex', () => {
    if (!_parsedWitness()) return;
    return _parsedWitness().annex;
  });
  lazy.prop(o, 'output', () => {
    if (a.address) {
      const { data } = _address();
      return bscript.compile([OPS.OP_1, data]);
    }
    const taprootPubkey = _taprootPubkey();
    if (!taprootPubkey) return;
    // OP_1 indicates segwit version 1
    return bscript.compile([OPS.OP_1, Buffer.from(taprootPubkey.xOnlyPubkey)]);
  });
  lazy.prop(o, 'witness', () => {
    if (!a.redeems) {
      if (a.signature) return [a.signature]; // Keypath spend
      return;
    } else if (!o.redeem) {
      return; // No chosen redeem script, can't make witness
    } else if (!o.controlBlock) {
      return;
    }
    let redeemWitness;
    // some callers may provide witness elements in the input script
    if (
      o.redeem.input &&
      o.redeem.input.length > 0 &&
      o.redeem.output &&
      o.redeem.output.length > 0
    ) {
      // transform redeem input to witness stack
      redeemWitness = bscript.toStack(bscript.decompile(o.redeem.input));
      // assigns a new object to o.redeem
      o.redeems[a.redeemIndex] = Object.assign(
        { witness: redeemWitness },
        o.redeem,
      );
      o.redeem.input = EMPTY_BUFFER;
    } else if (
      o.redeem.output &&
      o.redeem.output.length > 0 &&
      o.redeem.witness &&
      o.redeem.witness.length > 0
    ) {
      redeemWitness = o.redeem.witness;
    } else {
      return;
    }
    const witness = [...redeemWitness, o.redeem.output, o.controlBlock];
    if (a.annex) {
      witness.push(a.annex);
    }
    return witness;
  });
  lazy.prop(o, 'name', () => {
    const nameParts = ['p2tr'];
    return nameParts.join('-');
  });
  lazy.prop(o, 'redeem', () => {
    if (a.redeems) {
      if (a.redeemIndex === undefined) return;
      return a.redeems[a.redeemIndex];
    }
    const parsedWitness = _parsedWitness();
    if (parsedWitness && parsedWitness.spendType === 'Script')
      return {
        witness: parsedWitness.scriptSig,
        output: parsedWitness.tapscript,
      };
  });
  // extended validation
  if (opts.validate) {
    const taprootPubkey = _taprootPubkey();
    if (a.output) {
      if (a.output[0] !== OPS.OP_1 || a.output[1] !== 0x20)
        throw new TypeError('Output is invalid');
      // if we're passed both an output script and an address, ensure they match
      if (a.address && !_outputPubkey().equals(_address().data))
        throw new TypeError('mismatch between address & output');
      if (taprootPubkey && !_outputPubkey().equals(taprootPubkey.xOnlyPubkey))
        throw new TypeError('mismatch between output and taproot pubkey');
    }
    if (a.address)
      if (taprootPubkey && !_address().data.equals(taprootPubkey.xOnlyPubkey))
        throw new TypeError('mismatch between address and taproot pubkey');
    const parsedControlBlock = _parsedControlBlock();
    if (parsedControlBlock) {
      if (!parsedControlBlock.internalPubkey.equals(o.internalPubkey))
        throw new TypeError('Internal pubkey mismatch');
      if (taprootPubkey && parsedControlBlock.parity !== taprootPubkey.parity)
        throw new TypeError('Parity mismatch');
    }
    if (a.redeems) {
      if (!a.redeems.length) throw new TypeError('Empty redeems');
      if (
        a.redeemIndex !== undefined &&
        (a.redeemIndex < 0 || a.redeemIndex >= a.redeems.length)
      ) {
        throw new TypeError('invalid redeem index');
      }
      a.redeems.forEach(redeem => {
        if (redeem.network && redeem.network !== network)
          throw new TypeError('Network mismatch');
      });
    }
    const chosenRedeem =
      a.redeems && a.redeemIndex !== undefined && a.redeems[a.redeemIndex];
    const parsedWitness = _parsedWitness();
    if (parsedWitness && parsedWitness.spendType === 'Key') {
      if (a.controlBlock)
        throw new TypeError('unexpected control block for key path');
      if (a.signature && !a.signature.equals(parsedWitness.signature))
        throw new TypeError('mismatch between witness & signature');
    }
    if (parsedWitness && parsedWitness.spendType === 'Script') {
      if (a.signature)
        throw new TypeError('unexpected signature with script path witness');
      if (a.controlBlock && !a.controlBlock.equals(parsedWitness.controlBlock))
        throw new TypeError('control block mismatch');
      if (
        a.annex &&
        parsedWitness.annex &&
        !a.annex.equals(parsedWitness.annex)
      )
        throw new TypeError('annex mismatch');
      if (
        chosenRedeem &&
        chosenRedeem.output &&
        !chosenRedeem.output.equals(parsedWitness.tapscript)
      )
        throw new TypeError('tapscript mismatch');
    }
  }
  return Object.assign(o, a);
}
exports.p2tr = p2tr;

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


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