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..ddbf13d --- /dev/null +++ b/src/types.js @@ -0,0 +1,34 @@ +module.exports = function enforce(type, value) { + switch (type) { + case 'Array': { + if (Array.isArray(value)) return + break + } + + case 'Boolean': { + if (typeof value === 'boolean') return + break + } + + case 'Buffer': { + if (Buffer.isBuffer(value)) return + break + } + + case 'Number': { + if (typeof value === 'number') return + break + } + + case 'String': { + if (typeof value === 'string') return + break + } + + default: { + if (value instanceof type) return + } + } + + throw new TypeError('Expected ' + (type.name || type) + ', got ' + value) +} 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')) 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 ')) + }) + } + }) + }) + }) +})