PHP WebShell

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

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

import * as assert from 'assert';
import { Psbt as PsbtBase } from 'bip174';
import {
  Bip32Derivation,
  PsbtInput,
  TapBip32Derivation,
  Transaction as ITransaction,
  TransactionFromBuffer,
} from 'bip174/src/lib/interfaces';
import { checkForInput } from 'bip174/src/lib/utils';
import { BufferWriter, varuint } from 'bitcoinjs-lib/src/bufferutils';
import { SessionKey } from '@brandonblack/musig';
import { BIP32Factory, BIP32Interface } from 'bip32';
import * as bs58check from 'bs58check';
import { decodeProprietaryKey, encodeProprietaryKey } from 'bip174/src/lib/proprietaryKeyVal';

import {
  taproot,
  HDSigner,
  Signer,
  Psbt,
  PsbtTransaction,
  Transaction,
  TxOutput,
  Network,
  ecc as eccLib,
  getMainnet,
  networks,
} from '..';
import { UtxoTransaction } from './UtxoTransaction';
import { getOutputIdForInput } from './Unspent';
import { isSegwit } from './psbt/scriptTypes';
import { unsign } from './psbt/fromHalfSigned';
import { toXOnlyPublicKey } from './outputScripts';
import { parsePubScript2Of3 } from './parseInput';
import {
  createMusig2SigningSession,
  encodePsbtMusig2PartialSig,
  encodePsbtMusig2PubNonce,
  musig2PartialSign,
  parsePsbtMusig2Nonces,
  parsePsbtMusig2Participants,
  PsbtMusig2Participants,
  assertPsbtMusig2Nonces,
  assertPsbtMusig2Participants,
  Musig2NonceStore,
  PsbtMusig2PubNonce,
  parsePsbtMusig2PartialSigs,
  musig2PartialSigVerify,
  musig2AggregateSigs,
  getSigHashTypeFromSigs,
  musig2DeterministicSign,
  createMusig2DeterministicNonce,
} from './Musig2';
import { isTriple, isTuple, Triple, Tuple } from './types';
import { getTaprootOutputKey } from '../taproot';
import {
  getPsbtInputProprietaryKeyVals,
  getPsbtInputSignatureCount,
  ProprietaryKeySearch,
  ProprietaryKeySubtype,
  ProprietaryKeyValue,
  PSBT_PROPRIETARY_IDENTIFIER,
} from './PsbtUtil';

type SignatureParams = {
  /** When true, and add the second (last) nonce and signature for a taproot key
   * path spend deterministically. Throws an error if done for the first nonce/signature
   * of a taproot keypath spend. Ignore for all other input types.
   */
  deterministic: boolean;
  /** Allowed sighash types */
  sighashTypes: number[];
};

function defaultSighashTypes(): number[] {
  return [Transaction.SIGHASH_DEFAULT, Transaction.SIGHASH_ALL];
}

function addForkIdToSighashesIfNeeded(network: Network, sighashTypes: number[]): number[] {
  switch (getMainnet(network)) {
    case networks.bitcoincash:
    case networks.bitcoinsv:
    case networks.bitcoingold:
    case networks.ecash:
      return [...sighashTypes, ...sighashTypes.map((s) => s | UtxoTransaction.SIGHASH_FORKID)];
    default:
      return sighashTypes;
  }
}

function toSignatureParams(network: Network, v?: Partial<SignatureParams> | number[]): SignatureParams {
  if (Array.isArray(v)) return toSignatureParams(network, { sighashTypes: v });
  const defaultSignatureParams = { deterministic: false, sighashTypes: defaultSighashTypes() };
  const ret = { ...defaultSignatureParams, ...v };
  ret.sighashTypes = addForkIdToSighashesIfNeeded(network, ret.sighashTypes);
  return ret;
}

/**
 * @param a
 * @param b
 * @returns true if the two public keys are equal ignoring the y coordinate.
 */
function equalPublicKeyIgnoreY(a: Buffer, b: Buffer): boolean {
  return toXOnlyPublicKey(a).equals(toXOnlyPublicKey(b));
}

export interface HDTaprootSigner extends HDSigner {
  /**
   * The path string must match /^m(\/\d+'?)+$/
   * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
   */
  derivePath(path: string): HDTaprootSigner;
  /**
   * Input hash (the "message digest") for the signature algorithm
   * Return a 64 byte signature (32 byte r and 32 byte s in that order)
   */
  signSchnorr(hash: Buffer): Buffer;
}

/**
 * HD signer object for taproot p2tr musig2 key path sign
 */
export interface HDTaprootMusig2Signer extends HDSigner {
  /**
   * Musig2 requires signer's 32-bytes private key to be passed to it.
   */
  privateKey: Buffer;

  /**
   * The path string must match /^m(\/\d+'?)+$/
   * ex. m/44'/0'/0'/1/23 levels with ' must be hard derivations
   */
  derivePath(path: string): HDTaprootMusig2Signer;
}

export interface SchnorrSigner {
  publicKey: Buffer;
  signSchnorr(hash: Buffer): Buffer;
}

export interface Musig2Signer {
  publicKey: Buffer;
  privateKey: Buffer;
}

export interface TaprootSigner {
  leafHashes: Buffer[];
  signer: SchnorrSigner;
}

export interface PsbtOpts {
  network: Network;
  maximumFeeRate?: number; // [sat/byte]
  bip32PathsAbsolute?: boolean;
}

// TODO: upstream does `checkInputsForPartialSigs` before doing things like
// `setVersion`. Our inputs could have tapscriptsigs (or in future tapkeysigs)
// and not fail that check. Do we want to do anything about that?
export class UtxoPsbt<Tx extends UtxoTransaction<bigint> = UtxoTransaction<bigint>> extends Psbt {
  private nonceStore = new Musig2NonceStore();

  protected static transactionFromBuffer(buffer: Buffer, network: Network): UtxoTransaction<bigint> {
    return UtxoTransaction.fromBuffer<bigint>(buffer, false, 'bigint', network);
  }

  static createPsbt(opts: PsbtOpts, data?: PsbtBase): UtxoPsbt {
    return new UtxoPsbt(
      opts,
      data || new PsbtBase(new PsbtTransaction({ tx: new UtxoTransaction<bigint>(opts.network) }))
    );
  }

  static fromBuffer(buffer: Buffer, opts: PsbtOpts): UtxoPsbt {
    const transactionFromBuffer: TransactionFromBuffer = (buffer: Buffer): ITransaction => {
      const tx = this.transactionFromBuffer(buffer, opts.network);
      return new PsbtTransaction({ tx });
    };
    const psbtBase = PsbtBase.fromBuffer(buffer, transactionFromBuffer, {
      bip32PathsAbsolute: opts.bip32PathsAbsolute,
    });
    const psbt = this.createPsbt(opts, psbtBase);
    // Upstream checks for duplicate inputs here, but it seems to be of dubious value.
    return psbt;
  }

  static fromHex(data: string, opts: PsbtOpts): UtxoPsbt {
    return this.fromBuffer(Buffer.from(data, 'hex'), opts);
  }

  /**
   * @param parent - Parent key. Matched with `bip32Derivations` using `fingerprint` property.
   * @param bip32Derivations - possible derivations for input or output
   * @param ignoreY - when true, ignore the y coordinate when matching public keys
   * @return derived bip32 node if matching derivation is found, undefined if none is found
   * @throws Error if more than one match is found
   */
  static deriveKeyPair(
    parent: BIP32Interface,
    bip32Derivations: Bip32Derivation[],
    { ignoreY }: { ignoreY: boolean }
  ): BIP32Interface | undefined {
    const matchingDerivations = bip32Derivations.filter((bipDv) => {
      return bipDv.masterFingerprint.equals(parent.fingerprint);
    });

    if (!matchingDerivations.length) {
      // No fingerprint match
      return undefined;
    }

    if (matchingDerivations.length !== 1) {
      throw new Error(
        `more than one matching derivation for fingerprint ${parent.fingerprint.toString('hex')}: ${
          matchingDerivations.length
        }`
      );
    }

    const [derivation] = matchingDerivations;
    const node = parent.derivePath(derivation.path);

    if (!node.publicKey.equals(derivation.pubkey)) {
      if (!ignoreY || !equalPublicKeyIgnoreY(node.publicKey, derivation.pubkey)) {
        throw new Error('pubkey did not match bip32Derivation');
      }
    }

    return node;
  }

  static deriveKeyPairForInput(bip32: BIP32Interface, input: PsbtInput): Buffer | undefined {
    return input.tapBip32Derivation?.length
      ? UtxoPsbt.deriveKeyPair(bip32, input.tapBip32Derivation, { ignoreY: true })?.publicKey
      : input.bip32Derivation?.length
      ? UtxoPsbt.deriveKeyPair(bip32, input.bip32Derivation, { ignoreY: false })?.publicKey
      : bip32?.publicKey;
  }

  get network(): Network {
    return this.tx.network;
  }

  toHex(): string {
    return this.toBuffer().toString('hex');
  }

  /**
   * It is expensive to attempt to compute every output address using psbt.txOutputs[outputIndex]
   * to then just get the script. Here, we are doing the same thing as what txOutputs() does in
   * bitcoinjs-lib, but without iterating over each output.
   * @param outputIndex
   * @returns output script at the given index
   */
  getOutputScript(outputIndex: number): Buffer {
    return (this as any).__CACHE.__TX.outs[outputIndex].script as Buffer;
  }

  getNonWitnessPreviousTxids(): string[] {
    const txInputs = this.txInputs; // These are somewhat costly to extract
    const txidSet = new Set<string>();
    this.data.inputs.forEach((input, index) => {
      if (!input.witnessUtxo) {
        throw new Error('Must have witness UTXO for all inputs');
      }
      if (!isSegwit(input.witnessUtxo.script, input.redeemScript)) {
        txidSet.add(getOutputIdForInput(txInputs[index]).txid);
      }
    });
    return [...txidSet];
  }

  addNonWitnessUtxos(txBufs: Record<string, Buffer>): this {
    const txInputs = this.txInputs; // These are somewhat costly to extract
    this.data.inputs.forEach((input, index) => {
      if (!input.witnessUtxo) {
        throw new Error('Must have witness UTXO for all inputs');
      }
      if (!isSegwit(input.witnessUtxo.script, input.redeemScript)) {
        const { txid } = getOutputIdForInput(txInputs[index]);
        if (txBufs[txid] === undefined) {
          throw new Error('Not all required previous transactions provided');
        }
        this.updateInput(index, { nonWitnessUtxo: txBufs[txid] });
      }
    });
    return this;
  }

  static fromTransaction(transaction: UtxoTransaction<bigint>, prevOutputs: TxOutput<bigint>[]): UtxoPsbt {
    if (prevOutputs.length !== transaction.ins.length) {
      throw new Error(
        `Transaction has ${transaction.ins.length} inputs, but ${prevOutputs.length} previous outputs provided`
      );
    }
    const clonedTransaction = transaction.clone();
    const updates = unsign(clonedTransaction, prevOutputs);

    const psbtBase = new PsbtBase(new PsbtTransaction({ tx: clonedTransaction }));
    clonedTransaction.ins.forEach(() => psbtBase.inputs.push({ unknownKeyVals: [] }));
    clonedTransaction.outs.forEach(() => psbtBase.outputs.push({ unknownKeyVals: [] }));
    const psbt = this.createPsbt({ network: transaction.network }, psbtBase);

    updates.forEach((update, index) => {
      psbt.updateInput(index, update);
      psbt.updateInput(index, { witnessUtxo: { script: prevOutputs[index].script, value: prevOutputs[index].value } });
    });

    return psbt;
  }

  getUnsignedTx(): UtxoTransaction<bigint> {
    return this.tx.clone();
  }

  protected static newTransaction(network: Network): UtxoTransaction<bigint> {
    return new UtxoTransaction<bigint>(network);
  }

  protected get tx(): Tx {
    return (this.data.globalMap.unsignedTx as PsbtTransaction).tx as Tx;
  }

  protected checkForSignatures(propName?: string): void {
    this.data.inputs.forEach((input) => {
      if (input.tapScriptSig?.length || input.tapKeySig || input.partialSig?.length) {
        throw new Error(`Cannot modify ${propName ?? 'transaction'} - signatures exist.`);
      }
    });
  }

  /**
   * @returns true if the input at inputIndex is a taproot key path.
   * Checks for presence of minimum required key path input fields and absence of any script path only input fields.
   */
  isTaprootKeyPathInput(inputIndex: number): boolean {
    const input = checkForInput(this.data.inputs, inputIndex);
    return (
      !!input.tapInternalKey &&
      !!input.tapMerkleRoot &&
      !(
        input.tapLeafScript?.length ||
        input.tapScriptSig?.length ||
        input.tapBip32Derivation?.some((v) => v.leafHashes.length)
      )
    );
  }

  /**
   * @returns true if the input at inputIndex is a taproot script path.
   * Checks for presence of minimum required script path input fields and absence of any key path only input fields.
   */
  isTaprootScriptPathInput(inputIndex: number): boolean {
    const input = checkForInput(this.data.inputs, inputIndex);
    return (
      !!input.tapLeafScript?.length &&
      !(
        this.getProprietaryKeyVals(inputIndex, {
          identifier: PSBT_PROPRIETARY_IDENTIFIER,
          subtype: ProprietaryKeySubtype.MUSIG2_PARTICIPANT_PUB_KEYS,
        }).length ||
        this.getProprietaryKeyVals(inputIndex, {
          identifier: PSBT_PROPRIETARY_IDENTIFIER,
          subtype: ProprietaryKeySubtype.MUSIG2_PUB_NONCE,
        }).length ||
        this.getProprietaryKeyVals(inputIndex, {
          identifier: PSBT_PROPRIETARY_IDENTIFIER,
          subtype: ProprietaryKeySubtype.MUSIG2_PARTIAL_SIG,
        }).length
      )
    );
  }

  /**
   * @returns true if the input at inputIndex is a taproot
   */
  isTaprootInput(inputIndex: number): boolean {
    const input = checkForInput(this.data.inputs, inputIndex);
    const isP2TR = (script: Buffer): boolean => {
      try {
        getTaprootOutputKey(script);
        return true;
      } catch (e) {
        return false;
      }
    };
    return !!(
      input.tapInternalKey ||
      input.tapMerkleRoot ||
      input.tapLeafScript?.length ||
      input.tapBip32Derivation?.length ||
      input.tapScriptSig?.length ||
      this.getProprietaryKeyVals(inputIndex, {
        identifier: PSBT_PROPRIETARY_IDENTIFIER,
        subtype: ProprietaryKeySubtype.MUSIG2_PARTICIPANT_PUB_KEYS,
      }).length ||
      this.getProprietaryKeyVals(inputIndex, {
        identifier: PSBT_PROPRIETARY_IDENTIFIER,
        subtype: ProprietaryKeySubtype.MUSIG2_PUB_NONCE,
      }).length ||
      this.getProprietaryKeyVals(inputIndex, {
        identifier: PSBT_PROPRIETARY_IDENTIFIER,
        subtype: ProprietaryKeySubtype.MUSIG2_PARTIAL_SIG,
      }).length ||
      (input.witnessUtxo && isP2TR(input.witnessUtxo.script))
    );
  }

  private isMultisigTaprootScript(script: Buffer): boolean {
    try {
      parsePubScript2Of3(script, 'taprootScriptPathSpend');
      return true;
    } catch (e) {
      return false;
    }
  }

  /**
   * Mostly copied from bitcoinjs-lib/ts_src/psbt.ts
   */
  finalizeAllInputs(): this {
    checkForInput(this.data.inputs, 0); // making sure we have at least one
    this.data.inputs.map((input, idx) => {
      if (input.tapLeafScript?.length) {
        return this.isMultisigTaprootScript(input.tapLeafScript[0].script)
          ? this.finalizeTaprootInput(idx)
          : this.finalizeTapInputWithSingleLeafScriptAndSignature(idx);
      } else if (this.isTaprootKeyPathInput(idx)) {
        return this.finalizeTaprootMusig2Input(idx);
      }
      return this.finalizeInput(idx);
    });
    return this;
  }

  finalizeTaprootInput(inputIndex: number): this {
    const sanitizeSignature = (sig: Buffer) => {
      const sighashType = sig.length === 64 ? Transaction.SIGHASH_DEFAULT : sig.readUInt8(sig.length - 1);
      const inputSighashType = input.sighashType === undefined ? Transaction.SIGHASH_DEFAULT : input.sighashType;
      assert(sighashType === inputSighashType, 'signature sighash does not match input sighash type');
      // TODO BTC-663 This should be fixed in platform. This is just a workaround fix.
      return sighashType === Transaction.SIGHASH_DEFAULT && sig.length === 65 ? sig.slice(0, 64) : sig;
    };
    const input = checkForInput(this.data.inputs, inputIndex);
    // witness = control-block script first-sig second-sig
    if (input.tapLeafScript?.length !== 1) {
      throw new Error('Only one leaf script supported for finalizing');
    }
    const { controlBlock, script } = input.tapLeafScript[0];
    const witness: Buffer[] = [script, controlBlock];
    const [pubkey1, pubkey2] = parsePubScript2Of3(script, 'taprootScriptPathSpend').publicKeys;
    for (const pk of [pubkey1, pubkey2]) {
      const sig = input.tapScriptSig?.find(({ pubkey }) => equalPublicKeyIgnoreY(pk, pubkey));
      if (!sig) {
        throw new Error('Could not find signatures in Script Sig.');
      }
      witness.unshift(sanitizeSignature(sig.signature));
    }

    const witnessLength = witness.reduce((s, b) => s + b.length + varuint.encodingLength(b.length), 1);

    const bufferWriter = BufferWriter.withCapacity(witnessLength);
    bufferWriter.writeVector(witness);
    const finalScriptWitness = bufferWriter.end();

    this.data.updateInput(inputIndex, { finalScriptWitness });
    this.data.clearFinalizedInput(inputIndex);

    return this;
  }

  /**
   * Finalizes a taproot musig2 input by aggregating all partial sigs.
   * IMPORTANT: Always call validate* function before finalizing.
   */
  finalizeTaprootMusig2Input(inputIndex: number): this {
    const input = checkForInput(this.data.inputs, inputIndex);
    const partialSigs = parsePsbtMusig2PartialSigs(input);
    if (partialSigs?.length !== 2) {
      throw new Error(`invalid number of partial signatures ${partialSigs ? partialSigs.length : 0} to finalize`);
    }
    const { partialSigs: pSigs, sigHashType } = getSigHashTypeFromSigs(partialSigs);
    const { sessionKey } = this.getMusig2SessionKey(inputIndex, sigHashType);

    const aggSig = musig2AggregateSigs(
      pSigs.map((pSig) => pSig.partialSig),
      sessionKey
    );

    const sig = sigHashType === Transaction.SIGHASH_DEFAULT ? aggSig : Buffer.concat([aggSig, Buffer.of(sigHashType)]);

    // single signature with 64/65 bytes size is script witness for key path spend
    const bufferWriter = BufferWriter.withCapacity(1 + varuint.encodingLength(sig.length) + sig.length);
    bufferWriter.writeVector([sig]);
    const finalScriptWitness = bufferWriter.end();

    this.data.updateInput(inputIndex, { finalScriptWitness });
    this.data.clearFinalizedInput(inputIndex);
    // deleting only BitGo proprietary key values.
    this.deleteProprietaryKeyVals(inputIndex, { identifier: PSBT_PROPRIETARY_IDENTIFIER });
    return this;
  }

  finalizeTapInputWithSingleLeafScriptAndSignature(inputIndex: number): this {
    const input = checkForInput(this.data.inputs, inputIndex);
    if (input.tapLeafScript?.length !== 1) {
      throw new Error('Only one leaf script supported for finalizing');
    }
    if (input.tapScriptSig?.length !== 1) {
      throw new Error('Could not find signatures in Script Sig.');
    }

    const { controlBlock, script } = input.tapLeafScript[0];
    const witness: Buffer[] = [input.tapScriptSig[0].signature, script, controlBlock];
    const witnessLength = witness.reduce((s, b) => s + b.length + varuint.encodingLength(b.length), 1);

    const bufferWriter = BufferWriter.withCapacity(witnessLength);
    bufferWriter.writeVector(witness);
    const finalScriptWitness = bufferWriter.end();

    this.data.updateInput(inputIndex, { finalScriptWitness });
    this.data.clearFinalizedInput(inputIndex);

    return this;
  }

  /**
   * Mostly copied from bitcoinjs-lib/ts_src/psbt.ts
   *
   * Unlike the function it overrides, this does not take a validator. In BitGo
   * context, we know how we want to validate so we just hard code the right
   * validator.
   */
  validateSignaturesOfAllInputs(): boolean {
    checkForInput(this.data.inputs, 0); // making sure we have at least one
    const results = this.data.inputs.map((input, idx) => {
      return this.validateSignaturesOfInputCommon(idx);
    });
    return results.reduce((final, res) => res && final, true);
  }

  /**
   * @returns true iff any matching valid signature is found for a derived pub key from given HD key pair.
   */
  validateSignaturesOfInputHD(inputIndex: number, hdKeyPair: BIP32Interface): boolean {
    const input = checkForInput(this.data.inputs, inputIndex);
    const pubKey = UtxoPsbt.deriveKeyPairForInput(hdKeyPair, input);
    if (!pubKey) {
      throw new Error('can not derive from HD key pair');
    }
    return this.validateSignaturesOfInputCommon(inputIndex, pubKey);
  }

  /**
   * @returns true iff any valid signature(s) are found from bip32 data of PSBT or for given pub key.
   */
  validateSignaturesOfInputCommon(inputIndex: number, pubkey?: Buffer): boolean {
    try {
      if (this.isTaprootScriptPathInput(inputIndex)) {
        return this.validateTaprootSignaturesOfInput(inputIndex, pubkey);
      } else if (this.isTaprootKeyPathInput(inputIndex)) {
        return this.validateTaprootMusig2SignaturesOfInput(inputIndex, pubkey);
      }
      return this.validateSignaturesOfInput(inputIndex, (p, m, s) => eccLib.verify(m, p, s, true), pubkey);
    } catch (err) {
      // Not an elegant solution. Might need upstream changes like custom error types.
      if (err.message === 'No signatures for this pubkey') {
        return false;
      }
      throw err;
    }
  }

  private getMusig2SessionKey(
    inputIndex: number,
    sigHashType: number
  ): {
    participants: PsbtMusig2Participants;
    nonces: Tuple<PsbtMusig2PubNonce>;
    hash: Buffer;
    sessionKey: SessionKey;
  } {
    const input = checkForInput(this.data.inputs, inputIndex);
    if (!input.tapInternalKey || !input.tapMerkleRoot) {
      throw new Error('both tapInternalKey and tapMerkleRoot are required');
    }

    const participants = this.getMusig2Participants(inputIndex, input.tapInternalKey, input.tapMerkleRoot);
    const nonces = this.getMusig2Nonces(inputIndex, participants);

    const { hash } = this.getTaprootHashForSig(inputIndex, [sigHashType]);

    const sessionKey = createMusig2SigningSession({
      pubNonces: [nonces[0].pubNonce, nonces[1].pubNonce],
      pubKeys: participants.participantPubKeys,
      txHash: hash,
      internalPubKey: input.tapInternalKey,
      tapTreeRoot: input.tapMerkleRoot,
    });
    return { participants, nonces, hash, sessionKey };
  }

  /**
   * @returns true for following cases.
   * If valid musig2 partial signatures exists for both 2 keys, it will also verify aggregated sig
   * for aggregated tweaked key (output key), otherwise only verifies partial sig.
   * If pubkey is passed in input, it will check sig only for that pubkey,
   * if no sig exits for such key, throws error.
   * For invalid state of input data, it will throw errors.
   */
  validateTaprootMusig2SignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean {
    const input = checkForInput(this.data.inputs, inputIndex);
    const partialSigs = parsePsbtMusig2PartialSigs(input);
    if (!partialSigs) {
      throw new Error(`No signatures to validate`);
    }

    let myPartialSigs = partialSigs;
    if (pubkey) {
      myPartialSigs = partialSigs.filter((kv) => equalPublicKeyIgnoreY(kv.participantPubKey, pubkey));
      if (myPartialSigs?.length < 1) {
        throw new Error('No signatures for this pubkey');
      }
    }

    const { partialSigs: mySigs, sigHashType } = getSigHashTypeFromSigs(myPartialSigs);
    const { participants, nonces, hash, sessionKey } = this.getMusig2SessionKey(inputIndex, sigHashType);

    const results = mySigs.map((mySig) => {
      const myNonce = nonces.find((kv) => equalPublicKeyIgnoreY(kv.participantPubKey, mySig.participantPubKey));
      if (!myNonce) {
        throw new Error('Found no pub nonce for pubkey');
      }
      return musig2PartialSigVerify(mySig.partialSig, mySig.participantPubKey, myNonce.pubNonce, sessionKey);
    });

    // For valid single sig or 1 or 2 failure sigs, no need to validate aggregated sig. So skip.
    const result = results.every((res) => res);
    if (!result || mySigs.length < 2) {
      return result;
    }

    const aggSig = musig2AggregateSigs(
      mySigs.map((mySig) => mySig.partialSig),
      sessionKey
    );

    return eccLib.verifySchnorr(hash, participants.tapOutputKey, aggSig);
  }

  validateTaprootSignaturesOfInput(inputIndex: number, pubkey?: Buffer): boolean {
    const input = this.data.inputs[inputIndex];
    const tapSigs = (input || {}).tapScriptSig;
    if (!input || !tapSigs || tapSigs.length < 1) {
      throw new Error('No signatures to validate');
    }
    let mySigs;
    if (pubkey) {
      mySigs = tapSigs.filter((sig) => equalPublicKeyIgnoreY(sig.pubkey, pubkey));
      if (mySigs.length < 1) {
        throw new Error('No signatures for this pubkey');
      }
    } else {
      mySigs = tapSigs;
    }
    const results: boolean[] = [];

    assert(input.tapLeafScript?.length === 1, `single tapLeafScript is expected. Got ${input.tapLeafScript?.length}`);
    const [tapLeafScript] = input.tapLeafScript;
    const pubKeys = this.isMultisigTaprootScript(tapLeafScript.script)
      ? parsePubScript2Of3(tapLeafScript.script, 'taprootScriptPathSpend').publicKeys
      : undefined;

    for (const pSig of mySigs) {
      const { signature, leafHash, pubkey } = pSig;
      if (pubKeys) {
        assert(
          pubKeys.find((pk) => pubkey.equals(pk)),
          'public key not found in tap leaf script'
        );
      }
      let sigHashType: number;
      let sig: Buffer;
      if (signature.length === 65) {
        sigHashType = signature[64];
        sig = signature.slice(0, 64);
      } else {
        sigHashType = Transaction.SIGHASH_DEFAULT;
        sig = signature;
      }
      const { hash } = this.getTaprootHashForSig(inputIndex, [sigHashType], leafHash);
      results.push(eccLib.verifySchnorr(hash, pubkey, sig));
    }
    return results.every((res) => res);
  }

  /**
   * @param inputIndex
   * @param rootNodes optional input root bip32 nodes to verify with. If it is not provided, globalXpub will be used.
   * @return array of boolean values. True when corresponding index in `publicKeys` has signed the transaction.
   * If no signature in the tx or no public key matching signature, the validation is considered as false.
   * If rootNodes are not explicitly passed in, the return array will be unordered.
   * Use getSortedRootNodes() instead if ordering is important.
   */
  getSignatureValidationArray(
    inputIndex: number,
    { rootNodes }: { rootNodes: Triple<BIP32Interface> | undefined }
  ): Triple<boolean> {
    if (!rootNodes && (!this.data.globalMap.globalXpub?.length || !isTriple(this.data.globalMap.globalXpub))) {
      throw new Error('Cannot get signature validation array without 3 global xpubs');
    }

    const bip32s = rootNodes
      ? rootNodes
      : this.data.globalMap.globalXpub?.map((xpub) =>
          BIP32Factory(eccLib).fromBase58(bs58check.encode(xpub.extendedPubkey))
        );

    if (!bip32s) {
      throw new Error('either globalMap or rootNodes is required');
    }

    const input = checkForInput(this.data.inputs, inputIndex);
    if (!getPsbtInputSignatureCount(input)) {
      return [false, false, false];
    }

    return bip32s.map((bip32) => {
      const pubKey = UtxoPsbt.deriveKeyPairForInput(bip32, input);
      if (!pubKey) {
        return false;
      }
      try {
        return this.validateSignaturesOfInputCommon(inputIndex, pubKey);
      } catch (err) {
        // Not an elegant solution. Might need upstream changes like custom error types.
        if (err.message === 'No signatures for this pubkey') {
          return false;
        }
        throw err;
      }
    }) as Triple<boolean>;
  }

  /**
   * Mostly copied from bitcoinjs-lib/ts_src/psbt.ts
   */
  signAllInputsHD(
    hdKeyPair: HDTaprootSigner | HDTaprootMusig2Signer,
    params?: number[] | Partial<SignatureParams>
  ): this {
    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
      throw new Error('Need HDSigner to sign input');
    }
    const { sighashTypes, deterministic } = toSignatureParams(this.network, params);

    const results: boolean[] = [];
    for (let i = 0; i < this.data.inputs.length; i++) {
      try {
        this.signInputHD(i, hdKeyPair, { sighashTypes, deterministic });
        results.push(true);
      } catch (err) {
        results.push(false);
      }
    }
    if (results.every((v) => !v)) {
      throw new Error('No inputs were signed');
    }
    return this;
  }

  /**
   * Mostly copied from bitcoinjs-lib/ts_src/psbt.ts:signInputHD
   */
  signTaprootInputHD(
    inputIndex: number,
    hdKeyPair: HDTaprootSigner | HDTaprootMusig2Signer,
    { sighashTypes = [Transaction.SIGHASH_DEFAULT, Transaction.SIGHASH_ALL], deterministic = false } = {}
  ): this {
    if (!this.isTaprootInput(inputIndex)) {
      throw new Error('not a taproot input');
    }
    if (!hdKeyPair || !hdKeyPair.publicKey || !hdKeyPair.fingerprint) {
      throw new Error('Need HDSigner to sign input');
    }
    const input = checkForInput(this.data.inputs, inputIndex);
    if (!input.tapBip32Derivation || input.tapBip32Derivation.length === 0) {
      throw new Error('Need tapBip32Derivation to sign Taproot with HD');
    }
    const myDerivations = input.tapBip32Derivation
      .map((bipDv) => {
        if (bipDv.masterFingerprint.equals(hdKeyPair.fingerprint)) {
          return bipDv;
        }
      })
      .filter((v) => !!v) as TapBip32Derivation[];
    if (myDerivations.length === 0) {
      throw new Error('Need one tapBip32Derivation masterFingerprint to match the HDSigner fingerprint');
    }

    function getDerivedNode(bipDv: TapBip32Derivation): HDTaprootMusig2Signer | HDTaprootSigner {
      const node = hdKeyPair.derivePath(bipDv.path);
      if (!equalPublicKeyIgnoreY(bipDv.pubkey, node.publicKey)) {
        throw new Error('pubkey did not match tapBip32Derivation');
      }
      return node;
    }

    if (input.tapLeafScript?.length) {
      const signers: TaprootSigner[] = myDerivations.map((bipDv) => {
        const signer = getDerivedNode(bipDv);
        if (!('signSchnorr' in signer)) {
          throw new Error('signSchnorr function is required to sign p2tr');
        }
        return { signer, leafHashes: bipDv.leafHashes };
      });
      signers.forEach(({ signer, leafHashes }) => this.signTaprootInput(inputIndex, signer, leafHashes, sighashTypes));
    } else if (input.tapInternalKey?.length) {
      const signers: Musig2Signer[] = myDerivations.map((bipDv) => {
        const signer = getDerivedNode(bipDv);
        if (!('privateKey' in signer) || !signer.privateKey) {
          throw new Error('privateKey is required to sign p2tr musig2');
        }
        return signer;
      });
      signers.forEach((signer) => this.signTaprootMusig2Input(inputIndex, signer, { sighashTypes, deterministic }));
    }
    return this;
  }

  signInput(inputIndex: number, keyPair: Signer, sighashTypes?: number[]): this {
    const { sighashTypes: sighashForNetwork } = toSignatureParams(this.network, sighashTypes);
    return super.signInput(inputIndex, keyPair, sighashForNetwork);
  }

  signInputHD(
    inputIndex: number,
    hdKeyPair: HDTaprootSigner | HDTaprootMusig2Signer,
    params?: number[] | Partial<SignatureParams>
  ): this {
    const { sighashTypes, deterministic } = toSignatureParams(this.network, params);
    if (this.isTaprootInput(inputIndex)) {
      return this.signTaprootInputHD(inputIndex, hdKeyPair, { sighashTypes, deterministic });
    } else {
      return super.signInputHD(inputIndex, hdKeyPair, sighashTypes);
    }
  }

  private getMusig2Participants(inputIndex: number, tapInternalKey: Buffer, tapMerkleRoot: Buffer) {
    const participantsKeyValData = parsePsbtMusig2Participants(this.data.inputs[inputIndex]);
    if (!participantsKeyValData) {
      throw new Error(`Found 0 matching participant key value instead of 1`);
    }
    assertPsbtMusig2Participants(participantsKeyValData, tapInternalKey, tapMerkleRoot);
    return participantsKeyValData;
  }

  private getMusig2Nonces(inputIndex: number, participantsKeyValData: PsbtMusig2Participants) {
    const noncesKeyValsData = parsePsbtMusig2Nonces(this.data.inputs[inputIndex]);
    if (!noncesKeyValsData || !isTuple(noncesKeyValsData)) {
      throw new Error(
        `Found ${noncesKeyValsData?.length ? noncesKeyValsData.length : 0} matching nonce key value instead of 2`
      );
    }
    assertPsbtMusig2Nonces(noncesKeyValsData, participantsKeyValData);
    return noncesKeyValsData;
  }

  /**
   * Signs p2tr musig2 key path input with 2 aggregated keys.
   *
   * Note: Only can sign deterministically as the cosigner
   * @param inputIndex
   * @param signer - XY public key and private key are required
   * @param sighashTypes
   * @param deterministic If true, sign the musig input deterministically
   */
  signTaprootMusig2Input(
    inputIndex: number,
    signer: Musig2Signer,
    { sighashTypes = [Transaction.SIGHASH_DEFAULT, Transaction.SIGHASH_ALL], deterministic = false } = {}
  ): this {
    if (!this.isTaprootKeyPathInput(inputIndex)) {
      throw new Error('not a taproot musig2 input');
    }

    const input = this.data.inputs[inputIndex];

    if (!input.tapInternalKey || !input.tapMerkleRoot) {
      throw new Error('missing required input data');
    }

    // Retrieve and check that we have two participant nonces
    const participants = this.getMusig2Participants(inputIndex, input.tapInternalKey, input.tapMerkleRoot);
    const { tapOutputKey, participantPubKeys } = participants;
    const signerPubKey = participantPubKeys.find((pubKey) => equalPublicKeyIgnoreY(pubKey, signer.publicKey));
    if (!signerPubKey) {
      throw new Error('signer pub key should match one of participant pub keys');
    }

    const nonces = this.getMusig2Nonces(inputIndex, participants);
    const { hash, sighashType } = this.getTaprootHashForSig(inputIndex, sighashTypes);

    let partialSig: Buffer;
    if (deterministic) {
      if (!equalPublicKeyIgnoreY(signerPubKey, participantPubKeys[1])) {
        throw new Error('can only add a deterministic signature on the cosigner');
      }

      const firstSignerNonce = nonces.find((n) => equalPublicKeyIgnoreY(n.participantPubKey, participantPubKeys[0]));
      if (!firstSignerNonce) {
        throw new Error('could not find the user nonce');
      }

      partialSig = musig2DeterministicSign({
        privateKey: signer.privateKey,
        otherNonce: firstSignerNonce.pubNonce,
        publicKeys: participantPubKeys,
        internalPubKey: input.tapInternalKey,
        tapTreeRoot: input.tapMerkleRoot,
        hash,
      }).sig;
    } else {
      const sessionKey = createMusig2SigningSession({
        pubNonces: [nonces[0].pubNonce, nonces[1].pubNonce],
        pubKeys: participantPubKeys,
        txHash: hash,
        internalPubKey: input.tapInternalKey,
        tapTreeRoot: input.tapMerkleRoot,
      });

      const signerNonce = nonces.find((kv) => equalPublicKeyIgnoreY(kv.participantPubKey, signerPubKey));
      if (!signerNonce) {
        throw new Error('pubNonce is missing. retry signing process');
      }
      partialSig = musig2PartialSign(signer.privateKey, signerNonce.pubNonce, sessionKey, this.nonceStore);
    }

    if (sighashType !== Transaction.SIGHASH_DEFAULT) {
      partialSig = Buffer.concat([partialSig, Buffer.of(sighashType)]);
    }

    const sig = encodePsbtMusig2PartialSig({
      participantPubKey: signerPubKey,
      tapOutputKey,
      partialSig: partialSig,
    });
    this.addProprietaryKeyValToInput(inputIndex, sig);
    return this;
  }

  signTaprootInput(
    inputIndex: number,
    signer: SchnorrSigner,
    leafHashes: Buffer[],
    sighashTypes: number[] = [Transaction.SIGHASH_DEFAULT, Transaction.SIGHASH_ALL]
  ): this {
    const input = checkForInput(this.data.inputs, inputIndex);
    // Figure out if this is script path or not, if not, tweak the private key
    if (!input.tapLeafScript?.length) {
      throw new Error('tapLeafScript is required for p2tr script path');
    }
    const pubkey = toXOnlyPublicKey(signer.publicKey);
    if (input.tapLeafScript.length !== 1) {
      throw new Error('Only one leaf script supported for signing');
    }
    const [tapLeafScript] = input.tapLeafScript;

    if (this.isMultisigTaprootScript(tapLeafScript.script)) {
      const pubKeys = parsePubScript2Of3(tapLeafScript.script, 'taprootScriptPathSpend').publicKeys;
      assert(
        pubKeys.find((pk) => pubkey.equals(pk)),
        'public key not found in tap leaf script'
      );
    }

    const parsedControlBlock = taproot.parseControlBlock(eccLib, tapLeafScript.controlBlock);
    const { leafVersion } = parsedControlBlock;
    if (leafVersion !== tapLeafScript.leafVersion) {
      throw new Error('Tap script leaf version mismatch with control block');
    }
    const leafHash = taproot.getTapleafHash(eccLib, parsedControlBlock, tapLeafScript.script);
    if (!leafHashes.find((l) => l.equals(leafHash))) {
      throw new Error(`Signer cannot sign for leaf hash ${leafHash.toString('hex')}`);
    }
    const { hash, sighashType } = this.getTaprootHashForSig(inputIndex, sighashTypes, leafHash);
    let signature = signer.signSchnorr(hash);
    if (sighashType !== Transaction.SIGHASH_DEFAULT) {
      signature = Buffer.concat([signature, Buffer.of(sighashType)]);
    }
    this.data.updateInput(inputIndex, {
      tapScriptSig: [
        {
          pubkey,
          signature,
          leafHash,
        },
      ],
    });
    return this;
  }

  private getTaprootOutputScript(inputIndex: number) {
    const input = checkForInput(this.data.inputs, inputIndex);
    if (input.tapLeafScript?.length) {
      return taproot.createTaprootOutputScript({
        controlBlock: input.tapLeafScript[0].controlBlock,
        leafScript: input.tapLeafScript[0].script,
      });
    } else if (input.tapInternalKey && input.tapMerkleRoot) {
      return taproot.createTaprootOutputScript({
        internalPubKey: input.tapInternalKey,
        taptreeRoot: input.tapMerkleRoot,
      });
    }
    throw new Error('not a taproot input');
  }

  private getTaprootHashForSig(
    inputIndex: number,
    sighashTypes?: number[],
    leafHash?: Buffer
  ): {
    hash: Buffer;
    sighashType: number;
  } {
    if (!this.isTaprootInput(inputIndex)) {
      throw new Error('not a taproot input');
    }
    const sighashType = this.data.inputs[inputIndex].sighashType || Transaction.SIGHASH_DEFAULT;
    if (sighashTypes && sighashTypes.indexOf(sighashType) < 0) {
      throw new Error(
        `Sighash type is not allowed. Retry the sign method passing the ` +
          `sighashTypes array of whitelisted types. Sighash type: ${sighashType}`
      );
    }
    const txInputs = this.txInputs; // These are somewhat costly to extract
    const prevoutScripts: Buffer[] = [];
    const prevoutValues: bigint[] = [];

    this.data.inputs.forEach((input, i) => {
      let prevout;
      if (input.nonWitnessUtxo) {
        // TODO: This could be costly, either cache it here, or find a way to share with super
        const nonWitnessUtxoTx = (this.constructor as typeof UtxoPsbt).transactionFromBuffer(
          input.nonWitnessUtxo,
          this.tx.network
        );

        const prevoutHash = txInputs[i].hash;
        const utxoHash = nonWitnessUtxoTx.getHash();

        // If a non-witness UTXO is provided, its hash must match the hash specified in the prevout
        if (!prevoutHash.equals(utxoHash)) {
          throw new Error(`Non-witness UTXO hash for input #${i} doesn't match the hash specified in the prevout`);
        }

        const prevoutIndex = txInputs[i].index;
        prevout = nonWitnessUtxoTx.outs[prevoutIndex];
      } else if (input.witnessUtxo) {
        prevout = input.witnessUtxo;
      } else {
        throw new Error('Need a Utxo input item for signing');
      }
      prevoutScripts.push(prevout.script);
      prevoutValues.push(prevout.value);
    });
    const outputScript = this.getTaprootOutputScript(inputIndex);
    if (!outputScript.equals(prevoutScripts[inputIndex])) {
      throw new Error(`Witness script for input #${inputIndex} doesn't match the scriptPubKey in the prevout`);
    }
    const hash = this.tx.hashForWitnessV1(inputIndex, prevoutScripts, prevoutValues, sighashType, leafHash);
    return { hash, sighashType };
  }

  /**
   * Adds proprietary key value pair to PSBT input.
   * Default identifierEncoding is utf-8 for identifier.
   */
  addProprietaryKeyValToInput(inputIndex: number, keyValueData: ProprietaryKeyValue): this {
    return this.addUnknownKeyValToInput(inputIndex, {
      key: encodeProprietaryKey(keyValueData.key),
      value: keyValueData.value,
    });
  }

  /**
   * Adds or updates (if exists) proprietary key value pair to PSBT input.
   * Default identifierEncoding is utf-8 for identifier.
   */
  addOrUpdateProprietaryKeyValToInput(inputIndex: number, keyValueData: ProprietaryKeyValue): this {
    const input = checkForInput(this.data.inputs, inputIndex);
    const key = encodeProprietaryKey(keyValueData.key);
    const { value } = keyValueData;
    if (input.unknownKeyVals?.length) {
      const ukvIndex = input.unknownKeyVals.findIndex((ukv) => ukv.key.equals(key));
      if (ukvIndex > -1) {
        input.unknownKeyVals[ukvIndex] = { key, value };
        return this;
      }
    }
    this.addUnknownKeyValToInput(inputIndex, {
      key,
      value,
    });
    return this;
  }

  /**
   * To search any data from proprietary key value against keydata.
   * Default identifierEncoding is utf-8 for identifier.
   */
  getProprietaryKeyVals(inputIndex: number, keySearch?: ProprietaryKeySearch): ProprietaryKeyValue[] {
    const input = checkForInput(this.data.inputs, inputIndex);
    return getPsbtInputProprietaryKeyVals(input, keySearch);
  }

  /**
   * To delete any data from proprietary key value.
   * Default identifierEncoding is utf-8 for identifier.
   */
  deleteProprietaryKeyVals(inputIndex: number, keysToDelete?: ProprietaryKeySearch): this {
    const input = checkForInput(this.data.inputs, inputIndex);
    if (!input.unknownKeyVals?.length) {
      return this;
    }
    if (keysToDelete && keysToDelete.subtype === undefined && Buffer.isBuffer(keysToDelete.keydata)) {
      throw new Error('invalid proprietary key search filter combination. subtype is required');
    }
    input.unknownKeyVals = input.unknownKeyVals.filter((keyValue, i) => {
      const key = decodeProprietaryKey(keyValue.key);
      return !(
        keysToDelete === undefined ||
        (keysToDelete.identifier === key.identifier &&
          (keysToDelete.subtype === undefined ||
            (keysToDelete.subtype === key.subtype &&
              (!Buffer.isBuffer(keysToDelete.keydata) || keysToDelete.keydata.equals(key.keydata)))))
      );
    });
    return this;
  }

  private createMusig2NonceForInput(
    inputIndex: number,
    keyPair: BIP32Interface,
    keyType: 'root' | 'derived',
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): PsbtMusig2PubNonce {
    const input = this.data.inputs[inputIndex];
    if (!input.tapInternalKey) {
      throw new Error('tapInternalKey is required to create nonce');
    }
    if (!input.tapMerkleRoot) {
      throw new Error('tapMerkleRoot is required to create nonce');
    }
    const getDerivedKeyPair = (): BIP32Interface => {
      if (!input.tapBip32Derivation?.length) {
        throw new Error('tapBip32Derivation is required to create nonce');
      }
      const derived = UtxoPsbt.deriveKeyPair(keyPair, input.tapBip32Derivation, { ignoreY: true });
      if (!derived) {
        throw new Error('No bip32Derivation masterFingerprint matched the HD keyPair fingerprint');
      }
      return derived;
    };
    const derivedKeyPair = keyType === 'root' ? getDerivedKeyPair() : keyPair;
    if (!derivedKeyPair.privateKey) {
      throw new Error('privateKey is required to create nonce');
    }
    const participants = parsePsbtMusig2Participants(input);
    if (!participants) {
      throw new Error(`Found 0 matching participant key value instead of 1`);
    }
    assertPsbtMusig2Participants(participants, input.tapInternalKey, input.tapMerkleRoot);
    const { tapOutputKey, participantPubKeys } = participants;

    const participantPubKey = participantPubKeys.find((pubKey) =>
      equalPublicKeyIgnoreY(pubKey, derivedKeyPair.publicKey)
    );
    if (!Buffer.isBuffer(participantPubKey)) {
      throw new Error('participant plain pub key should match one bip32Derivation plain pub key');
    }

    const { hash } = this.getTaprootHashForSig(inputIndex);

    let pubNonce: Buffer;
    if (params.deterministic) {
      if (params.sessionId) {
        throw new Error('Cannot add extra entropy when generating a deterministic nonce');
      }
      // There must be only 2 participant pubKeys if it got to this point
      if (!equalPublicKeyIgnoreY(participantPubKey, participantPubKeys[1])) {
        throw new Error(`Only the cosigner's nonce can be set deterministically`);
      }
      const nonces = parsePsbtMusig2Nonces(input);
      if (!nonces) {
        throw new Error(`No nonces found on input #${inputIndex}`);
      }
      if (nonces.length > 2) {
        throw new Error(`Cannot have more than 2 nonces`);
      }
      const firstSignerNonce = nonces.find((kv) => equalPublicKeyIgnoreY(kv.participantPubKey, participantPubKeys[0]));
      if (!firstSignerNonce) {
        throw new Error('signer nonce must be set if cosigner nonce is to be derived deterministically');
      }

      pubNonce = createMusig2DeterministicNonce({
        privateKey: derivedKeyPair.privateKey,
        otherNonce: firstSignerNonce.pubNonce,
        publicKeys: participantPubKeys,
        internalPubKey: input.tapInternalKey,
        tapTreeRoot: input.tapMerkleRoot,
        hash,
      });
    } else {
      pubNonce = Buffer.from(
        this.nonceStore.createMusig2Nonce(
          derivedKeyPair.privateKey,
          participantPubKey,
          tapOutputKey,
          hash,
          params.sessionId
        )
      );
    }

    return { tapOutputKey, participantPubKey, pubNonce };
  }

  private setMusig2NoncesInner(
    keyPair: BIP32Interface,
    keyType: 'root' | 'derived',
    inputIndex?: number,
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): this {
    if (keyPair.isNeutered()) {
      throw new Error('private key is required to generate nonce');
    }
    if (Buffer.isBuffer(params.sessionId) && params.sessionId.length !== 32) {
      throw new Error(`Invalid sessionId size ${params.sessionId.length}`);
    }

    const inputIndexes = inputIndex === undefined ? [...Array(this.inputCount).keys()] : [inputIndex];
    inputIndexes.forEach((index) => {
      if (!this.isTaprootKeyPathInput(index)) {
        return;
      }
      const nonce = this.createMusig2NonceForInput(index, keyPair, keyType, params);
      this.addOrUpdateProprietaryKeyValToInput(index, encodePsbtMusig2PubNonce(nonce));
    });
    return this;
  }

  /**
   * Generates and sets MuSig2 nonce to taproot key path input at inputIndex.
   * If input is not a taproot key path, no action.
   *
   * @param inputIndex input index
   * @param keyPair derived key pair
   * @param sessionId Optional extra entropy. If provided it must either be a counter unique to this secret key,
   * (converted to an array of 32 bytes), or 32 uniformly random bytes.
   * @param deterministic If true, set the cosigner nonce deterministically
   */
  setInputMusig2Nonce(
    inputIndex: number,
    derivedKeyPair: BIP32Interface,
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): this {
    return this.setMusig2NoncesInner(derivedKeyPair, 'derived', inputIndex, params);
  }

  /**
   * Generates and sets MuSig2 nonce to taproot key path input at inputIndex.
   * If input is not a taproot key path, no action.
   *
   * @param inputIndex input index
   * @param keyPair HD root key pair
   * @param sessionId Optional extra entropy. If provided it must either be a counter unique to this secret key,
   * (converted to an array of 32 bytes), or 32 uniformly random bytes.
   * @param deterministic If true, set the cosigner nonce deterministically
   */
  setInputMusig2NonceHD(
    inputIndex: number,
    keyPair: BIP32Interface,
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): this {
    checkForInput(this.data.inputs, inputIndex);
    return this.setMusig2NoncesInner(keyPair, 'root', inputIndex, params);
  }

  /**
   * Generates and sets MuSig2 nonce to all taproot key path inputs. Other inputs will be skipped.
   *
   * @param inputIndex input index
   * @param keyPair derived key pair
   * @param sessionId Optional extra entropy. If provided it must either be a counter unique to this secret key,
   * (converted to an array of 32 bytes), or 32 uniformly random bytes.
   */
  setAllInputsMusig2Nonce(
    keyPair: BIP32Interface,
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): this {
    return this.setMusig2NoncesInner(keyPair, 'derived', undefined, params);
  }

  /**
   * Generates and sets MuSig2 nonce to all taproot key path inputs. Other inputs will be skipped.
   *
   * @param inputIndex input index
   * @param keyPair HD root key pair
   * @param sessionId Optional extra entropy. If provided it must either be a counter unique to this secret key,
   * (converted to an array of 32 bytes), or 32 uniformly random bytes.
   */
  setAllInputsMusig2NonceHD(
    keyPair: BIP32Interface,
    params: { sessionId?: Buffer; deterministic?: boolean } = { deterministic: false }
  ): this {
    return this.setMusig2NoncesInner(keyPair, 'root', undefined, params);
  }

  clone(): this {
    return UtxoPsbt.fromBuffer(this.toBuffer(), { network: this.network }) as this;
  }

  extractTransaction(disableFeeCheck = true): UtxoTransaction<bigint> {
    const tx = super.extractTransaction(disableFeeCheck);
    if (tx instanceof UtxoTransaction) {
      return tx;
    }
    throw new Error('extractTransaction did not return instace of UtxoTransaction');
  }
}

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


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