hd refactor.

This commit is contained in:
Christopher Jeffrey 2016-05-13 18:38:36 -07:00
parent 6d55077818
commit b164984307
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 106 additions and 94 deletions

View File

@ -87,7 +87,7 @@ var KeyPair = bcoin.keypair;
var LRU = require('./lru');
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
var unorm = require('../../vendor/unorm');
var unorm;
/**
* HD Mnemonic
@ -137,8 +137,8 @@ Mnemonic.prototype.toSeed = function toSeed() {
this.phrase = this.createMnemonic();
this.seed = utils.pbkdf2(
unorm.nfkd(this.phrase),
unorm.nfkd('mnemonic' + this.passphrase),
nfkd(this.phrase),
nfkd('mnemonic' + this.passphrase),
2048, 64);
return this.seed;
@ -337,6 +337,53 @@ HD.isExtended = function isExtended(data) {
|| HDPublicKey.isExtended(data);
};
/**
* Parse a derivation path and return an array of indexes.
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
* @param {String} path
* @param {Number?} max - Max index.
* @returns {Number[]}
*/
HD.parsePath = function parsePath(path, max) {
var parts = path.split('/');
var root = parts.shift();
var indexes = [];
var i, hardened, index;
if (max == null)
max = constants.hd.MAX_INDEX;
if (constants.hd.PATH_ROOTS.indexOf(path) !== -1)
return indexes;
if (constants.hd.PATH_ROOTS.indexOf(root) === -1)
throw new Error('Bad path root.');
for (i = 0; i < parts.length; i++) {
index = parts[i];
hardened = index[index.length - 1] === '\'';
if (hardened)
index = index.slice(0, -1);
index = +index;
if (!(index >= 0))
throw new Error('Non-number path index.');
if (hardened)
index += constants.hd.HARDENED;
if (!(index >= 0 && index < max))
throw new Error('Index out of range.');
indexes.push(index);
}
return indexes;
};
/**
* LRU cache to avoid deriving keys twice.
* @type {LRU}
@ -594,45 +641,6 @@ HDPrivateKey.isExtended = function isExtended(data) {
return false;
};
/**
* Parse a derivation path and return an array of indexes.
* @see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
* @returns {Number[]}
*/
HDPrivateKey._getIndexes = function _getIndexes(path) {
var steps = path.split('/');
var root = steps.shift();
var indexes = [];
var i, step, hardened, index;
if (~constants.hd.PATH_ROOTS.indexOf(path))
return indexes;
if (!~constants.hd.PATH_ROOTS.indexOf(root))
return null;
for (i = 0; i < steps.length; i++) {
step = steps[i];
hardened = step[step.length - 1] === '\'';
if (hardened)
step = step.slice(0, -1);
if (!step || step[0] === '-')
return null;
index = +step;
if (hardened)
index += constants.hd.HARDENED;
indexes.push(index);
}
return indexes;
};
/**
* Test whether a string is a valid path.
* @param {String} path
@ -640,21 +648,13 @@ HDPrivateKey._getIndexes = function _getIndexes(path) {
* @returns {Boolean}
*/
HDPrivateKey.isValidPath = function isValidPath(path, hardened) {
var indexes;
if (typeof path === 'string') {
indexes = HDPrivateKey._getIndexes(path);
return indexes !== null && indexes.every(HDPrivateKey.isValidPath);
HDPrivateKey.isValidPath = function isValidPath(path) {
try {
HD.parsePath(path, constants.hd.MAX_INDEX);
return true;
} catch (e) {
return false;
}
if (typeof path === 'number') {
if (path < constants.hd.HARDENED && hardened)
path += constants.hd.HARDENED;
return path >= 0 && path < constants.hd.MAX_INDEX;
}
return false;
};
/**
@ -665,16 +665,14 @@ HDPrivateKey.isValidPath = function isValidPath(path, hardened) {
*/
HDPrivateKey.prototype.derivePath = function derivePath(path) {
var indexes;
var indexes = HD.parsePath(path, constants.hd.MAX_INDEX);
var key = this;
var i;
if (!HDPrivateKey.isValidPath(path))
throw new Error('Invalid path.');
for (i = 0; i < indexes.length; i++)
key = key.derive(indexes[i]);
indexes = HDPrivateKey._getIndexes(path);
return indexes.reduce(function(prev, index) {
return prev.derive(index);
}, this);
return key;
};
/**
@ -970,7 +968,10 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
*/
HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
return obj && obj.xprivkey && typeof obj.derive === 'function';
return obj
&& obj.hdPublicKey
&& obj.hdPublicKey !== obj
&& typeof obj.derive === 'function';
};
/**
@ -1127,18 +1128,13 @@ HDPublicKey.prototype.isAccount44 = HDPrivateKey.prototype.isAccount44;
* @returns {Boolean}
*/
HDPublicKey.isValidPath = function isValidPath(arg) {
var indexes;
if (typeof arg === 'string') {
indexes = HDPrivateKey._getIndexes(arg);
return indexes !== null && indexes.every(HDPublicKey.isValidPath);
HDPublicKey.isValidPath = function isValidPath(path) {
try {
HD.parsePath(path, constants.hd.HARDENED);
return true;
} catch (e) {
return false;
}
if (typeof arg === 'number')
return arg >= 0 && arg < constants.hd.HARDENED;
return false;
};
/**
@ -1150,19 +1146,14 @@ HDPublicKey.isValidPath = function isValidPath(arg) {
*/
HDPublicKey.prototype.derivePath = function derivePath(path) {
var indexes;
var indexes = HD.parsePath(path, constants.hd.HARDENED);
var key = this;
var i;
if (path.indexOf('\'') !== -1)
throw new Error('Cannot derive hardened.');
for (i = 0; i < indexes.length; i++)
key = key.derive(indexes[i]);
if (!HDPublicKey.isValidPath(path))
throw new Error('Invalid path.');
indexes = HDPrivateKey._getIndexes(path);
return indexes.reduce(function(prev, index) {
return prev.derive(index);
}, this);
return key;
};
/**
@ -1291,8 +1282,8 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) {
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
return obj
&& obj.xpubkey
&& !obj.xprivkey
&& obj.hdPublicKey
&& obj.hdPublicKey === obj
&& typeof obj.derive === 'function';
};
@ -1352,10 +1343,28 @@ HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
* @returns {Base58String}
*/
HDPrivateKey.prototype.toSecret = function toSecret() {
return KeyPair.prototype.toSecret.call(this);
HDPrivateKey.prototype.toSecret = function toSecret(network) {
return KeyPair.prototype.toSecret.call(this, network);
};
/*
* Helpers
*/
function nfkd(str) {
if (str.normalize)
return str.normalize('NFKD');
if (!unorm)
unorm = require('../../vendor/unorm');
return unorm.nfkd(str);
}
/*
* Expose
*/
HD.Mnemonic = Mnemonic;
HD.PrivateKey = HDPrivateKey;
HD.PublicKey = HDPublicKey;

View File

@ -128,8 +128,11 @@ KeyPair.prototype.getPublicKey = function getPublicKey(enc) {
* @returns {Base58String}
*/
KeyPair.prototype.toSecret = function toSecret() {
return KeyPair.toSecret(this.getPrivateKey(), this.compressed, this.network);
KeyPair.prototype.toSecret = function toSecret(network) {
if (!network)
network = this.network;
return KeyPair.toSecret(this.getPrivateKey(), this.compressed, network);
};
/**
@ -180,7 +183,7 @@ KeyPair.parseSecret = function parseSecret(secret) {
privateKey = p.readBytes(32);
if (p.left() > 4) {
assert(p.readU8() === 1);
assert(p.readU8() === 1, 'Bad compression flag.');
compressed = true;
}