diff --git a/index.js b/index.js index 17e3d9f..51ed86a 100644 --- a/index.js +++ b/index.js @@ -18,7 +18,10 @@ bitcore.encoding.BufferReader = require('./lib/encoding/bufferreader'); bitcore.encoding.BufferWriter = require('./lib/encoding/bufferwriter'); bitcore.encoding.Varint = require('./lib/encoding/varint'); -bitcore.util = require('./lib/util'); +bitcore.util = {}; +bitcore.util.bitcoin = require('./lib/util/bitcoin'); +bitcore.util.buffer = require('./lib/util/buffer'); +bitcore.util.js = require('./lib/util/js'); // main bitcoin library bitcore.Address = require('./lib/address'); diff --git a/lib/crypto/point.js b/lib/crypto/point.js index d457b18..22ac66a 100644 --- a/lib/crypto/point.js +++ b/lib/crypto/point.js @@ -4,9 +4,10 @@ var BN = require('./bn'); var elliptic = require('elliptic'); var ec = elliptic.curves.secp256k1; -var ecpoint = ec.curve.point.bind(ec.curve) +var ecpoint = ec.curve.point.bind(ec.curve); var p = ec.curve.point(); -var Curve = Object.getPrototypeOf(ec.curve); + +var bufferUtil = require('../util/buffer'); var Point = function Point(x, y, isRed) { return ecpoint(x, y, isRed); @@ -27,7 +28,6 @@ Point.getN = function() { Point.prototype._getX = Point.prototype.getX; Point.prototype.getX = function() { - var n = BN(this._getX().toArray()); return BN(this._getX().toArray()); }; @@ -38,15 +38,34 @@ Point.prototype.getY = function() { //https://www.iacr.org/archive/pkc2003/25670211/25670211.pdf Point.prototype.validate = function() { + /* jshint maxcomplexity: 8 */ var p2 = Point.fromX(this.getY().isOdd(), this.getX()); - if (!(p2.y.cmp(this.y) === 0)) + if (p2.y.cmp(this.y) !== 0) { throw new Error('Invalid y value of public key'); - if (!(this.getX().gt(-1) && this.getX().lt(Point.getN())) - ||!(this.getY().gt(-1) && this.getY().lt(Point.getN()))) + } + var xValidRange = (this.getX().gt(-1) && this.getX().lt(Point.getN())); + var yValidRange = (this.getY().gt(-1) && this.getY().lt(Point.getN())); + if (!(xValidRange && yValidRange)) { throw new Error('Point does not lie on the curve'); - if (!(this.mul(Point.getN()).isInfinity())) + } + if (!(this.mul(Point.getN()).isInfinity())) { throw new Error('Point times N must be infinity'); + } return this; }; +Point.pointToCompressed = function pointToCompressed(point) { + var xbuf = point.getX().toBuffer({size: 32}); + var ybuf = point.getY().toBuffer({size: 32}); + + var prefix; + var odd = ybuf[ybuf.length - 1] % 2; + if (odd) { + prefix = new Buffer([0x03]); + } else { + prefix = new Buffer([0x02]); + } + return bufferUtil.concat([prefix, xbuf]); +}; + module.exports = Point; diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 78d5e53..d3e0acf 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -1,6 +1,10 @@ 'use strict'; + +var assert = require('assert'); +var buffer = require('buffer'); var _ = require('lodash'); + var BN = require('./crypto/bn'); var Base58 = require('./encoding/base58'); var Base58Check = require('./encoding/base58check'); @@ -11,9 +15,8 @@ var Point = require('./crypto/point'); var PrivateKey = require('./privatekey'); var Random = require('./crypto/random'); -var assert = require('assert'); -var buffer = require('buffer'); -var util = require('./util'); +var bufferUtil = require('./util/buffer'); +var jsUtil = require('./util/js'); var MINIMUM_ENTROPY_BITS = 128; var BITS_TO_BYTES = 1/8; @@ -29,10 +32,10 @@ function HDPrivateKey(arg) { return new HDPrivateKey(arg); } if (arg) { - if (_.isString(arg) || buffer.Buffer.isBuffer(arg)) { + if (_.isString(arg) || bufferUtil.isBuffer(arg)) { if (HDPrivateKey.isValidSerialized(arg)) { this._buildFromSerialized(arg); - } else if (util.isValidJson(arg)) { + } else if (jsUtil.isValidJson(arg)) { this._buildFromJson(arg); } else { throw new Error(HDPrivateKey.getSerializedError(arg)); @@ -73,12 +76,12 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { return cached; } - var indexBuffer = util.integerAsBuffer(index); + var indexBuffer = bufferUtil.integerAsBuffer(index); var data; if (hardened) { - data = buffer.Buffer.concat([new buffer.Buffer([0]), this.privateKey.toBuffer(), indexBuffer]); + data = bufferUtil.concat([new buffer.Buffer([0]), this.privateKey.toBuffer(), indexBuffer]); } else { - data = buffer.Buffer.concat([this.publicKey.toBuffer(), indexBuffer]); + data = bufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } var hash = Hash.sha512hmac(data, this._buffers.chainCode); var leftPart = BN().fromBuffer(hash.slice(0, 32), {size: 32}); @@ -143,7 +146,7 @@ HDPrivateKey.isValidSerialized = function(data, network) { */ HDPrivateKey.getSerializedError = function(data, network) { /* jshint maxcomplexity: 10 */ - if (!(_.isString(data) || buffer.Buffer.isBuffer(data))) { + if (!(_.isString(data) || bufferUtil.isBuffer(data))) { return HDPrivateKey.Errors.InvalidArgument; } if (!Base58.validCharacters(data)) { @@ -172,7 +175,7 @@ HDPrivateKey._validateNetwork = function(data, network) { return HDPrivateKey.Errors.InvalidNetworkArgument; } var version = data.slice(0, 4); - if (util.integerFromBuffer(version) !== network.xprivkey) { + if (bufferUtil.integerFromBuffer(version) !== network.xprivkey) { return HDPrivateKey.Errors.InvalidNetwork; } return null; @@ -186,13 +189,13 @@ HDPrivateKey.prototype._buildFromObject = function(arg) { /* jshint maxcomplexity: 12 */ // TODO: Type validation var buffers = { - version: arg.network ? util.integerAsBuffer(Network.get(arg.network).xprivkey) : arg.version, - depth: util.integerAsSingleByteBuffer(arg.depth), - parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? util.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint, - childIndex: _.isNumber(arg.childIndex) ? util.integerAsBuffer(arg.childIndex) : arg.childIndex, - chainCode: _.isString(arg.chainCode) ? util.hexToBuffer(arg.chainCode) : arg.chainCode, - privateKey: (_.isString(arg.privateKey) && util.isHexa(arg.privateKey)) ? util.hexToBuffer(arg.privateKey) : arg.privateKey, - checksum: arg.checksum ? (arg.checksum.length ? arg.checksum : util.integerAsBuffer(arg.checksum)) : undefined + version: arg.network ? bufferUtil.integerAsBuffer(Network.get(arg.network).xprivkey) : arg.version, + depth: bufferUtil.integerAsSingleByteBuffer(arg.depth), + parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? bufferUtil.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint, + childIndex: _.isNumber(arg.childIndex) ? bufferUtil.integerAsBuffer(arg.childIndex) : arg.childIndex, + chainCode: _.isString(arg.chainCode) ? bufferUtil.hexToBuffer(arg.chainCode) : arg.chainCode, + privateKey: (_.isString(arg.privateKey) && jsUtil.isHexa(arg.privateKey)) ? bufferUtil.hexToBuffer(arg.privateKey) : arg.privateKey, + checksum: arg.checksum ? (arg.checksum.length ? arg.checksum : bufferUtil.integerAsBuffer(arg.checksum)) : undefined }; return this._buildFromBuffers(buffers); }; @@ -220,8 +223,8 @@ HDPrivateKey.prototype._generateRandomly = function(network) { HDPrivateKey.fromSeed = function(hexa, network) { /* jshint maxcomplexity: 8 */ - if (util.isHexaString(hexa)) { - hexa = util.hexToBuffer(hexa); + if (jsUtil.isHexaString(hexa)) { + hexa = bufferUtil.hexToBuffer(hexa); } if (!Buffer.isBuffer(hexa)) { throw new Error(HDPrivateKey.Errors.InvalidEntropyArg); @@ -269,7 +272,7 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { var sequence = [ arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, - util.emptyBuffer(1), arg.privateKey + bufferUtil.emptyBuffer(1), arg.privateKey ]; if (!arg.checksum || !arg.checksum.length) { arg.checksum = Base58Check.checksum(buffer.Buffer.concat(sequence)); @@ -284,8 +287,8 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { } else { this.xprivkey = arg.xprivkey; } - this.network = Network.get(util.integerFromBuffer(arg.version)); - this.depth = util.integerFromSingleByteBuffer(arg.depth); + this.network = Network.get(bufferUtil.integerFromBuffer(arg.version)); + this.depth = bufferUtil.integerFromSingleByteBuffer(arg.depth); this.privateKey = new PrivateKey(BN().fromBuffer(arg.privateKey)); this.publicKey = this.privateKey.toPublicKey(); @@ -301,7 +304,7 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { HDPrivateKey._validateBufferArguments = function(arg) { var checkBuffer = function(name, size) { var buff = arg[name]; - assert(buffer.Buffer.isBuffer(buff), name + ' argument is not a buffer'); + assert(bufferUtil.isBuffer(buff), name + ' argument is not a buffer'); assert( buff.length === size, name + ' has not the expected size: found ' + buff.length + ', expected ' + size @@ -324,14 +327,14 @@ HDPrivateKey.prototype.toString = function() { HDPrivateKey.prototype.toObject = function() { return { - network: Network.get(util.integerFromBuffer(this._buffers.version)).name, - depth: util.integerFromSingleByteBuffer(this._buffers.depth), - fingerPrint: util.integerFromBuffer(this.fingerPrint), - parentFingerPrint: util.integerFromBuffer(this._buffers.parentFingerPrint), - childIndex: util.integerFromBuffer(this._buffers.childIndex), - chainCode: util.bufferToHex(this._buffers.chainCode), + network: Network.get(bufferUtil.integerFromBuffer(this._buffers.version)).name, + depth: bufferUtil.integerFromSingleByteBuffer(this._buffers.depth), + fingerPrint: bufferUtil.integerFromBuffer(this.fingerPrint), + parentFingerPrint: bufferUtil.integerFromBuffer(this._buffers.parentFingerPrint), + childIndex: bufferUtil.integerFromBuffer(this._buffers.childIndex), + chainCode: bufferUtil.bufferToHex(this._buffers.chainCode), privateKey: this.privateKey.toBuffer().toString('hex'), - checksum: util.integerFromBuffer(this._buffers.checksum), + checksum: bufferUtil.integerFromBuffer(this._buffers.checksum), xprivkey: this.xprivkey }; }; diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index faf5fbd..8f335a1 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -12,8 +12,9 @@ var Point = require('./crypto/point'); var PublicKey = require('./publickey'); var assert = require('assert'); -var buffer = require('buffer'); -var util = require('./util'); + +var jsUtil = require('./util/js'); +var bufferUtil = require('./util/buffer'); function HDPublicKey(arg) { @@ -26,10 +27,10 @@ function HDPublicKey(arg) { return new HDPublicKey(arg); } if (arg) { - if (_.isString(arg) || buffer.Buffer.isBuffer(arg)) { + if (_.isString(arg) || bufferUtil.isBuffer(arg)) { if (HDPublicKey.isValidSerialized(arg)) { return this._buildFromSerialized(arg); - } else if (util.isValidJson(arg)) { + } else if (jsUtil.isValidJson(arg)) { return this._buildFromJson(arg); } else { var error = HDPublicKey.getSerializedError(arg); @@ -73,8 +74,8 @@ HDPublicKey.prototype._deriveWithNumber = function (index, hardened) { return cached; } - var indexBuffer = util.integerAsBuffer(index); - var data = buffer.Buffer.concat([this.publicKey.toBuffer(), indexBuffer]); + var indexBuffer = bufferUtil.integerAsBuffer(index); + var data = bufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); var hash = Hash.sha512hmac(data, this._buffers.chainCode); var leftPart = BN().fromBuffer(hash.slice(0, 32), {size: 32}); var chainCode = hash.slice(32, 64); @@ -140,7 +141,7 @@ HDPublicKey.isValidSerialized = function (data, network) { HDPublicKey.getSerializedError = function (data, network) { /* jshint maxcomplexity: 10 */ /* jshint maxstatements: 20 */ - if (!(_.isString(data) || buffer.Buffer.isBuffer(data))) { + if (!(_.isString(data) || bufferUtil.isBuffer(data))) { return HDPublicKey.Errors.InvalidArgument; } if (!Base58.validCharacters(data)) { @@ -161,7 +162,7 @@ HDPublicKey.getSerializedError = function (data, network) { } } network = Network.get(network) || Network.defaultNetwork; - if (util.integerFromBuffer(data.slice(0, 4)) === network.xprivkey) { + if (bufferUtil.integerFromBuffer(data.slice(0, 4)) === network.xprivkey) { return HDPublicKey.Errors.ArgumentIsPrivateExtended; } return null; @@ -173,7 +174,7 @@ HDPublicKey._validateNetwork = function (data, network) { return HDPublicKey.Errors.InvalidNetworkArgument; } var version = data.slice(HDPublicKey.VersionStart, HDPublicKey.VersionEnd); - if (util.integerFromBuffer(version) !== network.xpubkey) { + if (bufferUtil.integerFromBuffer(version) !== network.xpubkey) { return HDPublicKey.Errors.InvalidNetwork; } return null; @@ -186,8 +187,8 @@ HDPublicKey.prototype._buildFromJson = function (arg) { HDPublicKey.prototype._buildFromPrivate = function (arg) { var args = _.clone(arg._buffers); var point = Point.getG().mul(BN().fromBuffer(args.privateKey)); - args.publicKey = util.pointToCompressed(point); - args.version = util.integerAsBuffer(Network.get(util.integerFromBuffer(args.version)).xpubkey); + args.publicKey = Point.pointToCompressed(point); + args.version = bufferUtil.integerAsBuffer(Network.get(bufferUtil.integerFromBuffer(args.version)).xpubkey); args.privateKey = undefined; args.checksum = undefined; args.xprivkey = undefined; @@ -198,14 +199,14 @@ HDPublicKey.prototype._buildFromObject = function (arg) { /* jshint maxcomplexity: 10 */ // TODO: Type validation var buffers = { - version: arg.network ? util.integerAsBuffer(Network.get(arg.network).xpubkey) : arg.version, - depth: util.integerAsSingleByteBuffer(arg.depth), - parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? util.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint, - childIndex: util.integerAsBuffer(arg.childIndex), - chainCode: _.isString(arg.chainCode) ? util.hexToBuffer(arg.chainCode) : arg.chainCode, - publicKey: _.isString(arg.publicKey) ? util.hexToBuffer(arg.publicKey) : - buffer.Buffer.isBuffer(arg.publicKey) ? arg.publicKey : arg.publicKey.toBuffer(), - checksum: _.isNumber(arg.checksum) ? util.integerAsBuffer(arg.checksum) : arg.checksum + version: arg.network ? bufferUtil.integerAsBuffer(Network.get(arg.network).xpubkey) : arg.version, + depth: bufferUtil.integerAsSingleByteBuffer(arg.depth), + parentFingerPrint: _.isNumber(arg.parentFingerPrint) ? bufferUtil.integerAsBuffer(arg.parentFingerPrint) : arg.parentFingerPrint, + childIndex: bufferUtil.integerAsBuffer(arg.childIndex), + chainCode: _.isString(arg.chainCode) ? bufferUtil.hexToBuffer(arg.chainCode) : arg.chainCode, + publicKey: _.isString(arg.publicKey) ? bufferUtil.hexToBuffer(arg.publicKey) : + bufferUtil.isBuffer(arg.publicKey) ? arg.publicKey : arg.publicKey.toBuffer(), + checksum: _.isNumber(arg.checksum) ? bufferUtil.integerAsBuffer(arg.checksum) : arg.checksum }; return this._buildFromBuffers(buffers); }; @@ -253,7 +254,7 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, arg.publicKey ]; - var concat = buffer.Buffer.concat(sequence); + var concat = bufferUtil.concat(sequence); var checksum = Base58Check.checksum(concat); if (!arg.checksum || !arg.checksum.length) { arg.checksum = checksum; @@ -264,13 +265,13 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { } if (!arg.xpubkey) { - this.xpubkey = Base58Check.encode(buffer.Buffer.concat(sequence)); + this.xpubkey = Base58Check.encode(bufferUtil.concat(sequence)); } else { this.xpubkey = arg.xpubkey; } - this.network = Network.get(util.integerFromBuffer(arg.version)); - this.depth = util.integerFromSingleByteBuffer(arg.depth); + this.network = Network.get(bufferUtil.integerFromBuffer(arg.version)); + this.depth = bufferUtil.integerFromSingleByteBuffer(arg.depth); this.publicKey = PublicKey.fromString(arg.publicKey); this.fingerPrint = Hash.sha256ripemd160(this.publicKey.toBuffer()).slice(0, HDPublicKey.ParentFingerPrintSize); @@ -280,7 +281,7 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { HDPublicKey._validateBufferArguments = function (arg) { var checkBuffer = function(name, size) { var buff = arg[name]; - assert(buffer.Buffer.isBuffer(buff), name + ' argument is not a buffer, it\'s ' + typeof buff); + assert(bufferUtil.isBuffer(buff), name + ' argument is not a buffer, it\'s ' + typeof buff); assert( buff.length === size, name + ' has not the expected size: found ' + buff.length + ', expected ' + size @@ -303,14 +304,14 @@ HDPublicKey.prototype.toString = function () { HDPublicKey.prototype.toObject = function () { return { - network: Network.get(util.integerFromBuffer(this._buffers.version)).name, - depth: util.integerFromSingleByteBuffer(this._buffers.depth), - fingerPrint: util.integerFromBuffer(this.fingerPrint), - parentFingerPrint: util.integerFromBuffer(this._buffers.parentFingerPrint), - childIndex: util.integerFromBuffer(this._buffers.childIndex), - chainCode: util.bufferToHex(this._buffers.chainCode), + network: Network.get(bufferUtil.integerFromBuffer(this._buffers.version)).name, + depth: bufferUtil.integerFromSingleByteBuffer(this._buffers.depth), + fingerPrint: bufferUtil.integerFromBuffer(this.fingerPrint), + parentFingerPrint: bufferUtil.integerFromBuffer(this._buffers.parentFingerPrint), + childIndex: bufferUtil.integerFromBuffer(this._buffers.childIndex), + chainCode: bufferUtil.bufferToHex(this._buffers.chainCode), publicKey: this.publicKey.toString(), - checksum: util.integerFromBuffer(this._buffers.checksum), + checksum: bufferUtil.integerFromBuffer(this._buffers.checksum), xpubkey: this.xpubkey }; }; diff --git a/lib/util.js b/lib/util.js deleted file mode 100644 index d4e8af4..0000000 --- a/lib/util.js +++ /dev/null @@ -1,81 +0,0 @@ -'use strict'; - -var _ = require('lodash'); -var buffer = require('buffer'); -var assert = require('assert'); - -var isHexa = function isHexa(value) { - if (!_.isString(value)) { - return false; - } - return /^[0-9a-fA-F]+$/.test(value); -}; - -var shallowEquals = function(obj1, obj2) { - var keys1 = _.keys(obj1); - var keys2 = _.keys(obj2); - if (_.size(keys1) !== _.size(keys2)) { - return false; - } - var compare = function(key) { return obj1[key] === obj2[key]; }; - return _.all(keys1, compare) && _.all(keys2, compare); -}; - -module.exports = { - shallowEquals: shallowEquals, - isValidJson: function isValidJson(arg) { - try { - JSON.parse(arg); - return true; - } catch (e) { - return false; - } - }, - emptyBuffer: function emptyBuffer(bytes) { - var result = new Buffer(bytes); - for (var i = 0; i < bytes; i++) { - result.write('\0', i); - } - return result; - }, - integerAsSingleByteBuffer: function integerAsSingleByteBuffer(integer) { - return new Buffer([integer & 0xff]); - }, - integerAsBuffer: function integerAsBuffer(integer) { - var bytes = []; - bytes.push((integer >> 24) & 0xff); - bytes.push((integer >> 16) & 0xff); - bytes.push((integer >> 8) & 0xff); - bytes.push(integer & 0xff); - return new Buffer(bytes); - }, - isHexa: isHexa, - isHexaString: isHexa, - - integerFromBuffer: function integerFromBuffer(buffer) { - return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; - }, - integerFromSingleByteBuffer: function integerFromBuffer(buffer) { - return buffer[0]; - }, - bufferToHex: function bufferToHex(buffer) { - return buffer.toString('hex'); - }, - hexToBuffer: function hexToBuffer(string) { - assert(isHexa(string)); - return new buffer.Buffer(string, 'hex'); - }, - pointToCompressed: function pointToCompressed(point) { - var xbuf = point.getX().toBuffer({size: 32}); - var ybuf = point.getY().toBuffer({size: 32}); - - var prefix; - var odd = ybuf[ybuf.length - 1] % 2; - if (odd) { - prefix = new Buffer([0x03]); - } else { - prefix = new Buffer([0x02]); - } - return buffer.Buffer.concat([prefix, xbuf]); - } -}; diff --git a/lib/util/bitcoin.js b/lib/util/bitcoin.js new file mode 100644 index 0000000..821c04d --- /dev/null +++ b/lib/util/bitcoin.js @@ -0,0 +1,18 @@ +/** + * @file util/bitcoin.js + * Contains utilities to handle magnitudes inside of bitcoin + */ +'use strict'; + +var SATOSHIS_PER_BTC = 1e8; + +module.exports = { + /** + * @param number satoshis - amount of satoshis to convert + * @return string an exact representation of such amount, in form of a string + * (avoids duplicate representations in ieee756 of the same number) + */ + satoshisToBitcoin: function(satoshis) { + return satoshis / SATOSHIS_PER_BTC; + } +}; diff --git a/lib/util/buffer.js b/lib/util/buffer.js new file mode 100644 index 0000000..5b9e65a --- /dev/null +++ b/lib/util/buffer.js @@ -0,0 +1,109 @@ +'use strict'; + +var buffer = require('buffer'); +var assert = require('assert'); + +var js = require('./js'); + +module.exports = { + /** + * Returns true if the given argument is an instance of a buffer. Tests for + * both node's Buffer and Uint8Array + * + * @param {*} arg + * @return {boolean} + */ + isBuffer: function isBuffer(arg) { + return buffer.Buffer.isBuffer(arg) || arg instanceof Uint8Array; + }, + + /** + * Returns a zero-filled byte array + * + * @param {number} bytes + * @return {Buffer} + */ + emptyBuffer: function emptyBuffer(bytes) { + var result = new buffer.Buffer(bytes); + for (var i = 0; i < bytes; i++) { + result.write('\0', i); + } + return result; + }, + + /** + * Concatenates a buffer + * + * Shortcut for buffer.Buffer.concat + */ + concat: buffer.Buffer.concat, + + /** + * Transforms a number from 0 to 255 into a Buffer of size 1 with that value + * + * @param {number} integer + * @return {Buffer} + */ + integerAsSingleByteBuffer: function integerAsSingleByteBuffer(integer) { + return new buffer.Buffer([integer & 0xff]); + }, + + /** + * Transform a 4-byte integer into a Buffer of length 4. + * + * @param {number} integer + * @return {Buffer} + */ + integerAsBuffer: function integerAsBuffer(integer) { + var bytes = []; + bytes.push((integer >> 24) & 0xff); + bytes.push((integer >> 16) & 0xff); + bytes.push((integer >> 8) & 0xff); + bytes.push(integer & 0xff); + return new Buffer(bytes); + }, + + /** + * Transform the first 4 values of a Buffer into a number, in little endian encoding + * + * @param {Buffer} buffer + * @return {number} + */ + integerFromBuffer: function integerFromBuffer(buffer) { + return buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3]; + }, + + /** + * Transforms the first byte of an array into a number ranging from -128 to 127 + * @param {Buffer} buffer + * @return {number} + */ + integerFromSingleByteBuffer: function integerFromBuffer(buffer) { + return buffer[0]; + }, + + /** + * Transforms a buffer into a string with a number in hexa representation + * + * Shorthand for buffer.toString('hex') + * + * @param {Buffer} buffer + * @return {string} + */ + bufferToHex: function bufferToHex(buffer) { + return buffer.toString('hex'); + }, + + /** + * Transforms an hexa encoded string into a Buffer with binary values + * + * Shorthand for Buffer(string, 'hex') + * + * @param {string} string + * @return {Buffer} + */ + hexToBuffer: function hexToBuffer(string) { + assert(js.isHexa(string)); + return new buffer.Buffer(string, 'hex'); + } +}; diff --git a/lib/util/js.js b/lib/util/js.js new file mode 100644 index 0000000..a4b2e3a --- /dev/null +++ b/lib/util/js.js @@ -0,0 +1,35 @@ +'use strict'; + +var _ = require('lodash'); + +/** + * Determines whether a string contains only hexadecimal values + * + * @param {string} value + * @return {boolean} true if the string is the hexa representation of a number + */ +var isHexa = function isHexa(value) { + if (!_.isString(value)) { + return false; + } + return /^[0-9a-fA-F]+$/.test(value); +}; + +module.exports = { + /** + * Test if an argument is a valid JSON object. If it is, returns a truthy + * value (the json object decoded), so no double JSON.parse call is necessary + * + * @param {string} arg + * @return {Object|boolean} false if the argument is not a JSON string. + */ + isValidJson: function isValidJson(arg) { + try { + return JSON.parse(arg); + } catch (e) { + return false; + } + }, + isHexa: isHexa, + isHexaString: isHexa +}; diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index bba1d9c..e40a0a3 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -6,7 +6,7 @@ var should = require('chai').should(); var expect = require('chai').expect; var bitcore = require('..'); var buffer = require('buffer'); -var util = bitcore.util; +var bufferUtil = bitcore.util.buffer; var HDPrivateKey = bitcore.HDPrivateKey; var Base58Check = bitcore.encoding.Base58Check; @@ -41,7 +41,7 @@ describe('HDPrivate key interface', function() { }); it('builds a json keeping the structure and same members', function() { - assert(util.shallowEquals( + assert(_.isEqual( JSON.parse(new HDPrivateKey(json).toJson()), JSON.parse(new HDPrivateKey(xprivkey).toJson()) )); @@ -130,7 +130,7 @@ describe('HDPrivate key interface', function() { var privKey = new HDPrivateKey(xprivkey); expect(function() { var buffers = privKey._buffers; - buffers.checksum = util.integerAsBuffer(0); + buffers.checksum = bufferUtil.integerAsBuffer(0); return new HDPrivateKey(buffers); }).to.throw(HDPrivateKey.Errors.InvalidB58Checksum); }); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index 219eaee..a9e150f 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -7,7 +7,7 @@ var should = require('chai').should(); var expect = require('chai').expect; var bitcore = require('..'); var buffer = require('buffer'); -var util = bitcore.util; +var bufferUtil = bitcore.util.buffer; var HDPrivateKey = bitcore.HDPrivateKey; var HDPublicKey = bitcore.HDPublicKey; var Base58Check = bitcore.encoding.Base58Check; @@ -71,7 +71,7 @@ describe('HDPublicKey interface', function() { }); it('can generate a json that has a particular structure', function() { - assert(util.shallowEquals( + assert(_.isEqual( JSON.parse(new HDPublicKey(json).toJson()), JSON.parse(new HDPublicKey(xpubkey).toJson()) )); @@ -83,7 +83,7 @@ describe('HDPublicKey interface', function() { it('checks the checksum', function() { var buffers = new HDPublicKey(xpubkey)._buffers; - buffers.checksum = util.integerAsBuffer(1); + buffers.checksum = bufferUtil.integerAsBuffer(1); expectFail(buffers, HDPublicKey.Errors.InvalidB58Checksum)(); });