Browse Source

Allow excluding wordlists when building for browserify

tsAllowExclude
junderw 6 years ago
parent
commit
c30b47b63d
No known key found for this signature in database GPG Key ID: B256185D3A971908
  1. 56
      README.md
  2. 51
      src/_wordlists.js
  3. 41
      src/index.js
  4. 42
      ts_src/_wordlists.ts
  5. 50
      ts_src/index.ts
  6. 3
      tsconfig.json
  7. 5
      types/_wordlists.d.ts
  8. 14
      types/index.d.ts
  9. 5
      types/wordlists.d.ts

56
README.md

@ -16,6 +16,62 @@ When a checksum is invalid, warn the user that the phrase is not something gener
However, there should be other checks in place, such as checking to make sure the user is inputting 12 words or more separated by a space. ie. `phrase.trim().split(/\s+/g).length >= 12` However, there should be other checks in place, such as checking to make sure the user is inputting 12 words or more separated by a space. ie. `phrase.trim().split(/\s+/g).length >= 12`
## Removing wordlists from webpack/browserify
Browserify/Webpack bundles can get very large if you include all the wordlists, so you can now exclude wordlists to make your bundle lighter.
For example, if we want to exclude all wordlists besides chinese_simplified, you could build using the browserify command below.
```bash
$ browserify -r bip39 -s bip39 \
--exclude=./wordlists/english.json \
--exclude=./wordlists/japanese.json \
--exclude=./wordlists/spanish.json \
--exclude=./wordlists/italian.json \
--exclude=./wordlists/french.json \
--exclude=./wordlists/korean.json \
--exclude=./wordlists/chinese_traditional.json \
> bip39.browser.js
```
This will create a bundle that only contains the chinese_simplified wordlist, and it will be the default wordlist for all calls without explicit wordlists.
This is how it will look in the browser console.
```javascript
> bip39.entropyToMnemonic('00000000000000000000000000000000')
"的 的 的 的 的 的 的 的 的 的 的 在"
> bip39.wordlists.chinese_simplified
Array(2048) [ "的", "一", "是", "在", "不", "了", "有", "和", "人", "这", … ]
> bip39.wordlists.english
undefined
> bip39.wordlists.japanese
undefined
> bip39.wordlists.spanish
undefined
> bip39.wordlists.italian
undefined
> bip39.wordlists.french
undefined
> bip39.wordlists.korean
undefined
> bip39.wordlists.chinese_traditional
undefined
```
For a list of supported wordlists check the wordlists folder. The name of the json file (minus the extension) is the name of the key to access the wordlist.
You can also change the default wordlist at runtime if you dislike the wordlist you were given as default.
```javascript
> bip39.entropyToMnemonic('00000000000000000000000000000fff')
"あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あまい ろんり"
> bip39.setDefaultWordlist('italian')
undefined
> bip39.entropyToMnemonic('00000000000000000000000000000fff')
"abaco abaco abaco abaco abaco abaco abaco abaco abaco abaco aforisma zibetto"
```
## Installation ## Installation
``` bash ``` bash
npm install bip39 npm install bip39

51
src/_wordlists.js

@ -0,0 +1,51 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// browserify by default only pulls in files that are hard coded in requires
// In order of last to first in this file, the default wordlist will be chosen
// based on what is present. (Bundles may remove wordlists they don't need)
const wordlists = {};
exports.wordlists = wordlists;
let _default;
exports._default = _default;
try {
exports._default = _default = require('./wordlists/chinese_simplified.json');
wordlists.chinese_simplified = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/chinese_traditional.json');
wordlists.chinese_traditional = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/korean.json');
wordlists.korean = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/french.json');
wordlists.french = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/italian.json');
wordlists.italian = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/spanish.json');
wordlists.spanish = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/japanese.json');
wordlists.japanese = _default;
wordlists.JA = _default;
}
catch (err) { }
try {
exports._default = _default = require('./wordlists/english.json');
wordlists.english = _default;
wordlists.EN = _default;
}
catch (err) { }

41
src/index.js

@ -5,18 +5,13 @@ const pbkdf2_1 = require("pbkdf2");
const randomBytes = require("randombytes"); const randomBytes = require("randombytes");
// use unorm until String.prototype.normalize gets better browser support // use unorm until String.prototype.normalize gets better browser support
const unorm = require("unorm"); const unorm = require("unorm");
const CHINESE_SIMPLIFIED_WORDLIST = require("./wordlists/chinese_simplified.json"); const _wordlists_1 = require("./_wordlists");
const CHINESE_TRADITIONAL_WORDLIST = require("./wordlists/chinese_traditional.json"); let DEFAULT_WORDLIST = _wordlists_1._default;
const ENGLISH_WORDLIST = require("./wordlists/english.json");
const FRENCH_WORDLIST = require("./wordlists/french.json");
const ITALIAN_WORDLIST = require("./wordlists/italian.json");
const JAPANESE_WORDLIST = require("./wordlists/japanese.json");
const KOREAN_WORDLIST = require("./wordlists/korean.json");
const SPANISH_WORDLIST = require("./wordlists/spanish.json");
const DEFAULT_WORDLIST = ENGLISH_WORDLIST;
const INVALID_MNEMONIC = 'Invalid mnemonic'; const INVALID_MNEMONIC = 'Invalid mnemonic';
const INVALID_ENTROPY = 'Invalid entropy'; const INVALID_ENTROPY = 'Invalid entropy';
const INVALID_CHECKSUM = 'Invalid mnemonic checksum'; const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
const WORDLIST_REQUIRED = 'A wordlist is required but a default could not be found.\n' +
'Please explicitly pass a 2048 word array explicitly.';
function lpad(str, padString, length) { function lpad(str, padString, length) {
while (str.length < length) while (str.length < length)
str = padString + str; str = padString + str;
@ -74,6 +69,9 @@ async function mnemonicToSeedHexAsync(mnemonic, password) {
exports.mnemonicToSeedHexAsync = mnemonicToSeedHexAsync; exports.mnemonicToSeedHexAsync = mnemonicToSeedHexAsync;
function mnemonicToEntropy(mnemonic, wordlist) { function mnemonicToEntropy(mnemonic, wordlist) {
wordlist = wordlist || DEFAULT_WORDLIST; wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
const words = unorm.nfkd(mnemonic).split(' '); const words = unorm.nfkd(mnemonic).split(' ');
if (words.length % 3 !== 0) if (words.length % 3 !== 0)
throw new Error(INVALID_MNEMONIC); throw new Error(INVALID_MNEMONIC);
@ -109,6 +107,9 @@ function entropyToMnemonic(entropy, wordlist) {
if (!Buffer.isBuffer(entropy)) if (!Buffer.isBuffer(entropy))
entropy = Buffer.from(entropy, 'hex'); entropy = Buffer.from(entropy, 'hex');
wordlist = wordlist || DEFAULT_WORDLIST; wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
// 128 <= ENT <= 256 // 128 <= ENT <= 256
if (entropy.length < 16) if (entropy.length < 16)
throw new TypeError(INVALID_ENTROPY); throw new TypeError(INVALID_ENTROPY);
@ -124,7 +125,7 @@ function entropyToMnemonic(entropy, wordlist) {
const index = binaryToByte(binary); const index = binaryToByte(binary);
return wordlist[index]; return wordlist[index];
}); });
return wordlist === JAPANESE_WORDLIST return wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093' // Japanese wordlist
? words.join('\u3000') ? words.join('\u3000')
: words.join(' '); : words.join(' ');
} }
@ -147,15 +148,11 @@ function validateMnemonic(mnemonic, wordlist) {
return true; return true;
} }
exports.validateMnemonic = validateMnemonic; exports.validateMnemonic = validateMnemonic;
exports.wordlists = { function setDefaultWordlist(language) {
EN: ENGLISH_WORDLIST, const result = _wordlists_1.wordlists[language];
JA: JAPANESE_WORDLIST, if (result)
chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST, DEFAULT_WORDLIST = result;
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST, }
english: ENGLISH_WORDLIST, exports.setDefaultWordlist = setDefaultWordlist;
french: FRENCH_WORDLIST, var _wordlists_2 = require("./_wordlists");
italian: ITALIAN_WORDLIST, exports.wordlists = _wordlists_2.wordlists;
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST,
};

42
ts_src/_wordlists.ts

@ -0,0 +1,42 @@
// browserify by default only pulls in files that are hard coded in requires
// In order of last to first in this file, the default wordlist will be chosen
// based on what is present. (Bundles may remove wordlists they don't need)
const wordlists: { [index: string]: string[] } = {};
let _default: string[] | undefined;
try {
_default = require('./wordlists/chinese_simplified.json');
wordlists.chinese_simplified = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/chinese_traditional.json');
wordlists.chinese_traditional = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/korean.json');
wordlists.korean = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/french.json');
wordlists.french = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/italian.json');
wordlists.italian = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/spanish.json');
wordlists.spanish = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/japanese.json');
wordlists.japanese = _default as string[];
wordlists.JA = _default as string[];
} catch (err) {}
try {
_default = require('./wordlists/english.json');
wordlists.english = _default as string[];
wordlists.EN = _default as string[];
} catch (err) {}
// Last one to overwrite wordlist gets to be default.
export { wordlists, _default };

50
ts_src/index.ts

@ -1,23 +1,18 @@
import createHash = require('create-hash'); import * as createHash from 'create-hash';
import { pbkdf2 as pbkdf2Async, pbkdf2Sync as pbkdf2 } from 'pbkdf2'; import { pbkdf2 as pbkdf2Async, pbkdf2Sync as pbkdf2 } from 'pbkdf2';
import randomBytes = require('randombytes'); import * as randomBytes from 'randombytes';
// use unorm until String.prototype.normalize gets better browser support // use unorm until String.prototype.normalize gets better browser support
import unorm = require('unorm'); import * as unorm from 'unorm';
import { _default as _DEFAULT_WORDLIST, wordlists } from './_wordlists';
import CHINESE_SIMPLIFIED_WORDLIST = require('./wordlists/chinese_simplified.json');
import CHINESE_TRADITIONAL_WORDLIST = require('./wordlists/chinese_traditional.json'); let DEFAULT_WORDLIST: string[] | undefined = _DEFAULT_WORDLIST;
import ENGLISH_WORDLIST = require('./wordlists/english.json');
import FRENCH_WORDLIST = require('./wordlists/french.json');
import ITALIAN_WORDLIST = require('./wordlists/italian.json');
import JAPANESE_WORDLIST = require('./wordlists/japanese.json');
import KOREAN_WORDLIST = require('./wordlists/korean.json');
import SPANISH_WORDLIST = require('./wordlists/spanish.json');
const DEFAULT_WORDLIST = ENGLISH_WORDLIST;
const INVALID_MNEMONIC = 'Invalid mnemonic'; const INVALID_MNEMONIC = 'Invalid mnemonic';
const INVALID_ENTROPY = 'Invalid entropy'; const INVALID_ENTROPY = 'Invalid entropy';
const INVALID_CHECKSUM = 'Invalid mnemonic checksum'; const INVALID_CHECKSUM = 'Invalid mnemonic checksum';
const WORDLIST_REQUIRED =
'A wordlist is required but a default could not be found.\n' +
'Please explicitly pass a 2048 word array explicitly.';
function lpad(str: string, padString: string, length: number): string { function lpad(str: string, padString: string, length: number): string {
while (str.length < length) str = padString + str; while (str.length < length) str = padString + str;
@ -97,6 +92,9 @@ export function mnemonicToEntropy(
wordlist?: string[], wordlist?: string[],
): string { ): string {
wordlist = wordlist || DEFAULT_WORDLIST; wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
const words = unorm.nfkd(mnemonic).split(' '); const words = unorm.nfkd(mnemonic).split(' ');
if (words.length % 3 !== 0) throw new Error(INVALID_MNEMONIC); if (words.length % 3 !== 0) throw new Error(INVALID_MNEMONIC);
@ -135,6 +133,9 @@ export function entropyToMnemonic(
): string { ): string {
if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex'); if (!Buffer.isBuffer(entropy)) entropy = Buffer.from(entropy, 'hex');
wordlist = wordlist || DEFAULT_WORDLIST; wordlist = wordlist || DEFAULT_WORDLIST;
if (!wordlist) {
throw new Error(WORDLIST_REQUIRED);
}
// 128 <= ENT <= 256 // 128 <= ENT <= 256
if (entropy.length < 16) throw new TypeError(INVALID_ENTROPY); if (entropy.length < 16) throw new TypeError(INVALID_ENTROPY);
@ -151,7 +152,7 @@ export function entropyToMnemonic(
return wordlist![index]; return wordlist![index];
}); });
return wordlist === JAPANESE_WORDLIST return wordlist[0] === '\u3042\u3044\u3053\u304f\u3057\u3093' // Japanese wordlist
? words.join('\u3000') ? words.join('\u3000')
: words.join(' '); : words.join(' ');
} }
@ -181,16 +182,9 @@ export function validateMnemonic(
return true; return true;
} }
export const wordlists = { export function setDefaultWordlist(language: string): void {
EN: ENGLISH_WORDLIST, const result = wordlists[language];
JA: JAPANESE_WORDLIST, if (result) DEFAULT_WORDLIST = result;
}
chinese_simplified: CHINESE_SIMPLIFIED_WORDLIST,
chinese_traditional: CHINESE_TRADITIONAL_WORDLIST, export { wordlists } from './_wordlists';
english: ENGLISH_WORDLIST,
french: FRENCH_WORDLIST,
italian: ITALIAN_WORDLIST,
japanese: JAPANESE_WORDLIST,
korean: KOREAN_WORDLIST,
spanish: SPANISH_WORDLIST,
};

3
tsconfig.json

@ -24,7 +24,8 @@
"resolveJsonModule": true "resolveJsonModule": true
}, },
"include": [ "include": [
"ts_src/**/*.ts" "ts_src/**/*.ts",
"ts_src/**/*.json"
], ],
"exclude": [ "exclude": [
"**/*.spec.ts", "**/*.spec.ts",

5
types/_wordlists.d.ts

@ -0,0 +1,5 @@
declare const wordlists: {
[index: string]: string[];
};
declare let _default: string[] | undefined;
export { wordlists, _default };

14
types/index.d.ts

@ -7,15 +7,5 @@ export declare function mnemonicToEntropy(mnemonic: string, wordlist?: string[])
export declare function entropyToMnemonic(entropy: Buffer | string, wordlist?: string[]): string; export declare function entropyToMnemonic(entropy: Buffer | string, wordlist?: string[]): string;
export declare function generateMnemonic(strength?: number, rng?: (size: number) => Buffer, wordlist?: string[]): string; export declare function generateMnemonic(strength?: number, rng?: (size: number) => Buffer, wordlist?: string[]): string;
export declare function validateMnemonic(mnemonic: string, wordlist?: string[]): boolean; export declare function validateMnemonic(mnemonic: string, wordlist?: string[]): boolean;
export declare const wordlists: { export declare function setDefaultWordlist(language: string): void;
EN: string[]; export { wordlists } from './_wordlists';
JA: string[];
chinese_simplified: string[];
chinese_traditional: string[];
english: string[];
french: string[];
italian: string[];
japanese: string[];
korean: string[];
spanish: string[];
};

5
types/wordlists.d.ts

@ -0,0 +1,5 @@
declare const wordlists: {
[index: string]: string[];
};
declare let _default: string[] | undefined;
export { wordlists, _default };
Loading…
Cancel
Save