diff --git a/lib/hd/mnemonic.js b/lib/hd/mnemonic.js index af2a623e..fa3cd632 100644 --- a/lib/hd/mnemonic.js +++ b/lib/hd/mnemonic.js @@ -19,6 +19,12 @@ const wordlist = require('./wordlist'); const common = require('./common'); const nfkd = require('../utils/nfkd'); +/* + * Constants + */ + +const wordlistCache = new Map(); + /** * HD Mnemonic * @alias module:hd.Mnemonic @@ -191,7 +197,7 @@ Mnemonic.prototype.getPhrase = function getPhrase() { // Build the mnemonic by reading // 11 bit indexes from the entropy. - const wordlist = Mnemonic.getWordlist(this.language); + const list = Mnemonic.getWordlist(this.language); let phrase = []; for (let i = 0; i < bits / 11; i++) { @@ -203,7 +209,7 @@ Mnemonic.prototype.getPhrase = function getPhrase() { index <<= 1; index |= (entropy[oct] >>> (7 - bit)) & 1; } - phrase.push(wordlist[index]); + phrase.push(list.words[index]); } // Japanese likes double-width spaces. @@ -224,6 +230,9 @@ Mnemonic.prototype.getPhrase = function getPhrase() { */ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) { + assert(typeof phrase === 'string'); + assert(phrase.length <= 1000); + const words = phrase.split(/[ \u3000]+/); let bits = words.length * 11; const cbits = bits % 32; @@ -240,14 +249,14 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) { ent.fill(0); const lang = Mnemonic.getLanguage(words[0]); - const wordlist = Mnemonic.getWordlist(lang); + const list = Mnemonic.getWordlist(lang); // Rebuild entropy bytes. for (let i = 0; i < words.length; i++) { const word = words[i]; - const index = wordlist.indexOf(word); + const index = list.map.get(word); - if (index === -1) + if (index == null) throw new Error('Could not find word.'); for (let j = 0; j < 11; j++) { @@ -267,9 +276,9 @@ Mnemonic.prototype.fromPhrase = function fromPhrase(phrase) { for (let i = 0; i < cbits; i++) { const bit = i % 8; const oct = (i - bit) / 8; - const a = (chk1[oct] >>> (7 - bit)) & 1; - const b = (chk2[oct] >>> (7 - bit)) & 1; - if (a !== b) + const b1 = (chk1[oct] >>> (7 - bit)) & 1; + const b2 = (chk2[oct] >>> (7 - bit)) & 1; + if (b1 !== b2) throw new Error('Invalid checksum.'); } @@ -337,8 +346,8 @@ Mnemonic.fromEntropy = function fromEntropy(entropy, lang) { Mnemonic.getLanguage = function getLanguage(word) { for (const lang of Mnemonic.languages) { - const wordlist = Mnemonic.getWordlist(lang); - if (wordlist.indexOf(word) !== -1) + const list = Mnemonic.getWordlist(lang); + if (list.map.has(word)) return lang; } @@ -347,12 +356,22 @@ Mnemonic.getLanguage = function getLanguage(word) { /** * Retrieve the wordlist for a language. - * @param {String} language - * @returns {String[]} + * @param {String} lang + * @returns {Object} */ -Mnemonic.getWordlist = function getWordlist(language) { - return wordlist.get(language); +Mnemonic.getWordlist = function getWordlist(lang) { + const cache = wordlistCache.get(lang); + + if (cache) + return cache; + + const words = wordlist.get(lang); + const list = new WordList(words); + + wordlistCache.set(lang, list); + + return list; }; /** @@ -530,6 +549,23 @@ Mnemonic.isMnemonic = function isMnemonic(obj) { && typeof obj.toSeed === 'function'; }; +/** + * Word List + * @constructor + * @ignore + * @param {Array} words + */ + +function WordList(words) { + this.words = words; + this.map = new Map(); + + for (let i = 0; i < words.length; i++) { + const word = words[i]; + this.map.set(word, i); + } +} + /* * Expose */