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 assert = require('assert')
var base58check = require('bs58check') var base58check = require('bs58check')
var enforceType = require('./types')
var networks = require('./networks') var networks = require('./networks')
var scripts = require('./scripts') var scripts = require('./scripts')
@ -13,7 +14,8 @@ function findScriptTypeByVersion(version) {
} }
function Address(hash, 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(hash.length, 20, 'Invalid hash length')
assert.strictEqual(version & 0xff, version, 'Invalid version byte') assert.strictEqual(version & 0xff, version, 'Invalid version byte')

7
src/ecdsa.js

@ -1,14 +1,17 @@
var assert = require('assert') var assert = require('assert')
var crypto = require('./crypto') var crypto = require('./crypto')
var enforceType = require('./types')
var BigInteger = require('bigi') var BigInteger = require('bigi')
var ECSignature = require('./ecsignature') var ECSignature = require('./ecsignature')
// https://tools.ietf.org/html/rfc6979#section-3.2 // https://tools.ietf.org/html/rfc6979#section-3.2
function deterministicGenerateK(curve, hash, d) { 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.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 x = d.toBuffer(32)
var k = new Buffer(32) var k = new Buffer(32)

4
src/eckey.js

@ -2,6 +2,7 @@ var assert = require('assert')
var base58check = require('bs58check') var base58check = require('bs58check')
var crypto = require('crypto') var crypto = require('crypto')
var ecdsa = require('./ecdsa') var ecdsa = require('./ecdsa')
var enforceType = require('./types')
var networks = require('./networks') var networks = require('./networks')
var BigInteger = require('bigi') var BigInteger = require('bigi')
@ -46,7 +47,8 @@ ECKey.makeRandom = function(compressed, rng) {
rng = rng || crypto.randomBytes rng = rng || crypto.randomBytes
var buffer = rng(32) 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) var d = BigInteger.fromBuffer(buffer)
d = d.mod(curve.n) d = d.mod(curve.n)

8
src/ecpubkey.js

@ -1,6 +1,6 @@
var assert = require('assert')
var crypto = require('./crypto') var crypto = require('./crypto')
var ecdsa = require('./ecdsa') var ecdsa = require('./ecdsa')
var enforceType = require('./types')
var networks = require('./networks') var networks = require('./networks')
var Address = require('./address') var Address = require('./address')
@ -9,10 +9,10 @@ var ecurve = require('ecurve')
var curve = ecurve.getCurveByName('secp256k1') var curve = ecurve.getCurveByName('secp256k1')
function ECPubKey(Q, compressed) { function ECPubKey(Q, compressed) {
assert(Q instanceof ecurve.Point, 'Expected Point, got ' + Q) if (compressed === undefined) compressed = true
if (compressed == undefined) compressed = true enforceType(ecurve.Point, Q)
assert.strictEqual(typeof compressed, 'boolean', 'Expected boolean, got ' + compressed) enforceType('Boolean', compressed)
this.compressed = compressed this.compressed = compressed
this.Q = Q this.Q = Q

7
src/ecsignature.js

@ -1,9 +1,12 @@
var assert = require('assert') var assert = require('assert')
var enforceType = require('./types')
var BigInteger = require('bigi') var BigInteger = require('bigi')
function ECSignature(r, s) { function ECSignature(r, s) {
assert(r instanceof BigInteger, 'Expected BigInteger, got ' + r) enforceType(BigInteger, r)
assert(s instanceof BigInteger, 'Expected BigInteger, got ' + s) enforceType(BigInteger, s)
this.r = r this.r = r
this.s = s this.s = s
} }

7
src/hdnode.js

@ -1,6 +1,7 @@
var assert = require('assert') var assert = require('assert')
var base58check = require('bs58check') var base58check = require('bs58check')
var crypto = require('./crypto') var crypto = require('./crypto')
var enforceType = require('./types')
var networks = require('./networks') var networks = require('./networks')
var BigInteger = require('bigi') var BigInteger = require('bigi')
@ -30,7 +31,8 @@ function findBIP32ParamsByVersion(version) {
function HDNode(K, chainCode, network) { function HDNode(K, chainCode, network) {
network = network || networks.bitcoin 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.equal(chainCode.length, 32, 'Expected chainCode length of 32, got ' + chainCode.length)
assert(network.bip32, 'Unknown BIP32 constants for network') assert(network.bip32, 'Unknown BIP32 constants for network')
@ -52,7 +54,8 @@ HDNode.HIGHEST_BIT = 0x80000000
HDNode.LENGTH = 78 HDNode.LENGTH = 78
HDNode.fromSeedBuffer = function(seed, network) { 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 >= 16, 'Seed should be at least 128 bits')
assert(seed.length <= 64, 'Seed should be at most 512 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 assert = require('assert')
var bufferutils = require('./bufferutils') var bufferutils = require('./bufferutils')
var crypto = require('./crypto') var crypto = require('./crypto')
var enforceType = require('./types')
var opcodes = require('./opcodes') var opcodes = require('./opcodes')
function Script(buffer, chunks) { function Script(buffer, chunks) {
assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer) enforceType('Buffer', buffer)
assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) enforceType('Array', chunks)
this.buffer = buffer this.buffer = buffer
this.chunks = chunks this.chunks = chunks
@ -55,7 +56,7 @@ Script.fromBuffer = function(buffer) {
} }
Script.fromChunks = function(chunks) { Script.fromChunks = function(chunks) {
assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) enforceType('Array', chunks)
var bufferSize = chunks.reduce(function(accum, chunk) { var bufferSize = chunks.reduce(function(accum, chunk) {
if (Buffer.isBuffer(chunk)) { if (Buffer.isBuffer(chunk)) {

16
src/scripts.js

@ -1,4 +1,5 @@
var assert = require('assert') var assert = require('assert')
var enforceType = require('./types')
var opcodes = require('./opcodes') var opcodes = require('./opcodes')
// FIXME: use ECPubKey, currently the circular dependency breaks everything. // FIXME: use ECPubKey, currently the circular dependency breaks everything.
@ -18,7 +19,7 @@ var ECSignature = require('./ecsignature')
var Script = require('./script') var Script = require('./script')
function classifyOutput(script) { function classifyOutput(script) {
assert(script instanceof Script, 'Expected Script, got ', script) enforceType(Script, script)
if (isPubKeyHashOutput.call(script)) { if (isPubKeyHashOutput.call(script)) {
return 'pubkeyhash' return 'pubkeyhash'
@ -36,7 +37,7 @@ function classifyOutput(script) {
} }
function classifyInput(script) { function classifyInput(script) {
assert(script instanceof Script, 'Expected Script, got ', script) enforceType(Script, script)
if (isPubKeyHashInput.call(script)) { if (isPubKeyHashInput.call(script)) {
return 'pubkeyhash' return 'pubkeyhash'
@ -171,7 +172,7 @@ function pubKeyOutput(pubKey) {
// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG
function pubKeyHashOutput(hash) { function pubKeyHashOutput(hash) {
assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) enforceType('Buffer', hash)
return Script.fromChunks([ return Script.fromChunks([
opcodes.OP_DUP, opcodes.OP_DUP,
@ -184,7 +185,7 @@ function pubKeyHashOutput(hash) {
// OP_HASH160 {scriptHash} OP_EQUAL // OP_HASH160 {scriptHash} OP_EQUAL
function scriptHashOutput(hash) { function scriptHashOutput(hash) {
assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) enforceType('Buffer', hash)
return Script.fromChunks([ return Script.fromChunks([
opcodes.OP_HASH160, opcodes.OP_HASH160,
@ -195,7 +196,8 @@ function scriptHashOutput(hash) {
// m [pubKeys ...] n OP_CHECKMULTISIG // m [pubKeys ...] n OP_CHECKMULTISIG
function multisigOutput(m, pubKeys) { function multisigOutput(m, pubKeys) {
assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys) enforceType('Array', pubKeys)
assert(pubKeys.length >= m, 'Not enough pubKeys provided') assert(pubKeys.length >= m, 'Not enough pubKeys provided')
var pubKeyBuffers = pubKeys.map(function(pubKey) { var pubKeyBuffers = pubKeys.map(function(pubKey) {
@ -213,14 +215,14 @@ function multisigOutput(m, pubKeys) {
// {signature} // {signature}
function pubKeyInput(signature) { function pubKeyInput(signature) {
assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) enforceType('Buffer', signature)
return Script.fromChunks([signature]) return Script.fromChunks([signature])
} }
// {signature} {pubKey} // {signature} {pubKey}
function pubKeyHashInput(signature, pubKey) { function pubKeyHashInput(signature, pubKey) {
assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) enforceType('Buffer', signature)
return Script.fromChunks([signature, pubKey.toBuffer()]) return Script.fromChunks([signature, pubKey.toBuffer()])
} }

45
src/transaction.js

@ -1,6 +1,7 @@
var assert = require('assert') var assert = require('assert')
var bufferutils = require('./bufferutils') var bufferutils = require('./bufferutils')
var crypto = require('./crypto') var crypto = require('./crypto')
var enforceType = require('./types')
var opcodes = require('./opcodes') var opcodes = require('./opcodes')
var scripts = require('./scripts') var scripts = require('./scripts')
@ -8,12 +9,6 @@ var Address = require('./address')
var ECSignature = require('./ecsignature') var ECSignature = require('./ecsignature')
var Script = require('./script') 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() { function Transaction() {
this.version = 1 this.version = 1
this.locktime = 0 this.locktime = 0
@ -21,6 +16,12 @@ function Transaction() {
this.outs = [] 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. * Create a new txin.
* *
@ -31,26 +32,23 @@ function Transaction() {
* *
* Note that this method does not sign the created input. * Note that this method does not sign the created input.
*/ */
Transaction.prototype.addInput = function(tx, index, sequence) { Transaction.prototype.addInput = function(hash, index, sequence) {
if (sequence == undefined) sequence = Transaction.DEFAULT_SEQUENCE if (sequence === undefined) sequence = Transaction.DEFAULT_SEQUENCE
var hash if (typeof hash === 'string') {
if (typeof tx === 'string') {
// TxId hex is big-endian, we need little-endian // 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) { } else if (hash instanceof Transaction) {
hash = tx.getHash() 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.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 // Add the input and return the input's index
return (this.ins.push({ return (this.ins.push({
@ -81,8 +79,8 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) {
scriptPubKey = scriptPubKey.toOutputScript() scriptPubKey = scriptPubKey.toOutputScript()
} }
assert(scriptPubKey instanceof Script, 'Expected Address or Script, got ' + scriptPubKey) enforceType(Script, scriptPubKey)
assert(isFinite(value), 'Expected number value, got ' + value) enforceType('Number', value)
// Add the output and return the output's index // Add the output and return the output's index
return (this.outs.push({ return (this.outs.push({
@ -172,9 +170,12 @@ Transaction.prototype.hashForSignature = function(inIndex, prevOutScript, hashTy
prevOutScript = tmp prevOutScript = tmp
} }
enforceType('Number', inIndex)
enforceType(Script, prevOutScript)
enforceType('Number', hashType)
assert(inIndex >= 0, 'Invalid vin index') assert(inIndex >= 0, 'Invalid vin index')
assert(inIndex < this.ins.length, 'Invalid vin index') assert(inIndex < this.ins.length, 'Invalid vin index')
assert(prevOutScript instanceof Script, 'Invalid Script object')
var txTmp = this.clone() var txTmp = this.clone()
var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR) 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