more hd improvements.

This commit is contained in:
Christopher Jeffrey 2016-05-13 19:28:19 -07:00
parent b164984307
commit 6690991acb
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
6 changed files with 90 additions and 27 deletions

View File

@ -174,6 +174,24 @@ ec.publicKeyVerify = function publicKeyVerify(key) {
return ec.elliptic.keyPair({ pub: key }).validate();
};
/**
* Validate a private key.
* @param {Buffer} key
* @returns {Boolean} True if buffer is a valid private key.
*/
ec.privateKeyVerify = function privateKeyVerify(key) {
if (secp256k1)
return secp256k1.privateKeyVerify(key);
key = new bn(key);
if (key.cmpn(0) === 0 || key.cmp(ec.elliptic.curve.n) >= 0)
return false;
return true;
};
/**
* Sign a message.
* @param {Buffer} msg

View File

@ -243,7 +243,7 @@ Mnemonic.isMnemonic = function isMnemonic(obj) {
function HD(options, network) {
if (!options)
return HD.fromSeed(null, network);
return HD.fromMnemonic(null, network);
return HD.fromAny(options, network);
}
@ -284,6 +284,17 @@ HD.fromSeed = function fromSeed(options, network) {
return HDPrivateKey.fromSeed(options, network);
};
/**
* Instantiate an hd private key from a mnemonic.
* @param {Mnemonic|Object} mnemonic
* @param {String?} network
* @returns {HDPrivateKey}
*/
HD.fromMnemonic = function fromMnemonic(options, network) {
return HDPrivateKey.fromMnemonic(options, network);
};
/**
* Instantiate an HD key from a jsonified key object.
* @param {Object} json - The jsonified transaction object.
@ -323,7 +334,7 @@ HD.fromAny = function fromAny(options, network) {
if (HDPublicKey.isExtended(xkey))
return HDPublicKey.fromBase58(xkey);
return HDPrivateKey.fromSeed(options, network);
return HDPrivateKey.fromMnemonic(options, network);
};
/**
@ -499,6 +510,9 @@ HDPrivateKey.prototype.derive = function derive(index, hardened) {
if (index < constants.hd.HARDENED && hardened)
index += constants.hd.HARDENED;
if (!(index >= 0 && index < constants.hd.MAX_INDEX))
throw new Error('Index out of range.');
p = new BufferWriter();
if (hardened) {
@ -686,35 +700,33 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
*/
HDPrivateKey.parseSeed = function parseSeed(seed, network) {
var data, hash;
var hash, chainCode, privateKey;
if (!seed)
seed = {};
if (Buffer.isBuffer(seed)) {
data = seed;
seed = null;
} else if (seed instanceof Mnemonic) {
data = seed.toSeed();
} else {
seed = new Mnemonic(seed);
data = seed.toSeed();
}
assert(Buffer.isBuffer(seed));
if (data.length < constants.hd.MIN_ENTROPY
|| data.length > constants.hd.MAX_ENTROPY) {
if (seed.length < constants.hd.MIN_ENTROPY
|| seed.length > constants.hd.MAX_ENTROPY) {
throw new Error('Entropy not in range.');
}
hash = utils.hmac('sha512', data, 'Bitcoin seed');
hash = utils.hmac('sha512', seed, 'Bitcoin seed');
privateKey = hash.slice(0, 32);
chainCode = hash.slice(32, 64);
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: hash.slice(32, 64),
privateKey: hash.slice(0, 32),
chainCode: chainCode,
privateKey: privateKey,
mnemonic: seed
};
};
@ -731,6 +743,40 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
return new HDPrivateKey(HDPrivateKey.parseSeed(seed, network));
};
/**
* Instantiate an hd private key from a mnemonic.
* @param {Mnemonic|Object} mnemonic
* @param {String?} network
* @returns {HDPrivateKey}
*/
HDPrivateKey.fromMnemonic = function fromMnemonic(mnemonic, network) {
var key;
if (!(mnemonic instanceof Mnemonic))
mnemonic = new Mnemonic(mnemonic);
if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy)
return HDPrivateKey.fromSeed(mnemonic.toSeed(), network);
for (;;) {
try {
key = HDPrivateKey.fromSeed(mnemonic.toSeed(), network);
} catch (e) {
if (e.message === 'Master private key is invalid.') {
mnemonic.seed = null;
mnemonic.phrase = null;
mnemonic.entropy = null;
continue;
}
throw e;
}
break;
}
return key;
};
/**
* Generate an hd private key from a key and/or entropy bytes.
* @param {Object?} options
@ -866,6 +912,7 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
var json = {
v: 1,
name: 'hdkey',
network: this.network,
encrypted: false
};
@ -953,7 +1000,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
}
if (json.mnemonic)
return HDPrivateKey.fromSeed(json.mnemonic);
return HDPrivateKey.fromMnemonic(json.mnemonic, json.network);
if (json.xpubkey)
return HDPublicKey.fromBase58(json.xprivkey);
@ -1048,10 +1095,10 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
return cached;
if (index >= constants.hd.HARDENED || hardened)
throw new Error('Invalid index.');
throw new Error('Index out of range.');
if (index < 0)
throw new Error('Invalid path.');
throw new Error('Index out of range.');
p = new BufferWriter();
p.writeBytes(this.publicKey);

View File

@ -70,7 +70,7 @@ function Wallet(options) {
}
if (!options.master)
options.master = bcoin.hd.fromSeed(null, this.network);
options.master = bcoin.hd.fromMnemonic(null, this.network);
this.provider = options.provider || null;
this.master = options.master || null;

View File

@ -142,7 +142,7 @@ describe('HD', function() {
});
it('should deserialize and reserialize', function() {
var key = bcoin.hd.fromSeed();
var key = bcoin.hd.fromMnemonic();
assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey);
});

View File

@ -20,7 +20,7 @@ describe('Mnemonic', function() {
mnemonic.toSeed();
assert.equal(mnemonic.phrase, phrase);
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
var key = bcoin.hd.fromSeed(mnemonic);
var key = bcoin.hd.fromMnemonic(mnemonic);
assert.equal(key.xprivkey, xpriv);
});
});
@ -37,11 +37,9 @@ describe('Mnemonic', function() {
passphrase: passphrase
});
mnemonic.toSeed();
// utils.print(new Buffer(mnemonic.phrase, 'utf8').toString('hex'));
// utils.print(new Buffer(phrase, 'utf8').toString('hex'));
assert.equal(mnemonic.phrase, phrase);
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
var key = bcoin.hd.fromSeed(mnemonic);
var key = bcoin.hd.fromMnemonic(mnemonic);
assert.equal(key.xprivkey, xpriv);
});
});

View File

@ -113,7 +113,7 @@ describe('Wallet', function() {
m: 1,
n: 2
});
var k2 = bcoin.hd.fromSeed().deriveAccount44(0).hdPublicKey;
var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey;
w.addKey(k2);
// Input transcation