<commit_before>const Buffer = require('safe-buffer').Buffer
const baddress = require('./address')
const bcrypto = require('./crypto')
const bscript = require('./script')
const networks = require('./networks')
const ops = require('bitcoin-ops')
const payments = require('./payments')
const typeforce = require('typeforce')
const types = require('./types')
const classify = require('./classify')
const SCRIPT_TYPES = classify.types

const ECPair = require('./ecpair')
const Transaction = require('./transaction')

function expandInput (scriptSig, witnessStack, type, scriptPubKey) {
  if (scriptSig.length === 0 && witnessStack.length === 0) return {}
  if (!type) {
    let ssType = classify.input(scriptSig, true)
    let wsType = classify.witness(witnessStack, true)
    if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined
    if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined
    type = ssType || wsType
  }

  switch (type) {
    case SCRIPT_TYPES.P2WPKH: {
      const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack })

      return {
        prevOutScript: output,
        prevOutType: SCRIPT_TYPES.P2WPKH,
        pubkeys: [pubkey],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2PKH: {
      const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig })

      return {
        prevOutScript: output,
        prevOutType: SCRIPT_TYPES.P2PKH,
        pubkeys: [pubkey],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2PK: {
      const { signature } = payments.p2pk({ input: scriptSig })

      return {
        prevOutType: SCRIPT_TYPES.P2PK,
        pubkeys: [undefined],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2MS: {
      const { m, pubkeys, signatures } = payments.p2ms({
        input: scriptSig,
        output: scriptPubKey
      }, { allowIncomplete: true })

      return {
        prevOutType: SCRIPT_TYPES.P2MS,
        pubkeys: pubkeys,
        signatures: signatures,
        maxSignatures: m
      }
    }
  }

  if (type === SCRIPT_TYPES.P2SH) {
    const { output, redeem } = payments.p2sh({
      input: scriptSig,
      witness: witnessStack
    })

    const outputType = classify.output(redeem.output)
    const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output)
    if (!expanded.prevOutType) return {}

    return {
      prevOutScript: output,
      prevOutType: SCRIPT_TYPES.P2SH,
      redeemScript: redeem.output,
      redeemScriptType: expanded.prevOutType,
      witnessScript: expanded.witnessScript,
      witnessScriptType: expanded.witnessScriptType,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures
    }
  }

  if (type === SCRIPT_TYPES.P2WSH) {
    const { output, redeem } = payments.p2wsh({
      input: scriptSig,
      witness: witnessStack
    })
    const outputType = classify.output(redeem.output)
    let expanded
    if (outputType === SCRIPT_TYPES.P2WPKH) {
      expanded = expandInput(redeem.input, redeem.witness, outputType)
    } else {
      expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output)
    }
    if (!expanded.prevOutType) return {}

    return {
      prevOutScript: output,
      prevOutType: SCRIPT_TYPES.P2WSH,
      witnessScript: redeem.output,
      witnessScriptType: expanded.prevOutType,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures
    }
  }

  return {
    prevOutType: SCRIPT_TYPES.NONSTANDARD,
    prevOutScript: scriptSig
  }
}

// could be done in expandInput, but requires the original Transaction for hashForSignature
function fixMultisigOrder (input, transaction, vin) {
  if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return
  if (input.pubkeys.length === input.signatures.length) return

  const unmatched = input.signatures.concat()

  input.signatures = input.pubkeys.map(function (pubKey) {
    const keyPair = ECPair.fromPublicKey(pubKey)
    let match

    // check for a signature
    unmatched.some(function (signature, i) {
      // skip if undefined || OP_0
      if (!signature) return false

      // TODO: avoid O(n) hashForSignature
      const parsed = bscript.signature.decode(signature)
      const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType)

      // skip if signature does not match pubKey
      if (!keyPair.verify(hash, parsed.signature)) return false

      // remove matched signature from unmatched
      unmatched[i] = undefined
      match = signature

      return true
    })

    return match
  })
}

function expandOutput (script, ourPubKey) {
  typeforce(types.Buffer, script)
  const type = classify.output(script)

  switch (type) {
    case SCRIPT_TYPES.P2PKH: {
      if (!ourPubKey) return { type }

      // does our hash160(pubKey) match the output scripts?
      const pkh1 = payments.p2pkh({ output: script }).hash
      const pkh2 = bcrypto.hash160(ourPubKey)
      if (!pkh1.equals(pkh2)) return { type }

      return {
        type,
        pubkeys: [ourPubKey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2WPKH: {
      if (!ourPubKey) return { type }

      // does our hash160(pubKey) match the output scripts?
      const wpkh1 = payments.p2wpkh({ output: script }).hash
      const wpkh2 = bcrypto.hash160(ourPubKey)
      if (!wpkh1.equals(wpkh2)) return { type }

      return {
        type,
        pubkeys: [ourPubKey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2PK: {
      const p2pk = payments.p2pk({ output: script })
      return {
        type,
        pubkeys: [p2pk.pubkey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2MS: {
      const p2ms = payments.p2ms({ output: script })
      return {
        type,
        pubkeys: p2ms.pubkeys,
        signatures: p2ms.pubkeys.map(() => undefined),
        maxSignatures: p2ms.m
      }
    }
  }

  return { type }
}

function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScript) {
  if (redeemScript && witnessScript) {
    const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } })
    const p2wshAlt = payments.p2wsh({ output: redeemScript })
    const p2sh = payments.p2sh({ redeem: { output: redeemScript } })
    const p2shAlt = payments.p2sh({ redeem: p2wsh })

    // enforces P2SH(P2WSH(...))
    if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript')
    if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript')

    const expanded = expandOutput(p2wsh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = witnessScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure')

    return {
      redeemScript,
      redeemScriptType: SCRIPT_TYPES.P2WSH,

      witnessScript,
      witnessScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2SH,
      prevOutScript: p2sh.output,

      hasWitness: true,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (redeemScript) {
    const p2sh = payments.p2sh({ redeem: { output: redeemScript } })

    if (input.prevOutScript) {
      let p2shAlt
      try {
        p2shAlt = payments.p2sh({ output: input.prevOutScript })
      } catch (e) { throw new Error('PrevOutScript must be P2SH') }
      if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript')
    }

    const expanded = expandOutput(p2sh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = redeemScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
    }

    return {
      redeemScript,
      redeemScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2SH,
      prevOutScript: p2sh.output,

      hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (witnessScript) {
    const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } })

    if (input.prevOutScript) {
      const p2wshAlt = payments.p2wsh({ output: input.prevOutScript })
      if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript')
    }

    const expanded = expandOutput(p2wsh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = witnessScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure')

    return {
      witnessScript,
      witnessScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2WSH,
      prevOutScript: p2wsh.output,

      hasWitness: true,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (input.prevOutType && input.prevOutScript) {
    // embedded scripts are not possible without extra information
    if (input.prevOutType === SCRIPT_TYPES.P2SH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript')
    if (input.prevOutType === SCRIPT_TYPES.P2WSH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript')
    if (!input.prevOutScript) throw new Error('PrevOutScript is missing')

    const expanded = expandOutput(input.prevOutScript, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = input.prevOutScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
    }

    return {
      prevOutType: expanded.type,
      prevOutScript: input.prevOutScript,

      hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output
  return {
    prevOutType: SCRIPT_TYPES.P2PKH,
    prevOutScript: prevOutScript,

    hasWitness: false,
    signScript: prevOutScript,
    signType: SCRIPT_TYPES.P2PKH,

    pubkeys: [ourPubKey],
    signatures: [undefined],
    maxSignatures: 1
  }
}

function build (type, input, allowIncomplete) {
  const pubkeys = input.pubkeys || []
  let signatures = input.signatures || []

  switch (type) {
    case SCRIPT_TYPES.P2PKH: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break

      return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] })
    }
    case SCRIPT_TYPES.P2WPKH: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break

      return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] })
    }
    case SCRIPT_TYPES.P2PK: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break<commit_msg>refactor(txb): maxSignatures is optional<commit_after>const Buffer = require('safe-buffer').Buffer
const baddress = require('./address')
const bcrypto = require('./crypto')
const bscript = require('./script')
const networks = require('./networks')
const ops = require('bitcoin-ops')
const payments = require('./payments')
const typeforce = require('typeforce')
const types = require('./types')
const classify = require('./classify')
const SCRIPT_TYPES = classify.types

const ECPair = require('./ecpair')
const Transaction = require('./transaction')

function expandInput (scriptSig, witnessStack, type, scriptPubKey) {
  if (scriptSig.length === 0 && witnessStack.length === 0) return {}
  if (!type) {
    let ssType = classify.input(scriptSig, true)
    let wsType = classify.witness(witnessStack, true)
    if (ssType === SCRIPT_TYPES.NONSTANDARD) ssType = undefined
    if (wsType === SCRIPT_TYPES.NONSTANDARD) wsType = undefined
    type = ssType || wsType
  }

  switch (type) {
    case SCRIPT_TYPES.P2WPKH: {
      const { output, pubkey, signature } = payments.p2wpkh({ witness: witnessStack })

      return {
        prevOutScript: output,
        prevOutType: SCRIPT_TYPES.P2WPKH,
        pubkeys: [pubkey],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2PKH: {
      const { output, pubkey, signature } = payments.p2pkh({ input: scriptSig })

      return {
        prevOutScript: output,
        prevOutType: SCRIPT_TYPES.P2PKH,
        pubkeys: [pubkey],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2PK: {
      const { signature } = payments.p2pk({ input: scriptSig })

      return {
        prevOutType: SCRIPT_TYPES.P2PK,
        pubkeys: [undefined],
        signatures: [signature]
      }
    }

    case SCRIPT_TYPES.P2MS: {
      const { m, pubkeys, signatures } = payments.p2ms({
        input: scriptSig,
        output: scriptPubKey
      }, { allowIncomplete: true })

      return {
        prevOutType: SCRIPT_TYPES.P2MS,
        pubkeys: pubkeys,
        signatures: signatures,
        maxSignatures: m
      }
    }
  }

  if (type === SCRIPT_TYPES.P2SH) {
    const { output, redeem } = payments.p2sh({
      input: scriptSig,
      witness: witnessStack
    })

    const outputType = classify.output(redeem.output)
    const expanded = expandInput(redeem.input, redeem.witness, outputType, redeem.output)
    if (!expanded.prevOutType) return {}

    return {
      prevOutScript: output,
      prevOutType: SCRIPT_TYPES.P2SH,
      redeemScript: redeem.output,
      redeemScriptType: expanded.prevOutType,
      witnessScript: expanded.witnessScript,
      witnessScriptType: expanded.witnessScriptType,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures
    }
  }

  if (type === SCRIPT_TYPES.P2WSH) {
    const { output, redeem } = payments.p2wsh({
      input: scriptSig,
      witness: witnessStack
    })
    const outputType = classify.output(redeem.output)
    let expanded
    if (outputType === SCRIPT_TYPES.P2WPKH) {
      expanded = expandInput(redeem.input, redeem.witness, outputType)
    } else {
      expanded = expandInput(bscript.compile(redeem.witness), [], outputType, redeem.output)
    }
    if (!expanded.prevOutType) return {}

    return {
      prevOutScript: output,
      prevOutType: SCRIPT_TYPES.P2WSH,
      witnessScript: redeem.output,
      witnessScriptType: expanded.prevOutType,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures
    }
  }

  return {
    prevOutType: SCRIPT_TYPES.NONSTANDARD,
    prevOutScript: scriptSig
  }
}

// could be done in expandInput, but requires the original Transaction for hashForSignature
function fixMultisigOrder (input, transaction, vin) {
  if (input.redeemScriptType !== SCRIPT_TYPES.P2MS || !input.redeemScript) return
  if (input.pubkeys.length === input.signatures.length) return

  const unmatched = input.signatures.concat()

  input.signatures = input.pubkeys.map(function (pubKey) {
    const keyPair = ECPair.fromPublicKey(pubKey)
    let match

    // check for a signature
    unmatched.some(function (signature, i) {
      // skip if undefined || OP_0
      if (!signature) return false

      // TODO: avoid O(n) hashForSignature
      const parsed = bscript.signature.decode(signature)
      const hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType)

      // skip if signature does not match pubKey
      if (!keyPair.verify(hash, parsed.signature)) return false

      // remove matched signature from unmatched
      unmatched[i] = undefined
      match = signature

      return true
    })

    return match
  })
}

function expandOutput (script, ourPubKey) {
  typeforce(types.Buffer, script)
  const type = classify.output(script)

  switch (type) {
    case SCRIPT_TYPES.P2PKH: {
      if (!ourPubKey) return { type }

      // does our hash160(pubKey) match the output scripts?
      const pkh1 = payments.p2pkh({ output: script }).hash
      const pkh2 = bcrypto.hash160(ourPubKey)
      if (!pkh1.equals(pkh2)) return { type }

      return {
        type,
        pubkeys: [ourPubKey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2WPKH: {
      if (!ourPubKey) return { type }

      // does our hash160(pubKey) match the output scripts?
      const wpkh1 = payments.p2wpkh({ output: script }).hash
      const wpkh2 = bcrypto.hash160(ourPubKey)
      if (!wpkh1.equals(wpkh2)) return { type }

      return {
        type,
        pubkeys: [ourPubKey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2PK: {
      const p2pk = payments.p2pk({ output: script })
      return {
        type,
        pubkeys: [p2pk.pubkey],
        signatures: [undefined]
      }
    }

    case SCRIPT_TYPES.P2MS: {
      const p2ms = payments.p2ms({ output: script })
      return {
        type,
        pubkeys: p2ms.pubkeys,
        signatures: p2ms.pubkeys.map(() => undefined),
        maxSignatures: p2ms.m
      }
    }
  }

  return { type }
}

function prepareInput (input, ourPubKey, redeemScript, witnessValue, witnessScript) {
  if (redeemScript && witnessScript) {
    const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } })
    const p2wshAlt = payments.p2wsh({ output: redeemScript })
    const p2sh = payments.p2sh({ redeem: { output: redeemScript } })
    const p2shAlt = payments.p2sh({ redeem: p2wsh })

    // enforces P2SH(P2WSH(...))
    if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript')
    if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript')

    const expanded = expandOutput(p2wsh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = witnessScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2SH(P2WSH(P2WPKH)) is a consensus failure')

    return {
      redeemScript,
      redeemScriptType: SCRIPT_TYPES.P2WSH,

      witnessScript,
      witnessScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2SH,
      prevOutScript: p2sh.output,

      hasWitness: true,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (redeemScript) {
    const p2sh = payments.p2sh({ redeem: { output: redeemScript } })

    if (input.prevOutScript) {
      let p2shAlt
      try {
        p2shAlt = payments.p2sh({ output: input.prevOutScript })
      } catch (e) { throw new Error('PrevOutScript must be P2SH') }
      if (!p2sh.hash.equals(p2shAlt.hash)) throw new Error('Redeem script inconsistent with prevOutScript')
    }

    const expanded = expandOutput(p2sh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as redeemScript (' + bscript.toASM(redeemScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = redeemScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
    }

    return {
      redeemScript,
      redeemScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2SH,
      prevOutScript: p2sh.output,

      hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (witnessScript) {
    const p2wsh = payments.p2wsh({ redeem: { output: witnessScript } })

    if (input.prevOutScript) {
      const p2wshAlt = payments.p2wsh({ output: input.prevOutScript })
      if (!p2wsh.hash.equals(p2wshAlt.hash)) throw new Error('Witness script inconsistent with prevOutScript')
    }

    const expanded = expandOutput(p2wsh.redeem.output, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported as witnessScript (' + bscript.toASM(witnessScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = witnessScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) throw new Error('P2WSH(P2WPKH) is a consensus failure')

    return {
      witnessScript,
      witnessScriptType: expanded.type,

      prevOutType: SCRIPT_TYPES.P2WSH,
      prevOutScript: p2wsh.output,

      hasWitness: true,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  if (input.prevOutType && input.prevOutScript) {
    // embedded scripts are not possible without extra information
    if (input.prevOutType === SCRIPT_TYPES.P2SH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript')
    if (input.prevOutType === SCRIPT_TYPES.P2WSH) throw new Error('PrevOutScript is ' + input.prevOutType + ', requires witnessScript')
    if (!input.prevOutScript) throw new Error('PrevOutScript is missing')

    const expanded = expandOutput(input.prevOutScript, ourPubKey)
    if (!expanded.pubkeys) throw new Error(expanded.type + ' not supported (' + bscript.toASM(input.prevOutScript) + ')')
    if (input.signatures && input.signatures.some(x => x)) {
      expanded.signatures = input.signatures
    }

    let signScript = input.prevOutScript
    if (expanded.type === SCRIPT_TYPES.P2WPKH) {
      signScript = payments.p2pkh({ pubkey: expanded.pubkeys[0] }).output
    }

    return {
      prevOutType: expanded.type,
      prevOutScript: input.prevOutScript,

      hasWitness: expanded.type === SCRIPT_TYPES.P2WPKH,
      signScript,
      signType: expanded.type,

      pubkeys: expanded.pubkeys,
      signatures: expanded.signatures,
      maxSignatures: expanded.maxSignatures
    }
  }

  const prevOutScript = payments.p2pkh({ pubkey: ourPubKey }).output
  return {
    prevOutType: SCRIPT_TYPES.P2PKH,
    prevOutScript: prevOutScript,

    hasWitness: false,
    signScript: prevOutScript,
    signType: SCRIPT_TYPES.P2PKH,

    pubkeys: [ourPubKey],
    signatures: [undefined]
  }
}

function build (type, input, allowIncomplete) {
  const pubkeys = input.pubkeys || []
  let signatures = input.signatures || []

  switch (type) {
    case SCRIPT_TYPES.P2PKH: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break

      return payments.p2pkh({ pubkey: pubkeys[0], signature: signatures[0] })
    }
    case SCRIPT_TYPES.P2WPKH: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break

      return payments.p2wpkh({ pubkey: pubkeys[0], signature: signatures[0] })
    }
    case SCRIPT_TYPES.P2PK: {
      if (pubkeys.length === 0) break
      if (signatures.length === 0) break
{'fraction_non_alphanumeric': 0.11198185192823262, 'fraction_numerical': 0.010847597442771705, 'mean_word_length': 3.0062789160608063, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1653011', 'n_tokens_mistral': 8099, 'n_tokens_neox': 7719, 'n_words': 1724}