hd refactor.

This commit is contained in:
Christopher Jeffrey 2016-07-01 19:21:20 -07:00
parent b59f6ae384
commit 4bf31f32bf
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 49 additions and 30 deletions

View File

@ -38,6 +38,7 @@ ec.elliptic = elliptic.ec('secp256k1');
/**
* elliptic.js signature constructor.
* @static
* @type {Function}
*/
ec.signature = require('elliptic/lib/elliptic/ec/signature');
@ -45,10 +46,18 @@ ec.signature = require('elliptic/lib/elliptic/ec/signature');
/**
* elliptic.js keypair constructor.
* @static
* @type {Function}
*/
ec.keypair = require('elliptic/lib/elliptic/ec/key');
/**
* A reference to the secp256k1 curve.
* @const {Object}
*/
ec.curve = ec.elliptic.curve;
/**
* Generate a private key.
* @returns {Buffer} Private key.

View File

@ -122,7 +122,7 @@ function Mnemonic(options) {
if (!(this instanceof Mnemonic))
return new Mnemonic(options);
this.bits = 128;
this.bits = constants.hd.MIN_ENTROPY;
this.language = 'english';
this.entropy = null;
this.phrase = null;
@ -159,7 +159,8 @@ Mnemonic.prototype.fromOptions = function fromOptions(options) {
if (options.bits != null) {
assert(utils.isNumber(options.bits));
assert(options.bits >= 128);
assert(options.bits >= constants.hd.MIN_ENTROPY);
assert(options.bits <= constants.hd.MAX_ENTROPY);
assert(options.bits % 32 === 0);
this.bits = options.bits;
}
@ -309,12 +310,20 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) {
words = phrase.split(/[ \u3000]+/);
bits = words.length * 11;
lang = Mnemonic.getLanguage(words[0]);
ent = new Buffer(Math.ceil(bits / 8));
wordlist = Mnemonic.getWordlist(lang);
cbits = bits % 32;
cbytes = Math.ceil(cbits / 8);
bits -= cbits;
assert(bits >= constants.hd.MIN_ENTROPY);
assert(bits <= constants.hd.MAX_ENTROPY);
assert(bits % 32 === 0);
ent = new Buffer(Math.ceil((bits + cbits) / 8));
ent.fill(0);
lang = Mnemonic.getLanguage(words[0]);
wordlist = Mnemonic.getWordlist(lang);
for (i = 0; i < words.length; i++) {
word = words[i];
index = wordlist.indexOf(word);
@ -331,10 +340,6 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) {
}
}
// Checksum bits:
cbits = bits % 32;
cbytes = Math.ceil(cbits / 8);
entropy = ent.slice(0, ent.length - cbytes);
ent = ent.slice(ent.length - cbytes);
chk = utils.sha256(entropy);
@ -348,11 +353,7 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) {
throw new Error('Invalid checksum.');
}
bits -= cbits;
assert(bits / 8 === entropy.length);
assert(bits >= 128);
assert(bits % 32 === 0);
this.bits = bits;
this.language = lang;
@ -382,7 +383,8 @@ Mnemonic.fromPhrase = function fromPhrase(phrase) {
Mnemonic.prototype.fromEntropy = function fromEntropy(entropy, lang) {
assert(Buffer.isBuffer(entropy));
assert(entropy.length * 8 >= 128);
assert(entropy.length * 8 >= constants.hd.MIN_ENTROPY);
assert(entropy.length * 8 <= constants.hd.MAX_ENTROPY);
assert((entropy.length * 8) % 32 === 0);
assert(!lang || Mnemonic.languages.indexOf(lang) !== -1);
@ -478,6 +480,10 @@ Mnemonic.prototype.fromJSON = function fromJSON(json) {
assert(typeof json.entropy === 'string');
assert(typeof json.phrase === 'string');
assert(typeof json.passphrase === 'string');
assert(json.bits >= constants.hd.MIN_ENTROPY);
assert(json.bits <= constants.hd.MAX_ENTROPY);
assert(json.bits % 32 === 0);
assert(json.bits / 8 === json.entropy.length / 2);
this.bits = json.bits;
this.language = json.language;
@ -485,10 +491,6 @@ Mnemonic.prototype.fromJSON = function fromJSON(json) {
this.phrase = json.phrase;
this.passphrase = json.passphrase;
assert(this.bits >= 128);
assert(this.bits % 32 === 0);
assert(this.bits / 8 === this.entropy.length);
return this;
};
@ -541,7 +543,8 @@ Mnemonic.prototype.fromRaw = function fromRaw(data) {
this.passphrase = p.readVarString('utf8');
assert(this.language);
assert(this.bits >= 128);
assert(this.bits >= constants.hd.MIN_ENTROPY);
assert(this.bits <= constants.hd.MAX_ENTROPY);
assert(this.bits % 32 === 0);
return this;
@ -881,7 +884,6 @@ HDPrivateKey.prototype.fromOptions = function fromOptions(options) {
this.childIndex = options.childIndex;
this.chainCode = options.chainCode;
this.privateKey = options.privateKey;
this.publicKey = ec.publicKeyCreate(options.privateKey, true);
if (options.mnemonic) {
@ -983,9 +985,13 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
privateKey = left
.add(new bn(this.privateKey))
.mod(ec.elliptic.curve.n)
.mod(ec.curve.n)
.toArrayLike(Buffer, 'be', 32);
// Only a 1 in 2^127 chance of happening.
if (!ec.privateKeyVerify(privateKey))
throw new Error('Private key is invalid.');
if (!this.fingerPrint)
this.fingerPrint = utils.hash160(this.publicKey).slice(0, 4);
@ -1176,8 +1182,8 @@ HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) {
assert(Buffer.isBuffer(seed));
if (seed.length < constants.hd.MIN_ENTROPY
|| seed.length > constants.hd.MAX_ENTROPY) {
if (!(seed.length * 8 >= constants.hd.MIN_ENTROPY
&& seed.length * 8 <= constants.hd.MAX_ENTROPY)) {
throw new Error('Entropy not in range.');
}
@ -1186,6 +1192,7 @@ HDPrivateKey.prototype.fromSeed = function fromSeed(seed, network) {
privateKey = hash.slice(0, 32);
chainCode = hash.slice(32, 64);
// Only a 1 in 2^127 chance of happening.
if (!ec.privateKeyVerify(privateKey))
throw new Error('Master private key is invalid.');
@ -1622,11 +1629,14 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
left = new bn(hash.slice(0, 32));
chainCode = hash.slice(32, 64);
point = ec.elliptic.curve.decodePoint(this.publicKey);
point = ec.elliptic.curve.g.mul(left).add(point);
point = ec.decodePoint(this.publicKey);
point = ec.curve.g.mul(left).add(point);
publicKey = new Buffer(point.encode('array', true));
assert(publicKey.length === 33);
if (!ec.publicKeyVerify(publicKey))
throw new Error('Public key is invalid.');
if (!this.fingerPrint)
this.fingerPrint = utils.hash160(this.publicKey).slice(0, 4);

View File

@ -522,8 +522,8 @@ exports.rejectByVal = utils.revMap(exports.reject);
exports.hd = {
HARDENED: 0x80000000,
MAX_INDEX: 0x100000000,
MIN_ENTROPY: 128 / 8,
MAX_ENTROPY: 512 / 8
MIN_ENTROPY: 128,
MAX_ENTROPY: 512
};
/**

View File

@ -178,7 +178,7 @@ Witness.prototype.getInputType = function getInputType() {
/**
* "Guess" the address of the witness.
* This method is not 100% reliable.
* @returns {String|null}
* @returns {Address|null}
*/
Witness.prototype.getInputAddress = function getInputAddress() {
@ -2807,7 +2807,7 @@ Script.prototype.getSize = function getSize() {
/**
* "Guess" the address of the input script.
* This method is not 100% reliable.
* @returns {Base58Address|null}
* @returns {Address|null}
*/
Script.prototype.getInputAddress = function getInputAddress() {
@ -2818,7 +2818,7 @@ Script.prototype.getInputAddress = function getInputAddress() {
* Get the address of the script if present. Note that
* pubkey and multisig scripts will be treated as though
* they are pubkeyhash and scripthashes respectively.
* @returns {Base58Address|null}
* @returns {Address|null}
*/
Script.prototype.getAddress = function getAddress() {