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