Browse Source

Change name of derive to deriveChild

This is to avoid any accidental upgrades to a bugfixed version without awareness of the change.
patch-2
Braydon Fuller 8 years ago
parent
commit
d32ae41adc
  1. 14
      docs/hierarchical.md
  2. 15
      lib/hdprivatekey.js
  3. 11
      lib/hdpublickey.js
  4. 24
      test/hdkeycache.js
  5. 83
      test/hdkeys.js
  6. 12
      test/hdprivatekey.js
  7. 20
      test/hdpublickey.js

14
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.

15
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;
};

11
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)) {

24
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);
});
});

83
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');
});

12
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);
});

20
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);
});
});

Loading…
Cancel
Save