hd refactor.
This commit is contained in:
parent
6d55077818
commit
b164984307
191
lib/bcoin/hd.js
191
lib/bcoin/hd.js
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user