From 540a6643ac44aec956dd2b99493474171acdd579 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Thu, 26 Nov 2015 12:40:26 +1100
Subject: [PATCH] txbuilder: clarify SIGHASH invalidation logic in
 addInput/addOutput

---
 src/transaction_builder.js | 34 +++++++++++++++++++++++-----------
 1 file changed, 23 insertions(+), 11 deletions(-)

diff --git a/src/transaction_builder.js b/src/transaction_builder.js
index 5c94ce6..73fd12b 100644
--- a/src/transaction_builder.js
+++ b/src/transaction_builder.js
@@ -208,13 +208,16 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu
     input.prevOutType = prevOutType
   }
 
-  var valid = this.inputs.every(function (input2) {
-    if (input2.hashType === undefined) return true
+  // if signatures exist, adding inputs is only acceptable if SIGHASH_ANYONECANPAY is used
+  // throw if any signatures *didn't* use SIGHASH_ANYONECANPAY
+  if (!this.inputs.every(function (otherInput) {
+    // no signature
+    if (otherInput.hashType === undefined) return true
 
-    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
-  })
-
-  if (!valid) throw new Error('No, this would invalidate signatures')
+    return otherInput.hashType & Transaction.SIGHASH_ANYONECANPAY
+  })) {
+    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')
@@ -228,15 +231,24 @@ TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOu
 
 TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
   var nOutputs = this.tx.outs.length
-  var valid = this.inputs.every(function (input, index) {
+
+  // if signatures exist, adding outputs is only acceptable if SIGHASH_NONE or SIGHASH_SINGLE is used
+  // throws if any signatures didn't use SIGHASH_NONE|SIGHASH_SINGLE
+  if (!this.inputs.every(function (input, index) {
+    // no signature
     if (input.hashType === undefined) return true
 
     var hashTypeMod = input.hashType & 0x1f
-    return (hashTypeMod === Transaction.SIGHASH_NONE) ||
-           (hashTypeMod === Transaction.SIGHASH_SINGLE && index < nOutputs)
-  })
+    if (hashTypeMod === Transaction.SIGHASH_NONE) return true
+    if (hashTypeMod === Transaction.SIGHASH_SINGLE) {
+      // account for SIGHASH_SINGLE signing of a non-existing output, aka the "SIGHASH_SINGLE" bug
+      return index < nOutputs
+    }
 
-  if (!valid) throw new Error('No, this would invalidate signatures')
+    return false
+  })) {
+    throw new Error('No, this would invalidate signatures')
+  }
 
   // Attempt to get a script if it's a base58 address string
   if (typeof scriptPubKey === 'string') {