diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index c4edf7de..bcac4a1a 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -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; diff --git a/lib/bcoin/network.js b/lib/bcoin/network.js index bf5ca684..1e819d21 100644 --- a/lib/bcoin/network.js +++ b/lib/bcoin/network.js @@ -105,4 +105,8 @@ Network.get = function get(options) { assert(false, 'Unknown network.'); }; +Network.prototype.inspect = function inspect() { + return this.type; +}; + module.exports = Network; diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index 83cef83d..9b808181 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -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); diff --git a/test/hd-test.js b/test/hd-test.js index e539922f..d578660a 100644 --- a/test/hd-test.js +++ b/test/hd-test.js @@ -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); }); diff --git a/test/mnemonic-test.js b/test/mnemonic-test.js index fb981870..aab433b8 100644 --- a/test/mnemonic-test.js +++ b/test/mnemonic-test.js @@ -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