TransactionBuilder: if prevOutScript is defined, but not signable, try 1 last time
This commit is contained in:
parent
aaa327b793
commit
17377381c4
3 changed files with 65 additions and 27 deletions
|
@ -87,11 +87,13 @@ function expandInput (scriptSig, redeemScript) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function expandOutput (script, ourPubKey) {
|
function expandOutput (script, scriptType, ourPubKey) {
|
||||||
typeforce(types.Buffer, script)
|
typeforce(types.Buffer, script)
|
||||||
|
|
||||||
var scriptChunks = bscript.decompile(script)
|
var scriptChunks = bscript.decompile(script)
|
||||||
var scriptType = bscript.classifyOutput(script)
|
if (!scriptType) {
|
||||||
|
scriptType = bscript.classifyOutput(scriptChunks)
|
||||||
|
}
|
||||||
|
|
||||||
var pubKeys = []
|
var pubKeys = []
|
||||||
|
|
||||||
|
@ -113,7 +115,7 @@ function expandOutput (script, ourPubKey) {
|
||||||
pubKeys = scriptChunks.slice(1, -2)
|
pubKeys = scriptChunks.slice(1, -2)
|
||||||
break
|
break
|
||||||
|
|
||||||
default: return
|
default: return { scriptType: scriptType }
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -196,8 +198,8 @@ function prepareInput (input, kpPubKey, redeemScript, hashType) {
|
||||||
input.prevOutType = 'scripthash'
|
input.prevOutType = 'scripthash'
|
||||||
}
|
}
|
||||||
|
|
||||||
var expanded = expandOutput(redeemScript, kpPubKey)
|
var expanded = expandOutput(redeemScript, undefined, kpPubKey)
|
||||||
if (!expanded) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')
|
if (!expanded.pubKeys) throw new Error('RedeemScript not supported "' + bscript.toASM(redeemScript) + '"')
|
||||||
|
|
||||||
input.pubKeys = expanded.pubKeys
|
input.pubKeys = expanded.pubKeys
|
||||||
input.redeemScript = redeemScript
|
input.redeemScript = redeemScript
|
||||||
|
@ -209,8 +211,13 @@ function prepareInput (input, kpPubKey, redeemScript, hashType) {
|
||||||
// pay-to-scriptHash is not possible without a redeemScript
|
// pay-to-scriptHash is not possible without a redeemScript
|
||||||
if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')
|
if (input.prevOutType === 'scripthash') throw new Error('PrevOutScript is P2SH, missing redeemScript')
|
||||||
|
|
||||||
// throw if we can't sign with it
|
// try to derive the missing information about the script now that we
|
||||||
if (!input.pubKeys || !input.signatures) throw new Error(input.prevOutType + ' not supported')
|
// have a kpPubKey
|
||||||
|
expanded = expandOutput(input.prevOutScript, input.prevOutType, kpPubKey)
|
||||||
|
if (!expanded.pubKeys) return
|
||||||
|
|
||||||
|
input.pubKeys = expanded.pubKeys
|
||||||
|
input.signatures = expanded.signatures
|
||||||
|
|
||||||
// no prior knowledge, assume pubKeyHash
|
// no prior knowledge, assume pubKeyHash
|
||||||
} else {
|
} else {
|
||||||
|
@ -356,19 +363,21 @@ TransactionBuilder.prototype.__addInputUnsafe = function (txHash, vout, sequence
|
||||||
|
|
||||||
// derive what we can from the previous transactions output script
|
// derive what we can from the previous transactions output script
|
||||||
if (!input.prevOutScript && prevOutScript) {
|
if (!input.prevOutScript && prevOutScript) {
|
||||||
var prevOutScriptChunks = bscript.decompile(prevOutScript)
|
var prevOutType
|
||||||
var prevOutType = bscript.classifyOutput(prevOutScriptChunks)
|
|
||||||
|
|
||||||
if (!input.pubKeys && !input.signatures) {
|
if (!input.pubKeys && !input.signatures) {
|
||||||
var expanded = expandOutput(prevOutScript)
|
var expanded = expandOutput(prevOutScript)
|
||||||
if (expanded) {
|
|
||||||
|
if (expanded.pubKeys) {
|
||||||
input.pubKeys = expanded.pubKeys
|
input.pubKeys = expanded.pubKeys
|
||||||
input.signatures = expanded.signatures
|
input.signatures = expanded.signatures
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prevOutType = expanded.scriptType
|
||||||
}
|
}
|
||||||
|
|
||||||
input.prevOutScript = prevOutScript
|
input.prevOutScript = prevOutScript
|
||||||
input.prevOutType = prevOutType
|
input.prevOutType = prevOutType || bscript.classifyOutput(prevOutScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
var vin = this.tx.addInput(txHash, vout, sequence, scriptSig)
|
var vin = this.tx.addInput(txHash, vout, sequence, scriptSig)
|
||||||
|
@ -436,29 +445,36 @@ TransactionBuilder.prototype.__build = function (allowIncomplete) {
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function canSign (input) {
|
||||||
|
return input.hashType !== undefined &&
|
||||||
|
input.prevOutScript !== undefined &&
|
||||||
|
input.pubKeys !== undefined &&
|
||||||
|
input.signatures !== undefined &&
|
||||||
|
input.signatures.length === input.pubKeys.length &&
|
||||||
|
input.pubKeys.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType) {
|
TransactionBuilder.prototype.sign = function (vin, keyPair, redeemScript, hashType) {
|
||||||
if (keyPair.network !== this.network) throw new Error('Inconsistent network')
|
if (keyPair.network !== this.network) throw new Error('Inconsistent network')
|
||||||
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
|
if (!this.inputs[vin]) throw new Error('No input at index: ' + vin)
|
||||||
hashType = hashType || Transaction.SIGHASH_ALL
|
hashType = hashType || Transaction.SIGHASH_ALL
|
||||||
|
|
||||||
var input = this.inputs[vin]
|
var input = this.inputs[vin]
|
||||||
var canSign = input.hashType !== undefined &&
|
|
||||||
input.prevOutScript !== undefined &&
|
|
||||||
input.pubKeys !== undefined &&
|
|
||||||
input.signatures !== undefined &&
|
|
||||||
input.signatures.length === input.pubKeys.length
|
|
||||||
|
|
||||||
var kpPubKey = keyPair.getPublicKeyBuffer()
|
|
||||||
|
|
||||||
if (canSign) {
|
|
||||||
// if redeemScript was provided, enforce consistency
|
// if redeemScript was provided, enforce consistency
|
||||||
if (redeemScript) {
|
if (input.redeemScript !== undefined && redeemScript) {
|
||||||
if (!input.redeemScript.equals(redeemScript)) throw new Error('Inconsistent redeemScript')
|
if (!input.redeemScript.equals(redeemScript)) throw new Error('Inconsistent redeemScript')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (input.hashType !== undefined) {
|
||||||
if (input.hashType !== hashType) throw new Error('Inconsistent hashType')
|
if (input.hashType !== hashType) throw new Error('Inconsistent hashType')
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
var kpPubKey = keyPair.getPublicKeyBuffer()
|
||||||
|
if (!canSign(input)) {
|
||||||
prepareInput(input, kpPubKey, redeemScript, hashType)
|
prepareInput(input, kpPubKey, redeemScript, hashType)
|
||||||
|
|
||||||
|
if (!canSign(input)) throw Error(input.prevOutType + ' not supported')
|
||||||
}
|
}
|
||||||
|
|
||||||
// ready to sign
|
// ready to sign
|
||||||
|
|
22
test/fixtures/transaction_builder.json
vendored
22
test/fixtures/transaction_builder.json
vendored
|
@ -285,6 +285,28 @@
|
||||||
"value": 10000
|
"value": 10000
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Transaction w/ 1 pubKeyHash transaction input (Issue #644)",
|
||||||
|
"network": "testnet",
|
||||||
|
"txHex": "010000000132595835c74fccf097db4ccae9dc2de621e58e0d3f697a27b469b61c7a223b39000000006b483045022100d771395776280955561190c09a6bca731684d09db8995c53496b816b8222019302202a21c9a90d0b5de188800673ad31861183c3f4cb15ea0988b485686aed9fce1d012103f29374a4c2c218a4077db9ba0b9d674cde3719560460af4eb3190d512dd5de92ffffffff0170170000000000001976a914ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a88ac00000000",
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"txHex": "0100000001f7e6430096cd2790bac115aaab22c0a50fb0a1794305302e1a399e81d8d354f4020000006a47304402205793a862d193264afc32713e2e14541e1ff9ebb647dd7e7e6a0051d0faa87de302205216653741ecbbed573ea2fc053209dd6980616701c27be5b958a159fc97f45a012103e877e7deb32d19250dcfe534ea82c99ad739800295cd5429a7f69e2896c36fcdfeffffff0340420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac8bda2702000000001976a9145a0ef60784137d03e7868d063b05424f2f43799f88ac40420f00000000001976a9145c7b8d623fba952d2387703d051d8e931a6aa0a188ac2fcc0e00",
|
||||||
|
"vout": 0,
|
||||||
|
"signs": [
|
||||||
|
{
|
||||||
|
"keyPair": "cQ6483mDWwoG8o4tn6nU9Jg52RKMjPUWXSY1vycAyPRXQJ1Pn2Rq"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"script": "OP_DUP OP_HASH160 ff99e06c1a4ac394b4e1cb3d3a4b2b47749e339a OP_EQUALVERIFY OP_CHECKSIG",
|
||||||
|
"value": 6000
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"fromTransaction": [
|
"fromTransaction": [
|
||||||
|
|
|
@ -23,11 +23,11 @@ function construct (f, sign) {
|
||||||
f.inputs.forEach(function (input) {
|
f.inputs.forEach(function (input) {
|
||||||
var prevTxScript
|
var prevTxScript
|
||||||
|
|
||||||
if (input.prevTxScript) {
|
if (!input.txHex && input.prevTxScript) {
|
||||||
prevTxScript = bscript.fromASM(input.prevTxScript)
|
prevTxScript = bscript.fromASM(input.prevTxScript)
|
||||||
}
|
}
|
||||||
|
|
||||||
txb.addInput(input.txId, input.vout, input.sequence, prevTxScript)
|
txb.addInput(input.txId || Transaction.fromHex(input.txHex), input.vout, input.sequence, prevTxScript)
|
||||||
})
|
})
|
||||||
|
|
||||||
f.outputs.forEach(function (output) {
|
f.outputs.forEach(function (output) {
|
||||||
|
|
Loading…
Reference in a new issue