utils and tests.

This commit is contained in:
Christopher Jeffrey 2016-05-15 17:41:45 -07:00
parent 6776c701e3
commit 535b6a823e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 151 additions and 55 deletions

View File

@ -648,69 +648,80 @@ utils.assert.fatal = function fatal(value, message) {
};
/**
* Convert satoshis to a BTC string.
* This function explicitly avoids
* any floating point arithmetic.
* @param {Number} satoshi
* Safely convert satoshis to a BTC string.
* This function explicitly avoids any
* floating point arithmetic.
* @param {Number} value - Satoshis.
* @returns {String} BTC string.
*/
utils.btc = function btc(satoshi) {
utils.btc = function btc(value) {
var negative = false;
var btc;
var hi, lo, result;
if (utils.isBTC(satoshi))
return satoshi;
if (utils.isFloat(value))
return value;
assert(utils.isSatoshi(satoshi), 'Non-satoshi value for conversion.');
assert(utils.isInt(value), 'Non-satoshi value for conversion.');
if (satoshi < 0) {
satoshi = -satoshi;
if (value < 0) {
value = -value;
negative = true;
}
btc = satoshi.toString(10);
assert(value <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1.');
while (btc.length < 9)
btc = '0' + btc;
// assert(value <= 21000000 * 100000000, 'Number exceeds MAX_MONEY.');
btc = btc.slice(0, -8) + '.' + btc.slice(-8);
value = value.toString(10);
btc = btc.replace(/0+$/, '');
assert(value.length <= 16, 'Number exceeds 2^53-1.');
if (btc[btc.length - 1] === '.')
btc = btc.slice(0, -1);
while (value.length < 9)
value = '0' + value;
hi = value.slice(0, -8);
lo = value.slice(-8);
lo = lo.replace(/0+$/, '');
if (lo.length === 0)
lo += '0';
result = hi + '.' + lo;
if (negative)
btc = '-' + btc;
result = '-' + result;
return btc;
return result;
};
/**
* Convert BTC string to satoshis.
* This function explicitly avoids
* any floating point arithmetic.
* @param {String} btc
* Safely convert a BTC string to satoshis.
* This function explicitly avoids any
* floating point arithmetic. It also does
* extra validation to ensure the resulting
* Number will be 53 bits or less.
* @param {String} value - BTC
* @returns {Number} Satoshis.
* @throws on parse error
*/
utils.satoshi = function satoshi(btc) {
utils.satoshi = function satoshi(value) {
var negative = false;
var satoshi, parts, hi, lo;
var parts, hi, lo, result;
if (utils.isSatoshi(btc))
return btc;
if (utils.isInt(value))
return value;
assert(utils.isBTC(btc), 'Non-BTC value for conversion.');
assert(utils.isFloat(value), 'Non-BTC value for conversion.');
if (btc[0] === '-') {
if (value[0] === '-') {
negative = true;
btc = btc.substring(1);
value = value.substring(1);
}
parts = btc.split('.');
parts = value.split('.');
assert(parts.length <= 2, 'Bad decimal point.');
@ -720,53 +731,93 @@ utils.satoshi = function satoshi(btc) {
hi = hi.replace(/^0+/, '');
lo = lo.replace(/0+$/, '');
assert(hi.length <= 8, 'Number exceeds MAX_MONEY.');
assert(+hi < 21000000, 'Number exceeds MAX_MONEY.');
assert(hi.length <= 8, 'Number exceeds 2^53-1.');
assert(lo.length <= 8, 'Too many decimal places.');
if (hi.length === 0)
hi = '0';
while (lo.length < 8)
lo += '0';
satoshi = parseInt(hi + lo, 10);
hi = parseInt(hi, 10);
lo = parseInt(lo, 10);
assert(hi < 90071992 || (hi === 90071992 && lo <= 54740991),
'Number exceeds 2^53-1.');
// assert(hi < 21000000 || (hi === 21000000 && lo === 0),
// 'Number exceeds MAX_MONEY.');
result = hi * 100000000 + lo;
if (negative)
satoshi = -satoshi;
result = -result;
return satoshi;
return result;
};
/**
* Test whether a number is both a Number and finite.
* @param {Number?} val
* @param {Number?} value
* @returns {Boolean}
*/
utils.isNumber = function isNumber(val) {
return typeof val === 'number' && isFinite(val);
utils.isNumber = function isNumber(value) {
return typeof value === 'number' && isFinite(value);
};
/**
* Test whether an object qualifies as satoshis.
* @param {BN?} val
* Test whether a string qualifies as a float.
* @param {String?} value
* @returns {Boolean}
*/
utils.isSatoshi = function isSatoshi(val) {
return utils.isNumber(val) && val % 1 === 0;
utils.isFloat = function isFloat(value) {
return typeof value === 'string'
&& /^-?(\d+)?(?:\.\d*)?$/.test(value)
&& value.length !== 0
&& value !== '-';
};
/**
* Test whether an object qualifies as a BTC string.
* @param {String?} val
* Test whether an object is a finite number and int.
* @param {BN?} value
* @returns {Boolean}
*/
utils.isBTC = function isBTC(val) {
return typeof val === 'string'
&& /^-?(\d+)?(?:\.\d*)?$/.test(val)
&& val.length !== 0
&& val !== '-';
utils.isInt = function isInt(value) {
return utils.isNumber(value) && value % 1 === 0;
};
/**
* Test and validate a satoshi value (Number).
* @param {Number?} value
* @returns {Boolean}
*/
utils.isSatoshi = function isSatoshi(value) {
try {
utils.satoshi(value);
return true;
} catch (e) {
return false;
}
};
/**
* Test and validate a BTC string.
* @param {String?} value
* @returns {Boolean}
*/
utils.isBTC = function isBTC(value) {
try {
utils.btc(value);
return true;
} catch (e) {
return false;
}
};
/**
@ -1600,6 +1651,15 @@ utils.writeU64NBE = function writeU64NBE(dst, num, off) {
utils.MAX_SAFE_INTEGER = 0x1fffffffffffff;
/**
* Max 52 bit integer (safe for additions).
* (MAX_SAFE_INTEGER - 1) / 2
* @type Number
* @const
*/
utils.MAX_SAFE_ADDITION = 0xfffffffffffff;
/**
* Write a javascript number as an int64le (faster than big numbers).
* @param {Number} value
@ -1613,8 +1673,6 @@ utils.write64N = function write64N(dst, num, off, be) {
off = off >>> 0;
assert(num <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1');
negative = num < 0;
if (negative) {
@ -1622,6 +1680,8 @@ utils.write64N = function write64N(dst, num, off, be) {
num -= 1;
}
assert(num <= utils.MAX_SAFE_INTEGER, 'Number exceeds 2^53-1');
hi = num / 0x100000000 | 0;
lo = num % 0x100000000;

View File

@ -27,7 +27,7 @@ describe('Utils', function() {
btc = utils.btc(54678 * 1000000);
assert.equal(btc, '546.78');
btc = utils.btc(5460 * 10000000);
assert.equal(btc, '546');
assert.equal(btc, '546.0');
});
it('should convert btc to satoshi', function() {
@ -37,6 +37,42 @@ describe('Utils', function() {
assert(btc === 54678 * 1000000);
btc = utils.satoshi('546');
assert(btc === 5460 * 10000000);
btc = utils.satoshi('546.0');
assert(btc === 5460 * 10000000);
btc = utils.satoshi('546.0000');
assert(btc === 5460 * 10000000);
assert.doesNotThrow(function() {
utils.satoshi('546.00000000000000000');
});
assert.throws(function() {
utils.satoshi('546.00000000000000001');
});
/*
assert.doesNotThrow(function() {
utils.satoshi('21000000');
});
assert.doesNotThrow(function() {
utils.satoshi('021000000');
});
assert.throws(function() {
utils.satoshi('21000001');
});
assert.throws(function() {
utils.satoshi('121000000');
});
*/
assert.doesNotThrow(function() {
utils.satoshi('90071992.54740991');
});
assert.doesNotThrow(function() {
utils.satoshi('090071992.547409910');
});
assert.throws(function() {
utils.satoshi('90071992.54740992');
});
assert.throws(function() {
utils.satoshi('190071992.54740991');
});
});
var unsigned = [