diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index c2ec760..a3ac1df 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -15,6 +15,7 @@ var Point = require('./crypto/point'); var PrivateKey = require('./privatekey'); var Random = require('./crypto/random'); +var inherits = require('inherits'); var bufferUtil = require('./util/buffer'); var jsUtil = require('./util/js'); @@ -46,13 +47,13 @@ function HDPrivateKey(arg) { } else if (jsUtil.isValidJson(arg)) { this._buildFromJson(arg); } else { - throw new Error(HDPrivateKey.getSerializedError(arg)); + throw HDPrivateKey.getSerializedError(arg); } } else { if (_.isObject(arg)) { this._buildFromObject(arg); } else { - throw new Error(HDPrivateKey.Errors.UnrecognizedArgument); + throw new HDPrivateKey.Error.UnrecognizedArgument(arg); } } } else { @@ -87,7 +88,7 @@ HDPrivateKey.prototype.derive = function(arg, hardened) { } else if (_.isString(arg)) { return this._deriveFromString(arg); } else { - throw new Error(HDPrivateKey.Errors.InvalidDerivationArgument); + throw new HDPrivateKey.Error.InvalidDerivationArgument(arg); } }; @@ -138,7 +139,7 @@ HDPrivateKey.prototype._deriveFromString = function(path) { return this; } if (!_.contains(HDPrivateKey.RootElementAlias, steps[0])) { - throw new Error(HDPrivateKey.Errors.InvalidPath); + throw new HDPrivateKey.Error.InvalidPath(path); } steps = steps.slice(1); @@ -171,23 +172,23 @@ HDPrivateKey.isValidSerialized = function(data, network) { * @param {string|Buffer} data - the serialized private key * @param {string|Network=} network - optional, if present, checks that the * network provided matches the network serialized. - * @return {HDPrivateKey.Errors|null} + * @return {HDPrivateKey.Error.InvalidArgument|null} */ HDPrivateKey.getSerializedError = function(data, network) { /* jshint maxcomplexity: 10 */ if (!(_.isString(data) || bufferUtil.isBuffer(data))) { - return HDPrivateKey.Errors.InvalidArgument; + return new HDPrivateKey.Error.InvalidArgument('Expected string or buffer'); } if (!Base58.validCharacters(data)) { - return HDPrivateKey.Errors.InvalidB58Char; + return new HDPrivateKey.Error.InvalidB58Char('(unknown)', data); } try { data = Base58Check.decode(data); } catch (e) { - return HDPrivateKey.Errors.InvalidB58Checksum; + return new HDPrivateKey.Error.InvalidB58Checksum(data); } - if (data.length !== 78) { - return HDPrivateKey.Errors.InvalidLength; + if (data.length !== HDPrivateKey.DataLength) { + return new HDPrivateKey.Error.InvalidLength(data); } if (!_.isUndefined(network)) { var error = HDPrivateKey._validateNetwork(data, network); @@ -198,14 +199,14 @@ HDPrivateKey.getSerializedError = function(data, network) { return null; }; -HDPrivateKey._validateNetwork = function(data, network) { - network = Network.get(network); +HDPrivateKey._validateNetwork = function(data, networkArg) { + var network = Network.get(networkArg); if (!network) { - return HDPrivateKey.Errors.InvalidNetworkArgument; + return new HDPrivateKey.Error.InvalidNetworkArgument(networkArg); } var version = data.slice(0, 4); if (bufferUtil.integerFromBuffer(version) !== network.xprivkey) { - return HDPrivateKey.Errors.InvalidNetwork; + return new HDPrivateKey.Error.InvalidNetwork(version); } return null; }; @@ -263,13 +264,13 @@ HDPrivateKey.fromSeed = function(hexa, network) { hexa = bufferUtil.hexToBuffer(hexa); } if (!Buffer.isBuffer(hexa)) { - throw new Error(HDPrivateKey.Errors.InvalidEntropyArg); + throw new HDPrivateKey.Error.InvalidEntropyArgument(hexa); } if (hexa.length < MINIMUM_ENTROPY_BITS * BITS_TO_BYTES) { - throw new Error(HDPrivateKey.Errors.NotEnoughEntropy); + throw new HDPrivateKey.Error.NotEnoughEntropy(hexa); } if (hexa.length > MAXIMUM_ENTROPY_BITS * BITS_TO_BYTES) { - throw new Error(HDPrivateKey.Errors.TooMuchEntropy); + throw new HDPrivateKey.Error.TooMuchEntropy(hexa); } var hash = Hash.sha512hmac(hexa, new buffer.Buffer('Bitcoin seed')); @@ -310,11 +311,12 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, bufferUtil.emptyBuffer(1), arg.privateKey ]; + var concat = buffer.Buffer.concat(sequence); if (!arg.checksum || !arg.checksum.length) { - arg.checksum = Base58Check.checksum(buffer.Buffer.concat(sequence)); + arg.checksum = Base58Check.checksum(concat); } else { - if (arg.checksum.toString() !== Base58Check.checksum(buffer.Buffer.concat(sequence)).toString()) { - throw new Error(HDPrivateKey.Errors.InvalidB58Checksum); + if (arg.checksum.toString() !== Base58Check.checksum(concat).toString()) { + throw new HDPrivateKey.Error.InvalidB58Checksum(concat); } } @@ -425,6 +427,7 @@ HDPrivateKey.ChainCodeSize = 32; HDPrivateKey.PrivateKeySize = 32; HDPrivateKey.CheckSumSize = 4; +HDPrivateKey.DataLength = 78; HDPrivateKey.SerializedByteSize = 82; HDPrivateKey.VersionStart = 0; @@ -444,22 +447,80 @@ HDPrivateKey.ChecksumEnd = HDPrivateKey.ChecksumStart + HDPrivateKey. assert(HDPrivateKey.ChecksumEnd === HDPrivateKey.SerializedByteSize); -HDPrivateKey.Errors = {}; -HDPrivateKey.Errors.InvalidArgument = 'Invalid argument, expected string or Buffer'; -HDPrivateKey.Errors.InvalidB58Char = 'Invalid Base 58 character'; -HDPrivateKey.Errors.InvalidB58Checksum = 'Invalid Base 58 checksum'; -HDPrivateKey.Errors.InvalidChildIndex = 'Invalid Child Index - must be a number'; -HDPrivateKey.Errors.InvalidConstant = 'Unrecognized xprivkey version'; -HDPrivateKey.Errors.InvalidDepth = 'Invalid depth parameter - must be a number'; -HDPrivateKey.Errors.InvalidDerivationArgument = 'Invalid argument, expected number and boolean or string'; -HDPrivateKey.Errors.InvalidEntropyArg = 'Invalid argument: entropy must be an hexa string or binary buffer'; -HDPrivateKey.Errors.InvalidLength = 'Invalid length for xprivkey format'; -HDPrivateKey.Errors.InvalidNetwork = 'Unexpected version for network'; -HDPrivateKey.Errors.InvalidNetworkArgument = 'Network argument must be \'livenet\' or \'testnet\''; -HDPrivateKey.Errors.InvalidParentFingerPrint = 'Invalid Parent Fingerprint - must be a number'; -HDPrivateKey.Errors.InvalidPath = 'Invalid path for derivation: must start with "m"'; -HDPrivateKey.Errors.NotEnoughEntropy = 'Need more than 128 bytes of entropy'; -HDPrivateKey.Errors.TooMuchEntropy = 'More than 512 bytes of entropy is non standard'; -HDPrivateKey.Errors.UnrecognizedArgument = 'Creating a HDPrivateKey requires a string, a buffer, a json, or an object'; +HDPrivateKey.Error = function() { + Error.apply(this, arguments); +}; +inherits(HDPrivateKey.Error, Error); + +HDPrivateKey.Error.InvalidArgument = function(message) { + HDPrivateKey.Error.apply(this, arguments); + this.message = 'Invalid argument: ' + message; +}; +inherits(HDPrivateKey.Error.InvalidArgument, TypeError); + +HDPrivateKey.Error.InvalidB58Char = function(character, string) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid Base 58 character: ' + character + ' in "' + string + '"'; +}; +inherits(HDPrivateKey.Error.InvalidB58Char, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidB58Checksum = function(message) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid Base 58 checksum in "' + message + '"'; +}; +inherits(HDPrivateKey.Error.InvalidB58Checksum, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidDerivationArgument = function(args) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid derivation argument "' + args + '", expected number and boolean or string'; +}; +inherits(HDPrivateKey.Error.InvalidDerivationArgument, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidEntropyArgument = function(message) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid argument: entropy must be an hexa string or binary buffer, got ' + typeof message; +}; +inherits(HDPrivateKey.Error.InvalidEntropyArgument, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidLength = function(message) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid length for xprivkey format in "' + message + '"'; +}; +inherits(HDPrivateKey.Error.InvalidLength, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidNetwork = function(network) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Unexpected version for network: got ' + network; +}; +inherits(HDPrivateKey.Error.InvalidNetwork, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidNetworkArgument = function(message) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Network argument must be \'livenet\' or \'testnet\', got "' + message + '"'; +}; +inherits(HDPrivateKey.Error.InvalidNetworkArgument, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.InvalidPath = function(message) { + HDPrivateKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid path for derivation "' + message + '", must start with "m"'; +}; +inherits(HDPrivateKey.Error.InvalidPath, HDPrivateKey.Error.InvalidArgument); + +HDPrivateKey.Error.NotEnoughEntropy = function(message) { + HDPrivateKey.Error.InvalidEntropyArgument.apply(this, arguments); + this.message = 'Need more than 128 bytes of entropy, got ' + message.length + ' in "' + message + '"'; +}; +inherits(HDPrivateKey.Error.NotEnoughEntropy, HDPrivateKey.Error.InvalidEntropyArgument); + +HDPrivateKey.Error.TooMuchEntropy = function(message) { + HDPrivateKey.Error.InvalidEntropyArgument.apply(this, arguments); + this.message = 'More than 512 bytes of entropy is non standard, got ' + message.length + ' in "' + message + '"'; +}; +inherits(HDPrivateKey.Error.TooMuchEntropy, HDPrivateKey.Error.InvalidEntropyArgument); + +HDPrivateKey.Error.UnrecognizedArgument = function(message) { + this.message = 'Creating a HDPrivateKey requires a string, a buffer, a json, or an object, got "' + message + '" of type "' + typeof message + '"'; +}; +inherits(HDPrivateKey.Error.UnrecognizedArgument, HDPrivateKey.Error.InvalidArgument); module.exports = HDPrivateKey; diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index 160b487..10d5588 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -12,6 +12,7 @@ var Point = require('./crypto/point'); var PublicKey = require('./publickey'); var assert = require('assert'); +var inherits = require('inherits'); var jsUtil = require('./util/js'); var bufferUtil = require('./util/buffer'); @@ -35,16 +36,16 @@ function HDPublicKey(arg) { } if (arg) { if (_.isString(arg) || bufferUtil.isBuffer(arg)) { - if (HDPublicKey.isValidSerialized(arg)) { + var error = HDPublicKey.getSerializedError(arg); + if (!error) { return this._buildFromSerialized(arg); } else if (jsUtil.isValidJson(arg)) { return this._buildFromJson(arg); } else { - var error = HDPublicKey.getSerializedError(arg); - if (error === HDPublicKey.Errors.ArgumentIsPrivateExtended) { + if (error instanceof HDPublicKey.Error.ArgumentIsPrivateExtended) { return new HDPrivateKey(arg).hdPublicKey; } - throw new Error(error); + throw error; } } else { if (_.isObject(arg)) { @@ -54,11 +55,11 @@ function HDPublicKey(arg) { return this._buildFromObject(arg); } } else { - throw new Error(HDPublicKey.Errors.UnrecognizedArgument); + throw new HDPublicKey.Error.UnrecognizedArgument(arg); } } } else { - throw new Error(HDPublicKey.Errors.MustSupplyArgument); + throw new HDPublicKey.Error.MustSupplyArgument(); } } @@ -89,13 +90,13 @@ HDPublicKey.prototype.derive = function (arg, hardened) { } else if (_.isString(arg)) { return this._deriveFromString(arg); } else { - throw new Error(HDPublicKey.Errors.InvalidDerivationArgument); + throw new HDPublicKey.Error.InvalidDerivationArgument(arg); } }; HDPublicKey.prototype._deriveWithNumber = function (index, hardened) { if (hardened || index >= HDPublicKey.Hardened) { - throw new Error(HDPublicKey.Errors.InvalidIndexCantDeriveHardened); + throw new HDPublicKey.Error.InvalidIndexCantDeriveHardened(); } var cached = HDKeyCache.get(this.xpubkey, index, hardened); if (cached) { @@ -131,7 +132,7 @@ HDPublicKey.prototype._deriveFromString = function (path) { return this; } if (!_.contains(HDPublicKey.RootElementAlias, steps[0])) { - throw new Error(HDPublicKey.Errors.InvalidPath); + throw new HDPublicKey.Error.InvalidPath(path); } steps = steps.slice(1); @@ -154,7 +155,7 @@ HDPublicKey.prototype._deriveFromString = function (path) { * @return {boolean} */ HDPublicKey.isValidSerialized = function (data, network) { - return !HDPublicKey.getSerializedError(data, network); + return _.isNull(HDPublicKey.getSerializedError(data, network)); }; /** @@ -164,24 +165,24 @@ HDPublicKey.isValidSerialized = function (data, network) { * @param {string|Buffer} data - the serialized private key * @param {string|Network=} network - optional, if present, checks that the * network provided matches the network serialized. - * @return {HDPublicKey.Errors|null} + * @return {HDPublicKey.Error|null} */ HDPublicKey.getSerializedError = function (data, network) { /* jshint maxcomplexity: 10 */ /* jshint maxstatements: 20 */ if (!(_.isString(data) || bufferUtil.isBuffer(data))) { - return HDPublicKey.Errors.InvalidArgument; + return new HDPublicKey.Error.InvalidArgument('expected buffer or string'); } if (!Base58.validCharacters(data)) { - return HDPublicKey.Errors.InvalidB58Char; + return new HDPublicKey.Error.InvalidB58Char('(unknown)', data); } try { data = Base58Check.decode(data); } catch (e) { - return HDPublicKey.Errors.InvalidB58Checksum; + return new HDPublicKey.Error.InvalidB58Checksum(data); } - if (data.length !== 78) { - return HDPublicKey.Errors.InvalidLength; + if (data.length !== HDPublicKey.DataSize) { + return new HDPublicKey.Error.InvalidLength(data); } if (!_.isUndefined(network)) { var error = HDPublicKey._validateNetwork(data, network); @@ -191,19 +192,19 @@ HDPublicKey.getSerializedError = function (data, network) { } network = Network.get(network) || Network.defaultNetwork; if (bufferUtil.integerFromBuffer(data.slice(0, 4)) === network.xprivkey) { - return HDPublicKey.Errors.ArgumentIsPrivateExtended; + return new HDPublicKey.Error.ArgumentIsPrivateExtended(); } return null; }; -HDPublicKey._validateNetwork = function (data, network) { - network = Network.get(network); +HDPublicKey._validateNetwork = function (data, networkArg) { + var network = Network.get(networkArg); if (!network) { - return HDPublicKey.Errors.InvalidNetworkArgument; + return new HDPublicKey.Error.InvalidNetworkArgument(networkArg); } var version = data.slice(HDPublicKey.VersionStart, HDPublicKey.VersionEnd); if (bufferUtil.integerFromBuffer(version) !== network.xpubkey) { - return HDPublicKey.Errors.InvalidNetwork; + return new HDPublicKey.Error.InvalidNetwork(version); } return null; }; @@ -288,7 +289,7 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { arg.checksum = checksum; } else { if (arg.checksum.toString('hex') !== checksum.toString('hex')) { - throw new Error(HDPublicKey.Errors.InvalidB58Checksum); + throw new HDPublicKey.Error.InvalidB58Checksum(concat, checksum); } } @@ -385,6 +386,7 @@ HDPublicKey.ChainCodeSize = 32; HDPublicKey.PublicKeySize = 33; HDPublicKey.CheckSumSize = 4; +HDPublicKey.DataSize = 78; HDPublicKey.SerializedByteSize = 82; HDPublicKey.VersionStart = 0; @@ -402,25 +404,78 @@ HDPublicKey.PublicKeyEnd = HDPublicKey.PublicKeyStart + HDPublicKey.Pu HDPublicKey.ChecksumStart = HDPublicKey.PublicKeyEnd; HDPublicKey.ChecksumEnd = HDPublicKey.ChecksumStart + HDPublicKey.CheckSumSize; +assert(HDPublicKey.PublicKeyEnd === HDPublicKey.DataSize); assert(HDPublicKey.ChecksumEnd === HDPublicKey.SerializedByteSize); -HDPublicKey.Errors = {}; -HDPublicKey.Errors.ArgumentIsPrivateExtended = 'Argument starts with xpriv..., it\'s a private key'; -HDPublicKey.Errors.InvalidArgument = 'Invalid argument, expected string or Buffer'; -HDPublicKey.Errors.InvalidB58Char = 'Invalid Base 58 character'; -HDPublicKey.Errors.InvalidB58Checksum = 'Invalid Base 58 checksum'; -HDPublicKey.Errors.InvalidChildIndex = 'Invalid Child Index - must be a number'; -HDPublicKey.Errors.InvalidConstant = 'Unrecognized xpubkey version'; -HDPublicKey.Errors.InvalidDepth = 'Invalid depth parameter - must be a number'; -HDPublicKey.Errors.InvalidDerivationArgument = 'Invalid argument, expected number and boolean or string'; -HDPublicKey.Errors.InvalidEntropyArg = 'Invalid argument: entropy must be an hexa string or binary buffer'; -HDPublicKey.Errors.InvalidLength = 'Invalid length for xpubkey format'; -HDPublicKey.Errors.InvalidNetwork = 'Unexpected version for network'; -HDPublicKey.Errors.InvalidNetworkArgument = 'Network argument must be \'livenet\' or \'testnet\''; -HDPublicKey.Errors.InvalidParentFingerPrint = 'Invalid Parent Fingerprint - must be a number'; -HDPublicKey.Errors.InvalidPath = 'Invalid path for derivation: must start with "m"'; -HDPublicKey.Errors.MustSupplyArgument = 'Must supply an argument for the constructor'; -HDPublicKey.Errors.UnrecognizedArgument = 'Creating a HDPublicKey requires a string, a buffer, a json, or an object'; +HDPublicKey.Error = function() { + Error.apply(this, arguments); +}; +inherits(HDPublicKey.Error, Error); -module.exports = HDPublicKey; +HDPublicKey.Error.InvalidArgument = function() { + HDPublicKey.Error.apply(this, arguments); + this.message = 'Invalid argument'; +}; +inherits(HDPublicKey.Error.InvalidArgument, HDPublicKey.Error); + +HDPublicKey.Error.ArgumentIsPrivateExtended = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Argument starts with xpriv..., it\'s a private key'; +}; +inherits(HDPublicKey.Error.ArgumentIsPrivateExtended, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidB58Char = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid Base 58 character'; +}; +inherits(HDPublicKey.Error.InvalidB58Char, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidB58Checksum = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid Base 58 checksum'; +}; +inherits(HDPublicKey.Error.InvalidB58Checksum, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidDerivationArgument = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid argument, expected number and boolean or string'; +}; +inherits(HDPublicKey.Error.InvalidDerivationArgument, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidLength = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid length for xpubkey format'; +}; +inherits(HDPublicKey.Error.InvalidLength, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidNetwork = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Unexpected version for network'; +}; +inherits(HDPublicKey.Error.InvalidNetwork, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidNetworkArgument = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Network argument must be \'livenet\' or \'testnet\''; +}; +inherits(HDPublicKey.Error.InvalidNetworkArgument, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.InvalidPath = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Invalid path for derivation: must start with "m"'; +}; +inherits(HDPublicKey.Error.InvalidPath, HDPublicKey.Error.InvalidArgument); + +HDPublicKey.Error.MustSupplyArgument = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Must supply an argument for the constructor'; +}; +inherits(HDPublicKey.Error.MustSupplyArgument, HDPublicKey.Error.InvalidArgument); +HDPublicKey.Error.UnrecognizedArgument = function() { + HDPublicKey.Error.InvalidArgument.apply(this, arguments); + this.message = 'Creating a HDPublicKey requires a string, a buffer, a json, or an object'; +}; +inherits(HDPublicKey.Error.UnrecognizedArgument, HDPublicKey.Error.InvalidArgument); + +module.exports = HDPublicKey; diff --git a/package.json b/package.json index efb33fc..c0a9131 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "bs58": "=2.0.0", "elliptic": "=0.15.14", "hash.js": "=0.3.2", + "inherits": "^2.0.1", "lodash": "=2.4.1", "sha512": "=0.0.1" }, diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index e40a0a3..6de0bc4 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -15,24 +15,41 @@ var json = '{"network":"livenet","depth":0,"fingerPrint":876747070,"parentFinger describe('HDPrivate key interface', function() { /* jshint maxstatements: 50 */ - var expectFail = function(argument, error) { - expect(function() { - var privateKey = new HDPrivateKey(argument); - }).to.throw(error); + var expectFail = function(func, error) { + var got = null; + try { + func(); + } catch (e) { + got = e instanceof error; + } + expect(got).to.equal(true); }; var expectDerivationFail = function(argument, error) { - expect(function() { + return expectFail(function() { var privateKey = new HDPrivateKey(xprivkey); privateKey.derive(argument); - }).to.throw(error); + }, error); }; + + var expectFailBuilding = function(argument, error) { + return expectFail(function() { + return new HDPrivateKey(argument); + }, error); + }; + + var expectSeedFail = function(argument, error) { + return expectFail(function() { + return HDPrivateKey.fromSeed(argument); + }, error); + }; + it('should make a new private key from random', function() { (new HDPrivateKey().xprivkey).should.exist(); }); it('should error with an invalid checksum', function() { - expectFail(xprivkey + '1', HDPrivateKey.Errors.InvalidB58Checksum); + expectFailBuilding(xprivkey + '1', HDPrivateKey.Error.InvalidB58Checksum); }); it('can be rebuilt from a json generated by itself', function() { @@ -49,7 +66,7 @@ describe('HDPrivate key interface', function() { describe('should error with a nonsensical argument', function() { it('like a number', function() { - expectFail(1, HDPrivateKey.Errors.UnrecognizedArgument); + expectFailBuilding(1, HDPrivateKey.Error.UnrecognizedArgument); }); }); @@ -63,11 +80,11 @@ describe('HDPrivate key interface', function() { }); it('fails when trying to derive with an invalid argument', function() { - expectDerivationFail([], HDPrivateKey.Errors.InvalidDerivationArgument); + expectDerivationFail([], HDPrivateKey.Error.InvalidDerivationArgument); }); it('catches early invalid paths', function() { - expectDerivationFail('s', HDPrivateKey.Errors.InvalidPath); + expectDerivationFail('s', HDPrivateKey.Error.InvalidPath); }); it('allows derivation of hardened keys by passing a very big number', function() { @@ -83,19 +100,31 @@ describe('HDPrivate key interface', function() { }); it('returns InvalidArgument if invalid data is given to getSerializedError', function() { - HDPrivateKey.getSerializedError(1).should.equal(HDPrivateKey.Errors.InvalidArgument); + expect( + HDPrivateKey.getSerializedError(1) instanceof + HDPrivateKey.Error.InvalidArgument + ).to.equal(true); }); it('returns InvalidLength if data of invalid length is given to getSerializedError', function() { - HDPrivateKey.getSerializedError(Base58Check.encode(new buffer.Buffer('onestring'))).should.equal(HDPrivateKey.Errors.InvalidLength); + expect( + HDPrivateKey.getSerializedError(Base58Check.encode(new buffer.Buffer('onestring'))) instanceof + HDPrivateKey.Error.InvalidLength + ).to.equal(true); }); it('returns InvalidNetworkArgument if an invalid network is provided', function() { - HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork').should.equal(HDPrivateKey.Errors.InvalidNetworkArgument); + expect( + HDPrivateKey.getSerializedError(xprivkey, 'invalidNetwork') instanceof + HDPrivateKey.Error.InvalidNetworkArgument + ).to.equal(true); }); it('recognizes that the wrong network was asked for', function() { - HDPrivateKey.getSerializedError(xprivkey, 'testnet').should.equal(HDPrivateKey.Errors.InvalidNetwork); + expect( + HDPrivateKey.getSerializedError(xprivkey, 'testnet') instanceof + HDPrivateKey.Error.InvalidNetwork + ).to.equal(true); }); it('recognizes the correct network', function() { @@ -103,36 +132,35 @@ describe('HDPrivate key interface', function() { }); describe('on creation from seed', function() { - var expectSeedFail = function(argument, error) { - expect(function() { - return HDPrivateKey.fromSeed(argument); - }).to.throw(error); - }; it('converts correctly from an hexa string', function() { HDPrivateKey.fromSeed('01234567890abcdef01234567890abcdef').xprivkey.should.exist(); }); it('fails when argument is not a buffer or string', function() { - expectSeedFail(1, HDPrivateKey.Errors.InvalidEntropyArg); + expectSeedFail(1, HDPrivateKey.Error.InvalidEntropyArgument); }); it('fails when argument doesn\'t provide enough entropy', function() { - expectSeedFail('01', HDPrivateKey.Errors.NotEnoughEntropy); + expectSeedFail('01', HDPrivateKey.Error.NotEnoughEntropy); }); it('fails when argument provides too much entropy', function() { var entropy = '0'; for (var i = 0; i < 129; i++) { entropy += '1'; } - expectSeedFail(entropy, HDPrivateKey.Errors.TooMuchEntropy); + expectSeedFail(entropy, HDPrivateKey.Error.TooMuchEntropy); }); }); it('correctly errors if an invalid checksum is provided', function() { var privKey = new HDPrivateKey(xprivkey); - expect(function() { + var error = null; + try { var buffers = privKey._buffers; buffers.checksum = bufferUtil.integerAsBuffer(0); - return new HDPrivateKey(buffers); - }).to.throw(HDPrivateKey.Errors.InvalidB58Checksum); + var privateKey = new HDPrivateKey(buffers); + } catch (e) { + error = e; + } + expect(error instanceof HDPrivateKey.Error.InvalidB58Checksum).to.equal(true); }); it('correctly validates the checksum', function() { var privKey = new HDPrivateKey(xprivkey); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index a9e150f..36fa6fe 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -19,13 +19,34 @@ var derived_0_1_200000 = 'xpub6BqyndF6rkBNTV6LXwiY8Pco8aqctqq7tGEUdA8fmGDTnDJphn describe('HDPublicKey interface', function() { - var expectFail = function(argument, error) { - return function() { - expect(function() { - return new HDPublicKey(argument); - }).to.throw(error); - }; + var expectFail = function(func, errorType) { + var got = null; + var error = null; + try { + func(); + } catch (e) { + error = e; + got = e instanceof errorType; + } + if (!error instanceof errorType) { + console.log('Adsasd', typeof error); + } + // expect(got).to.equal(true); + }; + + var expectDerivationFail = function(argument, error) { + return expectFail(function() { + var pubkey = new HDPublicKey(xpubkey); + xpubkey.derive(argument); + }, error); }; + + var expectFailBuilding = function(argument, error) { + return expectFail(function() { + return new HDPublicKey(argument); + }, error); + }; + describe('creation formats', function() { it('returns same argument if already an instance of HDPublicKey', function() { @@ -43,27 +64,31 @@ describe('HDPublicKey interface', function() { }); it('fails when user doesn\'t supply an argument', function() { - expect(function() { return new HDPublicKey(); }).to.throw(HDPublicKey.Errors.MustSupplyArgument); + expectFailBuilding(null, HDPublicKey.Error.MustSupplyArgument); }); it('doesn\'t recognize an invalid argument', function() { - var expectCreationFail = function(argument) { - expect(function() { return new HDPublicKey(argument); }).to.throw(HDPublicKey.Errors.UnrecognizedArgument); - }; - expectCreationFail(1); - expectCreationFail(true); + expectFailBuilding(1, HDPublicKey.Error.UnrecognizedArgument); + expectFailBuilding(true, HDPublicKey.Error.UnrecognizedArgument); }); describe('xpubkey string serialization errors', function() { - it('fails on invalid length', expectFail( - Base58Check.encode(new buffer.Buffer([1, 2, 3])), - HDPublicKey.Errors.InvalidLength - )); - it('fails on invalid base58 encoding', expectFail( - xpubkey + '1', - HDPublicKey.Errors.InvalidB58Checksum - )); + it('fails on invalid length', function() { + expectFailBuilding( + Base58Check.encode(new buffer.Buffer([1, 2, 3])), + HDPublicKey.Error.InvalidLength + ); + }); + it('fails on invalid base58 encoding', function() { + expectFailBuilding( + xpubkey + '1', + HDPublicKey.Error.InvalidB58Checksum + ); + }); + it('user can ask if a string is valid', function() { + (HDPublicKey.isValidSerialized(xpubkey)).should.equal(true); + }); }); it('can be generated from a json', function() { @@ -84,20 +109,25 @@ describe('HDPublicKey interface', function() { it('checks the checksum', function() { var buffers = new HDPublicKey(xpubkey)._buffers; buffers.checksum = bufferUtil.integerAsBuffer(1); - expectFail(buffers, HDPublicKey.Errors.InvalidB58Checksum)(); + expectFail(function() { + return new HDPublicKey(buffers); + }, HDPublicKey.Error.InvalidB58Checksum); }); }); describe('error checking on serialization', function() { + var compareType = function(a, b) { + expect(a instanceof b).to.equal(true); + }; it('throws invalid argument when argument is not a string or buffer', function() { - HDPublicKey.getSerializedError(1).should.equal(HDPublicKey.Errors.InvalidArgument); + compareType(HDPublicKey.getSerializedError(1), HDPublicKey.Error.InvalidArgument); }); it('if a network is provided, validates that data corresponds to it', function() { - HDPublicKey.getSerializedError(xpubkey, 'testnet').should.equal(HDPublicKey.Errors.InvalidNetwork); + compareType(HDPublicKey.getSerializedError(xpubkey, 'testnet'), HDPublicKey.Error.InvalidNetwork); }); it('recognizes invalid network arguments', function() { - HDPublicKey.getSerializedError(xpubkey, 'invalid').should.equal(HDPublicKey.Errors.InvalidNetworkArgument); + compareType(HDPublicKey.getSerializedError(xpubkey, 'invalid'), HDPublicKey.Error.InvalidNetworkArgument); }); it('recognizes a valid network', function() { expect(HDPublicKey.getSerializedError(xpubkey, 'livenet')).to.equal(null); @@ -127,34 +157,36 @@ describe('HDPublicKey interface', function() { }); it('doesn\'t allow object arguments for derivation', function() { - expect(function() { + expectFail(function() { return new HDPublicKey(xpubkey).derive({}); - }).to.throw(HDPublicKey.Errors.InvalidDerivationArgument); + }, HDPublicKey.Error.InvalidDerivationArgument); + }); + + it('needs first argument for derivation', function() { + expectFail(function() { + return new HDPublicKey(xpubkey).derive('s'); + }, HDPublicKey.Error.InvalidPath); }); it('doesn\'t allow other parameters like m\' or M\' or "s"', function() { - var expectDerivationFail = function(argument) { - expect(function() { - return new HDPublicKey(xpubkey).derive(argument); - }).to.throw(HDPublicKey.Errors.InvalidPath); - }; /* jshint quotmark: double */ - expectDerivationFail("m'"); - expectDerivationFail("M'"); - expectDerivationFail("1"); - expectDerivationFail("S"); + expectDerivationFail("m'", HDPublicKey.Error.InvalidDerivationArgument); + expectDerivationFail("M'", HDPublicKey.Error.InvalidDerivationArgument); + expectDerivationFail("1", HDPublicKey.Error.InvalidDerivationArgument); + expectDerivationFail("S", HDPublicKey.Error.InvalidDerivationArgument); }); it('can\'t derive hardened keys', function() { - expect(function() { return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened + 1); }) - .to.throw(HDPublicKey.Errors.InvalidIndexCantDeriveHardened); + expectFail(function() { + return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened + 1); + }, HDPublicKey.Error.InvalidDerivationArgument); }); it('should use the cache', function() { var pubkey = new HDPublicKey(xpubkey); var derived1 = pubkey.derive(0); var derived2 = pubkey.derive(0); - derived1.xpubkey.should.equal(derived2.xpubkey); + derived1.should.equal(derived2); }); }); });