]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - src/js/index.js
Allow BitPay address format for bitcoin cash
[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;
3abab9b0 17 var litecoinUseLtub = false;
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");
40 DOM.entropyMnemonicLength = DOM.entropyContainer.find(".mnemonic-length");
ee0981f1 41 DOM.entropyFilterWarning = DOM.entropyContainer.find(".filter-warning");
ebd8d4e8 42 DOM.phrase = $(".phrase");
1abcc511 43 DOM.passphrase = $(".passphrase");
c6624d51 44 DOM.generateContainer = $(".generate-container");
ebd8d4e8 45 DOM.generate = $(".generate");
3e0ed16a 46 DOM.seed = $(".seed");
ebd8d4e8 47 DOM.rootKey = $(".root-key");
3abab9b0
IC
48 DOM.litecoinLtubContainer = $(".litecoin-ltub-container");
49 DOM.litecoinUseLtub = $(".litecoin-use-ltub");
ebd8d4e8
IC
50 DOM.extendedPrivKey = $(".extended-priv-key");
51 DOM.extendedPubKey = $(".extended-pub-key");
d6cedc94
IC
52 DOM.bip32tab = $("#bip32-tab");
53 DOM.bip44tab = $("#bip44-tab");
6c08f364 54 DOM.bip49tab = $("#bip49-tab");
d6cedc94
IC
55 DOM.bip32panel = $("#bip32");
56 DOM.bip44panel = $("#bip44");
6c08f364 57 DOM.bip49panel = $("#bip49");
ebd8d4e8
IC
58 DOM.bip32path = $("#bip32-path");
59 DOM.bip44path = $("#bip44-path");
60 DOM.bip44purpose = $("#bip44 .purpose");
61 DOM.bip44coin = $("#bip44 .coin");
62 DOM.bip44account = $("#bip44 .account");
c554e6ff
IC
63 DOM.bip44accountXprv = $("#bip44 .account-xprv");
64 DOM.bip44accountXpub = $("#bip44 .account-xpub");
ebd8d4e8 65 DOM.bip44change = $("#bip44 .change");
6c08f364
IC
66 DOM.bip49unavailable = $("#bip49 .unavailable");
67 DOM.bip49available = $("#bip49 .available");
68 DOM.bip49path = $("#bip49-path");
69 DOM.bip49purpose = $("#bip49 .purpose");
70 DOM.bip49coin = $("#bip49 .coin");
71 DOM.bip49account = $("#bip49 .account");
72 DOM.bip49accountXprv = $("#bip49 .account-xprv");
73 DOM.bip49accountXpub = $("#bip49 .account-xpub");
74 DOM.bip49change = $("#bip49 .change");
88df3739 75 DOM.generatedStrength = $(".generate-container .strength");
146e089e 76 DOM.hardenedAddresses = $(".hardened-addresses");
88311463 77 DOM.useP2wpkhNestedInP2sh = $(".p2wpkh-nested-in-p2sh");
fe8f2d14
IC
78 DOM.useBitpayAddressesContainer = $(".use-bitpay-addresses-container");
79 DOM.useBitpayAddresses = $(".use-bitpay-addresses");
ebd8d4e8
IC
80 DOM.addresses = $(".addresses");
81 DOM.rowsToAdd = $(".rows-to-add");
82 DOM.more = $(".more");
83 DOM.feedback = $(".feedback");
84 DOM.tab = $(".derivation-type a");
85 DOM.indexToggle = $(".index-toggle");
86 DOM.addressToggle = $(".address-toggle");
1b12b2f5 87 DOM.publicKeyToggle = $(".public-key-toggle");
ebd8d4e8 88 DOM.privateKeyToggle = $(".private-key-toggle");
5ee7bb9e 89 DOM.languages = $(".languages a");
e00964cc 90 DOM.qrContainer = $(".qr-container");
97811c29 91 DOM.qrHider = DOM.qrContainer.find(".qr-hider");
e00964cc
IC
92 DOM.qrImage = DOM.qrContainer.find(".qr-image");
93 DOM.qrHint = DOM.qrContainer.find(".qr-hint");
94 DOM.showQrEls = $("[data-show-qr]");
ebd8d4e8 95
ebd8d4e8
IC
96 function init() {
97 // Events
d6cedc94 98 DOM.network.on("change", networkChanged);
29bf60f5 99 DOM.bip32Client.on("change", bip32ClientChanged);
c6624d51
IC
100 DOM.useEntropy.on("change", setEntropyVisibility);
101 DOM.entropy.on("input", delayedEntropyChanged);
3599674d 102 DOM.entropyMnemonicLength.on("change", entropyChanged);
a19a5498
IC
103 DOM.phrase.on("input", delayedPhraseChanged);
104 DOM.passphrase.on("input", delayedPhraseChanged);
ebd8d4e8
IC
105 DOM.generate.on("click", generateClicked);
106 DOM.more.on("click", showMore);
efe41586 107 DOM.rootKey.on("input", delayedRootKeyChanged);
3abab9b0 108 DOM.litecoinUseLtub.on("change", litecoinUseLtubChanged);
efe41586 109 DOM.bip32path.on("input", calcForDerivationPath);
efe41586
IC
110 DOM.bip44account.on("input", calcForDerivationPath);
111 DOM.bip44change.on("input", calcForDerivationPath);
6c08f364
IC
112 DOM.bip49account.on("input", calcForDerivationPath);
113 DOM.bip49change.on("input", calcForDerivationPath);
efe41586 114 DOM.tab.on("shown.bs.tab", calcForDerivationPath);
146e089e 115 DOM.hardenedAddresses.on("change", calcForDerivationPath);
88311463 116 DOM.useP2wpkhNestedInP2sh.on("change", calcForDerivationPath);
ebd8d4e8
IC
117 DOM.indexToggle.on("click", toggleIndexes);
118 DOM.addressToggle.on("click", toggleAddresses);
1b12b2f5 119 DOM.publicKeyToggle.on("click", togglePublicKeys);
ebd8d4e8 120 DOM.privateKeyToggle.on("click", togglePrivateKeys);
5ee7bb9e 121 DOM.languages.on("click", languageChanged);
fe8f2d14 122 DOM.useBitpayAddresses.on("change", useBitpayAddressesChange);
e00964cc 123 setQrEvents(DOM.showQrEls);
ebd8d4e8
IC
124 disableForms();
125 hidePending();
126 hideValidationError();
7f15cb6e 127 populateNetworkSelect();
b4fd763c 128 populateClientSelect();
ebd8d4e8
IC
129 }
130
131 // Event handlers
132
d6cedc94 133 function networkChanged(e) {
6c08f364
IC
134 clearDerivedKeys();
135 clearAddressesList();
3abab9b0 136 DOM.litecoinLtubContainer.addClass("hidden");
fe8f2d14 137 DOM.useBitpayAddressesContainer.addClass("hidden");
54563907 138 var networkIndex = e.target.value;
6c08f364
IC
139 var network = networks[networkIndex];
140 network.onSelect();
0cda44d5
IC
141 if (network.p2wpkhNestedInP2shAvailable) {
142 showP2wpkhNestedInP2shAvailable();
6c08f364
IC
143 }
144 else {
0cda44d5 145 showP2wpkhNestedInP2shUnavailable();
6c08f364 146 }
54563907
IC
147 if (seed != null) {
148 phraseChanged();
149 }
150 else {
151 rootKeyChanged();
152 }
d6cedc94 153 }
29bf60f5
IC
154
155 function bip32ClientChanged(e) {
156 var clientIndex = DOM.bip32Client.val();
157 if (clientIndex == "custom") {
158 DOM.bip32path.prop("readonly", false);
b4fd763c
AG
159 }
160 else {
29bf60f5
IC
161 DOM.bip32path.prop("readonly", true);
162 clients[clientIndex].onSelect();
163 if (seed != null) {
164 phraseChanged();
165 }
166 else {
167 rootKeyChanged();
168 }
b4fd763c
AG
169 }
170 }
d6cedc94 171
c6624d51
IC
172 function setEntropyVisibility() {
173 if (isUsingOwnEntropy()) {
174 DOM.entropyContainer.removeClass("hidden");
175 DOM.generateContainer.addClass("hidden");
176 DOM.phrase.prop("readonly", true);
177 DOM.entropy.focus();
178 entropyChanged();
179 }
180 else {
181 DOM.entropyContainer.addClass("hidden");
182 DOM.generateContainer.removeClass("hidden");
183 DOM.phrase.prop("readonly", false);
057722b0 184 hidePending();
c6624d51
IC
185 }
186 }
187
ebd8d4e8
IC
188 function delayedPhraseChanged() {
189 hideValidationError();
ed6d9d39
IC
190 seed = null;
191 bip32RootKey = null;
192 bip32ExtendedKey = null;
193 clearAddressesList();
ebd8d4e8
IC
194 showPending();
195 if (phraseChangeTimeoutEvent != null) {
196 clearTimeout(phraseChangeTimeoutEvent);
197 }
198 phraseChangeTimeoutEvent = setTimeout(phraseChanged, 400);
199 }
200
201 function phraseChanged() {
202 showPending();
5ee7bb9e 203 setMnemonicLanguage();
ebd8d4e8
IC
204 // Get the mnemonic phrase
205 var phrase = DOM.phrase.val();
206 var errorText = findPhraseErrors(phrase);
207 if (errorText) {
208 showValidationError(errorText);
209 return;
210 }
efe41586
IC
211 // Calculate and display
212 var passphrase = DOM.passphrase.val();
213 calcBip32RootKeyFromSeed(phrase, passphrase);
214 calcForDerivationPath();
efe41586
IC
215 }
216
c6624d51
IC
217 function delayedEntropyChanged() {
218 hideValidationError();
219 showPending();
220 if (entropyChangeTimeoutEvent != null) {
221 clearTimeout(entropyChangeTimeoutEvent);
222 }
223 entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
224 }
225
226 function entropyChanged() {
057722b0
IC
227 // If blank entropy, clear mnemonic, addresses, errors
228 if (DOM.entropy.val().trim().length == 0) {
229 clearDisplay();
0a84fe6a 230 clearEntropyFeedback();
057722b0
IC
231 DOM.phrase.val("");
232 showValidationError("Blank entropy");
233 return;
234 }
235 // Get the current phrase to detect changes
236 var phrase = DOM.phrase.val();
237 // Set the phrase from the entropy
c6624d51 238 setMnemonicFromEntropy();
057722b0
IC
239 // Recalc addresses if the phrase has changed
240 var newPhrase = DOM.phrase.val();
241 if (newPhrase != phrase) {
242 if (newPhrase.length == 0) {
243 clearDisplay();
244 }
245 else {
246 phraseChanged();
247 }
248 }
249 else {
250 hidePending();
251 }
c6624d51
IC
252 }
253
efe41586
IC
254 function delayedRootKeyChanged() {
255 // Warn if there is an existing mnemonic or passphrase.
256 if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
257 if (!confirm("This will clear existing mnemonic and passphrase")) {
258 DOM.rootKey.val(bip32RootKey);
259 return
260 }
261 }
262 hideValidationError();
263 showPending();
264 // Clear existing mnemonic and passphrase
265 DOM.phrase.val("");
266 DOM.passphrase.val("");
267 seed = null;
268 if (rootKeyChangedTimeoutEvent != null) {
269 clearTimeout(rootKeyChangedTimeoutEvent);
270 }
271 rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400);
272 }
273
274 function rootKeyChanged() {
275 showPending();
276 hideValidationError();
efe41586
IC
277 var rootKeyBase58 = DOM.rootKey.val();
278 var errorText = validateRootKey(rootKeyBase58);
279 if (errorText) {
280 showValidationError(errorText);
281 return;
282 }
283 // Calculate and display
284 calcBip32RootKeyFromBase58(rootKeyBase58);
285 calcForDerivationPath();
efe41586
IC
286 }
287
3abab9b0
IC
288 function litecoinUseLtubChanged() {
289 litecoinUseLtub = DOM.litecoinUseLtub.prop("checked");
290 if (litecoinUseLtub) {
291 network = bitcoinjs.bitcoin.networks.litecoinLtub;
292 }
293 else {
294 network = bitcoinjs.bitcoin.networks.litecoin;
295 }
296 phraseChanged();
297 }
298
efe41586 299 function calcForDerivationPath() {
6c08f364 300 clearDerivedKeys();
ba3cb9ec 301 clearAddressesList();
0eda54f5 302 showPending();
6c08f364
IC
303 // Don't show bip49 if it's selected but network doesn't support it
304 if (bip49TabSelected() && !networkHasBip49()) {
305 return;
306 }
ebd8d4e8 307 // Get the derivation path
38523d36
IC
308 var derivationPath = getDerivationPath();
309 var errorText = findDerivationPathErrors(derivationPath);
ebd8d4e8
IC
310 if (errorText) {
311 showValidationError(errorText);
312 return;
313 }
5eaa6877 314 bip32ExtendedKey = calcBip32ExtendedKey(derivationPath);
c554e6ff
IC
315 if (bip44TabSelected()) {
316 displayBip44Info();
317 }
6c08f364
IC
318 if (bip49TabSelected()) {
319 displayBip49Info();
320 }
ebd8d4e8 321 displayBip32Info();
ebd8d4e8
IC
322 }
323
324 function generateClicked() {
c6624d51
IC
325 if (isUsingOwnEntropy()) {
326 return;
327 }
ebd8d4e8
IC
328 clearDisplay();
329 showPending();
330 setTimeout(function() {
5ee7bb9e 331 setMnemonicLanguage();
ebd8d4e8
IC
332 var phrase = generateRandomPhrase();
333 if (!phrase) {
334 return;
335 }
336 phraseChanged();
337 }, 50);
338 }
339
5ee7bb9e
IC
340 function languageChanged() {
341 setTimeout(function() {
342 setMnemonicLanguage();
343 if (DOM.phrase.val().length > 0) {
344 var newPhrase = convertPhraseToNewLanguage();
345 DOM.phrase.val(newPhrase);
346 phraseChanged();
347 }
348 else {
349 DOM.generate.trigger("click");
350 }
351 }, 50);
352 }
353
fe8f2d14
IC
354 function useBitpayAddressesChange() {
355 setBitcoinCashNetworkValues();
356 phraseChanged();
357 }
358
ebd8d4e8 359 function toggleIndexes() {
700901cd 360 showIndex = !showIndex;
ebd8d4e8
IC
361 $("td.index span").toggleClass("invisible");
362 }
363
364 function toggleAddresses() {
700901cd 365 showAddress = !showAddress;
ebd8d4e8
IC
366 $("td.address span").toggleClass("invisible");
367 }
368
1b12b2f5
IC
369 function togglePublicKeys() {
370 showPubKey = !showPubKey;
371 $("td.pubkey span").toggleClass("invisible");
372 }
373
ebd8d4e8 374 function togglePrivateKeys() {
700901cd 375 showPrivKey = !showPrivKey;
ebd8d4e8
IC
376 $("td.privkey span").toggleClass("invisible");
377 }
378
379 // Private methods
380
381 function generateRandomPhrase() {
382 if (!hasStrongRandom()) {
383 var errorText = "This browser does not support strong randomness";
384 showValidationError(errorText);
385 return;
386 }
88df3739 387 var numWords = parseInt(DOM.generatedStrength.val());
ebd8d4e8
IC
388 var strength = numWords / 3 * 32;
389 var words = mnemonic.generate(strength);
390 DOM.phrase.val(words);
391 return words;
392 }
393
efe41586 394 function calcBip32RootKeyFromSeed(phrase, passphrase) {
3e0ed16a 395 seed = mnemonic.toSeed(phrase, passphrase);
a0091a40 396 bip32RootKey = bitcoinjs.bitcoin.HDNode.fromSeedHex(seed, network);
efe41586
IC
397 }
398
399 function calcBip32RootKeyFromBase58(rootKeyBase58) {
a0091a40 400 bip32RootKey = bitcoinjs.bitcoin.HDNode.fromBase58(rootKeyBase58, network);
efe41586
IC
401 }
402
403 function calcBip32ExtendedKey(path) {
0a1f0259
IC
404 // Check there's a root key to derive from
405 if (!bip32RootKey) {
406 return bip32RootKey;
407 }
5eaa6877 408 var extendedKey = bip32RootKey;
ebd8d4e8
IC
409 // Derive the key from the path
410 var pathBits = path.split("/");
411 for (var i=0; i<pathBits.length; i++) {
412 var bit = pathBits[i];
413 var index = parseInt(bit);
414 if (isNaN(index)) {
415 continue;
416 }
417 var hardened = bit[bit.length-1] == "'";
a0091a40 418 var isPriv = !(extendedKey.isNeutered());
ba3cb9ec
IC
419 var invalidDerivationPath = hardened && !isPriv;
420 if (invalidDerivationPath) {
421 extendedKey = null;
422 }
423 else if (hardened) {
5eaa6877 424 extendedKey = extendedKey.deriveHardened(index);
ebd8d4e8
IC
425 }
426 else {
5eaa6877 427 extendedKey = extendedKey.derive(index);
ebd8d4e8
IC
428 }
429 }
5eaa6877 430 return extendedKey
ebd8d4e8
IC
431 }
432
433 function showValidationError(errorText) {
434 DOM.feedback
435 .text(errorText)
436 .show();
437 }
438
439 function hideValidationError() {
440 DOM.feedback
441 .text("")
442 .hide();
443 }
444
445 function findPhraseErrors(phrase) {
ebd8d4e8 446 // Preprocess the words
783981de 447 phrase = mnemonic.normalizeString(phrase);
5ee7bb9e 448 var words = phraseToWordArray(phrase);
057722b0
IC
449 // Detect blank phrase
450 if (words.length == 0) {
451 return "Blank mnemonic";
452 }
563e401a 453 // Check each word
5ee7bb9e
IC
454 for (var i=0; i<words.length; i++) {
455 var word = words[i];
456 var language = getLanguage();
457 if (WORDLISTS[language].indexOf(word) == -1) {
563e401a
IC
458 console.log("Finding closest match to " + word);
459 var nearestWord = findNearestWord(word);
460 return word + " not in wordlist, did you mean " + nearestWord + "?";
461 }
462 }
ebd8d4e8 463 // Check the words are valid
5ee7bb9e 464 var properPhrase = wordArrayToPhrase(words);
ebd8d4e8
IC
465 var isValid = mnemonic.check(properPhrase);
466 if (!isValid) {
467 return "Invalid mnemonic";
468 }
469 return false;
470 }
471
efe41586
IC
472 function validateRootKey(rootKeyBase58) {
473 try {
cd7c8327 474 bitcoinjs.bitcoin.HDNode.fromBase58(rootKeyBase58, network);
efe41586
IC
475 }
476 catch (e) {
477 return "Invalid root key";
478 }
479 return "";
480 }
481
38523d36 482 function getDerivationPath() {
32fab2c3 483 if (bip44TabSelected()) {
38523d36
IC
484 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
485 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
486 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
487 var change = parseIntNoNaN(DOM.bip44change.val(), 0);
488 var path = "m/";
489 path += purpose + "'/";
490 path += coin + "'/";
491 path += account + "'/";
492 path += change;
493 DOM.bip44path.val(path);
494 var derivationPath = DOM.bip44path.val();
495 console.log("Using derivation path from BIP44 tab: " + derivationPath);
496 return derivationPath;
497 }
6c08f364
IC
498 if (bip49TabSelected()) {
499 var purpose = parseIntNoNaN(DOM.bip49purpose.val(), 49);
500 var coin = parseIntNoNaN(DOM.bip49coin.val(), 0);
501 var account = parseIntNoNaN(DOM.bip49account.val(), 0);
502 var change = parseIntNoNaN(DOM.bip49change.val(), 0);
503 var path = "m/";
504 path += purpose + "'/";
505 path += coin + "'/";
506 path += account + "'/";
507 path += change;
508 DOM.bip49path.val(path);
509 var derivationPath = DOM.bip49path.val();
510 console.log("Using derivation path from BIP49 tab: " + derivationPath);
511 return derivationPath;
512 }
32fab2c3 513 else if (bip32TabSelected()) {
38523d36
IC
514 var derivationPath = DOM.bip32path.val();
515 console.log("Using derivation path from BIP32 tab: " + derivationPath);
516 return derivationPath;
517 }
518 else {
519 console.log("Unknown derivation path");
520 }
521 }
522
ebd8d4e8 523 function findDerivationPathErrors(path) {
30c9e79d
IC
524 // TODO is not perfect but is better than nothing
525 // Inspired by
526 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors
527 // and
528 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys
529 var maxDepth = 255; // TODO verify this!!
530 var maxIndexValue = Math.pow(2, 31); // TODO verify this!!
531 if (path[0] != "m") {
532 return "First character must be 'm'";
533 }
534 if (path.length > 1) {
535 if (path[1] != "/") {
536 return "Separator must be '/'";
537 }
538 var indexes = path.split("/");
539 if (indexes.length > maxDepth) {
540 return "Derivation depth is " + indexes.length + ", must be less than " + maxDepth;
541 }
542 for (var depth = 1; depth<indexes.length; depth++) {
543 var index = indexes[depth];
544 var invalidChars = index.replace(/^[0-9]+'?$/g, "")
545 if (invalidChars.length > 0) {
546 return "Invalid characters " + invalidChars + " found at depth " + depth;
547 }
548 var indexValue = parseInt(index.replace("'", ""));
549 if (isNaN(depth)) {
550 return "Invalid number at depth " + depth;
551 }
552 if (indexValue > maxIndexValue) {
553 return "Value of " + indexValue + " at depth " + depth + " must be less than " + maxIndexValue;
554 }
555 }
556 }
0a1f0259
IC
557 // Check root key exists or else derivation path is useless!
558 if (!bip32RootKey) {
559 return "No root key";
560 }
ba3cb9ec 561 // Check no hardened derivation path when using xpub keys
b18eb97a
IC
562 var hardenedPath = path.indexOf("'") > -1;
563 var hardenedAddresses = bip32TabSelected() && DOM.hardenedAddresses.prop("checked");
564 var hardened = hardenedPath || hardenedAddresses;
a0091a40 565 var isXpubkey = bip32RootKey.isNeutered();
ba3cb9ec
IC
566 if (hardened && isXpubkey) {
567 return "Hardened derivation path is invalid with xpub key";
568 }
ebd8d4e8
IC
569 return false;
570 }
571
c554e6ff
IC
572 function displayBip44Info() {
573 // Get the derivation path for the account
574 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
575 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
576 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
577 var path = "m/";
578 path += purpose + "'/";
579 path += coin + "'/";
580 path += account + "'/";
581 // Calculate the account extended keys
582 var accountExtendedKey = calcBip32ExtendedKey(path);
583 var accountXprv = accountExtendedKey.toBase58();
a0091a40 584 var accountXpub = accountExtendedKey.neutered().toBase58();
c554e6ff
IC
585 // Display the extended keys
586 DOM.bip44accountXprv.val(accountXprv);
587 DOM.bip44accountXpub.val(accountXpub);
588 }
589
6c08f364
IC
590 function displayBip49Info() {
591 // Get the derivation path for the account
592 var purpose = parseIntNoNaN(DOM.bip49purpose.val(), 49);
593 var coin = parseIntNoNaN(DOM.bip49coin.val(), 0);
594 var account = parseIntNoNaN(DOM.bip49account.val(), 0);
595 var path = "m/";
596 path += purpose + "'/";
597 path += coin + "'/";
598 path += account + "'/";
599 // Calculate the account extended keys
600 var accountExtendedKey = calcBip32ExtendedKey(path);
601 var accountXprv = accountExtendedKey.toBase58();
602 var accountXpub = accountExtendedKey.neutered().toBase58();
603 // Display the extended keys
604 DOM.bip49accountXprv.val(accountXprv);
605 DOM.bip49accountXpub.val(accountXpub);
606 }
607
ebd8d4e8
IC
608 function displayBip32Info() {
609 // Display the key
3e0ed16a 610 DOM.seed.val(seed);
ebd8d4e8
IC
611 var rootKey = bip32RootKey.toBase58();
612 DOM.rootKey.val(rootKey);
ba3cb9ec 613 var xprvkeyB58 = "NA";
a0091a40 614 if (!bip32ExtendedKey.isNeutered()) {
ba3cb9ec
IC
615 xprvkeyB58 = bip32ExtendedKey.toBase58();
616 }
617 var extendedPrivKey = xprvkeyB58;
ebd8d4e8 618 DOM.extendedPrivKey.val(extendedPrivKey);
a0091a40 619 var extendedPubKey = bip32ExtendedKey.neutered().toBase58();
ebd8d4e8
IC
620 DOM.extendedPubKey.val(extendedPubKey);
621 // Display the addresses and privkeys
622 clearAddressesList();
623 displayAddresses(0, 20);
624 }
625
626 function displayAddresses(start, total) {
40892aba
IC
627 generationProcesses.push(new (function() {
628
629 var rows = [];
630
631 this.stop = function() {
632 for (var i=0; i<rows.length; i++) {
633 rows[i].shouldGenerate = false;
634 }
0eda54f5 635 hidePending();
40892aba
IC
636 }
637
638 for (var i=0; i<total; i++) {
639 var index = i + start;
0eda54f5
IC
640 var isLast = i == total - 1;
641 rows.push(new TableRow(index, isLast));
40892aba
IC
642 }
643
644 })());
ebd8d4e8
IC
645 }
646
0eda54f5 647 function TableRow(index, isLast) {
a8c45487 648
40892aba
IC
649 var self = this;
650 this.shouldGenerate = true;
146e089e 651 var useHardenedAddresses = DOM.hardenedAddresses.prop("checked");
88311463 652 var isP2wpkhNestedInP2sh = bip49TabSelected() || (bip32TabSelected() && useP2wpkhNestedInP2sh());
0cda44d5 653 var p2wpkhNestedInP2shAvailable = networkHasBip49();
146e089e 654
a8c45487
IC
655 function init() {
656 calculateValues();
657 }
658
659 function calculateValues() {
660 setTimeout(function() {
40892aba
IC
661 if (!self.shouldGenerate) {
662 return;
663 }
a0091a40 664 var key = "NA";
146e089e
IC
665 if (useHardenedAddresses) {
666 key = bip32ExtendedKey.deriveHardened(index);
667 }
668 else {
669 key = bip32ExtendedKey.derive(index);
670 }
a8c45487 671 var address = key.getAddress().toString();
ba3cb9ec 672 var privkey = "NA";
a0091a40
IC
673 if (!key.isNeutered()) {
674 privkey = key.keyPair.toWIF(network);
ba3cb9ec 675 }
a0091a40 676 var pubkey = key.getPublicKeyBuffer().toString('hex');
38523d36 677 var indexText = getDerivationPath() + "/" + index;
146e089e
IC
678 if (useHardenedAddresses) {
679 indexText = indexText + "'";
680 }
0edac945 681 // Ethereum values are different
534481b6 682 if (networks[DOM.network.val()].name == "ETH - Ethereum") {
a0091a40 683 var privKeyBuffer = key.keyPair.d.toBuffer();
0edac945
IC
684 privkey = privKeyBuffer.toString('hex');
685 var addressBuffer = ethUtil.privateToAddress(privKeyBuffer);
49b21f12
IC
686 var hexAddress = addressBuffer.toString('hex');
687 var checksumAddress = ethUtil.toChecksumAddress(hexAddress);
688 address = ethUtil.addHexPrefix(checksumAddress);
d0239db4
IC
689 privkey = ethUtil.addHexPrefix(privkey);
690 pubkey = ethUtil.addHexPrefix(pubkey);
0edac945 691 }
64a7d2aa 692 // Ripple values are different
534481b6 693 if (networks[DOM.network.val()].name == "XRP - Ripple") {
64a7d2aa 694 privkey = convertRipplePriv(privkey);
695 address = convertRippleAdrr(address);
696 }
6c08f364 697 // BIP49 addresses are different
0cda44d5
IC
698 if (isP2wpkhNestedInP2sh) {
699 if (!p2wpkhNestedInP2shAvailable) {
6c08f364
IC
700 return;
701 }
702 var keyhash = bitcoinjs.bitcoin.crypto.hash160(key.getPublicKeyBuffer());
703 var scriptsig = bitcoinjs.bitcoin.script.witnessPubKeyHash.output.encode(keyhash);
704 var addressbytes = bitcoinjs.bitcoin.crypto.hash160(scriptsig);
705 var scriptpubkey = bitcoinjs.bitcoin.script.scriptHash.output.encode(addressbytes);
706 address = bitcoinjs.bitcoin.address.fromOutputScript(scriptpubkey, network)
707 }
1b12b2f5 708 addAddressToList(indexText, address, pubkey, privkey);
0eda54f5
IC
709 if (isLast) {
710 hidePending();
711 }
a8c45487
IC
712 }, 50)
713 }
714
715 init();
716
717 }
718
ebd8d4e8
IC
719 function showMore() {
720 var start = DOM.addresses.children().length;
721 var rowsToAdd = parseInt(DOM.rowsToAdd.val());
722 if (isNaN(rowsToAdd)) {
723 rowsToAdd = 20;
724 DOM.rowsToAdd.val("20");
725 }
726 if (rowsToAdd > 200) {
727 var msg = "Generating " + rowsToAdd + " rows could take a while. ";
728 msg += "Do you want to continue?";
729 if (!confirm(msg)) {
730 return;
731 }
732 }
ebd8d4e8 733 displayAddresses(start, rowsToAdd);
ebd8d4e8
IC
734 }
735
736 function clearDisplay() {
737 clearAddressesList();
fa2e4e93 738 clearKeys();
ebd8d4e8
IC
739 hideValidationError();
740 }
741
742 function clearAddressesList() {
743 DOM.addresses.empty();
40892aba
IC
744 stopGenerating();
745 }
746
747 function stopGenerating() {
748 while (generationProcesses.length > 0) {
749 var generation = generationProcesses.shift();
750 generation.stop();
751 }
ebd8d4e8
IC
752 }
753
fa2e4e93
IC
754 function clearKeys() {
755 clearRootKey();
756 clearDerivedKeys();
757 }
758
759 function clearRootKey() {
ebd8d4e8 760 DOM.rootKey.val("");
fa2e4e93
IC
761 }
762
763 function clearDerivedKeys() {
ebd8d4e8
IC
764 DOM.extendedPrivKey.val("");
765 DOM.extendedPubKey.val("");
fa2e4e93
IC
766 DOM.bip44accountXprv.val("");
767 DOM.bip44accountXpub.val("");
ebd8d4e8
IC
768 }
769
1b12b2f5 770 function addAddressToList(indexText, address, pubkey, privkey) {
ebd8d4e8 771 var row = $(addressRowTemplate.html());
700901cd
IC
772 // Elements
773 var indexCell = row.find(".index span");
774 var addressCell = row.find(".address span");
1b12b2f5 775 var pubkeyCell = row.find(".pubkey span");
700901cd
IC
776 var privkeyCell = row.find(".privkey span");
777 // Content
ae30fed8 778 indexCell.text(indexText);
700901cd 779 addressCell.text(address);
1b12b2f5 780 pubkeyCell.text(pubkey);
700901cd
IC
781 privkeyCell.text(privkey);
782 // Visibility
783 if (!showIndex) {
784 indexCell.addClass("invisible");
785 }
786 if (!showAddress) {
787 addressCell.addClass("invisible");
788 }
1b12b2f5
IC
789 if (!showPubKey) {
790 pubkeyCell.addClass("invisible");
791 }
700901cd 792 if (!showPrivKey) {
6d628db7 793 privkeyCell.addClass("invisible");
700901cd 794 }
ebd8d4e8 795 DOM.addresses.append(row);
e00964cc
IC
796 var rowShowQrEls = row.find("[data-show-qr]");
797 setQrEvents(rowShowQrEls);
ebd8d4e8
IC
798 }
799
800 function hasStrongRandom() {
801 return 'crypto' in window && window['crypto'] !== null;
802 }
803
804 function disableForms() {
805 $("form").on("submit", function(e) {
806 e.preventDefault();
807 });
808 }
809
ebd8d4e8
IC
810 function parseIntNoNaN(val, defaultVal) {
811 var v = parseInt(val);
812 if (isNaN(v)) {
813 return defaultVal;
814 }
815 return v;
816 }
817
818 function showPending() {
819 DOM.feedback
820 .text("Calculating...")
821 .show();
822 }
823
563e401a 824 function findNearestWord(word) {
5ee7bb9e
IC
825 var language = getLanguage();
826 var words = WORDLISTS[language];
563e401a
IC
827 var minDistance = 99;
828 var closestWord = words[0];
829 for (var i=0; i<words.length; i++) {
830 var comparedTo = words[i];
6ea15134
IC
831 if (comparedTo.indexOf(word) == 0) {
832 return comparedTo;
833 }
563e401a
IC
834 var distance = Levenshtein.get(word, comparedTo);
835 if (distance < minDistance) {
836 closestWord = comparedTo;
837 minDistance = distance;
838 }
839 }
840 return closestWord;
841 }
842
ebd8d4e8
IC
843 function hidePending() {
844 DOM.feedback
845 .text("")
846 .hide();
847 }
848
7f15cb6e
IC
849 function populateNetworkSelect() {
850 for (var i=0; i<networks.length; i++) {
851 var network = networks[i];
852 var option = $("<option>");
853 option.attr("value", i);
854 option.text(network.name);
7b742f87
IC
855 if (network.name == "BTC - Bitcoin") {
856 option.prop("selected", true);
857 }
7f15cb6e
IC
858 DOM.phraseNetwork.append(option);
859 }
860 }
29bf60f5 861
b4fd763c
AG
862 function populateClientSelect() {
863 for (var i=0; i<clients.length; i++) {
864 var client = clients[i];
865 var option = $("<option>");
866 option.attr("value", i);
867 option.text(client.name);
29bf60f5 868 DOM.bip32Client.append(option);
b4fd763c
AG
869 }
870 }
7f15cb6e 871
5ee7bb9e
IC
872 function getLanguage() {
873 var defaultLanguage = "english";
874 // Try to get from existing phrase
875 var language = getLanguageFromPhrase();
876 // Try to get from url if not from phrase
877 if (language.length == 0) {
878 language = getLanguageFromUrl();
879 }
880 // Default to English if no other option
881 if (language.length == 0) {
882 language = defaultLanguage;
883 }
884 return language;
885 }
886
887 function getLanguageFromPhrase(phrase) {
888 // Check if how many words from existing phrase match a language.
889 var language = "";
890 if (!phrase) {
891 phrase = DOM.phrase.val();
892 }
893 if (phrase.length > 0) {
894 var words = phraseToWordArray(phrase);
895 var languageMatches = {};
896 for (l in WORDLISTS) {
897 // Track how many words match in this language
898 languageMatches[l] = 0;
899 for (var i=0; i<words.length; i++) {
900 var wordInLanguage = WORDLISTS[l].indexOf(words[i]) > -1;
901 if (wordInLanguage) {
902 languageMatches[l]++;
903 }
904 }
905 // Find languages with most word matches.
906 // This is made difficult due to commonalities between Chinese
907 // simplified vs traditional.
908 var mostMatches = 0;
909 var mostMatchedLanguages = [];
910 for (var l in languageMatches) {
911 var numMatches = languageMatches[l];
912 if (numMatches > mostMatches) {
913 mostMatches = numMatches;
914 mostMatchedLanguages = [l];
915 }
916 else if (numMatches == mostMatches) {
917 mostMatchedLanguages.push(l);
918 }
919 }
920 }
921 if (mostMatchedLanguages.length > 0) {
922 // Use first language and warn if multiple detected
923 language = mostMatchedLanguages[0];
924 if (mostMatchedLanguages.length > 1) {
925 console.warn("Multiple possible languages");
926 console.warn(mostMatchedLanguages);
927 }
928 }
929 }
930 return language;
931 }
932
933 function getLanguageFromUrl() {
c6624d51
IC
934 for (var language in WORDLISTS) {
935 if (window.location.hash.indexOf(language) > -1) {
936 return language;
937 }
938 }
939 return "";
5ee7bb9e
IC
940 }
941
942 function setMnemonicLanguage() {
943 var language = getLanguage();
944 // Load the bip39 mnemonic generator for this language if required
945 if (!(language in mnemonics)) {
946 mnemonics[language] = new Mnemonic(language);
947 }
948 mnemonic = mnemonics[language];
949 }
950
951 function convertPhraseToNewLanguage() {
952 var oldLanguage = getLanguageFromPhrase();
953 var newLanguage = getLanguageFromUrl();
954 var oldPhrase = DOM.phrase.val();
955 var oldWords = phraseToWordArray(oldPhrase);
956 var newWords = [];
957 for (var i=0; i<oldWords.length; i++) {
958 var oldWord = oldWords[i];
959 var index = WORDLISTS[oldLanguage].indexOf(oldWord);
960 var newWord = WORDLISTS[newLanguage][index];
961 newWords.push(newWord);
962 }
963 newPhrase = wordArrayToPhrase(newWords);
964 return newPhrase;
965 }
966
967 // TODO look at jsbip39 - mnemonic.splitWords
968 function phraseToWordArray(phrase) {
969 var words = phrase.split(/\s/g);
970 var noBlanks = [];
971 for (var i=0; i<words.length; i++) {
972 var word = words[i];
973 if (word.length > 0) {
974 noBlanks.push(word);
975 }
976 }
977 return noBlanks;
978 }
979
980 // TODO look at jsbip39 - mnemonic.joinWords
981 function wordArrayToPhrase(words) {
982 var phrase = words.join(" ");
983 var language = getLanguageFromPhrase(phrase);
984 if (language == "japanese") {
985 phrase = words.join("\u3000");
986 }
987 return phrase;
988 }
989
c6624d51
IC
990 function isUsingOwnEntropy() {
991 return DOM.useEntropy.prop("checked");
992 }
993
994 function setMnemonicFromEntropy() {
0a84fe6a 995 clearEntropyFeedback();
057722b0 996 // Get entropy value
c6624d51 997 var entropyStr = DOM.entropy.val();
057722b0 998 // Work out minimum base for entropy
c6624d51 999 var entropy = Entropy.fromString(entropyStr);
057722b0 1000 if (entropy.binaryStr.length == 0) {
c6624d51
IC
1001 return;
1002 }
1003 // Show entropy details
1cf1bbaf 1004 showEntropyFeedback(entropy);
3599674d
IC
1005 // Use entropy hash if not using raw entropy
1006 var bits = entropy.binaryStr;
1007 var mnemonicLength = DOM.entropyMnemonicLength.val();
1008 if (mnemonicLength != "raw") {
1009 // Get bits by hashing entropy with SHA256
1010 var hash = sjcl.hash.sha256.hash(entropy.cleanStr);
1011 var hex = sjcl.codec.hex.fromBits(hash);
1012 bits = BigInteger.parse(hex, 16).toString(2);
53aaab27 1013 while (bits.length % 256 != 0) {
3599674d
IC
1014 bits = "0" + bits;
1015 }
1016 // Truncate hash to suit number of words
1017 mnemonicLength = parseInt(mnemonicLength);
1018 var numberOfBits = 32 * mnemonicLength / 3;
1019 bits = bits.substring(0, numberOfBits);
1020 }
c6624d51 1021 // Discard trailing entropy
3599674d 1022 var bitsToUse = Math.floor(bits.length / 32) * 32;
d6fd8ebf
IC
1023 var start = bits.length - bitsToUse;
1024 var binaryStr = bits.substring(start);
c6624d51
IC
1025 // Convert entropy string to numeric array
1026 var entropyArr = [];
adc8ce12
IC
1027 for (var i=0; i<binaryStr.length / 8; i++) {
1028 var byteAsBits = binaryStr.substring(i*8, i*8+8);
1029 var entropyByte = parseInt(byteAsBits, 2);
c6624d51
IC
1030 entropyArr.push(entropyByte)
1031 }
1032 // Convert entropy array to mnemonic
1033 var phrase = mnemonic.toMnemonic(entropyArr);
1034 // Set the mnemonic in the UI
1035 DOM.phrase.val(phrase);
1036 }
1037
0a84fe6a 1038 function clearEntropyFeedback() {
20f459ce 1039 DOM.entropyCrackTime.text("...");
1cf1bbaf 1040 DOM.entropyType.text("");
0a84fe6a
IC
1041 DOM.entropyWordCount.text("0");
1042 DOM.entropyEventCount.text("0");
1043 DOM.entropyBitsPerEvent.text("0");
1044 DOM.entropyBits.text("0");
1045 DOM.entropyFiltered.html("&nbsp;");
1046 DOM.entropyBinary.html("&nbsp;");
c6624d51
IC
1047 }
1048
1cf1bbaf 1049 function showEntropyFeedback(entropy) {
6422c1cd 1050 var numberOfBits = entropy.binaryStr.length;
20f459ce 1051 var timeToCrack = "unknown";
9bc39377
IC
1052 try {
1053 var z = zxcvbn(entropy.base.parts.join(""));
20f459ce
IC
1054 timeToCrack = z.crack_times_display.offline_fast_hashing_1e10_per_second;
1055 if (z.feedback.warning != "") {
1056 timeToCrack = timeToCrack + " - " + z.feedback.warning;
1057 };
9bc39377
IC
1058 }
1059 catch (e) {
9bc39377
IC
1060 console.log("Error detecting entropy strength with zxcvbn:");
1061 console.log(e);
b299a6a7 1062 }
391c7f26 1063 var entropyTypeStr = getEntropyTypeStr(entropy);
6422c1cd 1064 var wordCount = Math.floor(numberOfBits / 32) * 3;
94959756 1065 var bitsPerEvent = entropy.bitsPerEvent.toFixed(2);
b54c1218 1066 DOM.entropyFiltered.html(entropy.cleanHtml);
391c7f26 1067 DOM.entropyType.text(entropyTypeStr);
20f459ce 1068 DOM.entropyCrackTime.text(timeToCrack);
1cf1bbaf 1069 DOM.entropyEventCount.text(entropy.base.ints.length);
6422c1cd 1070 DOM.entropyBits.text(numberOfBits);
0a84fe6a
IC
1071 DOM.entropyWordCount.text(wordCount);
1072 DOM.entropyBinary.text(entropy.binaryStr);
6422c1cd 1073 DOM.entropyBitsPerEvent.text(bitsPerEvent);
ee0981f1
IC
1074 // detect and warn of filtering
1075 var rawNoSpaces = DOM.entropy.val().replace(/\s/g, "");
1076 var cleanNoSpaces = entropy.cleanStr.replace(/\s/g, "");
1077 var isFiltered = rawNoSpaces.length != cleanNoSpaces.length;
1078 if (isFiltered) {
1079 DOM.entropyFilterWarning.removeClass('hidden');
1080 }
1081 else {
1082 DOM.entropyFilterWarning.addClass('hidden');
1083 }
02f05d3e
IC
1084 }
1085
391c7f26
IC
1086 function getEntropyTypeStr(entropy) {
1087 var typeStr = entropy.base.str;
1088 // Add some detail if these are cards
1089 if (entropy.base.asInt == 52) {
1090 var cardDetail = []; // array of message strings
1091 // Detect duplicates
1092 var dupes = [];
1093 var dupeTracker = {};
1094 for (var i=0; i<entropy.base.parts.length; i++) {
1095 var card = entropy.base.parts[i];
5c653a12
IC
1096 var cardUpper = card.toUpperCase();
1097 if (cardUpper in dupeTracker) {
391c7f26
IC
1098 dupes.push(card);
1099 }
5c653a12 1100 dupeTracker[cardUpper] = true;
391c7f26
IC
1101 }
1102 if (dupes.length > 0) {
1103 var dupeWord = "duplicates";
1104 if (dupes.length == 1) {
1105 dupeWord = "duplicate";
1106 }
1107 var msg = dupes.length + " " + dupeWord + ": " + dupes.slice(0,3).join(" ");
1108 if (dupes.length > 3) {
1109 msg += "...";
1110 }
1111 cardDetail.push(msg);
1112 }
1113 // Detect full deck
1114 var uniqueCards = [];
1115 for (var uniqueCard in dupeTracker) {
1116 uniqueCards.push(uniqueCard);
1117 }
1118 if (uniqueCards.length == 52) {
1119 cardDetail.unshift("full deck");
1120 }
bbc29c80
IC
1121 // Detect missing cards
1122 var values = "A23456789TJQK";
1123 var suits = "CDHS";
1124 var missingCards = [];
1125 for (var i=0; i<suits.length; i++) {
1126 for (var j=0; j<values.length; j++) {
1127 var card = values[j] + suits[i];
1128 if (!(card in dupeTracker)) {
1129 missingCards.push(card);
1130 }
1131 }
1132 }
1133 // Display missing cards if six or less, ie clearly going for full deck
1134 if (missingCards.length > 0 && missingCards.length <= 6) {
1135 var msg = missingCards.length + " missing: " + missingCards.slice(0,3).join(" ");
1136 if (missingCards.length > 3) {
1137 msg += "...";
1138 }
1139 cardDetail.push(msg);
1140 }
391c7f26
IC
1141 // Add card details to typeStr
1142 if (cardDetail.length > 0) {
1143 typeStr += " (" + cardDetail.join(", ") + ")";
1144 }
1145 }
1146 return typeStr;
1147 }
1148
e00964cc
IC
1149 function setQrEvents(els) {
1150 els.on("mouseenter", createQr);
1151 els.on("mouseleave", destroyQr);
1152 els.on("click", toggleQr);
1153 }
1154
1155 function createQr(e) {
1156 var content = e.target.textContent || e.target.value;
1157 if (content) {
1158 var size = 130;
1159 DOM.qrImage.qrcode({width: size, height: size, text: content});
1160 if (!showQr) {
97811c29
IC
1161 DOM.qrHider.addClass("hidden");
1162 }
1163 else {
1164 DOM.qrHider.removeClass("hidden");
e00964cc
IC
1165 }
1166 DOM.qrContainer.removeClass("hidden");
1167 }
1168 }
1169
1170 function destroyQr() {
1171 DOM.qrImage.text("");
1172 DOM.qrContainer.addClass("hidden");
1173 }
1174
1175 function toggleQr() {
1176 showQr = !showQr;
97811c29 1177 DOM.qrHider.toggleClass("hidden");
e00964cc
IC
1178 DOM.qrHint.toggleClass("hidden");
1179 }
1180
32fab2c3
IC
1181 function bip44TabSelected() {
1182 return DOM.bip44tab.hasClass("active");
1183 }
1184
1185 function bip32TabSelected() {
1186 return DOM.bip32tab.hasClass("active");
1187 }
1188
88311463
IC
1189 function useP2wpkhNestedInP2sh() {
1190 return DOM.useP2wpkhNestedInP2sh.prop("checked");
1191 }
1192
6c08f364 1193 function networkHasBip49() {
0cda44d5 1194 return networks[DOM.network.val()].p2wpkhNestedInP2shAvailable;
6c08f364
IC
1195 }
1196
1197 function bip49TabSelected() {
1198 return DOM.bip49tab.hasClass("active");
1199 }
1200
1201 function setHdCoin(coinValue) {
1202 DOM.bip44coin.val(coinValue);
1203 DOM.bip49coin.val(coinValue);
1204 }
1205
0cda44d5 1206 function showP2wpkhNestedInP2shAvailable() {
6c08f364
IC
1207 DOM.bip49unavailable.addClass("hidden");
1208 DOM.bip49available.removeClass("hidden");
88311463 1209 DOM.useP2wpkhNestedInP2sh.prop("disabled", false);
6c08f364
IC
1210 }
1211
0cda44d5 1212 function showP2wpkhNestedInP2shUnavailable() {
6c08f364
IC
1213 DOM.bip49available.addClass("hidden");
1214 DOM.bip49unavailable.removeClass("hidden");
88311463
IC
1215 DOM.useP2wpkhNestedInP2sh.prop("disabled", true);
1216 DOM.useP2wpkhNestedInP2sh.prop("checked", false);
6c08f364
IC
1217 }
1218
fe8f2d14
IC
1219 function useBitpayAddresses() {
1220 return !(DOM.useBitpayAddresses.prop("checked"));
1221 }
1222
1223 function setBitcoinCashNetworkValues() {
1224 if (useBitpayAddresses()) {
1225 network = bitcoinjs.bitcoin.networks.bitcoin;
1226 }
1227 else {
1228 network = bitcoinjs.bitcoin.networks.bitcoinCashBitbpay;
1229 }
1230 }
1231
7f15cb6e 1232 var networks = [
daab55dc
IC
1233 {
1234 name: "BCH - Bitcoin Cash",
0cda44d5 1235 p2wpkhNestedInP2shAvailable: false,
daab55dc 1236 onSelect: function() {
fe8f2d14
IC
1237 DOM.useBitpayAddressesContainer.removeClass("hidden");
1238 setBitcoinCashNetworkValues();
daab55dc
IC
1239 setHdCoin(145);
1240 },
1241 },
7f15cb6e 1242 {
534481b6 1243 name: "BTC - Bitcoin",
0cda44d5 1244 p2wpkhNestedInP2shAvailable: true,
7a995731 1245 onSelect: function() {
a0091a40 1246 network = bitcoinjs.bitcoin.networks.bitcoin;
6c08f364 1247 setHdCoin(0);
7a995731
IC
1248 },
1249 },
7f15cb6e 1250 {
534481b6 1251 name: "BTC - Bitcoin Testnet",
0cda44d5 1252 p2wpkhNestedInP2shAvailable: true,
7a995731 1253 onSelect: function() {
a0091a40 1254 network = bitcoinjs.bitcoin.networks.testnet;
6c08f364 1255 setHdCoin(1);
7a995731
IC
1256 },
1257 },
7f15cb6e 1258 {
534481b6 1259 name: "CLAM - Clams",
0cda44d5 1260 p2wpkhNestedInP2shAvailable: false,
7a995731 1261 onSelect: function() {
a0091a40 1262 network = bitcoinjs.bitcoin.networks.clam;
6c08f364 1263 setHdCoin(23);
7a995731
IC
1264 },
1265 },
0921f370 1266 {
1267 name: "CRW - Crown",
0cda44d5 1268 p2wpkhNestedInP2shAvailable: false,
0921f370 1269 onSelect: function() {
6c08f364
IC
1270 network = bitcoinjs.bitcoin.networks.crown;
1271 setHdCoin(72);
0921f370 1272 },
1273 },
7f15cb6e 1274 {
534481b6 1275 name: "DASH - Dash",
0cda44d5 1276 p2wpkhNestedInP2shAvailable: false,
e3a9508c 1277 onSelect: function() {
a0091a40 1278 network = bitcoinjs.bitcoin.networks.dash;
6c08f364 1279 setHdCoin(5);
e3a9508c
IC
1280 },
1281 },
c0386f3b 1282 {
534481b6 1283 name: "DASH - Dash Testnet",
0cda44d5 1284 p2wpkhNestedInP2shAvailable: false,
c0386f3b 1285 onSelect: function() {
a0091a40 1286 network = bitcoinjs.bitcoin.networks.dashtn;
6c08f364 1287 setHdCoin(1);
c0386f3b
KR
1288 },
1289 },
e3a9508c 1290 {
534481b6 1291 name: "DOGE - Dogecoin",
0cda44d5 1292 p2wpkhNestedInP2shAvailable: false,
534481b6 1293 onSelect: function() {
6c08f364
IC
1294 network = bitcoinjs.bitcoin.networks.dogecoin;
1295 setHdCoin(3);
534481b6 1296 },
1297 },
1298 {
1299 name: "ETH - Ethereum",
0cda44d5 1300 p2wpkhNestedInP2shAvailable: false,
e3a9508c 1301 onSelect: function() {
a0091a40 1302 network = bitcoinjs.bitcoin.networks.bitcoin;
6c08f364 1303 setHdCoin(60);
e3a9508c
IC
1304 },
1305 },
6e679905 1306 {
1307 name: "FJC - Fujicoin",
1308 p2wpkhNestedInP2shAvailable: false,
1309 onSelect: function() {
1310 network = bitcoinjs.bitcoin.networks.fujicoin;
1311 setHdCoin(75);
1312 },
1313 },
f88fab20 1314 {
534481b6 1315 name: "GAME - GameCredits",
0cda44d5 1316 p2wpkhNestedInP2shAvailable: false,
f88fab20 1317 onSelect: function() {
a0091a40 1318 network = bitcoinjs.bitcoin.networks.game;
6c08f364 1319 setHdCoin(101);
f88fab20 1320 },
1321 },
a3baa26e 1322 {
534481b6 1323 name: "JBS - Jumbucks",
0cda44d5 1324 p2wpkhNestedInP2shAvailable: false,
a3baa26e 1325 onSelect: function() {
a0091a40 1326 network = bitcoinjs.bitcoin.networks.jumbucks;
6c08f364 1327 setHdCoin(26);
a3baa26e
IC
1328 },
1329 },
1330 {
534481b6 1331 name: "LTC - Litecoin",
0cda44d5 1332 p2wpkhNestedInP2shAvailable: false,
a3baa26e 1333 onSelect: function() {
a0091a40 1334 network = bitcoinjs.bitcoin.networks.litecoin;
6c08f364 1335 setHdCoin(2);
3abab9b0 1336 DOM.litecoinLtubContainer.removeClass("hidden");
a3baa26e
IC
1337 },
1338 },
56ad9601
JS
1339 {
1340 name: "MAZA - Maza",
0cda44d5 1341 p2wpkhNestedInP2shAvailable: false,
56ad9601
JS
1342 onSelect: function() {
1343 network = bitcoinjs.bitcoin.networks.maza;
1344 setHdCoin(13);
1345 },
1346 },
1347
a3baa26e 1348 {
534481b6 1349 name: "NMC - Namecoin",
0cda44d5 1350 p2wpkhNestedInP2shAvailable: false,
a3baa26e 1351 onSelect: function() {
a0091a40 1352 network = bitcoinjs.bitcoin.networks.namecoin;
6c08f364 1353 setHdCoin(7);
a3baa26e
IC
1354 },
1355 },
c0df0189 1356 {
1357 name: "PIVX - PIVX",
0cda44d5 1358 p2wpkhNestedInP2shAvailable: false,
c0df0189 1359 onSelect: function() {
1360 network = bitcoinjs.bitcoin.networks.pivx;
1361 setHdCoin(119);
1362 },
1363 },
1364 {
1365 name: "PIVX - PIVX Testnet",
0cda44d5 1366 p2wpkhNestedInP2shAvailable: false,
c0df0189 1367 onSelect: function() {
1368 network = bitcoinjs.bitcoin.networks.pivxtestnet;
1369 setHdCoin(1);
1370 },
1371 },
5c434a8a 1372 {
534481b6 1373 name: "PPC - Peercoin",
0cda44d5 1374 p2wpkhNestedInP2shAvailable: false,
5c434a8a 1375 onSelect: function() {
a0091a40 1376 network = bitcoinjs.bitcoin.networks.peercoin;
6c08f364 1377 setHdCoin(6);
5c434a8a
CM
1378 },
1379 },
64a7d2aa 1380 {
534481b6 1381 name: "SDC - ShadowCash",
0cda44d5 1382 p2wpkhNestedInP2shAvailable: false,
82f91834 1383 onSelect: function() {
a0091a40 1384 network = bitcoinjs.bitcoin.networks.shadow;
6c08f364 1385 setHdCoin(35);
82f91834
DG
1386 },
1387 },
07ac4350 1388 {
534481b6 1389 name: "SDC - ShadowCash Testnet",
0cda44d5 1390 p2wpkhNestedInP2shAvailable: false,
07ac4350 1391 onSelect: function() {
a0091a40 1392 network = bitcoinjs.bitcoin.networks.shadowtn;
6c08f364 1393 setHdCoin(1);
07ac4350 1394 },
1395 },
7a5a87a0 1396 {
534481b6 1397 name: "SLM - Slimcoin",
0cda44d5 1398 p2wpkhNestedInP2shAvailable: false,
7a5a87a0 1399 onSelect: function() {
a0091a40 1400 network = bitcoinjs.bitcoin.networks.slimcoin;
6c08f364 1401 setHdCoin(63);
7a5a87a0
GH
1402 },
1403 },
1404 {
534481b6 1405 name: "SLM - Slimcoin Testnet",
0cda44d5 1406 p2wpkhNestedInP2shAvailable: false,
7a5a87a0 1407 onSelect: function() {
a0091a40 1408 network = bitcoinjs.bitcoin.networks.slimcointn;
6c08f364 1409 setHdCoin(111);
7a5a87a0
GH
1410 },
1411 },
8dd28f2c 1412 {
1413 name: "USNBT - NuBits",
1414 p2wpkhNestedInP2shAvailable: false,
1415 onSelect: function() {
1416 network = bitcoinjs.bitcoin.networks.nubits;
1417 setHdCoin(12);
1418 },
1419 },
07ac4350 1420 {
534481b6 1421 name: "VIA - Viacoin",
0cda44d5 1422 p2wpkhNestedInP2shAvailable: false,
07ac4350 1423 onSelect: function() {
a0091a40 1424 network = bitcoinjs.bitcoin.networks.viacoin;
6c08f364 1425 setHdCoin(14);
07ac4350 1426 },
1427 },
0edac945 1428 {
534481b6 1429 name: "VIA - Viacoin Testnet",
0cda44d5 1430 p2wpkhNestedInP2shAvailable: false,
0edac945 1431 onSelect: function() {
a0091a40 1432 network = bitcoinjs.bitcoin.networks.viacointestnet;
6c08f364 1433 setHdCoin(1);
0edac945
IC
1434 },
1435 },
534481b6 1436 {
7ebdf61c 1437 name: "XMY - Myriadcoin",
0cda44d5 1438 p2wpkhNestedInP2shAvailable: false,
534481b6 1439 onSelect: function() {
7ebdf61c
IC
1440 network = bitcoinjs.bitcoin.networks.myriadcoin;
1441 setHdCoin(90);
534481b6 1442 },
adedbf91 1443 },
1444 {
7ebdf61c 1445 name: "XRP - Ripple",
0cda44d5 1446 p2wpkhNestedInP2shAvailable: false,
adedbf91 1447 onSelect: function() {
7ebdf61c
IC
1448 network = bitcoinjs.bitcoin.networks.bitcoin;
1449 setHdCoin(144);
adedbf91 1450 },
534481b6 1451 }
7f15cb6e 1452 ]
6ee4fb7d 1453
b4fd763c
AG
1454 var clients = [
1455 {
1456 name: "Bitcoin Core",
1457 onSelect: function() {
1458 DOM.bip32path.val("m/0'/0'");
1459 DOM.hardenedAddresses.prop('checked', true);
1460 },
1461 },
1462 {
1463 name: "blockchain.info",
1464 onSelect: function() {
1465 DOM.bip32path.val("m/44'/0'/0'");
1466 DOM.hardenedAddresses.prop('checked', false);
1467 },
1468 },
1469 {
1470 name: "MultiBit HD",
1471 onSelect: function() {
1472 DOM.bip32path.val("m/0'/0");
1473 DOM.hardenedAddresses.prop('checked', false);
1474 },
1475 }
1476 ]
7a995731 1477
ebd8d4e8
IC
1478 init();
1479
1480})();