hd refactor.

This commit is contained in:
Christopher Jeffrey 2016-05-13 13:04:55 -07:00
parent 8db6e1c9a6
commit 897e4ae662
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
5 changed files with 119 additions and 95 deletions

View File

@ -241,10 +241,10 @@ Mnemonic.isMnemonic = function isMnemonic(obj) {
* or {@link HDPrivateKey} options.
*/
function HD(options) {
function HD(options, network) {
if (!options)
return HD.fromSeed();
return HD.fromAny(options);
return HD.fromSeed(null, network);
return HD.fromAny(options, network);
}
/**
@ -264,35 +264,46 @@ HD.fromBase58 = function fromBase58(xkey) {
* @param {Object} options
* @param {Buffer?} options.privateKey
* @param {Buffer?} options.entropy
* @param {String?} networkType
* @param {String?} network
* @returns {HDPrivateKey}
*/
HD.generate = function generate(options, networkType) {
return HDPrivateKey.generate(options, networkType);
HD.generate = function generate(options, network) {
return HDPrivateKey.generate(options, network);
};
/**
* Generate an {@link HDPrivateKey} from a seed.
* @param {Object|Mnemonic|Buffer} options - seed,
* mnemonic, mnemonic options.
* @param {String?} networkType
* @param {String?} network
* @returns {HDPrivateKey}
*/
HD.fromSeed = function fromSeed(options, networkType) {
return HDPrivateKey.fromSeed(options, networkType);
HD.fromSeed = function fromSeed(options, network) {
return HDPrivateKey.fromSeed(options, network);
};
/**
* Instantiate an HD key from a jsonified key object.
* @param {Object} json - The jsonified transaction object.
* @param {String?} passphrase
* @returns {HDPrivateKey}
*/
HD.fromJSON = function fromJSON(json, passphrase) {
return HDPrivateKey.fromJSON(json, passphrase);
};
/**
* Generate an hdkey from any number of options.
* @param {Object|Mnemonic|Buffer} options - mnemonic, mnemonic
* options, seed, or base58 key.
* @param {String?} networkType
* @param {String?} network
* @returns {HDPrivateKey|HDPublicKey}
*/
HD.fromAny = function fromAny(options, networkType) {
HD.fromAny = function fromAny(options, network) {
var xkey;
assert(options, 'Options required.');
@ -312,7 +323,7 @@ HD.fromAny = function fromAny(options, networkType) {
if (HDPublicKey.isExtended(xkey))
return HDPublicKey.fromBase58(xkey);
return HDPrivateKey.fromSeed(options, networkType);
return HDPrivateKey.fromSeed(options, network);
};
/**
@ -351,7 +362,6 @@ HD.isHD = function isHD(obj) {
* @param {Object|Base58String} options
* @param {Base58String?} options.xkey - Serialized base58 key.
* @param {(Mnemonic|Object)?} options.mnemonic - mnemonic or mnemonic options.
* @param {Number?} options.version
* @param {Number?} options.depth
* @param {Buffer?} options.parentFingerPrint
* @param {Number?} options.childIndex
@ -361,7 +371,6 @@ HD.isHD = function isHD(obj) {
* @property {Base58String} xprivkey
* @property {Base58String} xpubkey
* @property {Mnemonic?} mnemonic
* @property {Number} version
* @property {Number} depth
* @property {Buffer} parentFingerPrint
* @property {Number} childIndex
@ -377,11 +386,7 @@ function HDPrivateKey(options) {
assert(options, 'No options for HD private key.');
assert(options.depth <= 0xff, 'Depth is too high.');
this.network = bcoin.network.get(options.network);
this.xprivkey = options.xprivkey;
this.mnemonic = options.mnemonic;
this.version = options.version;
this.network = bcoin.network.get(options.network).type;
this.depth = options.depth;
this.parentFingerPrint = options.parentFingerPrint;
this.childIndex = options.childIndex;
@ -391,11 +396,12 @@ function HDPrivateKey(options) {
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
this.fingerPrint = null;
this.mnemonic = options.mnemonic;
this._xprivkey = options.xprivkey;
this.hdPrivateKey = this;
this._hdPublicKey = null;
if (!this.xprivkey)
this.xprivkey = HDPrivateKey.render(options);
}
utils.inherits(HDPrivateKey, HD);
@ -404,7 +410,6 @@ HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
if (!this._hdPublicKey) {
this._hdPublicKey = new HDPublicKey({
network: this.network,
version: this.network.prefixes.xpubkey,
depth: this.depth,
parentFingerPrint: this.parentFingerPrint,
childIndex: this.childIndex,
@ -415,6 +420,12 @@ HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
return this._hdPublicKey;
});
HDPrivateKey.prototype.__defineGetter__('xprivkey', function() {
if (!this._xprivkey)
this._xprivkey = this.toBase58();
return this._xprivkey;
});
HDPrivateKey.prototype.__defineGetter__('xpubkey', function() {
return this.hdPublicKey.xpubkey;
});
@ -470,7 +481,6 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
child = new HDPrivateKey({
network: this.network,
version: this.version,
depth: this.depth + 1,
parentFingerPrint: this.fingerPrint,
childIndex: index,
@ -505,7 +515,7 @@ HDPrivateKey.prototype.deriveAccount44 = function deriveAccount44(options) {
}
if (coinType == null)
coinType = this.network.type === 'main' ? 0 : 1;
coinType = this.network === 'main' ? 0 : 1;
assert(utils.isNumber(coinType));
assert(utils.isNumber(accountIndex));
@ -671,13 +681,13 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
* Create an hd private key from a seed.
* @param {Buffer|Mnemonic|Object} options - A seed,
* mnemonic, or mnemonic options.
* @param {String?} networkType
* @param {String?} network
* @returns {Object} A "naked" key (a
* plain javascript object which is suitable
* for passing to the HDPrivateKey constructor).
*/
HDPrivateKey.parseSeed = function parseSeed(seed, networkType) {
HDPrivateKey.parseSeed = function parseSeed(seed, network) {
var data, hash;
if (!seed)
@ -701,7 +711,7 @@ HDPrivateKey.parseSeed = function parseSeed(seed, networkType) {
hash = utils.hmac('sha512', data, 'Bitcoin seed');
return {
version: bcoin.network.get(networkType).prefixes.xprivkey,
network: network,
depth: 0,
parentFingerPrint: new Buffer([0, 0, 0, 0]),
childIndex: 0,
@ -715,12 +725,12 @@ HDPrivateKey.parseSeed = function parseSeed(seed, networkType) {
* Instantiate an hd private key from a seed.
* @param {Buffer|Mnemonic|Object} seed - A
* seed, mnemonic, or mnemonic options.
* @param {String?} networkType
* @param {String?} network
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromSeed = function fromSeed(seed, networkType) {
return new HDPrivateKey(HDPrivateKey.parseSeed(seed, networkType));
HDPrivateKey.fromSeed = function fromSeed(seed, network) {
return new HDPrivateKey(HDPrivateKey.parseSeed(seed, network));
};
/**
@ -728,13 +738,13 @@ HDPrivateKey.fromSeed = function fromSeed(seed, networkType) {
* @param {Object?} options
* @param {Buffer?} options.privateKey
* @param {Buffer?} options.entropy
* @param {String?} networkType
* @param {String?} network
* @returns {Object} A "naked" key (a
* plain javascript object which is suitable
* for passing to the HDPrivateKey constructor).
*/
HDPrivateKey._generate = function _generate(options, networkType) {
HDPrivateKey._generate = function _generate(options, network) {
var privateKey, entropy;
if (!options)
@ -753,7 +763,7 @@ HDPrivateKey._generate = function _generate(options, networkType) {
entropy = ec.random(32);
return {
version: bcoin.network.get(networkType).prefixes.xprivkey,
network: network,
depth: 0,
parentFingerPrint: new Buffer([0, 0, 0, 0]),
childIndex: 0,
@ -767,12 +777,12 @@ HDPrivateKey._generate = function _generate(options, networkType) {
* @param {Object?} options
* @param {Buffer?} options.privateKey
* @param {Buffer?} options.entropy
* @param {String?} networkType
* @param {String?} network
* @returns {HDPrivateKey}
*/
HDPrivateKey.generate = function generate(options, networkType) {
return new HDPrivateKey(HDPrivateKey._generate(options, networkType));
HDPrivateKey.generate = function generate(options, network) {
return new HDPrivateKey(HDPrivateKey._generate(options, network));
};
/**
@ -781,7 +791,7 @@ HDPrivateKey.generate = function generate(options, networkType) {
* @returns {Object}
*/
HDPrivateKey.parse = function parse(xkey) {
HDPrivateKey.parseBase58 = function parseBase58(xkey) {
var raw = utils.fromBase58(xkey);
var p = new BufferReader(raw, true);
var data = {};
@ -811,6 +821,32 @@ HDPrivateKey.parse = function parse(xkey) {
return data;
};
/**
* Serialize key to a base58 string.
* @param {Network|String} network
* @returns {Base58String}
*/
HDPrivateKey.prototype.toBase58 = function toBase58(network) {
var p = new BufferWriter();
if (!network)
network = this.network;
network = bcoin.network.get(network);
p.writeU32BE(network.prefixes.xprivkey);
p.writeU8(this.depth);
p.writeBytes(this.parentFingerPrint);
p.writeU32BE(this.childIndex);
p.writeBytes(this.chainCode);
p.writeU8(0);
p.writeBytes(this.privateKey);
p.writeChecksum();
return utils.toBase58(p.render());
};
/**
* Instantiate a transaction from a base58 string.
* @param {Base58String} xkey
@ -818,29 +854,10 @@ HDPrivateKey.parse = function parse(xkey) {
*/
HDPrivateKey.fromBase58 = function fromBase58(xkey) {
var data = HDPrivateKey.parse(xkey);
var data = HDPrivateKey.parseBase58(xkey);
return new HDPrivateKey(data);
};
/**
* Serialize key data to base58 extended key.
* @param {Object|HDPrivateKey}
* @returns {Base58String}
*/
HDPrivateKey.render = function render(data) {
var p = new BufferWriter();
p.writeU32BE(data.version);
p.writeU8(data.depth);
p.writeBytes(data.parentFingerPrint);
p.writeU32BE(data.childIndex);
p.writeBytes(data.chainCode);
p.writeU8(0);
p.writeBytes(data.privateKey);
p.writeChecksum();
return utils.toBase58(p.render());
};
/**
* Convert key to a more json-friendly object.
* @param {String?} passphrase - Address passphrase
@ -962,7 +979,6 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
* @constructor
* @param {Object|Base58String} options
* @param {Base58String?} options.xkey - Serialized base58 key.
* @param {Number?} options.version
* @param {Number?} options.depth
* @param {Buffer?} options.parentFingerPrint
* @param {Number?} options.childIndex
@ -970,7 +986,6 @@ HDPrivateKey.isHDPrivateKey = function isHDPrivateKey(obj) {
* @param {Buffer?} options.publicKey
* @property {String} network
* @property {Base58String} xpubkey
* @property {Number} version
* @property {Number} depth
* @property {Buffer} parentFingerPrint
* @property {Number} childIndex
@ -985,11 +1000,7 @@ function HDPublicKey(options) {
assert(options, 'No options for HDPublicKey');
assert(options.depth <= 0xff, 'Depth is too high.');
this.network = bcoin.network.get(options.network);
this.xpubkey = options.xpubkey;
this.xprivkey = null;
this.version = options.version;
this.network = bcoin.network.get(options.network).type;
this.depth = options.depth;
this.parentFingerPrint = options.parentFingerPrint;
this.childIndex = options.childIndex;
@ -999,15 +1010,21 @@ function HDPublicKey(options) {
this.privateKey = null;
this.fingerPrint = null;
this.xprivkey = null;
this._xpubkey = options.xpubkey;
this.hdPublicKey = this;
this.hdPrivateKey = null;
if (!this.xpubkey)
this.xpubkey = HDPublicKey.render(options);
}
utils.inherits(HDPublicKey, HD);
HDPublicKey.prototype.__defineGetter__('xpubkey', function() {
if (!this._xpubkey)
this._xpubkey = this.toBase58();
return this._xpubkey;
});
/**
* Derive a child key.
* @param {Number|String} - Child index or path.
@ -1056,7 +1073,6 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
child = new HDPublicKey({
network: this.network,
version: this.version,
depth: this.depth + 1,
parentFingerPrint: this.fingerPrint,
childIndex: index,
@ -1202,7 +1218,7 @@ HDPublicKey.isExtended = function isExtended(data) {
* @returns {Object}
*/
HDPublicKey.parse = function parse(xkey) {
HDPublicKey.parseBase58 = function parseBase58(xkey) {
var raw = utils.fromBase58(xkey);
var p = new BufferReader(raw, true);
var data = {};
@ -1233,19 +1249,26 @@ HDPublicKey.parse = function parse(xkey) {
/**
* Serialize key data to base58 extended key.
* @param {Object|HDPublicKey}
* @param {Network|String} network
* @returns {Base58String}
*/
HDPublicKey.render = function render(data) {
HDPublicKey.prototype.toBase58 = function toBase58(network) {
var p = new BufferWriter();
p.writeU32BE(data.version);
p.writeU8(data.depth);
p.writeBytes(data.parentFingerPrint);
p.writeU32BE(data.childIndex);
p.writeBytes(data.chainCode);
p.writeBytes(data.publicKey);
if (!network)
network = this.network;
network = bcoin.network.get(network);
p.writeU32BE(network.prefixes.xpubkey);
p.writeU8(this.depth);
p.writeBytes(this.parentFingerPrint);
p.writeU32BE(this.childIndex);
p.writeBytes(this.chainCode);
p.writeBytes(this.publicKey);
p.writeChecksum();
return utils.toBase58(p.render());
};
@ -1256,7 +1279,7 @@ HDPublicKey.render = function render(data) {
*/
HDPublicKey.fromBase58 = function fromBase58(xkey) {
var data = HDPublicKey.parse(xkey);
var data = HDPublicKey.parseBase58(xkey);
return new HDPublicKey(data);
};
@ -1333,11 +1356,8 @@ HDPrivateKey.prototype.toSecret = function toSecret() {
return KeyPair.prototype.toSecret.call(this);
};
HD.mnemonic = Mnemonic;
HD.priv = HDPrivateKey;
HD.pub = HDPublicKey;
HD.privateKey = HDPrivateKey;
HD.publicKey = HDPublicKey;
HD.fromJSON = HDPrivateKey.fromJSON;
HD.Mnemonic = Mnemonic;
HD.PrivateKey = HDPrivateKey;
HD.PublicKey = HDPublicKey;
module.exports = HD;

View File

@ -105,4 +105,8 @@ Network.get = function get(options) {
assert(false, 'Unknown network.');
};
Network.prototype.inspect = function inspect() {
return this.type;
};
module.exports = Network;

View File

@ -57,18 +57,18 @@ function Wallet(options) {
options = utils.merge({}, options);
this.options = options;
this.network = bcoin.network.get(options.network);
if (typeof options.master === 'string')
options.master = { xkey: options.master };
if (options.master
&& typeof options.master === 'object'
&& !(options.master instanceof bcoin.hd)) {
options.master = bcoin.hd.fromAny(options.master);
options.master = bcoin.hd.fromAny(options.master, this.network);
}
this.options = options;
this.network = bcoin.network.get(options.network);
if (!options.master)
options.master = bcoin.hd.fromSeed(null, this.network);

View File

@ -93,7 +93,7 @@ describe('HD', function() {
});
it('should create master private key', function() {
master = bcoin.hd.priv.fromSeed(new Buffer(seed, 'hex'));
master = bcoin.hd.PrivateKey.fromSeed(new Buffer(seed, 'hex'));
assert.equal(master.xprivkey, master_priv);
assert.equal(master.xpubkey, master_pub);
});
@ -134,11 +134,11 @@ describe('HD', function() {
});
it('should deserialize master private key', function() {
bcoin.hd.priv.parse(master.xprivkey);
bcoin.hd.PrivateKey.parseBase58(master.xprivkey);
});
it('should deserialize master public key', function() {
bcoin.hd.pub.parse(master.hdPublicKey.xpubkey);
bcoin.hd.PublicKey.parseBase58(master.hdPublicKey.xpubkey);
});
it('should deserialize and reserialize', function() {
@ -162,7 +162,7 @@ describe('HD', function() {
delete vector.seed;
delete vector.m;
it('should create from a seed', function() {
master = bcoin.hd.priv.fromSeed(new Buffer(seed, 'hex'));
master = bcoin.hd.PrivateKey.fromSeed(new Buffer(seed, 'hex'));
equal(master.xprivkey, m.prv);
equal(master.xpubkey, m.pub);
});

View File

@ -12,7 +12,7 @@ describe('Mnemonic', function() {
var seed = new Buffer(data[2], 'hex');
var xpriv = data[3];
it('should create an english mnemonic (' + i + ')', function() {
var mnemonic = new bcoin.hd.mnemonic({
var mnemonic = new bcoin.hd.Mnemonic({
language: 'english',
entropy: entropy,
passphrase: 'TREZOR'
@ -31,7 +31,7 @@ describe('Mnemonic', function() {
var passphrase = data.passphrase;
var xpriv = data.bip32_xprv;
it('should create a japanese mnemonic (' + i + ')', function() {
var mnemonic = new bcoin.hd.mnemonic({
var mnemonic = new bcoin.hd.Mnemonic({
language: 'japanese',
entropy: entropy,
passphrase: passphrase