bip39 work.
This commit is contained in:
parent
7821d65db4
commit
f2edf8f4b0
402
lib/bcoin/hd.js
402
lib/bcoin/hd.js
@ -123,16 +123,30 @@ function Mnemonic(options) {
|
|||||||
return new Mnemonic(options);
|
return new Mnemonic(options);
|
||||||
|
|
||||||
this.bits = 128;
|
this.bits = 128;
|
||||||
|
this.language = 'english';
|
||||||
this.entropy = null;
|
this.entropy = null;
|
||||||
this.phrase = null;
|
this.phrase = null;
|
||||||
this.passphrase = '';
|
this.passphrase = '';
|
||||||
this.language = 'english';
|
|
||||||
this.seed = null;
|
|
||||||
|
|
||||||
if (options)
|
if (options)
|
||||||
this.fromOptions(options);
|
this.fromOptions(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of languages.
|
||||||
|
* @const {String[]}
|
||||||
|
* @default
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.languages = [
|
||||||
|
'simplified chinese',
|
||||||
|
'traditional chinese',
|
||||||
|
'english',
|
||||||
|
'french',
|
||||||
|
'italian',
|
||||||
|
'japanese'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inject properties from options object.
|
* Inject properties from options object.
|
||||||
* @private
|
* @private
|
||||||
@ -143,22 +157,35 @@ Mnemonic.prototype.fromOptions = function fromOptions(options) {
|
|||||||
if (typeof options === 'string')
|
if (typeof options === 'string')
|
||||||
options = { phrase: options };
|
options = { phrase: options };
|
||||||
|
|
||||||
if (options.bits != null)
|
if (options.bits != null) {
|
||||||
|
assert(utils.isNumber(options.bits));
|
||||||
|
assert(options.bits >= 128);
|
||||||
|
assert(options.bits % 32 === 0);
|
||||||
this.bits = options.bits;
|
this.bits = options.bits;
|
||||||
|
}
|
||||||
|
|
||||||
this.entropy = options.entropy;
|
if (options.language) {
|
||||||
this.phrase = options.phrase;
|
assert(typeof options.language === 'string');
|
||||||
|
assert(Mnemonic.languages.indexOf(options.language) !== -1);
|
||||||
if (options.passphrase)
|
|
||||||
this.passphrase = options.passphrase;
|
|
||||||
|
|
||||||
if (options.language)
|
|
||||||
this.language = options.language;
|
this.language = options.language;
|
||||||
|
}
|
||||||
|
|
||||||
this.seed = options.seed;
|
if (options.passphrase) {
|
||||||
|
assert(typeof options.passphrase === 'string');
|
||||||
|
this.passphrase = options.passphrase;
|
||||||
|
}
|
||||||
|
|
||||||
assert(this.bits >= 128);
|
if (options.phrase) {
|
||||||
assert(this.bits % 32 === 0);
|
this.fromPhrase(options.phrase);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.entropy) {
|
||||||
|
this.fromEntropy(options.entropy);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -171,24 +198,163 @@ Mnemonic.fromOptions = function fromOptions(options) {
|
|||||||
return new Mnemonic().fromOptions(options);
|
return new Mnemonic().fromOptions(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject properties from entropy.
|
||||||
|
* @private
|
||||||
|
* @param {Buffer} entropy
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.fromEntropy = function fromEntropy(entropy, lang) {
|
||||||
|
assert(Buffer.isBuffer(entropy));
|
||||||
|
assert(entropy.length * 8 >= 128);
|
||||||
|
assert((entropy.length * 8) % 32 === 0);
|
||||||
|
assert(!lang || Mnemonic.languages.indexOf(lang) !== -1);
|
||||||
|
|
||||||
|
this.entropy = entropy;
|
||||||
|
this.bits = entropy.length * 8;
|
||||||
|
|
||||||
|
if (lang)
|
||||||
|
this.language = lang;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate mnemonic from entropy.
|
||||||
|
* @param {Buffer} entropy
|
||||||
|
* @returns {Mnemonic}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.fromEntropy = function fromEntropy(entropy, lang) {
|
||||||
|
return new Mnemonic().fromEntropy(entropy, lang);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inject properties from phrase.
|
||||||
|
* @private
|
||||||
|
* @param {String} phrase
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) {
|
||||||
|
var i, j, bits, pos, oct, bit, b, ent, entropy, lang;
|
||||||
|
var chk, word, wordlist, index, cbits, cbytes, words;
|
||||||
|
|
||||||
|
assert(typeof phrase === 'string');
|
||||||
|
|
||||||
|
words = phrase.split(/[ \u3000]/);
|
||||||
|
|
||||||
|
bits = words.length * 11;
|
||||||
|
|
||||||
|
lang = Mnemonic.getLanguage(words[0]);
|
||||||
|
|
||||||
|
ent = new Buffer(Math.ceil(bits / 8));
|
||||||
|
ent.fill(0);
|
||||||
|
|
||||||
|
wordlist = Mnemonic.getWordlist(lang);
|
||||||
|
|
||||||
|
for (i = 0; i < words.length; i++) {
|
||||||
|
word = words[i];
|
||||||
|
index = wordlist.indexOf(word);
|
||||||
|
|
||||||
|
if (index === -1)
|
||||||
|
throw new Error('Could not find word.');
|
||||||
|
|
||||||
|
for (j = 0; j < 11; j++) {
|
||||||
|
pos = i * 11 + j;
|
||||||
|
bit = pos % 8;
|
||||||
|
oct = (pos - bit) / 8;
|
||||||
|
b = (index >>> (10 - j)) & 1;
|
||||||
|
ent[oct] |= b << (7 - bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checksum bits:
|
||||||
|
cbits = bits % 32;
|
||||||
|
|
||||||
|
// Checksum bytes:
|
||||||
|
cbytes = Math.ceil(cbits / 8);
|
||||||
|
|
||||||
|
bits -= bits % 32;
|
||||||
|
entropy = ent.slice(0, ent.length - cbytes);
|
||||||
|
|
||||||
|
ent = ent.slice(ent.length - cbytes);
|
||||||
|
chk = utils.sha256(entropy);
|
||||||
|
|
||||||
|
for (i = 0; i < cbits; i++) {
|
||||||
|
bit = i % 8;
|
||||||
|
oct = (i - bit) / 8;
|
||||||
|
b = (chk[oct] >>> (7 - bit)) & 1;
|
||||||
|
j = (ent[oct] >>> (7 - bit)) & 1;
|
||||||
|
if (b !== j)
|
||||||
|
throw new Error('Invalid checksum.');
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(bits / 8 === entropy.length);
|
||||||
|
assert(bits >= 128);
|
||||||
|
assert(bits % 32 === 0);
|
||||||
|
|
||||||
|
this.bits = bits;
|
||||||
|
this.language = lang;
|
||||||
|
this.entropy = entropy;
|
||||||
|
this.phrase = phrase;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiate mnemonic from a phrase.
|
||||||
|
* @param {String} phrase
|
||||||
|
* @returns {Mnemonic}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.fromPhrase = function fromPhrase(phrase) {
|
||||||
|
return new Mnemonic().fromPhrase(phrase);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate the seed.
|
* Generate the seed.
|
||||||
|
* @param {String?} passphrase
|
||||||
* @returns {Buffer} pbkdf2 seed.
|
* @returns {Buffer} pbkdf2 seed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Mnemonic.prototype.toSeed = function toSeed() {
|
Mnemonic.prototype.toSeed = function toSeed(passphrase) {
|
||||||
if (this.seed)
|
if (!passphrase)
|
||||||
return this.seed;
|
passphrase = this.passphrase;
|
||||||
|
|
||||||
if (!this.phrase)
|
this.passphrase = passphrase;
|
||||||
this.phrase = this.createMnemonic();
|
|
||||||
|
|
||||||
this.seed = utils.pbkdf2Sync(
|
return utils.pbkdf2Sync(
|
||||||
nfkd(this.phrase),
|
nfkd(this.getPhrase()),
|
||||||
nfkd('mnemonic' + this.passphrase),
|
nfkd('mnemonic' + passphrase),
|
||||||
2048, 64, 'sha512');
|
2048, 64, 'sha512');
|
||||||
|
};
|
||||||
|
|
||||||
return this.seed;
|
/**
|
||||||
|
* Generate seed and create an hd private key.
|
||||||
|
* @param {String?} passphrase
|
||||||
|
* @param {(Network|NetworkType)?} network
|
||||||
|
* @returns {HDPrivateKey}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.toKey = function toKey(passphrase, network) {
|
||||||
|
var seed = this.toSeed(passphrase);
|
||||||
|
var key = HDPrivateKey.fromSeed(seed, network);
|
||||||
|
key.mnemonic = this;
|
||||||
|
return key;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get or generate entropy.
|
||||||
|
* @returns {Buffer}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.getEntropy = function getEntropy() {
|
||||||
|
if (!this.entropy)
|
||||||
|
this.entropy = ec.random(this.bits / 8);
|
||||||
|
|
||||||
|
assert(this.bits / 8 === this.entropy.length);
|
||||||
|
|
||||||
|
return this.entropy;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,23 +362,22 @@ Mnemonic.prototype.toSeed = function toSeed() {
|
|||||||
* @returns {String}
|
* @returns {String}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Mnemonic.prototype.createMnemonic = function createMnemonic() {
|
Mnemonic.prototype.getPhrase = function getPhrase() {
|
||||||
var mnemonic = [];
|
var i, j, phrase, wordlist, bits, entropy, index, pos, oct, bit;
|
||||||
var wordlist = Mnemonic.getWordlist(this.language);
|
|
||||||
var i, j, bits, entropy, word, oct, bit;
|
|
||||||
|
|
||||||
if (!this.entropy)
|
if (this.phrase)
|
||||||
this.entropy = ec.random(this.bits / 8);
|
return this.phrase;
|
||||||
|
|
||||||
bits = this.entropy.length * 8;
|
phrase = [];
|
||||||
|
wordlist = Mnemonic.getWordlist(this.language);
|
||||||
|
|
||||||
|
entropy = this.getEntropy();
|
||||||
|
bits = this.bits;
|
||||||
|
|
||||||
// Append the hash to the entropy to
|
// Append the hash to the entropy to
|
||||||
// make things easy when grabbing
|
// make things easy when grabbing
|
||||||
// the checksum bits.
|
// the checksum bits.
|
||||||
entropy = Buffer.concat([
|
entropy = Buffer.concat([entropy, utils.sha256(entropy)]);
|
||||||
this.entropy,
|
|
||||||
utils.sha256(this.entropy)
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Include the first `ENT / 32` bits
|
// Include the first `ENT / 32` bits
|
||||||
// of the hash (the checksum).
|
// of the hash (the checksum).
|
||||||
@ -220,24 +385,47 @@ Mnemonic.prototype.createMnemonic = function createMnemonic() {
|
|||||||
|
|
||||||
// Build the mnemonic by reading
|
// Build the mnemonic by reading
|
||||||
// 11 bit indexes from the entropy.
|
// 11 bit indexes from the entropy.
|
||||||
for (i = 0; i < bits; i++) {
|
for (i = 0; i < bits / 11; i++) {
|
||||||
i--;
|
index = 0;
|
||||||
word = 0;
|
|
||||||
for (j = 0; j < 11; j++) {
|
for (j = 0; j < 11; j++) {
|
||||||
i++;
|
pos = i * 11 + j;
|
||||||
bit = i % 8;
|
bit = pos % 8;
|
||||||
oct = (i - bit) / 8;
|
oct = (pos - bit) / 8;
|
||||||
word <<= 1;
|
index <<= 1;
|
||||||
word |= (entropy[oct] >>> (7 - bit)) & 1;
|
index |= (entropy[oct] >>> (7 - bit)) & 1;
|
||||||
}
|
}
|
||||||
mnemonic.push(wordlist[word]);
|
phrase.push(wordlist[index]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Japanese likes double-width spaces.
|
// Japanese likes double-width spaces.
|
||||||
if (this.language === 'japanese')
|
if (this.language === 'japanese')
|
||||||
return mnemonic.join('\u3000');
|
phrase = phrase.join('\u3000');
|
||||||
|
else
|
||||||
|
phrase = phrase.join(' ');
|
||||||
|
|
||||||
return mnemonic.join(' ');
|
this.phrase = phrase;
|
||||||
|
|
||||||
|
return phrase;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine a single word's language.
|
||||||
|
* @param {String} word
|
||||||
|
* @returns {String} Language.
|
||||||
|
* @throws on not found.
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.getLanguage = function getLanguage(word) {
|
||||||
|
var i, lang, wordlist;
|
||||||
|
|
||||||
|
for (i = 0; i < Mnemonic.languages.length; i++) {
|
||||||
|
lang = Mnemonic.languages[i];
|
||||||
|
wordlist = Mnemonic.getWordlist(lang);
|
||||||
|
if (wordlist.indexOf(word) !== -1)
|
||||||
|
return lang;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Could not determine language.');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -261,7 +449,7 @@ Mnemonic.getWordlist = function getWordlist(language) {
|
|||||||
case 'japanese':
|
case 'japanese':
|
||||||
return require('../../etc/japanese.js');
|
return require('../../etc/japanese.js');
|
||||||
default:
|
default:
|
||||||
assert(false, 'Unknown language: ' + language);
|
throw new Error('Unknown language: ' + language);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -273,11 +461,10 @@ Mnemonic.getWordlist = function getWordlist(language) {
|
|||||||
Mnemonic.prototype.toJSON = function toJSON() {
|
Mnemonic.prototype.toJSON = function toJSON() {
|
||||||
return {
|
return {
|
||||||
bits: this.bits,
|
bits: this.bits,
|
||||||
entropy: this.entropy ? this.entropy.toString('hex') : null,
|
|
||||||
phrase: this.phrase,
|
|
||||||
passphrase: this.passphrase,
|
|
||||||
language: this.language,
|
language: this.language,
|
||||||
seed: this.seed ? this.seed.toString('hex') : null
|
entropy: this.getEntropy().toString('hex'),
|
||||||
|
phrase: this.getPhrase(),
|
||||||
|
passphrase: this.passphrase
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -289,25 +476,18 @@ Mnemonic.prototype.toJSON = function toJSON() {
|
|||||||
|
|
||||||
Mnemonic.prototype.fromJSON = function fromJSON(json) {
|
Mnemonic.prototype.fromJSON = function fromJSON(json) {
|
||||||
assert(utils.isNumber(json.bits));
|
assert(utils.isNumber(json.bits));
|
||||||
assert(!json.entropy || typeof json.entropy === 'string');
|
|
||||||
assert(!json.phrase || typeof json.phrase === 'string');
|
|
||||||
assert(typeof json.passphrase === 'string');
|
|
||||||
assert(typeof json.language === 'string');
|
assert(typeof json.language === 'string');
|
||||||
assert(!json.seed || typeof json.seed === 'string');
|
assert(typeof json.entropy === 'string');
|
||||||
|
assert(typeof json.phrase === 'string');
|
||||||
|
assert(typeof json.passphrase === 'string');
|
||||||
|
|
||||||
this.bits = json.bits;
|
this.bits = json.bits;
|
||||||
|
|
||||||
if (json.entry)
|
|
||||||
this.entropy = new Buffer(json.entropy, 'hex');
|
|
||||||
|
|
||||||
if (json.phrase)
|
|
||||||
this.phrase = json.phrase;
|
|
||||||
|
|
||||||
this.passphrase = json.passphrase;
|
|
||||||
this.language = json.language;
|
this.language = json.language;
|
||||||
|
this.entropy = new Buffer(json.entropy, 'hex');
|
||||||
|
this.phrase = json.phrase;
|
||||||
|
this.passphrase = json.passphrase;
|
||||||
|
|
||||||
if (json.seed)
|
assert(this.bits / 8 === this.entropy.length);
|
||||||
this.seed = new Buffer(json.seed, 'hex');
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -331,30 +511,10 @@ Mnemonic.prototype.toRaw = function toRaw(writer) {
|
|||||||
var p = new BufferWriter(writer);
|
var p = new BufferWriter(writer);
|
||||||
|
|
||||||
p.writeU16(this.bits);
|
p.writeU16(this.bits);
|
||||||
|
|
||||||
if (this.entropy) {
|
|
||||||
p.writeU8(1);
|
|
||||||
p.writeBytes(this.entropy);
|
|
||||||
} else {
|
|
||||||
p.writeU8(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.phrase) {
|
|
||||||
p.writeU8(1);
|
|
||||||
p.writeVarString(this.phrase, 'utf8');
|
|
||||||
} else {
|
|
||||||
p.writeU8(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.writeVarString(this.passphrase, 'utf8');
|
|
||||||
p.writeVarString(this.language, 'utf8');
|
p.writeVarString(this.language, 'utf8');
|
||||||
|
p.writeBytes(this.getEntropy());
|
||||||
if (this.seed) {
|
p.writeVarString(this.getPhrase(), 'utf8');
|
||||||
p.writeU8(1);
|
p.writeVarString(this.passphrase, 'utf8');
|
||||||
p.writeBytes(this.seed);
|
|
||||||
} else {
|
|
||||||
p.writeU8(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!writer)
|
if (!writer)
|
||||||
p = p.render();
|
p = p.render();
|
||||||
@ -372,18 +532,10 @@ Mnemonic.prototype.fromRaw = function fromRaw(data) {
|
|||||||
var p = new BufferReader(data);
|
var p = new BufferReader(data);
|
||||||
|
|
||||||
this.bits = p.readU16();
|
this.bits = p.readU16();
|
||||||
|
|
||||||
if (p.readU8() === 1)
|
|
||||||
this.entropy = p.readBytes(this.bits / 8);
|
|
||||||
|
|
||||||
if (p.readU8() === 1)
|
|
||||||
this.phrase = p.readVarString('utf8');
|
|
||||||
|
|
||||||
this.passphrase = p.readVarString('utf8');
|
|
||||||
this.language = p.readVarString('utf8');
|
this.language = p.readVarString('utf8');
|
||||||
|
this.entropy = p.readBytes(this.bits / 8);
|
||||||
if (p.readU8() === 1)
|
this.phrase = p.readVarString('utf8');
|
||||||
this.seed = p.readBytes(64);
|
this.passphrase = p.readVarString('utf8');
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
@ -398,6 +550,24 @@ Mnemonic.fromRaw = function fromRaw(data) {
|
|||||||
return new Mnemonic().fromRaw(data);
|
return new Mnemonic().fromRaw(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert the mnemonic to a string.
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.toString = function toString() {
|
||||||
|
return this.getPhrase();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inspect the mnemonic.
|
||||||
|
* @returns {String}
|
||||||
|
*/
|
||||||
|
|
||||||
|
Mnemonic.prototype.inspect = function inspect() {
|
||||||
|
return '<Mnemonic: ' + this.getPhrase() + '>';
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test whether an object is a Mnemonic.
|
* Test whether an object is a Mnemonic.
|
||||||
* @param {Object} obj
|
* @param {Object} obj
|
||||||
@ -1038,31 +1208,8 @@ HDPrivateKey.fromSeed = function fromSeed(seed, network) {
|
|||||||
HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) {
|
HDPrivateKey.prototype.fromMnemonic = function fromMnemonic(mnemonic, network) {
|
||||||
if (!(mnemonic instanceof Mnemonic))
|
if (!(mnemonic instanceof Mnemonic))
|
||||||
mnemonic = new Mnemonic(mnemonic);
|
mnemonic = new Mnemonic(mnemonic);
|
||||||
|
this.fromSeed(mnemonic.toSeed(), network);
|
||||||
if (mnemonic.seed || mnemonic.phrase || mnemonic.entropy) {
|
this.mnemonic = mnemonic;
|
||||||
this.fromSeed(mnemonic.toSeed(), network);
|
|
||||||
this.mnemonic = mnemonic;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Very unlikely, but not impossible
|
|
||||||
// to get an invalid private key.
|
|
||||||
for (;;) {
|
|
||||||
try {
|
|
||||||
this.fromSeed(mnemonic.toSeed(), network);
|
|
||||||
this.mnemonic = mnemonic;
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message === 'Master private key is invalid.') {
|
|
||||||
mnemonic.seed = null;
|
|
||||||
mnemonic.phrase = null;
|
|
||||||
mnemonic.entropy = null;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1301,11 +1448,11 @@ HDPrivateKey.prototype.toJSON = function toJSON() {
|
|||||||
HDPrivateKey.prototype.fromJSON = function fromJSON(json) {
|
HDPrivateKey.prototype.fromJSON = function fromJSON(json) {
|
||||||
assert(json.xprivkey, 'Could not handle key JSON.');
|
assert(json.xprivkey, 'Could not handle key JSON.');
|
||||||
|
|
||||||
|
this.fromBase58(json.xprivkey);
|
||||||
|
|
||||||
if (json.mnemonic)
|
if (json.mnemonic)
|
||||||
this.mnemonic = Mnemonic.fromJSON(json.mnemonic);
|
this.mnemonic = Mnemonic.fromJSON(json.mnemonic);
|
||||||
|
|
||||||
this.fromBase58(json.xprivkey);
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1861,8 +2008,9 @@ function nfkd(str) {
|
|||||||
* Expose
|
* Expose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
HD.Mnemonic = Mnemonic;
|
exports = HD;
|
||||||
HD.PrivateKey = HDPrivateKey;
|
exports.Mnemonic = Mnemonic;
|
||||||
HD.PublicKey = HDPublicKey;
|
exports.PrivateKey = HDPrivateKey;
|
||||||
|
exports.PublicKey = HDPublicKey;
|
||||||
|
|
||||||
module.exports = HD;
|
module.exports = HD;
|
||||||
|
|||||||
@ -13,7 +13,6 @@ var assert = utils.assert;
|
|||||||
var constants = bcoin.protocol.constants;
|
var constants = bcoin.protocol.constants;
|
||||||
var Script = bcoin.script;
|
var Script = bcoin.script;
|
||||||
var opcodes = constants.opcodes;
|
var opcodes = constants.opcodes;
|
||||||
var HASH160 = constants.ZERO_HASH.slice(0, 20);
|
|
||||||
var FundingError = bcoin.errors.FundingError;
|
var FundingError = bcoin.errors.FundingError;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1061,7 +1060,7 @@ MTX.prototype.selectCoins = function selectCoins(coins, options) {
|
|||||||
// use a fake p2pkh output to gauge size.
|
// use a fake p2pkh output to gauge size.
|
||||||
script: options.changeAddress
|
script: options.changeAddress
|
||||||
? Script.fromAddress(options.changeAddress)
|
? Script.fromAddress(options.changeAddress)
|
||||||
: Script.fromPubkeyhash(HASH160),
|
: Script.fromPubkeyhash(constants.ZERO_HASH160),
|
||||||
value: 0
|
value: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -19,13 +19,13 @@ describe('Mnemonic', function() {
|
|||||||
entropy: entropy,
|
entropy: entropy,
|
||||||
passphrase: 'TREZOR'
|
passphrase: 'TREZOR'
|
||||||
});
|
});
|
||||||
mnemonic.toSeed();
|
assert.equal(mnemonic.getPhrase(), 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.fromMnemonic(mnemonic);
|
var key = bcoin.hd.fromMnemonic(mnemonic);
|
||||||
assert.equal(key.xprivkey, xpriv);
|
assert.equal(key.xprivkey, xpriv);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
mnemonic2.forEach(function(data, i) {
|
mnemonic2.forEach(function(data, i) {
|
||||||
var entropy = new Buffer(data.entropy, 'hex');
|
var entropy = new Buffer(data.entropy, 'hex');
|
||||||
var phrase = data.mnemonic;
|
var phrase = data.mnemonic;
|
||||||
@ -38,11 +38,19 @@ describe('Mnemonic', function() {
|
|||||||
entropy: entropy,
|
entropy: entropy,
|
||||||
passphrase: passphrase
|
passphrase: passphrase
|
||||||
});
|
});
|
||||||
mnemonic.toSeed();
|
assert.equal(mnemonic.getPhrase(), 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.fromMnemonic(mnemonic);
|
var key = bcoin.hd.fromMnemonic(mnemonic);
|
||||||
assert.equal(key.xprivkey, xpriv);
|
assert.equal(key.xprivkey, xpriv);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should verify phrase', function() {
|
||||||
|
var m1 = new bcoin.hd.Mnemonic();
|
||||||
|
var m2 = bcoin.hd.Mnemonic.fromPhrase(m1.getPhrase());
|
||||||
|
assert.deepEqual(m2.getEntropy(), m1.getEntropy());
|
||||||
|
assert.equal(m2.bits, m1.bits);
|
||||||
|
assert.equal(m2.language, m1.language);
|
||||||
|
assert.deepEqual(m2.toSeed(), m1.toSeed());
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user