PHP WebShell

Текущая директория: /opt/bitgo-express/node_modules/bitcoinjs-lib/src

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

var baddress = require('./address')
var bcrypto = require('./crypto')
var bscript = require('./script')
var bufferEquals = require('buffer-equals')
var networks = require('./networks')
var ops = require('./opcodes')

var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Transaction = require('./transaction')

// re-orders signatures to match pubKeys, fills undefined otherwise
function fixMSSignatures (transaction, vin, pubKeys, signatures, prevOutScript, hashType, skipPubKey) {
  // maintain a local copy of unmatched signatures
  var unmatched = signatures.slice()
  var cache = {}

  return pubKeys.map(function (pubKey) {
    // skip optionally provided pubKey
    if (skipPubKey && bufferEquals(skipPubKey, pubKey)) return undefined

    var matched
    var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)

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

      var signatureHash = cache[hashType] = cache[hashType] || transaction.hashForSignature(vin, prevOutScript, hashType)
      if (!keyPair2.verify(signatureHash, signature)) return false

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

      return true
    })

    return matched || undefined
  })
}

function extractInput (transaction, txIn, vin) {
  var redeemScript
  var scriptSig = txIn.script
  var scriptSigChunks = bscript.decompile(scriptSig)

  var prevOutScript
  var prevOutType = bscript.classifyInput(scriptSig, true)
  var scriptType

  // Re-classify if scriptHash
  if (prevOutType === 'scripthash') {
    redeemScript = scriptSigChunks.slice(-1)[0]
    prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))

    scriptSig = bscript.compile(scriptSigChunks.slice(0, -1))
    scriptSigChunks = scriptSigChunks.slice(0, -1)

    scriptType = bscript.classifyInput(scriptSig, true)
  } else {
    scriptType = prevOutType
  }

  // pre-empt redeemScript decompilation
  var redeemScriptChunks
  if (redeemScript) {
    redeemScriptChunks = bscript.decompile(redeemScript)
  }

  // Extract hashType, pubKeys and signatures
  var hashType, parsed, pubKeys, signatures

  switch (scriptType) {
    case 'pubkeyhash':
      parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
      hashType = parsed.hashType
      pubKeys = scriptSigChunks.slice(1)
      signatures = [parsed.signature]
      prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(pubKeys[0]))

      break

    case 'pubkey':
      parsed = ECSignature.parseScriptSignature(scriptSigChunks[0])
      hashType = parsed.hashType
      signatures = [parsed.signature]

      if (redeemScript) {
        pubKeys = redeemScriptChunks.slice(0, 1)
      }

      break

    case 'multisig':
      signatures = scriptSigChunks.slice(1).map(function (chunk) {
        if (chunk === ops.OP_0) return undefined

        var parsed = ECSignature.parseScriptSignature(chunk)
        hashType = parsed.hashType

        return parsed.signature
      })

      if (redeemScript) {
        pubKeys = redeemScriptChunks.slice(1, -2)

        if (pubKeys.length !== signatures.length) {
          signatures = fixMSSignatures(transaction, vin, pubKeys, signatures, redeemScript, hashType, redeemScript)
        }
      }

      break
  }

  return {
    hashType: hashType,
    prevOutScript: prevOutScript,
    prevOutType: prevOutType,
    pubKeys: pubKeys,
    redeemScript: redeemScript,
    scriptType: scriptType,
    signatures: signatures
  }
}

function TransactionBuilder (network) {
  this.prevTxMap = {}
  this.prevOutScripts = {}
  this.prevOutTypes = {}
  this.network = network || networks.bitcoin

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.fromTransaction = function (transaction, network) {
  var txb = new TransactionBuilder(network)

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function (txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function (txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function (txIn, vin) {
    // TODO: verify whether extractInput is sane with coinbase scripts
    if (Transaction.isCoinbaseHash(txIn.hash)) {
      throw new Error('coinbase inputs not supported')
    }

    // Ignore empty scripts
    if (txIn.script.length === 0) return {}

    return extractInput(transaction, txIn, vin)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
  // is it a hex string?
  if (typeof txHash === 'string') {
    // transaction hashs's are displayed in reverse order, un-reverse it
    txHash = [].reverse.call(new Buffer(txHash, 'hex'))

  // is it a Transaction object?
  } else if (txHash instanceof Transaction) {
    prevOutScript = txHash.outs[vout].script
    txHash = txHash.getHash()
  }

  var input = {}
  if (prevOutScript) {
    var prevOutScriptChunks = bscript.decompile(prevOutScript)
    var prevOutType = bscript.classifyOutput(prevOutScriptChunks)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig':
        input.pubKeys = prevOutScriptChunks.slice(1, -2)
        input.signatures = input.pubKeys.map(function () { return undefined })

        break

      case 'pubkey':
        input.pubKeys = prevOutScriptChunks.slice(0, 1)
        input.signatures = [undefined]

        break
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  var valid = this.inputs.every(function (input2) {
    if (input2.hashType === undefined) return true

    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
  })

  if (!valid) throw new Error('No, this would invalidate signatures')

  var prevOut = txHash.toString('hex') + ':' + vout
  if (this.prevTxMap[prevOut]) throw new Error('Transaction is already an input')

  var vin = this.tx.addInput(txHash, vout, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
  var tx = this.tx
  var valid = this.inputs.every(function (input, index) {
    if (input.hashType === undefined) return true

    var hashType = input.hashType & 0x1f
    return hashType === Transaction.SIGHASH_NONE ||
           (hashType === Transaction.SIGHASH_SINGLE && index < tx.outs.length)
  })

  if (!valid) throw new Error('No, this would invalidate signatures')

  // Attempt to get a script if it's a base58 address string
  if (typeof scriptPubKey === 'string') {
    scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
  }

  return tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function () {
  return this.__build(false)
}
TransactionBuilder.prototype.buildIncomplete = function () {
  return this.__build(true)
}

var canBuildTypes = {
  'multisig': true,
  'pubkey': true,
  'pubkeyhash': true
}

TransactionBuilder.prototype.__build = function (allowIncomplete) {
  if (!allowIncomplete) {
    if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
    if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from inputs
  this.inputs.forEach(function (input, index) {
    var scriptType = input.scriptType
    var scriptSig

    if (!allowIncomplete) {
      if (!scriptType) throw new Error('Transaction is not complete')
      if (!canBuildTypes[scriptType]) throw new Error(scriptType + ' not supported')

      // XXX: only relevant to types that need signatures
      if (!input.signatures) throw new Error('Transaction is missing signatures')
    }

    if (input.signatures) {
      switch (scriptType) {
        case 'pubkeyhash':
          var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = bscript.pubKeyHashInput(pkhSignature, input.pubKeys[0])
          break

        case 'multisig':
          var msSignatures = input.signatures.map(function (signature) {
            return signature && signature.toScriptSignature(input.hashType)
          })

          // fill in blanks with OP_0
          if (allowIncomplete) {
            for (var i = 0; i < msSignatures.length; ++i) {
              msSignatures[i] = msSignatures[i] || ops.OP_0
            }

          // remove blank signatures
          } else {
            msSignatures = msSignatures.filter(function (x) { return x })
          }

          var redeemScript = allowIncomplete ? undefined : input.redeemScript
          scriptSig = bscript.multisigInput(msSignatures, redeemScript)
          break

        case 'pubkey':
          var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = bscript.pubKeyInput(pkSignature)
          break
      }
    }

    // did we build a scriptSig?
    if (scriptSig) {
      // wrap as scriptHash if necessary
      if (input.prevOutType === 'scripthash') {
        scriptSig = bscript.scriptHashInput(scriptSig, input.redeemScript)
      }

      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
  if (keyPair.network !== this.network) throw new Error('Inconsistent network')
  if (!this.inputs[index]) throw new Error('No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]
  var canSign = input.hashType &&
    input.prevOutScript &&
    input.prevOutType &&
    input.pubKeys &&
    input.scriptType &&
    input.signatures &&
    input.signatures.length === input.pubKeys.length

  var kpPubKey = keyPair.getPublicKeyBuffer()

  // are we ready to sign?
  if (canSign) {
    // if redeemScript was provided, enforce consistency
    if (redeemScript) {
      if (!bufferEquals(input.redeemScript, redeemScript)) throw new Error('Inconsistent redeemScript')
    }

    if (input.hashType !== hashType) throw new Error('Inconsistent hashType')

  // no? prepare
  } else {
    // must be pay-to-scriptHash?
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        if (input.prevOutType !== 'scripthash') throw new Error('PrevOutScript must be P2SH')

        var scriptHash = bscript.decompile(input.prevOutScript)[1]
        if (!bufferEquals(scriptHash, bcrypto.hash160(redeemScript))) throw new Error('RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var scriptType = bscript.classifyOutput(redeemScript)
      var redeemScriptChunks = bscript.decompile(redeemScript)
      var pubKeys

      switch (scriptType) {
        case 'multisig':
          pubKeys = redeemScriptChunks.slice(1, -2)

          break

        case 'pubkeyhash':
          var pkh1 = redeemScriptChunks[2]
          var pkh2 = bcrypto.hash160(keyPair.getPublicKeyBuffer())

          if (!bufferEquals(pkh1, pkh2)) throw new Error('privateKey cannot sign for this input')
          pubKeys = [kpPubKey]

          break

        case 'pubkey':
          pubKeys = redeemScriptChunks.slice(0, 1)

          break

        default:
          throw new Error('RedeemScript not supported (' + scriptType + ')')
      }

      // if we don't have a prevOutScript, generate a P2SH script
      if (!input.prevOutScript) {
        input.prevOutScript = bscript.scriptHashOutput(bcrypto.hash160(redeemScript))
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = pubKeys
      input.redeemScript = redeemScript
      input.scriptType = scriptType
      input.signatures = pubKeys.map(function () { return undefined })
    } else {
      // pay-to-scriptHash is not possible without a redeemScript
      if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')

      // if we don't have a scriptType, assume pubKeyHash otherwise
      if (!input.scriptType) {
        input.prevOutScript = bscript.pubKeyHashOutput(bcrypto.hash160(keyPair.getPublicKeyBuffer()))
        input.prevOutType = 'pubkeyhash'
        input.pubKeys = [kpPubKey]
        input.scriptType = input.prevOutType
        input.signatures = [undefined]
      } else {
        // throw if we can't sign with it
        if (!input.pubKeys || !input.signatures) throw new Error(input.scriptType + ' not supported')
      }
    }

    input.hashType = hashType
  }

  // ready to sign?
  var signatureScript = input.redeemScript || input.prevOutScript
  var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)

  // enforce in order signing of public keys
  var valid = input.pubKeys.some(function (pubKey, i) {
    if (!bufferEquals(kpPubKey, pubKey)) return false
    if (input.signatures[i]) throw new Error('Signature already exists')

    var signature = keyPair.sign(signatureHash)
    input.signatures[i] = signature

    return true
  })

  if (!valid) throw new Error('Key pair cannot sign for this input')
}

module.exports = TransactionBuilder

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


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