From 48f8edbe804cd9ff9d5dffb129fc65bf438062de Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Mon, 16 May 2016 12:36:52 -0700 Subject: [PATCH] hd testing methods. --- lib/bcoin/hd.js | 124 +++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 53 deletions(-) diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index a7817636..ce0f85b0 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -139,7 +139,8 @@ Mnemonic.prototype.toSeed = function toSeed() { this.seed = utils.pbkdf2( nfkd(this.phrase), nfkd('mnemonic' + this.passphrase), - 2048, 64); + 2048, + 64); return this.seed; }; @@ -560,34 +561,24 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) { * @throws Error if key is not a master key. */ -HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) { - var coinType, accountIndex, child; +HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(accountIndex) { + var coinType; - if (typeof options === 'number') - options = { accountIndex: options }; - - coinType = options.coinType; - accountIndex = options.accountIndex; + assert(utils.isNumber(accountIndex), 'accountIndex must be a number.'); if (this instanceof HDPublicKey) { - assert(this.isAccount44()); + assert(this.isAccount44(accountIndex), 'Cannot derive account index.'); return this; } - if (coinType == null) - coinType = this.network === 'main' ? 0 : 1; + assert(this.isMaster(), 'Cannot derive account index.'); - assert(utils.isNumber(coinType)); - assert(utils.isNumber(accountIndex)); + coinType = this.network === 'main' ? 0 : 1; - child = this + return this .derive(44, true) .derive(coinType, true) .derive(accountIndex, true); - - assert(child.isAccount44()); - - return child; }; /** @@ -599,15 +590,38 @@ HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() { var child; if (this instanceof HDPublicKey) { - assert(this.isPurpose45()); + assert(this.isPurpose45(), 'Cannot derive purpose 45.'); return this; } - child = this.derive(45, true); + assert(this.isMaster(), 'Cannot derive purpose 45.'); - assert(child.isPurpose45()); + return this.derive(45, true); +}; - return child; +/** + * Test whether the key is a master key. + * @returns {Boolean} + */ + +HDPrivateKey.prototype.isMaster = function isMaster() { + return this.depth === 0 + && this.childIndex === 0 + && utils.readU32(this.parentFingerPrint, 0) === 0; +}; + +/** + * Test whether the key is (most likely) a BIP44 account key. + * @param {Number?} accountIndex + * @returns {Boolean} + */ + +HDPrivateKey.prototype.isAccount44 = function isAccount44(accountIndex) { + if (accountIndex != null) { + if (this.childIndex !== constants.hd.HARDENED + accountIndex) + return false; + } + return this.depth === 3 && this.childIndex >= constants.hd.HARDENED; }; /** @@ -616,20 +630,7 @@ HDPrivateKey.prototype.derivePurpose45 = function derivePurpose45() { */ HDPrivateKey.prototype.isPurpose45 = function isPurpose45() { - if (this.depth !== 1) - return false; - return this.childIndex === constants.hd.HARDENED + 45; -}; - -/** - * Test whether the key is (most likely) a BIP44 account key. - * @returns {Boolean} - */ - -HDPrivateKey.prototype.isAccount44 = function isAccount44() { - if (this.childIndex < constants.hd.HARDENED) - return false; - return this.depth === 3; + return this.depth === 1 && this.childIndex === constants.hd.HARDENED + 45; }; /** @@ -662,6 +663,9 @@ HDPrivateKey.isExtended = function isExtended(data) { */ HDPrivateKey.isValidPath = function isValidPath(path) { + if (typeof path !== 'string') + return false; + try { HD.parsePath(path, constants.hd.MAX_INDEX); return true; @@ -722,8 +726,7 @@ HDPrivateKey.parseSeed = function parseSeed(seed, network) { parentFingerPrint: new Buffer([0, 0, 0, 0]), childIndex: 0, chainCode: chainCode, - privateKey: privateKey, - mnemonic: seed + privateKey: privateKey }; }; @@ -752,14 +755,19 @@ HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) { if (!(mnemonic instanceof Mnemonic)) mnemonic = new Mnemonic(mnemonic); - if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy) - return HDPrivateKey.fromSeed(mnemonic.toSeed(), network); + if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy) { + key = HDPrivateKey.parseSeed(mnemonic.toSeed(), network); + key.mnemonic = mnemonic; + return new HDPrivateKey(key); + } // Very unlikely, but not impossible // to get an invalid private key. for (;;) { try { - key = HDPrivateKey.fromSeed(mnemonic.toSeed(), network); + key = HDPrivateKey.parseSeed(mnemonic.toSeed(), network); + key.mnemonic = mnemonic; + key = new HDPrivateKey(key); } catch (e) { if (e.message === 'Master private key is invalid.') { mnemonic.seed = null; @@ -896,8 +904,7 @@ HDPrivateKey.prototype.toBase58 = function toBase58(network) { */ HDPrivateKey.fromBase58 = function fromBase58(xkey) { - var data = HDPrivateKey.parseBase58(xkey); - return new HDPrivateKey(data); + return new HDPrivateKey(HDPrivateKey.parseBase58(xkey)); }; /** @@ -1149,6 +1156,23 @@ HDPublicKey.prototype.deriveAccount44 = HDPrivateKey.prototype.deriveAccount44; HDPublicKey.prototype.derivePurpose45 = HDPrivateKey.prototype.derivePurpose45; +/** + * Test whether the key is a master key. + * @method + * @returns {Boolean} + */ + +HDPublicKey.prototype.isMaster = HDPrivateKey.prototype.isMaster; + +/** + * Test whether the key is (most likely) a BIP44 account key. + * @method + * @param {Number?} accountIndex + * @returns {Boolean} + */ + +HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44; + /** * Test whether the key is a BIP45 purpose key. * @method @@ -1157,14 +1181,6 @@ HDPublicKey.prototype.derivePurpose45 = HDPrivateKey.prototype.derivePurpose45; HDPublicKey.prototype.isPurpose45 = HDPrivateKey.prototype.isPurpose45; -/** - * Test whether the key is (most likely) a BIP44 account key. - * @method - * @returns {Boolean} - */ - -HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44; - /** * Test whether a string is a valid path. * @param {String} path @@ -1173,6 +1189,9 @@ HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44; */ HDPublicKey.isValidPath = function isValidPath(path) { + if (typeof path !== 'string') + return false; + try { HD.parsePath(path, constants.hd.HARDENED); return true; @@ -1314,8 +1333,7 @@ HDPublicKey.prototype.toBase58 = function toBase58(network) { */ HDPublicKey.fromBase58 = function fromBase58(xkey) { - var data = HDPublicKey.parseBase58(xkey); - return new HDPublicKey(data); + return new HDPublicKey(HDPublicKey.parseBase58(xkey)); }; /**