validation: use stricter validation for ints.

This commit is contained in:
Christopher Jeffrey 2017-08-06 14:42:10 -07:00
parent d8aea55f97
commit 4ce070fad4
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
35 changed files with 208 additions and 180 deletions

View File

@ -1,5 +1,6 @@
'use strict'; 'use strict';
const assert = require('assert');
const net = require('net'); const net = require('net');
const EventEmitter = require('events').EventEmitter; const EventEmitter = require('events').EventEmitter;
const IOServer = require('socket.io'); const IOServer = require('socket.io');
@ -26,27 +27,32 @@ function WSProxy(options) {
this.options = options; this.options = options;
this.target = options.target || TARGET; this.target = options.target || TARGET;
this.pow = options.pow === true; this.pow = options.pow === true;
this.ports = options.ports || []; this.ports = new Set();
this.io = new IOServer(); this.io = new IOServer();
this.sockets = new WeakMap(); this.sockets = new WeakMap();
this._init(); if (options.ports) {
for (const port of options.ports)
this.ports.add(port);
}
this.init();
} }
util.inherits(WSProxy, EventEmitter); util.inherits(WSProxy, EventEmitter);
WSProxy.prototype._init = function _init() { WSProxy.prototype.init = function init() {
this.io.on('error', (err) => { this.io.on('error', (err) => {
this.emit('error', err); this.emit('error', err);
}); });
this.io.on('connection', (ws) => { this.io.on('connection', (ws) => {
this._handleSocket(ws); this.handleSocket(ws);
}); });
}; };
WSProxy.prototype._handleSocket = function _handleSocket(ws) { WSProxy.prototype.handleSocket = function handleSocket(ws) {
let state = new SocketState(this, ws); const state = new SocketState(this, ws);
// Use a weak map to avoid // Use a weak map to avoid
// mutating the websocket object. // mutating the websocket object.
@ -59,20 +65,20 @@ WSProxy.prototype._handleSocket = function _handleSocket(ws) {
}); });
ws.on('tcp connect', (port, host, nonce) => { ws.on('tcp connect', (port, host, nonce) => {
this._handleConnect(ws, port, host, nonce); this.handleConnect(ws, port, host, nonce);
}); });
}; };
WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce) { WSProxy.prototype.handleConnect = function handleConnect(ws, port, host, nonce) {
let state = this.sockets.get(ws); const state = this.sockets.get(ws);
let socket, pow, raw; assert(state);
if (state.socket) { if (state.socket) {
this.log('Client is trying to reconnect (%s).', state.host); this.log('Client is trying to reconnect (%s).', state.host);
return; return;
} }
if (!util.isNumber(port) if (!util.isU16(port)
|| typeof host !== 'string' || typeof host !== 'string'
|| host.length === 0) { || host.length === 0) {
this.log('Client gave bad arguments (%s).', state.host); this.log('Client gave bad arguments (%s).', state.host);
@ -82,19 +88,20 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
} }
if (this.pow) { if (this.pow) {
if (!util.isNumber(nonce)) { if (!util.isU32(nonce)) {
this.log('Client did not solve proof of work (%s).', state.host); this.log('Client did not solve proof of work (%s).', state.host);
ws.emit('tcp close'); ws.emit('tcp close');
ws.disconnect(); ws.disconnect();
return; return;
} }
pow = new BufferWriter(); const bw = new BufferWriter();
pow.writeU32(nonce); bw.writeU32(nonce);
pow.writeBytes(state.snonce); bw.writeBytes(state.snonce);
pow.writeU32(port); bw.writeU32(port);
pow.writeString(host, 'ascii'); bw.writeString(host, 'ascii');
pow = pow.render();
const pow = bw.render();
if (digest.hash256(pow).compare(this.target) > 0) { if (digest.hash256(pow).compare(this.target) > 0) {
this.log('Client did not solve proof of work (%s).', state.host); this.log('Client did not solve proof of work (%s).', state.host);
@ -104,9 +111,10 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
} }
} }
let raw, addr;
try { try {
raw = IP.toBuffer(host); raw = IP.toBuffer(host);
host = IP.toString(raw); addr = IP.toString(raw);
} catch (e) { } catch (e) {
this.log('Client gave a bad host: %s (%s).', host, state.host); this.log('Client gave a bad host: %s (%s).', host, state.host);
ws.emit('tcp error', { ws.emit('tcp error', {
@ -120,7 +128,7 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
if (!IP.isRoutable(raw) || IP.isOnion(raw)) { if (!IP.isRoutable(raw) || IP.isOnion(raw)) {
this.log( this.log(
'Client is trying to connect to a bad ip: %s (%s).', 'Client is trying to connect to a bad ip: %s (%s).',
host, state.host); addr, state.host);
ws.emit('tcp error', { ws.emit('tcp error', {
message: 'ENETUNREACH', message: 'ENETUNREACH',
code: 'ENETUNREACH' code: 'ENETUNREACH'
@ -129,7 +137,7 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
return; return;
} }
if (this.ports.indexOf(port) === -1) { if (!this.ports.has(port)) {
this.log('Client is connecting to non-whitelist port (%s).', state.host); this.log('Client is connecting to non-whitelist port (%s).', state.host);
ws.emit('tcp error', { ws.emit('tcp error', {
message: 'ENETUNREACH', message: 'ENETUNREACH',
@ -139,8 +147,9 @@ WSProxy.prototype._handleConnect = function _handleConnect(ws, port, host, nonce
return; return;
} }
let socket;
try { try {
socket = state.connect(port, host); socket = state.connect(port, addr);
this.log('Connecting to %s (%s).', state.remoteHost, state.host); this.log('Connecting to %s (%s).', state.remoteHost, state.host);
} catch (e) { } catch (e) {
this.log(e.message); this.log(e.message);

View File

@ -64,12 +64,12 @@ PaymentDetails.prototype.fromOptions = function fromOptions(options) {
} }
if (options.time != null) { if (options.time != null) {
assert(util.isNumber(options.time)); assert(util.isInt(options.time));
this.time = options.time; this.time = options.time;
} }
if (options.expires != null) { if (options.expires != null) {
assert(util.isNumber(options.expires)); assert(util.isInt(options.expires));
this.expires = options.expires; this.expires = options.expires;
} }

View File

@ -50,7 +50,7 @@ function PaymentRequest(options) {
PaymentRequest.prototype.fromOptions = function fromOptions(options) { PaymentRequest.prototype.fromOptions = function fromOptions(options) {
if (options.version != null) { if (options.version != null) {
assert(util.isNumber(options.version)); assert(util.isInt(options.version));
this.version = options.version; this.version = options.version;
} }

View File

@ -2455,12 +2455,12 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) {
} }
if (options.maxFiles != null) { if (options.maxFiles != null) {
assert(util.isNumber(options.maxFiles)); assert(util.isU32(options.maxFiles));
this.maxFiles = options.maxFiles; this.maxFiles = options.maxFiles;
} }
if (options.cacheSize != null) { if (options.cacheSize != null) {
assert(util.isNumber(options.cacheSize)); assert(util.isU64(options.cacheSize));
this.cacheSize = options.cacheSize; this.cacheSize = options.cacheSize;
} }
@ -2500,17 +2500,17 @@ ChainOptions.prototype.fromOptions = function fromOptions(options) {
} }
if (options.coinCache != null) { if (options.coinCache != null) {
assert(util.isNumber(options.coinCache)); assert(util.isU64(options.coinCache));
this.coinCache = options.coinCache; this.coinCache = options.coinCache;
} }
if (options.entryCache != null) { if (options.entryCache != null) {
assert(util.isNumber(options.entryCache)); assert(util.isU32(options.entryCache));
this.entryCache = options.entryCache; this.entryCache = options.entryCache;
} }
if (options.maxOrphans != null) { if (options.maxOrphans != null) {
assert(util.isNumber(options.maxOrphans)); assert(util.isU32(options.maxOrphans));
this.maxOrphans = options.maxOrphans; this.maxOrphans = options.maxOrphans;
} }

View File

@ -56,7 +56,7 @@ function ChainEntry(chain, options, prev) {
this.time = 0; this.time = 0;
this.bits = 0; this.bits = 0;
this.nonce = 0; this.nonce = 0;
this.height = -1; this.height = 0;
this.chainwork = ZERO; this.chainwork = ZERO;
if (options) if (options)
@ -88,12 +88,13 @@ ChainEntry.MEDIAN_TIMESPAN = 11;
ChainEntry.prototype.fromOptions = function fromOptions(options, prev) { ChainEntry.prototype.fromOptions = function fromOptions(options, prev) {
assert(options, 'Block data is required.'); assert(options, 'Block data is required.');
assert(typeof options.hash === 'string'); assert(typeof options.hash === 'string');
assert(util.isNumber(options.version)); assert(util.isU32(options.version));
assert(typeof options.prevBlock === 'string'); assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string'); assert(typeof options.merkleRoot === 'string');
assert(util.isNumber(options.time)); assert(util.isU32(options.time));
assert(util.isNumber(options.bits)); assert(util.isU32(options.bits));
assert(util.isNumber(options.nonce)); assert(util.isU32(options.nonce));
assert(util.isU32(options.height));
assert(!options.chainwork || BN.isBN(options.chainwork)); assert(!options.chainwork || BN.isBN(options.chainwork));
this.hash = options.hash; this.hash = options.hash;
@ -104,7 +105,7 @@ ChainEntry.prototype.fromOptions = function fromOptions(options, prev) {
this.bits = options.bits; this.bits = options.bits;
this.nonce = options.nonce; this.nonce = options.nonce;
this.height = options.height; this.height = options.height;
this.chainwork = options.chainwork; this.chainwork = options.chainwork || ZERO;
if (!this.chainwork) if (!this.chainwork)
this.chainwork = this.getChainwork(prev); this.chainwork = this.getChainwork(prev);

View File

@ -371,10 +371,8 @@ Amount.encode = function encode(value, exp, num) {
*/ */
Amount.decode = function decode(str, exp, num) { Amount.decode = function decode(str, exp, num) {
if (num && typeof str === 'number') { if (num && typeof str === 'number')
assert(util.isNumber(str), 'Non-BTC value for conversion.');
str = str.toString(10); str = str.toString(10);
}
return util.fromFixed(str, exp); return util.fromFixed(str, exp);
}; };

View File

@ -77,7 +77,7 @@ Mnemonic.prototype.fromOptions = function fromOptions(options) {
options = { phrase: options }; options = { phrase: options };
if (options.bits != null) { if (options.bits != null) {
assert(util.isNumber(options.bits)); assert(util.isU16(options.bits));
assert(options.bits >= common.MIN_ENTROPY); assert(options.bits >= common.MIN_ENTROPY);
assert(options.bits <= common.MAX_ENTROPY); assert(options.bits <= common.MAX_ENTROPY);
assert(options.bits % 32 === 0); assert(options.bits % 32 === 0);
@ -377,7 +377,7 @@ Mnemonic.prototype.toJSON = function toJSON() {
*/ */
Mnemonic.prototype.fromJSON = function fromJSON(json) { Mnemonic.prototype.fromJSON = function fromJSON(json) {
assert(util.isNumber(json.bits)); assert(util.isU16(json.bits));
assert(typeof json.language === 'string'); assert(typeof json.language === 'string');
assert(typeof json.entropy === 'string'); assert(typeof json.entropy === 'string');
assert(typeof json.phrase === 'string'); assert(typeof json.phrase === 'string');

View File

@ -70,10 +70,9 @@ function HDPrivateKey(options) {
HDPrivateKey.prototype.fromOptions = function fromOptions(options) { HDPrivateKey.prototype.fromOptions = function fromOptions(options) {
assert(options, 'No options for HD private key.'); assert(options, 'No options for HD private key.');
assert(util.isNumber(options.depth)); assert(util.isU8(options.depth));
assert(options.depth >= 0 && options.depth <= 0xff);
assert(Buffer.isBuffer(options.parentFingerPrint)); assert(Buffer.isBuffer(options.parentFingerPrint));
assert(util.isNumber(options.childIndex)); assert(util.isU32(options.childIndex));
assert(Buffer.isBuffer(options.chainCode)); assert(Buffer.isBuffer(options.chainCode));
assert(Buffer.isBuffer(options.privateKey)); assert(Buffer.isBuffer(options.privateKey));
@ -258,7 +257,7 @@ HDPrivateKey.prototype.getID = function getID(index) {
*/ */
HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(account, bip48) { HDPrivateKey.prototype.deriveBIP44 = function deriveBIP44(account, bip48) {
assert(util.isNumber(account), 'Account index must be a number.'); assert(util.isU32(account), 'Account index must be a number.');
assert(this.isMaster(), 'Cannot derive account index.'); assert(this.isMaster(), 'Cannot derive account index.');
return this return this
.derive(bip48 ? 48 : 44, true) .derive(bip48 ? 48 : 44, true)

View File

@ -64,10 +64,9 @@ function HDPublicKey(options) {
HDPublicKey.prototype.fromOptions = function fromOptions(options) { HDPublicKey.prototype.fromOptions = function fromOptions(options) {
assert(options, 'No options for HDPublicKey'); assert(options, 'No options for HDPublicKey');
assert(util.isNumber(options.depth)); assert(util.isU8(options.depth));
assert(options.depth >= 0 && options.depth <= 0xff);
assert(Buffer.isBuffer(options.parentFingerPrint)); assert(Buffer.isBuffer(options.parentFingerPrint));
assert(util.isNumber(options.childIndex)); assert(util.isU32(options.childIndex));
assert(Buffer.isBuffer(options.chainCode)); assert(Buffer.isBuffer(options.chainCode));
assert(Buffer.isBuffer(options.publicKey)); assert(Buffer.isBuffer(options.publicKey));

View File

@ -427,7 +427,7 @@ MinerOptions.prototype.fromOptions = function fromOptions(options) {
} }
if (options.version != null) { if (options.version != null) {
assert(util.isNumber(options.version)); assert(util.isInt(options.version));
this.version = options.version; this.version = options.version;
} }
@ -461,41 +461,41 @@ MinerOptions.prototype.fromOptions = function fromOptions(options) {
} }
if (options.minWeight != null) { if (options.minWeight != null) {
assert(util.isNumber(options.minWeight)); assert(util.isU32(options.minWeight));
this.minWeight = options.minWeight; this.minWeight = options.minWeight;
} }
if (options.maxWeight != null) { if (options.maxWeight != null) {
assert(util.isNumber(options.maxWeight)); assert(util.isU32(options.maxWeight));
assert(options.maxWeight <= consensus.MAX_BLOCK_WEIGHT, assert(options.maxWeight <= consensus.MAX_BLOCK_WEIGHT,
'Max weight must be below MAX_BLOCK_WEIGHT'); 'Max weight must be below MAX_BLOCK_WEIGHT');
this.maxWeight = options.maxWeight; this.maxWeight = options.maxWeight;
} }
if (options.maxSigops != null) { if (options.maxSigops != null) {
assert(util.isNumber(options.maxSigops)); assert(util.isU32(options.maxSigops));
assert(options.maxSigops <= consensus.MAX_BLOCK_SIGOPS_COST, assert(options.maxSigops <= consensus.MAX_BLOCK_SIGOPS_COST,
'Max sigops must be below MAX_BLOCK_SIGOPS_COST'); 'Max sigops must be below MAX_BLOCK_SIGOPS_COST');
this.maxSigops = options.maxSigops; this.maxSigops = options.maxSigops;
} }
if (options.priorityWeight != null) { if (options.priorityWeight != null) {
assert(util.isNumber(options.priorityWeight)); assert(util.isU32(options.priorityWeight));
this.priorityWeight = options.priorityWeight; this.priorityWeight = options.priorityWeight;
} }
if (options.priorityThreshold != null) { if (options.priorityThreshold != null) {
assert(util.isNumber(options.priorityThreshold)); assert(util.isU32(options.priorityThreshold));
this.priorityThreshold = options.priorityThreshold; this.priorityThreshold = options.priorityThreshold;
} }
if (options.reservedWeight != null) { if (options.reservedWeight != null) {
assert(util.isNumber(options.reservedWeight)); assert(util.isU32(options.reservedWeight));
this.reservedWeight = options.reservedWeight; this.reservedWeight = options.reservedWeight;
} }
if (options.reservedSigops != null) { if (options.reservedSigops != null) {
assert(util.isNumber(options.reservedSigops)); assert(util.isU32(options.reservedSigops));
this.reservedSigops = options.reservedSigops; this.reservedSigops = options.reservedSigops;
} }

View File

@ -1374,13 +1374,13 @@ HostEntry.prototype.fromJSON = function fromJSON(json, network) {
assert(typeof json.services === 'string'); assert(typeof json.services === 'string');
assert(json.services.length > 0); assert(json.services.length > 0);
assert(json.services.length <= 32); assert(json.services.length <= 32);
this.addr.services = parseInt(json.services, 2); const services = parseInt(json.services, 2);
assert(util.isU32(this.addr.services)); assert(util.isU32(services));
this.addr.services = services;
} }
if (json.time != null) { if (json.time != null) {
assert(util.isNumber(json.time)); assert(util.isU64(json.time));
assert(json.time >= 0);
this.addr.time = json.time; this.addr.time = json.time;
} }
@ -1390,20 +1390,17 @@ HostEntry.prototype.fromJSON = function fromJSON(json, network) {
} }
if (json.attempts != null) { if (json.attempts != null) {
assert(util.isNumber(json.attempts)); assert(util.isU64(json.attempts));
assert(json.attempts >= 0);
this.attempts = json.attempts; this.attempts = json.attempts;
} }
if (json.lastSuccess != null) { if (json.lastSuccess != null) {
assert(util.isNumber(json.lastSuccess)); assert(util.isU64(json.lastSuccess));
assert(json.lastSuccess >= 0);
this.lastSuccess = json.lastSuccess; this.lastSuccess = json.lastSuccess;
} }
if (json.lastAttempt != null) { if (json.lastAttempt != null) {
assert(util.isNumber(json.lastAttempt)); assert(util.isU64(json.lastAttempt));
assert(json.lastAttempt >= 0);
this.lastAttempt = json.lastAttempt; this.lastAttempt = json.lastAttempt;
} }

View File

@ -202,9 +202,14 @@ Config.prototype.get = function get(key, fallback) {
if (typeof key === 'number') { if (typeof key === 'number') {
assert(key >= 0, 'Index must be positive.'); assert(key >= 0, 'Index must be positive.');
if (key >= this.argv.length) if (key >= this.argv.length)
return fallback; return fallback;
return this.argv[key];
if (this.argv[key] != null)
return this.argv[key];
return fallback;
} }
assert(typeof key === 'string', 'Key must be a string.'); assert(typeof key === 'string', 'Key must be a string.');
@ -232,6 +237,21 @@ Config.prototype.get = function get(key, fallback) {
return fallback; return fallback;
}; };
/**
* Get a value's type.
* @param {String} key
* @returns {String}
*/
Config.prototype.typeOf = function typeOf(key) {
const value = this.get(key);
if (value === null)
return 'null';
return typeof value;
};
/** /**
* Get a config option (as a string). * Get a config option (as a string).
* @param {String} key * @param {String} key
@ -275,10 +295,7 @@ Config.prototype.int = function int(key, fallback) {
throw new Error(`${fmt(key)} must be an int.`); throw new Error(`${fmt(key)} must be an int.`);
if (!Number.isSafeInteger(value)) if (!Number.isSafeInteger(value))
throw new Error(`${fmt(key)} must be an int (53 bit max).`); throw new Error(`${fmt(key)} must be an int.`);
if (value % 1 !== 0)
throw new Error(`${fmt(key)} must be an int (float).`);
return value; return value;
} }
@ -289,7 +306,7 @@ Config.prototype.int = function int(key, fallback) {
value = parseInt(value, 10); value = parseInt(value, 10);
if (!Number.isSafeInteger(value)) if (!Number.isSafeInteger(value))
throw new Error(`${fmt(key)} must be an int (53 bit max).`); throw new Error(`${fmt(key)} must be an int.`);
return value; return value;
}; };
@ -441,6 +458,15 @@ Config.prototype.bool = function bool(key, fallback) {
if (value === null) if (value === null)
return fallback; return fallback;
// Bitcoin Core compat.
if (typeof value === 'number') {
if (value === 1)
return true;
if (value === 0)
return false;
}
if (typeof value !== 'string') { if (typeof value !== 'string') {
if (typeof value !== 'boolean') if (typeof value !== 'boolean')
throw new Error(`${fmt(key)} must be a boolean.`); throw new Error(`${fmt(key)} must be a boolean.`);
@ -484,7 +510,7 @@ Config.prototype.buf = function buf(key, fallback, enc) {
const data = Buffer.from(value, enc); const data = Buffer.from(value, enc);
if (data.length !== Buffer.byteLength(value, enc)) if (data.length !== Buffer.byteLength(value, enc))
throw new Error(`${fmt(key)} must be a hex string.`); throw new Error(`${fmt(key)} must be a ${enc} string.`);
return data; return data;
}; };
@ -540,7 +566,7 @@ Config.prototype.obj = function obj(key, fallback) {
if (value === null) if (value === null)
return fallback; return fallback;
if (!value || typeof value !== 'object') if (typeof value !== 'object')
throw new Error(`${fmt(key)} must be an object.`); throw new Error(`${fmt(key)} must be an object.`);
return value; return value;

View File

@ -57,12 +57,12 @@ function AbstractBlock() {
AbstractBlock.prototype.parseOptions = function parseOptions(options) { AbstractBlock.prototype.parseOptions = function parseOptions(options) {
assert(options, 'Block data is required.'); assert(options, 'Block data is required.');
assert(util.isNumber(options.version)); assert(util.isU32(options.version));
assert(typeof options.prevBlock === 'string'); assert(typeof options.prevBlock === 'string');
assert(typeof options.merkleRoot === 'string'); assert(typeof options.merkleRoot === 'string');
assert(util.isNumber(options.time)); assert(util.isU32(options.time));
assert(util.isNumber(options.bits)); assert(util.isU32(options.bits));
assert(util.isNumber(options.nonce)); assert(util.isU32(options.nonce));
this.version = options.version; this.version = options.version;
this.prevBlock = options.prevBlock; this.prevBlock = options.prevBlock;
@ -85,12 +85,12 @@ AbstractBlock.prototype.parseOptions = function parseOptions(options) {
AbstractBlock.prototype.parseJSON = function parseJSON(json) { AbstractBlock.prototype.parseJSON = function parseJSON(json) {
assert(json, 'Block data is required.'); assert(json, 'Block data is required.');
assert(util.isNumber(json.version)); assert(util.isU32(json.version));
assert(typeof json.prevBlock === 'string'); assert(typeof json.prevBlock === 'string');
assert(typeof json.merkleRoot === 'string'); assert(typeof json.merkleRoot === 'string');
assert(util.isNumber(json.time)); assert(util.isU32(json.time));
assert(util.isNumber(json.bits)); assert(util.isU32(json.bits));
assert(util.isNumber(json.nonce)); assert(util.isU32(json.nonce));
this.version = json.version; this.version = json.version;
this.prevBlock = util.revHex(json.prevBlock); this.prevBlock = util.revHex(json.prevBlock);

View File

@ -579,8 +579,8 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) {
network = Network.get(network); network = Network.get(network);
assert(Buffer.isBuffer(hash)); assert(Buffer.isBuffer(hash));
assert(util.isNumber(type)); assert(util.isU8(type));
assert(util.isNumber(version)); assert(util.isI8(version));
assert(type >= Address.types.PUBKEYHASH && type <= Address.types.WITNESS, assert(type >= Address.types.PUBKEYHASH && type <= Address.types.WITNESS,
'Not a valid address type.'); 'Not a valid address type.');

View File

@ -1519,37 +1519,36 @@ CoinSelector.prototype.fromOptions = function fromOptions(options) {
} }
if (options.height != null) { if (options.height != null) {
assert(util.isNumber(options.height)); assert(util.isInt(options.height));
assert(options.height >= -1); assert(options.height >= -1);
this.height = options.height; this.height = options.height;
} }
if (options.confirmations != null) { if (options.confirmations != null) {
assert(util.isNumber(options.confirmations)); assert(util.isInt(options.confirmations));
assert(options.confirmations >= -1); assert(options.confirmations >= -1);
this.depth = options.confirmations; this.depth = options.confirmations;
} }
if (options.depth != null) { if (options.depth != null) {
assert(util.isNumber(options.depth)); assert(util.isInt(options.depth));
assert(options.depth >= -1); assert(options.depth >= -1);
this.depth = options.depth; this.depth = options.depth;
} }
if (options.hardFee != null) { if (options.hardFee != null) {
assert(util.isNumber(options.hardFee)); assert(util.isInt(options.hardFee));
assert(options.hardFee >= -1); assert(options.hardFee >= -1);
this.hardFee = options.hardFee; this.hardFee = options.hardFee;
} }
if (options.rate != null) { if (options.rate != null) {
assert(util.isNumber(options.rate)); assert(util.isU64(options.rate));
assert(options.rate >= 0);
this.rate = options.rate; this.rate = options.rate;
} }
if (options.maxFee != null) { if (options.maxFee != null) {
assert(util.isNumber(options.maxFee)); assert(util.isInt(options.maxFee));
assert(options.maxFee >= -1); assert(options.maxFee >= -1);
this.maxFee = options.maxFee; this.maxFee = options.maxFee;
} }

View File

@ -439,10 +439,9 @@ NetAddress.prototype.toJSON = function toJSON() {
*/ */
NetAddress.prototype.fromJSON = function fromJSON(json) { NetAddress.prototype.fromJSON = function fromJSON(json) {
assert(util.isNumber(json.port)); assert(util.isU16(json.port));
assert(json.port >= 0 && json.port <= 0xffff); assert(util.isU32(json.services));
assert(util.isNumber(json.services)); assert(util.isU32(json.time));
assert(util.isNumber(json.time));
this.raw = IP.toBuffer(json.host); this.raw = IP.toBuffer(json.host);
this.host = json.host; this.host = json.host;
this.port = json.port; this.port = json.port;

View File

@ -28,7 +28,7 @@ function TXMeta(options) {
this.height = -1; this.height = -1;
this.block = null; this.block = null;
this.time = 0; this.time = 0;
this.index = 0; this.index = -1;
if (options) if (options)
this.fromOptions(options); this.fromOptions(options);
@ -47,12 +47,12 @@ TXMeta.prototype.fromOptions = function fromOptions(options) {
} }
if (options.mtime != null) { if (options.mtime != null) {
assert(util.isNumber(options.mtime)); assert(util.isU32(options.mtime));
this.mtime = options.mtime; this.mtime = options.mtime;
} }
if (options.height != null) { if (options.height != null) {
assert(util.isNumber(options.height)); assert(util.isInt(options.height));
this.height = options.height; this.height = options.height;
} }
@ -62,12 +62,12 @@ TXMeta.prototype.fromOptions = function fromOptions(options) {
} }
if (options.time != null) { if (options.time != null) {
assert(util.isNumber(options.time)); assert(util.isU32(options.time));
this.time = options.time; this.time = options.time;
} }
if (options.index != null) { if (options.index != null) {
assert(util.isNumber(options.index)); assert(util.isInt(options.index));
this.index = options.index; this.index = options.index;
} }
@ -169,11 +169,11 @@ TXMeta.prototype.getJSON = function getJSON(network, view) {
TXMeta.prototype.fromJSON = function fromJSON(json) { TXMeta.prototype.fromJSON = function fromJSON(json) {
this.tx.fromJSON(json); this.tx.fromJSON(json);
assert(util.isNumber(json.mtime)); assert(util.isU32(json.mtime));
assert(util.isNumber(json.height)); assert(util.isInt(json.height));
assert(!json.block || typeof json.block === 'string'); assert(!json.block || typeof json.block === 'string');
assert(util.isNumber(json.time)); assert(util.isU32(json.time));
assert(util.isNumber(json.index)); assert(util.isInt(json.index));
this.mtime = json.mtime; this.mtime = json.mtime;
this.height = json.height; this.height = json.height;

View File

@ -758,8 +758,10 @@ exports.num = function num(value, minimal, size) {
*/ */
exports.array = function array(value) { exports.array = function array(value) {
if (util.isNumber(value)) if (typeof value === 'number') {
assert(util.isInt(value));
value = new BN(value); value = new BN(value);
}
assert(BN.isBN(value)); assert(BN.isBN(value));

View File

@ -357,7 +357,7 @@ Opcode.fromNumber = function fromNumber(num) {
*/ */
Opcode.fromSmall = function fromSmall(num) { Opcode.fromSmall = function fromSmall(num) {
assert(util.isNumber(num) && num >= 0 && num <= 16); assert(util.isU8(num) && num >= 0 && num <= 16);
return Opcode.fromOp(num === 0 ? 0 : num + 0x50); return Opcode.fromOp(num === 0 ? 0 : num + 0x50);
}; };

View File

@ -29,9 +29,9 @@ function Program(version, data) {
if (!(this instanceof Program)) if (!(this instanceof Program))
return new Program(version, data); return new Program(version, data);
assert(util.isNumber(version)); assert(util.isU8(version));
assert(Buffer.isBuffer(data));
assert(version >= 0 && version <= 16); assert(version >= 0 && version <= 16);
assert(Buffer.isBuffer(data));
assert(data.length >= 2 && data.length <= 40); assert(data.length >= 2 && data.length <= 40);
this.version = version; this.version = version;

View File

@ -1470,7 +1470,7 @@ Script.fromPubkeyhash = function fromPubkeyhash(hash) {
*/ */
Script.prototype.fromMultisig = function fromMultisig(m, n, keys) { Script.prototype.fromMultisig = function fromMultisig(m, n, keys) {
assert(util.isNumber(m) && util.isNumber(n)); assert(util.isU8(m) && util.isU8(n));
assert(Array.isArray(keys)); assert(Array.isArray(keys));
assert(keys.length === n, '`n` keys are required for multisig.'); assert(keys.length === n, '`n` keys are required for multisig.');
assert(m >= 1 && m <= n); assert(m >= 1 && m <= n);
@ -1570,7 +1570,7 @@ Script.fromNulldata = function fromNulldata(flags) {
*/ */
Script.prototype.fromProgram = function fromProgram(version, data) { Script.prototype.fromProgram = function fromProgram(version, data) {
assert(util.isNumber(version) && version >= 0 && version <= 16); assert(util.isU8(version) && version >= 0 && version <= 16);
assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40); assert(Buffer.isBuffer(data) && data.length >= 2 && data.length <= 40);
const op = Opcode.fromSmall(version); const op = Opcode.fromSmall(version);

View File

@ -27,8 +27,7 @@ function SigCache(size) {
if (size == null) if (size == null)
size = 10000; size = 10000;
assert(util.isNumber(size)); assert(util.isU32(size));
assert(size >= 0);
this.size = size; this.size = size;
this.keys = []; this.keys = [];
@ -41,8 +40,7 @@ function SigCache(size) {
*/ */
SigCache.prototype.resize = function resize(size) { SigCache.prototype.resize = function resize(size) {
assert(util.isNumber(size)); assert(util.isU32(size));
assert(size >= 0);
this.size = size; this.size = size;
this.keys.length = 0; this.keys.length = 0;

View File

@ -152,7 +152,7 @@ AsyncEmitter.prototype.removeListener = function removeListener(type, handler) {
AsyncEmitter.prototype.setMaxListeners = function setMaxListeners(max) { AsyncEmitter.prototype.setMaxListeners = function setMaxListeners(max) {
assert(typeof max === 'number', '`max` must be a number.'); assert(typeof max === 'number', '`max` must be a number.');
assert(max >= 0, '`max` must be non-negative.'); assert(max >= 0, '`max` must be non-negative.');
assert(max % 1 === 0, '`max` must be an integer.'); assert(Number.isSafeInteger(max), '`max` must be an integer.');
}; };
/** /**

View File

@ -119,7 +119,7 @@ Bloom.flagsByVal = {
Bloom.prototype.fromOptions = function fromOptions(size, n, tweak, update) { Bloom.prototype.fromOptions = function fromOptions(size, n, tweak, update) {
assert(typeof size === 'number', '`size` must be a number.'); assert(typeof size === 'number', '`size` must be a number.');
assert(size > 0, '`size` must be greater than zero.'); assert(size > 0, '`size` must be greater than zero.');
assert(size % 1 === 0, '`size` must be an integer.'); assert(Number.isSafeInteger(size), '`size` must be an integer.');
size -= size % 8; size -= size % 8;
@ -139,9 +139,9 @@ Bloom.prototype.fromOptions = function fromOptions(size, n, tweak, update) {
assert(size > 0, '`size` must be greater than zero.'); assert(size > 0, '`size` must be greater than zero.');
assert(n > 0, '`n` must be greater than zero.'); assert(n > 0, '`n` must be greater than zero.');
assert(n % 1 === 0, '`n` must be an integer.'); assert(Number.isSafeInteger(n), '`n` must be an integer.');
assert(typeof tweak === 'number', '`tweak` must be a number.'); assert(typeof tweak === 'number', '`tweak` must be a number.');
assert(tweak % 1 === 0, '`tweak` must be an integer.'); assert(Number.isSafeInteger(tweak), '`tweak` must be an integer.');
assert(Bloom.flagsByVal[update], 'Unknown update flag.'); assert(Bloom.flagsByVal[update], 'Unknown update flag.');
this.filter = filter; this.filter = filter;
@ -258,7 +258,7 @@ Bloom.prototype.added = function added(val, enc) {
Bloom.fromRate = function fromRate(items, rate, update) { Bloom.fromRate = function fromRate(items, rate, update) {
assert(typeof items === 'number', '`items` must be a number.'); assert(typeof items === 'number', '`items` must be a number.');
assert(items > 0, '`items` must be greater than zero.'); assert(items > 0, '`items` must be greater than zero.');
assert(items % 1 === 0, '`items` must be an integer.'); assert(Number.isSafeInteger(items), '`items` must be an integer.');
assert(typeof rate === 'number', '`rate` must be a number.'); assert(typeof rate === 'number', '`rate` must be a number.');
assert(rate >= 0 && rate <= 1, '`rate` must be between 0.0 and 1.0.'); assert(rate >= 0 && rate <= 1, '`rate` must be between 0.0 and 1.0.');

View File

@ -12,6 +12,7 @@
*/ */
const BN = require('bn.js'); const BN = require('bn.js');
const util = require('./util');
const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER; const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER;
const encoding = exports; const encoding = exports;
@ -1037,7 +1038,7 @@ encoding.EncodingError = function EncodingError(offset, reason) {
Error.captureStackTrace(this, EncodingError); Error.captureStackTrace(this, EncodingError);
}; };
inherits(encoding.EncodingError, Error); util.inherits(encoding.EncodingError, Error);
/* /*
* Helpers * Helpers
@ -1048,15 +1049,6 @@ function Varint(size, value) {
this.value = value; this.value = value;
} }
function inherits(child, parent) {
child.super_ = parent;
Object.setPrototypeOf(child.prototype, parent.prototype);
Object.defineProperty(child.prototype, 'constructor', {
value: child,
enumerable: false
});
}
function enforce(value, offset, reason) { function enforce(value, offset, reason) {
if (!value) if (!value)
throw new encoding.EncodingError(offset, reason); throw new encoding.EncodingError(offset, reason);

View File

@ -50,7 +50,7 @@ function RollingFilter(items, rate) {
RollingFilter.prototype.fromRate = function fromRate(items, rate) { RollingFilter.prototype.fromRate = function fromRate(items, rate) {
assert(typeof items === 'number', '`items` must be a number.'); assert(typeof items === 'number', '`items` must be a number.');
assert(items > 0, '`items` must be greater than zero.'); assert(items > 0, '`items` must be greater than zero.');
assert(items % 1 === 0, '`items` must be an integer.'); assert(Number.isSafeInteger(items), '`items` must be an integer.');
assert(typeof rate === 'number', '`rate` must be a number.'); assert(typeof rate === 'number', '`rate` must be a number.');
assert(rate >= 0 && rate <= 1, '`rate` must be between 0.0 and 1.0.'); assert(rate >= 0 && rate <= 1, '`rate` must be between 0.0 and 1.0.');

View File

@ -38,7 +38,10 @@ const inspectOptions = {
*/ */
util.isNumber = function isNumber(value) { util.isNumber = function isNumber(value) {
return Number.isSafeInteger(value); return typeof value === 'number'
&& isFinite(value)
&& value >= -Number.MAX_SAFE_INTEGER
&& value <= Number.MAX_SAFE_INTEGER;
}; };
/** /**
@ -48,11 +51,11 @@ util.isNumber = function isNumber(value) {
*/ */
util.isInt = function isInt(value) { util.isInt = function isInt(value) {
return util.isNumber(value) && value % 1 === 0; return Number.isSafeInteger(value);
}; };
/** /**
* Test whether an object is an int. * Test whether an object is a uint.
* @param {Number?} value * @param {Number?} value
* @returns {Boolean} * @returns {Boolean}
*/ */
@ -844,7 +847,7 @@ util.memoryUsage = function memoryUsage() {
util.toFixed = function toFixed(num, exp) { util.toFixed = function toFixed(num, exp) {
assert(typeof num === 'number'); assert(typeof num === 'number');
assert(Number.isSafeInteger(num) && num % 1 === 0, 'Invalid integer value.'); assert(Number.isSafeInteger(num), 'Invalid integer value.');
let sign = ''; let sign = '';

View File

@ -97,6 +97,21 @@ Validator.prototype.get = function get(key, fallback) {
return fallback; return fallback;
}; };
/**
* Get a value's type.
* @param {String} key
* @returns {String}
*/
Validator.prototype.typeOf = function typeOf(key) {
const value = this.get(key);
if (value == null)
return 'null';
return typeof value;
};
/** /**
* Get a value (as a string). * Get a value (as a string).
* @param {String} key * @param {String} key
@ -140,10 +155,7 @@ Validator.prototype.int = function int(key, fallback) {
throw new ValidationError(key, 'int'); throw new ValidationError(key, 'int');
if (!Number.isSafeInteger(value)) if (!Number.isSafeInteger(value))
throw new ValidationError(key, 'int (53 bit max)'); throw new ValidationError(key, 'int');
if (value % 1 !== 0)
throw new ValidationError(key, 'int (float)');
return value; return value;
} }
@ -154,7 +166,7 @@ Validator.prototype.int = function int(key, fallback) {
value = parseInt(value, 10); value = parseInt(value, 10);
if (!Number.isSafeInteger(value)) if (!Number.isSafeInteger(value))
throw new ValidationError(key, 'int (53 bit max)'); throw new ValidationError(key, 'int');
return value; return value;
}; };
@ -492,15 +504,9 @@ Validator.prototype.hash = function hash(key, fallback) {
*/ */
Validator.prototype.numhash = function numhash(key, fallback) { Validator.prototype.numhash = function numhash(key, fallback) {
const value = this.get(key); if (this.typeOf(key) === 'string')
return this.hash(key, fallback);
if (value === null) return this.uint(key, fallback);
return fallback;
if (typeof value === 'string')
return this.hash(key);
return this.uint(key);
}; };
/** /**
@ -519,14 +525,14 @@ Validator.prototype.bool = function bool(key, fallback) {
if (value === null) if (value === null)
return fallback; return fallback;
// Bitcoin Core mixes semantics of truthiness // Bitcoin Core compat.
// amoung rpc methods most "verbose" parameters if (typeof value === 'number') {
// are bools, but getrawtransaction is 1/0. if (value === 1)
if (value === 1) return true;
return true;
if (value === 0) if (value === 0)
return false; return false;
}
if (typeof value !== 'string') { if (typeof value !== 'string') {
if (typeof value !== 'boolean') if (typeof value !== 'boolean')
@ -628,7 +634,7 @@ Validator.prototype.obj = function obj(key, fallback) {
if (value === null) if (value === null)
return fallback; return fallback;
if (!value || typeof value !== 'object') if (typeof value !== 'object')
throw new ValidationError(key, 'object'); throw new ValidationError(key, 'object');
return value; return value;

View File

@ -105,10 +105,10 @@ Account.typesByVal = {
Account.prototype.fromOptions = function fromOptions(options) { Account.prototype.fromOptions = function fromOptions(options) {
assert(options, 'Options are required.'); assert(options, 'Options are required.');
assert(util.isNumber(options.wid)); assert(util.isU32(options.wid));
assert(common.isName(options.id), 'Bad Wallet ID.'); assert(common.isName(options.id), 'Bad Wallet ID.');
assert(HD.isHD(options.accountKey), 'Account key is required.'); assert(HD.isHD(options.accountKey), 'Account key is required.');
assert(util.isNumber(options.accountIndex), 'Account index is required.'); assert(util.isU32(options.accountIndex), 'Account index is required.');
this.wid = options.wid; this.wid = options.wid;
this.id = options.id; this.id = options.id;
@ -145,37 +145,37 @@ Account.prototype.fromOptions = function fromOptions(options) {
} }
if (options.m != null) { if (options.m != null) {
assert(util.isNumber(options.m)); assert(util.isU8(options.m));
this.m = options.m; this.m = options.m;
} }
if (options.n != null) { if (options.n != null) {
assert(util.isNumber(options.n)); assert(util.isU8(options.n));
this.n = options.n; this.n = options.n;
} }
if (options.accountIndex != null) { if (options.accountIndex != null) {
assert(util.isNumber(options.accountIndex)); assert(util.isU32(options.accountIndex));
this.accountIndex = options.accountIndex; this.accountIndex = options.accountIndex;
} }
if (options.receiveDepth != null) { if (options.receiveDepth != null) {
assert(util.isNumber(options.receiveDepth)); assert(util.isU32(options.receiveDepth));
this.receiveDepth = options.receiveDepth; this.receiveDepth = options.receiveDepth;
} }
if (options.changeDepth != null) { if (options.changeDepth != null) {
assert(util.isNumber(options.changeDepth)); assert(util.isU32(options.changeDepth));
this.changeDepth = options.changeDepth; this.changeDepth = options.changeDepth;
} }
if (options.nestedDepth != null) { if (options.nestedDepth != null) {
assert(util.isNumber(options.nestedDepth)); assert(util.isU32(options.nestedDepth));
this.nestedDepth = options.nestedDepth; this.nestedDepth = options.nestedDepth;
} }
if (options.lookahead != null) { if (options.lookahead != null) {
assert(util.isNumber(options.lookahead)); assert(util.isU32(options.lookahead));
assert(options.lookahead >= 0); assert(options.lookahead >= 0);
assert(options.lookahead <= Account.MAX_LOOKAHEAD); assert(options.lookahead <= Account.MAX_LOOKAHEAD);
this.lookahead = options.lookahead; this.lookahead = options.lookahead;

View File

@ -129,22 +129,22 @@ MasterKey.prototype.fromOptions = function fromOptions(options) {
} }
if (options.rounds != null) { if (options.rounds != null) {
assert(util.isNumber(options.rounds)); assert(util.isU32(options.rounds));
this.N = options.rounds; this.N = options.rounds;
} }
if (options.N != null) { if (options.N != null) {
assert(util.isNumber(options.N)); assert(util.isU32(options.N));
this.N = options.N; this.N = options.N;
} }
if (options.r != null) { if (options.r != null) {
assert(util.isNumber(options.r)); assert(util.isU32(options.r));
this.r = options.r; this.r = options.r;
} }
if (options.p != null) { if (options.p != null) {
assert(util.isNumber(options.p)); assert(util.isU32(options.p));
this.p = options.p; this.p = options.p;
} }

View File

@ -124,7 +124,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
this.master.fromKey(key, mnemonic); this.master.fromKey(key, mnemonic);
if (options.wid != null) { if (options.wid != null) {
assert(util.isNumber(options.wid)); assert(util.isU32(options.wid));
this.wid = options.wid; this.wid = options.wid;
} }
@ -144,7 +144,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
} }
if (options.accountDepth != null) { if (options.accountDepth != null) {
assert(util.isNumber(options.accountDepth)); assert(util.isU32(options.accountDepth));
this.accountDepth = options.accountDepth; this.accountDepth = options.accountDepth;
} }
@ -155,7 +155,7 @@ Wallet.prototype.fromOptions = function fromOptions(options) {
} }
if (options.tokenDepth != null) { if (options.tokenDepth != null) {
assert(util.isNumber(options.tokenDepth)); assert(util.isU32(options.tokenDepth));
this.tokenDepth = options.tokenDepth; this.tokenDepth = options.tokenDepth;
} }

View File

@ -2220,12 +2220,12 @@ WalletOptions.prototype.fromOptions = function fromOptions(options) {
} }
if (options.maxFiles != null) { if (options.maxFiles != null) {
assert(util.isNumber(options.maxFiles)); assert(util.isU32(options.maxFiles));
this.maxFiles = options.maxFiles; this.maxFiles = options.maxFiles;
} }
if (options.cacheSize != null) { if (options.cacheSize != null) {
assert(util.isNumber(options.cacheSize)); assert(util.isU64(options.cacheSize));
this.cacheSize = options.cacheSize; this.cacheSize = options.cacheSize;
} }

View File

@ -68,14 +68,14 @@ WorkerPool.prototype.set = function set(options) {
} }
if (options.size != null) { if (options.size != null) {
assert(util.isNumber(options.size)); assert(util.isU32(options.size));
assert(options.size > 0); assert(options.size > 0);
this.size = options.size; this.size = options.size;
} }
if (options.timeout != null) { if (options.timeout != null) {
assert(util.isNumber(options.timeout)); assert(util.isInt(options.timeout));
assert(options.timeout > 0); assert(options.timeout >= -1);
this.timeout = options.timeout; this.timeout = options.timeout;
} }

View File

@ -52,7 +52,7 @@ function Coins(options) {
Coins.prototype.fromOptions = function fromOptions(options) { Coins.prototype.fromOptions = function fromOptions(options) {
if (options.version != null) { if (options.version != null) {
assert(util.isNumber(options.version)); assert(util.isU32(options.version));
this.version = options.version; this.version = options.version;
} }
@ -62,7 +62,7 @@ Coins.prototype.fromOptions = function fromOptions(options) {
} }
if (options.height != null) { if (options.height != null) {
assert(util.isNumber(options.height)); assert(util.isInt(options.height));
this.height = options.height; this.height = options.height;
} }

View File

@ -64,7 +64,7 @@ Coins.prototype.fromOptions = function fromOptions(options) {
} }
if (options.height != null) { if (options.height != null) {
assert(util.isNumber(options.height)); assert(util.isInt(options.height));
this.height = options.height; this.height = options.height;
} }