Browse Source

types: enforce consistent type checking

hk-custom-address
Daniel Cousens 10 years ago
parent
commit
35542e115d
  1. 4
      src/address.js
  2. 7
      src/ecdsa.js
  3. 4
      src/eckey.js
  4. 8
      src/ecpubkey.js
  5. 7
      src/ecsignature.js
  6. 7
      src/hdnode.js
  7. 7
      src/script.js
  8. 16
      src/scripts.js
  9. 45
      src/transaction.js
  10. 38
      src/types.js

4
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')

7
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)

4
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)

8
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

7
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
}

7
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')

7
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)) {

16
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()])
}

45
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)

38
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)
}
Loading…
Cancel
Save