]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - src/js/index.js
jsbip39 updated to latest version
[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"];
3e0ed16a 6 var seed = null
ebd8d4e8
IC
7 var bip32RootKey = null;
8 var bip32ExtendedKey = null;
1759e5e8 9 var network = 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
IC
15 var showPrivKey = true;
16
c6624d51 17 var entropyChangeTimeoutEvent = null;
ebd8d4e8 18 var phraseChangeTimeoutEvent = null;
efe41586 19 var rootKeyChangedTimeoutEvent = null;
ebd8d4e8
IC
20
21 var DOM = {};
d6cedc94
IC
22 DOM.network = $(".network");
23 DOM.phraseNetwork = $("#network-phrase");
c6624d51
IC
24 DOM.useEntropy = $(".use-entropy");
25 DOM.entropyContainer = $(".entropy-container");
26 DOM.entropy = $(".entropy");
27 DOM.entropyError = $(".entropy-error");
ebd8d4e8 28 DOM.phrase = $(".phrase");
1abcc511 29 DOM.passphrase = $(".passphrase");
c6624d51 30 DOM.generateContainer = $(".generate-container");
ebd8d4e8 31 DOM.generate = $(".generate");
3e0ed16a 32 DOM.seed = $(".seed");
ebd8d4e8
IC
33 DOM.rootKey = $(".root-key");
34 DOM.extendedPrivKey = $(".extended-priv-key");
35 DOM.extendedPubKey = $(".extended-pub-key");
d6cedc94
IC
36 DOM.bip32tab = $("#bip32-tab");
37 DOM.bip44tab = $("#bip44-tab");
38 DOM.bip32panel = $("#bip32");
39 DOM.bip44panel = $("#bip44");
ebd8d4e8
IC
40 DOM.bip32path = $("#bip32-path");
41 DOM.bip44path = $("#bip44-path");
42 DOM.bip44purpose = $("#bip44 .purpose");
43 DOM.bip44coin = $("#bip44 .coin");
44 DOM.bip44account = $("#bip44 .account");
45 DOM.bip44change = $("#bip44 .change");
46 DOM.strength = $(".strength");
146e089e 47 DOM.hardenedAddresses = $(".hardened-addresses");
ebd8d4e8
IC
48 DOM.addresses = $(".addresses");
49 DOM.rowsToAdd = $(".rows-to-add");
50 DOM.more = $(".more");
51 DOM.feedback = $(".feedback");
52 DOM.tab = $(".derivation-type a");
53 DOM.indexToggle = $(".index-toggle");
54 DOM.addressToggle = $(".address-toggle");
1b12b2f5 55 DOM.publicKeyToggle = $(".public-key-toggle");
ebd8d4e8 56 DOM.privateKeyToggle = $(".private-key-toggle");
5ee7bb9e 57 DOM.languages = $(".languages a");
ebd8d4e8 58
ebd8d4e8
IC
59 function init() {
60 // Events
d6cedc94 61 DOM.network.on("change", networkChanged);
c6624d51
IC
62 DOM.useEntropy.on("change", setEntropyVisibility);
63 DOM.entropy.on("input", delayedEntropyChanged);
a19a5498
IC
64 DOM.phrase.on("input", delayedPhraseChanged);
65 DOM.passphrase.on("input", delayedPhraseChanged);
ebd8d4e8
IC
66 DOM.generate.on("click", generateClicked);
67 DOM.more.on("click", showMore);
efe41586
IC
68 DOM.rootKey.on("input", delayedRootKeyChanged);
69 DOM.bip32path.on("input", calcForDerivationPath);
70 DOM.bip44purpose.on("input", calcForDerivationPath);
71 DOM.bip44coin.on("input", calcForDerivationPath);
72 DOM.bip44account.on("input", calcForDerivationPath);
73 DOM.bip44change.on("input", calcForDerivationPath);
74 DOM.tab.on("shown.bs.tab", calcForDerivationPath);
146e089e 75 DOM.hardenedAddresses.on("change", calcForDerivationPath);
ebd8d4e8
IC
76 DOM.indexToggle.on("click", toggleIndexes);
77 DOM.addressToggle.on("click", toggleAddresses);
1b12b2f5 78 DOM.publicKeyToggle.on("click", togglePublicKeys);
ebd8d4e8 79 DOM.privateKeyToggle.on("click", togglePrivateKeys);
5ee7bb9e 80 DOM.languages.on("click", languageChanged);
ebd8d4e8
IC
81 disableForms();
82 hidePending();
83 hideValidationError();
7f15cb6e 84 populateNetworkSelect();
ebd8d4e8
IC
85 }
86
87 // Event handlers
88
d6cedc94 89 function networkChanged(e) {
54563907
IC
90 var networkIndex = e.target.value;
91 networks[networkIndex].onSelect();
92 if (seed != null) {
93 phraseChanged();
94 }
95 else {
96 rootKeyChanged();
97 }
d6cedc94
IC
98 }
99
c6624d51
IC
100 function setEntropyVisibility() {
101 if (isUsingOwnEntropy()) {
102 DOM.entropyContainer.removeClass("hidden");
103 DOM.generateContainer.addClass("hidden");
104 DOM.phrase.prop("readonly", true);
105 DOM.entropy.focus();
106 entropyChanged();
107 }
108 else {
109 DOM.entropyContainer.addClass("hidden");
110 DOM.generateContainer.removeClass("hidden");
111 DOM.phrase.prop("readonly", false);
112 }
113 }
114
ebd8d4e8
IC
115 function delayedPhraseChanged() {
116 hideValidationError();
117 showPending();
118 if (phraseChangeTimeoutEvent != null) {
119 clearTimeout(phraseChangeTimeoutEvent);
120 }
121 phraseChangeTimeoutEvent = setTimeout(phraseChanged, 400);
122 }
123
124 function phraseChanged() {
125 showPending();
126 hideValidationError();
5ee7bb9e 127 setMnemonicLanguage();
ebd8d4e8
IC
128 // Get the mnemonic phrase
129 var phrase = DOM.phrase.val();
130 var errorText = findPhraseErrors(phrase);
131 if (errorText) {
132 showValidationError(errorText);
133 return;
134 }
efe41586
IC
135 // Calculate and display
136 var passphrase = DOM.passphrase.val();
137 calcBip32RootKeyFromSeed(phrase, passphrase);
138 calcForDerivationPath();
139 hidePending();
140 }
141
c6624d51
IC
142 function delayedEntropyChanged() {
143 hideValidationError();
144 showPending();
145 if (entropyChangeTimeoutEvent != null) {
146 clearTimeout(entropyChangeTimeoutEvent);
147 }
148 entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
149 }
150
151 function entropyChanged() {
152 setMnemonicFromEntropy();
153 phraseChanged();
154 }
155
efe41586
IC
156 function delayedRootKeyChanged() {
157 // Warn if there is an existing mnemonic or passphrase.
158 if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
159 if (!confirm("This will clear existing mnemonic and passphrase")) {
160 DOM.rootKey.val(bip32RootKey);
161 return
162 }
163 }
164 hideValidationError();
165 showPending();
166 // Clear existing mnemonic and passphrase
167 DOM.phrase.val("");
168 DOM.passphrase.val("");
169 seed = null;
170 if (rootKeyChangedTimeoutEvent != null) {
171 clearTimeout(rootKeyChangedTimeoutEvent);
172 }
173 rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400);
174 }
175
176 function rootKeyChanged() {
177 showPending();
178 hideValidationError();
179 // Validate the root key TODO
180 var rootKeyBase58 = DOM.rootKey.val();
181 var errorText = validateRootKey(rootKeyBase58);
182 if (errorText) {
183 showValidationError(errorText);
184 return;
185 }
186 // Calculate and display
187 calcBip32RootKeyFromBase58(rootKeyBase58);
188 calcForDerivationPath();
189 hidePending();
190 }
191
192 function calcForDerivationPath() {
193 showPending();
194 hideValidationError();
ebd8d4e8 195 // Get the derivation path
38523d36
IC
196 var derivationPath = getDerivationPath();
197 var errorText = findDerivationPathErrors(derivationPath);
ebd8d4e8
IC
198 if (errorText) {
199 showValidationError(errorText);
200 return;
201 }
efe41586 202 calcBip32ExtendedKey(derivationPath);
ebd8d4e8
IC
203 displayBip32Info();
204 hidePending();
205 }
206
207 function generateClicked() {
c6624d51
IC
208 if (isUsingOwnEntropy()) {
209 return;
210 }
ebd8d4e8
IC
211 clearDisplay();
212 showPending();
213 setTimeout(function() {
5ee7bb9e 214 setMnemonicLanguage();
ebd8d4e8
IC
215 var phrase = generateRandomPhrase();
216 if (!phrase) {
217 return;
218 }
219 phraseChanged();
220 }, 50);
221 }
222
5ee7bb9e
IC
223 function languageChanged() {
224 setTimeout(function() {
225 setMnemonicLanguage();
226 if (DOM.phrase.val().length > 0) {
227 var newPhrase = convertPhraseToNewLanguage();
228 DOM.phrase.val(newPhrase);
229 phraseChanged();
230 }
231 else {
232 DOM.generate.trigger("click");
233 }
234 }, 50);
235 }
236
ebd8d4e8 237 function toggleIndexes() {
700901cd 238 showIndex = !showIndex;
ebd8d4e8
IC
239 $("td.index span").toggleClass("invisible");
240 }
241
242 function toggleAddresses() {
700901cd 243 showAddress = !showAddress;
ebd8d4e8
IC
244 $("td.address span").toggleClass("invisible");
245 }
246
1b12b2f5
IC
247 function togglePublicKeys() {
248 showPubKey = !showPubKey;
249 $("td.pubkey span").toggleClass("invisible");
250 }
251
ebd8d4e8 252 function togglePrivateKeys() {
700901cd 253 showPrivKey = !showPrivKey;
ebd8d4e8
IC
254 $("td.privkey span").toggleClass("invisible");
255 }
256
257 // Private methods
258
259 function generateRandomPhrase() {
260 if (!hasStrongRandom()) {
261 var errorText = "This browser does not support strong randomness";
262 showValidationError(errorText);
263 return;
264 }
265 var numWords = parseInt(DOM.strength.val());
ebd8d4e8
IC
266 var strength = numWords / 3 * 32;
267 var words = mnemonic.generate(strength);
268 DOM.phrase.val(words);
269 return words;
270 }
271
efe41586 272 function calcBip32RootKeyFromSeed(phrase, passphrase) {
3e0ed16a 273 seed = mnemonic.toSeed(phrase, passphrase);
1759e5e8 274 bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network);
efe41586
IC
275 }
276
277 function calcBip32RootKeyFromBase58(rootKeyBase58) {
3821c0d3 278 bip32RootKey = bitcoin.HDNode.fromBase58(rootKeyBase58, network);
efe41586
IC
279 }
280
281 function calcBip32ExtendedKey(path) {
ebd8d4e8
IC
282 bip32ExtendedKey = bip32RootKey;
283 // Derive the key from the path
284 var pathBits = path.split("/");
285 for (var i=0; i<pathBits.length; i++) {
286 var bit = pathBits[i];
287 var index = parseInt(bit);
288 if (isNaN(index)) {
289 continue;
290 }
291 var hardened = bit[bit.length-1] == "'";
292 if (hardened) {
293 bip32ExtendedKey = bip32ExtendedKey.deriveHardened(index);
294 }
295 else {
296 bip32ExtendedKey = bip32ExtendedKey.derive(index);
297 }
298 }
299 }
300
301 function showValidationError(errorText) {
302 DOM.feedback
303 .text(errorText)
304 .show();
305 }
306
307 function hideValidationError() {
308 DOM.feedback
309 .text("")
310 .hide();
311 }
312
313 function findPhraseErrors(phrase) {
314 // TODO make this right
315 // Preprocess the words
783981de 316 phrase = mnemonic.normalizeString(phrase);
5ee7bb9e 317 var words = phraseToWordArray(phrase);
563e401a 318 // Check each word
5ee7bb9e
IC
319 for (var i=0; i<words.length; i++) {
320 var word = words[i];
321 var language = getLanguage();
322 if (WORDLISTS[language].indexOf(word) == -1) {
563e401a
IC
323 console.log("Finding closest match to " + word);
324 var nearestWord = findNearestWord(word);
325 return word + " not in wordlist, did you mean " + nearestWord + "?";
326 }
327 }
ebd8d4e8 328 // Check the words are valid
5ee7bb9e 329 var properPhrase = wordArrayToPhrase(words);
ebd8d4e8
IC
330 var isValid = mnemonic.check(properPhrase);
331 if (!isValid) {
332 return "Invalid mnemonic";
333 }
334 return false;
335 }
336
efe41586
IC
337 function validateRootKey(rootKeyBase58) {
338 try {
339 bitcoin.HDNode.fromBase58(rootKeyBase58);
340 }
341 catch (e) {
342 return "Invalid root key";
343 }
344 return "";
345 }
346
38523d36
IC
347 function getDerivationPath() {
348 if (DOM.bip44tab.hasClass("active")) {
349 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
350 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
351 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
352 var change = parseIntNoNaN(DOM.bip44change.val(), 0);
353 var path = "m/";
354 path += purpose + "'/";
355 path += coin + "'/";
356 path += account + "'/";
357 path += change;
358 DOM.bip44path.val(path);
359 var derivationPath = DOM.bip44path.val();
360 console.log("Using derivation path from BIP44 tab: " + derivationPath);
361 return derivationPath;
362 }
363 else if (DOM.bip32tab.hasClass("active")) {
364 var derivationPath = DOM.bip32path.val();
365 console.log("Using derivation path from BIP32 tab: " + derivationPath);
366 return derivationPath;
367 }
368 else {
369 console.log("Unknown derivation path");
370 }
371 }
372
ebd8d4e8 373 function findDerivationPathErrors(path) {
30c9e79d
IC
374 // TODO is not perfect but is better than nothing
375 // Inspired by
376 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#test-vectors
377 // and
378 // https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#extended-keys
379 var maxDepth = 255; // TODO verify this!!
380 var maxIndexValue = Math.pow(2, 31); // TODO verify this!!
381 if (path[0] != "m") {
382 return "First character must be 'm'";
383 }
384 if (path.length > 1) {
385 if (path[1] != "/") {
386 return "Separator must be '/'";
387 }
388 var indexes = path.split("/");
389 if (indexes.length > maxDepth) {
390 return "Derivation depth is " + indexes.length + ", must be less than " + maxDepth;
391 }
392 for (var depth = 1; depth<indexes.length; depth++) {
393 var index = indexes[depth];
394 var invalidChars = index.replace(/^[0-9]+'?$/g, "")
395 if (invalidChars.length > 0) {
396 return "Invalid characters " + invalidChars + " found at depth " + depth;
397 }
398 var indexValue = parseInt(index.replace("'", ""));
399 if (isNaN(depth)) {
400 return "Invalid number at depth " + depth;
401 }
402 if (indexValue > maxIndexValue) {
403 return "Value of " + indexValue + " at depth " + depth + " must be less than " + maxIndexValue;
404 }
405 }
406 }
ebd8d4e8
IC
407 return false;
408 }
409
410 function displayBip32Info() {
411 // Display the key
3e0ed16a 412 DOM.seed.val(seed);
ebd8d4e8
IC
413 var rootKey = bip32RootKey.toBase58();
414 DOM.rootKey.val(rootKey);
415 var extendedPrivKey = bip32ExtendedKey.toBase58();
416 DOM.extendedPrivKey.val(extendedPrivKey);
417 var extendedPubKey = bip32ExtendedKey.toBase58(false);
418 DOM.extendedPubKey.val(extendedPubKey);
419 // Display the addresses and privkeys
420 clearAddressesList();
421 displayAddresses(0, 20);
422 }
423
424 function displayAddresses(start, total) {
425 for (var i=0; i<total; i++) {
a8c45487
IC
426 var index = i + start;
427 new TableRow(index);
ebd8d4e8
IC
428 }
429 }
430
a8c45487
IC
431 function TableRow(index) {
432
146e089e
IC
433 var useHardenedAddresses = DOM.hardenedAddresses.prop("checked");
434
a8c45487
IC
435 function init() {
436 calculateValues();
437 }
438
439 function calculateValues() {
440 setTimeout(function() {
146e089e
IC
441 var key = "";
442 if (useHardenedAddresses) {
443 key = bip32ExtendedKey.deriveHardened(index);
444 }
445 else {
446 key = bip32ExtendedKey.derive(index);
447 }
a8c45487
IC
448 var address = key.getAddress().toString();
449 var privkey = key.privKey.toWIF(network);
1b12b2f5 450 var pubkey = key.pubKey.toHex();
38523d36 451 var indexText = getDerivationPath() + "/" + index;
146e089e
IC
452 if (useHardenedAddresses) {
453 indexText = indexText + "'";
454 }
1b12b2f5 455 addAddressToList(indexText, address, pubkey, privkey);
a8c45487
IC
456 }, 50)
457 }
458
459 init();
460
461 }
462
ebd8d4e8
IC
463 function showMore() {
464 var start = DOM.addresses.children().length;
465 var rowsToAdd = parseInt(DOM.rowsToAdd.val());
466 if (isNaN(rowsToAdd)) {
467 rowsToAdd = 20;
468 DOM.rowsToAdd.val("20");
469 }
470 if (rowsToAdd > 200) {
471 var msg = "Generating " + rowsToAdd + " rows could take a while. ";
472 msg += "Do you want to continue?";
473 if (!confirm(msg)) {
474 return;
475 }
476 }
ebd8d4e8 477 displayAddresses(start, rowsToAdd);
ebd8d4e8
IC
478 }
479
480 function clearDisplay() {
481 clearAddressesList();
482 clearKey();
483 hideValidationError();
484 }
485
486 function clearAddressesList() {
487 DOM.addresses.empty();
488 }
489
490 function clearKey() {
491 DOM.rootKey.val("");
492 DOM.extendedPrivKey.val("");
493 DOM.extendedPubKey.val("");
494 }
495
1b12b2f5 496 function addAddressToList(indexText, address, pubkey, privkey) {
ebd8d4e8 497 var row = $(addressRowTemplate.html());
700901cd
IC
498 // Elements
499 var indexCell = row.find(".index span");
500 var addressCell = row.find(".address span");
1b12b2f5 501 var pubkeyCell = row.find(".pubkey span");
700901cd
IC
502 var privkeyCell = row.find(".privkey span");
503 // Content
ae30fed8 504 indexCell.text(indexText);
700901cd 505 addressCell.text(address);
1b12b2f5 506 pubkeyCell.text(pubkey);
700901cd
IC
507 privkeyCell.text(privkey);
508 // Visibility
509 if (!showIndex) {
510 indexCell.addClass("invisible");
511 }
512 if (!showAddress) {
513 addressCell.addClass("invisible");
514 }
1b12b2f5
IC
515 if (!showPubKey) {
516 pubkeyCell.addClass("invisible");
517 }
700901cd 518 if (!showPrivKey) {
6d628db7 519 privkeyCell.addClass("invisible");
700901cd 520 }
ebd8d4e8
IC
521 DOM.addresses.append(row);
522 }
523
524 function hasStrongRandom() {
525 return 'crypto' in window && window['crypto'] !== null;
526 }
527
528 function disableForms() {
529 $("form").on("submit", function(e) {
530 e.preventDefault();
531 });
532 }
533
ebd8d4e8
IC
534 function parseIntNoNaN(val, defaultVal) {
535 var v = parseInt(val);
536 if (isNaN(v)) {
537 return defaultVal;
538 }
539 return v;
540 }
541
542 function showPending() {
543 DOM.feedback
544 .text("Calculating...")
545 .show();
546 }
547
563e401a 548 function findNearestWord(word) {
5ee7bb9e
IC
549 var language = getLanguage();
550 var words = WORDLISTS[language];
563e401a
IC
551 var minDistance = 99;
552 var closestWord = words[0];
553 for (var i=0; i<words.length; i++) {
554 var comparedTo = words[i];
555 var distance = Levenshtein.get(word, comparedTo);
556 if (distance < minDistance) {
557 closestWord = comparedTo;
558 minDistance = distance;
559 }
560 }
561 return closestWord;
562 }
563
ebd8d4e8
IC
564 function hidePending() {
565 DOM.feedback
566 .text("")
567 .hide();
568 }
569
7f15cb6e
IC
570 function populateNetworkSelect() {
571 for (var i=0; i<networks.length; i++) {
572 var network = networks[i];
573 var option = $("<option>");
574 option.attr("value", i);
575 option.text(network.name);
576 DOM.phraseNetwork.append(option);
577 }
578 }
579
5ee7bb9e
IC
580 function getLanguage() {
581 var defaultLanguage = "english";
582 // Try to get from existing phrase
583 var language = getLanguageFromPhrase();
584 // Try to get from url if not from phrase
585 if (language.length == 0) {
586 language = getLanguageFromUrl();
587 }
588 // Default to English if no other option
589 if (language.length == 0) {
590 language = defaultLanguage;
591 }
592 return language;
593 }
594
595 function getLanguageFromPhrase(phrase) {
596 // Check if how many words from existing phrase match a language.
597 var language = "";
598 if (!phrase) {
599 phrase = DOM.phrase.val();
600 }
601 if (phrase.length > 0) {
602 var words = phraseToWordArray(phrase);
603 var languageMatches = {};
604 for (l in WORDLISTS) {
605 // Track how many words match in this language
606 languageMatches[l] = 0;
607 for (var i=0; i<words.length; i++) {
608 var wordInLanguage = WORDLISTS[l].indexOf(words[i]) > -1;
609 if (wordInLanguage) {
610 languageMatches[l]++;
611 }
612 }
613 // Find languages with most word matches.
614 // This is made difficult due to commonalities between Chinese
615 // simplified vs traditional.
616 var mostMatches = 0;
617 var mostMatchedLanguages = [];
618 for (var l in languageMatches) {
619 var numMatches = languageMatches[l];
620 if (numMatches > mostMatches) {
621 mostMatches = numMatches;
622 mostMatchedLanguages = [l];
623 }
624 else if (numMatches == mostMatches) {
625 mostMatchedLanguages.push(l);
626 }
627 }
628 }
629 if (mostMatchedLanguages.length > 0) {
630 // Use first language and warn if multiple detected
631 language = mostMatchedLanguages[0];
632 if (mostMatchedLanguages.length > 1) {
633 console.warn("Multiple possible languages");
634 console.warn(mostMatchedLanguages);
635 }
636 }
637 }
638 return language;
639 }
640
641 function getLanguageFromUrl() {
c6624d51
IC
642 for (var language in WORDLISTS) {
643 if (window.location.hash.indexOf(language) > -1) {
644 return language;
645 }
646 }
647 return "";
5ee7bb9e
IC
648 }
649
650 function setMnemonicLanguage() {
651 var language = getLanguage();
652 // Load the bip39 mnemonic generator for this language if required
653 if (!(language in mnemonics)) {
654 mnemonics[language] = new Mnemonic(language);
655 }
656 mnemonic = mnemonics[language];
657 }
658
659 function convertPhraseToNewLanguage() {
660 var oldLanguage = getLanguageFromPhrase();
661 var newLanguage = getLanguageFromUrl();
662 var oldPhrase = DOM.phrase.val();
663 var oldWords = phraseToWordArray(oldPhrase);
664 var newWords = [];
665 for (var i=0; i<oldWords.length; i++) {
666 var oldWord = oldWords[i];
667 var index = WORDLISTS[oldLanguage].indexOf(oldWord);
668 var newWord = WORDLISTS[newLanguage][index];
669 newWords.push(newWord);
670 }
671 newPhrase = wordArrayToPhrase(newWords);
672 return newPhrase;
673 }
674
675 // TODO look at jsbip39 - mnemonic.splitWords
676 function phraseToWordArray(phrase) {
677 var words = phrase.split(/\s/g);
678 var noBlanks = [];
679 for (var i=0; i<words.length; i++) {
680 var word = words[i];
681 if (word.length > 0) {
682 noBlanks.push(word);
683 }
684 }
685 return noBlanks;
686 }
687
688 // TODO look at jsbip39 - mnemonic.joinWords
689 function wordArrayToPhrase(words) {
690 var phrase = words.join(" ");
691 var language = getLanguageFromPhrase(phrase);
692 if (language == "japanese") {
693 phrase = words.join("\u3000");
694 }
695 return phrase;
696 }
697
c6624d51
IC
698 function isUsingOwnEntropy() {
699 return DOM.useEntropy.prop("checked");
700 }
701
702 function setMnemonicFromEntropy() {
703 hideEntropyError();
704 // Work out minimum base for entropy
705 var entropyStr = DOM.entropy.val();
706 var entropy = Entropy.fromString(entropyStr);
707 if (entropy.hexStr.length == 0) {
708 return;
709 }
710 // Show entropy details
711 var extraBits = 32 - (entropy.binaryStr.length % 32);
712 var extraChars = Math.ceil(extraBits * Math.log(2) / Math.log(entropy.base.asInt));
713 var strength = "an extremely weak";
714 if (entropy.hexStr.length >= 8) {
715 strength = "a very weak";
716 }
717 if (entropy.hexStr.length >= 12) {
718 strength = "a weak";
719 }
720 if (entropy.hexStr.length >= 24) {
721 strength = "a strong";
722 }
723 if (entropy.hexStr.length >= 32) {
724 strength = "a very strong";
725 }
726 if (entropy.hexStr.length >= 40) {
727 strength = "an extremely strong";
728 }
729 if (entropy.hexStr.length >=48) {
730 strength = "an even stronger"
731 }
732 var msg = "Have " + entropy.binaryStr.length + " bits of entropy, " + extraChars + " more " + entropy.base.str + " chars required to generate " + strength + " mnemonic: " + entropy.cleanStr;
733 showEntropyError(msg);
734 // Discard trailing entropy
735 var hexStr = entropy.hexStr.substring(0, Math.floor(entropy.hexStr.length / 8) * 8);
736 // Convert entropy string to numeric array
737 var entropyArr = [];
738 for (var i=0; i<hexStr.length / 2; i++) {
739 var entropyByte = parseInt(hexStr[i*2].concat(hexStr[i*2+1]), 16);
740 entropyArr.push(entropyByte)
741 }
742 // Convert entropy array to mnemonic
743 var phrase = mnemonic.toMnemonic(entropyArr);
744 // Set the mnemonic in the UI
745 DOM.phrase.val(phrase);
746 }
747
748 function hideEntropyError() {
749 DOM.entropyError.addClass("hidden");
750 }
751
752 function showEntropyError(msg) {
753 DOM.entropyError.text(msg);
754 DOM.entropyError.removeClass("hidden");
755 }
756
7f15cb6e
IC
757 var networks = [
758 {
7a995731
IC
759 name: "Bitcoin",
760 onSelect: function() {
1759e5e8 761 network = bitcoin.networks.bitcoin;
7a995731 762 DOM.bip44coin.val(0);
7a995731
IC
763 },
764 },
7f15cb6e 765 {
7a995731
IC
766 name: "Bitcoin Testnet",
767 onSelect: function() {
1759e5e8 768 network = bitcoin.networks.testnet;
7a995731 769 DOM.bip44coin.val(1);
7a995731
IC
770 },
771 },
7f15cb6e 772 {
7a995731
IC
773 name: "Litecoin",
774 onSelect: function() {
1759e5e8 775 network = bitcoin.networks.litecoin;
7a995731
IC
776 DOM.bip44coin.val(2);
777 },
778 },
7f15cb6e 779 {
7a995731
IC
780 name: "Dogecoin",
781 onSelect: function() {
1759e5e8 782 network = bitcoin.networks.dogecoin;
7a995731
IC
783 DOM.bip44coin.val(3);
784 },
785 },
e3a9508c
IC
786 {
787 name: "ShadowCash",
788 onSelect: function() {
789 network = bitcoin.networks.shadow;
790 DOM.bip44coin.val(35);
791 },
792 },
793 {
794 name: "ShadowCash Testnet",
795 onSelect: function() {
796 network = bitcoin.networks.shadowtn;
797 DOM.bip44coin.val(1);
798 },
799 },
a3baa26e
IC
800 {
801 name: "Viacoin",
802 onSelect: function() {
803 network = bitcoin.networks.viacoin;
804 DOM.bip44coin.val(14);
805 },
806 },
807 {
808 name: "Viacoin Testnet",
809 onSelect: function() {
810 network = bitcoin.networks.viacointestnet;
811 DOM.bip44coin.val(1);
812 },
813 },
814 {
815 name: "Jumbucks",
816 onSelect: function() {
817 network = bitcoin.networks.jumbucks;
818 DOM.bip44coin.val(26);
819 },
820 },
5c434a8a
CM
821 {
822 name: "CLAM",
823 onSelect: function() {
824 network = bitcoin.networks.clam;
825 DOM.bip44coin.val(23);
826 },
827 },
82f91834
DG
828 {
829 name: "DASH",
830 onSelect: function() {
831 network = bitcoin.networks.dash;
832 DOM.bip44coin.val(5);
833 },
834 },
07ac4350 835 {
836 name: "Namecoin",
837 onSelect: function() {
838 network = bitcoin.networks.namecoin;
839 DOM.bip44coin.val(7);
840 },
841 },
842 {
843 name: "Peercoin",
844 onSelect: function() {
845 network = bitcoin.networks.peercoin;
846 DOM.bip44coin.val(6);
847 },
848 },
7f15cb6e 849 ]
7a995731 850
ebd8d4e8
IC
851 init();
852
853})();