var showIndex = true;
var showAddress = true;
+ var showPubKey = true;
var showPrivKey = true;
+ var entropyChangeTimeoutEvent = null;
var phraseChangeTimeoutEvent = null;
var rootKeyChangedTimeoutEvent = null;
var DOM = {};
DOM.network = $(".network");
DOM.phraseNetwork = $("#network-phrase");
+ DOM.useEntropy = $(".use-entropy");
+ DOM.entropyContainer = $(".entropy-container");
+ DOM.entropy = $(".entropy");
+ DOM.entropyError = $(".entropy-error");
DOM.phrase = $(".phrase");
DOM.passphrase = $(".passphrase");
+ DOM.generateContainer = $(".generate-container");
DOM.generate = $(".generate");
DOM.seed = $(".seed");
DOM.rootKey = $(".root-key");
DOM.tab = $(".derivation-type a");
DOM.indexToggle = $(".index-toggle");
DOM.addressToggle = $(".address-toggle");
+ DOM.publicKeyToggle = $(".public-key-toggle");
DOM.privateKeyToggle = $(".private-key-toggle");
DOM.languages = $(".languages a");
function init() {
// Events
DOM.network.on("change", networkChanged);
+ DOM.useEntropy.on("change", setEntropyVisibility);
+ DOM.entropy.on("input", delayedEntropyChanged);
DOM.phrase.on("input", delayedPhraseChanged);
DOM.passphrase.on("input", delayedPhraseChanged);
DOM.generate.on("click", generateClicked);
DOM.hardenedAddresses.on("change", calcForDerivationPath);
DOM.indexToggle.on("click", toggleIndexes);
DOM.addressToggle.on("click", toggleAddresses);
+ DOM.publicKeyToggle.on("click", togglePublicKeys);
DOM.privateKeyToggle.on("click", togglePrivateKeys);
DOM.languages.on("click", languageChanged);
disableForms();
}
}
+ function setEntropyVisibility() {
+ if (isUsingOwnEntropy()) {
+ DOM.entropyContainer.removeClass("hidden");
+ DOM.generateContainer.addClass("hidden");
+ DOM.phrase.prop("readonly", true);
+ DOM.entropy.focus();
+ entropyChanged();
+ }
+ else {
+ DOM.entropyContainer.addClass("hidden");
+ DOM.generateContainer.removeClass("hidden");
+ DOM.phrase.prop("readonly", false);
+ hidePending();
+ }
+ }
+
function delayedPhraseChanged() {
hideValidationError();
showPending();
hidePending();
}
+ function delayedEntropyChanged() {
+ hideValidationError();
+ showPending();
+ if (entropyChangeTimeoutEvent != null) {
+ clearTimeout(entropyChangeTimeoutEvent);
+ }
+ entropyChangeTimeoutEvent = setTimeout(entropyChanged, 400);
+ }
+
+ function entropyChanged() {
+ // If blank entropy, clear mnemonic, addresses, errors
+ if (DOM.entropy.val().trim().length == 0) {
+ clearDisplay();
+ hideEntropyError();
+ DOM.phrase.val("");
+ showValidationError("Blank entropy");
+ return;
+ }
+ // Get the current phrase to detect changes
+ var phrase = DOM.phrase.val();
+ // Set the phrase from the entropy
+ setMnemonicFromEntropy();
+ // Recalc addresses if the phrase has changed
+ var newPhrase = DOM.phrase.val();
+ if (newPhrase != phrase) {
+ if (newPhrase.length == 0) {
+ clearDisplay();
+ }
+ else {
+ phraseChanged();
+ }
+ }
+ else {
+ hidePending();
+ }
+ }
+
function delayedRootKeyChanged() {
// Warn if there is an existing mnemonic or passphrase.
if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) {
}
function generateClicked() {
+ if (isUsingOwnEntropy()) {
+ return;
+ }
clearDisplay();
showPending();
setTimeout(function() {
$("td.address span").toggleClass("invisible");
}
+ function togglePublicKeys() {
+ showPubKey = !showPubKey;
+ $("td.pubkey span").toggleClass("invisible");
+ }
+
function togglePrivateKeys() {
showPrivKey = !showPrivKey;
$("td.privkey span").toggleClass("invisible");
}
function findPhraseErrors(phrase) {
- // TODO make this right
// Preprocess the words
phrase = mnemonic.normalizeString(phrase);
var words = phraseToWordArray(phrase);
+ // Detect blank phrase
+ if (words.length == 0) {
+ return "Blank mnemonic";
+ }
// Check each word
for (var i=0; i<words.length; i++) {
var word = words[i];
}
var address = key.getAddress().toString();
var privkey = key.privKey.toWIF(network);
+ var pubkey = key.pubKey.toHex();
var indexText = getDerivationPath() + "/" + index;
if (useHardenedAddresses) {
indexText = indexText + "'";
}
- addAddressToList(indexText, address, privkey);
+ addAddressToList(indexText, address, pubkey, privkey);
}, 50)
}
DOM.extendedPubKey.val("");
}
- function addAddressToList(indexText, address, privkey) {
+ function addAddressToList(indexText, address, pubkey, privkey) {
var row = $(addressRowTemplate.html());
// Elements
var indexCell = row.find(".index span");
var addressCell = row.find(".address span");
+ var pubkeyCell = row.find(".pubkey span");
var privkeyCell = row.find(".privkey span");
// Content
indexCell.text(indexText);
addressCell.text(address);
+ pubkeyCell.text(pubkey);
privkeyCell.text(privkey);
// Visibility
if (!showIndex) {
if (!showAddress) {
addressCell.addClass("invisible");
}
+ if (!showPubKey) {
+ pubkeyCell.addClass("invisible");
+ }
if (!showPrivKey) {
privkeyCell.addClass("invisible");
}
}
function getLanguageFromUrl() {
- return window.location.hash.substring(1);
+ for (var language in WORDLISTS) {
+ if (window.location.hash.indexOf(language) > -1) {
+ return language;
+ }
+ }
+ return "";
}
function setMnemonicLanguage() {
return phrase;
}
+ function isUsingOwnEntropy() {
+ return DOM.useEntropy.prop("checked");
+ }
+
+ function setMnemonicFromEntropy() {
+ hideEntropyError();
+ // Get entropy value
+ var entropyStr = DOM.entropy.val();
+ // Work out minimum base for entropy
+ var entropy = Entropy.fromString(entropyStr);
+ if (entropy.binaryStr.length == 0) {
+ return;
+ }
+ // Show entropy details
+ var extraBits = 32 - (entropy.binaryStr.length % 32);
+ var extraChars = Math.ceil(extraBits * Math.log(2) / Math.log(entropy.base.asInt));
+ var words = Math.floor(entropy.binaryStr.length / 32) * 3;
+ var strength = "an extremely weak";
+ if (words >= 3) {
+ strength = "a very weak";
+ }
+ if (words >= 6) {
+ strength = "a weak";
+ }
+ if (words >= 9) {
+ strength = "a strong";
+ }
+ if (words >= 12) {
+ strength = "a very strong";
+ }
+ if (words >= 15) {
+ strength = "an extremely strong";
+ }
+ if (words >= 18) {
+ strength = "an even stronger"
+ }
+ var msg = "Have " + entropy.binaryStr.length + " bits of entropy, " + extraChars + " more " + entropy.base.str + " chars required to generate " + strength + " mnemonic: " + entropy.cleanStr;
+ showEntropyError(msg);
+ // Discard trailing entropy
+ var bitsToUse = Math.floor(entropy.binaryStr.length / 32) * 32;
+ var binaryStr = entropy.binaryStr.substring(0, bitsToUse);
+ // Convert entropy string to numeric array
+ var entropyArr = [];
+ for (var i=0; i<binaryStr.length / 8; i++) {
+ var byteAsBits = binaryStr.substring(i*8, i*8+8);
+ var entropyByte = parseInt(byteAsBits, 2);
+ entropyArr.push(entropyByte)
+ }
+ // Convert entropy array to mnemonic
+ var phrase = mnemonic.toMnemonic(entropyArr);
+ // Set the mnemonic in the UI
+ DOM.phrase.val(phrase);
+ }
+
+ function hideEntropyError() {
+ DOM.entropyError.addClass("hidden");
+ }
+
+ function showEntropyError(msg) {
+ DOM.entropyError.text(msg);
+ DOM.entropyError.removeClass("hidden");
+ }
+
var networks = [
{
name: "Bitcoin",