more hd improvements.
This commit is contained in:
parent
b164984307
commit
6690991acb
@ -174,6 +174,24 @@ ec.publicKeyVerify = function publicKeyVerify(key) {
|
|||||||
return ec.elliptic.keyPair({ pub: key }).validate();
|
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.
|
* Sign a message.
|
||||||
* @param {Buffer} msg
|
* @param {Buffer} msg
|
||||||
|
|||||||
@ -243,7 +243,7 @@ Mnemonic.isMnemonic = function isMnemonic(obj) {
|
|||||||
|
|
||||||
function HD(options, network) {
|
function HD(options, network) {
|
||||||
if (!options)
|
if (!options)
|
||||||
return HD.fromSeed(null, network);
|
return HD.fromMnemonic(null, network);
|
||||||
return HD.fromAny(options, network);
|
return HD.fromAny(options, network);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,6 +284,17 @@ HD.fromSeed = function fromSeed(options, network) {
|
|||||||
return HDPrivateKey.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.
|
* Instantiate an HD key from a jsonified key object.
|
||||||
* @param {Object} json - The jsonified transaction object.
|
* @param {Object} json - The jsonified transaction object.
|
||||||
@ -323,7 +334,7 @@ HD.fromAny = function fromAny(options, network) {
|
|||||||
if (HDPublicKey.isExtended(xkey))
|
if (HDPublicKey.isExtended(xkey))
|
||||||
return HDPublicKey.fromBase58(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)
|
if (index < constants.hd.HARDENED && hardened)
|
||||||
index += constants.hd.HARDENED;
|
index += constants.hd.HARDENED;
|
||||||
|
|
||||||
|
if (!(index >= 0 && index < constants.hd.MAX_INDEX))
|
||||||
|
throw new Error('Index out of range.');
|
||||||
|
|
||||||
p = new BufferWriter();
|
p = new BufferWriter();
|
||||||
|
|
||||||
if (hardened) {
|
if (hardened) {
|
||||||
@ -686,35 +700,33 @@ HDPrivateKey.prototype.derivePath = function derivePath(path) {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
HDPrivateKey.parseSeed = function parseSeed(seed, network) {
|
HDPrivateKey.parseSeed = function parseSeed(seed, network) {
|
||||||
var data, hash;
|
var hash, chainCode, privateKey;
|
||||||
|
|
||||||
if (!seed)
|
if (!seed)
|
||||||
seed = {};
|
seed = {};
|
||||||
|
|
||||||
if (Buffer.isBuffer(seed)) {
|
assert(Buffer.isBuffer(seed));
|
||||||
data = seed;
|
|
||||||
seed = null;
|
|
||||||
} else if (seed instanceof Mnemonic) {
|
|
||||||
data = seed.toSeed();
|
|
||||||
} else {
|
|
||||||
seed = new Mnemonic(seed);
|
|
||||||
data = seed.toSeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.length < constants.hd.MIN_ENTROPY
|
if (seed.length < constants.hd.MIN_ENTROPY
|
||||||
|| data.length > constants.hd.MAX_ENTROPY) {
|
|| seed.length > constants.hd.MAX_ENTROPY) {
|
||||||
throw new Error('Entropy not in range.');
|
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 {
|
return {
|
||||||
network: network,
|
network: network,
|
||||||
depth: 0,
|
depth: 0,
|
||||||
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
parentFingerPrint: new Buffer([0, 0, 0, 0]),
|
||||||
childIndex: 0,
|
childIndex: 0,
|
||||||
chainCode: hash.slice(32, 64),
|
chainCode: chainCode,
|
||||||
privateKey: hash.slice(0, 32),
|
privateKey: privateKey,
|
||||||
mnemonic: seed
|
mnemonic: seed
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -731,6 +743,40 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
|
|||||||
return new HDPrivateKey(HDPrivateKey.parseSeed(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.
|
* Generate an hd private key from a key and/or entropy bytes.
|
||||||
* @param {Object?} options
|
* @param {Object?} options
|
||||||
@ -866,6 +912,7 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) {
|
|||||||
var json = {
|
var json = {
|
||||||
v: 1,
|
v: 1,
|
||||||
name: 'hdkey',
|
name: 'hdkey',
|
||||||
|
network: this.network,
|
||||||
encrypted: false
|
encrypted: false
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -953,7 +1000,7 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (json.mnemonic)
|
if (json.mnemonic)
|
||||||
return HDPrivateKey.fromSeed(json.mnemonic);
|
return HDPrivateKey.fromMnemonic(json.mnemonic, json.network);
|
||||||
|
|
||||||
if (json.xpubkey)
|
if (json.xpubkey)
|
||||||
return HDPublicKey.fromBase58(json.xprivkey);
|
return HDPublicKey.fromBase58(json.xprivkey);
|
||||||
@ -1048,10 +1095,10 @@ HDPublicKey.prototype.derive = function derive(index, hardened) {
|
|||||||
return cached;
|
return cached;
|
||||||
|
|
||||||
if (index >= constants.hd.HARDENED || hardened)
|
if (index >= constants.hd.HARDENED || hardened)
|
||||||
throw new Error('Invalid index.');
|
throw new Error('Index out of range.');
|
||||||
|
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
throw new Error('Invalid path.');
|
throw new Error('Index out of range.');
|
||||||
|
|
||||||
p = new BufferWriter();
|
p = new BufferWriter();
|
||||||
p.writeBytes(this.publicKey);
|
p.writeBytes(this.publicKey);
|
||||||
|
|||||||
@ -70,7 +70,7 @@ function Wallet(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!options.master)
|
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.provider = options.provider || null;
|
||||||
this.master = options.master || null;
|
this.master = options.master || null;
|
||||||
|
|||||||
@ -142,7 +142,7 @@ describe('HD', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should deserialize and reserialize', 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);
|
assert.equal(bcoin.hd.fromJSON(key.toJSON()).xprivkey, key.xprivkey);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,7 @@ describe('Mnemonic', function() {
|
|||||||
mnemonic.toSeed();
|
mnemonic.toSeed();
|
||||||
assert.equal(mnemonic.phrase, phrase);
|
assert.equal(mnemonic.phrase, phrase);
|
||||||
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
|
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);
|
assert.equal(key.xprivkey, xpriv);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -37,11 +37,9 @@ describe('Mnemonic', function() {
|
|||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
});
|
});
|
||||||
mnemonic.toSeed();
|
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.phrase, phrase);
|
||||||
assert.equal(mnemonic.toSeed().toString('hex'), seed.toString('hex'));
|
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);
|
assert.equal(key.xprivkey, xpriv);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -113,7 +113,7 @@ describe('Wallet', function() {
|
|||||||
m: 1,
|
m: 1,
|
||||||
n: 2
|
n: 2
|
||||||
});
|
});
|
||||||
var k2 = bcoin.hd.fromSeed().deriveAccount44(0).hdPublicKey;
|
var k2 = bcoin.hd.fromMnemonic().deriveAccount44(0).hdPublicKey;
|
||||||
w.addKey(k2);
|
w.addKey(k2);
|
||||||
|
|
||||||
// Input transcation
|
// Input transcation
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user