2015-08-20 05:37:19 +02:00
|
|
|
var baddress = require('./address')
|
2015-07-08 07:56:21 +02:00
|
|
|
var bcrypto = require('./crypto')
|
2015-08-20 05:37:19 +02:00
|
|
|
var bscript = require('./script')
|
2015-07-24 04:16:37 +02:00
|
|
|
var networks = require('./networks')
|
2016-12-17 15:24:00 +01:00
|
|
|
var ops = require('bitcoin-ops')
|
2015-11-26 02:40:06 +01:00
|
|
|
var typeforce = require('typeforce')
|
|
|
|
var types = require('./types')
|
2016-10-27 11:05:45 +02:00
|
|
|
var scriptTypes = bscript.types
|
2016-12-22 13:18:45 +01:00
|
|
|
var SIGNABLE = [bscript.types.P2PKH, bscript.types.P2PK, bscript.types.MULTISIG]
|
|
|
|
var P2SH = SIGNABLE.concat([bscript.types.P2WPKH, bscript.types.P2WSH])
|
|
|
|
var EMPTY_SCRIPT = new Buffer(0)
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2015-03-02 06:48:36 +01:00
|
|
|
var ECPair = require('./ecpair')
|
2014-07-28 06:28:44 +02:00
|
|
|
var ECSignature = require('./ecsignature')
|
|
|
|
var Transaction = require('./transaction')
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2016-10-12 02:58:03 +02:00
|
|
|
// inspects a scriptSig w/ optional redeemScript and
|
|
|
|
// derives any input information required
|
2016-12-14 05:41:24 +01:00
|
|
|
function expandInput (scriptSig, redeemScript, witnessStack) {
|
|
|
|
var witnessType
|
|
|
|
if (witnessStack) {
|
|
|
|
witnessType = bscript.classifyWitness(witnessStack)
|
|
|
|
}
|
|
|
|
|
|
|
|
var prevOutType, scriptSigChunks
|
|
|
|
if (scriptSig.length === 0 && witnessStack) {
|
|
|
|
prevOutType = witnessType
|
|
|
|
} else {
|
|
|
|
scriptSigChunks = bscript.decompile(scriptSig)
|
|
|
|
prevOutType = bscript.classifyInput(scriptSigChunks, true)
|
|
|
|
}
|
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
var pubKeys, signatures, prevOutScript
|
2015-09-08 13:22:54 +02:00
|
|
|
|
2016-10-12 02:58:03 +02:00
|
|
|
switch (prevOutType) {
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.P2SH:
|
2016-09-27 16:46:37 +02:00
|
|
|
// FIXME: maybe depth limit instead, how possible is this anyway?
|
|
|
|
if (redeemScript) throw new Error('Recursive P2SH script')
|
2015-09-08 13:22:54 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
var redeemScriptSig = scriptSigChunks.slice(0, -1)
|
|
|
|
redeemScript = scriptSigChunks[scriptSigChunks.length - 1]
|
2016-04-08 04:23:32 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
var result = expandInput(redeemScriptSig, redeemScript)
|
|
|
|
result.redeemScript = redeemScript
|
|
|
|
result.redeemScriptType = result.prevOutType
|
2016-11-02 02:30:37 +01:00
|
|
|
result.prevOutScript = bscript.scriptHash.output.encode(bcrypto.hash160(redeemScript))
|
2016-10-27 11:05:45 +02:00
|
|
|
result.prevOutType = scriptTypes.P2SH
|
2016-12-14 05:41:24 +01:00
|
|
|
result.witness = false
|
2016-09-27 16:46:37 +02:00
|
|
|
return result
|
2015-08-07 08:30:24 +02:00
|
|
|
|
2016-12-14 05:41:24 +01:00
|
|
|
case scriptTypes.P2WPKH:
|
|
|
|
pubKeys = witnessStack.slice(1)
|
|
|
|
signatures = witnessStack.slice(0, 1)
|
|
|
|
break
|
|
|
|
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.P2PKH:
|
2016-09-27 16:46:37 +02:00
|
|
|
// if (redeemScript) throw new Error('Nonstandard... P2SH(P2PKH)')
|
|
|
|
pubKeys = scriptSigChunks.slice(1)
|
2016-10-12 03:53:51 +02:00
|
|
|
signatures = scriptSigChunks.slice(0, 1)
|
2014-12-12 05:19:03 +01:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
if (redeemScript) break
|
2016-11-02 02:30:37 +01:00
|
|
|
prevOutScript = bscript.pubKeyHash.output.encode(bcrypto.hash160(pubKeys[0]))
|
2016-09-27 16:46:37 +02:00
|
|
|
break
|
2015-02-05 04:13:27 +01:00
|
|
|
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.P2PK:
|
2016-09-27 16:46:37 +02:00
|
|
|
if (redeemScript) {
|
|
|
|
pubKeys = bscript.decompile(redeemScript).slice(0, 1)
|
|
|
|
}
|
2016-02-08 15:12:02 +01:00
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
signatures = scriptSigChunks.slice(0, 1)
|
2016-09-27 16:46:37 +02:00
|
|
|
break
|
2016-02-08 15:12:02 +01:00
|
|
|
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.MULTISIG:
|
2016-09-27 16:46:37 +02:00
|
|
|
if (redeemScript) {
|
|
|
|
pubKeys = bscript.decompile(redeemScript).slice(1, -2)
|
|
|
|
}
|
2015-02-05 04:13:27 +01:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
signatures = scriptSigChunks.slice(1).map(function (chunk) {
|
2016-10-12 03:53:51 +02:00
|
|
|
return chunk === ops.OP_0 ? undefined : chunk
|
2016-09-27 16:46:37 +02:00
|
|
|
})
|
|
|
|
break
|
2016-12-14 05:41:24 +01:00
|
|
|
|
|
|
|
case scriptTypes.NONSTANDARD:
|
|
|
|
return { prevOutType: prevOutType, prevOutScript: EMPTY_SCRIPT }
|
|
|
|
|
|
|
|
default: return {}
|
2014-12-12 02:48:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
2016-09-27 16:46:37 +02:00
|
|
|
pubKeys: pubKeys,
|
|
|
|
signatures: signatures,
|
|
|
|
prevOutScript: prevOutScript,
|
2016-12-14 05:41:24 +01:00
|
|
|
prevOutType: prevOutType,
|
|
|
|
witness: Boolean(witnessStack)
|
2014-12-12 02:48:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-12 03:17:19 +02:00
|
|
|
// could be done in expandInput, but requires the original Transaction for hashForSignature
|
2016-10-12 03:15:13 +02:00
|
|
|
function fixMultisigOrder (input, transaction, vin) {
|
2016-10-27 11:05:45 +02:00
|
|
|
if (input.redeemScriptType !== scriptTypes.MULTISIG || !input.redeemScript) return
|
2016-10-12 03:15:13 +02:00
|
|
|
if (input.pubKeys.length === input.signatures.length) return
|
|
|
|
|
|
|
|
var unmatched = input.signatures.concat()
|
|
|
|
|
|
|
|
input.signatures = input.pubKeys.map(function (pubKey, y) {
|
|
|
|
var keyPair = ECPair.fromPublicKeyBuffer(pubKey)
|
|
|
|
var match
|
|
|
|
|
|
|
|
// check for a signature
|
|
|
|
unmatched.some(function (signature, i) {
|
|
|
|
// skip if undefined || OP_0
|
|
|
|
if (!signature) return false
|
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
// TODO: avoid O(n) hashForSignature
|
|
|
|
var parsed = ECSignature.parseScriptSignature(signature)
|
|
|
|
var hash = transaction.hashForSignature(vin, input.redeemScript, parsed.hashType)
|
|
|
|
|
2016-10-12 03:15:13 +02:00
|
|
|
// skip if signature does not match pubKey
|
2016-10-12 03:53:51 +02:00
|
|
|
if (!keyPair.verify(hash, parsed.signature)) return false
|
2016-10-12 03:15:13 +02:00
|
|
|
|
|
|
|
// remove matched signature from unmatched
|
|
|
|
unmatched[i] = undefined
|
|
|
|
match = signature
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
|
|
|
return match
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-10-06 04:18:14 +02:00
|
|
|
function expandOutput (script, scriptType, ourPubKey) {
|
2016-09-27 16:46:37 +02:00
|
|
|
typeforce(types.Buffer, script)
|
|
|
|
|
|
|
|
var scriptChunks = bscript.decompile(script)
|
2016-10-06 04:18:14 +02:00
|
|
|
if (!scriptType) {
|
2016-11-01 16:06:01 +01:00
|
|
|
scriptType = bscript.classifyOutput(script)
|
2016-10-06 04:18:14 +02:00
|
|
|
}
|
2016-09-27 16:46:37 +02:00
|
|
|
|
|
|
|
var pubKeys = []
|
2016-09-27 13:04:32 +02:00
|
|
|
|
|
|
|
switch (scriptType) {
|
2016-09-27 16:46:37 +02:00
|
|
|
// does our hash160(pubKey) match the output scripts?
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.P2PKH:
|
2016-09-27 16:46:37 +02:00
|
|
|
if (!ourPubKey) break
|
2016-09-27 13:04:32 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
var pkh1 = scriptChunks[2]
|
|
|
|
var pkh2 = bcrypto.hash160(ourPubKey)
|
2016-10-06 12:30:35 +02:00
|
|
|
if (pkh1.equals(pkh2)) pubKeys = [ourPubKey]
|
2016-09-27 13:08:48 +02:00
|
|
|
break
|
2016-09-27 13:04:32 +02:00
|
|
|
|
2016-12-14 05:41:24 +01:00
|
|
|
// does our hash160(pubKey) match the output scripts?
|
|
|
|
case scriptTypes.P2WPKH:
|
|
|
|
if (!ourPubKey) break
|
|
|
|
|
|
|
|
var wpkh1 = scriptChunks[1]
|
|
|
|
var wpkh2 = bcrypto.hash160(ourPubKey)
|
|
|
|
if (wpkh1.equals(wpkh2)) pubKeys = [ourPubKey]
|
|
|
|
break
|
|
|
|
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.P2PK:
|
2016-09-27 16:46:37 +02:00
|
|
|
pubKeys = scriptChunks.slice(0, 1)
|
2016-09-27 13:08:48 +02:00
|
|
|
break
|
2016-09-27 13:04:32 +02:00
|
|
|
|
2016-10-27 11:05:45 +02:00
|
|
|
case scriptTypes.MULTISIG:
|
2016-09-27 16:46:37 +02:00
|
|
|
pubKeys = scriptChunks.slice(1, -2)
|
2016-09-27 13:08:48 +02:00
|
|
|
break
|
|
|
|
|
2016-10-06 04:18:14 +02:00
|
|
|
default: return { scriptType: scriptType }
|
2016-09-27 13:08:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
pubKeys: pubKeys,
|
2016-09-27 16:46:37 +02:00
|
|
|
scriptType: scriptType,
|
|
|
|
signatures: pubKeys.map(function () { return undefined })
|
2016-09-27 13:04:32 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
function checkP2shInput (input, redeemScriptHash) {
|
|
|
|
if (input.prevOutType) {
|
|
|
|
if (input.prevOutType !== scriptTypes.P2SH) throw new Error('PrevOutScript must be P2SH')
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
var prevOutScriptScriptHash = bscript.decompile(input.prevOutScript)[1]
|
|
|
|
if (!prevOutScriptScriptHash.equals(redeemScriptHash)) throw new Error('Inconsistent hash160(RedeemScript)')
|
|
|
|
}
|
|
|
|
}
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
function checkP2WSHInput (input, witnessScriptHash) {
|
|
|
|
if (input.prevOutType) {
|
|
|
|
if (input.prevOutType !== scriptTypes.P2WSH) throw new Error('PrevOutScript must be P2WSH')
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
var scriptHash = bscript.decompile(input.prevOutScript)[1]
|
|
|
|
if (!scriptHash.equals(witnessScriptHash)) throw new Error('Inconsistent hash160(WitnessScript)')
|
|
|
|
}
|
|
|
|
}
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
function prepareInput (input, kpPubKey, redeemScript, witnessScript) {
|
|
|
|
var expanded
|
|
|
|
var prevOutType
|
|
|
|
var prevOutScript
|
|
|
|
|
|
|
|
var p2sh = false
|
|
|
|
var p2shType
|
|
|
|
var redeemScriptHash
|
|
|
|
|
|
|
|
var witness = false
|
|
|
|
var witnessType
|
|
|
|
var witnessScriptHash
|
|
|
|
|
|
|
|
if (redeemScript && witnessScript) {
|
|
|
|
redeemScriptHash = bcrypto.hash160(redeemScript)
|
|
|
|
witnessScriptHash = bcrypto.hash256(witnessScript)
|
|
|
|
checkP2shInput(input, redeemScriptHash)
|
|
|
|
if (!redeemScript.equals(bscript.witnessScriptHash.output.encode(witnessScriptHash))) throw new Error('Witness script inconsistent with redeem script')
|
|
|
|
|
|
|
|
expanded = expandOutput(witnessScript, undefined, kpPubKey)
|
|
|
|
if (!expanded.pubKeys) throw new Error('WitnessScript not supported "' + bscript.toASM(redeemScript) + '"')
|
|
|
|
|
|
|
|
prevOutType = bscript.types.P2SH
|
|
|
|
prevOutScript = bscript.scriptHash.output.encode(redeemScriptHash)
|
|
|
|
p2sh = witness = true
|
|
|
|
p2shType = bscript.types.P2WSH
|
|
|
|
witnessType = expanded.scriptType
|
|
|
|
} else if (redeemScript) {
|
|
|
|
redeemScriptHash = bcrypto.hash160(redeemScript)
|
|
|
|
checkP2shInput(input, redeemScriptHash)
|
|
|
|
|
|
|
|
expanded = expandOutput(redeemScript, undefined, kpPubKey)
|
|
|
|
if (!expanded.pubKeys) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
prevOutType = bscript.types.P2SH
|
|
|
|
prevOutScript = bscript.scriptHash.output.encode(redeemScriptHash)
|
|
|
|
p2sh = true
|
|
|
|
p2shType = expanded.scriptType
|
|
|
|
} else if (witnessScript) {
|
|
|
|
witnessScriptHash = bcrypto.hash256(witnessScript)
|
|
|
|
checkP2WSHInput(input, witnessScriptHash)
|
|
|
|
|
|
|
|
expanded = expandOutput(witnessScript, undefined, kpPubKey)
|
|
|
|
if (!expanded.pubKeys) throw new Error('WitnessScript not supported "' + bscript.toASM(redeemScript) + '"')
|
|
|
|
|
|
|
|
prevOutType = bscript.types.P2WSH
|
|
|
|
prevOutScript = bscript.witnessScriptHash.output.encode(witnessScriptHash)
|
|
|
|
witness = true
|
|
|
|
witnessType = expanded.scriptType
|
2016-09-28 07:22:47 +02:00
|
|
|
} else if (input.prevOutType) {
|
2016-12-14 05:41:24 +01:00
|
|
|
// embedded scripts are not possible without a redeemScript
|
|
|
|
if (input.prevOutType === scriptTypes.P2SH ||
|
2016-12-29 17:26:23 +01:00
|
|
|
input.prevOutType === scriptTypes.P2WSH) {
|
2016-12-14 05:41:24 +01:00
|
|
|
throw new Error('PrevOutScript is ' + input.prevOutType + ', requires redeemScript')
|
|
|
|
}
|
2016-09-28 07:22:47 +02:00
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
prevOutType = input.prevOutType
|
|
|
|
prevOutScript = input.prevOutScript
|
2016-10-06 04:18:14 +02:00
|
|
|
expanded = expandOutput(input.prevOutScript, input.prevOutType, kpPubKey)
|
|
|
|
if (!expanded.pubKeys) return
|
|
|
|
|
2016-12-29 17:26:23 +01:00
|
|
|
witness = (input.prevOutScript === scriptTypes.P2WPKH)
|
2016-09-28 07:22:47 +02:00
|
|
|
} else {
|
2016-12-29 17:26:23 +01:00
|
|
|
prevOutScript = bscript.pubKeyHash.output.encode(bcrypto.hash160(kpPubKey))
|
|
|
|
expanded = expandOutput(prevOutScript, scriptTypes.P2PKH, kpPubKey)
|
|
|
|
prevOutType = scriptTypes.P2PKH
|
|
|
|
witness = false
|
2016-09-28 07:22:47 +02:00
|
|
|
}
|
2016-12-29 17:26:23 +01:00
|
|
|
|
|
|
|
if (p2sh) {
|
|
|
|
input.redeemScript = redeemScript
|
|
|
|
input.redeemScriptType = p2shType
|
|
|
|
}
|
|
|
|
|
|
|
|
if (witness && witnessType === bscript.types.P2WSH) {
|
|
|
|
input.witnessScript = witnessScript
|
|
|
|
input.witnessScriptType = witnessType
|
|
|
|
}
|
|
|
|
|
|
|
|
input.pubKeys = expanded.pubKeys
|
|
|
|
input.signatures = expanded.signatures
|
|
|
|
input.prevOutScript = prevOutScript
|
|
|
|
input.prevOutType = prevOutType
|
|
|
|
input.witness = witness
|
2016-09-28 07:22:47 +02:00
|
|
|
}
|
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
function buildStack (type, signatures, pubKeys, allowIncomplete) {
|
|
|
|
if (type === scriptTypes.P2PKH) {
|
|
|
|
if (signatures.length < 1 || !signatures[0]) throw new Error('Not enough signatures provided')
|
|
|
|
return bscript.pubKeyHash.input.encodeStack(signatures[0], pubKeys[0])
|
|
|
|
} else if (type === scriptTypes.P2PK) {
|
|
|
|
if (signatures.length < 1 || !signatures[0]) throw new Error('Not enough signatures provided')
|
|
|
|
return bscript.pubKey.input.encodeStack(signatures[0])
|
|
|
|
} else {
|
|
|
|
signatures = signatures.map(function (signature) {
|
|
|
|
return signature || ops.OP_0
|
|
|
|
})
|
2016-09-28 07:27:14 +02:00
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
if (!allowIncomplete) {
|
|
|
|
// remove blank signatures
|
|
|
|
signatures = signatures.filter(function (x) { return x !== ops.OP_0 })
|
|
|
|
}
|
2016-09-28 07:27:14 +02:00
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
return bscript.multisig.input.encodeStack(signatures /* see if it's necessary first */)
|
2016-10-12 03:15:13 +02:00
|
|
|
}
|
2016-12-22 13:18:45 +01:00
|
|
|
}
|
2016-10-12 03:15:13 +02:00
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
function buildInput (input, allowIncomplete) {
|
|
|
|
var scriptType = input.prevOutType
|
|
|
|
var sig = []
|
|
|
|
var witness = []
|
|
|
|
if (SIGNABLE.indexOf(scriptType) !== -1) {
|
|
|
|
sig = buildStack(scriptType, input.signatures, input.pubKeys, input.script, allowIncomplete)
|
2016-12-14 05:41:24 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
var p2sh = false
|
|
|
|
if (scriptType === bscript.types.P2SH) {
|
|
|
|
// We can remove this error later when we have a guarantee prepareInput
|
|
|
|
// rejects unsignabale scripts - it MUST be signable at this point.
|
|
|
|
if (P2SH.indexOf(input.redeemScriptType) === -1) {
|
|
|
|
throw new Error('Impossible to sign this type')
|
|
|
|
}
|
|
|
|
p2sh = true
|
|
|
|
if (SIGNABLE.indexOf(input.redeemScriptType) !== -1) {
|
|
|
|
sig = buildStack(input.redeemScriptType, input.signatures, input.pubKeys, allowIncomplete)
|
|
|
|
}
|
|
|
|
// If it wasn't SIGNABLE, it's witness, defer to that
|
|
|
|
scriptType = input.redeemScriptType
|
2016-12-14 05:41:24 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
if (scriptType === bscript.types.P2WPKH) {
|
|
|
|
// P2WPKH is a special case of P2PKH
|
|
|
|
witness = buildStack(bscript.types.P2PKH, input.signatures, input.pubKeys, allowIncomplete)
|
|
|
|
} else if (scriptType === bscript.types.P2WSH) {
|
|
|
|
// We can remove this check later
|
|
|
|
if (SIGNABLE.indexOf(input.witnessScriptType) !== -1) {
|
|
|
|
witness = buildStack(input.witnessScriptType, input.signatures, input.pubKeys, allowIncomplete)
|
|
|
|
witness.push(input.witnessScript)
|
|
|
|
} else {
|
|
|
|
// We can remove this error later when we have a guarantee prepareInput
|
|
|
|
// rejects unsignble scripts - it MUST be signable at this point.
|
|
|
|
throw new Error()
|
|
|
|
}
|
|
|
|
|
|
|
|
scriptType = input.witnessScriptType
|
2016-12-14 05:41:24 +01:00
|
|
|
}
|
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
// append redeemScript if necessary
|
|
|
|
if (p2sh) {
|
|
|
|
sig.push(input.redeemScript)
|
2016-10-12 03:15:13 +02:00
|
|
|
}
|
|
|
|
|
2016-12-14 05:41:24 +01:00
|
|
|
return {
|
2016-12-22 13:18:45 +01:00
|
|
|
type: scriptType,
|
|
|
|
script: bscript.compile(sig),
|
2016-12-14 05:41:24 +01:00
|
|
|
witness: witness
|
|
|
|
}
|
2016-09-28 07:27:14 +02:00
|
|
|
}
|
|
|
|
|
2016-11-12 03:19:11 +01:00
|
|
|
function TransactionBuilder (network, maximumFeeRate) {
|
2015-01-05 05:30:04 +01:00
|
|
|
this.prevTxMap = {}
|
2015-07-24 04:16:37 +02:00
|
|
|
this.network = network || networks.bitcoin
|
2015-01-06 06:05:38 +01:00
|
|
|
|
2016-11-12 03:19:11 +01:00
|
|
|
// WARNING: This is __NOT__ to be relied on, its just another potential safety mechanism (safety in-depth)
|
|
|
|
this.maximumFeeRate = maximumFeeRate || 1000
|
|
|
|
|
2015-01-06 02:33:49 +01:00
|
|
|
this.inputs = []
|
2015-01-06 06:05:38 +01:00
|
|
|
this.tx = new Transaction()
|
|
|
|
}
|
|
|
|
|
2015-11-26 02:40:06 +01:00
|
|
|
TransactionBuilder.prototype.setLockTime = function (locktime) {
|
|
|
|
typeforce(types.UInt32, locktime)
|
|
|
|
|
|
|
|
// if any signatures exist, throw
|
|
|
|
if (this.inputs.some(function (input) {
|
|
|
|
if (!input.signatures) return false
|
|
|
|
|
|
|
|
return input.signatures.some(function (s) { return s })
|
|
|
|
})) {
|
|
|
|
throw new Error('No, this would invalidate signatures')
|
|
|
|
}
|
|
|
|
|
|
|
|
this.tx.locktime = locktime
|
|
|
|
}
|
|
|
|
|
2016-06-22 06:57:11 +02:00
|
|
|
TransactionBuilder.prototype.setVersion = function (version) {
|
|
|
|
typeforce(types.UInt32, version)
|
|
|
|
|
|
|
|
// XXX: this might eventually become more complex depending on what the versions represent
|
|
|
|
this.tx.version = version
|
|
|
|
}
|
|
|
|
|
2015-08-07 08:41:24 +02:00
|
|
|
TransactionBuilder.fromTransaction = function (transaction, network) {
|
2015-08-07 08:55:13 +02:00
|
|
|
var txb = new TransactionBuilder(network)
|
2014-08-18 00:59:26 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
// Copy transaction fields
|
|
|
|
txb.setVersion(transaction.version)
|
|
|
|
txb.setLockTime(transaction.locktime)
|
2014-08-18 00:59:26 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
// Copy outputs (done first to avoid signature invalidation)
|
2015-02-23 00:36:57 +01:00
|
|
|
transaction.outs.forEach(function (txOut) {
|
2014-12-02 04:20:04 +01:00
|
|
|
txb.addOutput(txOut.script, txOut.value)
|
2014-08-18 00:59:26 +02:00
|
|
|
})
|
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
// Copy inputs
|
|
|
|
transaction.ins.forEach(function (txIn) {
|
2016-11-09 03:01:29 +01:00
|
|
|
txb.__addInputUnsafe(txIn.hash, txIn.index, {
|
|
|
|
sequence: txIn.sequence,
|
2016-12-14 05:41:24 +01:00
|
|
|
script: txIn.script,
|
|
|
|
witness: txIn.witness
|
2016-11-09 03:01:29 +01:00
|
|
|
})
|
2016-09-27 16:46:37 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
// fix some things not possible through the public API
|
|
|
|
txb.inputs.forEach(function (input, i) {
|
2016-10-12 03:04:45 +02:00
|
|
|
fixMultisigOrder(input, transaction, i)
|
2014-08-18 00:59:26 +02:00
|
|
|
})
|
|
|
|
|
|
|
|
return txb
|
|
|
|
}
|
|
|
|
|
2015-03-02 08:06:49 +01:00
|
|
|
TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
|
2016-09-28 08:46:35 +02:00
|
|
|
if (!this.__canModifyInputs()) {
|
2016-09-27 16:46:37 +02:00
|
|
|
throw new Error('No, this would invalidate signatures')
|
|
|
|
}
|
|
|
|
|
2016-11-09 03:01:29 +01:00
|
|
|
var value
|
|
|
|
|
2015-09-14 07:03:35 +02:00
|
|
|
// is it a hex string?
|
2015-03-02 08:06:49 +01:00
|
|
|
if (typeof txHash === 'string') {
|
2015-09-14 07:03:35 +02:00
|
|
|
// transaction hashs's are displayed in reverse order, un-reverse it
|
2016-12-21 02:52:09 +01:00
|
|
|
txHash = new Buffer(txHash, 'hex').reverse()
|
2015-03-02 08:06:49 +01:00
|
|
|
|
2015-09-14 07:03:35 +02:00
|
|
|
// is it a Transaction object?
|
2015-03-02 08:06:49 +01:00
|
|
|
} else if (txHash instanceof Transaction) {
|
2016-11-09 03:01:29 +01:00
|
|
|
var txOut = txHash.outs[vout]
|
|
|
|
prevOutScript = txOut.script
|
|
|
|
value = txOut.value
|
|
|
|
|
2015-03-02 08:06:49 +01:00
|
|
|
txHash = txHash.getHash()
|
2014-06-16 08:05:31 +02:00
|
|
|
}
|
|
|
|
|
2016-11-09 03:01:29 +01:00
|
|
|
return this.__addInputUnsafe(txHash, vout, {
|
|
|
|
sequence: sequence,
|
|
|
|
prevOutScript: prevOutScript,
|
|
|
|
value: value
|
|
|
|
})
|
2016-09-27 16:46:37 +02:00
|
|
|
}
|
2015-01-06 02:33:49 +01:00
|
|
|
|
2016-11-09 03:01:29 +01:00
|
|
|
TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, options) {
|
2016-09-27 16:46:37 +02:00
|
|
|
if (Transaction.isCoinbaseHash(txHash)) {
|
|
|
|
throw new Error('coinbase inputs not supported')
|
|
|
|
}
|
2015-09-08 13:22:54 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
var prevTxOut = txHash.toString('hex') + ':' + vout
|
2016-12-14 01:46:45 +01:00
|
|
|
if (this.prevTxMap[prevTxOut] !== undefined) throw new Error('Duplicate TxOut: ' + prevTxOut)
|
2015-01-06 02:33:49 +01:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
var input = {}
|
2015-09-08 13:22:54 +02:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
// derive what we can from the scriptSig
|
2016-11-09 03:01:29 +01:00
|
|
|
if (options.script !== undefined) {
|
2016-12-14 05:41:24 +01:00
|
|
|
input = expandInput(options.script, null, options.witness)
|
2016-11-09 03:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// if an input value was given, retain it
|
|
|
|
if (options.value !== undefined) {
|
|
|
|
input.value = options.value
|
2016-09-27 16:46:37 +02:00
|
|
|
}
|
2015-01-06 02:33:49 +01:00
|
|
|
|
2016-09-27 16:46:37 +02:00
|
|
|
// derive what we can from the previous transactions output script
|
2016-11-09 03:01:29 +01:00
|
|
|
if (!input.prevOutScript && options.prevOutScript) {
|
2016-10-06 04:18:14 +02:00
|
|
|
var prevOutType
|
2016-09-27 16:46:37 +02:00
|
|
|
|
|
|
|
if (!input.pubKeys && !input.signatures) {
|
2016-11-09 03:01:29 +01:00
|
|
|
var expanded = expandOutput(options.prevOutScript)
|
2016-10-06 04:18:14 +02:00
|
|
|
|
|
|
|
if (expanded.pubKeys) {
|
2016-09-27 16:46:37 +02:00
|
|
|
input.pubKeys = expanded.pubKeys
|
|
|
|
input.signatures = expanded.signatures
|
|
|
|
}
|
2016-10-06 04:18:14 +02:00
|
|
|
|
|
|
|
prevOutType = expanded.scriptType
|
2015-01-06 02:33:49 +01:00
|
|
|
}
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2016-11-09 03:01:29 +01:00
|
|
|
input.prevOutScript = options.prevOutScript
|
|
|
|
input.prevOutType = prevOutType || bscript.classifyOutput(options.prevOutScript)
|
2014-06-16 08:05:31 +02:00
|
|
|
}
|
|
|
|
|
2016-11-09 03:01:29 +01:00
|
|
|
var vin = this.tx.addInput(txHash, vout, options.sequence, options.scriptSig)
|
2015-02-05 03:29:59 +01:00
|
|
|
this.inputs[vin] = input
|
2016-12-14 01:46:45 +01:00
|
|
|
this.prevTxMap[prevTxOut] = vin
|
2015-01-06 02:33:49 +01:00
|
|
|
|
2015-02-05 03:29:59 +01:00
|
|
|
return vin
|
2014-06-16 08:05:31 +02:00
|
|
|
}
|
|
|
|
|
2015-02-23 00:36:57 +01:00
|
|
|
TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
|
2016-09-28 08:46:35 +02:00
|
|
|
if (!this.__canModifyOutputs()) {
|
2015-11-26 02:40:26 +01:00
|
|
|
throw new Error('No, this would invalidate signatures')
|
|
|
|
}
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2015-08-13 11:07:09 +02:00
|
|
|
// Attempt to get a script if it's a base58 address string
|
2015-03-02 07:18:56 +01:00
|
|
|
if (typeof scriptPubKey === 'string') {
|
2015-08-20 05:37:19 +02:00
|
|
|
scriptPubKey = baddress.toOutputScript(scriptPubKey, this.network)
|
2015-03-02 07:18:56 +01:00
|
|
|
}
|
|
|
|
|
2015-11-26 02:07:32 +01:00
|
|
|
return this.tx.addOutput(scriptPubKey, value)
|
2014-06-16 08:05:31 +02:00
|
|
|
}
|
|
|
|
|
2015-02-23 00:36:57 +01:00
|
|
|
TransactionBuilder.prototype.build = function () {
|
|
|
|
return this.__build(false)
|
|
|
|
}
|
|
|
|
TransactionBuilder.prototype.buildIncomplete = function () {
|
|
|
|
return this.__build(true)
|
|
|
|
}
|
2015-02-05 03:57:21 +01:00
|
|
|
|
2015-02-23 00:36:57 +01:00
|
|
|
TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
2014-06-16 08:05:31 +02:00
|
|
|
if (!allowIncomplete) {
|
2015-08-11 10:39:59 +02:00
|
|
|
if (!this.tx.ins.length) throw new Error('Transaction has no inputs')
|
|
|
|
if (!this.tx.outs.length) throw new Error('Transaction has no outputs')
|
2014-06-16 08:05:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var tx = this.tx.clone()
|
|
|
|
|
2015-09-08 13:22:54 +02:00
|
|
|
// Create script signatures from inputs
|
2016-09-27 17:12:39 +02:00
|
|
|
this.inputs.forEach(function (input, i) {
|
2016-02-08 15:12:02 +01:00
|
|
|
var scriptType = input.redeemScriptType || input.prevOutType
|
2016-10-12 02:11:15 +02:00
|
|
|
if (!scriptType && !allowIncomplete) throw new Error('Transaction is not complete')
|
2016-12-14 05:41:24 +01:00
|
|
|
var result = buildInput(input, allowIncomplete)
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2016-12-14 05:41:24 +01:00
|
|
|
// skip if no result
|
2016-12-22 13:18:45 +01:00
|
|
|
if (!allowIncomplete) {
|
|
|
|
if (SIGNABLE.indexOf(result.type) === -1 && result.type !== bscript.types.P2WPKH) {
|
|
|
|
throw new Error(result.type + ' not supported')
|
|
|
|
}
|
|
|
|
}
|
2014-06-16 08:05:31 +02:00
|
|
|
|
2016-12-22 13:18:45 +01:00
|
|
|
tx.setInputScript(i, result.script)
|
|
|
|
tx.setWitness(i, result.witness)
|
2014-06-16 08:05:31 +02:00
|
|
|
})
|
|
|
|
|
2016-11-12 03:08:10 +01:00
|
|
|
if (!allowIncomplete) {
|
|
|
|
// do not rely on this, its merely a last resort
|
2016-11-12 03:19:11 +01:00
|
|
|
if (this.__overMaximumFees(tx.byteLength())) {
|
2016-11-12 03:08:10 +01:00
|
|
|
throw new Error('Transaction has absurd fees')
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-16 08:05:31 +02:00
|
|
|
return tx
|
|
|
|
}
|
|
|
|
|
2016-10-06 04:18:14 +02:00
|
|
|
function canSign (input) {
|
2016-10-12 03:53:51 +02:00
|
|
|
return input.prevOutScript !== undefined &&
|
2016-10-06 04:18:14 +02:00
|
|
|
input.pubKeys !== undefined &&
|
|
|
|
input.signatures !== undefined &&
|
|
|
|
input.signatures.length === input.pubKeys.length &&
|
2016-12-14 05:41:24 +01:00
|
|
|
input.pubKeys.length > 0 &&
|
|
|
|
input.witness !== undefined
|
2016-10-06 04:18:14 +02:00
|
|
|
}
|
|
|
|
|
2016-12-14 05:41:24 +01:00
|
|
|
TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType, witnessValue) {
|
2015-08-11 10:39:59 +02:00
|
|
|
if (keyPair.network !== this.network) throw new Error('Inconsistent network')
|
2016-09-27 17:12:39 +02:00
|
|
|
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
|
2014-08-18 00:59:26 +02:00
|
|
|
hashType = hashType || Transaction.SIGHASH_ALL
|
2014-07-28 06:28:44 +02:00
|
|
|
|
2016-09-27 17:12:39 +02:00
|
|
|
var input = this.inputs[vin]
|
2015-03-02 06:48:36 +01:00
|
|
|
|
2016-10-12 01:52:33 +02:00
|
|
|
// if redeemScript was previously provided, enforce consistency
|
|
|
|
if (input.redeemScript !== undefined &&
|
|
|
|
redeemScript &&
|
|
|
|
!input.redeemScript.equals(redeemScript)) {
|
|
|
|
throw new Error('Inconsistent redeemScript')
|
2016-10-06 04:18:14 +02:00
|
|
|
}
|
2014-07-28 07:40:07 +02:00
|
|
|
|
2016-10-06 04:18:14 +02:00
|
|
|
var kpPubKey = keyPair.getPublicKeyBuffer()
|
|
|
|
if (!canSign(input)) {
|
2016-12-14 05:41:24 +01:00
|
|
|
prepareInput(input, kpPubKey, redeemScript, witnessValue)
|
2016-10-06 04:18:14 +02:00
|
|
|
|
|
|
|
if (!canSign(input)) throw Error(input.prevOutType + ' not supported')
|
2015-01-06 02:33:49 +01:00
|
|
|
}
|
|
|
|
|
2016-09-28 07:22:47 +02:00
|
|
|
// ready to sign
|
2016-09-27 16:46:37 +02:00
|
|
|
var hashScript = input.redeemScript || input.prevOutScript
|
2016-12-14 05:41:24 +01:00
|
|
|
|
|
|
|
var signatureHash
|
|
|
|
if (input.witness) {
|
|
|
|
signatureHash = this.tx.hashForWitnessV0(vin, hashScript, witnessValue, hashType)
|
|
|
|
} else {
|
|
|
|
signatureHash = this.tx.hashForSignature(vin, hashScript, hashType)
|
|
|
|
}
|
2015-03-03 11:51:37 +01:00
|
|
|
|
2015-02-05 04:13:27 +01:00
|
|
|
// enforce in order signing of public keys
|
2016-10-12 01:52:33 +02:00
|
|
|
var signed = input.pubKeys.some(function (pubKey, i) {
|
2016-10-06 12:30:35 +02:00
|
|
|
if (!kpPubKey.equals(pubKey)) return false
|
2015-08-11 10:39:59 +02:00
|
|
|
if (input.signatures[i]) throw new Error('Signature already exists')
|
2015-03-02 06:48:36 +01:00
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
input.signatures[i] = keyPair.sign(signatureHash).toScriptSignature(hashType)
|
2014-12-12 05:19:03 +01:00
|
|
|
return true
|
2015-08-11 10:39:59 +02:00
|
|
|
})
|
|
|
|
|
2016-10-12 01:52:33 +02:00
|
|
|
if (!signed) throw new Error('Key pair cannot sign for this input')
|
2014-07-28 06:28:44 +02:00
|
|
|
}
|
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
function signatureHashType (buffer) {
|
|
|
|
return buffer.readUInt8(buffer.length - 1)
|
|
|
|
}
|
|
|
|
|
2016-09-28 08:46:35 +02:00
|
|
|
TransactionBuilder.prototype.__canModifyInputs = function () {
|
2016-10-12 03:53:51 +02:00
|
|
|
return this.inputs.every(function (input) {
|
|
|
|
// any signatures?
|
|
|
|
if (input.signatures === undefined) return true
|
|
|
|
|
|
|
|
return input.signatures.every(function (signature) {
|
|
|
|
if (!signature) return true
|
|
|
|
var hashType = signatureHashType(signature)
|
2016-09-28 08:46:35 +02:00
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
// if SIGHASH_ANYONECANPAY is set, signatures would not
|
|
|
|
// be invalidated by more inputs
|
|
|
|
return hashType & Transaction.SIGHASH_ANYONECANPAY
|
|
|
|
})
|
2016-09-28 08:46:35 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
TransactionBuilder.prototype.__canModifyOutputs = function () {
|
|
|
|
var nInputs = this.tx.ins.length
|
|
|
|
var nOutputs = this.tx.outs.length
|
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
return this.inputs.every(function (input) {
|
|
|
|
if (input.signatures === undefined) return true
|
|
|
|
|
|
|
|
return input.signatures.every(function (signature) {
|
|
|
|
if (!signature) return true
|
|
|
|
var hashType = signatureHashType(signature)
|
2016-09-28 08:46:35 +02:00
|
|
|
|
2016-10-12 03:53:51 +02:00
|
|
|
var hashTypeMod = hashType & 0x1f
|
|
|
|
if (hashTypeMod === Transaction.SIGHASH_NONE) return true
|
|
|
|
if (hashTypeMod === Transaction.SIGHASH_SINGLE) {
|
|
|
|
// if SIGHASH_SINGLE is set, and nInputs > nOutputs
|
|
|
|
// some signatures would be invalidated by the addition
|
|
|
|
// of more outputs
|
|
|
|
return nInputs <= nOutputs
|
|
|
|
}
|
|
|
|
})
|
2016-09-28 08:46:35 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2016-11-12 03:19:11 +01:00
|
|
|
TransactionBuilder.prototype.__overMaximumFees = function (bytes) {
|
2016-11-09 03:01:29 +01:00
|
|
|
// not all inputs will have .value defined
|
|
|
|
var incoming = this.inputs.reduce(function (a, x) { return a + (x.value >>> 0) }, 0)
|
|
|
|
|
|
|
|
// but all outputs do, and if we have any input value
|
|
|
|
// we can immediately determine if the outputs are too small
|
|
|
|
var outgoing = this.tx.outs.reduce(function (a, x) { return a + x.value }, 0)
|
|
|
|
var fee = incoming - outgoing
|
2016-11-12 03:08:10 +01:00
|
|
|
var feeRate = fee / bytes
|
2016-11-09 03:01:29 +01:00
|
|
|
|
2016-11-12 03:19:11 +01:00
|
|
|
return feeRate > this.maximumFeeRate
|
2016-11-09 03:01:29 +01:00
|
|
|
}
|
|
|
|
|
2014-06-16 08:05:31 +02:00
|
|
|
module.exports = TransactionBuilder
|