]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - src/js/index.js
Warn when generating low entropy mnemonics
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / src / js / index.js
CommitLineData
ebd8d4e8
IC
1(function() {
2
5ee7bb9e
IC
3 // mnemonics is populated as required by getLanguage
4 var mnemonics = { "english": new Mnemonic("english") };
5 var mnemonic = mnemonics["english"];
3725abb5 6 var seed = null;
ebd8d4e8
IC
7 var bip32RootKey = null;
8 var bip32ExtendedKey = null;
a0091a40 9 var network = bitcoinjs.bitcoin.networks.bitcoin;
ebd8d4e8
IC
10 var addressRowTemplate = $("#address-row-template");
11
700901cd
IC
12 var showIndex = true;
13 var showAddress = true;
1b12b2f5 14 var showPubKey = true;
700901cd 15 var showPrivKey = true;
8a93952c 16 var showQr = false;
1c2b8c6b 17 var litecoinUseLtub = true;
700901cd 18
c6624d51 19 var entropyChangeTimeoutEvent = null;
ebd8d4e8 20 var phraseChangeTimeoutEvent = null;
efe41586 21 var rootKeyChangedTimeoutEvent = null;
ebd8d4e8 22
40892aba
IC
23 var generationProcesses = [];
24
ebd8d4e8 25 var DOM = {};
d6cedc94 26 DOM.network = $(".network");
29bf60f5 27 DOM.bip32Client = $("#bip32-client");
d6cedc94 28 DOM.phraseNetwork = $("#network-phrase");
c6624d51
IC
29 DOM.useEntropy = $(".use-entropy");
30 DOM.entropyContainer = $(".entropy-container");
31 DOM.entropy = $(".entropy");
0a84fe6a
IC
32 DOM.entropyFiltered = DOM.entropyContainer.find(".filtered");
33 DOM.entropyType = DOM.entropyContainer.find(".type");
20f459ce 34 DOM.entropyCrackTime = DOM.entropyContainer.find(".crack-time");
0a84fe6a
IC
35 DOM.entropyEventCount = DOM.entropyContainer.find(".event-count");
36 DOM.entropyBits = DOM.entropyContainer.find(".bits");
37 DOM.entropyBitsPerEvent = DOM.entropyContainer.find(".bits-per-event");
38 DOM.entropyWordCount = DOM.entropyContainer.find(".word-count");
39 DOM.entropyBinary = DOM.entropyContainer.find(".binary");
74ab4cbe 40 DOM.entropyWordIndexes = DOM.entropyContainer.find(".word-indexes");
09d63290 41 DOM.entropyChecksum = DOM.entropyContainer.find(".checksum");
0a84fe6a 42 DOM.entropyMnemonicLength = DOM.entropyContainer.find(".mnemonic-length");
ee0981f1 43 DOM.entropyFilterWarning = DOM.entropyContainer.find(".filter-warning");
ebd8d4e8 44 DOM.phrase = $(".phrase");
1abcc511 45 DOM.passphrase = $(".passphrase");
c6624d51 46 DOM.generateContainer = $(".generate-container");
ebd8d4e8 47 DOM.generate = $(".generate");
3e0ed16a 48 DOM.seed = $(".seed");
ebd8d4e8 49 DOM.rootKey = $(".root-key");
3abab9b0
IC
50 DOM.litecoinLtubContainer = $(".litecoin-ltub-container");
51 DOM.litecoinUseLtub = $(".litecoin-use-ltub");
ebd8d4e8
IC
52 DOM.extendedPrivKey = $(".extended-priv-key");
53 DOM.extendedPubKey = $(".extended-pub-key");
d6cedc94
IC
54 DOM.bip32tab = $("#bip32-tab");
55 DOM.bip44tab = $("#bip44-tab");
6c08f364 56 DOM.bip49tab = $("#bip49-tab");
4e9b492c 57 DOM.bip84tab = $("#bip84-tab");
c49e8812 58 DOM.bip141tab = $("#bip141-tab");
d6cedc94
IC
59 DOM.bip32panel = $("#bip32");
60 DOM.bip44panel = $("#bip44");
6c08f364 61 DOM.bip49panel = $("#bip49");
ebd8d4e8
IC
62 DOM.bip32path = $("#bip32-path");
63 DOM.bip44path = $("#bip44-path");
64 DOM.bip44purpose = $("#bip44 .purpose");
65 DOM.bip44coin = $("#bip44 .coin");
66 DOM.bip44account = $("#bip44 .account");
c554e6ff
IC
67 DOM.bip44accountXprv = $("#bip44 .account-xprv");
68 DOM.bip44accountXpub = $("#bip44 .account-xpub");
ebd8d4e8 69 DOM.bip44change = $("#bip44 .change");
6c08f364
IC
70 DOM.bip49unavailable = $("#bip49 .unavailable");
71 DOM.bip49available = $("#bip49 .available");
72 DOM.bip49path = $("#bip49-path");
73 DOM.bip49purpose = $("#bip49 .purpose");
74 DOM.bip49coin = $("#bip49 .coin");
75 DOM.bip49account = $("#bip49 .account");
76 DOM.bip49accountXprv = $("#bip49 .account-xprv");
77 DOM.bip49accountXpub = $("#bip49 .account-xpub");
78 DOM.bip49change = $("#bip49 .change");
4e9b492c
IC
79 DOM.bip84path = $("#bip84-path");
80 DOM.bip84purpose = $("#bip84 .purpose");
81 DOM.bip84coin = $("#bip84 .coin");
82 DOM.bip84account = $("#bip84 .account");
83 DOM.bip84accountXprv = $("#bip84 .account-xprv");
84 DOM.bip84accountXpub = $("#bip84 .account-xpub");
85 DOM.bip84change = $("#bip84 .change");
c49e8812
IC
86 DOM.bip141unavailable = $("#bip141 .unavailable");
87 DOM.bip141available = $("#bip141 .available");
88 DOM.bip141path = $("#bip141-path");
89 DOM.bip141semantics = $(".bip141-semantics");
88df3739 90 DOM.generatedStrength = $(".generate-container .strength");
85c90672 91 DOM.generatedStrengthWarning = $(".generate-container .warning");
146e089e 92 DOM.hardenedAddresses = $(".hardened-addresses");
fe8f2d14
IC
93 DOM.useBitpayAddressesContainer = $(".use-bitpay-addresses-container");
94 DOM.useBitpayAddresses = $(".use-bitpay-addresses");
0b39e9dc
IC
95 DOM.useBip38 = $(".use-bip38");
96 DOM.bip38Password = $(".bip38-password");
ebd8d4e8 97 DOM.addresses = $(".addresses");
a78f4e28
IC
98 DOM.csvTab = $("#csv-tab a");
99 DOM.csv = $(".csv");
ebd8d4e8
IC
100 DOM.rowsToAdd = $(".rows-to-add");
101 DOM.more = $(".more");
9183f9f6 102 DOM.moreRowsStartIndex = $(".more-rows-start-index");
ebd8d4e8
IC
103 DOM.feedback = $(".feedback");
104 DOM.tab = $(".derivation-type a");
105 DOM.indexToggle = $(".index-toggle");
106 DOM.addressToggle = $(".address-toggle");
1b12b2f5 107 DOM.publicKeyToggle = $(".public-key-toggle");
ebd8d4e8 108 DOM.privateKeyToggle = $(".private-key-toggle");
5ee7bb9e 109 DOM.languages = $(".languages a");
e00964cc 110 DOM.qrContainer = $(".qr-container");
97811c29 111 DOM.qrHider = DOM.qrContainer.find(".qr-hider");
e00964cc
IC
112 DOM.qrImage = DOM.qrContainer.find(".qr-image");
113 DOM.qrHint = DOM.qrContainer.find(".qr-hint");
114 DOM.showQrEls = $("[data-show-qr]");
ebd8d4e8 115
ebd8d4e8
IC
116 function init() {
117 // Events
85c90672 118 DOM.generatedStrength.on("change", generatedStrengthChanged);
d6cedc94 119 DOM.network.on("change", networkChanged);
29bf60f5 120 DOM.bip32Client.on("change", bip32ClientChanged);
c6624d51
IC
121 DOM.useEntropy.on("change", setEntropyVisibility);
122 DOM.entropy.on("input", delayedEntropyChanged);
3599674d 123 DOM.entropyMnemonicLength.on("change", entropyChanged);
a19a5498
IC
124 DOM.phrase.on("input", delayedPhraseChanged);
125 DOM.passphrase.on("input", delayedPhraseChanged);
ebd8d4e8
IC
126 DOM.generate.on("click", generateClicked);
127 DOM.more.on("click", showMore);
efe41586 128 DOM.rootKey.on("input", delayedRootKeyChanged);
3abab9b0 129 DOM.litecoinUseLtub.on("change", litecoinUseLtubChanged);
efe41586 130 DOM.bip32path.on("input", calcForDerivationPath);
efe41586
IC
131 DOM.bip44account.on("input", calcForDerivationPath);
132 DOM.bip44change.on("input", calcForDerivationPath);
6c08f364
IC
133 DOM.bip49account.on("input", calcForDerivationPath);
134 DOM.bip49change.on("input", calcForDerivationPath);
4e9b492c
IC
135 DOM.bip84account.on("input", calcForDerivationPath);
136 DOM.bip84change.on("input", calcForDerivationPath);
c49e8812
IC
137 DOM.bip141path.on("input", calcForDerivationPath);
138 DOM.bip141semantics.on("change", tabChanged);
93c3ef47 139 DOM.tab.on("shown.bs.tab", tabChanged);
146e089e 140 DOM.hardenedAddresses.on("change", calcForDerivationPath);
0b39e9dc
IC
141 DOM.useBip38.on("change", calcForDerivationPath);
142 DOM.bip38Password.on("change", calcForDerivationPath);
ebd8d4e8
IC
143 DOM.indexToggle.on("click", toggleIndexes);
144 DOM.addressToggle.on("click", toggleAddresses);
1b12b2f5 145 DOM.publicKeyToggle.on("click", togglePublicKeys);
ebd8d4e8 146 DOM.privateKeyToggle.on("click", togglePrivateKeys);
a78f4e28 147 DOM.csvTab.on("click", updateCsv);
5ee7bb9e 148 DOM.languages.on("click", languageChanged);
fe8f2d14 149 DOM.useBitpayAddresses.on("change", useBitpayAddressesChange);
e00964cc 150 setQrEvents(DOM.showQrEls);
ebd8d4e8
IC
151 disableForms();
152 hidePending();
153 hideValidationError();
7f15cb6e 154 populateNetworkSelect();
b4fd763c 155 populateClientSelect();
ebd8d4e8
IC
156 }
157
158 // Event handlers
159
85c90672
IC
160 function generatedStrengthChanged() {
161 var strength = parseInt(DOM.generatedStrength.val());
162 if (strength < 12) {
163 DOM.generatedStrengthWarning.removeClass("hidden");
164 }
165 else {
166 DOM.generatedStrengthWarning.addClass("hidden");
167 }
168 }
169
d6cedc94 170 function networkChanged(e) {
6c08f364
IC
171 clearDerivedKeys();
172 clearAddressesList();
3abab9b0 173 DOM.litecoinLtubContainer.addClass("hidden");
fe8f2d14 174 DOM.useBitpayAddressesContainer.addClass("hidden");
54563907 175 var networkIndex = e.target.value;
6c08f364
IC
176 var network = networks[networkIndex];
177 network.onSelect();
0fd67b59 178 if (network.segwitAvailable) {
c49e8812 179 adjustNetworkForSegwit();
0fd67b59 180 showSegwitAvailable();
6c08f364
IC
181 }
182 else {
0fd67b59 183 showSegwitUnavailable();
6c08f364 184 }
54563907
IC
185 if (seed != null) {
186 phraseChanged();
187 }
188 else {
189 rootKeyChanged();
190 }
d6cedc94 191 }
29bf60f5
IC
192
193 function bip32ClientChanged(e) {
194 var clientIndex = DOM.bip32Client.val();
195 if (clientIndex == "custom") {
196 DOM.bip32path.prop("readonly", false);
b4fd763c
AG
197 }
198 else {
29bf60f5
IC
199 DOM.bip32path.prop("readonly", true);
200 clients[clientIndex].onSelect();
201 if (seed != null) {
202 phraseChanged();
203 }
204 else {
205 rootKeyChanged();
206 }
b4fd763c
AG
207 }
208 }
d6cedc94 209
c6624d51
IC
210 function setEntropyVisibility() {
211 if (isUsingOwnEntropy()) {
212 DOM.entropyContainer.removeClass("hidden");
213 DOM.generateContainer.addClass("hidden");
214 DOM.phrase.prop("readonly", true);
215 DOM.entropy.focus();
216 entropyChanged();
217 }
218 else {
219 DOM.entropyContainer.addClass("hidden");
220 DOM.generateContainer.removeClass("hidden");
221 DOM.phrase.prop("readonly", false);
057722b0 222 hidePending();
c6624d51
IC
223 }
224 }
225
ebd8d4e8
IC
226 function delayedPhraseChanged() {
227 hideValidationError();
ed6d9d39
IC
228 seed = null;
229 bip32RootKey = null;
230 bip32ExtendedKey = null;
231 clearAddressesList();
ebd8d4e8
IC
232 showPending();
233 if (phraseChangeTimeoutEvent != null) {
234 clearTimeout(phraseChangeTimeoutEvent);
235 }
236 phraseChangeTimeoutEvent = setTimeout(phraseChanged, 400);
237 }
238
239 function phraseChanged() {
240 showPending();
5ee7bb9e 241 setMnemonicLanguage();
ebd8d4e8
IC
242 // Get the mnemonic phrase
243 var phrase = DOM.phrase.val();
244 var errorText = findPhraseErrors(phrase);
245 if (errorText) {
246 showValidationError(errorText);
247 return;
248 }
efe41586
IC
249 // Calculate and display
250 var passphrase = DOM.passphrase.val();
251 calcBip32RootKeyFromSeed(phrase, passphrase);
252 calcForDerivationPath();
74ab4cbe
IC
253 // Show the word indexes
254 showWordIndexes();
efe41586
IC
255 }
256
93c3ef47
IC
257 function tabChanged() {
258 showPending();
0fd67b59 259 adjustNetworkForSegwit();
93c3ef47
IC
260 var phrase = DOM.phrase.val();
261 if (phrase != "") {
262 // Calculate and display for mnemonic
263 var errorText = findPhraseErrors(phrase);
264 if (errorText) {
265 showValidationError(errorText);
266 return;
267 }
268 // Calculate and display
269 var passphrase = DOM.passphrase.val();
270 calcBip32RootKeyFromSeed(phrase, passphrase);
271 }
272 else {
273 // Calculate and display for root key
274 var rootKeyBase58 = DOM.rootKey.val();
275 var errorText = validateRootKey(rootKeyBase58);
276 if (errorText) {
277 showValidationError(errorText);
278 return;
279 }
280 // Calculate and display
281 calcBip32RootKeyFromBase58(rootKeyBase58);
282 }
283 calcForDerivationPath();
284 }
285
c6624d51
IC
286 function delayedEntropyChanged() {
287 hideValidationError();
288 showPending();
289 if (entropyChangeTimeoutEvent != null) {
290 clearTimeout(entropyChangeTimeoutEvent);
291 }
292 entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
293 }
294
295 function entropyChanged() {
057722b0
IC
296 // If blank entropy, clear mnemonic, addresses, errors
297 if (DOM.entropy.val().trim().length == 0) {
298 clearDisplay();
0a84fe6a 299 clearEntropyFeedback();
057722b0
IC
300 DOM.phrase.val("");
301 showValidationError("Blank entropy");
302 return;
303 }
304 // Get the current phrase to detect changes
305 var phrase = DOM.phrase.val();
306 // Set the phrase from the entropy
c6624d51 307 setMnemonicFromEntropy();
057722b0
IC
308 // Recalc addresses if the phrase has changed
309 var newPhrase = DOM.phrase.val();
310 if (newPhrase != phrase) {
311 if (newPhrase.length == 0) {
312 clearDisplay();
313 }
314 else {
315 phraseChanged();
316 }
317 }
318 else {
319 hidePending();
320 }
c6624d51
IC
321 }
322
efe41586
IC
323 function delayedRootKeyChanged() {
324 // Warn if there is an existing mnemonic or passphrase.
325 if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
326 if (!confirm("This will clear existing mnemonic and passphrase")) {
327 DOM.rootKey.val(bip32RootKey);
328 return
329 }
330 }
331 hideValidationError();
332 showPending();
333 // Clear existing mnemonic and passphrase
334 DOM.phrase.val("");
335 DOM.passphrase.val("");
336 seed = null;
337 if (rootKeyChangedTimeoutEvent != null) {
338 clearTimeout(rootKeyChangedTimeoutEvent);
339 }
340 rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400);
341 }
342
343 function rootKeyChanged() {
344 showPending();
345 hideValidationError();
efe41586
IC
346 var rootKeyBase58 = DOM.rootKey.val();
347 var errorText = validateRootKey(rootKeyBase58);
348 if (errorText) {
349 showValidationError(errorText);
350 return;
351 }
352 // Calculate and display
353 calcBip32RootKeyFromBase58(rootKeyBase58);
354 calcForDerivationPath();
efe41586
IC
355 }
356
3abab9b0
IC
357 function litecoinUseLtubChanged() {
358 litecoinUseLtub = DOM.litecoinUseLtub.prop("checked");
359 if (litecoinUseLtub) {
1c2b8c6b 360 network = bitcoinjs.bitcoin.networks.litecoin;
3abab9b0
IC
361 }
362 else {
1c2b8c6b 363 network = bitcoinjs.bitcoin.networks.litecoinXprv;
3abab9b0
IC
364 }
365 phraseChanged();
366 }
367
efe41586 368 function calcForDerivationPath() {
6c08f364 369 clearDerivedKeys();
ba3cb9ec 370 clearAddressesList();
0eda54f5 371 showPending();
0fd67b59
IC
372 // Don't show segwit if it's selected but network doesn't support it
373 if (segwitSelected() && !networkHasSegwit()) {
6c08f364
IC
374 return;
375 }
ebd8d4e8 376 // Get the derivation path
38523d36
IC
377 var derivationPath = getDerivationPath();
378 var errorText = findDerivationPathErrors(derivationPath);
ebd8d4e8
IC
379 if (errorText) {
380 showValidationError(errorText);
381 return;
382 }
5eaa6877 383 bip32ExtendedKey = calcBip32ExtendedKey(derivationPath);
c554e6ff
IC
384 if (bip44TabSelected()) {
385 displayBip44Info();
386 }
c49e8812 387 else if (bip49TabSelected()) {
6c08f364
IC
388 displayBip49Info();
389 }
4e9b492c
IC
390 else if (bip84TabSelected()) {
391 displayBip84Info();
392 }
ebd8d4e8 393 displayBip32Info();
ebd8d4e8
IC
394 }
395
396 function generateClicked() {
c6624d51
IC
397 if (isUsingOwnEntropy()) {
398 return;
399 }
ebd8d4e8
IC
400 clearDisplay();
401 showPending();
402 setTimeout(function() {
5ee7bb9e 403 setMnemonicLanguage();
ebd8d4e8
IC
404 var phrase = generateRandomPhrase();
405 if (!phrase) {
406 return;
407 }
408 phraseChanged();
409 }, 50);
410 }
411
5ee7bb9e
IC
412 function languageChanged() {
413 setTimeout(function() {
414 setMnemonicLanguage();
415 if (DOM.phrase.val().length > 0) {
416 var newPhrase = convertPhraseToNewLanguage();
417 DOM.phrase.val(newPhrase);
418 phraseChanged();
419 }
420 else {
421 DOM.generate.trigger("click");
422 }
423 }, 50);
424 }
425
fe8f2d14
IC
426 function useBitpayAddressesChange() {
427 setBitcoinCashNetworkValues();
428 phraseChanged();
429 }
430
ebd8d4e8 431 function toggleIndexes() {
700901cd 432 showIndex = !showIndex;
ebd8d4e8
IC
433 $("td.index span").toggleClass("invisible");
434 }
435
436 function toggleAddresses() {
700901cd 437 showAddress = !showAddress;
ebd8d4e8
IC
438 $("td.address span").toggleClass("invisible");
439 }
440
1b12b2f5
IC
441 function togglePublicKeys() {
442 showPubKey = !showPubKey;
443 $("td.pubkey span").toggleClass("invisible");
444 }
445
ebd8d4e8 446 function togglePrivateKeys() {
700901cd 447 showPrivKey = !showPrivKey;
ebd8d4e8
IC
448 $("td.privkey span").toggleClass("invisible");
449 }
450
451 // Private methods
452
453 function generateRandomPhrase() {
454 if (!hasStrongRandom()) {
455 var errorText = "This browser does not support strong randomness";
456 showValidationError(errorText);
457 return;
458 }
74ab4cbe 459 // get the amount of entropy to use
88df3739 460 var numWords = parseInt(DOM.generatedStrength.val());
ebd8d4e8 461 var strength = numWords / 3 * 32;
74ab4cbe
IC
462 var buffer = new Uint8Array(strength / 8);
463 // create secure entropy
464 var data = crypto.getRandomValues(buffer);
465 // show the words
466 var words = mnemonic.toMnemonic(data);
ebd8d4e8 467 DOM.phrase.val(words);
74ab4cbe
IC
468 // show the entropy
469 var entropyHex = uint8ArrayToHex(data);
470 DOM.entropy.val(entropyHex);
471 // ensure entropy fields are consistent with what is being displayed
472 DOM.entropyMnemonicLength.val("raw");
ebd8d4e8
IC
473 return words;
474 }
475
efe41586 476 function calcBip32RootKeyFromSeed(phrase, passphrase) {
3e0ed16a 477 seed = mnemonic.toSeed(phrase, passphrase);
a0091a40 478 bip32RootKey = bitcoinjs.bitcoin.HDNode.fromSeedHex(seed, network);
efe41586
IC
479 }
480
481 function calcBip32RootKeyFromBase58(rootKeyBase58) {
a0091a40 482 bip32RootKey = bitcoinjs.bitcoin.HDNode.fromBase58(rootKeyBase58, network);
efe41586
IC
483 }
484
485 function calcBip32ExtendedKey(path) {
0a1f0259
IC
486 // Check there's a root key to derive from
487 if (!bip32RootKey) {
488 return bip32RootKey;
489 }
5eaa6877 490 var extendedKey = bip32RootKey;
ebd8d4e8
IC
491 // Derive the key from the path
492 var pathBits = path.split("/");
493 for (var i=0; i<pathBits.length; i++) {
494 var bit = pathBits[i];
495 var index = parseInt(bit);
496 if (isNaN(index)) {
497 continue;
498 }
499 var hardened = bit[bit.length-1] == "'";
a0091a40 500 var isPriv = !(extendedKey.isNeutered());
ba3cb9ec
IC
501 var invalidDerivationPath = hardened && !isPriv;
502 if (invalidDerivationPath) {
503 extendedKey = null;
504 }
505 else if (hardened) {
5eaa6877 506 extendedKey = extendedKey.deriveHardened(index);
ebd8d4e8
IC
507 }
508 else {
5eaa6877 509 extendedKey = extendedKey.derive(index);
ebd8d4e8
IC
510 }
511 }
5eaa6877 512 return extendedKey
ebd8d4e8
IC
513 }
514
515 function showValidationError(errorText) {
516 DOM.feedback
517 .text(errorText)
518 .show();
519 }
520
521 function hideValidationError() {
522 DOM.feedback
523 .text("")
524 .hide();
525 }
526
527 function findPhraseErrors(phrase) {
ebd8d4e8 528 // Preprocess the words
783981de 529 phrase = mnemonic.normalizeString(phrase);
5ee7bb9e 530 var words = phraseToWordArray(phrase);
057722b0
IC
531 // Detect blank phrase
532 if (words.length == 0) {
533 return "Blank mnemonic";
534 }
563e401a 535 // Check each word
5ee7bb9e
IC
536 for (var i=0; i<words.length; i++) {
537 var word = words[i];
538 var language = getLanguage();
539 if (WORDLISTS[language].indexOf(word) == -1) {
563e401a
IC
540 console.log("Finding closest match to " + word);
541 var nearestWord = findNearestWord(word);
542 return word + " not in wordlist, did you mean " + nearestWord + "?";
543 }
544 }
ebd8d4e8 545 // Check the words are valid
5ee7bb9e 546 var properPhrase = wordArrayToPhrase(words);
ebd8d4e8
IC
547 var isValid = mnemonic.check(properPhrase);
548 if (!isValid) {
549 return "Invalid mnemonic";
550 }
551 return false;
552 }
553
efe41586
IC
554 function validateRootKey(rootKeyBase58) {
555 try {
cd7c8327 556 bitcoinjs.bitcoin.HDNode.fromBase58(rootKeyBase58, network);
efe41586
IC
557 }
558 catch (e) {
559 return "Invalid root key";
560 }
561 return "";
562 }
563
38523d36 564 function getDerivationPath() {
32fab2c3 565 if (bip44TabSelected()) {
38523d36
IC
566 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
567 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
568 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
569 var change = parseIntNoNaN(DOM.bip44change.val(), 0);
570 var path = "m/";
571 path += purpose + "'/";
572 path += coin + "'/";
573 path += account + "'/";
574 path += change;
575 DOM.bip44path.val(path);
576 var derivationPath = DOM.bip44path.val();
577 console.log("Using derivation path from BIP44 tab: " + derivationPath);
578 return derivationPath;
579 }
c49e8812 580 else if (bip49TabSelected()) {
6c08f364
IC
581 var purpose = parseIntNoNaN(DOM.bip49purpose.val(), 49);
582 var coin = parseIntNoNaN(DOM.bip49coin.val(), 0);
583 var account = parseIntNoNaN(DOM.bip49account.val(), 0);
584 var change = parseIntNoNaN(DOM.bip49change.val(), 0);
585 var path = "m/";
586 path += purpose + "'/";
587 path += coin + "'/";
588 path += account + "'/";
589 path += change;
590 DOM.bip49path.val(path);
591 var derivationPath = DOM.bip49path.val();
592 console.log("Using derivation path from BIP49 tab: " + derivationPath);
593 return derivationPath;
594 }
4e9b492c
IC
595 else if (bip84TabSelected()) {
596 var purpose = parseIntNoNaN(DOM.bip84purpose.val(), 84);
597 var coin = parseIntNoNaN(DOM.bip84coin.val(), 0);
598 var account = parseIntNoNaN(DOM.bip84account.val(), 0);
599 var change = parseIntNoNaN(DOM.bip84change.val(), 0);
600 var path = "m/";
601 path += purpose + "'/";
602 path += coin + "'/";
603 path += account + "'/";
604 path += change;
605 DOM.bip84path.val(path);
606 var derivationPath = DOM.bip84path.val();
607 console.log("Using derivation path from BIP84 tab: " + derivationPath);
608 return derivationPath;
609 }
32fab2c3 610 else if (bip32TabSelected()) {
38523d36
IC
611 var derivationPath = DOM.bip32path.val();
612 console.log("Using derivation path from BIP32 tab: " + derivationPath);
613 return derivationPath;
614 }
c49e8812
IC
615 else if (bip141TabSelected()) {
616 var derivationPath = DOM.bip141path.val();
617 console.log("Using derivation path from BIP141 tab: " + derivationPath);
618 return derivationPath;
619 }
38523d36
IC
620 else {
621 console.log("Unknown derivation path");
622 }
623 }
624
ebd8d4e8 625 function findDerivationPathErrors(path) {
30c9e79d
IC
626 // TODO is not perfect but is better than nothing
627 // Inspired by
628 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors
629 // and
630 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys
631 var maxDepth = 255; // TODO verify this!!
632 var maxIndexValue = Math.pow(2, 31); // TODO verify this!!
633 if (path[0] != "m") {
634 return "First character must be 'm'";
635 }
636 if (path.length > 1) {
637 if (path[1] != "/") {
638 return "Separator must be '/'";
639 }
640 var indexes = path.split("/");
641 if (indexes.length > maxDepth) {
642 return "Derivation depth is " + indexes.length + ", must be less than " + maxDepth;
643 }
644 for (var depth = 1; depth<indexes.length; depth++) {
645 var index = indexes[depth];
646 var invalidChars = index.replace(/^[0-9]+'?$/g, "")
647 if (invalidChars.length > 0) {
648 return "Invalid characters " + invalidChars + " found at depth " + depth;
649 }
650 var indexValue = parseInt(index.replace("'", ""));
651 if (isNaN(depth)) {
652 return "Invalid number at depth " + depth;
653 }
654 if (indexValue > maxIndexValue) {
655 return "Value of " + indexValue + " at depth " + depth + " must be less than " + maxIndexValue;
656 }
657 }
658 }
0a1f0259
IC
659 // Check root key exists or else derivation path is useless!
660 if (!bip32RootKey) {
661 return "No root key";
662 }
ba3cb9ec 663 // Check no hardened derivation path when using xpub keys
b18eb97a
IC
664 var hardenedPath = path.indexOf("'") > -1;
665 var hardenedAddresses = bip32TabSelected() && DOM.hardenedAddresses.prop("checked");
666 var hardened = hardenedPath || hardenedAddresses;
a0091a40 667 var isXpubkey = bip32RootKey.isNeutered();
ba3cb9ec
IC
668 if (hardened && isXpubkey) {
669 return "Hardened derivation path is invalid with xpub key";
670 }
ebd8d4e8
IC
671 return false;
672 }
673
c554e6ff
IC
674 function displayBip44Info() {
675 // Get the derivation path for the account
676 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
677 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
678 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
679 var path = "m/";
680 path += purpose + "'/";
681 path += coin + "'/";
682 path += account + "'/";
683 // Calculate the account extended keys
684 var accountExtendedKey = calcBip32ExtendedKey(path);
685 var accountXprv = accountExtendedKey.toBase58();
a0091a40 686 var accountXpub = accountExtendedKey.neutered().toBase58();
c554e6ff
IC
687 // Display the extended keys
688 DOM.bip44accountXprv.val(accountXprv);
689 DOM.bip44accountXpub.val(accountXpub);
690 }
691
6c08f364
IC
692 function displayBip49Info() {
693 // Get the derivation path for the account
694 var purpose = parseIntNoNaN(DOM.bip49purpose.val(), 49);
695 var coin = parseIntNoNaN(DOM.bip49coin.val(), 0);
696 var account = parseIntNoNaN(DOM.bip49account.val(), 0);
697 var path = "m/";
698 path += purpose + "'/";
699 path += coin + "'/";
700 path += account + "'/";
701 // Calculate the account extended keys
702 var accountExtendedKey = calcBip32ExtendedKey(path);
703 var accountXprv = accountExtendedKey.toBase58();
704 var accountXpub = accountExtendedKey.neutered().toBase58();
705 // Display the extended keys
706 DOM.bip49accountXprv.val(accountXprv);
707 DOM.bip49accountXpub.val(accountXpub);
708 }
709
4e9b492c
IC
710 function displayBip84Info() {
711 // Get the derivation path for the account
712 var purpose = parseIntNoNaN(DOM.bip84purpose.val(), 84);
713 var coin = parseIntNoNaN(DOM.bip84coin.val(), 0);
714 var account = parseIntNoNaN(DOM.bip84account.val(), 0);
715 var path = "m/";
716 path += purpose + "'/";
717 path += coin + "'/";
718 path += account + "'/";
719 // Calculate the account extended keys
720 var accountExtendedKey = calcBip32ExtendedKey(path);
721 var accountXprv = accountExtendedKey.toBase58();
722 var accountXpub = accountExtendedKey.neutered().toBase58();
723 // Display the extended keys
724 DOM.bip84accountXprv.val(accountXprv);
725 DOM.bip84accountXpub.val(accountXpub);
726 }
727
ebd8d4e8
IC
728 function displayBip32Info() {
729 // Display the key
3e0ed16a 730 DOM.seed.val(seed);
ebd8d4e8
IC
731 var rootKey = bip32RootKey.toBase58();
732 DOM.rootKey.val(rootKey);
ba3cb9ec 733 var xprvkeyB58 = "NA";
a0091a40 734 if (!bip32ExtendedKey.isNeutered()) {
ba3cb9ec
IC
735 xprvkeyB58 = bip32ExtendedKey.toBase58();
736 }
737 var extendedPrivKey = xprvkeyB58;
ebd8d4e8 738 DOM.extendedPrivKey.val(extendedPrivKey);
a0091a40 739 var extendedPubKey = bip32ExtendedKey.neutered().toBase58();
ebd8d4e8
IC
740 DOM.extendedPubKey.val(extendedPubKey);
741 // Display the addresses and privkeys
742 clearAddressesList();
ba678b11
IC
743 var initialAddressCount = parseInt(DOM.rowsToAdd.val());
744 displayAddresses(0, initialAddressCount);
ebd8d4e8
IC
745 }
746
747 function displayAddresses(start, total) {
40892aba
IC
748 generationProcesses.push(new (function() {
749
750 var rows = [];
751
752 this.stop = function() {
753 for (var i=0; i<rows.length; i++) {
754 rows[i].shouldGenerate = false;
755 }
0eda54f5 756 hidePending();
40892aba
IC
757 }
758
759 for (var i=0; i<total; i++) {
760 var index = i + start;
0eda54f5
IC
761 var isLast = i == total - 1;
762 rows.push(new TableRow(index, isLast));
40892aba
IC
763 }
764
765 })());
ebd8d4e8
IC
766 }
767
0fd67b59 768 function segwitSelected() {
4e9b492c 769 return bip49TabSelected() || bip84TabSelected() || bip141TabSelected();
c49e8812
IC
770 }
771
772 function p2wpkhSelected() {
4e9b492c
IC
773 return bip84TabSelected() ||
774 bip141TabSelected() && DOM.bip141semantics.val() == "p2wpkh";
c49e8812
IC
775 }
776
777 function p2wpkhInP2shSelected() {
778 return bip49TabSelected() ||
779 (bip141TabSelected() && DOM.bip141semantics.val() == "p2wpkh-p2sh");
93c3ef47
IC
780 }
781
0eda54f5 782 function TableRow(index, isLast) {
a8c45487 783
40892aba
IC
784 var self = this;
785 this.shouldGenerate = true;
146e089e 786 var useHardenedAddresses = DOM.hardenedAddresses.prop("checked");
0b39e9dc
IC
787 var useBip38 = DOM.useBip38.prop("checked");
788 var bip38password = DOM.bip38Password.val();
0fd67b59
IC
789 var isSegwit = segwitSelected();
790 var segwitAvailable = networkHasSegwit();
c49e8812
IC
791 var isP2wpkh = p2wpkhSelected();
792 var isP2wpkhInP2sh = p2wpkhInP2shSelected();
146e089e 793
a8c45487
IC
794 function init() {
795 calculateValues();
796 }
797
798 function calculateValues() {
799 setTimeout(function() {
40892aba
IC
800 if (!self.shouldGenerate) {
801 return;
802 }
cc61ec30 803 // derive HDkey for this row of the table
a0091a40 804 var key = "NA";
146e089e
IC
805 if (useHardenedAddresses) {
806 key = bip32ExtendedKey.deriveHardened(index);
807 }
808 else {
809 key = bip32ExtendedKey.derive(index);
810 }
cc61ec30
IC
811 // bip38 requires uncompressed keys
812 // see https://github.com/iancoleman/bip39/issues/140#issuecomment-352164035
813 var keyPair = key.keyPair;
814 var useUncompressed = useBip38;
815 if (useUncompressed) {
816 keyPair = new bitcoinjs.bitcoin.ECPair(keyPair.d, null, { compressed: false });
817 }
818 // get address
819 var address = keyPair.getAddress().toString();
820 // get privkey
821 var hasPrivkey = !key.isNeutered();
ba3cb9ec 822 var privkey = "NA";
cc61ec30
IC
823 if (hasPrivkey) {
824 privkey = keyPair.toWIF(network);
825 // BIP38 encode private key if required
826 if (useBip38) {
827 privkey = bitcoinjsBip38.encrypt(keyPair.d.toBuffer(), false, bip38password, function(p) {
828 console.log("Progressed " + p.percent.toFixed(1) + "% for index " + index);
829 });
830 }
ba3cb9ec 831 }
cc61ec30
IC
832 // get pubkey
833 var pubkey = keyPair.getPublicKeyBuffer().toString('hex');
38523d36 834 var indexText = getDerivationPath() + "/" + index;
146e089e
IC
835 if (useHardenedAddresses) {
836 indexText = indexText + "'";
837 }
0edac945 838 // Ethereum values are different
534481b6 839 if (networks[DOM.network.val()].name == "ETH - Ethereum") {
cc61ec30 840 var privKeyBuffer = keyPair.d.toBuffer(32);
0edac945
IC
841 privkey = privKeyBuffer.toString('hex');
842 var addressBuffer = ethUtil.privateToAddress(privKeyBuffer);
49b21f12
IC
843 var hexAddress = addressBuffer.toString('hex');
844 var checksumAddress = ethUtil.toChecksumAddress(hexAddress);
845 address = ethUtil.addHexPrefix(checksumAddress);
d0239db4
IC
846 privkey = ethUtil.addHexPrefix(privkey);
847 pubkey = ethUtil.addHexPrefix(pubkey);
0edac945 848 }
64a7d2aa 849 // Ripple values are different
534481b6 850 if (networks[DOM.network.val()].name == "XRP - Ripple") {
64a7d2aa 851 privkey = convertRipplePriv(privkey);
852 address = convertRippleAdrr(address);
853 }
0fd67b59
IC
854 // Segwit addresses are different
855 if (isSegwit) {
856 if (!segwitAvailable) {
6c08f364
IC
857 return;
858 }
c49e8812
IC
859 if (isP2wpkh) {
860 var keyhash = bitcoinjs.bitcoin.crypto.hash160(key.getPublicKeyBuffer());
861 var scriptpubkey = bitcoinjs.bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
862 address = bitcoinjs.bitcoin.address.fromOutputScript(scriptpubkey, network)
863 }
864 else if (isP2wpkhInP2sh) {
865 var keyhash = bitcoinjs.bitcoin.crypto.hash160(key.getPublicKeyBuffer());
866 var scriptsig = bitcoinjs.bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
867 var addressbytes = bitcoinjs.bitcoin.crypto.hash160(scriptsig);
868 var scriptpubkey = bitcoinjs.bitcoin.script.scriptHash.output.encode(addressbytes);
869 address = bitcoinjs.bitcoin.address.fromOutputScript(scriptpubkey, network)
870 }
6c08f364 871 }
1b12b2f5 872 addAddressToList(indexText, address, pubkey, privkey);
0eda54f5
IC
873 if (isLast) {
874 hidePending();
a78f4e28 875 updateCsv();
0eda54f5 876 }
a8c45487
IC
877 }, 50)
878 }
879
880 init();
881
882 }
883
ebd8d4e8 884 function showMore() {
ebd8d4e8
IC
885 var rowsToAdd = parseInt(DOM.rowsToAdd.val());
886 if (isNaN(rowsToAdd)) {
887 rowsToAdd = 20;
888 DOM.rowsToAdd.val("20");
889 }
9183f9f6
IC
890 var start = parseInt(DOM.moreRowsStartIndex.val())
891 if (isNaN(start)) {
892 start = lastIndexInTable() + 1;
893 }
894 else {
895 var newStart = start + rowsToAdd;
896 DOM.moreRowsStartIndex.val(newStart);
897 }
ebd8d4e8
IC
898 if (rowsToAdd > 200) {
899 var msg = "Generating " + rowsToAdd + " rows could take a while. ";
900 msg += "Do you want to continue?";
901 if (!confirm(msg)) {
902 return;
903 }
904 }
ebd8d4e8 905 displayAddresses(start, rowsToAdd);
ebd8d4e8
IC
906 }
907
908 function clearDisplay() {
909 clearAddressesList();
fa2e4e93 910 clearKeys();
ebd8d4e8
IC
911 hideValidationError();
912 }
913
914 function clearAddressesList() {
915 DOM.addresses.empty();
a78f4e28 916 DOM.csv.val("");
40892aba
IC
917 stopGenerating();
918 }
919
920 function stopGenerating() {
921 while (generationProcesses.length > 0) {
922 var generation = generationProcesses.shift();
923 generation.stop();
924 }
ebd8d4e8
IC
925 }
926
fa2e4e93
IC
927 function clearKeys() {
928 clearRootKey();
929 clearDerivedKeys();
930 }
931
932 function clearRootKey() {
ebd8d4e8 933 DOM.rootKey.val("");
fa2e4e93
IC
934 }
935
936 function clearDerivedKeys() {
ebd8d4e8
IC
937 DOM.extendedPrivKey.val("");
938 DOM.extendedPubKey.val("");
fa2e4e93
IC
939 DOM.bip44accountXprv.val("");
940 DOM.bip44accountXpub.val("");
ebd8d4e8
IC
941 }
942
1b12b2f5 943 function addAddressToList(indexText, address, pubkey, privkey) {
ebd8d4e8 944 var row = $(addressRowTemplate.html());
700901cd
IC
945 // Elements
946 var indexCell = row.find(".index span");
947 var addressCell = row.find(".address span");
1b12b2f5 948 var pubkeyCell = row.find(".pubkey span");
700901cd
IC
949 var privkeyCell = row.find(".privkey span");
950 // Content
ae30fed8 951 indexCell.text(indexText);
700901cd 952 addressCell.text(address);
1b12b2f5 953 pubkeyCell.text(pubkey);
700901cd
IC
954 privkeyCell.text(privkey);
955 // Visibility
956 if (!showIndex) {
957 indexCell.addClass("invisible");
958 }
959 if (!showAddress) {
960 addressCell.addClass("invisible");
961 }
1b12b2f5
IC
962 if (!showPubKey) {
963 pubkeyCell.addClass("invisible");
964 }
700901cd 965 if (!showPrivKey) {
6d628db7 966 privkeyCell.addClass("invisible");
700901cd 967 }
ebd8d4e8 968 DOM.addresses.append(row);
e00964cc
IC
969 var rowShowQrEls = row.find("[data-show-qr]");
970 setQrEvents(rowShowQrEls);
ebd8d4e8
IC
971 }
972
973 function hasStrongRandom() {
974 return 'crypto' in window && window['crypto'] !== null;
975 }
976
977 function disableForms() {
978 $("form").on("submit", function(e) {
979 e.preventDefault();
980 });
981 }
982
ebd8d4e8
IC
983 function parseIntNoNaN(val, defaultVal) {
984 var v = parseInt(val);
985 if (isNaN(v)) {
986 return defaultVal;
987 }
988 return v;
989 }
990
991 function showPending() {
992 DOM.feedback
993 .text("Calculating...")
994 .show();
995 }
996
563e401a 997 function findNearestWord(word) {
5ee7bb9e
IC
998 var language = getLanguage();
999 var words = WORDLISTS[language];
563e401a
IC
1000 var minDistance = 99;
1001 var closestWord = words[0];
1002 for (var i=0; i<words.length; i++) {
1003 var comparedTo = words[i];
6ea15134
IC
1004 if (comparedTo.indexOf(word) == 0) {
1005 return comparedTo;
1006 }
563e401a
IC
1007 var distance = Levenshtein.get(word, comparedTo);
1008 if (distance < minDistance) {
1009 closestWord = comparedTo;
1010 minDistance = distance;
1011 }
1012 }
1013 return closestWord;
1014 }
1015
ebd8d4e8
IC
1016 function hidePending() {
1017 DOM.feedback
1018 .text("")
1019 .hide();
1020 }
1021
7f15cb6e
IC
1022 function populateNetworkSelect() {
1023 for (var i=0; i<networks.length; i++) {
1024 var network = networks[i];
1025 var option = $("<option>");
1026 option.attr("value", i);
1027 option.text(network.name);
7b742f87
IC
1028 if (network.name == "BTC - Bitcoin") {
1029 option.prop("selected", true);
1030 }
7f15cb6e
IC
1031 DOM.phraseNetwork.append(option);
1032 }
1033 }
29bf60f5 1034
b4fd763c
AG
1035 function populateClientSelect() {
1036 for (var i=0; i<clients.length; i++) {
1037 var client = clients[i];
1038 var option = $("<option>");
1039 option.attr("value", i);
1040 option.text(client.name);
29bf60f5 1041 DOM.bip32Client.append(option);
b4fd763c
AG
1042 }
1043 }
7f15cb6e 1044
5ee7bb9e
IC
1045 function getLanguage() {
1046 var defaultLanguage = "english";
1047 // Try to get from existing phrase
1048 var language = getLanguageFromPhrase();
1049 // Try to get from url if not from phrase
1050 if (language.length == 0) {
1051 language = getLanguageFromUrl();
1052 }
1053 // Default to English if no other option
1054 if (language.length == 0) {
1055 language = defaultLanguage;
1056 }
1057 return language;
1058 }
1059
1060 function getLanguageFromPhrase(phrase) {
1061 // Check if how many words from existing phrase match a language.
1062 var language = "";
1063 if (!phrase) {
1064 phrase = DOM.phrase.val();
1065 }
1066 if (phrase.length > 0) {
1067 var words = phraseToWordArray(phrase);
1068 var languageMatches = {};
1069 for (l in WORDLISTS) {
1070 // Track how many words match in this language
1071 languageMatches[l] = 0;
1072 for (var i=0; i<words.length; i++) {
1073 var wordInLanguage = WORDLISTS[l].indexOf(words[i]) > -1;
1074 if (wordInLanguage) {
1075 languageMatches[l]++;
1076 }
1077 }
1078 // Find languages with most word matches.
1079 // This is made difficult due to commonalities between Chinese
1080 // simplified vs traditional.
1081 var mostMatches = 0;
1082 var mostMatchedLanguages = [];
1083 for (var l in languageMatches) {
1084 var numMatches = languageMatches[l];
1085 if (numMatches > mostMatches) {
1086 mostMatches = numMatches;
1087 mostMatchedLanguages = [l];
1088 }
1089 else if (numMatches == mostMatches) {
1090 mostMatchedLanguages.push(l);
1091 }
1092 }
1093 }
1094 if (mostMatchedLanguages.length > 0) {
1095 // Use first language and warn if multiple detected
1096 language = mostMatchedLanguages[0];
1097 if (mostMatchedLanguages.length > 1) {
1098 console.warn("Multiple possible languages");
1099 console.warn(mostMatchedLanguages);
1100 }
1101 }
1102 }
1103 return language;
1104 }
1105
1106 function getLanguageFromUrl() {
c6624d51
IC
1107 for (var language in WORDLISTS) {
1108 if (window.location.hash.indexOf(language) > -1) {
1109 return language;
1110 }
1111 }
1112 return "";
5ee7bb9e
IC
1113 }
1114
1115 function setMnemonicLanguage() {
1116 var language = getLanguage();
1117 // Load the bip39 mnemonic generator for this language if required
1118 if (!(language in mnemonics)) {
1119 mnemonics[language] = new Mnemonic(language);
1120 }
1121 mnemonic = mnemonics[language];
1122 }
1123
1124 function convertPhraseToNewLanguage() {
1125 var oldLanguage = getLanguageFromPhrase();
1126 var newLanguage = getLanguageFromUrl();
1127 var oldPhrase = DOM.phrase.val();
1128 var oldWords = phraseToWordArray(oldPhrase);
1129 var newWords = [];
1130 for (var i=0; i<oldWords.length; i++) {
1131 var oldWord = oldWords[i];
1132 var index = WORDLISTS[oldLanguage].indexOf(oldWord);
1133 var newWord = WORDLISTS[newLanguage][index];
1134 newWords.push(newWord);
1135 }
1136 newPhrase = wordArrayToPhrase(newWords);
1137 return newPhrase;
1138 }
1139
1140 // TODO look at jsbip39 - mnemonic.splitWords
1141 function phraseToWordArray(phrase) {
1142 var words = phrase.split(/\s/g);
1143 var noBlanks = [];
1144 for (var i=0; i<words.length; i++) {
1145 var word = words[i];
1146 if (word.length > 0) {
1147 noBlanks.push(word);
1148 }
1149 }
1150 return noBlanks;
1151 }
1152
1153 // TODO look at jsbip39 - mnemonic.joinWords
1154 function wordArrayToPhrase(words) {
1155 var phrase = words.join(" ");
1156 var language = getLanguageFromPhrase(phrase);
139becaf 1157 if (language == "japanese" || language == "korean") {
5ee7bb9e
IC
1158 phrase = words.join("\u3000");
1159 }
1160 return phrase;
1161 }
1162
c6624d51
IC
1163 function isUsingOwnEntropy() {
1164 return DOM.useEntropy.prop("checked");
1165 }
1166
1167 function setMnemonicFromEntropy() {
0a84fe6a 1168 clearEntropyFeedback();
057722b0 1169 // Get entropy value
c6624d51 1170 var entropyStr = DOM.entropy.val();
057722b0 1171 // Work out minimum base for entropy
c6624d51 1172 var entropy = Entropy.fromString(entropyStr);
057722b0 1173 if (entropy.binaryStr.length == 0) {
c6624d51
IC
1174 return;
1175 }
1176 // Show entropy details
1cf1bbaf 1177 showEntropyFeedback(entropy);
3599674d
IC
1178 // Use entropy hash if not using raw entropy
1179 var bits = entropy.binaryStr;
1180 var mnemonicLength = DOM.entropyMnemonicLength.val();
1181 if (mnemonicLength != "raw") {
1182 // Get bits by hashing entropy with SHA256
1183 var hash = sjcl.hash.sha256.hash(entropy.cleanStr);
1184 var hex = sjcl.codec.hex.fromBits(hash);
1185 bits = BigInteger.parse(hex, 16).toString(2);
53aaab27 1186 while (bits.length % 256 != 0) {
3599674d
IC
1187 bits = "0" + bits;
1188 }
1189 // Truncate hash to suit number of words
1190 mnemonicLength = parseInt(mnemonicLength);
1191 var numberOfBits = 32 * mnemonicLength / 3;
1192 bits = bits.substring(0, numberOfBits);
1193 }
c6624d51 1194 // Discard trailing entropy
3599674d 1195 var bitsToUse = Math.floor(bits.length / 32) * 32;
d6fd8ebf
IC
1196 var start = bits.length - bitsToUse;
1197 var binaryStr = bits.substring(start);
c6624d51
IC
1198 // Convert entropy string to numeric array
1199 var entropyArr = [];
adc8ce12
IC
1200 for (var i=0; i<binaryStr.length / 8; i++) {
1201 var byteAsBits = binaryStr.substring(i*8, i*8+8);
1202 var entropyByte = parseInt(byteAsBits, 2);
c6624d51
IC
1203 entropyArr.push(entropyByte)
1204 }
1205 // Convert entropy array to mnemonic
1206 var phrase = mnemonic.toMnemonic(entropyArr);
1207 // Set the mnemonic in the UI
1208 DOM.phrase.val(phrase);
74ab4cbe
IC
1209 // Show the word indexes
1210 showWordIndexes();
09d63290
IC
1211 // Show the checksum
1212 showChecksum();
c6624d51
IC
1213 }
1214
0a84fe6a 1215 function clearEntropyFeedback() {
20f459ce 1216 DOM.entropyCrackTime.text("...");
1cf1bbaf 1217 DOM.entropyType.text("");
0a84fe6a
IC
1218 DOM.entropyWordCount.text("0");
1219 DOM.entropyEventCount.text("0");
1220 DOM.entropyBitsPerEvent.text("0");
1221 DOM.entropyBits.text("0");
1222 DOM.entropyFiltered.html("&nbsp;");
1223 DOM.entropyBinary.html("&nbsp;");
c6624d51
IC
1224 }
1225
1cf1bbaf 1226 function showEntropyFeedback(entropy) {
6422c1cd 1227 var numberOfBits = entropy.binaryStr.length;
20f459ce 1228 var timeToCrack = "unknown";
9bc39377
IC
1229 try {
1230 var z = zxcvbn(entropy.base.parts.join(""));
20f459ce
IC
1231 timeToCrack = z.crack_times_display.offline_fast_hashing_1e10_per_second;
1232 if (z.feedback.warning != "") {
1233 timeToCrack = timeToCrack + " - " + z.feedback.warning;
1234 };
9bc39377
IC
1235 }
1236 catch (e) {
9bc39377
IC
1237 console.log("Error detecting entropy strength with zxcvbn:");
1238 console.log(e);
b299a6a7 1239 }
391c7f26 1240 var entropyTypeStr = getEntropyTypeStr(entropy);
6422c1cd 1241 var wordCount = Math.floor(numberOfBits / 32) * 3;
94959756 1242 var bitsPerEvent = entropy.bitsPerEvent.toFixed(2);
f8ca25c3 1243 var spacedBinaryStr = addSpacesEveryElevenBits(entropy.binaryStr);
b54c1218 1244 DOM.entropyFiltered.html(entropy.cleanHtml);
391c7f26 1245 DOM.entropyType.text(entropyTypeStr);
20f459ce 1246 DOM.entropyCrackTime.text(timeToCrack);
1cf1bbaf 1247 DOM.entropyEventCount.text(entropy.base.ints.length);
6422c1cd 1248 DOM.entropyBits.text(numberOfBits);
0a84fe6a 1249 DOM.entropyWordCount.text(wordCount);
d6cade86 1250 DOM.entropyBinary.text(spacedBinaryStr);
6422c1cd 1251 DOM.entropyBitsPerEvent.text(bitsPerEvent);
ee0981f1
IC
1252 // detect and warn of filtering
1253 var rawNoSpaces = DOM.entropy.val().replace(/\s/g, "");
1254 var cleanNoSpaces = entropy.cleanStr.replace(/\s/g, "");
1255 var isFiltered = rawNoSpaces.length != cleanNoSpaces.length;
1256 if (isFiltered) {
1257 DOM.entropyFilterWarning.removeClass('hidden');
1258 }
1259 else {
1260 DOM.entropyFilterWarning.addClass('hidden');
1261 }
02f05d3e
IC
1262 }
1263
391c7f26
IC
1264 function getEntropyTypeStr(entropy) {
1265 var typeStr = entropy.base.str;
1266 // Add some detail if these are cards
1267 if (entropy.base.asInt == 52) {
1268 var cardDetail = []; // array of message strings
1269 // Detect duplicates
1270 var dupes = [];
1271 var dupeTracker = {};
1272 for (var i=0; i<entropy.base.parts.length; i++) {
1273 var card = entropy.base.parts[i];
5c653a12
IC
1274 var cardUpper = card.toUpperCase();
1275 if (cardUpper in dupeTracker) {
391c7f26
IC
1276 dupes.push(card);
1277 }
5c653a12 1278 dupeTracker[cardUpper] = true;
391c7f26
IC
1279 }
1280 if (dupes.length > 0) {
1281 var dupeWord = "duplicates";
1282 if (dupes.length == 1) {
1283 dupeWord = "duplicate";
1284 }
1285 var msg = dupes.length + " " + dupeWord + ": " + dupes.slice(0,3).join(" ");
1286 if (dupes.length > 3) {
1287 msg += "...";
1288 }
1289 cardDetail.push(msg);
1290 }
1291 // Detect full deck
1292 var uniqueCards = [];
1293 for (var uniqueCard in dupeTracker) {
1294 uniqueCards.push(uniqueCard);
1295 }
1296 if (uniqueCards.length == 52) {
1297 cardDetail.unshift("full deck");
1298 }
bbc29c80
IC
1299 // Detect missing cards
1300 var values = "A23456789TJQK";
1301 var suits = "CDHS";
1302 var missingCards = [];
1303 for (var i=0; i<suits.length; i++) {
1304 for (var j=0; j<values.length; j++) {
1305 var card = values[j] + suits[i];
1306 if (!(card in dupeTracker)) {
1307 missingCards.push(card);
1308 }
1309 }
1310 }
1311 // Display missing cards if six or less, ie clearly going for full deck
1312 if (missingCards.length > 0 && missingCards.length <= 6) {
1313 var msg = missingCards.length + " missing: " + missingCards.slice(0,3).join(" ");
1314 if (missingCards.length > 3) {
1315 msg += "...";
1316 }
1317 cardDetail.push(msg);
1318 }
391c7f26
IC
1319 // Add card details to typeStr
1320 if (cardDetail.length > 0) {
1321 typeStr += " (" + cardDetail.join(", ") + ")";
1322 }
1323 }
1324 return typeStr;
1325 }
1326
e00964cc
IC
1327 function setQrEvents(els) {
1328 els.on("mouseenter", createQr);
1329 els.on("mouseleave", destroyQr);
1330 els.on("click", toggleQr);
1331 }
1332
1333 function createQr(e) {
1334 var content = e.target.textContent || e.target.value;
1335 if (content) {
9225b805
IC
1336 var qrEl = kjua({
1337 text: content,
1338 render: "canvas",
1339 size: 310,
1340 ecLevel: 'H',
1341 });
1342 DOM.qrImage.append(qrEl);
e00964cc 1343 if (!showQr) {
97811c29
IC
1344 DOM.qrHider.addClass("hidden");
1345 }
1346 else {
1347 DOM.qrHider.removeClass("hidden");
e00964cc
IC
1348 }
1349 DOM.qrContainer.removeClass("hidden");
1350 }
1351 }
1352
1353 function destroyQr() {
1354 DOM.qrImage.text("");
1355 DOM.qrContainer.addClass("hidden");
1356 }
1357
1358 function toggleQr() {
1359 showQr = !showQr;
97811c29 1360 DOM.qrHider.toggleClass("hidden");
e00964cc
IC
1361 DOM.qrHint.toggleClass("hidden");
1362 }
1363
32fab2c3
IC
1364 function bip44TabSelected() {
1365 return DOM.bip44tab.hasClass("active");
1366 }
1367
1368 function bip32TabSelected() {
1369 return DOM.bip32tab.hasClass("active");
1370 }
1371
0fd67b59
IC
1372 function networkHasSegwit() {
1373 return networks[DOM.network.val()].segwitAvailable;
6c08f364
IC
1374 }
1375
1376 function bip49TabSelected() {
1377 return DOM.bip49tab.hasClass("active");
1378 }
1379
4e9b492c
IC
1380 function bip84TabSelected() {
1381 return DOM.bip84tab.hasClass("active");
1382 }
1383
c49e8812
IC
1384 function bip141TabSelected() {
1385 return DOM.bip141tab.hasClass("active");
1386 }
1387
6c08f364
IC
1388 function setHdCoin(coinValue) {
1389 DOM.bip44coin.val(coinValue);
1390 DOM.bip49coin.val(coinValue);
4e9b492c 1391 DOM.bip84coin.val(coinValue);
6c08f364
IC
1392 }
1393
0fd67b59 1394 function showSegwitAvailable() {
6c08f364
IC
1395 DOM.bip49unavailable.addClass("hidden");
1396 DOM.bip49available.removeClass("hidden");
c49e8812
IC
1397 DOM.bip141unavailable.addClass("hidden");
1398 DOM.bip141available.removeClass("hidden");
6c08f364
IC
1399 }
1400
0fd67b59 1401 function showSegwitUnavailable() {
6c08f364
IC
1402 DOM.bip49available.addClass("hidden");
1403 DOM.bip49unavailable.removeClass("hidden");
c49e8812
IC
1404 DOM.bip141available.addClass("hidden");
1405 DOM.bip141unavailable.removeClass("hidden");
6c08f364
IC
1406 }
1407
fe8f2d14
IC
1408 function useBitpayAddresses() {
1409 return !(DOM.useBitpayAddresses.prop("checked"));
1410 }
1411
1412 function setBitcoinCashNetworkValues() {
1413 if (useBitpayAddresses()) {
1414 network = bitcoinjs.bitcoin.networks.bitcoin;
1415 }
1416 else {
1417 network = bitcoinjs.bitcoin.networks.bitcoinCashBitbpay;
1418 }
1419 }
1420
0fd67b59
IC
1421 function adjustNetworkForSegwit() {
1422 // If segwit is selected the xpub/xprv prefixes need to be adjusted
93c3ef47
IC
1423 // to avoid accidentally importing BIP49 xpub to BIP44 watch only
1424 // wallet.
1425 // See https://github.com/iancoleman/bip39/issues/125
c49e8812
IC
1426 var segwitNetworks = null;
1427 // if a segwit network is alread selected, need to use base network to
1428 // look up new parameters
1429 if ("baseNetwork" in network) {
1430 network = bitcoinjs.bitcoin.networks[network.baseNetwork];
1431 }
1432 // choose the right segwit params
1433 if (p2wpkhSelected() && "p2wpkh" in network) {
1434 network = network.p2wpkh;
1435 }
1436 else if (p2wpkhInP2shSelected() && "p2wpkhInP2sh" in network) {
1437 network = network.p2wpkhInP2sh;
93c3ef47
IC
1438 }
1439 }
1440
9183f9f6
IC
1441 function lastIndexInTable() {
1442 var pathText = DOM.addresses.find(".index").last().text();
1443 var pathBits = pathText.split("/");
1444 var lastBit = pathBits[pathBits.length-1];
1445 var lastBitClean = lastBit.replace("'", "");
1446 return parseInt(lastBitClean);
1447 }
1448
74ab4cbe
IC
1449 function uint8ArrayToHex(a) {
1450 var s = ""
1451 for (var i=0; i<a.length; i++) {
1452 var h = a[i].toString(16);
1453 while (h.length < 2) {
1454 h = "0" + h;
1455 }
1456 s = s + h;
1457 }
1458 return s;
1459 }
1460
1461 function showWordIndexes() {
1462 var phrase = DOM.phrase.val();
1463 var words = phraseToWordArray(phrase);
1464 var wordIndexes = [];
1465 var language = getLanguage();
1466 for (var i=0; i<words.length; i++) {
1467 var word = words[i];
1468 var wordIndex = WORDLISTS[language].indexOf(word);
1469 wordIndexes.push(wordIndex);
1470 }
1471 var wordIndexesStr = wordIndexes.join(", ");
1472 DOM.entropyWordIndexes.text(wordIndexesStr);
1473 }
a78f4e28 1474
09d63290
IC
1475 function showChecksum() {
1476 var phrase = DOM.phrase.val();
1477 var words = phraseToWordArray(phrase);
1478 var checksumBitlength = words.length / 3;
1479 var checksum = "";
1480 var binaryStr = "";
1481 var language = getLanguage();
1482 for (var i=words.length-1; i>=0; i--) {
1483 var word = words[i];
1484 var wordIndex = WORDLISTS[language].indexOf(word);
1485 var wordBinary = wordIndex.toString(2);
1486 while (wordBinary.length < 11) {
1487 wordBinary = "0" + wordBinary;
1488 }
1489 var binaryStr = wordBinary + binaryStr;
1490 if (binaryStr.length >= checksumBitlength) {
1491 var start = binaryStr.length - checksumBitlength;
1492 var end = binaryStr.length;
1493 checksum = binaryStr.substring(start, end);
f8ca25c3
IC
1494 // add spaces so the last group is 11 bits, not the first
1495 checksum = checksum.split("").reverse().join("")
1496 checksum = addSpacesEveryElevenBits(checksum);
1497 checksum = checksum.split("").reverse().join("")
1498 break;
09d63290
IC
1499 }
1500 }
1501 DOM.entropyChecksum.text(checksum);
1502 }
1503
a78f4e28
IC
1504 function updateCsv() {
1505 var tableCsv = "path,address,public key,private key\n";
1506 var rows = DOM.addresses.find("tr");
1507 for (var i=0; i<rows.length; i++) {
1508 var row = $(rows[i]);
1509 var cells = row.find("td");
1510 for (var j=0; j<cells.length; j++) {
1511 var cell = $(cells[j]);
1512 if (!cell.children().hasClass("invisible")) {
1513 tableCsv = tableCsv + cell.text();
1514 }
1515 if (j != cells.length - 1) {
1516 tableCsv = tableCsv + ",";
1517 }
1518 }
1519 tableCsv = tableCsv + "\n";
1520 }
1521 DOM.csv.val(tableCsv);
1522 }
74ab4cbe 1523
f8ca25c3
IC
1524 function addSpacesEveryElevenBits(binaryStr) {
1525 return binaryStr.match(/.{1,11}/g).join(" ");
1526 }
1527
7f15cb6e 1528 var networks = [
85f762c9 1529 {
1530 name: "AC - Asiacoin",
1531 segwitAvailable: false,
1532 onSelect: function() {
1533 network = bitcoinjs.bitcoin.networks.asiacoin;
1534 setHdCoin(51);
1535 },
1536 },
1537 {
1538 name: "ACC - Adcoin",
1539 segwitAvailable: true,
1540 onSelect: function() {
1541 network = bitcoinjs.bitcoin.networks.adcoin;
1542 setHdCoin(161);
1543 },
1544 },
1545 {
1546 name: "AUR - Auroracoin",
1547 segwitAvailable: false,
1548 onSelect: function() {
1549 network = bitcoinjs.bitcoin.networks.auroracoin;
1550 setHdCoin(85);
1551 },
1552 },
cfc0c4d7 1553 {
1554 name: "AXE - Axe",
1555 segwitAvailable: false,
1556 onSelect: function() {
1557 network = bitcoinjs.bitcoin.networks.axe;
1558 setHdCoin(0);
1559 },
1560 },
85f762c9 1561 {
1562 name: "BCA - Bitcoin Atom",
1563 segwitAvailable: true,
1564 onSelect: function() {
1565 network = bitcoinjs.bitcoin.networks.atom;
1566 setHdCoin(185);
1567 },
1568 },
52d698e4 1569 {
2eab7c32 1570 name: "BCH - Bitcoin Cash",
52d698e4 1571 segwitAvailable: false,
1572 onSelect: function() {
2eab7c32
IC
1573 DOM.useBitpayAddressesContainer.removeClass("hidden");
1574 setBitcoinCashNetworkValues();
1575 setHdCoin(145);
52d698e4 1576 },
1577 },
daab55dc 1578 {
2eab7c32 1579 name: "BEET - Beetlecoin",
0fd67b59 1580 segwitAvailable: false,
daab55dc 1581 onSelect: function() {
2eab7c32
IC
1582 network = bitcoinjs.bitcoin.networks.beetlecoin;
1583 setHdCoin(800);
daab55dc
IC
1584 },
1585 },
85f762c9 1586 {
1587 name: "BELA - Belacoin",
1588 segwitAvailable: false,
1589 onSelect: function() {
1590 network = bitcoinjs.bitcoin.networks.belacoin;
1591 setHdCoin(73);
1592 },
1593 },
892ce76f
IC
1594 {
1595 name: "BLK - BlackCoin",
1596 segwitAvailable: false,
1597 onSelect: function() {
1598 network = bitcoinjs.bitcoin.networks.blackcoin;
1599 setHdCoin(10);
1600 },
1601 },
85f762c9 1602 {
1603 name: "BRIT - Britcoin",
1604 segwitAvailable: false,
1605 onSelect: function() {
1606 network = bitcoinjs.bitcoin.networks.britcoin;
1607 setHdCoin(70);
1608 },
1609 },
1610 {
1611 name: "BSD - Bitsend",
1612 segwitAvailable: true,
1613 onSelect: function() {
1614 network = bitcoinjs.bitcoin.networks.bitsend;
1615 setHdCoin(91);
1616 },
1617 },
1618 {
1619 name: "BTA - Bata",
1620 segwitAvailable: false,
1621 onSelect: function() {
1622 network = bitcoinjs.bitcoin.networks.bata;
1623 setHdCoin(89);
1624 },
1625 },
7f15cb6e 1626 {
534481b6 1627 name: "BTC - Bitcoin",
0fd67b59 1628 segwitAvailable: true,
7a995731 1629 onSelect: function() {
a0091a40 1630 network = bitcoinjs.bitcoin.networks.bitcoin;
6c08f364 1631 setHdCoin(0);
7a995731
IC
1632 },
1633 },
7f15cb6e 1634 {
534481b6 1635 name: "BTC - Bitcoin Testnet",
0fd67b59 1636 segwitAvailable: true,
7a995731 1637 onSelect: function() {
a0091a40 1638 network = bitcoinjs.bitcoin.networks.testnet;
6c08f364 1639 setHdCoin(1);
7a995731
IC
1640 },
1641 },
39608073
RS
1642 {
1643 name: "BTG - Bitcoin Gold",
0fd67b59 1644 segwitAvailable: true,
39608073
RS
1645 onSelect: function() {
1646 network = bitcoinjs.bitcoin.networks.bgold;
88ae1301 1647 setHdCoin(156);
39608073
RS
1648 },
1649 },
85f762c9 1650 {
1651 name: "BTX - Bitcore",
1652 segwitAvailable: true,
1653 onSelect: function() {
1654 network = bitcoinjs.bitcoin.networks.bitcore;
1655 setHdCoin(160);
1656 },
1657 },
1658 {
1659 name: "CCN - Cannacoin",
1660 segwitAvailable: false,
1661 onSelect: function() {
1662 network = bitcoinjs.bitcoin.networks.cannacoin;
1663 setHdCoin(19);
1664 },
1665 },
1666 {
1667 name: "CDN - Canadaecoin",
1668 segwitAvailable: false,
1669 onSelect: function() {
1670 network = bitcoinjs.bitcoin.networks.canadaecoin;
1671 setHdCoin(34);
1672 },
1673 },
7f15cb6e 1674 {
534481b6 1675 name: "CLAM - Clams",
0fd67b59 1676 segwitAvailable: false,
7a995731 1677 onSelect: function() {
a0091a40 1678 network = bitcoinjs.bitcoin.networks.clam;
6c08f364 1679 setHdCoin(23);
7a995731
IC
1680 },
1681 },
85f762c9 1682 {
1683 name: "CLUB - Clubcoin",
1684 segwitAvailable: false,
1685 onSelect: function() {
1686 network = bitcoinjs.bitcoin.networks.clubcoin;
1687 setHdCoin(79);
1688 },
1689 },
1690 {
1691 name: "CMP - Compcoin",
1692 segwitAvailable: false,
1693 onSelect: function() {
1694 network = bitcoinjs.bitcoin.networks.compcoin;
1695 setHdCoin(71);
1696 },
1697 },
1698 {
1699 name: "CRAVE - Crave",
1700 segwitAvailable: false,
1701 onSelect: function() {
1702 network = bitcoinjs.bitcoin.networks.crave;
1703 setHdCoin(186);
1704 },
1705 },
0921f370 1706 {
1707 name: "CRW - Crown",
0fd67b59 1708 segwitAvailable: false,
0921f370 1709 onSelect: function() {
6c08f364
IC
1710 network = bitcoinjs.bitcoin.networks.crown;
1711 setHdCoin(72);
0921f370 1712 },
1713 },
7f15cb6e 1714 {
534481b6 1715 name: "DASH - Dash",
0fd67b59 1716 segwitAvailable: false,
e3a9508c 1717 onSelect: function() {
a0091a40 1718 network = bitcoinjs.bitcoin.networks.dash;
6c08f364 1719 setHdCoin(5);
e3a9508c
IC
1720 },
1721 },
c0386f3b 1722 {
534481b6 1723 name: "DASH - Dash Testnet",
0fd67b59 1724 segwitAvailable: false,
c0386f3b 1725 onSelect: function() {
a0091a40 1726 network = bitcoinjs.bitcoin.networks.dashtn;
6c08f364 1727 setHdCoin(1);
c0386f3b
KR
1728 },
1729 },
85f762c9 1730 {
1731 name: "DFC - Defcoin",
1732 segwitAvailable: false,
1733 onSelect: function() {
1734 network = bitcoinjs.bitcoin.networks.defcoin;
1735 setHdCoin(1337);
1736 },
1737 },
1738 {
1739 name: "DGB - Digibyte",
1740 segwitAvailable: true,
1741 onSelect: function() {
1742 network = bitcoinjs.bitcoin.networks.digibyte;
1743 setHdCoin(20);
1744 },
1745 },
1746 {
1747 name: "DGC - Digitalcoin",
1748 segwitAvailable: false,
1749 onSelect: function() {
1750 network = bitcoinjs.bitcoin.networks.digitalcoin;
1751 setHdCoin(18);
1752 },
1753 },
1754 {
1755 name: "DMD - Diamond",
1756 segwitAvailable: false,
1757 onSelect: function() {
1758 network = bitcoinjs.bitcoin.networks.diamond;
1759 setHdCoin(152);
1760 },
1761 },
40209fd8 1762 {
1763 name: "DNR - Denarius",
1764 segwitAvailable: false,
1765 onSelect: function() {
1766 network = bitcoinjs.bitcoin.networks.denarius;
50289881 1767 setHdCoin(116);
40209fd8 1768 },
1769 },
e3a9508c 1770 {
534481b6 1771 name: "DOGE - Dogecoin",
0fd67b59 1772 segwitAvailable: false,
534481b6 1773 onSelect: function() {
6c08f364
IC
1774 network = bitcoinjs.bitcoin.networks.dogecoin;
1775 setHdCoin(3);
534481b6 1776 },
1777 },
85f762c9 1778 {
1779 name: "ECN - Ecoin",
1780 segwitAvailable: false,
1781 onSelect: function() {
1782 network = bitcoinjs.bitcoin.networks.ecoin;
1783 setHdCoin(115);
1784 },
1785 },
1786 {
1787 name: "EDRC - Edrcoin",
1788 segwitAvailable: false,
1789 onSelect: function() {
1790 network = bitcoinjs.bitcoin.networks.edrcoin;
1791 setHdCoin(56);
1792 },
1793 },
1794 {
1795 name: "EFL - Egulden",
1796 segwitAvailable: true,
1797 onSelect: function() {
1798 network = bitcoinjs.bitcoin.networks.egulden;
1799 setHdCoin(78);
1800 },
1801 },
1802 {
1803 name: "EMC2 - Einsteinium",
1804 segwitAvailable: false,
1805 onSelect: function() {
1806 network = bitcoinjs.bitcoin.networks.einsteinium;
1807 setHdCoin(41);
1808 },
1809 },
1810 {
1811 name: "ERC - Europecoin",
1812 segwitAvailable: false,
1813 onSelect: function() {
1814 network = bitcoinjs.bitcoin.networks.europecoin;
1815 setHdCoin(151);
1816 },
1817 },
534481b6 1818 {
1819 name: "ETH - Ethereum",
0fd67b59 1820 segwitAvailable: false,
e3a9508c 1821 onSelect: function() {
a0091a40 1822 network = bitcoinjs.bitcoin.networks.bitcoin;
6c08f364 1823 setHdCoin(60);
e3a9508c
IC
1824 },
1825 },
85f762c9 1826 {
1827 name: "EXCL - Exclusivecoin",
1828 segwitAvailable: false,
1829 onSelect: function() {
1830 network = bitcoinjs.bitcoin.networks.exclusivecoin;
1831 setHdCoin(190);
1832 },
1833 },
6e679905 1834 {
1835 name: "FJC - Fujicoin",
30218707 1836 segwitAvailable: true,
6e679905 1837 onSelect: function() {
1838 network = bitcoinjs.bitcoin.networks.fujicoin;
1839 setHdCoin(75);
1840 },
1841 },
85f762c9 1842 {
1843 name: "FLASH - Flashcoin",
1844 segwitAvailable: false,
1845 onSelect: function() {
1846 network = bitcoinjs.bitcoin.networks.flashcoin;
1847 setHdCoin(120);
1848 },
1849 },
1850 {
1851 name: "FRST - Firstcoin",
1852 segwitAvailable: false,
1853 onSelect: function() {
1854 network = bitcoinjs.bitcoin.networks.firstcoin;
1855 setHdCoin(167);
1856 },
1857 },
1858 {
1859 name: "FTC - Feathercoin",
1860 segwitAvailable: true,
1861 onSelect: function() {
1862 network = bitcoinjs.bitcoin.networks.feathercoin;
1863 setHdCoin(8);
1864 },
1865 },
f88fab20 1866 {
534481b6 1867 name: "GAME - GameCredits",
0fd67b59 1868 segwitAvailable: false,
f88fab20 1869 onSelect: function() {
a0091a40 1870 network = bitcoinjs.bitcoin.networks.game;
6c08f364 1871 setHdCoin(101);
f88fab20 1872 },
1873 },
85f762c9 1874 {
1875 name: "GBX - Gobyte",
1876 segwitAvailable: false,
1877 onSelect: function() {
1878 network = bitcoinjs.bitcoin.networks.gobyte;
1879 setHdCoin(176);
1880 },
1881 },
1882 {
1883 name: "GCR - GCRCoin",
1884 segwitAvailable: false,
1885 onSelect: function() {
1886 network = bitcoinjs.bitcoin.networks.gcr;
1887 setHdCoin(79);
1888 },
1889 },
1890 {
1891 name: "GRC - Gridcoin",
1892 segwitAvailable: false,
1893 onSelect: function() {
1894 network = bitcoinjs.bitcoin.networks.gridcoin;
1895 setHdCoin(84);
1896 },
1897 },
1898 {
1899 name: "HNC - Helleniccoin",
1900 segwitAvailable: false,
1901 onSelect: function() {
1902 network = bitcoinjs.bitcoin.networks.helleniccoin;
1903 setHdCoin(168);
1904 },
1905 },
1906 {
1907 name: "INSN - Insane",
1908 segwitAvailable: false,
1909 onSelect: function() {
1910 network = bitcoinjs.bitcoin.networks.insane;
1911 setHdCoin(68);
1912 },
1913 },
1914 {
1915 name: "IOP - Iop",
1916 segwitAvailable: true,
1917 onSelect: function() {
1918 network = bitcoinjs.bitcoin.networks.iop;
1919 setHdCoin(66);
1920 },
1921 },
1922 {
1923 name: "IXC - Ixcoin",
1924 segwitAvailable: false,
1925 onSelect: function() {
1926 network = bitcoinjs.bitcoin.networks.ixcoin;
1927 setHdCoin(86);
1928 },
1929 },
a3baa26e 1930 {
534481b6 1931 name: "JBS - Jumbucks",
0fd67b59 1932 segwitAvailable: false,
a3baa26e 1933 onSelect: function() {
a0091a40 1934 network = bitcoinjs.bitcoin.networks.jumbucks;
6c08f364 1935 setHdCoin(26);
a3baa26e
IC
1936 },
1937 },
aab3645f 1938 {
1939 name: "KMD - Komodo",
1940 bip49available: false,
1941 onSelect: function() {
1942 network = bitcoinjs.bitcoin.networks.komodo;
1943 setHdCoin(141);
1944 },
1945 },
85f762c9 1946 {
1947 name: "LBC - Library Credits",
1948 segwitAvailable: false,
1949 onSelect: function() {
1950 network = bitcoinjs.bitcoin.networks.lbry;
1951 setHdCoin(140);
1952 },
1953 },
1954 {
1955 name: "LCC - Litecoincash",
1956 segwitAvailable: true,
1957 onSelect: function() {
1958 network = bitcoinjs.bitcoin.networks.litecoincash;
1959 setHdCoin(192);
1960 },
1961 },
1962 {
1963 name: "LDCN - Landcoin",
1964 segwitAvailable: false,
1965 onSelect: function() {
1966 network = bitcoinjs.bitcoin.networks.landcoin;
1967 setHdCoin(63);
1968 },
1969 },
1970 {
1971 name: "LINX - Linx",
1972 segwitAvailable: false,
1973 onSelect: function() {
1974 network = bitcoinjs.bitcoin.networks.linx;
1975 setHdCoin(114);
1976 },
1977 },
a3baa26e 1978 {
534481b6 1979 name: "LTC - Litecoin",
0fd67b59 1980 segwitAvailable: true,
a3baa26e 1981 onSelect: function() {
a0091a40 1982 network = bitcoinjs.bitcoin.networks.litecoin;
6c08f364 1983 setHdCoin(2);
3abab9b0 1984 DOM.litecoinLtubContainer.removeClass("hidden");
a3baa26e
IC
1985 },
1986 },
85f762c9 1987 {
1988 name: "LYNX - Lynx",
1989 segwitAvailable: false,
1990 onSelect: function() {
1991 network = bitcoinjs.bitcoin.networks.lynx;
1992 setHdCoin(191);
1993 },
1994 },
56ad9601
JS
1995 {
1996 name: "MAZA - Maza",
0fd67b59 1997 segwitAvailable: false,
56ad9601
JS
1998 onSelect: function() {
1999 network = bitcoinjs.bitcoin.networks.maza;
2000 setHdCoin(13);
2001 },
2002 },
85f762c9 2003 {
2004 name: "MNX - Minexcoin",
2005 segwitAvailable: true,
2006 onSelect: function() {
2007 network = bitcoinjs.bitcoin.networks.minexcoin;
2008 setHdCoin(182);
2009 },
2010 },
f487fea9
IC
2011 {
2012 name: "MONA - Monacoin",
0fd67b59 2013 segwitAvailable: true,
f487fea9
IC
2014 onSelect: function() {
2015 network = bitcoinjs.bitcoin.networks.monacoin,
2016 setHdCoin(22);
2017 },
2018 },
85f762c9 2019 {
2020 name: "NAV - Navcoin",
2021 segwitAvailable: true,
2022 onSelect: function() {
2023 network = bitcoinjs.bitcoin.networks.navcoin;
2024 setHdCoin(130);
2025 },
2026 },
db37d639
IC
2027 {
2028 name: "NEBL - Neblio",
2029 segwitAvailable: false,
2030 onSelect: function() {
2031 network = bitcoinjs.bitcoin.networks.neblio;
2032 setHdCoin(146);
2033 },
2034 },
85f762c9 2035 {
2036 name: "NEOS - Neoscoin",
2037 segwitAvailable: false,
2038 onSelect: function() {
2039 network = bitcoinjs.bitcoin.networks.neoscoin;
2040 setHdCoin(25);
2041 },
2042 },
2043 {
2044 name: "NLG - Gulden",
2045 segwitAvailable: false,
2046 onSelect: function() {
2047 network = bitcoinjs.bitcoin.networks.gulden;
2048 setHdCoin(87);
2049 },
2050 },
a3baa26e 2051 {
534481b6 2052 name: "NMC - Namecoin",
0fd67b59 2053 segwitAvailable: false,
a3baa26e 2054 onSelect: function() {
a0091a40 2055 network = bitcoinjs.bitcoin.networks.namecoin;
6c08f364 2056 setHdCoin(7);
a3baa26e
IC
2057 },
2058 },
85f762c9 2059 {
2060 name: "NRO - Neurocoin",
2061 segwitAvailable: false,
2062 onSelect: function() {
2063 network = bitcoinjs.bitcoin.networks.neurocoin;
2064 setHdCoin(110);
2065 },
2066 },
2067 {
2068 name: "NSR - Nushares",
2069 segwitAvailable: false,
2070 onSelect: function() {
2071 network = bitcoinjs.bitcoin.networks.nushares;
2072 setHdCoin(11);
2073 },
2074 },
2075 {
2076 name: "NYC - Newyorkc",
2077 segwitAvailable: false,
2078 onSelect: function() {
2079 network = bitcoinjs.bitcoin.networks.newyorkc;
2080 setHdCoin(179);
2081 },
2082 },
2083 {
2084 name: "NVC - Novacoin",
2085 segwitAvailable: false,
2086 onSelect: function() {
2087 network = bitcoinjs.bitcoin.networks.novacoin;
2088 setHdCoin(50);
2089 },
2090 },
2091 {
2092 name: "OK - Okcash",
2093 segwitAvailable: false,
2094 onSelect: function() {
2095 network = bitcoinjs.bitcoin.networks.okcash;
2096 setHdCoin(69);
2097 },
2098 },
2099 {
2100 name: "OMNI - Omnicore",
2101 segwitAvailable: true,
2102 onSelect: function() {
2103 network = bitcoinjs.bitcoin.networks.omnicore;
2104 setHdCoin(200);
2105 },
2106 },
66419cf3 2107 {
2108 name: "ONX - Onixcoin",
2109 segwitAvailable: false,
2110 onSelect: function() {
2111 network = bitcoinjs.bitcoin.networks.onixcoin;
d00c7199 2112 setHdCoin(174);
66419cf3 2113 },
2114 },
85f762c9 2115 {
2116 name: "PINK - Pinkcoin",
2117 segwitAvailable: false,
2118 onSelect: function() {
2119 network = bitcoinjs.bitcoin.networks.pinkcoin;
2120 setHdCoin(117);
2121 },
2122 },
c0df0189 2123 {
2124 name: "PIVX - PIVX",
0fd67b59 2125 segwitAvailable: false,
c0df0189 2126 onSelect: function() {
2127 network = bitcoinjs.bitcoin.networks.pivx;
2128 setHdCoin(119);
2129 },
2130 },
2131 {
2132 name: "PIVX - PIVX Testnet",
0fd67b59 2133 segwitAvailable: false,
c0df0189 2134 onSelect: function() {
2135 network = bitcoinjs.bitcoin.networks.pivxtestnet;
2136 setHdCoin(1);
2137 },
2138 },
85f762c9 2139 {
2140 name: "POSW - POSWcoin",
2141 segwitAvailable: false,
2142 onSelect: function() {
2143 network = bitcoinjs.bitcoin.networks.poswcoin;
2144 setHdCoin(47);
2145 },
2146 },
2147 {
2148 name: "POT - Potcoin",
2149 segwitAvailable: false,
2150 onSelect: function() {
2151 network = bitcoinjs.bitcoin.networks.potcoin;
2152 setHdCoin(81);
2153 },
2154 },
5c434a8a 2155 {
534481b6 2156 name: "PPC - Peercoin",
0fd67b59 2157 segwitAvailable: false,
5c434a8a 2158 onSelect: function() {
a0091a40 2159 network = bitcoinjs.bitcoin.networks.peercoin;
6c08f364 2160 setHdCoin(6);
5c434a8a
CM
2161 },
2162 },
85f762c9 2163 {
2164 name: "PSB - Pesobit",
2165 segwitAvailable: false,
2166 onSelect: function() {
2167 network = bitcoinjs.bitcoin.networks.pesobit;
2168 setHdCoin(62);
2169 },
2170 },
2171 {
2172 name: "PUT - Putincoin",
2173 segwitAvailable: false,
2174 onSelect: function() {
2175 network = bitcoinjs.bitcoin.networks.putincoin;
2176 setHdCoin(122);
2177 },
2178 },
2179 {
2180 name: "RBY - Rubycoin",
2181 segwitAvailable: false,
2182 onSelect: function() {
2183 network = bitcoinjs.bitcoin.networks.rubycoin;
2184 setHdCoin(16);
2185 },
2186 },
2187 {
2188 name: "RDD - Reddcoin",
2189 segwitAvailable: false,
2190 onSelect: function() {
2191 network = bitcoinjs.bitcoin.networks.reddoin;
2192 setHdCoin(4);
2193 },
2194 },
2195 {
2196 name: "RVR - RevolutionVR",
2197 segwitAvailable: false,
2198 onSelect: function() {
2199 network = bitcoinjs.bitcoin.networks.revolutionvr;
2200 setHdCoin(129);
2201 },
2202 },
64a7d2aa 2203 {
534481b6 2204 name: "SDC - ShadowCash",
0fd67b59 2205 segwitAvailable: false,
82f91834 2206 onSelect: function() {
a0091a40 2207 network = bitcoinjs.bitcoin.networks.shadow;
6c08f364 2208 setHdCoin(35);
82f91834
DG
2209 },
2210 },
07ac4350 2211 {
534481b6 2212 name: "SDC - ShadowCash Testnet",
0fd67b59 2213 segwitAvailable: false,
07ac4350 2214 onSelect: function() {
a0091a40 2215 network = bitcoinjs.bitcoin.networks.shadowtn;
6c08f364 2216 setHdCoin(1);
07ac4350 2217 },
2218 },
7a5a87a0 2219 {
534481b6 2220 name: "SLM - Slimcoin",
0fd67b59 2221 segwitAvailable: false,
7a5a87a0 2222 onSelect: function() {
a0091a40 2223 network = bitcoinjs.bitcoin.networks.slimcoin;
6c08f364 2224 setHdCoin(63);
7a5a87a0
GH
2225 },
2226 },
2227 {
534481b6 2228 name: "SLM - Slimcoin Testnet",
0fd67b59 2229 segwitAvailable: false,
7a5a87a0 2230 onSelect: function() {
a0091a40 2231 network = bitcoinjs.bitcoin.networks.slimcointn;
6c08f364 2232 setHdCoin(111);
7a5a87a0
GH
2233 },
2234 },
85f762c9 2235 {
2236 name: "SLR - Solarcoin",
2237 segwitAvailable: false,
2238 onSelect: function() {
2239 network = bitcoinjs.bitcoin.networks.solarcoin;
2240 setHdCoin(58);
2241 },
2242 },
2243 {
2244 name: "SMLY - Smileycoin",
2245 segwitAvailable: false,
2246 onSelect: function() {
2247 network = bitcoinjs.bitcoin.networks.smileycoin;
2248 setHdCoin(59);
2249 },
2250 },
2251 {
2252 name: "STRAT - Stratis",
2253 segwitAvailable: false,
2254 onSelect: function() {
2255 network = bitcoinjs.bitcoin.networks.stratis;
2256 setHdCoin(105);
2257 },
2258 },
2259 {
2260 name: "SYS - Syscoin",
2261 segwitAvailable: true,
2262 onSelect: function() {
2263 network = bitcoinjs.bitcoin.networks.syscoin;
2264 setHdCoin(57);
2265 },
2266 },
2267 {
2268 name: "THC - Hempcoin",
2269 segwitAvailable: false,
2270 onSelect: function() {
2271 network = bitcoinjs.bitcoin.networks.hempcoin;
2272 setHdCoin(113);
2273 },
2274 },
2275 {
2276 name: "TOA - Toa",
2277 segwitAvailable: false,
2278 onSelect: function() {
2279 network = bitcoinjs.bitcoin.networks.toa;
2280 setHdCoin(159);
2281 },
2282 },
2283 {
2284 name: "USC - Ultimatesecurecash",
2285 segwitAvailable: false,
2286 onSelect: function() {
2287 network = bitcoinjs.bitcoin.networks.ultimatesecurecash;
2288 setHdCoin(112);
2289 },
2290 },
8dd28f2c 2291 {
2292 name: "USNBT - NuBits",
0fd67b59 2293 segwitAvailable: false,
8dd28f2c 2294 onSelect: function() {
2295 network = bitcoinjs.bitcoin.networks.nubits;
2296 setHdCoin(12);
2297 },
2298 },
85f762c9 2299 {
2300 name: "UNO - Unobtanium",
2301 segwitAvailable: false,
2302 onSelect: function() {
2303 network = bitcoinjs.bitcoin.networks.unobtanium;
2304 setHdCoin(92);
2305 },
2306 },
2307 {
2308 name: "VASH - Vpncoin",
2309 segwitAvailable: false,
2310 onSelect: function() {
2311 network = bitcoinjs.bitcoin.networks.vpncoin;
2312 setHdCoin(33);
2313 },
2314 },
07ac4350 2315 {
534481b6 2316 name: "VIA - Viacoin",
0fd67b59 2317 segwitAvailable: false,
07ac4350 2318 onSelect: function() {
a0091a40 2319 network = bitcoinjs.bitcoin.networks.viacoin;
6c08f364 2320 setHdCoin(14);
07ac4350 2321 },
2322 },
0edac945 2323 {
534481b6 2324 name: "VIA - Viacoin Testnet",
0fd67b59 2325 segwitAvailable: false,
0edac945 2326 onSelect: function() {
a0091a40 2327 network = bitcoinjs.bitcoin.networks.viacointestnet;
6c08f364 2328 setHdCoin(1);
0edac945
IC
2329 },
2330 },
85f762c9 2331 {
2332 name: "VIVO - Vivo",
2333 segwitAvailable: false,
2334 onSelect: function() {
2335 network = bitcoinjs.bitcoin.networks.vivo;
2336 setHdCoin(166);
2337 },
2338 },
2339 {
2340 name: "VTC - Vertcoin",
2341 segwitAvailable: false,
2342 onSelect: function() {
2343 network = bitcoinjs.bitcoin.networks.vertcoin;
2344 setHdCoin(28);
2345 },
2346 },
2347 {
2348 name: "WC - Wincoin",
2349 segwitAvailable: false,
2350 onSelect: function() {
2351 network = bitcoinjs.bitcoin.networks.wincoin;
2352 setHdCoin(181);
2353 },
2354 },
2355 {
2356 name: "XBC - Bitcoinplus",
2357 segwitAvailable: false,
2358 onSelect: function() {
2359 network = bitcoinjs.bitcoin.networks.bitcoinplus;
2360 setHdCoin(65);
2361 },
2362 },
534481b6 2363 {
7ebdf61c 2364 name: "XMY - Myriadcoin",
0fd67b59 2365 segwitAvailable: false,
534481b6 2366 onSelect: function() {
7ebdf61c
IC
2367 network = bitcoinjs.bitcoin.networks.myriadcoin;
2368 setHdCoin(90);
534481b6 2369 },
adedbf91 2370 },
2371 {
7ebdf61c 2372 name: "XRP - Ripple",
0fd67b59 2373 segwitAvailable: false,
adedbf91 2374 onSelect: function() {
7ebdf61c
IC
2375 network = bitcoinjs.bitcoin.networks.bitcoin;
2376 setHdCoin(144);
adedbf91 2377 },
e1d0cf4f 2378 },
85f762c9 2379 {
2380 name: "XVC - Vcash",
2381 segwitAvailable: false,
2382 onSelect: function() {
2383 network = bitcoinjs.bitcoin.networks.vcash;
2384 setHdCoin(127);
2385 },
2386 },
2387 {
2388 name: "XVG - Verge",
2389 segwitAvailable: false,
2390 onSelect: function() {
2391 network = bitcoinjs.bitcoin.networks.verge;
2392 setHdCoin(77);
2393 },
2394 },
2395 {
2396 name: "XWC - Whitecoin",
2397 segwitAvailable: false,
2398 onSelect: function() {
2399 network = bitcoinjs.bitcoin.networks.whitecoin;
2400 setHdCoin(155);
2401 },
2402 },
2403 {
2404 name: "XZC - Zcoin",
2405 segwitAvailable: true,
2406 onSelect: function() {
2407 network = bitcoinjs.bitcoin.networks.zcoin;
2408 setHdCoin(136);
2409 },
2410 },
7f15cb6e 2411 ]
6ee4fb7d 2412
b4fd763c
AG
2413 var clients = [
2414 {
2415 name: "Bitcoin Core",
2416 onSelect: function() {
2417 DOM.bip32path.val("m/0'/0'");
2418 DOM.hardenedAddresses.prop('checked', true);
2419 },
2420 },
2421 {
2422 name: "blockchain.info",
2423 onSelect: function() {
2424 DOM.bip32path.val("m/44'/0'/0'");
2425 DOM.hardenedAddresses.prop('checked', false);
2426 },
2427 },
2428 {
2429 name: "MultiBit HD",
2430 onSelect: function() {
2431 DOM.bip32path.val("m/0'/0");
2432 DOM.hardenedAddresses.prop('checked', false);
2433 },
2434 }
2435 ]
7a995731 2436
ebd8d4e8
IC
2437 init();
2438
2439})();