From 35542e115df07eefd9f0cb6fb7d181fa7946116a Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Mon, 15 Sep 2014 14:21:01 +1000
Subject: [PATCH 1/5] types: enforce consistent type checking

---
 src/address.js     |  4 +++-
 src/ecdsa.js       |  7 +++++--
 src/eckey.js       |  4 +++-
 src/ecpubkey.js    |  8 ++++----
 src/ecsignature.js |  7 +++++--
 src/hdnode.js      |  7 +++++--
 src/script.js      |  7 ++++---
 src/scripts.js     | 16 +++++++++-------
 src/transaction.js | 45 +++++++++++++++++++++++----------------------
 src/types.js       | 38 ++++++++++++++++++++++++++++++++++++++
 10 files changed, 99 insertions(+), 44 deletions(-)
 create mode 100644 src/types.js

diff --git a/src/address.js b/src/address.js
index fde2b28..124c0ef 100644
--- a/src/address.js
+++ b/src/address.js
@@ -1,5 +1,6 @@
 var assert = require('assert')
 var base58check = require('bs58check')
+var enforceType = require('./types')
 var networks = require('./networks')
 var scripts = require('./scripts')
 
@@ -13,7 +14,8 @@ function findScriptTypeByVersion(version) {
 }
 
 function Address(hash, version) {
-  assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash)
+  enforceType('Buffer', hash)
+
   assert.strictEqual(hash.length, 20, 'Invalid hash length')
   assert.strictEqual(version & 0xff, version, 'Invalid version byte')
 
diff --git a/src/ecdsa.js b/src/ecdsa.js
index dabcd05..7b9db0b 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -1,14 +1,17 @@
 var assert = require('assert')
 var crypto = require('./crypto')
+var enforceType = require('./types')
 
 var BigInteger = require('bigi')
 var ECSignature = require('./ecsignature')
 
 // https://tools.ietf.org/html/rfc6979#section-3.2
 function deterministicGenerateK(curve, hash, d) {
-  assert(Buffer.isBuffer(hash), 'Hash must be a Buffer, not ' + hash)
+  enforceType('Buffer', hash)
+  enforceType(BigInteger, d)
+
+  // sanity check
   assert.equal(hash.length, 32, 'Hash must be 256 bit')
-  assert(d instanceof BigInteger, 'Private key must be a BigInteger')
 
   var x = d.toBuffer(32)
   var k = new Buffer(32)
diff --git a/src/eckey.js b/src/eckey.js
index a3073ec..23cd43d 100644
--- a/src/eckey.js
+++ b/src/eckey.js
@@ -2,6 +2,7 @@ var assert = require('assert')
 var base58check = require('bs58check')
 var crypto = require('crypto')
 var ecdsa = require('./ecdsa')
+var enforceType = require('./types')
 var networks = require('./networks')
 
 var BigInteger = require('bigi')
@@ -46,7 +47,8 @@ ECKey.makeRandom = function(compressed, rng) {
   rng = rng || crypto.randomBytes
 
   var buffer = rng(32)
-  assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer)
+  enforceType('Buffer', buffer)
+  assert.equal(buffer.length, 32, 'Expected 256-bit Buffer from RNG')
 
   var d = BigInteger.fromBuffer(buffer)
   d = d.mod(curve.n)
diff --git a/src/ecpubkey.js b/src/ecpubkey.js
index 3716729..840fa50 100644
--- a/src/ecpubkey.js
+++ b/src/ecpubkey.js
@@ -1,6 +1,6 @@
-var assert = require('assert')
 var crypto = require('./crypto')
 var ecdsa = require('./ecdsa')
+var enforceType = require('./types')
 var networks = require('./networks')
 
 var Address = require('./address')
@@ -9,10 +9,10 @@ var ecurve = require('ecurve')
 var curve = ecurve.getCurveByName('secp256k1')
 
 function ECPubKey(Q, compressed) {
-  assert(Q instanceof ecurve.Point, 'Expected Point, got ' + Q)
+  if (compressed === undefined) compressed = true
 
-  if (compressed == undefined) compressed = true
-  assert.strictEqual(typeof compressed, 'boolean', 'Expected boolean, got ' + compressed)
+  enforceType(ecurve.Point, Q)
+  enforceType('Boolean', compressed)
 
   this.compressed = compressed
   this.Q = Q
diff --git a/src/ecsignature.js b/src/ecsignature.js
index bcd59d5..7b71c61 100644
--- a/src/ecsignature.js
+++ b/src/ecsignature.js
@@ -1,9 +1,12 @@
 var assert = require('assert')
+var enforceType = require('./types')
+
 var BigInteger = require('bigi')
 
 function ECSignature(r, s) {
-  assert(r instanceof BigInteger, 'Expected BigInteger, got ' + r)
-  assert(s instanceof BigInteger, 'Expected BigInteger, got ' + s)
+  enforceType(BigInteger, r)
+  enforceType(BigInteger, s)
+
   this.r = r
   this.s = s
 }
diff --git a/src/hdnode.js b/src/hdnode.js
index e14ad44..018b913 100644
--- a/src/hdnode.js
+++ b/src/hdnode.js
@@ -1,6 +1,7 @@
 var assert = require('assert')
 var base58check = require('bs58check')
 var crypto = require('./crypto')
+var enforceType = require('./types')
 var networks = require('./networks')
 
 var BigInteger = require('bigi')
@@ -30,7 +31,8 @@ function findBIP32ParamsByVersion(version) {
 function HDNode(K, chainCode, network) {
   network = network || networks.bitcoin
 
-  assert(Buffer.isBuffer(chainCode), 'Expected Buffer, got ' + chainCode)
+  enforceType('Buffer', chainCode)
+
   assert.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length)
   assert(network.bip32, 'Unknown BIP32 constants for network')
 
@@ -52,7 +54,8 @@ HDNode.HIGHEST_BIT = 0x80000000
 HDNode.LENGTH = 78
 
 HDNode.fromSeedBuffer = function(seed, network) {
-  assert(Buffer.isBuffer(seed), 'Expected Buffer, got ' + seed)
+  enforceType('Buffer', seed)
+
   assert(seed.length >= 16, 'Seed should be at least 128 bits')
   assert(seed.length <= 64, 'Seed should be at most 512 bits')
 
diff --git a/src/script.js b/src/script.js
index 1556598..d3afc70 100644
--- a/src/script.js
+++ b/src/script.js
@@ -1,11 +1,12 @@
 var assert = require('assert')
 var bufferutils = require('./bufferutils')
 var crypto = require('./crypto')
+var enforceType = require('./types')
 var opcodes = require('./opcodes')
 
 function Script(buffer, chunks) {
-  assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer)
-  assert(Array.isArray(chunks), 'Expected Array, got ' + chunks)
+  enforceType('Buffer', buffer)
+  enforceType('Array', chunks)
 
   this.buffer = buffer
   this.chunks = chunks
@@ -55,7 +56,7 @@ Script.fromBuffer = function(buffer) {
 }
 
 Script.fromChunks = function(chunks) {
-  assert(Array.isArray(chunks), 'Expected Array, got ' + chunks)
+  enforceType('Array', chunks)
 
   var bufferSize = chunks.reduce(function(accum, chunk) {
     if (Buffer.isBuffer(chunk)) {
diff --git a/src/scripts.js b/src/scripts.js
index 88e3e61..911fd1c 100644
--- a/src/scripts.js
+++ b/src/scripts.js
@@ -1,4 +1,5 @@
 var assert = require('assert')
+var enforceType = require('./types')
 var opcodes = require('./opcodes')
 
 // FIXME: use ECPubKey, currently the circular dependency breaks everything.
@@ -18,7 +19,7 @@ var ECSignature = require('./ecsignature')
 var Script = require('./script')
 
 function classifyOutput(script) {
-  assert(script instanceof Script, 'Expected Script, got ', script)
+  enforceType(Script, script)
 
   if (isPubKeyHashOutput.call(script)) {
     return 'pubkeyhash'
@@ -36,7 +37,7 @@ function classifyOutput(script) {
 }
 
 function classifyInput(script) {
-  assert(script instanceof Script, 'Expected Script, got ', script)
+  enforceType(Script, script)
 
   if (isPubKeyHashInput.call(script)) {
     return 'pubkeyhash'
@@ -171,7 +172,7 @@ function pubKeyOutput(pubKey) {
 
 // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
 function pubKeyHashOutput(hash) {
-  assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash)
+  enforceType('Buffer', hash)
 
   return Script.fromChunks([
     opcodes.OP_DUP,
@@ -184,7 +185,7 @@ function pubKeyHashOutput(hash) {
 
 // OP_HASH160 {scriptHash} OP_EQUAL
 function scriptHashOutput(hash) {
-  assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash)
+  enforceType('Buffer', hash)
 
   return Script.fromChunks([
     opcodes.OP_HASH160,
@@ -195,7 +196,8 @@ function scriptHashOutput(hash) {
 
 // m [pubKeys ...] n OP_CHECKMULTISIG
 function multisigOutput(m, pubKeys) {
-  assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys)
+  enforceType('Array', pubKeys)
+
   assert(pubKeys.length >= m, 'Not enough pubKeys provided')
 
   var pubKeyBuffers = pubKeys.map(function(pubKey) {
@@ -213,14 +215,14 @@ function multisigOutput(m, pubKeys) {
 
 // {signature}
 function pubKeyInput(signature) {
-  assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature)
+  enforceType('Buffer', signature)
 
   return Script.fromChunks([signature])
 }
 
 // {signature} {pubKey}
 function pubKeyHashInput(signature, pubKey) {
-  assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature)
+  enforceType('Buffer', signature)
 
   return Script.fromChunks([signature, pubKey.toBuffer()])
 }
diff --git a/src/transaction.js b/src/transaction.js
index 29e1a43..72ab842 100644
--- a/src/transaction.js
+++ b/src/transaction.js
@@ -1,6 +1,7 @@
 var assert = require('assert')
 var bufferutils = require('./bufferutils')
 var crypto = require('./crypto')
+var enforceType = require('./types')
 var opcodes = require('./opcodes')
 var scripts = require('./scripts')
 
@@ -8,12 +9,6 @@ var Address = require('./address')
 var ECSignature = require('./ecsignature')
 var Script = require('./script')
 
-Transaction.DEFAULT_SEQUENCE = 0xffffffff
-Transaction.SIGHASH_ALL = 0x01
-Transaction.SIGHASH_NONE = 0x02
-Transaction.SIGHASH_SINGLE = 0x03
-Transaction.SIGHASH_ANYONECANPAY = 0x80
-
 function Transaction() {
   this.version = 1
   this.locktime = 0
@@ -21,6 +16,12 @@ function Transaction() {
   this.outs = []
 }
 
+Transaction.DEFAULT_SEQUENCE = 0xffffffff
+Transaction.SIGHASH_ALL = 0x01
+Transaction.SIGHASH_NONE = 0x02
+Transaction.SIGHASH_SINGLE = 0x03
+Transaction.SIGHASH_ANYONECANPAY = 0x80
+
 /**
  * Create a new txin.
  *
@@ -31,26 +32,23 @@ function Transaction() {
  *
  * Note that this method does not sign the created input.
  */
-Transaction.prototype.addInput = function(tx, index, sequence) {
-  if (sequence == undefined) sequence = Transaction.DEFAULT_SEQUENCE
+Transaction.prototype.addInput = function(hash, index, sequence) {
+  if (sequence === undefined) sequence = Transaction.DEFAULT_SEQUENCE
 
-  var hash
-
-  if (typeof tx === 'string') {
+  if (typeof hash === 'string') {
     // TxId hex is big-endian, we need little-endian
-    hash = bufferutils.reverse(new Buffer(tx, 'hex'))
+    hash = bufferutils.reverse(new Buffer(hash, 'hex'))
 
-  } else if (tx instanceof Transaction) {
-    hash = tx.getHash()
+  } else if (hash instanceof Transaction) {
+    hash = hash.getHash()
 
-  } else {
-    hash = tx
   }
 
-  assert(Buffer.isBuffer(hash), 'Expected Transaction, txId or txHash, got ' + tx)
+  enforceType('Buffer', hash)
+  enforceType('Number', index)
+  enforceType('Number', sequence)
+
   assert.equal(hash.length, 32, 'Expected hash length of 32, got ' + hash.length)
-  assert(isFinite(index), 'Expected number index, got ' + index)
-  assert(isFinite(sequence), 'Expected number sequence, got ' + sequence)
 
   // Add the input and return the input's index
   return (this.ins.push({
@@ -81,8 +79,8 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) {
     scriptPubKey = scriptPubKey.toOutputScript()
   }
 
-  assert(scriptPubKey instanceof Script, 'Expected Address or Script, got ' + scriptPubKey)
-  assert(isFinite(value), 'Expected number value, got ' + value)
+  enforceType(Script, scriptPubKey)
+  enforceType('Number', value)
 
   // Add the output and return the output's index
   return (this.outs.push({
@@ -172,9 +170,12 @@ Transaction.prototype.hashForSignature = function(inIndex, prevOutScript, hashTy
     prevOutScript = tmp
   }
 
+  enforceType('Number', inIndex)
+  enforceType(Script, prevOutScript)
+  enforceType('Number', hashType)
+
   assert(inIndex >= 0, 'Invalid vin index')
   assert(inIndex < this.ins.length, 'Invalid vin index')
-  assert(prevOutScript instanceof Script, 'Invalid Script object')
 
   var txTmp = this.clone()
   var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR)
diff --git a/src/types.js b/src/types.js
new file mode 100644
index 0000000..dfcde9f
--- /dev/null
+++ b/src/types.js
@@ -0,0 +1,38 @@
+module.exports = function enforce(type, value) {
+  switch (type) {
+    // http://jsperf.com/array-typecheck-2
+    case 'Array': {
+      if (value != null && value.constructor === Array) return
+      break
+    }
+
+    // http://jsperf.com/boolean-typecheck
+    case 'Boolean': {
+      if (typeof value === 'boolean') return
+      break
+    }
+
+    case 'Buffer': {
+      if (Buffer.isBuffer(value)) return
+      break
+    }
+
+    // http://jsperf.com/number-constructor-v-isnan
+    case 'Number': {
+      if (typeof value === 'number') return
+      break
+    }
+
+    // http://jsperf.com/string-typecheck-2
+    case 'String': {
+      if (value != null && value.constructor === String) return
+      break
+    }
+
+    default: {
+      if (value instanceof type) return
+    }
+  }
+
+  throw new TypeError('Expected ' + (type.name || type) + ', got ' + value)
+}

From deaf06b3505c0f513ddb5612de72dfa956a287e3 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sun, 5 Oct 2014 15:43:14 +1100
Subject: [PATCH 2/5] Wallet: use enforceType where applicable

---
 src/wallet.js | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/wallet.js b/src/wallet.js
index 593912e..799bc81 100644
--- a/src/wallet.js
+++ b/src/wallet.js
@@ -1,6 +1,7 @@
 var assert = require('assert')
 var bufferutils = require('./bufferutils')
 var crypto = require('crypto')
+var enforceType = require('./types')
 var networks = require('./networks')
 
 var Address = require('./address')
@@ -300,15 +301,17 @@ Wallet.prototype.setUnspentOutputs = function(unspents) {
       index = unspent.outputIndex
     }
 
-    assert.equal(typeof txId, 'string', 'Expected txId, got ' + txId)
+    enforceType('String', txId)
+    enforceType('Number', index)
+    enforceType('Number', unspent.value)
+
     assert.equal(txId.length, 64, 'Expected valid txId, got ' + txId)
     assert.doesNotThrow(function() { Address.fromBase58Check(unspent.address) }, 'Expected Base58 Address, got ' + unspent.address)
-    assert(isFinite(index), 'Expected number index, got ' + index)
-    assert.equal(typeof unspent.value, 'number', 'Expected number value, got ' + unspent.value)
+    assert(isFinite(index), 'Expected finite index, got ' + index)
 
     // FIXME: remove branch in 2.0.0
     if (unspent.confirmations !== undefined) {
-      assert.equal(typeof unspent.confirmations, 'number', 'Expected number confirmations, got ' + unspent.confirmations)
+      enforceType('Number', unspent.confirmations)
     }
 
     var txHash = bufferutils.reverse(new Buffer(txId, 'hex'))

From 0c380a063ab44f161d73a0ab6a9a7e4caaad8bbb Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Tue, 7 Oct 2014 16:49:20 +1100
Subject: [PATCH 3/5] tests: add tests for types

---
 test/types.js | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
 create mode 100644 test/types.js

diff --git a/test/types.js b/test/types.js
new file mode 100644
index 0000000..4b6c123
--- /dev/null
+++ b/test/types.js
@@ -0,0 +1,28 @@
+var assert = require('assert')
+var enforceType = require('../src/types')
+
+function CustomType() {}
+
+var types = ['Array', 'Boolean', 'Buffer', 'Number', 'String', CustomType]
+var values = [[], true, new Buffer(1), 1234, 'foobar', new CustomType()]
+
+describe('enforceType', function() {
+  types.forEach(function(type, i) {
+    describe(type, function() {
+      values.forEach(function(value, j) {
+        if (j === i) {
+          it('passes for ' + types[j], function() {
+            enforceType(type, value)
+          })
+
+        } else {
+          it('fails for ' + types[j], function() {
+            assert.throws(function() {
+              enforceType(type, value)
+            }, new RegExp('Expected ' + (type.name || type) + ', got '))
+          })
+        }
+      })
+    })
+  })
+})

From 01a96e887c9bb0de172d23135d2cf3644ab9e9fb Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Tue, 7 Oct 2014 16:50:37 +1100
Subject: [PATCH 4/5] types: use the idiomatic equivalents

---
 src/types.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/types.js b/src/types.js
index dfcde9f..257156b 100644
--- a/src/types.js
+++ b/src/types.js
@@ -2,7 +2,7 @@ module.exports = function enforce(type, value) {
   switch (type) {
     // http://jsperf.com/array-typecheck-2
     case 'Array': {
-      if (value != null && value.constructor === Array) return
+      if (Array.isArray(value)) return
       break
     }
 
@@ -25,7 +25,7 @@ module.exports = function enforce(type, value) {
 
     // http://jsperf.com/string-typecheck-2
     case 'String': {
-      if (value != null && value.constructor === String) return
+      if (typeof value === 'string') return
       break
     }
 

From 967e724b479b37db1b9656d18b300344628c3cbe Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Tue, 7 Oct 2014 16:57:49 +1100
Subject: [PATCH 5/5] types: remove JSPerf references

---
 src/types.js | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/types.js b/src/types.js
index 257156b..ddbf13d 100644
--- a/src/types.js
+++ b/src/types.js
@@ -1,12 +1,10 @@
 module.exports = function enforce(type, value) {
   switch (type) {
-    // http://jsperf.com/array-typecheck-2
     case 'Array': {
       if (Array.isArray(value)) return
       break
     }
 
-    // http://jsperf.com/boolean-typecheck
     case 'Boolean': {
       if (typeof value === 'boolean') return
       break
@@ -17,13 +15,11 @@ module.exports = function enforce(type, value) {
       break
     }
 
-    // http://jsperf.com/number-constructor-v-isnan
     case 'Number': {
       if (typeof value === 'number') return
       break
     }
 
-    // http://jsperf.com/string-typecheck-2
     case 'String': {
       if (typeof value === 'string') return
       break