hd refactor.
This commit is contained in:
parent
a1943bcb5e
commit
9b81126ea4
331
lib/bcoin/hd.js
331
lib/bcoin/hd.js
@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -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() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user