diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 2cf785c..21e5c86 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -16,6 +16,71 @@ function TransactionBuilder() { this.tx = new Transaction() } +function extractSignature(txIn) { + assert(!Array.prototype.every.call(txIn.hash, function(x) { + return x === 0 + }), 'coinbase inputs not supported') + + var redeemScript + var scriptSig = txIn.script + var scriptType = scripts.classifyInput(scriptSig, true) + + // Re-classify if P2SH + if (scriptType === 'scripthash') { + redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0]) + scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1)) + + scriptType = scripts.classifyInput(scriptSig, true) + assert.equal(scripts.classifyOutput(redeemScript), scriptType, 'Non-matching scriptSig and scriptPubKey in input') + } + + // Extract hashType, pubKeys and signatures + var hashType, parsed, pubKeys, signatures + + switch (scriptType) { + case 'pubkeyhash': + parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) + var pubKey = ECPubKey.fromBuffer(scriptSig.chunks[1]) + + hashType = parsed.hashType + pubKeys = [pubKey] + signatures = [parsed.signature] + + break + + case 'multisig': + parsed = scriptSig.chunks.slice(1).filter(function(chunk) { + return chunk !== ops.OP_0 + }).map(ECSignature.parseScriptSignature) + + hashType = parsed[0].hashType + pubKeys = [] + signatures = parsed.map(function(p) { return p.signature }) + + break + + case 'pubkey': + parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) + + hashType = parsed.hashType + pubKeys = [] + signatures = [parsed.signature] + + break + + default: + assert(false, scriptType + ' inputs not supported') + } + + return { + hashType: hashType, + pubKeys: pubKeys, + redeemScript: redeemScript, + scriptType: scriptType, + signatures: signatures + } +} + // Static constructors TransactionBuilder.fromTransaction = function(transaction) { var txb = new TransactionBuilder() @@ -35,72 +100,16 @@ TransactionBuilder.fromTransaction = function(transaction) { }) // Extract/add signatures - transaction.ins.forEach(function(txIn, i) { - // Ignore empty scripts - if (txIn.script.buffer.length === 0) return - + txb.signatures = transaction.ins.map(function(txIn) { + // Coinbase inputs not supported assert(!Array.prototype.every.call(txIn.hash, function(x) { return x === 0 }), 'coinbase inputs not supported') - var redeemScript - var scriptSig = txIn.script - var scriptType = scripts.classifyInput(scriptSig, true) - - // Re-classify if P2SH - if (scriptType === 'scripthash') { - redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0]) - scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1)) - - scriptType = scripts.classifyInput(scriptSig, true) - assert.equal(scripts.classifyOutput(redeemScript), scriptType, 'Non-matching scriptSig and scriptPubKey in input') - } - - // Extract hashType, pubKeys and signatures - var hashType, parsed, pubKeys, signatures - - switch (scriptType) { - case 'pubkeyhash': - parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) - var pubKey = ECPubKey.fromBuffer(scriptSig.chunks[1]) - - hashType = parsed.hashType - pubKeys = [pubKey] - signatures = [parsed.signature] - - break - - case 'multisig': - parsed = scriptSig.chunks.slice(1).filter(function(chunk) { - return chunk !== ops.OP_0 - }).map(ECSignature.parseScriptSignature) - - hashType = parsed[0].hashType - pubKeys = [] - signatures = parsed.map(function(p) { return p.signature }) - - break - - case 'pubkey': - parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) - - hashType = parsed.hashType - pubKeys = [] - signatures = [parsed.signature] - - break - - default: - assert(false, scriptType + ' inputs not supported') - } + // Ignore empty scripts + if (txIn.script.buffer.length === 0) return - txb.signatures[i] = { - hashType: hashType, - pubKeys: pubKeys, - redeemScript: redeemScript, - scriptType: scriptType, - signatures: signatures - } + return extractSignature(txIn) }) return txb