+ // Show the word indexes
+ showWordIndexes();
+ // Show the checksum
+ showChecksum();
+ }
+
+ function clearEntropyFeedback() {
+ DOM.entropyCrackTime.text("...");
+ DOM.entropyType.text("");
+ DOM.entropyWordCount.text("0");
+ DOM.entropyEventCount.text("0");
+ DOM.entropyBitsPerEvent.text("0");
+ DOM.entropyBits.text("0");
+ DOM.entropyFiltered.html(" ");
+ DOM.entropyBinary.html(" ");
+ }
+
+ function showEntropyFeedback(entropy) {
+ var numberOfBits = entropy.binaryStr.length;
+ var timeToCrack = "unknown";
+ try {
+ var z = zxcvbn(entropy.base.parts.join(""));
+ timeToCrack = z.crack_times_display.offline_fast_hashing_1e10_per_second;
+ if (z.feedback.warning != "") {
+ timeToCrack = timeToCrack + " - " + z.feedback.warning;
+ };
+ }
+ catch (e) {
+ console.log("Error detecting entropy strength with zxcvbn:");
+ console.log(e);
+ }
+ var entropyTypeStr = getEntropyTypeStr(entropy);
+ var wordCount = Math.floor(numberOfBits / 32) * 3;
+ var bitsPerEvent = entropy.bitsPerEvent.toFixed(2);
+ var spacedBinaryStr = addSpacesEveryElevenBits(entropy.binaryStr);
+ DOM.entropyFiltered.html(entropy.cleanHtml);
+ DOM.entropyType.text(entropyTypeStr);
+ DOM.entropyCrackTime.text(timeToCrack);
+ DOM.entropyEventCount.text(entropy.base.ints.length);
+ DOM.entropyBits.text(numberOfBits);
+ DOM.entropyWordCount.text(wordCount);
+ DOM.entropyBinary.text(spacedBinaryStr);
+ DOM.entropyBitsPerEvent.text(bitsPerEvent);
+ // detect and warn of filtering
+ var rawNoSpaces = DOM.entropy.val().replace(/\s/g, "");
+ var cleanNoSpaces = entropy.cleanStr.replace(/\s/g, "");
+ var isFiltered = rawNoSpaces.length != cleanNoSpaces.length;
+ if (isFiltered) {
+ DOM.entropyFilterWarning.removeClass('hidden');
+ }
+ else {
+ DOM.entropyFilterWarning.addClass('hidden');
+ }
+ }
+
+ function getEntropyTypeStr(entropy) {
+ var typeStr = entropy.base.str;
+ // Add some detail if these are cards
+ if (entropy.base.asInt == 52) {
+ var cardDetail = []; // array of message strings
+ // Detect duplicates
+ var dupes = [];
+ var dupeTracker = {};
+ for (var i=0; i<entropy.base.parts.length; i++) {
+ var card = entropy.base.parts[i];
+ var cardUpper = card.toUpperCase();
+ if (cardUpper in dupeTracker) {
+ dupes.push(card);
+ }
+ dupeTracker[cardUpper] = true;
+ }
+ if (dupes.length > 0) {
+ var dupeWord = "duplicates";
+ if (dupes.length == 1) {
+ dupeWord = "duplicate";
+ }
+ var msg = dupes.length + " " + dupeWord + ": " + dupes.slice(0,3).join(" ");
+ if (dupes.length > 3) {
+ msg += "...";
+ }
+ cardDetail.push(msg);
+ }
+ // Detect full deck
+ var uniqueCards = [];
+ for (var uniqueCard in dupeTracker) {
+ uniqueCards.push(uniqueCard);
+ }
+ if (uniqueCards.length == 52) {
+ cardDetail.unshift("full deck");
+ }
+ // Detect missing cards
+ var values = "A23456789TJQK";
+ var suits = "CDHS";
+ var missingCards = [];
+ for (var i=0; i<suits.length; i++) {
+ for (var j=0; j<values.length; j++) {
+ var card = values[j] + suits[i];
+ if (!(card in dupeTracker)) {
+ missingCards.push(card);
+ }
+ }
+ }
+ // Display missing cards if six or less, ie clearly going for full deck
+ if (missingCards.length > 0 && missingCards.length <= 6) {
+ var msg = missingCards.length + " missing: " + missingCards.slice(0,3).join(" ");
+ if (missingCards.length > 3) {
+ msg += "...";
+ }
+ cardDetail.push(msg);
+ }
+ // Add card details to typeStr
+ if (cardDetail.length > 0) {
+ typeStr += " (" + cardDetail.join(", ") + ")";
+ }
+ }
+ return typeStr;
+ }
+
+ function setQrEvents(els) {
+ els.on("mouseenter", createQr);
+ els.on("mouseleave", destroyQr);
+ els.on("click", toggleQr);
+ }
+
+ function createQr(e) {
+ var content = e.target.textContent || e.target.value;
+ if (content) {
+ var qrEl = kjua({
+ text: content,
+ render: "canvas",
+ size: 310,
+ ecLevel: 'H',
+ });
+ DOM.qrImage.append(qrEl);
+ if (!showQr) {
+ DOM.qrHider.addClass("hidden");
+ }
+ else {
+ DOM.qrHider.removeClass("hidden");
+ }
+ DOM.qrContainer.removeClass("hidden");
+ }
+ }
+
+ function destroyQr() {
+ DOM.qrImage.text("");
+ DOM.qrContainer.addClass("hidden");
+ }
+
+ function toggleQr() {
+ showQr = !showQr;
+ DOM.qrHider.toggleClass("hidden");
+ DOM.qrHint.toggleClass("hidden");
+ }
+
+ function bip44TabSelected() {
+ return DOM.bip44tab.hasClass("active");
+ }
+
+ function bip32TabSelected() {
+ return DOM.bip32tab.hasClass("active");
+ }
+
+ function networkIsEthereum() {
+ var name = networks[DOM.network.val()].name;
+ return (name == "ETH - Ethereum")
+ || (name == "ETC - Ethereum Classic")
+ || (name == "PIRL - Pirl")
+ || (name == "MIX - MIX")
+ || (name == "MUSIC - Musicoin")
+ || (name == "POA - Poa")
+ || (name == "EXP - Expanse")
+ || (name == "CLO - Callisto");
+ }
+
+ function networkHasSegwit() {
+ var n = network;
+ if ("baseNetwork" in network) {
+ n = bitcoinjs.bitcoin.networks[network.baseNetwork];
+ }
+ // check if only p2wpkh params are required
+ if (p2wpkhSelected()) {
+ return "p2wpkh" in n;
+ }
+ // check if only p2wpkh-in-p2sh params are required
+ else if (p2wpkhInP2shSelected()) {
+ return "p2wpkhInP2sh" in n;
+ }
+ // require both if it's unclear which params are required
+ return "p2wpkh" in n && "p2wpkhInP2sh" in n;
+ }
+
+ function bip49TabSelected() {
+ return DOM.bip49tab.hasClass("active");
+ }
+
+ function bip84TabSelected() {
+ return DOM.bip84tab.hasClass("active");
+ }
+
+ function bip141TabSelected() {
+ return DOM.bip141tab.hasClass("active");
+ }
+
+ function setBip44ChangeValue() {
+ if (isDefaultBip44ChangeValue) {
+ if (networkIsEthereum()) {
+ DOM.bip44change.val("");
+ } else {
+ DOM.bip44change.val(0);
+ }
+ }
+ }
+
+ function modifiedDefaultBip44ChangeValue() {
+ isDefaultBip44ChangeValue = false;
+ }
+
+ function resetDefaultBip44ChangeValue() {
+ isDefaultBip44ChangeValue = true;
+ setBip44ChangeValue();
+ }
+
+ function setHdCoin(coinValue) {
+ DOM.bip44coin.val(coinValue);
+ DOM.bip49coin.val(coinValue);
+ DOM.bip84coin.val(coinValue);
+ setBip44ChangeValue();
+ }
+
+ function showSegwitAvailable() {
+ DOM.bip49unavailable.addClass("hidden");
+ DOM.bip49available.removeClass("hidden");
+ DOM.bip84unavailable.addClass("hidden");
+ DOM.bip84available.removeClass("hidden");
+ DOM.bip141unavailable.addClass("hidden");
+ DOM.bip141available.removeClass("hidden");
+ }
+
+ function showSegwitUnavailable() {
+ DOM.bip49available.addClass("hidden");
+ DOM.bip49unavailable.removeClass("hidden");
+ DOM.bip84available.addClass("hidden");
+ DOM.bip84unavailable.removeClass("hidden");
+ DOM.bip141available.addClass("hidden");
+ DOM.bip141unavailable.removeClass("hidden");
+ }
+
+ function adjustNetworkForSegwit() {
+ // If segwit is selected the xpub/xprv prefixes need to be adjusted
+ // to avoid accidentally importing BIP49 xpub to BIP44 watch only
+ // wallet.
+ // See https://github.com/iancoleman/bip39/issues/125
+ var segwitNetworks = null;
+ // if a segwit network is alread selected, need to use base network to
+ // look up new parameters
+ if ("baseNetwork" in network) {
+ network = bitcoinjs.bitcoin.networks[network.baseNetwork];
+ }
+ // choose the right segwit params
+ if (p2wpkhSelected() && "p2wpkh" in network) {
+ network = network.p2wpkh;
+ }
+ else if (p2wpkhInP2shSelected() && "p2wpkhInP2sh" in network) {
+ network = network.p2wpkhInP2sh;
+ }
+ }
+
+ function lastIndexInTable() {
+ var pathText = DOM.addresses.find(".index").last().text();
+ var pathBits = pathText.split("/");
+ var lastBit = pathBits[pathBits.length-1];
+ var lastBitClean = lastBit.replace("'", "");
+ return parseInt(lastBitClean);
+ }
+
+ function uint8ArrayToHex(a) {
+ var s = ""
+ for (var i=0; i<a.length; i++) {
+ var h = a[i].toString(16);
+ while (h.length < 2) {
+ h = "0" + h;
+ }
+ s = s + h;
+ }
+ return s;
+ }
+
+ function showWordIndexes() {
+ var phrase = DOM.phrase.val();
+ var words = phraseToWordArray(phrase);
+ var wordIndexes = [];
+ var language = getLanguage();
+ for (var i=0; i<words.length; i++) {
+ var word = words[i];
+ var wordIndex = WORDLISTS[language].indexOf(word);
+ wordIndexes.push(wordIndex);
+ }
+ var wordIndexesStr = wordIndexes.join(", ");
+ DOM.entropyWordIndexes.text(wordIndexesStr);
+ }
+
+ function showChecksum() {
+ var phrase = DOM.phrase.val();
+ var words = phraseToWordArray(phrase);
+ var checksumBitlength = words.length / 3;
+ var checksum = "";
+ var binaryStr = "";
+ var language = getLanguage();
+ for (var i=words.length-1; i>=0; i--) {
+ var word = words[i];
+ var wordIndex = WORDLISTS[language].indexOf(word);
+ var wordBinary = wordIndex.toString(2);
+ while (wordBinary.length < 11) {
+ wordBinary = "0" + wordBinary;
+ }
+ var binaryStr = wordBinary + binaryStr;
+ if (binaryStr.length >= checksumBitlength) {
+ var start = binaryStr.length - checksumBitlength;
+ var end = binaryStr.length;
+ checksum = binaryStr.substring(start, end);
+ // add spaces so the last group is 11 bits, not the first
+ checksum = checksum.split("").reverse().join("")
+ checksum = addSpacesEveryElevenBits(checksum);
+ checksum = checksum.split("").reverse().join("")
+ break;
+ }
+ }
+ DOM.entropyChecksum.text(checksum);