refactor hd.
This commit is contained in:
parent
77f203f7cb
commit
c8e771d05b
280
lib/bcoin/hd.js
280
lib/bcoin/hd.js
@ -91,6 +91,9 @@ function HDSeed(options) {
|
||||
this.mnemonic = options.mnemonic;
|
||||
this.passphrase = options.passphrase || '';
|
||||
|
||||
if (Buffer.isBuffer(options))
|
||||
this.seed = options;
|
||||
|
||||
assert(this.bits % 8 === 0);
|
||||
}
|
||||
|
||||
@ -163,7 +166,9 @@ HDSeed.isHDSeed = function isHDSeed(obj) {
|
||||
*/
|
||||
|
||||
function HD(options) {
|
||||
return new HDPrivateKey(options);
|
||||
if (!options)
|
||||
return HD.fromSeed();
|
||||
return HD.fromAny(options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,6 +207,42 @@ HD.fromSeed = function fromSeed(options, networkType) {
|
||||
return HDPrivateKey.fromSeed(options, networkType);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate an hdkey from any number of options.
|
||||
* @param {Object|HDSeed} options - HD seed, HD seed
|
||||
* options, buffer seed, or base58 key.
|
||||
* @param {String?} networkType
|
||||
* @returns {HDPrivateKey|HDPublicKey}
|
||||
*/
|
||||
|
||||
HD.fromAny = function fromAny(options, networkType) {
|
||||
var xkey;
|
||||
|
||||
assert(options, 'Options required.');
|
||||
|
||||
if (options.xkey)
|
||||
xkey = options.xkey;
|
||||
else if (options.xpubkey)
|
||||
xkey = options.xpubkey;
|
||||
else if (options.xprivkey)
|
||||
xkey = options.xprivkey;
|
||||
else
|
||||
xkey = options;
|
||||
|
||||
if (HDPrivateKey.isExtended(xkey))
|
||||
return HDPrivateKey.fromBase58(xkey);
|
||||
|
||||
if (HDPublicKey.isExtended(xkey))
|
||||
return HDPublicKey.fromBase58(xkey);
|
||||
|
||||
return HDPrivateKey.fromSeed(options, networkType);
|
||||
};
|
||||
|
||||
/**
|
||||
* LRU cache to avoid deriving keys twice.
|
||||
* @type {LRU}
|
||||
*/
|
||||
|
||||
HD.cache = new LRU(500);
|
||||
|
||||
/**
|
||||
@ -228,7 +269,7 @@ HD.isHD = function isHD(obj) {
|
||||
* @param {Number?} options.childIndex
|
||||
* @param {Buffer?} options.chainCode
|
||||
* @param {Buffer?} options.privateKey
|
||||
* @property {String} networkType
|
||||
* @property {String} network
|
||||
* @property {Base58String} xprivkey
|
||||
* @property {Base58String} xpubkey
|
||||
* @property {HDSeed?} seed
|
||||
@ -239,67 +280,33 @@ HD.isHD = function isHD(obj) {
|
||||
* @property {Buffer} chainCode
|
||||
* @property {Buffer} privateKey
|
||||
* @property {HDPublicKey} hdPublicKey
|
||||
* @property {Boolean} isPrivate
|
||||
* @property {Boolean} isPublic
|
||||
*/
|
||||
|
||||
function HDPrivateKey(options) {
|
||||
var data;
|
||||
|
||||
if (!(this instanceof HDPrivateKey))
|
||||
return new HDPrivateKey(options);
|
||||
|
||||
assert(options, 'No options for HD private key.');
|
||||
assert(!(options instanceof HDPrivateKey));
|
||||
assert(!(options instanceof HDPublicKey));
|
||||
assert(options.depth <= 0xff, 'Depth is too high.');
|
||||
|
||||
if (HDPrivateKey.isExtended(options))
|
||||
options = { xkey: options };
|
||||
|
||||
if (options.xpubkey)
|
||||
options.xkey = options.xpubkey;
|
||||
|
||||
if (options.xprivkey)
|
||||
options.xkey = options.xprivkey;
|
||||
|
||||
if (HDPublicKey.isExtended(options.xkey))
|
||||
return new HDPublicKey(options);
|
||||
|
||||
this.networkType = options.networkType || network.type;
|
||||
this.xprivkey = options.xkey;
|
||||
this.network = options.network || network.type;
|
||||
this.xprivkey = options.xprivkey;
|
||||
this.seed = options.seed;
|
||||
|
||||
if (options.data) {
|
||||
data = options.data;
|
||||
} else if (options.xkey) {
|
||||
data = HDPrivateKey.parse(options.xkey);
|
||||
this.networkType = data.networkType;
|
||||
data = data.data;
|
||||
} else if (options.seed) {
|
||||
data = HDPrivateKey._fromSeed(options.seed, this.networkType);
|
||||
} else {
|
||||
assert(false, 'No data passed to HD key.');
|
||||
}
|
||||
this.version = options.version;
|
||||
this.depth = options.depth;
|
||||
this.parentFingerPrint = options.parentFingerPrint;
|
||||
this.childIndex = options.childIndex;
|
||||
this.chainCode = options.chainCode;
|
||||
this.privateKey = options.privateKey;
|
||||
|
||||
assert(data.depth <= 0xff, 'Depth is too high.');
|
||||
|
||||
this.version = data.version;
|
||||
this.depth = data.depth;
|
||||
this.parentFingerPrint = data.parentFingerPrint;
|
||||
this.childIndex = data.childIndex;
|
||||
this.chainCode = data.chainCode;
|
||||
this.privateKey = data.privateKey;
|
||||
|
||||
this.publicKey = ec.publicKeyCreate(data.privateKey, true);
|
||||
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
|
||||
this.fingerPrint = null;
|
||||
|
||||
this.hdPrivateKey = this;
|
||||
|
||||
if (!this.xprivkey)
|
||||
this.xprivkey = HDPrivateKey.render(data);
|
||||
|
||||
this.isPrivate = true;
|
||||
this.isPublic = false;
|
||||
this.xprivkey = HDPrivateKey.render(options);
|
||||
}
|
||||
|
||||
utils.inherits(HDPrivateKey, HD);
|
||||
@ -307,15 +314,13 @@ utils.inherits(HDPrivateKey, HD);
|
||||
HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
|
||||
if (!this._hdPublicKey) {
|
||||
this._hdPublicKey = new HDPublicKey({
|
||||
networkType: this.networkType,
|
||||
data: {
|
||||
version: network[this.networkType].prefixes.xpubkey,
|
||||
depth: this.depth,
|
||||
parentFingerPrint: this.parentFingerPrint,
|
||||
childIndex: this.childIndex,
|
||||
chainCode: this.chainCode,
|
||||
publicKey: this.publicKey
|
||||
}
|
||||
network: this.network,
|
||||
version: network[this.network].prefixes.xpubkey,
|
||||
depth: this.depth,
|
||||
parentFingerPrint: this.parentFingerPrint,
|
||||
childIndex: this.childIndex,
|
||||
chainCode: this.chainCode,
|
||||
publicKey: this.publicKey
|
||||
});
|
||||
}
|
||||
return this._hdPublicKey;
|
||||
@ -375,15 +380,13 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
|
||||
}
|
||||
|
||||
child = new HDPrivateKey({
|
||||
networkType: this.networkType,
|
||||
data: {
|
||||
version: this.version,
|
||||
depth: this.depth + 1,
|
||||
parentFingerPrint: this.fingerPrint,
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
privateKey: privateKey
|
||||
}
|
||||
network: this.network,
|
||||
version: this.version,
|
||||
depth: this.depth + 1,
|
||||
parentFingerPrint: this.fingerPrint,
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
privateKey: privateKey
|
||||
});
|
||||
|
||||
HD.cache.set(this.xprivkey + '/' + index, child);
|
||||
@ -413,7 +416,7 @@ HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
|
||||
}
|
||||
|
||||
if (coinType == null)
|
||||
coinType = this.networkType === 'main' ? 0 : 1;
|
||||
coinType = this.network === 'main' ? 0 : 1;
|
||||
|
||||
assert(utils.isFinite(coinType));
|
||||
assert(utils.isFinite(accountIndex));
|
||||
@ -577,7 +580,7 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
|
||||
|
||||
/**
|
||||
* Create an hd private key from a seed.
|
||||
* @param {HDSeed} seed
|
||||
* @param {Buffer|HDSeed|Object} options - A buffer, HD seed, or HD seed options.
|
||||
* @param {String?} networkType
|
||||
* @returns {Object} A "naked" key (a
|
||||
* plain javascript object which is suitable
|
||||
@ -585,8 +588,20 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
|
||||
*/
|
||||
|
||||
HDPrivateKey._fromSeed = function _fromSeed(seed, networkType) {
|
||||
var data = seed.createSeed();
|
||||
var hash;
|
||||
var data, hash;
|
||||
|
||||
if (!seed)
|
||||
seed = {};
|
||||
|
||||
if (Buffer.isBuffer(seed)) {
|
||||
data = seed;
|
||||
seed = null;
|
||||
} else if (seed instanceof HDSeed) {
|
||||
data = seed.createSeed();
|
||||
} else {
|
||||
seed = new HDSeed(seed);
|
||||
data = seed.createSeed();
|
||||
}
|
||||
|
||||
if (data.length < constants.hd.MIN_ENTROPY
|
||||
|| data.length > constants.hd.MAX_ENTROPY) {
|
||||
@ -603,34 +618,20 @@ HDPrivateKey._fromSeed = function _fromSeed(seed, networkType) {
|
||||
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
||||
childIndex: 0,
|
||||
chainCode: hash.slice(32, 64),
|
||||
privateKey: hash.slice(0, 32)
|
||||
privateKey: hash.slice(0, 32),
|
||||
seed: seed
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a transaction from an HD seed.
|
||||
* @param {HDSeed|Object} options - An HD seed or HD seed options.
|
||||
* @param {Buffer|HDSeed|Object} seed - A buffer, HD seed, or HD seed options.
|
||||
* @param {String?} networkType
|
||||
* @returns {HDPrivateKey}
|
||||
*/
|
||||
|
||||
HDPrivateKey.fromSeed = function fromSeed(options, networkType) {
|
||||
var seed, key;
|
||||
|
||||
if (!options)
|
||||
options = {};
|
||||
|
||||
seed = (options instanceof HDSeed)
|
||||
? options
|
||||
: new HDSeed(options);
|
||||
|
||||
key = new HDPrivateKey({
|
||||
data: HDPrivateKey._fromSeed(seed, networkType)
|
||||
});
|
||||
|
||||
key.seed = seed;
|
||||
|
||||
return key;
|
||||
HDPrivateKey.fromSeed = function fromSeed(seed, networkType) {
|
||||
return new HDPrivateKey(HDPrivateKey._fromSeed(seed, networkType));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -679,9 +680,7 @@ HDPrivateKey._generate = function _generate(options, networkType) {
|
||||
*/
|
||||
|
||||
HDPrivateKey.generate = function generate(options, networkType) {
|
||||
return new HDPrivateKey({
|
||||
data: HDPrivateKey._generate(options, networkType)
|
||||
});
|
||||
return new HDPrivateKey(HDPrivateKey._generate(options, networkType));
|
||||
};
|
||||
|
||||
/**
|
||||
@ -714,11 +713,10 @@ HDPrivateKey.parse = function parse(xkey) {
|
||||
|
||||
assert(i < network.types.length, 'Network not found.');
|
||||
|
||||
return {
|
||||
networkType: type,
|
||||
xprivkey: xkey,
|
||||
data: data
|
||||
};
|
||||
data.network = type;
|
||||
data.xprivkey = xkey;
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -865,7 +863,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
|
||||
*/
|
||||
|
||||
HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
|
||||
return obj && obj.isPrivate && typeof obj.derive === 'function';
|
||||
return obj && obj.xprivkey && typeof obj.derive === 'function';
|
||||
};
|
||||
|
||||
/**
|
||||
@ -880,7 +878,7 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
|
||||
* @param {Number?} options.childIndex
|
||||
* @param {Buffer?} options.chainCode
|
||||
* @param {Buffer?} options.publicKey
|
||||
* @property {String} networkType
|
||||
* @property {String} network
|
||||
* @property {Base58String} xpubkey
|
||||
* @property {Number} version
|
||||
* @property {Number} depth
|
||||
@ -888,67 +886,34 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
|
||||
* @property {Number} childIndex
|
||||
* @property {Buffer} chainCode
|
||||
* @property {Buffer} publicKey
|
||||
* @property {Boolean} isPrivate
|
||||
* @property {Boolean} isPublic
|
||||
*/
|
||||
|
||||
function HDPublicKey(options) {
|
||||
var data;
|
||||
|
||||
if (!(this instanceof HDPublicKey))
|
||||
return new HDPublicKey(options);
|
||||
|
||||
assert(options, 'No options for HDPublicKey');
|
||||
assert(!(options instanceof HDPrivateKey));
|
||||
assert(!(options instanceof HDPublicKey));
|
||||
assert(options.depth <= 0xff, 'Depth is too high.');
|
||||
|
||||
if (HDPublicKey.isExtended(options))
|
||||
options = { xkey: options };
|
||||
this.network = options.network || network.type;
|
||||
this.xpubkey = options.xpubkey;
|
||||
this.xprivkey = null;
|
||||
|
||||
if (options.xprivkey)
|
||||
options.xkey = options.xprivkey;
|
||||
|
||||
if (options.xpubkey)
|
||||
options.xkey = options.xpubkey;
|
||||
|
||||
if (HDPrivateKey.isExtended(options.xkey))
|
||||
throw new Error('Cannot pass xprivkey into HDPublicKey');
|
||||
|
||||
this.networkType = options.networkType || network.type;
|
||||
this.xpubkey = options.xkey;
|
||||
|
||||
if (options.data) {
|
||||
data = options.data;
|
||||
} else if (options.xkey) {
|
||||
data = HDPublicKey.parse(options.xkey);
|
||||
this.networkType = data.networkType;
|
||||
data = data.data;
|
||||
} else {
|
||||
assert(false, 'No data passed to HD key.');
|
||||
}
|
||||
|
||||
assert(data.depth <= 0xff, 'Depth is too high.');
|
||||
|
||||
this.version = data.version;
|
||||
this.depth = data.depth;
|
||||
this.parentFingerPrint = data.parentFingerPrint;
|
||||
this.childIndex = data.childIndex;
|
||||
this.chainCode = data.chainCode;
|
||||
this.publicKey = data.publicKey;
|
||||
this.version = options.version;
|
||||
this.depth = options.depth;
|
||||
this.parentFingerPrint = options.parentFingerPrint;
|
||||
this.childIndex = options.childIndex;
|
||||
this.chainCode = options.chainCode;
|
||||
this.publicKey = options.publicKey;
|
||||
|
||||
this.privateKey = null;
|
||||
this.fingerPrint = null;
|
||||
|
||||
this.hdPublicKey = this;
|
||||
|
||||
this.hdPrivateKey = null;
|
||||
this.xprivkey = null;
|
||||
|
||||
if (!this.xpubkey)
|
||||
this.xpubkey = HDPublicKey.render(data);
|
||||
|
||||
this.isPrivate = false;
|
||||
this.isPublic = true;
|
||||
this.xpubkey = HDPublicKey.render(options);
|
||||
}
|
||||
|
||||
utils.inherits(HDPublicKey, HD);
|
||||
@ -999,15 +964,13 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
||||
}
|
||||
|
||||
child = new HDPublicKey({
|
||||
networkType: this.networkType,
|
||||
data: {
|
||||
version: this.version,
|
||||
depth: this.depth + 1,
|
||||
parentFingerPrint: this.fingerPrint,
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
publicKey: publicKey
|
||||
}
|
||||
network: this.network,
|
||||
version: this.version,
|
||||
depth: this.depth + 1,
|
||||
parentFingerPrint: this.fingerPrint,
|
||||
childIndex: index,
|
||||
chainCode: chainCode,
|
||||
publicKey: publicKey
|
||||
});
|
||||
|
||||
HD.cache.set(this.xpubkey + '/' + index, child);
|
||||
@ -1015,7 +978,6 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
||||
return child;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Derive a BIP44 account key (does not derive, only ensures account key).
|
||||
* @method
|
||||
@ -1164,11 +1126,10 @@ HDPublicKey.parse = function parse(xkey) {
|
||||
|
||||
assert(i < network.types.length, 'Network not found.');
|
||||
|
||||
return {
|
||||
networkType: type,
|
||||
xpubkey: xkey,
|
||||
data: data
|
||||
};
|
||||
data.network = type;
|
||||
data.xpubkey = xkey;
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1207,7 +1168,10 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) {
|
||||
*/
|
||||
|
||||
HDPublicKey.isHDPublicKey = function isHDPublicKey(obj) {
|
||||
return obj && obj.isPublic && typeof obj.derive === 'function';
|
||||
return obj
|
||||
&& obj.xpubkey
|
||||
&& !obj.xprivkey
|
||||
&& typeof obj.derive === 'function';
|
||||
};
|
||||
|
||||
[HDPrivateKey, HDPublicKey].forEach(function(HD) {
|
||||
|
||||
@ -44,6 +44,9 @@ var assert = utils.assert;
|
||||
var constants = bcoin.protocol.constants;
|
||||
var network = bcoin.protocol.network;
|
||||
var BufferWriter = require('./writer');
|
||||
var HD = bcoin.hd;
|
||||
var HDPrivateKey = bcoin.hd.privateKey;
|
||||
var HDPublicKey = bcoin.hd.publicKey;
|
||||
|
||||
/**
|
||||
* HD BIP-44/45 wallet
|
||||
@ -95,12 +98,12 @@ function Wallet(options) {
|
||||
|
||||
if (options.master
|
||||
&& typeof options.master === 'object'
|
||||
&& !(options.master instanceof bcoin.hd)) {
|
||||
options.master = bcoin.hd(options.master);
|
||||
&& !(options.master instanceof HD)) {
|
||||
options.master = HD.fromAny(options.master);
|
||||
}
|
||||
|
||||
if (!options.master)
|
||||
options.master = bcoin.hd.privateKey.fromSeed();
|
||||
options.master = HD.fromSeed();
|
||||
|
||||
this.options = options;
|
||||
this.provider = options.provider || null;
|
||||
@ -274,15 +277,15 @@ Wallet.prototype.addKey = function addKey(key) {
|
||||
key = key.accountKey;
|
||||
}
|
||||
|
||||
if (bcoin.hd.privateKey.isExtended(key))
|
||||
key = bcoin.hd.privateKey.fromBase58(key);
|
||||
else if (bcoin.hd.publicKey.isExtended(key))
|
||||
key = bcoin.hd.publicKey.fromBase58(key);
|
||||
if (HDPrivateKey.isExtended(key))
|
||||
key = HDPrivateKey.fromBase58(key);
|
||||
else if (HDPublicKey.isExtended(key))
|
||||
key = HDPublicKey.fromBase58(key);
|
||||
|
||||
if (key instanceof bcoin.hd.privateKey)
|
||||
if (key instanceof HDPrivateKey)
|
||||
key = key.hdPublicKey;
|
||||
|
||||
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
|
||||
assert(key instanceof HD, 'Must add HD keys to wallet.');
|
||||
|
||||
if (this.derivation === 'bip44') {
|
||||
if (!key || !key.isAccount44())
|
||||
@ -327,15 +330,15 @@ Wallet.prototype.removeKey = function removeKey(key) {
|
||||
key = key.accountKey;
|
||||
}
|
||||
|
||||
if (bcoin.hd.privateKey.isExtended(key))
|
||||
key = bcoin.hd.privateKey.fromBase58(key);
|
||||
else if (bcoin.hd.publicKey.isExtended(key))
|
||||
key = bcoin.hd.publicKey.fromBase58(key);
|
||||
if (HDPrivateKey.isExtended(key))
|
||||
key = HDPrivateKey.fromBase58(key);
|
||||
else if (HDPublicKey.isExtended(key))
|
||||
key = HDPublicKey.fromBase58(key);
|
||||
|
||||
if (key instanceof bcoin.hd.privateKey)
|
||||
if (key instanceof HDPrivateKey)
|
||||
key = key.hdPublicKey;
|
||||
|
||||
assert(key instanceof bcoin.hd, 'Must add HD keys to wallet.');
|
||||
assert(key instanceof HD, 'Must add HD keys to wallet.');
|
||||
|
||||
if (this.derivation === 'bip44') {
|
||||
if (!key || !key.isAccount44())
|
||||
|
||||
Loading…
Reference in New Issue
Block a user