config/validator/utils: fix fixed parsing/serialization.
This commit is contained in:
parent
2fea1319d9
commit
faabd36f9e
@ -77,7 +77,7 @@ Amount.prototype.toSatoshis = function toSatoshis(num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.toBits = function toBits(num) {
|
Amount.prototype.toBits = function toBits(num) {
|
||||||
return Amount.serialize(this.value, 2, num);
|
return Amount.encode(this.value, 2, num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,7 +87,7 @@ Amount.prototype.toBits = function toBits(num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.toMBTC = function toMBTC(num) {
|
Amount.prototype.toMBTC = function toMBTC(num) {
|
||||||
return Amount.serialize(this.value, 5, num);
|
return Amount.encode(this.value, 5, num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,7 +97,7 @@ Amount.prototype.toMBTC = function toMBTC(num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.toBTC = function toBTC(num) {
|
Amount.prototype.toBTC = function toBTC(num) {
|
||||||
return Amount.serialize(this.value, 8, num);
|
return Amount.encode(this.value, 8, num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,7 +154,7 @@ Amount.prototype.fromValue = function fromValue(value) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.fromSatoshis = function fromSatoshis(value, num) {
|
Amount.prototype.fromSatoshis = function fromSatoshis(value, num) {
|
||||||
this.value = Amount.parse(value, 0, num);
|
this.value = Amount.decode(value, 0, num);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ Amount.prototype.fromSatoshis = function fromSatoshis(value, num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.fromBits = function fromBits(value, num) {
|
Amount.prototype.fromBits = function fromBits(value, num) {
|
||||||
this.value = Amount.parse(value, 2, num);
|
this.value = Amount.decode(value, 2, num);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ Amount.prototype.fromBits = function fromBits(value, num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.fromMBTC = function fromMBTC(value, num) {
|
Amount.prototype.fromMBTC = function fromMBTC(value, num) {
|
||||||
this.value = Amount.parse(value, 5, num);
|
this.value = Amount.decode(value, 5, num);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -193,7 +193,7 @@ Amount.prototype.fromMBTC = function fromMBTC(value, num) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.prototype.fromBTC = function fromBTC(value, num) {
|
Amount.prototype.fromBTC = function fromBTC(value, num) {
|
||||||
this.value = Amount.parse(value, 8, num);
|
this.value = Amount.decode(value, 8, num);
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -318,10 +318,24 @@ Amount.prototype.inspect = function inspect() {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.btc = function btc(value, num) {
|
Amount.btc = function btc(value, num) {
|
||||||
if (util.isFloat(value))
|
if (typeof value === 'string')
|
||||||
return value;
|
return value;
|
||||||
|
|
||||||
return Amount.serialize(value, 8, num);
|
return Amount.encode(value, 8, num);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely convert a BTC string to satoshis.
|
||||||
|
* @param {String} str - BTC
|
||||||
|
* @returns {Amount} Satoshis.
|
||||||
|
* @throws on parse error
|
||||||
|
*/
|
||||||
|
|
||||||
|
Amount.value = function value(str, num) {
|
||||||
|
if (typeof str === 'number')
|
||||||
|
return str;
|
||||||
|
|
||||||
|
return Amount.decode(str, 8, num);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -334,80 +348,13 @@ Amount.btc = function btc(value, num) {
|
|||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.serialize = function serialize(value, exp, num) {
|
Amount.encode = function encode(value, exp, num) {
|
||||||
assert(util.isInt(value), 'Non-satoshi value for conversion.');
|
const str = util.toFixed(value, exp);
|
||||||
|
|
||||||
let negative = false;
|
|
||||||
|
|
||||||
if (value < 0) {
|
|
||||||
value = -value;
|
|
||||||
negative = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
value = value.toString(10);
|
|
||||||
|
|
||||||
assert(value.length <= 16, 'Number exceeds 2^53-1.');
|
|
||||||
|
|
||||||
while (value.length < exp + 1)
|
|
||||||
value = '0' + value;
|
|
||||||
|
|
||||||
const hi = value.slice(0, -exp);
|
|
||||||
let lo = value.slice(-exp);
|
|
||||||
|
|
||||||
lo = lo.replace(/0+$/, '');
|
|
||||||
|
|
||||||
if (lo.length === 0)
|
|
||||||
lo += '0';
|
|
||||||
|
|
||||||
let result = `${hi}.${lo}`;
|
|
||||||
|
|
||||||
if (negative)
|
|
||||||
result = '-' + result;
|
|
||||||
|
|
||||||
if (num)
|
if (num)
|
||||||
return Number(result);
|
return Number(str);
|
||||||
|
|
||||||
return result;
|
return str;
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsafely convert satoshis to a BTC string.
|
|
||||||
* @param {Amount} value
|
|
||||||
* @param {Number} exp - Exponent.
|
|
||||||
* @param {Boolean} num - Return a number.
|
|
||||||
* @returns {String}
|
|
||||||
*/
|
|
||||||
|
|
||||||
Amount.serializeUnsafe = function serializeUnsafe(value, exp, num) {
|
|
||||||
assert(util.isInt(value), 'Non-satoshi value for conversion.');
|
|
||||||
|
|
||||||
value /= pow10(exp);
|
|
||||||
value = value.toFixed(exp);
|
|
||||||
|
|
||||||
if (num)
|
|
||||||
return Number(value);
|
|
||||||
|
|
||||||
if (exp !== 0) {
|
|
||||||
value = value.replace(/0+$/, '');
|
|
||||||
if (value[value.length - 1] === '.')
|
|
||||||
value += '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Safely convert a BTC string to satoshis.
|
|
||||||
* @param {String} value - BTC
|
|
||||||
* @returns {Amount} Satoshis.
|
|
||||||
* @throws on parse error
|
|
||||||
*/
|
|
||||||
|
|
||||||
Amount.value = function _value(value, num) {
|
|
||||||
if (util.isInt(value))
|
|
||||||
return value;
|
|
||||||
|
|
||||||
return Amount.parse(value, 8, num);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -416,165 +363,22 @@ Amount.value = function _value(value, num) {
|
|||||||
* floating point arithmetic. It also does
|
* floating point arithmetic. It also does
|
||||||
* extra validation to ensure the resulting
|
* extra validation to ensure the resulting
|
||||||
* Number will be 53 bits or less.
|
* Number will be 53 bits or less.
|
||||||
* @param {String} value - BTC
|
* @param {String} str - BTC
|
||||||
* @param {Number} exp - Exponent.
|
* @param {Number} exp - Exponent.
|
||||||
* @param {Boolean} num - Allow numbers.
|
* @param {Boolean} num - Allow numbers.
|
||||||
* @returns {Amount} Satoshis.
|
* @returns {Amount} Satoshis.
|
||||||
* @throws on parse error
|
* @throws on parse error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Amount.parse = function parse(value, exp, num) {
|
Amount.decode = function decode(str, exp, num) {
|
||||||
if (num && typeof value === 'number') {
|
if (num && typeof str === 'number') {
|
||||||
assert(util.isNumber(value), 'Non-BTC value for conversion.');
|
assert(util.isNumber(str), 'Non-BTC value for conversion.');
|
||||||
value = value.toString(10);
|
str = str.toString(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(util.isFloat(value), 'Non-BTC value for conversion.');
|
return util.fromFixed(str, exp);
|
||||||
|
|
||||||
const mult = pow10(exp);
|
|
||||||
const maxLo = modSafe(mult);
|
|
||||||
const maxHi = divSafe(mult);
|
|
||||||
let negative = false;
|
|
||||||
|
|
||||||
if (value[0] === '-') {
|
|
||||||
negative = true;
|
|
||||||
value = value.substring(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parts = value.split('.');
|
|
||||||
|
|
||||||
assert(parts.length <= 2, 'Bad decimal point.');
|
|
||||||
|
|
||||||
let hi = parts[0] || '0';
|
|
||||||
let lo = parts[1] || '0';
|
|
||||||
|
|
||||||
hi = hi.replace(/^0+/, '');
|
|
||||||
lo = lo.replace(/0+$/, '');
|
|
||||||
|
|
||||||
assert(hi.length <= 16 - exp, 'Number exceeds 2^53-1.');
|
|
||||||
assert(lo.length <= exp, 'Too many decimal places.');
|
|
||||||
|
|
||||||
if (hi.length === 0)
|
|
||||||
hi = '0';
|
|
||||||
|
|
||||||
while (lo.length < exp)
|
|
||||||
lo += '0';
|
|
||||||
|
|
||||||
hi = parseInt(hi, 10);
|
|
||||||
lo = parseInt(lo, 10);
|
|
||||||
|
|
||||||
assert(hi < maxHi || (hi === maxHi && lo <= maxLo),
|
|
||||||
'Number exceeds 2^53-1.');
|
|
||||||
|
|
||||||
let result = hi * mult + lo;
|
|
||||||
|
|
||||||
if (negative)
|
|
||||||
result = -result;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsafely convert a BTC string to satoshis.
|
|
||||||
* @param {String} value - BTC
|
|
||||||
* @param {Number} exp - Exponent.
|
|
||||||
* @param {Boolean} num - Allow numbers.
|
|
||||||
* @returns {Amount} Satoshis.
|
|
||||||
* @throws on parse error
|
|
||||||
*/
|
|
||||||
|
|
||||||
Amount.parseUnsafe = function parseUnsafe(value, exp, num) {
|
|
||||||
if (typeof value === 'string') {
|
|
||||||
assert(util.isFloat(value), 'Non-BTC value for conversion.');
|
|
||||||
value = parseFloat(value);
|
|
||||||
} else {
|
|
||||||
assert(util.isNumber(value), 'Non-BTC value for conversion.');
|
|
||||||
assert(num, 'Cannot parse number.');
|
|
||||||
}
|
|
||||||
|
|
||||||
value *= pow10(exp);
|
|
||||||
|
|
||||||
assert(value % 1 === 0, 'Too many decimal places.');
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Helpers
|
|
||||||
*/
|
|
||||||
|
|
||||||
function pow10(exp) {
|
|
||||||
switch (exp) {
|
|
||||||
case 0:
|
|
||||||
return 1;
|
|
||||||
case 1:
|
|
||||||
return 10;
|
|
||||||
case 2:
|
|
||||||
return 100;
|
|
||||||
case 3:
|
|
||||||
return 1000;
|
|
||||||
case 4:
|
|
||||||
return 10000;
|
|
||||||
case 5:
|
|
||||||
return 100000;
|
|
||||||
case 6:
|
|
||||||
return 1000000;
|
|
||||||
case 7:
|
|
||||||
return 10000000;
|
|
||||||
case 8:
|
|
||||||
return 100000000;
|
|
||||||
}
|
|
||||||
throw new Error('Exponent is too large.');
|
|
||||||
}
|
|
||||||
|
|
||||||
function modSafe(mod) {
|
|
||||||
switch (mod) {
|
|
||||||
case 1:
|
|
||||||
return 0;
|
|
||||||
case 10:
|
|
||||||
return 1;
|
|
||||||
case 100:
|
|
||||||
return 91;
|
|
||||||
case 1000:
|
|
||||||
return 991;
|
|
||||||
case 10000:
|
|
||||||
return 991;
|
|
||||||
case 100000:
|
|
||||||
return 40991;
|
|
||||||
case 1000000:
|
|
||||||
return 740991;
|
|
||||||
case 10000000:
|
|
||||||
return 4740991;
|
|
||||||
case 100000000:
|
|
||||||
return 54740991;
|
|
||||||
}
|
|
||||||
throw new Error('Exponent is too large.');
|
|
||||||
}
|
|
||||||
|
|
||||||
function divSafe(div) {
|
|
||||||
switch (div) {
|
|
||||||
case 1:
|
|
||||||
return 9007199254740991;
|
|
||||||
case 10:
|
|
||||||
return 900719925474099;
|
|
||||||
case 100:
|
|
||||||
return 90071992547409;
|
|
||||||
case 1000:
|
|
||||||
return 9007199254740;
|
|
||||||
case 10000:
|
|
||||||
return 900719925474;
|
|
||||||
case 100000:
|
|
||||||
return 90071992547;
|
|
||||||
case 1000000:
|
|
||||||
return 9007199254;
|
|
||||||
case 10000000:
|
|
||||||
return 900719925;
|
|
||||||
case 100000000:
|
|
||||||
return 90071992;
|
|
||||||
}
|
|
||||||
throw new Error('Exponent is too large.');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -10,6 +10,7 @@ const assert = require('assert');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const fs = require('../utils/fs');
|
const fs = require('../utils/fs');
|
||||||
|
const util = require('../utils/util');
|
||||||
const HOME = os.homedir ? os.homedir() : '/';
|
const HOME = os.homedir ? os.homedir() : '/';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -248,7 +249,7 @@ Config.prototype.str = function str(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (typeof value !== 'string')
|
if (typeof value !== 'string')
|
||||||
throw new Error(`${key} must be a string.`);
|
throw new Error(`${fmt(key)} must be a string.`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -271,17 +272,17 @@ Config.prototype.num = function num(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (typeof value !== 'number')
|
if (typeof value !== 'number')
|
||||||
throw new Error(`${key} must be a positive integer.`);
|
throw new Error(`${fmt(key)} must be a positive integer.`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^\d+$/.test(value))
|
if (!/^\d+$/.test(value))
|
||||||
throw new Error(`${key} must be a positive integer.`);
|
throw new Error(`${fmt(key)} must be a positive integer.`);
|
||||||
|
|
||||||
value = parseInt(value, 10);
|
value = parseInt(value, 10);
|
||||||
|
|
||||||
if (!isFinite(value))
|
if (!isFinite(value))
|
||||||
throw new Error(`${key} must be a positive integer.`);
|
throw new Error(`${fmt(key)} must be a positive integer.`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -304,17 +305,17 @@ Config.prototype.flt = function flt(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (typeof value !== 'number')
|
if (typeof value !== 'number')
|
||||||
throw new Error(`${key} must be a float.`);
|
throw new Error(`${fmt(key)} must be a float.`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^\d*(?:\.\d*)?$/.test(value))
|
if (!/^\d*(?:\.\d*)?$/.test(value))
|
||||||
throw new Error(`${key} must be a float.`);
|
throw new Error(`${fmt(key)} must be a float.`);
|
||||||
|
|
||||||
value = parseFloat(value);
|
value = parseFloat(value);
|
||||||
|
|
||||||
if (!isFinite(value))
|
if (!isFinite(value))
|
||||||
throw new Error(`${key} must be a float.`);
|
throw new Error(`${fmt(key)} must be a float.`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -327,7 +328,7 @@ Config.prototype.flt = function flt(key, fallback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Config.prototype.amt = function amt(key, fallback) {
|
Config.prototype.amt = function amt(key, fallback) {
|
||||||
let value = this.get(key);
|
const value = this.get(key);
|
||||||
|
|
||||||
if (fallback === undefined)
|
if (fallback === undefined)
|
||||||
fallback = null;
|
fallback = null;
|
||||||
@ -337,24 +338,17 @@ Config.prototype.amt = function amt(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (typeof value !== 'number')
|
if (typeof value !== 'number')
|
||||||
throw new Error(`${key} must be an amount.`);
|
throw new Error(`${fmt(key)} must be an amount.`);
|
||||||
|
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
|
||||||
|
throw new Error(`${fmt(key)} must be an amount (u64).`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^\d+(\.\d{0,8})?$/.test(value))
|
try {
|
||||||
throw new Error(`${key} must be an amount.`);
|
return util.fromFixed(value, 8);
|
||||||
|
} catch (e) {
|
||||||
value = parseFloat(value);
|
throw new Error(`${fmt(key)} must be an amount (parse).`);
|
||||||
|
}
|
||||||
if (!isFinite(value))
|
|
||||||
throw new Error(`${key} must be an amount.`);
|
|
||||||
|
|
||||||
value *= 1e8;
|
|
||||||
|
|
||||||
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
|
|
||||||
throw new Error(`${key} must be an amount (uint64).`);
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -375,7 +369,7 @@ Config.prototype.bool = function bool(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (typeof value !== 'boolean')
|
if (typeof value !== 'boolean')
|
||||||
throw new Error(`${key} must be a boolean.`);
|
throw new Error(`${fmt(key)} must be a boolean.`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -385,7 +379,7 @@ Config.prototype.bool = function bool(key, fallback) {
|
|||||||
if (value === 'false' || value === '0')
|
if (value === 'false' || value === '0')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
throw new Error(`${key} must be a boolean.`);
|
throw new Error(`${fmt(key)} must be a boolean.`);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,14 +400,14 @@ Config.prototype.buf = function buf(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (!Buffer.isBuffer(value))
|
if (!Buffer.isBuffer(value))
|
||||||
throw new Error(`${key} must be a buffer.`);
|
throw new Error(`${fmt(key)} must be a buffer.`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = Buffer.from(value, 'hex');
|
const data = Buffer.from(value, 'hex');
|
||||||
|
|
||||||
if (data.length !== value.length / 2)
|
if (data.length !== value.length / 2)
|
||||||
throw new Error(`${key} must be a hex string.`);
|
throw new Error(`${fmt(key)} must be a hex string.`);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
@ -436,7 +430,7 @@ Config.prototype.array = function array(key, fallback) {
|
|||||||
|
|
||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (!Array.isArray(value))
|
if (!Array.isArray(value))
|
||||||
throw new Error(`${key} must be an array.`);
|
throw new Error(`${fmt(key)} must be an array.`);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +464,7 @@ Config.prototype.obj = function obj(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (!value || typeof value !== 'object')
|
if (!value || typeof value !== 'object')
|
||||||
throw new Error(`${key} must be an object.`);
|
throw new Error(`${fmt(key)} must be an object.`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -492,7 +486,7 @@ Config.prototype.func = function func(key, fallback) {
|
|||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
if (!value || typeof value !== 'function')
|
if (!value || typeof value !== 'function')
|
||||||
throw new Error(`${key} must be a function.`);
|
throw new Error(`${fmt(key)} must be a function.`);
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
@ -933,6 +927,16 @@ Config.prototype.parseForm = function parseForm(query, map) {
|
|||||||
* Helpers
|
* Helpers
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
function fmt(key) {
|
||||||
|
if (Array.isArray(key))
|
||||||
|
key = key[0];
|
||||||
|
|
||||||
|
if (typeof key === 'number')
|
||||||
|
return `Argument #${key}`;
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
function unescape(str) {
|
function unescape(str) {
|
||||||
try {
|
try {
|
||||||
str = decodeURIComponent(str);
|
str = decodeURIComponent(str);
|
||||||
@ -945,13 +949,7 @@ function unescape(str) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isAlpha(str) {
|
function isAlpha(str) {
|
||||||
if (typeof str !== 'string')
|
return /^[a-z0-9]+$/.test(str);
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!/^[a-z0-9]+$/.test(str))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -2066,7 +2066,7 @@ TX.prototype.format = function format(view, entry, index) {
|
|||||||
rate = this.getRate(view);
|
rate = this.getRate(view);
|
||||||
|
|
||||||
// Rate can exceed 53 bits in testing.
|
// Rate can exceed 53 bits in testing.
|
||||||
if (!util.isSafeInteger(rate))
|
if (!Number.isSafeInteger(rate))
|
||||||
rate = 0;
|
rate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2135,7 +2135,7 @@ TX.prototype.getJSON = function getJSON(network, view, entry, index) {
|
|||||||
rate = this.getRate(view);
|
rate = this.getRate(view);
|
||||||
|
|
||||||
// Rate can exceed 53 bits in testing.
|
// Rate can exceed 53 bits in testing.
|
||||||
if (!util.isSafeInteger(rate))
|
if (!Number.isSafeInteger(rate))
|
||||||
rate = 0;
|
rate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -98,7 +98,7 @@ ProtoWriter.prototype.writeFieldVarint = function writeFieldVarint(tag, value) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
ProtoWriter.prototype.writeFieldU64 = function writeFieldU64(tag, value) {
|
ProtoWriter.prototype.writeFieldU64 = function writeFieldU64(tag, value) {
|
||||||
assert(util.isSafeInteger(value));
|
assert(Number.isSafeInteger(value));
|
||||||
this.writeFieldVarint(tag, value);
|
this.writeFieldVarint(tag, value);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -125,7 +125,7 @@ ProtoWriter.prototype.writeFieldString = function writeFieldString(tag, data, en
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function writeVarint(data, num, off) {
|
function writeVarint(data, num, off) {
|
||||||
assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
assert(Number.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
||||||
|
|
||||||
do {
|
do {
|
||||||
assert(off < data.length);
|
assert(off < data.length);
|
||||||
@ -142,7 +142,7 @@ function writeVarint(data, num, off) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function slipVarint(num) {
|
function slipVarint(num) {
|
||||||
assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
assert(Number.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
||||||
|
|
||||||
let data = 0;
|
let data = 0;
|
||||||
let size = 0;
|
let size = 0;
|
||||||
@ -163,7 +163,7 @@ function slipVarint(num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function sizeVarint(num) {
|
function sizeVarint(num) {
|
||||||
assert(util.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
assert(Number.isSafeInteger(num), 'Number exceeds 2^53-1.');
|
||||||
|
|
||||||
let size = 0;
|
let size = 0;
|
||||||
|
|
||||||
|
|||||||
@ -105,16 +105,6 @@ util.revHex = function revHex(data) {
|
|||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether a number is below MAX_SAFE_INTEGER.
|
|
||||||
* @param {Number} value
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
util.isSafeInteger = function isSafeInteger(value) {
|
|
||||||
return Number.isSafeInteger(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether the result of a positive
|
* Test whether the result of a positive
|
||||||
* addition would be below MAX_SAFE_INTEGER.
|
* addition would be below MAX_SAFE_INTEGER.
|
||||||
@ -168,9 +158,7 @@ util.isSafeAddition = function isSafeAddition(a, b) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
util.isNumber = function isNumber(value) {
|
util.isNumber = function isNumber(value) {
|
||||||
return typeof value === 'number'
|
return typeof value === 'number' && Number.isSafeInteger(value);
|
||||||
&& isFinite(value)
|
|
||||||
&& util.isSafeInteger(value);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,24 +251,6 @@ util.isHex256 = function isHex256(hash) {
|
|||||||
return util.isHex(hash) && hash.length === 64;
|
return util.isHex(hash) && hash.length === 64;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Test whether a string qualifies as a float.
|
|
||||||
*
|
|
||||||
* This is stricter than checking if the result of parseFloat() is NaN
|
|
||||||
* as, e.g. parseFloat successfully parses the string '1.2.3' as 1.2, and
|
|
||||||
* we also check that the value is a string.
|
|
||||||
*
|
|
||||||
* @param {String?} value
|
|
||||||
* @returns {Boolean}
|
|
||||||
*/
|
|
||||||
|
|
||||||
util.isFloat = function isFloat(value) {
|
|
||||||
return typeof value === 'string'
|
|
||||||
&& /^-?(\d+)?(?:\.\d*)?$/.test(value)
|
|
||||||
&& value.length !== 0
|
|
||||||
&& value !== '-';
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* util.inspect() with 20 levels of depth.
|
* util.inspect() with 20 levels of depth.
|
||||||
* @param {Object|String} obj
|
* @param {Object|String} obj
|
||||||
@ -809,3 +779,180 @@ util.memoryUsage = function memoryUsage() {
|
|||||||
external: util.mb(mem.external)
|
external: util.mb(mem.external)
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert int to fixed number string and reduce by a
|
||||||
|
* power of ten (uses no floating point arithmetic).
|
||||||
|
* @param {Number} num
|
||||||
|
* @param {Number} exp - Number of decimal places.
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
util.toFixed = function toFixed(num, exp) {
|
||||||
|
assert(typeof num === 'number');
|
||||||
|
assert(Number.isSafeInteger(num) && num % 1 === 0, 'Invalid integer value.');
|
||||||
|
|
||||||
|
let sign = '';
|
||||||
|
|
||||||
|
if (num < 0) {
|
||||||
|
num = -num;
|
||||||
|
sign = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
const mult = pow10(exp);
|
||||||
|
let lo = num % mult;
|
||||||
|
const hi = (num - lo) / mult;
|
||||||
|
|
||||||
|
lo = lo.toString(10);
|
||||||
|
|
||||||
|
while (lo.length < exp)
|
||||||
|
lo = '0' + lo;
|
||||||
|
|
||||||
|
lo = lo.replace(/0+$/, '');
|
||||||
|
|
||||||
|
assert(lo.length <= exp, 'Invalid integer value.');
|
||||||
|
|
||||||
|
if (lo.length === 0)
|
||||||
|
lo = '0';
|
||||||
|
|
||||||
|
if (exp === 0)
|
||||||
|
return `${sign}${hi}`;
|
||||||
|
|
||||||
|
return `${sign}${hi}.${lo}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a fixed number string and multiply by a
|
||||||
|
* power of ten (uses no floating point arithmetic).
|
||||||
|
* @param {String} str
|
||||||
|
* @param {Number} exp - Number of decimal places.
|
||||||
|
* @returns {Number}
|
||||||
|
*/
|
||||||
|
|
||||||
|
util.fromFixed = function fromFixed(str, exp) {
|
||||||
|
assert(typeof str === 'string');
|
||||||
|
assert(str.length <= 32, 'Fixed number string too large.');
|
||||||
|
|
||||||
|
let sign = 1;
|
||||||
|
|
||||||
|
if (str.length > 0 && str[0] === '-') {
|
||||||
|
str = str.substring(1);
|
||||||
|
sign = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let hi = str;
|
||||||
|
let lo = '0';
|
||||||
|
|
||||||
|
const index = str.indexOf('.');
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
hi = str.substring(0, index);
|
||||||
|
lo = str.substring(index + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
hi = hi.replace(/^0+/, '');
|
||||||
|
lo = lo.replace(/0+$/, '');
|
||||||
|
|
||||||
|
assert(hi.length <= 16 - exp,
|
||||||
|
'Fixed number string exceeds 2^53-1.');
|
||||||
|
|
||||||
|
assert(lo.length <= exp,
|
||||||
|
'Too many decimal places in fixed number string.');
|
||||||
|
|
||||||
|
if (hi.length === 0)
|
||||||
|
hi = '0';
|
||||||
|
|
||||||
|
while (lo.length < exp)
|
||||||
|
lo += '0';
|
||||||
|
|
||||||
|
assert(/^\d*$/.test(hi) && /^\d*$/.test(lo),
|
||||||
|
'Non-numeric characters in fixed number string.');
|
||||||
|
|
||||||
|
hi = parseInt(hi, 10);
|
||||||
|
lo = parseInt(lo, 10);
|
||||||
|
|
||||||
|
const mult = pow10(exp);
|
||||||
|
const maxLo = modSafe(mult);
|
||||||
|
const maxHi = divSafe(mult);
|
||||||
|
|
||||||
|
assert(hi < maxHi || (hi === maxHi && lo <= maxLo),
|
||||||
|
'Fixed number string exceeds 2^53-1.');
|
||||||
|
|
||||||
|
return sign * (hi * mult + lo);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helpers
|
||||||
|
*/
|
||||||
|
|
||||||
|
function pow10(exp) {
|
||||||
|
switch (exp) {
|
||||||
|
case 0:
|
||||||
|
return 1;
|
||||||
|
case 1:
|
||||||
|
return 10;
|
||||||
|
case 2:
|
||||||
|
return 100;
|
||||||
|
case 3:
|
||||||
|
return 1000;
|
||||||
|
case 4:
|
||||||
|
return 10000;
|
||||||
|
case 5:
|
||||||
|
return 100000;
|
||||||
|
case 6:
|
||||||
|
return 1000000;
|
||||||
|
case 7:
|
||||||
|
return 10000000;
|
||||||
|
case 8:
|
||||||
|
return 100000000;
|
||||||
|
}
|
||||||
|
throw new Error('Exponent is too large.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function modSafe(mod) {
|
||||||
|
switch (mod) {
|
||||||
|
case 1:
|
||||||
|
return 0;
|
||||||
|
case 10:
|
||||||
|
return 1;
|
||||||
|
case 100:
|
||||||
|
return 91;
|
||||||
|
case 1000:
|
||||||
|
return 991;
|
||||||
|
case 10000:
|
||||||
|
return 991;
|
||||||
|
case 100000:
|
||||||
|
return 40991;
|
||||||
|
case 1000000:
|
||||||
|
return 740991;
|
||||||
|
case 10000000:
|
||||||
|
return 4740991;
|
||||||
|
case 100000000:
|
||||||
|
return 54740991;
|
||||||
|
}
|
||||||
|
throw new Error('Exponent is too large.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function divSafe(div) {
|
||||||
|
switch (div) {
|
||||||
|
case 1:
|
||||||
|
return 9007199254740991;
|
||||||
|
case 10:
|
||||||
|
return 900719925474099;
|
||||||
|
case 100:
|
||||||
|
return 90071992547409;
|
||||||
|
case 1000:
|
||||||
|
return 9007199254740;
|
||||||
|
case 10000:
|
||||||
|
return 900719925474;
|
||||||
|
case 100000:
|
||||||
|
return 90071992547;
|
||||||
|
case 1000000:
|
||||||
|
return 9007199254;
|
||||||
|
case 10000000:
|
||||||
|
return 900719925;
|
||||||
|
case 100000000:
|
||||||
|
return 90071992;
|
||||||
|
}
|
||||||
|
throw new Error('Exponent is too large.');
|
||||||
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
const util = require('../utils/util');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validator
|
* Validator
|
||||||
@ -280,7 +281,7 @@ Validator.prototype.i64 = function i64(key, fallback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Validator.prototype.amt = function amt(key, fallback) {
|
Validator.prototype.amt = function amt(key, fallback) {
|
||||||
let value = this.get(key);
|
const value = this.get(key);
|
||||||
|
|
||||||
if (fallback === undefined)
|
if (fallback === undefined)
|
||||||
fallback = null;
|
fallback = null;
|
||||||
@ -291,23 +292,16 @@ Validator.prototype.amt = function amt(key, fallback) {
|
|||||||
if (typeof value !== 'string') {
|
if (typeof value !== 'string') {
|
||||||
if (typeof value !== 'number')
|
if (typeof value !== 'number')
|
||||||
throw new ValidationError(key, 'amount');
|
throw new ValidationError(key, 'amount');
|
||||||
|
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
|
||||||
|
throw new ValidationError(key, 'amount');
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!/^\d+(\.\d{0,8})?$/.test(value))
|
try {
|
||||||
|
return util.fromFixed(value, 8);
|
||||||
|
} catch (e) {
|
||||||
throw new ValidationError(key, 'amount');
|
throw new ValidationError(key, 'amount');
|
||||||
|
}
|
||||||
value = parseFloat(value);
|
|
||||||
|
|
||||||
if (!isFinite(value))
|
|
||||||
throw new ValidationError(key, 'amount');
|
|
||||||
|
|
||||||
value *= 1e8;
|
|
||||||
|
|
||||||
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
|
|
||||||
throw new ValidationError(key, 'amount (uint64)');
|
|
||||||
|
|
||||||
return value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,7 +312,7 @@ Validator.prototype.amt = function amt(key, fallback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
Validator.prototype.btc = function btc(key, fallback) {
|
Validator.prototype.btc = function btc(key, fallback) {
|
||||||
let value = this.num(key);
|
const value = this.flt(key);
|
||||||
|
|
||||||
if (fallback === undefined)
|
if (fallback === undefined)
|
||||||
fallback = null;
|
fallback = null;
|
||||||
@ -326,12 +320,14 @@ Validator.prototype.btc = function btc(key, fallback) {
|
|||||||
if (value === null)
|
if (value === null)
|
||||||
return fallback;
|
return fallback;
|
||||||
|
|
||||||
value *= 1e8;
|
if (value < 0 || value > 0x1fffffffffffff)
|
||||||
|
|
||||||
if (value % 1 !== 0 || value < 0 || value > 0x1fffffffffffff)
|
|
||||||
throw new ValidationError(key, 'btc float (uint64)');
|
throw new ValidationError(key, 'btc float (uint64)');
|
||||||
|
|
||||||
return value;
|
try {
|
||||||
|
return util.fromFixed(value.toString(10), 8);
|
||||||
|
} catch (e) {
|
||||||
|
throw new ValidationError(key, 'btc float');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -581,18 +577,13 @@ Validator.prototype.func = function func(key, fallback) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
function fmt(key) {
|
function fmt(key) {
|
||||||
|
if (Array.isArray(key))
|
||||||
|
key = key[0];
|
||||||
|
|
||||||
if (typeof key === 'number')
|
if (typeof key === 'number')
|
||||||
return `Param #${key}`;
|
return `Param #${key}`;
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
function inherits(child, parent) {
|
return key;
|
||||||
child.super_ = parent;
|
|
||||||
Object.setPrototypeOf(child.prototype, parent.prototype);
|
|
||||||
Object.defineProperty(child.prototype, 'constructor', {
|
|
||||||
value: child,
|
|
||||||
enumerable: false
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ValidationError(key, type) {
|
function ValidationError(key, type) {
|
||||||
@ -608,14 +599,10 @@ function ValidationError(key, type) {
|
|||||||
Error.captureStackTrace(this, ValidationError);
|
Error.captureStackTrace(this, ValidationError);
|
||||||
}
|
}
|
||||||
|
|
||||||
inherits(ValidationError, Error);
|
util.inherits(ValidationError, Error);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports = Validator;
|
module.exports = Validator;
|
||||||
exports.Validator = Validator;
|
|
||||||
exports.Error = ValidationError;
|
|
||||||
|
|
||||||
module.exports = exports;
|
|
||||||
|
|||||||
@ -2963,7 +2963,7 @@ Details.prototype.toJSON = function toJSON() {
|
|||||||
let rate = this.getRate(fee);
|
let rate = this.getRate(fee);
|
||||||
|
|
||||||
// Rate can exceed 53 bits in testing.
|
// Rate can exceed 53 bits in testing.
|
||||||
if (!util.isSafeInteger(rate))
|
if (!Number.isSafeInteger(rate))
|
||||||
rate = 0;
|
rate = 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -10,6 +10,7 @@ const encoding = require('../lib/utils/encoding');
|
|||||||
const Amount = require('../lib/btc/amount');
|
const Amount = require('../lib/btc/amount');
|
||||||
const consensus = require('../lib/protocol/consensus');
|
const consensus = require('../lib/protocol/consensus');
|
||||||
const Validator = require('../lib/utils/validator');
|
const Validator = require('../lib/utils/validator');
|
||||||
|
const util = require('../lib/utils/util');
|
||||||
|
|
||||||
const base58Tests = [
|
const base58Tests = [
|
||||||
['', ''],
|
['', ''],
|
||||||
@ -78,24 +79,34 @@ describe('Utils', function() {
|
|||||||
assert(btc === 5460 * 10000000);
|
assert(btc === 5460 * 10000000);
|
||||||
btc = Amount.value('546.0000');
|
btc = Amount.value('546.0000');
|
||||||
assert(btc === 5460 * 10000000);
|
assert(btc === 5460 * 10000000);
|
||||||
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
Amount.value('546.00000000000000000');
|
Amount.value('546.00000000000000000');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
Amount.value('546.00000000000000001');
|
Amount.value('546.00000000000000001');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
Amount.value('90071992.54740991');
|
Amount.value('90071992.54740991');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.doesNotThrow(() => {
|
assert.doesNotThrow(() => {
|
||||||
Amount.value('090071992.547409910');
|
Amount.value('090071992.547409910');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
Amount.value('90071992.54740992');
|
Amount.value('90071992.54740992');
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
Amount.value('190071992.54740991');
|
Amount.value('190071992.54740991');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
assert.strictEqual(parseFloat('0.15645647') * 1e8, 15645646.999999998);
|
||||||
|
assert.strictEqual(util.fromFixed('0.15645647', 8), 15645647);
|
||||||
|
assert.strictEqual(util.toFixed(15645647, 8), '0.15645647');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should write/read new varints', () => {
|
it('should write/read new varints', () => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user