]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - src/js/index.js
Make CSS and JS path relative
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / src / js / index.js
CommitLineData
ebd8d4e8
IC
1(function() {
2
3 var mnemonic = new Mnemonic("english");
4 var bip32RootKey = null;
5 var bip32ExtendedKey = null;
1759e5e8 6 var network = bitcoin.networks.bitcoin;
ebd8d4e8
IC
7 var addressRowTemplate = $("#address-row-template");
8
700901cd
IC
9 var showIndex = true;
10 var showAddress = true;
11 var showPrivKey = true;
12
ebd8d4e8
IC
13 var phraseChangeTimeoutEvent = null;
14
15 var DOM = {};
d6cedc94
IC
16 DOM.network = $(".network");
17 DOM.phraseNetwork = $("#network-phrase");
ebd8d4e8 18 DOM.phrase = $(".phrase");
1abcc511 19 DOM.passphrase = $(".passphrase");
ebd8d4e8
IC
20 DOM.generate = $(".generate");
21 DOM.rootKey = $(".root-key");
22 DOM.extendedPrivKey = $(".extended-priv-key");
23 DOM.extendedPubKey = $(".extended-pub-key");
d6cedc94
IC
24 DOM.bip32tab = $("#bip32-tab");
25 DOM.bip44tab = $("#bip44-tab");
26 DOM.bip32panel = $("#bip32");
27 DOM.bip44panel = $("#bip44");
ebd8d4e8
IC
28 DOM.bip32path = $("#bip32-path");
29 DOM.bip44path = $("#bip44-path");
30 DOM.bip44purpose = $("#bip44 .purpose");
31 DOM.bip44coin = $("#bip44 .coin");
32 DOM.bip44account = $("#bip44 .account");
33 DOM.bip44change = $("#bip44 .change");
34 DOM.strength = $(".strength");
35 DOM.addresses = $(".addresses");
36 DOM.rowsToAdd = $(".rows-to-add");
37 DOM.more = $(".more");
38 DOM.feedback = $(".feedback");
39 DOM.tab = $(".derivation-type a");
40 DOM.indexToggle = $(".index-toggle");
41 DOM.addressToggle = $(".address-toggle");
42 DOM.privateKeyToggle = $(".private-key-toggle");
43
42887888 44 var derivationPath = $(".tab-pane.active .path").val();
ebd8d4e8
IC
45
46 function init() {
47 // Events
d6cedc94 48 DOM.network.on("change", networkChanged);
a19a5498
IC
49 DOM.phrase.on("input", delayedPhraseChanged);
50 DOM.passphrase.on("input", delayedPhraseChanged);
ebd8d4e8
IC
51 DOM.generate.on("click", generateClicked);
52 DOM.more.on("click", showMore);
a19a5498
IC
53 DOM.bip32path.on("input", bip32Changed);
54 DOM.bip44purpose.on("input", bip44Changed);
55 DOM.bip44coin.on("input", bip44Changed);
56 DOM.bip44account.on("input", bip44Changed);
57 DOM.bip44change.on("input", bip44Changed);
ebd8d4e8
IC
58 DOM.tab.on("click", tabClicked);
59 DOM.indexToggle.on("click", toggleIndexes);
60 DOM.addressToggle.on("click", toggleAddresses);
61 DOM.privateKeyToggle.on("click", togglePrivateKeys);
62 disableForms();
63 hidePending();
64 hideValidationError();
7f15cb6e 65 populateNetworkSelect();
ebd8d4e8
IC
66 }
67
68 // Event handlers
69
d6cedc94 70 function networkChanged(e) {
7a995731 71 var network = e.target.value;
7f15cb6e 72 networks[network].onSelect();
1543fdbf 73 setBip44DerivationPath();
d6cedc94
IC
74 delayedPhraseChanged();
75 }
76
ebd8d4e8
IC
77 function delayedPhraseChanged() {
78 hideValidationError();
79 showPending();
80 if (phraseChangeTimeoutEvent != null) {
81 clearTimeout(phraseChangeTimeoutEvent);
82 }
83 phraseChangeTimeoutEvent = setTimeout(phraseChanged, 400);
84 }
85
86 function phraseChanged() {
87 showPending();
88 hideValidationError();
89 // Get the mnemonic phrase
90 var phrase = DOM.phrase.val();
1abcc511 91 var passphrase = DOM.passphrase.val();
ebd8d4e8
IC
92 var errorText = findPhraseErrors(phrase);
93 if (errorText) {
94 showValidationError(errorText);
95 return;
96 }
97 // Get the derivation path
98 var errorText = findDerivationPathErrors();
99 if (errorText) {
100 showValidationError(errorText);
101 return;
102 }
103 // Calculate and display
1abcc511 104 calcBip32Seed(phrase, passphrase, derivationPath);
ebd8d4e8
IC
105 displayBip32Info();
106 hidePending();
107 }
108
109 function generateClicked() {
110 clearDisplay();
111 showPending();
112 setTimeout(function() {
113 var phrase = generateRandomPhrase();
114 if (!phrase) {
115 return;
116 }
117 phraseChanged();
118 }, 50);
119 }
120
121 function tabClicked(e) {
122 var activePath = $(e.target.getAttribute("href") + " .path");
123 derivationPath = activePath.val();
124 derivationChanged();
125 }
126
127 function derivationChanged() {
59780293 128 delayedPhraseChanged();
ebd8d4e8
IC
129 }
130
131 function bip32Changed() {
132 derivationPath = DOM.bip32path.val();
133 derivationChanged();
134 }
135
136 function bip44Changed() {
137 setBip44DerivationPath();
ebd8d4e8
IC
138 derivationChanged();
139 }
140
141 function toggleIndexes() {
700901cd 142 showIndex = !showIndex;
ebd8d4e8
IC
143 $("td.index span").toggleClass("invisible");
144 }
145
146 function toggleAddresses() {
700901cd 147 showAddress = !showAddress;
ebd8d4e8
IC
148 $("td.address span").toggleClass("invisible");
149 }
150
151 function togglePrivateKeys() {
700901cd 152 showPrivKey = !showPrivKey;
ebd8d4e8
IC
153 $("td.privkey span").toggleClass("invisible");
154 }
155
156 // Private methods
157
158 function generateRandomPhrase() {
159 if (!hasStrongRandom()) {
160 var errorText = "This browser does not support strong randomness";
161 showValidationError(errorText);
162 return;
163 }
164 var numWords = parseInt(DOM.strength.val());
ebd8d4e8
IC
165 var strength = numWords / 3 * 32;
166 var words = mnemonic.generate(strength);
167 DOM.phrase.val(words);
168 return words;
169 }
170
1abcc511
PR
171 function calcBip32Seed(phrase, passphrase, path) {
172 var seed = mnemonic.toSeed(phrase, passphrase);
1759e5e8 173 bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network);
ebd8d4e8
IC
174 bip32ExtendedKey = bip32RootKey;
175 // Derive the key from the path
176 var pathBits = path.split("/");
177 for (var i=0; i<pathBits.length; i++) {
178 var bit = pathBits[i];
179 var index = parseInt(bit);
180 if (isNaN(index)) {
181 continue;
182 }
183 var hardened = bit[bit.length-1] == "'";
184 if (hardened) {
185 bip32ExtendedKey = bip32ExtendedKey.deriveHardened(index);
186 }
187 else {
188 bip32ExtendedKey = bip32ExtendedKey.derive(index);
189 }
190 }
191 }
192
193 function showValidationError(errorText) {
194 DOM.feedback
195 .text(errorText)
196 .show();
197 }
198
199 function hideValidationError() {
200 DOM.feedback
201 .text("")
202 .hide();
203 }
204
205 function findPhraseErrors(phrase) {
206 // TODO make this right
207 // Preprocess the words
783981de 208 phrase = mnemonic.normalizeString(phrase);
ebd8d4e8
IC
209 var parts = phrase.split(" ");
210 var proper = [];
211 for (var i=0; i<parts.length; i++) {
212 var part = parts[i];
213 if (part.length > 0) {
214 // TODO check that lowercasing is always valid to do
215 proper.push(part.toLowerCase());
216 }
217 }
218 // TODO some levenstein on the words
219 var properPhrase = proper.join(' ');
220 // Check the words are valid
221 var isValid = mnemonic.check(properPhrase);
222 if (!isValid) {
223 return "Invalid mnemonic";
224 }
225 return false;
226 }
227
228 function findDerivationPathErrors(path) {
229 // TODO
230 return false;
231 }
232
233 function displayBip32Info() {
234 // Display the key
235 var rootKey = bip32RootKey.toBase58();
236 DOM.rootKey.val(rootKey);
237 var extendedPrivKey = bip32ExtendedKey.toBase58();
238 DOM.extendedPrivKey.val(extendedPrivKey);
239 var extendedPubKey = bip32ExtendedKey.toBase58(false);
240 DOM.extendedPubKey.val(extendedPubKey);
241 // Display the addresses and privkeys
242 clearAddressesList();
243 displayAddresses(0, 20);
244 }
245
246 function displayAddresses(start, total) {
247 for (var i=0; i<total; i++) {
a8c45487
IC
248 var index = i + start;
249 new TableRow(index);
ebd8d4e8
IC
250 }
251 }
252
a8c45487
IC
253 function TableRow(index) {
254
255 function init() {
256 calculateValues();
257 }
258
259 function calculateValues() {
260 setTimeout(function() {
261 var key = bip32ExtendedKey.derive(index);
262 var address = key.getAddress().toString();
263 var privkey = key.privKey.toWIF(network);
264 addAddressToList(index, address, privkey);
265 }, 50)
266 }
267
268 init();
269
270 }
271
ebd8d4e8
IC
272 function showMore() {
273 var start = DOM.addresses.children().length;
274 var rowsToAdd = parseInt(DOM.rowsToAdd.val());
275 if (isNaN(rowsToAdd)) {
276 rowsToAdd = 20;
277 DOM.rowsToAdd.val("20");
278 }
279 if (rowsToAdd > 200) {
280 var msg = "Generating " + rowsToAdd + " rows could take a while. ";
281 msg += "Do you want to continue?";
282 if (!confirm(msg)) {
283 return;
284 }
285 }
ebd8d4e8 286 displayAddresses(start, rowsToAdd);
ebd8d4e8
IC
287 }
288
289 function clearDisplay() {
290 clearAddressesList();
291 clearKey();
292 hideValidationError();
293 }
294
295 function clearAddressesList() {
296 DOM.addresses.empty();
297 }
298
299 function clearKey() {
300 DOM.rootKey.val("");
301 DOM.extendedPrivKey.val("");
302 DOM.extendedPubKey.val("");
303 }
304
305 function addAddressToList(index, address, privkey) {
306 var row = $(addressRowTemplate.html());
700901cd
IC
307 // Elements
308 var indexCell = row.find(".index span");
309 var addressCell = row.find(".address span");
310 var privkeyCell = row.find(".privkey span");
311 // Content
ae30fed8
IC
312 var indexText = derivationPath + "/" + index;
313 indexCell.text(indexText);
700901cd
IC
314 addressCell.text(address);
315 privkeyCell.text(privkey);
316 // Visibility
317 if (!showIndex) {
318 indexCell.addClass("invisible");
319 }
320 if (!showAddress) {
321 addressCell.addClass("invisible");
322 }
323 if (!showPrivKey) {
6d628db7 324 privkeyCell.addClass("invisible");
700901cd 325 }
ebd8d4e8
IC
326 DOM.addresses.append(row);
327 }
328
329 function hasStrongRandom() {
330 return 'crypto' in window && window['crypto'] !== null;
331 }
332
333 function disableForms() {
334 $("form").on("submit", function(e) {
335 e.preventDefault();
336 });
337 }
338
339 function setBip44DerivationPath() {
340 var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44);
341 var coin = parseIntNoNaN(DOM.bip44coin.val(), 0);
342 var account = parseIntNoNaN(DOM.bip44account.val(), 0);
343 var change = parseIntNoNaN(DOM.bip44change.val(), 0);
344 var path = "m/";
345 path += purpose + "'/";
346 path += coin + "'/";
347 path += account + "'/";
348 path += change;
349 DOM.bip44path.val(path);
2b831bc6 350 derivationPath = DOM.bip44path.val();
ebd8d4e8
IC
351 }
352
353 function parseIntNoNaN(val, defaultVal) {
354 var v = parseInt(val);
355 if (isNaN(v)) {
356 return defaultVal;
357 }
358 return v;
359 }
360
361 function showPending() {
362 DOM.feedback
363 .text("Calculating...")
364 .show();
365 }
366
367 function hidePending() {
368 DOM.feedback
369 .text("")
370 .hide();
371 }
372
7f15cb6e
IC
373 function populateNetworkSelect() {
374 for (var i=0; i<networks.length; i++) {
375 var network = networks[i];
376 var option = $("<option>");
377 option.attr("value", i);
378 option.text(network.name);
379 DOM.phraseNetwork.append(option);
380 }
381 }
382
383 var networks = [
384 {
7a995731
IC
385 name: "Bitcoin",
386 onSelect: function() {
1759e5e8 387 network = bitcoin.networks.bitcoin;
7a995731 388 DOM.bip44coin.val(0);
7a995731
IC
389 },
390 },
7f15cb6e 391 {
7a995731
IC
392 name: "Bitcoin Testnet",
393 onSelect: function() {
1759e5e8 394 network = bitcoin.networks.testnet;
7a995731 395 DOM.bip44coin.val(1);
7a995731
IC
396 },
397 },
7f15cb6e 398 {
7a995731
IC
399 name: "Litecoin",
400 onSelect: function() {
1759e5e8 401 network = bitcoin.networks.litecoin;
7a995731
IC
402 DOM.bip44coin.val(2);
403 },
404 },
7f15cb6e 405 {
7a995731
IC
406 name: "Dogecoin",
407 onSelect: function() {
1759e5e8 408 network = bitcoin.networks.dogecoin;
7a995731
IC
409 DOM.bip44coin.val(3);
410 },
411 },
e3a9508c
IC
412 {
413 name: "ShadowCash",
414 onSelect: function() {
415 network = bitcoin.networks.shadow;
416 DOM.bip44coin.val(35);
417 },
418 },
419 {
420 name: "ShadowCash Testnet",
421 onSelect: function() {
422 network = bitcoin.networks.shadowtn;
423 DOM.bip44coin.val(1);
424 },
425 },
a3baa26e
IC
426 {
427 name: "Viacoin",
428 onSelect: function() {
429 network = bitcoin.networks.viacoin;
430 DOM.bip44coin.val(14);
431 },
432 },
433 {
434 name: "Viacoin Testnet",
435 onSelect: function() {
436 network = bitcoin.networks.viacointestnet;
437 DOM.bip44coin.val(1);
438 },
439 },
440 {
441 name: "Jumbucks",
442 onSelect: function() {
443 network = bitcoin.networks.jumbucks;
444 DOM.bip44coin.val(26);
445 },
446 },
5c434a8a
CM
447 {
448 name: "CLAM",
449 onSelect: function() {
450 network = bitcoin.networks.clam;
451 DOM.bip44coin.val(23);
452 },
453 },
7f15cb6e 454 ]
7a995731 455
ebd8d4e8
IC
456 init();
457
458})();