From 8f51d4884ed6b42ef2b832a370dbc83455c57449 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Thu, 25 Feb 2016 19:03:39 +0300 Subject: [PATCH 01/14] Fix PrivateKey.toBuffer --- lib/privatekey.js | 2 +- test/privatekey.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/privatekey.js b/lib/privatekey.js index 9697bbfba..636eb2d52 100644 --- a/lib/privatekey.js +++ b/lib/privatekey.js @@ -336,7 +336,7 @@ PrivateKey.prototype.toBigNumber = function(){ * @returns {Buffer} A buffer of the private key */ PrivateKey.prototype.toBuffer = function(){ - return this.bn.toBuffer(); + return this.bn.toBuffer({ size: 32 }); }; /** diff --git a/test/privatekey.js b/test/privatekey.js index 6df029934..ffbaaf893 100644 --- a/test/privatekey.js +++ b/test/privatekey.js @@ -330,6 +330,13 @@ describe('PrivateKey', function() { var fromBuffer = PrivateKey.fromBuffer(toBuffer.toBuffer()); fromBuffer.toString().should.equal(privkey.toString()); }); + + it('should return buffer with length equal 32', function() { + var bn = BN.fromBuffer(buf.slice(0, 31)); + var privkey = new PrivateKey(bn, 'livenet'); + var expected = Buffer.concat([ new Buffer([0]), buf.slice(0, 31) ]); + privkey.toBuffer().toString('hex').should.equal(expected.toString('hex')); + }); }); describe('#toBigNumber', function() { From 969aec2492f1c9283176e1d0002e735133bead0b Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 12 Sep 2016 19:01:01 -0400 Subject: [PATCH 02/14] Add note to README for 1.0.0 staging/development --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 4e4456a7f..b3606e883 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +**Note**: This is a development/staging branch for 1.0.0 + Bitcore Library ======= From 23df2492c8062092fb9426a1f131e3d63bd50c96 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 12 Sep 2016 20:05:47 -0400 Subject: [PATCH 03/14] Remove Node.js 0.10, 0.12 and iojs 2.0.0 from travis ci --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 202aa51e9..b3e4d403c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: node_js sudo: false node_js: -- '0.10' -- '0.12' -- '2.0.0' - '4' before_install: - npm install -g bower From b79a9b274ae8b48701daf5a1284e4f9035c3df0d Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 12 Sep 2016 18:14:30 -0400 Subject: [PATCH 04/14] Fix implementation of hd derivation to be bip32 compliant https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#child-key-derivation-ckd-functions --- lib/hdkeycache.js | 8 +++---- lib/hdprivatekey.js | 51 ++++++++++++++++++++++++++++++++++++--------- test/hdkeycache.js | 10 ++++----- test/hdkeys.js | 32 ++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 19 deletions(-) diff --git a/lib/hdkeycache.js b/lib/hdkeycache.js index 721d9da48..606e00c1a 100644 --- a/lib/hdkeycache.js +++ b/lib/hdkeycache.js @@ -8,17 +8,17 @@ module.exports = { _usedIndex: {}, _CACHE_SIZE: 5000, - get: function(xkey, number, hardened) { + get: function(xkey, number, hardened, nonCompliant) { hardened = !!hardened; - var key = xkey + '/' + number + '/' + hardened; + var key = (nonCompliant ? '1' : '0') + xkey + '/' + number + '/' + hardened; if (this._cache[key]) { this._cacheHit(key); return this._cache[key]; } }, - set: function(xkey, number, hardened, derived) { + set: function(xkey, number, hardened, derived, nonCompliant) { hardened = !!hardened; - var key = xkey + '/' + number + '/' + hardened; + var key = (nonCompliant ? '1' : '0') + xkey + '/' + number + '/' + hardened; this._cache[key] = derived; this._cacheHit(key); }, diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 3826e0b40..8d5ed1b35 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -139,6 +139,9 @@ HDPrivateKey._getDerivationIndexes = function(path) { * derived. If the second argument is truthy, the hardened version will be * derived. See the example usage for clarification. * + * WARNING: The `nonCompliant` option should NOT be used, except for older implementation + * that used a derivation strategy that used a non-zero padded private key. + * * @example * ```javascript * var parent = new HDPrivateKey('xprv...'); @@ -149,18 +152,38 @@ HDPrivateKey._getDerivationIndexes = function(path) { * * @param {string|number} arg * @param {boolean?} hardened + * @param {boolean?} nonCompliant - This should not be used and only for backwards compatibility + * */ -HDPrivateKey.prototype.derive = function(arg, hardened) { +HDPrivateKey.prototype.derive = function(arg, hardened, nonCompliant) { if (_.isNumber(arg)) { - return this._deriveWithNumber(arg, hardened); + return this._deriveWithNumber(arg, hardened, nonCompliant); } else if (_.isString(arg)) { - return this._deriveFromString(arg); + return this._deriveFromString(arg, nonCompliant); } else { throw new hdErrors.InvalidDerivationArgument(arg); } }; -HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { +/** + * WARNING: If this is a new implementation you should NOT use this method, you should be using + * `derive` instead. + * + * This method is explicitly for use and compatibility with an implementation that + * was not compliant with BIP32 regarding the derivation algorithm. The private key + * must be 32 bytes hashing, and this implementation will use the non-zero padded + * serialization of a private key, such that it's still possible to derive the privateKey + * to recover those funds. + * + * @param {string|number} arg + * @param {boolean?} hardened + */ +HDPrivateKey.prototype.deriveNonCompliant = function(arg, hardened) { + var derived = this.derive(arg, hardened, true); + return derived; +}; + +HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonCompliant) { /* jshint maxstatements: 20 */ /* jshint maxcomplexity: 10 */ if (!HDPrivateKey.isValidPath(index, hardened)) { @@ -172,15 +195,23 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { index += HDPrivateKey.Hardened; } - var cached = HDKeyCache.get(this.xprivkey, index, hardened); + var cached = HDKeyCache.get(this.xprivkey, index, hardened, nonCompliant); if (cached) { return cached; } var indexBuffer = BufferUtil.integerAsBuffer(index); var data; - if (hardened) { - data = BufferUtil.concat([new buffer.Buffer([0]), this.privateKey.toBuffer(), indexBuffer]); + if (hardened && nonCompliant) { + // The private key serialization in this case will not be exactly 32 bytes and can be + // any value less, and the value is not zero-padded. + var nonZeroPadded = this.privateKey.bn.toBuffer(); + data = BufferUtil.concat([new buffer.Buffer([0]), nonZeroPadded, indexBuffer]); + } else if (hardened) { + // This will use a 32 byte zero padded serialization of the private key + var privateKeyBuffer = this.privateKey.bn.toBuffer({size: 32}); + assert(privateKeyBuffer.length === 32, 'length of private key buffer is expected to be 32 bytes'); + data = BufferUtil.concat([new buffer.Buffer([0]), privateKeyBuffer, indexBuffer]); } else { data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); } @@ -202,18 +233,18 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened) { chainCode: chainCode, privateKey: privateKey }); - HDKeyCache.set(this.xprivkey, index, hardened, derived); + HDKeyCache.set(this.xprivkey, index, hardened, derived, nonCompliant); return derived; }; -HDPrivateKey.prototype._deriveFromString = function(path) { +HDPrivateKey.prototype._deriveFromString = function(path, nonCompliant) { if (!HDPrivateKey.isValidPath(path)) { throw new hdErrors.InvalidPath(path); } var indexes = HDPrivateKey._getDerivationIndexes(path); var derived = indexes.reduce(function(prev, index) { - return prev._deriveWithNumber(index); + return prev._deriveWithNumber(index, null, nonCompliant); }, this); return derived; diff --git a/test/hdkeycache.js b/test/hdkeycache.js index db1f4fc18..692c01d3e 100644 --- a/test/hdkeycache.js +++ b/test/hdkeycache.js @@ -25,22 +25,22 @@ describe('HDKey cache', function() { it('saves a derived key', function() { var child = master.derive(0); - expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child.xprivkey); + expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child.xprivkey); }); it('starts erasing unused keys', function() { var child1 = master.derive(0); var child2 = child1.derive(0); var child3 = child2.derive(0); - expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); + expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); var child4 = child3.derive(0); - expect(cache._cache[master.xprivkey + '/0/false']).to.equal(undefined); + expect(cache._cache['0' + master.xprivkey + '/0/false']).to.equal(undefined); }); it('avoids erasing keys that get cache hits ("hot keys")', function() { var child1 = master.derive(0); var child2 = master.derive(0).derive(0); - expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); + expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); var child1_copy = master.derive(0); - expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); + expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); }); it('keeps the size of the cache small', function() { var child1 = master.derive(0); diff --git a/test/hdkeys.js b/test/hdkeys.js index d4bd6bda7..377e18b52 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -221,6 +221,38 @@ describe('BIP32 compliance', function() { .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); + it('should use full 32 bytes for private key data that is hashed (as per bip32)', function() { + // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki + var privateKeyBuffer = new Buffer('00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd', 'hex'); + var chainCodeBuffer = new Buffer('9c8a5c863e5941f3d99453e6ba66b328bb17cf0b8dec89ed4fc5ace397a1c089', 'hex'); + var key = HDPrivateKey.fromObject({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + privateKey: privateKeyBuffer, + chainCode: chainCodeBuffer + }); + var derived = key.derive("m/44'/0'/0'/0/0'"); + derived.privateKey.toString().should.equal('3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb'); + }); + + it('should NOT use full 32 bytes for private key data that is hashed with nonCompliant flag', function() { + // This is to test that the previously implemented non-compliant to BIP32 + var privateKeyBuffer = new Buffer('00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd', 'hex'); + var chainCodeBuffer = new Buffer('9c8a5c863e5941f3d99453e6ba66b328bb17cf0b8dec89ed4fc5ace397a1c089', 'hex'); + var key = HDPrivateKey.fromObject({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + privateKey: privateKeyBuffer, + chainCode: chainCodeBuffer + }); + var derived = key.deriveNonCompliant("m/44'/0'/0'/0/0'"); + derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); + }); + describe('seed', function() { it('should initialize a new BIP32 correctly from test vector 1 seed', function() { From 0906d988ca40e59207e5dc4b927d1f864f31083c Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 13 Sep 2016 18:59:16 -0400 Subject: [PATCH 05/14] Handle edge case that invalid private key is derived --- lib/hdprivatekey.js | 5 +++++ test/hdkeys.js | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 8d5ed1b35..33ec36327 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -225,6 +225,11 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonComplian size: 32 }); + if (!PrivateKey.isValid(privateKey)) { + // Index at this point is already hardened, we can pass null as the hardened arg + return this._deriveWithNumber(index + 1, null, nonCompliant); + } + var derived = new HDPrivateKey({ network: this.network, depth: this.depth + 1, diff --git a/test/hdkeys.js b/test/hdkeys.js index 377e18b52..d57a54b5b 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -13,6 +13,7 @@ var _ = require('lodash'); var should = require('chai').should(); var expect = require('chai').expect; +var sinon = require('sinon'); var bitcore = require('..'); var Networks = bitcore.Networks; var HDPrivateKey = bitcore.HDPrivateKey; @@ -253,6 +254,42 @@ describe('BIP32 compliance', function() { derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); + describe('edge cases', function() { + var sandbox = sinon.sandbox.create(); + afterEach(function() { + sandbox.restore(); + }); + it('will handle edge case that derived private key is invalid', function() { + var invalid = new Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'); + var privateKeyBuffer = new Buffer('5f72914c48581fc7ddeb944a9616389200a9560177d24f458258e5b04527bcd1', 'hex'); + var chainCodeBuffer = new Buffer('39816057bba9d952fe87fe998b7fd4d690a1bb58c2ff69141469e4d1dffb4b91', 'hex'); + var unstubbed = bitcore.crypto.BN.prototype.toBuffer; + var count = 0; + var stub = sandbox.stub(bitcore.crypto.BN.prototype, 'toBuffer', function(args) { + // On the fourth call to the function give back an invalid private key + // otherwise use the normal behavior. + count++; + if (count === 4) { + return invalid; + } + var ret = unstubbed.apply(this, arguments); + return ret; + }); + sandbox.spy(bitcore.PrivateKey, 'isValid'); + var key = HDPrivateKey.fromObject({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + privateKey: privateKeyBuffer, + chainCode: chainCodeBuffer + }); + var derived = key.derive("m/44'"); + derived.privateKey.toString().should.equal('b15bce3608d607ee3a49069197732c656bca942ee59f3e29b4d56914c1de6825'); + bitcore.PrivateKey.isValid.callCount.should.equal(2); + }); + }); + describe('seed', function() { it('should initialize a new BIP32 correctly from test vector 1 seed', function() { From d0e3f84104a6c7959524bc42ab62dc1e86843715 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 13 Sep 2016 19:25:10 -0400 Subject: [PATCH 06/14] Handle invalid public key derivation rather than throw error --- lib/hdpublickey.js | 7 ++++++- test/hdkeys.js | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index 198947d06..04aad48d9 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -136,7 +136,12 @@ HDPublicKey.prototype._deriveWithNumber = function(index, hardened) { var leftPart = BN.fromBuffer(hash.slice(0, 32), {size: 32}); var chainCode = hash.slice(32, 64); - var publicKey = PublicKey.fromPoint(Point.getG().mul(leftPart).add(this.publicKey.point)); + var publicKey; + try { + publicKey = PublicKey.fromPoint(Point.getG().mul(leftPart).add(this.publicKey.point)); + } catch (e) { + return this._deriveWithNumber(index + 1); + } var derived = new HDPublicKey({ network: this.network, diff --git a/test/hdkeys.js b/test/hdkeys.js index d57a54b5b..edbe8468a 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -288,6 +288,27 @@ describe('BIP32 compliance', function() { derived.privateKey.toString().should.equal('b15bce3608d607ee3a49069197732c656bca942ee59f3e29b4d56914c1de6825'); bitcore.PrivateKey.isValid.callCount.should.equal(2); }); + it('will handle edge case that a derive public key is invalid', function() { + var publicKeyBuffer = new Buffer('029e58b241790284ef56502667b15157b3fc58c567f044ddc35653860f9455d099', 'hex'); + var chainCodeBuffer = new Buffer('39816057bba9d952fe87fe998b7fd4d690a1bb58c2ff69141469e4d1dffb4b91', 'hex'); + var key = new HDPublicKey({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + chainCode: chainCodeBuffer, + publicKey: publicKeyBuffer + }); + var unstubbed = bitcore.PublicKey.fromPoint; + bitcore.PublicKey.fromPoint = function() { + bitcore.PublicKey.fromPoint = unstubbed; + throw new Error('Point cannot be equal to Infinity'); + }; + sandbox.spy(key, '_deriveWithNumber'); + var derived = key.derive("m/44"); + key._deriveWithNumber.callCount.should.equal(2); + key.publicKey.toString().should.equal('029e58b241790284ef56502667b15157b3fc58c567f044ddc35653860f9455d099'); + }); }); describe('seed', function() { From e9d1237228ec17858dc40d11c2383d24ba55f911 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Fri, 16 Sep 2016 10:13:43 -0400 Subject: [PATCH 07/14] Add toBufferNoPadding method to private key --- lib/privatekey.js | 9 +++++++++ test/privatekey.js | 14 ++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/privatekey.js b/lib/privatekey.js index 636eb2d52..440b0e5e1 100644 --- a/lib/privatekey.js +++ b/lib/privatekey.js @@ -339,6 +339,15 @@ PrivateKey.prototype.toBuffer = function(){ return this.bn.toBuffer({ size: 32 }); }; +/** + * Will return the private key as a BN buffer without leading zero padding + * + * @returns {Buffer} A buffer of the private key + */ +PrivateKey.prototype.toBufferNoPadding = function() { + return this.bn.toBuffer(); +}; + /** * Will return the corresponding public key * diff --git a/test/privatekey.js b/test/privatekey.js index ffbaaf893..90f9aeaa4 100644 --- a/test/privatekey.js +++ b/test/privatekey.js @@ -331,6 +331,20 @@ describe('PrivateKey', function() { fromBuffer.toString().should.equal(privkey.toString()); }); + it('will output a 32 byte buffer', function() { + var bn = BN.fromBuffer(new Buffer('9b5a0e8fee1835e21170ce1431f9b6f19b487e67748ed70d8a4462bc031915', 'hex')); + var privkey = new PrivateKey(bn); + var buffer = privkey.toBuffer(); + buffer.length.should.equal(32); + }); + + it('will output a 31 byte buffer', function() { + var bn = BN.fromBuffer(new Buffer('9b5a0e8fee1835e21170ce1431f9b6f19b487e67748ed70d8a4462bc031915', 'hex')); + var privkey = new PrivateKey(bn); + var buffer = privkey.toBufferNoPadding(); + buffer.length.should.equal(31); + }); + it('should return buffer with length equal 32', function() { var bn = BN.fromBuffer(buf.slice(0, 31)); var privkey = new PrivateKey(bn, 'livenet'); From d32ae41adc6b10a8cb3c892995477d1edd4a5ccb Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Tue, 6 Dec 2016 01:13:04 -0500 Subject: [PATCH 08/14] Change name of derive to deriveChild This is to avoid any accidental upgrades to a bugfixed version without awareness of the change. --- docs/hierarchical.md | 14 +++++--- lib/hdprivatekey.js | 15 +++++--- lib/hdpublickey.js | 11 ++++-- test/hdkeycache.js | 24 ++++++------- test/hdkeys.js | 83 ++++++++++++++++++++++++++------------------ test/hdprivatekey.js | 12 +++---- test/hdpublickey.js | 20 +++++------ 7 files changed, 105 insertions(+), 74 deletions(-) diff --git a/docs/hierarchical.md b/docs/hierarchical.md index dc35571e5..bac5e37c6 100644 --- a/docs/hierarchical.md +++ b/docs/hierarchical.md @@ -15,9 +15,9 @@ var HDPrivateKey = bitcore.HDPrivateKey; var hdPrivateKey = new HDPrivateKey(); var retrieved = new HDPrivateKey('xpriv...'); -var derived = hdPrivateKey.derive("m/0'"); -var derivedByNumber = hdPrivateKey.derive(1).derive(2, true); -var derivedByArgument = hdPrivateKey.derive("m/1/2'"); +var derived = hdPrivateKey.deriveChild("m/0'"); +var derivedByNumber = hdPrivateKey.deriveChild(1).deriveChild(2, true); +var derivedByArgument = hdPrivateKey.deriveChild("m/1/2'"); assert(derivedByNumber.xprivkey === derivedByArgument.xprivkey); var address = derived.privateKey.toAddress(); @@ -39,5 +39,11 @@ try { } var address = new Address(hdPublicKey.publicKey, Networks.livenet); -var derivedAddress = new Address(hdPublicKey.derive(100).publicKey, Networks.testnet); +var derivedAddress = new Address(hdPublicKey.deriveChild(100).publicKey, Networks.testnet); ``` + +## Upgrading from v0.13.x and previous to v1.0.0 + +There was a bug that was discovered with derivation that would incorrectly calculate the child key against the [BIP32 specification](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). The method `derive` has been deprecated and replaced with `deriveChild` and `deriveNonCompliantChild`. As the names indicate `deriveNonCompliantChild` will derive using the non-BIP32 derivation and is the equivalent of `derive` in v0.13 and previous versions. The `deriveNonCompliantChild` method should not be used unless you're upgrading and need to maintain compatibility with the old derivation. + +The bug only affected hardened derivations using an extended private key, and did not affect public key derivation. It also did not affect every derivation and would happen 1 in 256 times where where the private key for the extended private key had a leading zero *(e.g. any private key less than or equal to '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')*. The leading zero was not included in serialization before hashing to derive a child key, as it should have been. diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 33ec36327..f8e560bc3 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -127,6 +127,11 @@ HDPrivateKey._getDerivationIndexes = function(path) { return _.any(indexes, isNaN) ? null : indexes; }; +HDPrivateKey.prototype.derive = function() { + throw new Error('Method has been deprecated, please use deriveChild or deriveNonCompliantChild' + + 'and see documentation for more information'); +}; + /** * Get a derived child based on a string or number. * @@ -145,8 +150,8 @@ HDPrivateKey._getDerivationIndexes = function(path) { * @example * ```javascript * var parent = new HDPrivateKey('xprv...'); - * var child_0_1_2h = parent.derive(0).derive(1).derive(2, true); - * var copy_of_child_0_1_2h = parent.derive("m/0/1/2'"); + * var child_0_1_2h = parent.deriveChild(0).deriveChild(1).deriveChild(2, true); + * var copy_of_child_0_1_2h = parent.deriveChild("m/0/1/2'"); * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h); * ``` * @@ -155,7 +160,7 @@ HDPrivateKey._getDerivationIndexes = function(path) { * @param {boolean?} nonCompliant - This should not be used and only for backwards compatibility * */ -HDPrivateKey.prototype.derive = function(arg, hardened, nonCompliant) { +HDPrivateKey.prototype.deriveChild = function(arg, hardened, nonCompliant) { if (_.isNumber(arg)) { return this._deriveWithNumber(arg, hardened, nonCompliant); } else if (_.isString(arg)) { @@ -178,8 +183,8 @@ HDPrivateKey.prototype.derive = function(arg, hardened, nonCompliant) { * @param {string|number} arg * @param {boolean?} hardened */ -HDPrivateKey.prototype.deriveNonCompliant = function(arg, hardened) { - var derived = this.derive(arg, hardened, true); +HDPrivateKey.prototype.deriveNonCompliantChild = function(arg, hardened) { + var derived = this.deriveChild(arg, hardened, true); return derived; }; diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index 04aad48d9..d28a82bf5 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -86,6 +86,11 @@ HDPublicKey.isValidPath = function(arg) { return false; }; +HDPublicKey.prototype.derive = function() { + throw new Error('Method has been deprecated, please use deriveChild instead' + + 'and see documentation for more information'); +}; + /** * Get a derivated child based on a string or number. * @@ -101,14 +106,14 @@ HDPublicKey.isValidPath = function(arg) { * @example * ```javascript * var parent = new HDPublicKey('xpub...'); - * var child_0_1_2 = parent.derive(0).derive(1).derive(2); - * var copy_of_child_0_1_2 = parent.derive("m/0/1/2"); + * var child_0_1_2 = parent.deriveChild(0).deriveChild(1).deriveChild(2); + * var copy_of_child_0_1_2 = parent.deriveChild("m/0/1/2"); * assert(child_0_1_2.xprivkey === copy_of_child_0_1_2); * ``` * * @param {string|number} arg */ -HDPublicKey.prototype.derive = function(arg, hardened) { +HDPublicKey.prototype.deriveChild = function(arg, hardened) { if (_.isNumber(arg)) { return this._deriveWithNumber(arg, hardened); } else if (_.isString(arg)) { diff --git a/test/hdkeycache.js b/test/hdkeycache.js index 692c01d3e..35a51678a 100644 --- a/test/hdkeycache.js +++ b/test/hdkeycache.js @@ -24,29 +24,29 @@ describe('HDKey cache', function() { }); it('saves a derived key', function() { - var child = master.derive(0); + var child = master.deriveChild(0); expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child.xprivkey); }); it('starts erasing unused keys', function() { - var child1 = master.derive(0); - var child2 = child1.derive(0); - var child3 = child2.derive(0); + var child1 = master.deriveChild(0); + var child2 = child1.deriveChild(0); + var child3 = child2.deriveChild(0); expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); - var child4 = child3.derive(0); + var child4 = child3.deriveChild(0); expect(cache._cache['0' + master.xprivkey + '/0/false']).to.equal(undefined); }); it('avoids erasing keys that get cache hits ("hot keys")', function() { - var child1 = master.derive(0); - var child2 = master.derive(0).derive(0); + var child1 = master.deriveChild(0); + var child2 = master.deriveChild(0).deriveChild(0); expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); - var child1_copy = master.derive(0); + var child1_copy = master.deriveChild(0); expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); }); it('keeps the size of the cache small', function() { - var child1 = master.derive(0); - var child2 = child1.derive(0); - var child3 = child2.derive(0); - var child4 = child3.derive(0); + var child1 = master.deriveChild(0); + var child2 = child1.deriveChild(0); + var child3 = child2.deriveChild(0); + var child4 = child3.deriveChild(0); expect(_.size(cache._cache)).to.equal(3); }); }); diff --git a/test/hdkeys.js b/test/hdkeys.js index edbe8468a..0c591ff40 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -81,69 +81,69 @@ describe('BIP32 compliance', function() { }); it("should get m/0' ext. private key from test vector 1", function() { - var privateKey = new HDPrivateKey(vector1_m_private).derive("m/0'"); + var privateKey = new HDPrivateKey(vector1_m_private).deriveChild("m/0'"); privateKey.xprivkey.should.equal(vector1_m0h_private); }); it("should get m/0' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'") + HDPrivateKey(vector1_m_private).deriveChild("m/0'") .xpubkey.should.equal(vector1_m0h_public); }); it("should get m/0'/1 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") .xprivkey.should.equal(vector1_m0h1_private); }); it("should get m/0'/1 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") .xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { - var derivedPublic = HDPrivateKey(vector1_m_private).derive("m/0'").hdPublicKey.derive("m/1"); + var derivedPublic = HDPrivateKey(vector1_m_private).deriveChild("m/0'").hdPublicKey.deriveChild("m/1"); derivedPublic.xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1/2' ext. private key from test vector 1", function() { var privateKey = new HDPrivateKey(vector1_m_private); - var derived = privateKey.derive("m/0'/1/2'"); + var derived = privateKey.deriveChild("m/0'/1/2'"); derived.xprivkey.should.equal(vector1_m0h12h_private); }); it("should get m/0'/1/2' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1/2'") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'") .xpubkey.should.equal(vector1_m0h12h_public); }); it("should get m/0'/1/2'/2 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") .xprivkey.should.equal(vector1_m0h12h2_private); }); it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'").hdPublicKey; - derived.derive("m/2").xpubkey.should.equal(vector1_m0h12h2_public); + var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'").hdPublicKey; + derived.deriveChild("m/2").xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") .xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") .xprivkey.should.equal(vector1_m0h12h21000000000_private); }); it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") .xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2").hdPublicKey; - derived.derive("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); + var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2").hdPublicKey; + derived.deriveChild("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it('should initialize test vector 2 from the extended public key', function() { @@ -159,66 +159,66 @@ describe('BIP32 compliance', function() { }); it("should get m/0 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive(0).xprivkey.should.equal(vector2_m0_private); + HDPrivateKey(vector2_m_private).deriveChild(0).xprivkey.should.equal(vector2_m0_private); }); it("should get m/0 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).deriveChild(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0 ext. public key from m public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).hdPublicKey.derive(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).hdPublicKey.deriveChild(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0/2147483647h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") .xprivkey.should.equal(vector2_m02147483647h_private); }); it("should get m/0/2147483647h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") .xpubkey.should.equal(vector2_m02147483647h_public); }); it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") .xprivkey.should.equal(vector2_m02147483647h1_private); }); it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") .xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { - var derived = HDPrivateKey(vector2_m_private).derive("m/0/2147483647'").hdPublicKey; - derived.derive(1).xpubkey.should.equal(vector2_m02147483647h1_public); + var derived = HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'").hdPublicKey; + derived.deriveChild(1).xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") .xprivkey.should.equal(vector2_m02147483647h12147483646h_private); }); it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") .xpubkey.should.equal(vector2_m02147483647h12147483646h_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") .xprivkey.should.equal(vector2_m02147483647h12147483646h2_private); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { var derivedPublic = HDPrivateKey(vector2_m_private) - .derive("m/0/2147483647'/1/2147483646'").hdPublicKey; - derivedPublic.derive("m/2") + .deriveChild("m/0/2147483647'/1/2147483646'").hdPublicKey; + derivedPublic.deriveChild("m/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); @@ -234,7 +234,7 @@ describe('BIP32 compliance', function() { privateKey: privateKeyBuffer, chainCode: chainCodeBuffer }); - var derived = key.derive("m/44'/0'/0'/0/0'"); + var derived = key.deriveChild("m/44'/0'/0'/0/0'"); derived.privateKey.toString().should.equal('3348069561d2a0fb925e74bf198762acc47dce7db27372257d2d959a9e6f8aeb'); }); @@ -250,10 +250,25 @@ describe('BIP32 compliance', function() { privateKey: privateKeyBuffer, chainCode: chainCodeBuffer }); - var derived = key.deriveNonCompliant("m/44'/0'/0'/0/0'"); + var derived = key.deriveNonCompliantChild("m/44'/0'/0'/0/0'"); derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); + it('hdprivatekey will throw deprecation message', function() { + expect(function() { + var key = new HDPrivateKey(); + key.derive(); + }).to.throw('Method has been deprecated'); + }); + + it('hdpublickey will throw deprecation message', function() { + expect(function() { + var key = new HDPrivateKey(); + var pubkey = key.hdPublicKey; + pubkey.derive(); + }).to.throw('Method has been deprecated'); + }); + describe('edge cases', function() { var sandbox = sinon.sandbox.create(); afterEach(function() { @@ -284,7 +299,7 @@ describe('BIP32 compliance', function() { privateKey: privateKeyBuffer, chainCode: chainCodeBuffer }); - var derived = key.derive("m/44'"); + var derived = key.deriveChild("m/44'"); derived.privateKey.toString().should.equal('b15bce3608d607ee3a49069197732c656bca942ee59f3e29b4d56914c1de6825'); bitcore.PrivateKey.isValid.callCount.should.equal(2); }); @@ -305,7 +320,7 @@ describe('BIP32 compliance', function() { throw new Error('Point cannot be equal to Infinity'); }; sandbox.spy(key, '_deriveWithNumber'); - var derived = key.derive("m/44"); + var derived = key.deriveChild("m/44"); key._deriveWithNumber.callCount.should.equal(2); key.publicKey.toString().should.equal('029e58b241790284ef56502667b15157b3fc58c567f044ddc35653860f9455d099'); }); diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index 6b63e6279..2e99591f4 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -30,7 +30,7 @@ describe('HDPrivate key interface', function() { var expectDerivationFail = function(argument, error) { return expectFail(function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.derive(argument); + privateKey.deriveChild(argument); }, error); }; @@ -123,14 +123,14 @@ describe('HDPrivate key interface', function() { it('allows derivation of hardened keys by passing a very big number', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByNumber = privateKey.derive(0x80000000); - var derivedByArgument = privateKey.derive(0, true); + var derivedByNumber = privateKey.deriveChild(0x80000000); + var derivedByArgument = privateKey.deriveChild(0, true); derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey); }); it('returns itself with \'m\' parameter', function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.should.equal(privateKey.derive('m')); + privateKey.should.equal(privateKey.deriveChild('m')); }); it('returns InvalidArgument if invalid data is given to getSerializedError', function() { @@ -203,8 +203,8 @@ describe('HDPrivate key interface', function() { it('shouldn\'t matter if derivations are made with strings or numbers', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByString = privateKey.derive('m/0\'/1/2\''); - var derivedByNumber = privateKey.derive(0, true).derive(1).derive(2, true); + var derivedByString = privateKey.deriveChild('m/0\'/1/2\''); + var derivedByNumber = privateKey.deriveChild(0, true).deriveChild(1).deriveChild(2, true); derivedByNumber.xprivkey.should.equal(derivedByString.xprivkey); }); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index f006d737b..19f86b304 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -32,7 +32,7 @@ describe('HDPublicKey interface', function() { var expectDerivationFail = function(argument, error) { (function() { var pubkey = new HDPublicKey(xpubkey); - pubkey.derive(argument); + pubkey.deriveChild(argument); }).should.throw(error); }; @@ -188,15 +188,15 @@ describe('HDPublicKey interface', function() { describe('derivation', function() { it('derivation is the same whether deriving with number or string', function() { var pubkey = new HDPublicKey(xpubkey); - var derived1 = pubkey.derive(0).derive(1).derive(200000); - var derived2 = pubkey.derive('m/0/1/200000'); + var derived1 = pubkey.deriveChild(0).deriveChild(1).deriveChild(200000); + var derived2 = pubkey.deriveChild('m/0/1/200000'); derived1.xpubkey.should.equal(derived_0_1_200000); derived2.xpubkey.should.equal(derived_0_1_200000); }); it('allows special parameters m, M', function() { var expectDerivationSuccess = function(argument) { - new HDPublicKey(xpubkey).derive(argument).xpubkey.should.equal(xpubkey); + new HDPublicKey(xpubkey).deriveChild(argument).xpubkey.should.equal(xpubkey); }; expectDerivationSuccess('m'); expectDerivationSuccess('M'); @@ -204,13 +204,13 @@ describe('HDPublicKey interface', function() { it('doesn\'t allow object arguments for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).derive({}); + return new HDPublicKey(xpubkey).deriveChild({}); }, hdErrors.InvalidDerivationArgument); }); it('needs first argument for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).derive('s'); + return new HDPublicKey(xpubkey).deriveChild('s'); }, hdErrors.InvalidPath); }); @@ -224,13 +224,13 @@ describe('HDPublicKey interface', function() { it('can\'t derive hardened keys', function() { expectFail(function() { - return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened); + return new HDPublicKey(xpubkey).deriveChild(HDPublicKey.Hardened); }, hdErrors.InvalidIndexCantDeriveHardened); }); it('can\'t derive hardened keys via second argument', function() { expectFail(function() { - return new HDPublicKey(xpubkey).derive(5, true); + return new HDPublicKey(xpubkey).deriveChild(5, true); }, hdErrors.InvalidIndexCantDeriveHardened); }); @@ -274,8 +274,8 @@ describe('HDPublicKey interface', function() { it('should use the cache', function() { var pubkey = new HDPublicKey(xpubkey); - var derived1 = pubkey.derive(0); - var derived2 = pubkey.derive(0); + var derived1 = pubkey.deriveChild(0); + var derived2 = pubkey.deriveChild(0); derived1.should.equal(derived2); }); }); From a9aeada69e1ae335b1282d34700b7d5f9f3376ec Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Tue, 6 Dec 2016 18:23:35 -0500 Subject: [PATCH 09/14] remove hdkeycache --- index.js | 3 +-- lib/hdkeycache.js | 45 --------------------------------------- lib/hdprivatekey.js | 8 +------ lib/hdpublickey.js | 7 +----- test/hdkeycache.js | 52 --------------------------------------------- test/hdpublickey.js | 7 ------ 6 files changed, 3 insertions(+), 119 deletions(-) delete mode 100644 lib/hdkeycache.js delete mode 100644 test/hdkeycache.js diff --git a/index.js b/index.js index 9aa78d978..eaa6107a4 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,7 @@ var bitcore = module.exports; bitcore.version = 'v' + require('./package.json').version; bitcore.versionGuard = function(version) { if (version !== undefined) { - var message = 'More than one instance of bitcore-lib found. ' + + var message = 'More than one instance of bitcore-lib found. ' + 'Please make sure to require bitcore-lib and check that submodules do' + ' not also include their own bitcore-lib dependency.'; throw new Error(message); @@ -66,5 +66,4 @@ bitcore.deps.elliptic = require('elliptic'); bitcore.deps._ = require('lodash'); // Internal usage, exposed for testing/advanced tweaking -bitcore._HDKeyCache = require('./lib/hdkeycache'); bitcore.Transaction.sighash = require('./lib/transaction/sighash'); diff --git a/lib/hdkeycache.js b/lib/hdkeycache.js deleted file mode 100644 index 606e00c1a..000000000 --- a/lib/hdkeycache.js +++ /dev/null @@ -1,45 +0,0 @@ -'use strict'; - -module.exports = { - _cache: {}, - _count: 0, - _eraseIndex: 0, - _usedList: {}, - _usedIndex: {}, - _CACHE_SIZE: 5000, - - get: function(xkey, number, hardened, nonCompliant) { - hardened = !!hardened; - var key = (nonCompliant ? '1' : '0') + xkey + '/' + number + '/' + hardened; - if (this._cache[key]) { - this._cacheHit(key); - return this._cache[key]; - } - }, - set: function(xkey, number, hardened, derived, nonCompliant) { - hardened = !!hardened; - var key = (nonCompliant ? '1' : '0') + xkey + '/' + number + '/' + hardened; - this._cache[key] = derived; - this._cacheHit(key); - }, - _cacheHit: function(key) { - if (this._usedIndex[key]) { - delete this._usedList[this._usedIndex[key]]; - } - this._usedList[this._count] = key; - this._usedIndex[key] = this._count; - this._count++; - this._cacheRemove(); - }, - _cacheRemove: function() { - while (this._eraseIndex < this._count - this._CACHE_SIZE) { - if (this._usedList[this._eraseIndex]) { - var removeKey = this._usedList[this._eraseIndex]; - delete this._usedIndex[removeKey]; - delete this._cache[removeKey]; - } - delete this._usedList[this._eraseIndex]; - this._eraseIndex++; - } - } -}; diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index f8e560bc3..8c4e2bb80 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -11,7 +11,6 @@ var Base58 = require('./encoding/base58'); var Base58Check = require('./encoding/base58check'); var Hash = require('./crypto/hash'); var Network = require('./networks'); -var HDKeyCache = require('./hdkeycache'); var Point = require('./crypto/point'); var PrivateKey = require('./privatekey'); var Random = require('./crypto/random'); @@ -200,11 +199,6 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonComplian index += HDPrivateKey.Hardened; } - var cached = HDKeyCache.get(this.xprivkey, index, hardened, nonCompliant); - if (cached) { - return cached; - } - var indexBuffer = BufferUtil.integerAsBuffer(index); var data; if (hardened && nonCompliant) { @@ -243,7 +237,7 @@ HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonComplian chainCode: chainCode, privateKey: privateKey }); - HDKeyCache.set(this.xprivkey, index, hardened, derived, nonCompliant); + return derived; }; diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index d28a82bf5..ee083b612 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -8,7 +8,6 @@ var Base58 = require('./encoding/base58'); var Base58Check = require('./encoding/base58check'); var Hash = require('./crypto/hash'); var HDPrivateKey = require('./hdprivatekey'); -var HDKeyCache = require('./hdkeycache'); var Network = require('./networks'); var Point = require('./crypto/point'); var PublicKey = require('./publickey'); @@ -130,10 +129,6 @@ HDPublicKey.prototype._deriveWithNumber = function(index, hardened) { if (index < 0) { throw new hdErrors.InvalidPath(index); } - var cached = HDKeyCache.get(this.xpubkey, index, false); - if (cached) { - return cached; - } var indexBuffer = BufferUtil.integerAsBuffer(index); var data = BufferUtil.concat([this.publicKey.toBuffer(), indexBuffer]); @@ -156,7 +151,7 @@ HDPublicKey.prototype._deriveWithNumber = function(index, hardened) { chainCode: chainCode, publicKey: publicKey }); - HDKeyCache.set(this.xpubkey, index, false, derived); + return derived; }; diff --git a/test/hdkeycache.js b/test/hdkeycache.js deleted file mode 100644 index 35a51678a..000000000 --- a/test/hdkeycache.js +++ /dev/null @@ -1,52 +0,0 @@ -'use strict'; - -var _ = require('lodash'); -var expect = require('chai').expect; -var bitcore = require('..'); -var HDPrivateKey = bitcore.HDPrivateKey; - -var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi'; - -describe('HDKey cache', function() { - this.timeout(10000); - - /* jshint unused: false */ - var cache = bitcore._HDKeyCache; - var master = new HDPrivateKey(xprivkey); - - beforeEach(function() { - cache._cache = {}; - cache._count = 0; - cache._eraseIndex = 0; - cache._usedIndex = {}; - cache._usedList = {}; - cache._CACHE_SIZE = 3; // Reduce for quick testing - }); - - it('saves a derived key', function() { - var child = master.deriveChild(0); - expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child.xprivkey); - }); - it('starts erasing unused keys', function() { - var child1 = master.deriveChild(0); - var child2 = child1.deriveChild(0); - var child3 = child2.deriveChild(0); - expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); - var child4 = child3.deriveChild(0); - expect(cache._cache['0' + master.xprivkey + '/0/false']).to.equal(undefined); - }); - it('avoids erasing keys that get cache hits ("hot keys")', function() { - var child1 = master.deriveChild(0); - var child2 = master.deriveChild(0).deriveChild(0); - expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); - var child1_copy = master.deriveChild(0); - expect(cache._cache['0' + master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey); - }); - it('keeps the size of the cache small', function() { - var child1 = master.deriveChild(0); - var child2 = child1.deriveChild(0); - var child3 = child2.deriveChild(0); - var child4 = child3.deriveChild(0); - expect(_.size(cache._cache)).to.equal(3); - }); -}); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index 19f86b304..457f1bdaa 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -271,12 +271,5 @@ describe('HDPublicKey interface', function() { valid = HDPublicKey.isValidPath(HDPublicKey.Hardened); valid.should.equal(false); }); - - it('should use the cache', function() { - var pubkey = new HDPublicKey(xpubkey); - var derived1 = pubkey.deriveChild(0); - var derived2 = pubkey.deriveChild(0); - derived1.should.equal(derived2); - }); }); }); From 206aaa0bee7ad1df15457885d98031530672acec Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Tue, 6 Dec 2016 18:28:55 -0500 Subject: [PATCH 10/14] revert premature changes --- .travis.yml | 3 +++ README.md | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index b3e4d403c..6ea5bbfd1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,9 @@ language: node_js sudo: false node_js: + '0.10' + '0.12' + '2.0.0' - '4' before_install: - npm install -g bower diff --git a/README.md b/README.md index b3606e883..4e4456a7f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -**Note**: This is a development/staging branch for 1.0.0 - Bitcore Library ======= From 1b2c41978f5033bf48ca6d872d685c9c18f474db Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Tue, 6 Dec 2016 18:58:01 -0500 Subject: [PATCH 11/14] revert behavior of derive --- lib/hdprivatekey.js | 37 ++++++++++++++++-- lib/hdpublickey.js | 33 ++++++++++++++-- test/hdkeys.js | 91 ++++++++++++++++++++++---------------------- test/hdprivatekey.js | 12 +++--- test/hdpublickey.js | 16 ++++---- 5 files changed, 124 insertions(+), 65 deletions(-) diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 8c4e2bb80..9d0a42e9e 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -126,12 +126,40 @@ HDPrivateKey._getDerivationIndexes = function(path) { return _.any(indexes, isNaN) ? null : indexes; }; -HDPrivateKey.prototype.derive = function() { - throw new Error('Method has been deprecated, please use deriveChild or deriveNonCompliantChild' + - 'and see documentation for more information'); +/** + * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. + * + * + * Get a derived child based on a string or number. + * + * If the first argument is a string, it's parsed as the full path of + * derivation. Valid values for this argument include "m" (which returns the + * same private key), "m/0/1/40/2'/1000", where the ' quote means a hardened + * derivation. + * + * If the first argument is a number, the child with that index will be + * derived. If the second argument is truthy, the hardened version will be + * derived. See the example usage for clarification. + * + * @example + * ```javascript + * var parent = new HDPrivateKey('xprv...'); + * var child_0_1_2h = parent.derive(0).derive(1).derive(2, true); + * var copy_of_child_0_1_2h = parent.derive("m/0/1/2'"); + * assert(child_0_1_2h.xprivkey === copy_of_child_0_1_2h); + * ``` + * + * @param {string|number} arg + * @param {boolean?} hardened + */ +HDPrivateKey.prototype.derive = function(arg, hardened) { + return this.deriveNonCompliantChild(arg, hardened); }; /** + * WARNING: This method will not be officially supported until v1.0.0. + * + * * Get a derived child based on a string or number. * * If the first argument is a string, it's parsed as the full path of @@ -170,6 +198,9 @@ HDPrivateKey.prototype.deriveChild = function(arg, hardened, nonCompliant) { }; /** + * WARNING: This method will not be officially supported until v1.0.0 + * + * * WARNING: If this is a new implementation you should NOT use this method, you should be using * `derive` instead. * diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index ee083b612..b4024b726 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -85,12 +85,39 @@ HDPublicKey.isValidPath = function(arg) { return false; }; -HDPublicKey.prototype.derive = function() { - throw new Error('Method has been deprecated, please use deriveChild instead' + - 'and see documentation for more information'); +/** + * WARNING: This method is deprecated. Use deriveChild instead. + * + * + * Get a derivated child based on a string or number. + * + * If the first argument is a string, it's parsed as the full path of + * derivation. Valid values for this argument include "m" (which returns the + * same public key), "m/0/1/40/2/1000". + * + * Note that hardened keys can't be derived from a public extended key. + * + * If the first argument is a number, the child with that index will be + * derived. See the example usage for clarification. + * + * @example + * ```javascript + * var parent = new HDPublicKey('xpub...'); + * var child_0_1_2 = parent.derive(0).derive(1).derive(2); + * var copy_of_child_0_1_2 = parent.derive("m/0/1/2"); + * assert(child_0_1_2.xprivkey === copy_of_child_0_1_2); + * ``` + * + * @param {string|number} arg + */ +HDPublicKey.prototype.derive = function(arg, hardened) { + return this.deriveChild(arg, hardened); }; /** + * WARNING: This method will not be officially supported until v1.0.0. + * + * * Get a derivated child based on a string or number. * * If the first argument is a string, it's parsed as the full path of diff --git a/test/hdkeys.js b/test/hdkeys.js index 0c591ff40..ec680d70e 100644 --- a/test/hdkeys.js +++ b/test/hdkeys.js @@ -81,69 +81,69 @@ describe('BIP32 compliance', function() { }); it("should get m/0' ext. private key from test vector 1", function() { - var privateKey = new HDPrivateKey(vector1_m_private).deriveChild("m/0'"); + var privateKey = new HDPrivateKey(vector1_m_private).derive("m/0'"); privateKey.xprivkey.should.equal(vector1_m0h_private); }); it("should get m/0' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'") + HDPrivateKey(vector1_m_private).derive("m/0'") .xpubkey.should.equal(vector1_m0h_public); }); it("should get m/0'/1 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") + HDPrivateKey(vector1_m_private).derive("m/0'/1") .xprivkey.should.equal(vector1_m0h1_private); }); it("should get m/0'/1 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1") + HDPrivateKey(vector1_m_private).derive("m/0'/1") .xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1 ext. public key from m/0' public key from test vector 1", function() { - var derivedPublic = HDPrivateKey(vector1_m_private).deriveChild("m/0'").hdPublicKey.deriveChild("m/1"); + var derivedPublic = HDPrivateKey(vector1_m_private).derive("m/0'").hdPublicKey.derive("m/1"); derivedPublic.xpubkey.should.equal(vector1_m0h1_public); }); it("should get m/0'/1/2' ext. private key from test vector 1", function() { var privateKey = new HDPrivateKey(vector1_m_private); - var derived = privateKey.deriveChild("m/0'/1/2'"); + var derived = privateKey.derive("m/0'/1/2'"); derived.xprivkey.should.equal(vector1_m0h12h_private); }); it("should get m/0'/1/2' ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'") .xpubkey.should.equal(vector1_m0h12h_public); }); it("should get m/0'/1/2'/2 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") .xprivkey.should.equal(vector1_m0h12h2_private); }); it("should get m/0'/1/2'/2 ext. public key from m/0'/1/2' public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'").hdPublicKey; - derived.deriveChild("m/2").xpubkey.should.equal(vector1_m0h12h2_public); + var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'").hdPublicKey; + derived.derive("m/2").xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2") .xpubkey.should.equal(vector1_m0h12h2_public); }); it("should get m/0'/1/2h/2/1000000000 ext. private key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") .xprivkey.should.equal(vector1_m0h12h21000000000_private); }); it("should get m/0'/1/2h/2/1000000000 ext. public key from test vector 1", function() { - HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2/1000000000") + HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2/1000000000") .xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it("should get m/0'/1/2'/2/1000000000 ext. public key from m/0'/1/2'/2 public key from test vector 1", function() { - var derived = HDPrivateKey(vector1_m_private).deriveChild("m/0'/1/2'/2").hdPublicKey; - derived.deriveChild("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); + var derived = HDPrivateKey(vector1_m_private).derive("m/0'/1/2'/2").hdPublicKey; + derived.derive("m/1000000000").xpubkey.should.equal(vector1_m0h12h21000000000_public); }); it('should initialize test vector 2 from the extended public key', function() { @@ -159,66 +159,66 @@ describe('BIP32 compliance', function() { }); it("should get m/0 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild(0).xprivkey.should.equal(vector2_m0_private); + HDPrivateKey(vector2_m_private).derive(0).xprivkey.should.equal(vector2_m0_private); }); it("should get m/0 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).derive(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0 ext. public key from m public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).hdPublicKey.deriveChild(0).xpubkey.should.equal(vector2_m0_public); + HDPrivateKey(vector2_m_private).hdPublicKey.derive(0).xpubkey.should.equal(vector2_m0_public); }); it("should get m/0/2147483647h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") .xprivkey.should.equal(vector2_m02147483647h_private); }); it("should get m/0/2147483647h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'") .xpubkey.should.equal(vector2_m02147483647h_public); }); it("should get m/0/2147483647h/1 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") .xprivkey.should.equal(vector2_m02147483647h1_private); }); it("should get m/0/2147483647h/1 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1") .xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1 ext. public key from m/0/2147483647h public key from test vector 2", function() { - var derived = HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'").hdPublicKey; - derived.deriveChild(1).xpubkey.should.equal(vector2_m02147483647h1_public); + var derived = HDPrivateKey(vector2_m_private).derive("m/0/2147483647'").hdPublicKey; + derived.derive(1).xpubkey.should.equal(vector2_m02147483647h1_public); }); it("should get m/0/2147483647h/1/2147483646h ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") .xprivkey.should.equal(vector2_m02147483647h12147483646h_private); }); it("should get m/0/2147483647h/1/2147483646h ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'") .xpubkey.should.equal(vector2_m02147483647h12147483646h_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. private key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") .xprivkey.should.equal(vector2_m02147483647h12147483646h2_private); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from test vector 2", function() { - HDPrivateKey(vector2_m_private).deriveChild("m/0/2147483647'/1/2147483646'/2") + HDPrivateKey(vector2_m_private).derive("m/0/2147483647'/1/2147483646'/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); it("should get m/0/2147483647h/1/2147483646h/2 ext. public key from m/0/2147483647h/2147483646h public key from test vector 2", function() { var derivedPublic = HDPrivateKey(vector2_m_private) - .deriveChild("m/0/2147483647'/1/2147483646'").hdPublicKey; - derivedPublic.deriveChild("m/2") + .derive("m/0/2147483647'/1/2147483646'").hdPublicKey; + derivedPublic.derive("m/2") .xpubkey.should.equal(vector2_m02147483647h12147483646h2_public); }); @@ -254,19 +254,20 @@ describe('BIP32 compliance', function() { derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); - it('hdprivatekey will throw deprecation message', function() { - expect(function() { - var key = new HDPrivateKey(); - key.derive(); - }).to.throw('Method has been deprecated'); - }); - - it('hdpublickey will throw deprecation message', function() { - expect(function() { - var key = new HDPrivateKey(); - var pubkey = key.hdPublicKey; - pubkey.derive(); - }).to.throw('Method has been deprecated'); + it('should NOT use full 32 bytes for private key data that is hashed with the nonCompliant derive method', function() { + // This is to test that the previously implemented non-compliant to BIP32 + var privateKeyBuffer = new Buffer('00000055378cf5fafb56c711c674143f9b0ee82ab0ba2924f19b64f5ae7cdbfd', 'hex'); + var chainCodeBuffer = new Buffer('9c8a5c863e5941f3d99453e6ba66b328bb17cf0b8dec89ed4fc5ace397a1c089', 'hex'); + var key = HDPrivateKey.fromObject({ + network: 'testnet', + depth: 0, + parentFingerPrint: 0, + childIndex: 0, + privateKey: privateKeyBuffer, + chainCode: chainCodeBuffer + }); + var derived = key.derive("m/44'/0'/0'/0/0'"); + derived.privateKey.toString().should.equal('4811a079bab267bfdca855b3bddff20231ff7044e648514fa099158472df2836'); }); describe('edge cases', function() { @@ -299,7 +300,7 @@ describe('BIP32 compliance', function() { privateKey: privateKeyBuffer, chainCode: chainCodeBuffer }); - var derived = key.deriveChild("m/44'"); + var derived = key.derive("m/44'"); derived.privateKey.toString().should.equal('b15bce3608d607ee3a49069197732c656bca942ee59f3e29b4d56914c1de6825'); bitcore.PrivateKey.isValid.callCount.should.equal(2); }); @@ -320,7 +321,7 @@ describe('BIP32 compliance', function() { throw new Error('Point cannot be equal to Infinity'); }; sandbox.spy(key, '_deriveWithNumber'); - var derived = key.deriveChild("m/44"); + var derived = key.derive("m/44"); key._deriveWithNumber.callCount.should.equal(2); key.publicKey.toString().should.equal('029e58b241790284ef56502667b15157b3fc58c567f044ddc35653860f9455d099'); }); diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index 2e99591f4..6b63e6279 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -30,7 +30,7 @@ describe('HDPrivate key interface', function() { var expectDerivationFail = function(argument, error) { return expectFail(function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.deriveChild(argument); + privateKey.derive(argument); }, error); }; @@ -123,14 +123,14 @@ describe('HDPrivate key interface', function() { it('allows derivation of hardened keys by passing a very big number', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByNumber = privateKey.deriveChild(0x80000000); - var derivedByArgument = privateKey.deriveChild(0, true); + var derivedByNumber = privateKey.derive(0x80000000); + var derivedByArgument = privateKey.derive(0, true); derivedByNumber.xprivkey.should.equal(derivedByArgument.xprivkey); }); it('returns itself with \'m\' parameter', function() { var privateKey = new HDPrivateKey(xprivkey); - privateKey.should.equal(privateKey.deriveChild('m')); + privateKey.should.equal(privateKey.derive('m')); }); it('returns InvalidArgument if invalid data is given to getSerializedError', function() { @@ -203,8 +203,8 @@ describe('HDPrivate key interface', function() { it('shouldn\'t matter if derivations are made with strings or numbers', function() { var privateKey = new HDPrivateKey(xprivkey); - var derivedByString = privateKey.deriveChild('m/0\'/1/2\''); - var derivedByNumber = privateKey.deriveChild(0, true).deriveChild(1).deriveChild(2, true); + var derivedByString = privateKey.derive('m/0\'/1/2\''); + var derivedByNumber = privateKey.derive(0, true).derive(1).derive(2, true); derivedByNumber.xprivkey.should.equal(derivedByString.xprivkey); }); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index 457f1bdaa..d51ee3532 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -32,7 +32,7 @@ describe('HDPublicKey interface', function() { var expectDerivationFail = function(argument, error) { (function() { var pubkey = new HDPublicKey(xpubkey); - pubkey.deriveChild(argument); + pubkey.derive(argument); }).should.throw(error); }; @@ -188,15 +188,15 @@ describe('HDPublicKey interface', function() { describe('derivation', function() { it('derivation is the same whether deriving with number or string', function() { var pubkey = new HDPublicKey(xpubkey); - var derived1 = pubkey.deriveChild(0).deriveChild(1).deriveChild(200000); - var derived2 = pubkey.deriveChild('m/0/1/200000'); + var derived1 = pubkey.derive(0).derive(1).derive(200000); + var derived2 = pubkey.derive('m/0/1/200000'); derived1.xpubkey.should.equal(derived_0_1_200000); derived2.xpubkey.should.equal(derived_0_1_200000); }); it('allows special parameters m, M', function() { var expectDerivationSuccess = function(argument) { - new HDPublicKey(xpubkey).deriveChild(argument).xpubkey.should.equal(xpubkey); + new HDPublicKey(xpubkey).derive(argument).xpubkey.should.equal(xpubkey); }; expectDerivationSuccess('m'); expectDerivationSuccess('M'); @@ -204,13 +204,13 @@ describe('HDPublicKey interface', function() { it('doesn\'t allow object arguments for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild({}); + return new HDPublicKey(xpubkey).derive({}); }, hdErrors.InvalidDerivationArgument); }); it('needs first argument for derivation', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild('s'); + return new HDPublicKey(xpubkey).derive('s'); }, hdErrors.InvalidPath); }); @@ -224,13 +224,13 @@ describe('HDPublicKey interface', function() { it('can\'t derive hardened keys', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild(HDPublicKey.Hardened); + return new HDPublicKey(xpubkey).derive(HDPublicKey.Hardened); }, hdErrors.InvalidIndexCantDeriveHardened); }); it('can\'t derive hardened keys via second argument', function() { expectFail(function() { - return new HDPublicKey(xpubkey).deriveChild(5, true); + return new HDPublicKey(xpubkey).derive(5, true); }, hdErrors.InvalidIndexCantDeriveHardened); }); From a9636322879216c0c66341a24a192d545ef720d4 Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Tue, 6 Dec 2016 19:07:13 -0500 Subject: [PATCH 12/14] remove nonCompliant argument from deriveChild, fix travis file --- .travis.yml | 6 +++--- lib/hdprivatekey.js | 17 ++++++++++------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ea5bbfd1..202aa51e9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,9 @@ language: node_js sudo: false node_js: - '0.10' - '0.12' - '2.0.0' +- '0.10' +- '0.12' +- '2.0.0' - '4' before_install: - npm install -g bower diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 9d0a42e9e..239e97fff 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -184,14 +184,12 @@ HDPrivateKey.prototype.derive = function(arg, hardened) { * * @param {string|number} arg * @param {boolean?} hardened - * @param {boolean?} nonCompliant - This should not be used and only for backwards compatibility - * */ -HDPrivateKey.prototype.deriveChild = function(arg, hardened, nonCompliant) { +HDPrivateKey.prototype.deriveChild = function(arg, hardened) { if (_.isNumber(arg)) { - return this._deriveWithNumber(arg, hardened, nonCompliant); + return this._deriveWithNumber(arg, hardened); } else if (_.isString(arg)) { - return this._deriveFromString(arg, nonCompliant); + return this._deriveFromString(arg); } else { throw new hdErrors.InvalidDerivationArgument(arg); } @@ -214,8 +212,13 @@ HDPrivateKey.prototype.deriveChild = function(arg, hardened, nonCompliant) { * @param {boolean?} hardened */ HDPrivateKey.prototype.deriveNonCompliantChild = function(arg, hardened) { - var derived = this.deriveChild(arg, hardened, true); - return derived; + if (_.isNumber(arg)) { + return this._deriveWithNumber(arg, hardened, true); + } else if (_.isString(arg)) { + return this._deriveFromString(arg, true); + } else { + throw new hdErrors.InvalidDerivationArgument(arg); + } }; HDPrivateKey.prototype._deriveWithNumber = function(index, hardened, nonCompliant) { From d9c295b2d844a38695f696bada890e2a3d80c626 Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Wed, 7 Dec 2016 14:11:54 -0500 Subject: [PATCH 13/14] revert change to privateKey.toBuffer(). Change will be delayed until v1.0.0 --- lib/hdprivatekey.js | 2 +- lib/privatekey.js | 6 +++++- test/privatekey.js | 28 +++++++++++++++------------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 239e97fff..cd65f8d1f 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -127,7 +127,7 @@ HDPrivateKey._getDerivationIndexes = function(path) { }; /** - * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. + * WARNING: This method is deprecated. Use deriveChild or deriveNonCompliantChild instead. This is not BIP32 compliant * * * Get a derived child based on a string or number. diff --git a/lib/privatekey.js b/lib/privatekey.js index 440b0e5e1..f820cae09 100644 --- a/lib/privatekey.js +++ b/lib/privatekey.js @@ -336,10 +336,14 @@ PrivateKey.prototype.toBigNumber = function(){ * @returns {Buffer} A buffer of the private key */ PrivateKey.prototype.toBuffer = function(){ - return this.bn.toBuffer({ size: 32 }); + // TODO: use `return this.bn.toBuffer({ size: 32 })` in v1.0.0 + return this.bn.toBuffer(); }; /** + * WARNING: This method will not be officially supported until v1.0.0. + * + * * Will return the private key as a BN buffer without leading zero padding * * @returns {Buffer} A buffer of the private key diff --git a/test/privatekey.js b/test/privatekey.js index 90f9aeaa4..db45f6080 100644 --- a/test/privatekey.js +++ b/test/privatekey.js @@ -331,13 +331,6 @@ describe('PrivateKey', function() { fromBuffer.toString().should.equal(privkey.toString()); }); - it('will output a 32 byte buffer', function() { - var bn = BN.fromBuffer(new Buffer('9b5a0e8fee1835e21170ce1431f9b6f19b487e67748ed70d8a4462bc031915', 'hex')); - var privkey = new PrivateKey(bn); - var buffer = privkey.toBuffer(); - buffer.length.should.equal(32); - }); - it('will output a 31 byte buffer', function() { var bn = BN.fromBuffer(new Buffer('9b5a0e8fee1835e21170ce1431f9b6f19b487e67748ed70d8a4462bc031915', 'hex')); var privkey = new PrivateKey(bn); @@ -345,12 +338,21 @@ describe('PrivateKey', function() { buffer.length.should.equal(31); }); - it('should return buffer with length equal 32', function() { - var bn = BN.fromBuffer(buf.slice(0, 31)); - var privkey = new PrivateKey(bn, 'livenet'); - var expected = Buffer.concat([ new Buffer([0]), buf.slice(0, 31) ]); - privkey.toBuffer().toString('hex').should.equal(expected.toString('hex')); - }); + // TODO: enable for v1.0.0 when toBuffer is changed to always be 32 bytes long + // it('will output a 32 byte buffer', function() { + // var bn = BN.fromBuffer(new Buffer('9b5a0e8fee1835e21170ce1431f9b6f19b487e67748ed70d8a4462bc031915', 'hex')); + // var privkey = new PrivateKey(bn); + // var buffer = privkey.toBuffer(); + // buffer.length.should.equal(32); + // }); + + // TODO: enable for v1.0.0 when toBuffer is changed to always be 32 bytes long + // it('should return buffer with length equal 32', function() { + // var bn = BN.fromBuffer(buf.slice(0, 31)); + // var privkey = new PrivateKey(bn, 'livenet'); + // var expected = Buffer.concat([ new Buffer([0]), buf.slice(0, 31) ]); + // privkey.toBuffer().toString('hex').should.equal(expected.toString('hex')); + // }); }); describe('#toBigNumber', function() { From 8339bda2d99e16606d8b780cfa26e3e480448067 Mon Sep 17 00:00:00 2001 From: Gabe Gattis Date: Wed, 7 Dec 2016 14:45:20 -0500 Subject: [PATCH 14/14] update hierarchical key docs --- docs/hierarchical.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/hierarchical.md b/docs/hierarchical.md index bac5e37c6..73972d408 100644 --- a/docs/hierarchical.md +++ b/docs/hierarchical.md @@ -15,9 +15,9 @@ var HDPrivateKey = bitcore.HDPrivateKey; var hdPrivateKey = new HDPrivateKey(); var retrieved = new HDPrivateKey('xpriv...'); -var derived = hdPrivateKey.deriveChild("m/0'"); -var derivedByNumber = hdPrivateKey.deriveChild(1).deriveChild(2, true); -var derivedByArgument = hdPrivateKey.deriveChild("m/1/2'"); +var derived = hdPrivateKey.derive("m/0'"); // see deprecation warning for derive +var derivedByNumber = hdPrivateKey.derive(1).derive(2, true); +var derivedByArgument = hdPrivateKey.derive("m/1/2'"); assert(derivedByNumber.xprivkey === derivedByArgument.xprivkey); var address = derived.privateKey.toAddress(); @@ -39,11 +39,14 @@ try { } var address = new Address(hdPublicKey.publicKey, Networks.livenet); -var derivedAddress = new Address(hdPublicKey.deriveChild(100).publicKey, Networks.testnet); +var derivedAddress = new Address(hdPublicKey.derive(100).publicKey, Networks.testnet); // see deprecation warning for derive ``` -## Upgrading from v0.13.x and previous to v1.0.0 +## Deprecation Warning for `HDPublicKey.derive()` and `HDPrivateKey.derive()` -There was a bug that was discovered with derivation that would incorrectly calculate the child key against the [BIP32 specification](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). The method `derive` has been deprecated and replaced with `deriveChild` and `deriveNonCompliantChild`. As the names indicate `deriveNonCompliantChild` will derive using the non-BIP32 derivation and is the equivalent of `derive` in v0.13 and previous versions. The `deriveNonCompliantChild` method should not be used unless you're upgrading and need to maintain compatibility with the old derivation. +There was a bug that was discovered with derivation that would incorrectly calculate the child key against the [BIP32 specification](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki). The bug only affected hardened derivations using an extended private key, and did not affect public key derivation. It also did not affect every derivation and would happen 1 in 256 times where where the private key for the extended private key had a leading zero *(e.g. any private key less than or equal to '0fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')*. The leading zero was not included in serialization before hashing to derive a child key, as it should have been. + +As a result, `HDPublicKey.derive()` and `HDPrivateKey.derive()` are now deprecated. These methods will throw an error in the next major release. +`HDPublicKey.deriveChild()`, `HDPrivateKey.deriveChild()`, and `HDPrivateKey.deriveNonCompliantChild()` have been implemented as alternatives. Note that these new methods will not be officially supported until v1.0.0. `deriveNonCompliantChild` will derive using the non-BIP32 derivation and is equivalent to the buggy version, `derive`. The `deriveNonCompliantChild` method should not be used unless you're upgrading and need to maintain compatibility with the old derivation.