From 3a371fccecf60d5ff180b15ab8efbc839ce46e12 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 5 Feb 2015 14:13:27 +1100 Subject: [PATCH] TxBuilder: remove initialized field from signature inputs --- src/transaction_builder.js | 116 ++++++++++++------------- test/fixtures/transaction_builder.json | 7 +- 2 files changed, 58 insertions(+), 65 deletions(-) diff --git a/src/transaction_builder.js b/src/transaction_builder.js index 7d44f57..fb394ee 100644 --- a/src/transaction_builder.js +++ b/src/transaction_builder.js @@ -33,60 +33,51 @@ function extractInput(txIn) { } // Extract hashType, pubKeys and signatures - var hashType, initialized, parsed, pubKeys, signatures + var hashType, parsed, pubKeys, signatures switch (scriptType) { - case 'pubkeyhash': + case 'pubkeyhash': { parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) hashType = parsed.hashType pubKeys = [ECPubKey.fromBuffer(scriptSig.chunks[1])] signatures = [parsed.signature] - - initialized = true prevOutScript = pubKeys[0].getAddress().toOutputScript() break + } - case 'multisig': - signatures = scriptSig.chunks.slice(1).map(function(chunk) { - if (chunk === ops.OP_0) return chunk - - var parsed = ECSignature.parseScriptSignature(chunk) - hashType = parsed.hashType - - return parsed.signature - }) - - if (redeemScript) { - pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) - } - - break - - case 'pubkey': + case 'pubkey': { parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0]) hashType = parsed.hashType signatures = [parsed.signature] - initialized = true - if (redeemScript) { pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])] } break + } + + case 'multisig': { + signatures = scriptSig.chunks.slice(1).map(function(chunk) { + if (chunk === ops.OP_0) return chunk + + var parsed = ECSignature.parseScriptSignature(chunk) + hashType = parsed.hashType + + return parsed.signature + }) - default: if (redeemScript) { - initialized = true + pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer) } break + } } return { hashType: hashType, - initialized: initialized, prevOutScript: prevOutScript, prevOutType: prevOutType, pubKeys: pubKeys, @@ -222,38 +213,40 @@ TransactionBuilder.prototype.__build = function(allowIncomplete) { var scriptSig if (!allowIncomplete) { - assert(input.initialized, 'Transaction is not complete') + assert(!!scriptType, 'Transaction is not complete') assert(scriptType in canSignTypes, scriptType + ' not supported') assert(input.signatures, 'Transaction is missing signatures') } - switch (scriptType) { - case 'pubkeyhash': - var pkhSignature = input.signatures[0].toScriptSignature(input.hashType) - scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0]) - break + if (input.signatures) { + switch (scriptType) { + case 'pubkeyhash': + var pkhSignature = input.signatures[0].toScriptSignature(input.hashType) + scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0]) + break - case 'multisig': - // Array.prototype.map is sparse-compatible - var msSignatures = input.signatures.map(function(signature) { - return signature.toScriptSignature(input.hashType) - }) + case 'multisig': + // Array.prototype.map is sparse-compatible + var msSignatures = input.signatures.map(function(signature) { + return signature.toScriptSignature(input.hashType) + }) - // fill in blanks with OP_0 - for (var i = 0; i < msSignatures.length; ++i) { - if (msSignatures[i]) continue + // fill in blanks with OP_0 + for (var i = 0; i < msSignatures.length; ++i) { + if (msSignatures[i]) continue - msSignatures[i] = ops.OP_0 - } + msSignatures[i] = ops.OP_0 + } - var redeemScript = allowIncomplete ? undefined : input.redeemScript - scriptSig = scripts.multisigInput(signatures, redeemScript) - break + var redeemScript = allowIncomplete ? undefined : input.redeemScript + scriptSig = scripts.multisigInput(msSignatures, redeemScript) + break - case 'pubkey': - var pkSignature = input.signatures[0].toScriptSignature(input.hashType) - scriptSig = scripts.pubKeyInput(pkSignature) - break + case 'pubkey': + var pkSignature = input.signatures[0].toScriptSignature(input.hashType) + scriptSig = scripts.pubKeyInput(pkSignature) + break + } } // if we built a scriptSig, wrap as scriptHash if necessary @@ -277,18 +270,19 @@ TransactionBuilder.prototype.sign = function(index, privKey, redeemScript, hashT assert.equal(input.hashType, hashType, 'Inconsistent hashType') } + var initialized = input.prevOutScript && + input.prevOutType && + input.hashType && + input.pubKeys && + input.signatures + // are we already initialized? - if (input.initialized) { + if (initialized) { // redeemScript only needed to initialize, but if provided again, enforce consistency if (redeemScript) { assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript') } - // if signatures already exist, enforce multisig scriptType - if (input.signatures.length > 0) { - assert.equal(input.scriptType, 'multisig', input.scriptType + ' doesn\'t support multiple signatures') - } - // initialize it } else { if (redeemScript) { @@ -344,26 +338,24 @@ TransactionBuilder.prototype.sign = function(index, privKey, redeemScript, hashT } input.hashType = hashType - input.initialized = true input.signatures = input.signatures || [] } // do we know how to sign this? assert(input.scriptType in canSignTypes, input.scriptType + ' not supported') - var signatureScript = input.redeemScript || input.prevOutScript - var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) - var signature = privKey.sign(signatureHash) - - // enforce signing in order of public keys + // enforce in order signing of public keys assert(input.pubKeys.some(function(pubKey, i) { if (!privKey.pub.Q.equals(pubKey.Q)) return false assert(!input.signatures[i], 'Signature already exists') + var signatureScript = input.redeemScript || input.prevOutScript + var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType) + var signature = privKey.sign(signatureHash) input.signatures[i] = signature return true - }), 'privateKey cannot sign for this input') + }, this), 'privateKey cannot sign for this input') } module.exports = TransactionBuilder diff --git a/test/fixtures/transaction_builder.json b/test/fixtures/transaction_builder.json index b6342fd..265ad2a 100644 --- a/test/fixtures/transaction_builder.json +++ b/test/fixtures/transaction_builder.json @@ -206,7 +206,7 @@ }, { "description": "Incomplete transaction w/ prevTxScript defined", - "exception": "Transaction is not complete", + "exception": "Transaction is missing signatures", "alwaysThrows": true, "inputs": [ { @@ -240,7 +240,8 @@ ], "sign": [ { - "exception": "pubkeyhash doesn\\'t support multiple signatures", + "description": "Too many signatures - pubKeyHash", + "exception": "Signature already exists", "inputs": [ { "txId": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", @@ -406,6 +407,7 @@ ] }, { + "description": "Too many signatures - scriptHash(multisig 1-of-1)", "exception": "Signature already exists", "inputs": [ { @@ -418,7 +420,6 @@ }, { "privKey": "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn", - "redeemScript": "OP_1 0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 OP_1 OP_CHECKMULTISIG", "throws": true } ]