hd refactor.

This commit is contained in:
Christopher Jeffrey 2016-06-25 03:40:54 -07:00
parent a1943bcb5e
commit 9b81126ea4
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 216 additions and 119 deletions

View File

@ -118,6 +118,24 @@ function Mnemonic(options) {
if (typeof options === 'string')
options = { phrase: options };
this.bits = 128;
this.entropy = null;
this.phrase = null;
this.passphrase = '';
this.language = 'english';
this.seed = null;
if (options)
this.fromOptions(options);
}
Mnemonic.prototype.fromOptions = function fromOptions(options) {
if (!options)
options = {};
if (typeof options === 'string')
options = { phrase: options };
this.bits = options.bits || 128;
this.entropy = options.entropy;
this.phrase = options.phrase;
@ -127,7 +145,66 @@ function Mnemonic(options) {
assert(this.bits >= 128);
assert(this.bits % 32 === 0);
}
};
Mnemonic.fromOptions = function fromOptions(options) {
return new Mnemonic().fromOptions(options);
};
Mnemonic.prototype.toJSON = function toJSON() {
return {
bits: this.bits,
entropy: this.entropy ? this.entropy.toString('hex') : null,
phrase: this.phrase,
passphrase: this.passphrase,
language: this.language,
seed: this.seed ? this.seed.toString('hex') : null
};
};
Mnemonic.prototype.fromJSON = function fromJSON(json) {
this.bits = json.bits;
if (json.entropy)
this.entropy = new Buffer(json.entropy, 'hex');
this.phrase = json.phrase;
this.passphrase = json.passphrase;
this.language = json.language;
if (json.seed)
this.seed = new Buffer(json.seed, 'hex');
return this;
};
Mnemonic.fromJSON = function fromJSON(json) {
return new Mnemonic().fromJSON(json);
};
Mnemonic.prototype.toRaw = function toRaw(writer) {
var p = new BufferWriter(writer);
p.writeU16(this.bits);
p.writeBytes(this.entropy);
p.writeVarString(this.phrase, 'utf8');
p.writeVarString(this.passphrase, 'utf8');
p.writeVarString(this.language, 'utf8');
p.writeBytes(this.seed);
if (!writer)
p = p.render();
return p;
};
Mnemonic.prototype.fromRaw = function fromRaw(data) {
var p = new BufferReader(data);
this.bits = p.readU16();
this.entropy = p.readBytes(this.bits / 8);
this.phrase = p.readVarString('utf8');
this.passphrase = p.readVarString('utf8');
this.language = p.readVarString('utf8');
this.seed = p.readBytes(64);
return this;
};
Mnemonic.fromRaw = function fromRaw(data) {
return new Mnemonic().fromRaw(data);
};
/**
* Generate the seed.
@ -471,6 +548,30 @@ function HDPrivateKey(options) {
if (!(this instanceof HDPrivateKey))
return new HDPrivateKey(options);
this.network = null;
this.depth = null;
this.parentFingerPrint = null;
this.childIndex = null;
this.chainCode = null;
this.privateKey = null;
this.publicKey = null;
this.fingerPrint = null;
this.mnemonic = null;
this._xprivkey = null;
this.hdPrivateKey = this;
this._hdPublicKey = null;
if (options)
this.fromOptions(options);
}
utils.inherits(HDPrivateKey, HD);
HDPrivateKey.prototype.fromOptions = function fromOptions(options) {
assert(options, 'No options for HD private key.');
assert(options.depth <= 0xff, 'Depth is too high.');
@ -482,17 +583,16 @@ function HDPrivateKey(options) {
this.privateKey = options.privateKey;
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
this.fingerPrint = null;
this.mnemonic = options.mnemonic || null;
this._xprivkey = options.xprivkey || null;
this.hdPrivateKey = this;
this._hdPublicKey = null;
}
return this;
};
utils.inherits(HDPrivateKey, HD);
HDPrivateKey.fromOptions = function fromOptions(options) {
return new HDPrivateKey().fromOptions(options);
};
HDPrivateKey.prototype.__defineGetter__('hdPublicKey', function() {
if (!this._hdPublicKey) {
@ -755,7 +855,7 @@ HDPrivateKey.prototype.equal = function equal(obj) {
* for passing to the HDPrivateKey constructor).
*/
HDPrivateKey.parseSeed = function parseSeed(seed, network) {
HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) {
var hash, chainCode, privateKey;
assert(Buffer.isBuffer(seed));
@ -773,14 +873,15 @@ HDPrivateKey.parseSeed = function parseSeed(seed, network) {
if (!ec.privateKeyVerify(privateKey))
throw new Error('Master private key is invalid.');
return {
network: network,
depth: 0,
parentFingerPrint: new Buffer([0, 0, 0, 0]),
childIndex: 0,
chainCode: chainCode,
privateKey: privateKey
};
this.network = bcoin.network.get(network).type;
this.depth = 0;
this.parentFingerPrint = new Buffer([0, 0, 0, 0]);
this.childIndex = 0;
this.chainCode = chainCode;
this.privateKey = privateKey;
this.publicKey = ec.publicKeyCreate(this.privateKey, true);
return this;
};
/**
@ -792,7 +893,7 @@ HDPrivateKey.parseSeed = function parseSeed(seed, network) {
*/
HDPrivateKey.fromSeed = function fromSeed(seed, network) {
return new HDPrivateKey(HDPrivateKey.parseSeed(seed, network));
return new HDPrivateKey().fromSeed(seed, network);
};
/**
@ -802,25 +903,24 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) {
var key;
if (!(mnemonic instanceof Mnemonic))
mnemonic = new Mnemonic(mnemonic);
if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy) {
key = HDPrivateKey.parseSeed(mnemonic.toSeed(), network);
key.mnemonic = mnemonic;
return new HDPrivateKey(key);
this.fromSeed(mnemonic.toSeed(), network);
this.mnemonic = mnemonic;
return this;
}
// Very unlikely, but not impossible
// to get an invalid private key.
for (;;) {
try {
key = HDPrivateKey.parseSeed(mnemonic.toSeed(), network);
key.mnemonic = mnemonic;
key = new HDPrivateKey(key);
this.fromSeed(mnemonic.toSeed(), network);
this.mnemonic = mnemonic;
} catch (e) {
if (e.message === 'Master private key is invalid.') {
mnemonic.seed = null;
@ -833,7 +933,11 @@ HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
break;
}
return key;
return this;
};
HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
return new HDPrivateKey().fromMnemonic(mnemonic, network);
};
/**
@ -847,7 +951,7 @@ HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
* for passing to the HDPrivateKey constructor).
*/
HDPrivateKey._generate = function _generate(options, network) {
HDPrivateKey.prototype.generate = function _generate(options, network) {
var privateKey, entropy;
if (!options)
@ -865,14 +969,15 @@ HDPrivateKey._generate = function _generate(options, network) {
if (!entropy)
entropy = ec.random(32);
return {
network: network,
depth: 0,
parentFingerPrint: new Buffer([0, 0, 0, 0]),
childIndex: 0,
chainCode: entropy,
privateKey: privateKey
};
this.network = bcoin.network.get(network).type;
this.depth = 0;
this.parentFingerPrint = new Buffer([0, 0, 0, 0]);
this.childIndex = 0;
this.chainCode = entropy;
this.privateKey = privateKey;
this.publicKey = ec.publicKeyCreate(this.privateKey, true);
return this;
};
/**
@ -885,7 +990,7 @@ HDPrivateKey._generate = function _generate(options, network) {
*/
HDPrivateKey.generate = function generate(options, network) {
return new HDPrivateKey(HDPrivateKey._generate(options, network));
return new HDPrivateKey().generate(options, network);
};
/**
@ -894,10 +999,10 @@ HDPrivateKey.generate = function generate(options, network) {
* @returns {Object}
*/
HDPrivateKey.parseBase58 = function parseBase58(xkey) {
var data = HDPrivateKey.parseRaw(utils.fromBase58(xkey));
data.xprivkey = xkey;
return data;
HDPrivateKey.prototype.fromBase58 = function fromBase58(xkey) {
this.fromRaw(utils.fromBase58(xkey));
this._xprivkey = xkey;
return this;
};
/**
@ -906,32 +1011,32 @@ HDPrivateKey.parseBase58 = function parseBase58(xkey) {
* @returns {Object}
*/
HDPrivateKey.parseRaw = function parseRaw(raw) {
HDPrivateKey.prototype.fromRaw = function fromRaw(raw) {
var p = new BufferReader(raw);
var data = {};
var i, type, prefix;
data.version = p.readU32BE();
data.depth = p.readU8();
data.parentFingerPrint = p.readBytes(4);
data.childIndex = p.readU32BE();
data.chainCode = p.readBytes(32);
this.version = p.readU32BE();
this.depth = p.readU8();
this.parentFingerPrint = p.readBytes(4);
this.childIndex = p.readU32BE();
this.chainCode = p.readBytes(32);
p.readU8();
data.privateKey = p.readBytes(32);
this.privateKey = p.readBytes(32);
p.verifyChecksum();
for (i = 0; i < network.types.length; i++) {
type = network.types[i];
prefix = network[type].prefixes.xprivkey;
if (data.version === prefix)
if (this.version === prefix)
break;
}
assert(i < network.types.length, 'Network not found.');
data.network = type;
this.publicKey = ec.publicKeyCreate(this.privateKey, true);
this.network = type;
return data;
return this;
};
/**
@ -980,7 +1085,7 @@ HDPrivateKey.prototype.toRaw = function toRaw(network, writer) {
*/
HDPrivateKey.fromBase58 = function fromBase58(xkey) {
return new HDPrivateKey(HDPrivateKey.parseBase58(xkey));
return new HDPrivateKey().fromBase58(xkey);
};
/**
@ -990,7 +1095,7 @@ HDPrivateKey.fromBase58 = function fromBase58(xkey) {
*/
HDPrivateKey.fromRaw = function fromRaw(raw) {
return new HDPrivateKey(HDPrivateKey.parseRaw(raw));
return new HDPrivateKey().fromRaw(raw);
};
/**
@ -999,22 +1104,10 @@ HDPrivateKey.fromRaw = function fromRaw(raw) {
*/
HDPrivateKey.prototype.toJSON = function toJSON() {
var json = {
network: this.network
return {
xprivkey: this.xprivkey,
mnemonic: this.mnemonic ? this.mnemonic.toJSON() : null
};
if (this instanceof HDPrivateKey) {
if (this.mnemonic) {
json.phrase = this.mnemonic.phrase;
json.passphrase = this.mnemonic.passphrase;
}
json.xprivkey = this.xprivkey;
return json;
}
json.xpubkey = this.xpubkey;
return json;
};
/**
@ -1023,21 +1116,15 @@ HDPrivateKey.prototype.toJSON = function toJSON() {
* @returns {Object} A "naked" HDPrivateKey.
*/
HDPrivateKey.parseJSON = function parseJSON(json) {
var data = {};
if (json.phrase) {
data.mnemonic = {
phrase: json.phrase,
passphrase: json.passphrase
};
}
HDPrivateKey.prototype.fromJSON = function fromJSON(json) {
assert(json.xprivkey, 'Could not handle key JSON.');
data.xprivkey = json.xprivkey;
if (json.mnemonic)
this.mnemonic = Mnemonic.fromJSON(json.mnemonic);
return data;
this.fromBase58(json.xprivkey);
return this;
};
/**
@ -1047,11 +1134,7 @@ HDPrivateKey.parseJSON = function parseJSON(json) {
*/
HDPrivateKey.fromJSON = function fromJSON(json) {
var key;
json = HDPrivateKey.parseJSON(json);
key = HDPrivateKey.fromBase58(json.xprivkey);
key.mnemonic = json.mnemonic ? new Mnemonic(json.mnemonic) : null;
return key;
return new HDPrivateKey().fromJSON(json);
};
/**
@ -1091,6 +1174,28 @@ function HDPublicKey(options) {
if (!(this instanceof HDPublicKey))
return new HDPublicKey(options);
this.network = null;
this.depth = null;
this.parentFingerPrint = null;
this.childIndex = null;
this.chainCode = null;
this.publicKey = null;
this.privateKey = null;
this.fingerPrint = null;
this._xpubkey = null;
this.hdPublicKey = this;
this.hdPrivateKey = null;
if (options)
this.fromOptions(options);
}
utils.inherits(HDPublicKey, HD);
HDPublicKey.prototype.fromOptions = function fromOptions(options) {
assert(options, 'No options for HDPublicKey');
assert(options.depth <= 0xff, 'Depth is too high.');
@ -1101,17 +1206,14 @@ function HDPublicKey(options) {
this.chainCode = options.chainCode;
this.publicKey = options.publicKey;
this.privateKey = null;
this.fingerPrint = null;
this.xprivkey = null;
this._xpubkey = options.xpubkey;
this.hdPublicKey = this;
this.hdPrivateKey = null;
}
return this;
};
utils.inherits(HDPublicKey, HD);
HDPublicKey.fromOptions = function fromOptions(options) {
return new HDPublicKey().fromOptions(options);
};
HDPublicKey.prototype.__defineGetter__('xpubkey', function() {
if (!this._xpubkey)
@ -1292,23 +1394,20 @@ HDPublicKey.prototype.equal = function equal(obj) {
HDPublicKey.prototype.toJSON = function toJSON() {
return {
network: this.network,
xpubkey: this.xpubkey
};
};
/**
* Handle a deserialized JSON HDPublicKey object.
* @param {Object} json
* @returns {Object} A "naked" HDPublicKey.
*/
HDPublicKey.parseJSON = function parseJSON(json) {
HDPublicKey.prototype.fromJSON = function fromJSON(json) {
assert(json.xpubkey, 'Could not handle HD key JSON.');
return {
xpubkey: json.xpubkey
};
this.fromBase58(json.xpubkey);
return this;
};
/**
@ -1319,8 +1418,7 @@ HDPublicKey.parseJSON = function parseJSON(json) {
*/
HDPublicKey.fromJSON = function fromJSON(json) {
json = HDPrivateKey.parseJSON(json);
return HDPublicKey.fromBase58(json.xpubkey);
return new HDPublicKey().fromJSON(json);
};
/**
@ -1375,10 +1473,10 @@ HDPublicKey.hasPrefix = function hasPrefix(data) {
* @returns {Object}
*/
HDPublicKey.parseBase58 = function parseBase58(xkey) {
var data = HDPublicKey.parseRaw(utils.fromBase58(xkey));
data.xpubkey = xkey;
return data;
HDPublicKey.prototype.fromBase58 = function fromBase58(xkey) {
this.fromRaw(utils.fromBase58(xkey));
this._xpubkey = xkey;
return this;
};
/**
@ -1387,31 +1485,30 @@ HDPublicKey.parseBase58 = function parseBase58(xkey) {
* @returns {Object}
*/
HDPublicKey.parseRaw = function parseRaw(raw) {
HDPublicKey.prototype.fromRaw = function fromRaw(raw) {
var p = new BufferReader(raw);
var data = {};
var i, type, prefix;
data.version = p.readU32BE();
data.depth = p.readU8();
data.parentFingerPrint = p.readBytes(4);
data.childIndex = p.readU32BE();
data.chainCode = p.readBytes(32);
data.publicKey = p.readBytes(33);
this.version = p.readU32BE();
this.depth = p.readU8();
this.parentFingerPrint = p.readBytes(4);
this.childIndex = p.readU32BE();
this.chainCode = p.readBytes(32);
this.publicKey = p.readBytes(33);
p.verifyChecksum();
for (i = 0; i < network.types.length; i++) {
type = network.types[i];
prefix = network[type].prefixes.xpubkey;
if (data.version === prefix)
if (this.version === prefix)
break;
}
assert(i < network.types.length, 'Network not found.');
data.network = type;
this.network = type;
return data;
return this;
};
/**
@ -1459,7 +1556,7 @@ HDPublicKey.prototype.toRaw = function toRaw(network, writer) {
*/
HDPublicKey.fromBase58 = function fromBase58(xkey) {
return new HDPublicKey(HDPublicKey.parseBase58(xkey));
return new HDPublicKey().fromBase58(xkey);
};
/**
@ -1469,7 +1566,7 @@ HDPublicKey.fromBase58 = function fromBase58(xkey) {
*/
HDPublicKey.fromRaw = function fromRaw(data) {
return new HDPublicKey(HDPublicKey.parseRaw(data));
return new HDPublicKey().fromRaw(data);
};
/**

View File

@ -136,11 +136,11 @@ describe('HD', function() {
});
it('should deserialize master private key', function() {
bcoin.hd.PrivateKey.parseBase58(master.xprivkey);
bcoin.hd.PrivateKey.fromBase58(master.xprivkey);
});
it('should deserialize master public key', function() {
bcoin.hd.PublicKey.parseBase58(master.hdPublicKey.xpubkey);
bcoin.hd.PublicKey.fromBase58(master.hdPublicKey.xpubkey);
});
it('should deserialize and reserialize', function() {