]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blame - tests.js
Ethereum addresses include checksum
[perso/Immae/Projets/Cryptomonnaies/BIP39.git] / tests.js
CommitLineData
88e2cdaa
IC
1// Usage:
2// $ phantomjs tests.js
3
4
5var page = require('webpage').create();
6var url = 'src/index.html';
ddeb8613 7var testMaxTime = 20000;
88e2cdaa 8
e00964cc
IC
9page.viewportSize = {
10 width: 1024,
11 height: 720
12};
13
88e2cdaa
IC
14page.onResourceError = function(e) {
15 console.log("Error loading " + e.url);
16 phantom.exit();
17}
18
19function fail() {
20 console.log("Failed");
21 phantom.exit();
22}
23
3eef9d0d
IC
24function waitForGenerate(fn, maxTime) {
25 if (!maxTime) {
26 maxTime = testMaxTime;
27 }
28 var start = new Date().getTime();
29 var prevAddressCount = -1;
30 var wait = function keepWaiting() {
31 var now = new Date().getTime();
32 var hasTimedOut = now - start > maxTime;
33 var addressCount = page.evaluate(function() {
34 return $(".address").length;
35 });
36 var hasFinished = addressCount > 0 && addressCount == prevAddressCount;
37 prevAddressCount = addressCount;
38 if (hasFinished) {
39 fn();
40 }
41 else if (hasTimedOut) {
42 console.log("Test timed out");
43 fn();
44 }
45 else {
46 setTimeout(keepWaiting, 100);
47 }
48 }
49 wait();
50}
51
c3719b00
IC
52function waitForFeedback(fn, maxTime) {
53 if (!maxTime) {
54 maxTime = testMaxTime;
55 }
56 var start = new Date().getTime();
57 var wait = function keepWaiting() {
58 var now = new Date().getTime();
59 var hasTimedOut = now - start > maxTime;
60 if (hasTimedOut) {
61 console.log("Test timed out");
62 fn();
63 return;
64 }
65 var feedback = page.evaluate(function() {
66 var feedback = $(".feedback");
67 if (feedback.css("display") == "none") {
68 return "";
69 }
70 return feedback.text();
71 });
72 var hasFinished = feedback.length > 0 && feedback != "Calculating...";
73 if (hasFinished) {
74 fn();
75 }
76 else {
77 setTimeout(keepWaiting, 100);
78 }
79 }
80 wait();
81}
82
057722b0
IC
83function waitForEntropyFeedback(fn, maxTime) {
84 if (!maxTime) {
85 maxTime = testMaxTime;
86 }
87 var origFeedback = page.evaluate(function() {
dd944906 88 return $(".entropy-container").text();
057722b0
IC
89 });
90 var start = new Date().getTime();
91 var wait = function keepWaiting() {
92 var now = new Date().getTime();
93 var hasTimedOut = now - start > maxTime;
94 if (hasTimedOut) {
95 console.log("Test timed out");
96 fn();
97 return;
98 }
99 var feedback = page.evaluate(function() {
dd944906 100 return $(".entropy-container").text();
057722b0
IC
101 });
102 var hasFinished = feedback != origFeedback;
103 if (hasFinished) {
104 fn();
105 }
106 else {
107 setTimeout(keepWaiting, 100);
108 }
109 }
110 wait();
111}
112
88e2cdaa
IC
113function next() {
114 if (tests.length > 0) {
115 var testsStr = tests.length == 1 ? "test" : "tests";
116 console.log(tests.length + " " + testsStr + " remaining");
117 tests.shift()();
118 }
119 else {
120 console.log("Finished with 0 failures");
121 phantom.exit();
122 }
123}
124
fb372687
IC
125/**
126 * Randomize array element order in-place.
127 * Using Durstenfeld shuffle algorithm.
128 * See http://stackoverflow.com/a/12646864
129 */
130function shuffle(array) {
131 for (var i = array.length - 1; i > 0; i--) {
132 var j = Math.floor(Math.random() * (i + 1));
133 var temp = array[i];
134 array[i] = array[j];
135 array[j] = temp;
136 }
137 return array;
138}
139
88e2cdaa
IC
140tests = [
141
142// Page loads with status of 'success'
143function() {
144page.open(url, function(status) {
145 if (status != "success") {
146 console.log("Page did not load with status 'success'");
147 fail();
148 }
149 next();
150});
151},
152
153// Page has text
154function() {
155page.open(url, function(status) {
156 var content = page.evaluate(function() {
157 return document.body.textContent.trim();
158 });
159 if (!content) {
160 console.log("Page does not have text");
161 fail();
162 }
163 next();
164});
165},
166
167// Entering mnemonic generates addresses
168function() {
169page.open(url, function(status) {
88e2cdaa
IC
170 // set the phrase
171 page.evaluate(function() {
172 $(".phrase").val("abandon abandon ability").trigger("input");
173 });
174 // get the address
3eef9d0d 175 waitForGenerate(function() {
06c4c6e3
IC
176 var addressCount = page.evaluate(function() {
177 return $(".address").length;
88e2cdaa 178 });
06c4c6e3
IC
179 if (addressCount != 20) {
180 console.log("Mnemonic did not generate addresses");
88e2cdaa
IC
181 console.log("Expected: " + expected);
182 console.log("Got: " + actual);
183 fail();
184 }
185 next();
3eef9d0d 186 });
88e2cdaa
IC
187});
188},
189
190// Random button generates random mnemonic
191function() {
192page.open(url, function(status) {
193 // check initial phrase is empty
194 var phrase = page.evaluate(function() {
195 return $(".phrase").text();
196 });
197 if (phrase != "") {
198 console.log("Initial phrase is not blank");
199 fail();
200 }
201 // press the 'generate' button
202 page.evaluate(function() {
203 $(".generate").click();
204 });
205 // get the new phrase
3eef9d0d 206 waitForGenerate(function() {
88e2cdaa
IC
207 var phrase = page.evaluate(function() {
208 return $(".phrase").val();
209 });
210 if (phrase.length <= 0) {
211 console.log("Phrase not generated by pressing button");
212 fail();
213 }
214 next();
3eef9d0d 215 });
88e2cdaa
IC
216});
217},
218
219// Mnemonic length can be customized
220function() {
221page.open(url, function(status) {
222 // set the length to 6
54563907 223 var expectedLength = "6";
88e2cdaa 224 page.evaluate(function() {
54563907
IC
225 $(".strength option[selected]").removeAttr("selected");
226 $(".strength option[value=6]").prop("selected", true);
88e2cdaa
IC
227 });
228 // press the 'generate' button
229 page.evaluate(function() {
230 $(".generate").click();
231 });
232 // check the new phrase is six words long
3eef9d0d 233 waitForGenerate(function() {
88e2cdaa
IC
234 var actualLength = page.evaluate(function() {
235 var words = $(".phrase").val().split(" ");
236 return words.length;
237 });
238 if (actualLength != expectedLength) {
239 console.log("Phrase not generated with correct length");
240 console.log("Expected: " + expectedLength);
241 console.log("Actual: " + actualLength);
242 fail();
243 }
54563907 244 next();
3eef9d0d 245 });
88e2cdaa
IC
246});
247},
248
88e2cdaa 249// Passphrase can be set
54563907
IC
250function() {
251page.open(url, function(status) {
252 // set the phrase and passphrase
253 var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
254 page.evaluate(function() {
255 $(".phrase").val("abandon abandon ability");
256 $(".passphrase").val("secure_passphrase").trigger("input");
257 });
258 // check the address is generated correctly
3eef9d0d 259 waitForGenerate(function() {
54563907
IC
260 var actual = page.evaluate(function() {
261 return $(".address:first").text();
262 });
263 if (actual != expected) {
264 console.log("Passphrase results in wrong address");
265 console.log("Expected: " + expected);
266 console.log("Actual: " + actual);
267 fail();
268 }
269 next();
3eef9d0d 270 });
54563907
IC
271});
272},
273
88e2cdaa 274// Network can be set to bitcoin testnet
54563907
IC
275function() {
276page.open(url, function(status) {
59193779 277 // set the phrase and coin
54563907
IC
278 var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
279 page.evaluate(function() {
280 $(".phrase").val("abandon abandon ability");
281 $(".phrase").trigger("input");
282 $(".network option[selected]").removeAttr("selected");
283 $(".network option[value=1]").prop("selected", true);
284 $(".network").trigger("change");
285 });
286 // check the address is generated correctly
3eef9d0d 287 waitForGenerate(function() {
54563907
IC
288 var actual = page.evaluate(function() {
289 return $(".address:first").text();
290 });
291 if (actual != expected) {
292 console.log("Bitcoin testnet address is incorrect");
293 console.log("Expected: " + expected);
294 console.log("Actual: " + actual);
295 fail();
296 }
297 next();
3eef9d0d 298 });
54563907
IC
299});
300},
301
88e2cdaa 302// Network can be set to litecoin
59193779
IC
303function() {
304page.open(url, function(status) {
305 // set the phrase and coin
306 var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
307 page.evaluate(function() {
308 $(".phrase").val("abandon abandon ability");
309 $(".phrase").trigger("input");
310 $(".network option[selected]").removeAttr("selected");
311 $(".network option[value=2]").prop("selected", true);
312 $(".network").trigger("change");
313 });
314 // check the address is generated correctly
3eef9d0d 315 waitForGenerate(function() {
59193779
IC
316 var actual = page.evaluate(function() {
317 return $(".address:first").text();
318 });
319 if (actual != expected) {
320 console.log("Litecoin address is incorrect");
321 console.log("Expected: " + expected);
322 console.log("Actual: " + actual);
323 fail();
324 }
325 next();
3eef9d0d 326 });
59193779
IC
327});
328},
329
88e2cdaa 330// Network can be set to dogecoin
59193779
IC
331function() {
332page.open(url, function(status) {
333 // set the phrase and coin
334 var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
335 page.evaluate(function() {
336 $(".phrase").val("abandon abandon ability");
337 $(".phrase").trigger("input");
338 $(".network option[selected]").removeAttr("selected");
339 $(".network option[value=3]").prop("selected", true);
340 $(".network").trigger("change");
341 });
342 // check the address is generated correctly
3eef9d0d 343 waitForGenerate(function() {
59193779
IC
344 var actual = page.evaluate(function() {
345 return $(".address:first").text();
346 });
347 if (actual != expected) {
348 console.log("Dogecoin address is incorrect");
349 console.log("Expected: " + expected);
350 console.log("Actual: " + actual);
351 fail();
352 }
353 next();
3eef9d0d 354 });
59193779
IC
355});
356},
357
88e2cdaa 358// Network can be set to shadowcash
59193779
IC
359function() {
360page.open(url, function(status) {
361 // set the phrase and coin
362 var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
363 page.evaluate(function() {
364 $(".phrase").val("abandon abandon ability");
365 $(".phrase").trigger("input");
366 $(".network option[selected]").removeAttr("selected");
367 $(".network option[value=4]").prop("selected", true);
368 $(".network").trigger("change");
369 });
370 // check the address is generated correctly
3eef9d0d 371 waitForGenerate(function() {
59193779
IC
372 var actual = page.evaluate(function() {
373 return $(".address:first").text();
374 });
375 if (actual != expected) {
376 console.log("Shadowcash address is incorrect");
377 console.log("Expected: " + expected);
378 console.log("Actual: " + actual);
379 fail();
380 }
381 next();
3eef9d0d 382 });
59193779
IC
383});
384},
385
88e2cdaa 386// Network can be set to shadowcash testnet
59193779
IC
387function() {
388page.open(url, function(status) {
389 // set the phrase and coin
390 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
391 page.evaluate(function() {
392 $(".phrase").val("abandon abandon ability");
393 $(".phrase").trigger("input");
394 $(".network option[selected]").removeAttr("selected");
395 $(".network option[value=5]").prop("selected", true);
396 $(".network").trigger("change");
397 });
398 // check the address is generated correctly
3eef9d0d 399 waitForGenerate(function() {
59193779
IC
400 var actual = page.evaluate(function() {
401 return $(".address:first").text();
402 });
403 if (actual != expected) {
404 console.log("Shadowcash testnet address is incorrect");
405 console.log("Expected: " + expected);
406 console.log("Actual: " + actual);
407 fail();
408 }
409 next();
3eef9d0d 410 });
59193779
IC
411});
412},
413
88e2cdaa 414// Network can be set to viacoin
59193779
IC
415function() {
416page.open(url, function(status) {
417 // set the phrase and coin
418 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
419 page.evaluate(function() {
420 $(".phrase").val("abandon abandon ability");
421 $(".phrase").trigger("input");
422 $(".network option[selected]").removeAttr("selected");
423 $(".network option[value=6]").prop("selected", true);
424 $(".network").trigger("change");
425 });
426 // check the address is generated correctly
3eef9d0d 427 waitForGenerate(function() {
59193779
IC
428 var actual = page.evaluate(function() {
429 return $(".address:first").text();
430 });
431 if (actual != expected) {
432 console.log("Viacoin address is incorrect");
433 console.log("Expected: " + expected);
434 console.log("Actual: " + actual);
435 fail();
436 }
437 next();
3eef9d0d 438 });
59193779
IC
439});
440},
441
88e2cdaa 442// Network can be set to viacoin testnet
59193779
IC
443function() {
444page.open(url, function(status) {
445 // set the phrase and coin
446 var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
447 page.evaluate(function() {
448 $(".phrase").val("abandon abandon ability");
449 $(".phrase").trigger("input");
450 $(".network option[selected]").removeAttr("selected");
451 $(".network option[value=7]").prop("selected", true);
452 $(".network").trigger("change");
453 });
454 // check the address is generated correctly
3eef9d0d 455 waitForGenerate(function() {
59193779
IC
456 var actual = page.evaluate(function() {
457 return $(".address:first").text();
458 });
459 if (actual != expected) {
460 console.log("Viacoin testnet address is incorrect");
461 console.log("Expected: " + expected);
462 console.log("Actual: " + actual);
463 fail();
464 }
465 next();
3eef9d0d 466 });
59193779
IC
467});
468},
469
88e2cdaa 470// Network can be set to jumbucks
59193779
IC
471function() {
472page.open(url, function(status) {
473 // set the phrase and coin
474 var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
475 page.evaluate(function() {
476 $(".phrase").val("abandon abandon ability");
477 $(".phrase").trigger("input");
478 $(".network option[selected]").removeAttr("selected");
479 $(".network option[value=8]").prop("selected", true);
480 $(".network").trigger("change");
481 });
482 // check the address is generated correctly
3eef9d0d 483 waitForGenerate(function() {
59193779
IC
484 var actual = page.evaluate(function() {
485 return $(".address:first").text();
486 });
487 if (actual != expected) {
488 console.log("Jumbucks address is incorrect");
489 console.log("Expected: " + expected);
490 console.log("Actual: " + actual);
491 fail();
492 }
493 next();
3eef9d0d 494 });
59193779
IC
495});
496},
497
88e2cdaa 498// Network can be set to clam
59193779
IC
499function() {
500page.open(url, function(status) {
501 // set the phrase and coin
502 var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
503 page.evaluate(function() {
504 $(".phrase").val("abandon abandon ability");
505 $(".phrase").trigger("input");
506 $(".network option[selected]").removeAttr("selected");
507 $(".network option[value=9]").prop("selected", true);
508 $(".network").trigger("change");
509 });
510 // check the address is generated correctly
3eef9d0d 511 waitForGenerate(function() {
59193779
IC
512 var actual = page.evaluate(function() {
513 return $(".address:first").text();
514 });
515 if (actual != expected) {
516 console.log("CLAM address is incorrect");
517 console.log("Expected: " + expected);
518 console.log("Actual: " + actual);
519 fail();
f3fad1b5
IC
520 }
521 next();
522 });
523});
524},
525
526// Network can be set to dash
527function() {
528page.open(url, function(status) {
529 // set the phrase and coin
530 var expected = "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
531 page.evaluate(function() {
532 $(".phrase").val("abandon abandon ability");
533 $(".phrase").trigger("input");
534 $(".network option[selected]").removeAttr("selected");
535 $(".network option[value=10]").prop("selected", true);
536 $(".network").trigger("change");
537 });
538 // check the address is generated correctly
539 waitForGenerate(function() {
540 var actual = page.evaluate(function() {
541 return $(".address:first").text();
542 });
543 if (actual != expected) {
544 console.log("DASH address is incorrect");
545 console.log("Expected: " + expected);
546 console.log("Actual: " + actual);
547 fail();
59193779 548 }
bfb3dab6
IC
549 next();
550 });
551});
552},
553
554// Network can be set to namecoin
555function() {
556page.open(url, function(status) {
557 // set the phrase and coin
558 var expected = "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
559 page.evaluate(function() {
560 $(".phrase").val("abandon abandon ability");
561 $(".phrase").trigger("input");
562 $(".network option[selected]").removeAttr("selected");
563 $(".network option[value=11]").prop("selected", true);
564 $(".network").trigger("change");
565 });
566 // check the address is generated correctly
567 waitForGenerate(function() {
568 var actual = page.evaluate(function() {
569 return $(".address:first").text();
570 });
571 if (actual != expected) {
572 console.log("Namecoin address is incorrect");
573 console.log("Expected: " + expected);
574 console.log("Actual: " + actual);
575 fail();
576 }
577 next();
578 });
579});
580},
581
582// Network can be set to peercoin
583function() {
584page.open(url, function(status) {
585 // set the phrase and coin
586 var expected = "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
587 page.evaluate(function() {
588 $(".phrase").val("abandon abandon ability");
589 $(".phrase").trigger("input");
590 $(".network option[selected]").removeAttr("selected");
591 $(".network option[value=12]").prop("selected", true);
592 $(".network").trigger("change");
593 });
594 // check the address is generated correctly
595 waitForGenerate(function() {
596 var actual = page.evaluate(function() {
597 return $(".address:first").text();
598 });
599 if (actual != expected) {
600 console.log("Peercoin address is incorrect");
601 console.log("Expected: " + expected);
602 console.log("Actual: " + actual);
603 fail();
604 }
59193779 605 next();
3eef9d0d 606 });
59193779
IC
607});
608},
609
24137d96
IC
610// Network can be set to ethereum
611function() {
612
613page.open(url, function(status) {
614
615 // set the phrase and coin
616 page.evaluate(function() {
617 $(".phrase").val("abandon abandon ability");
618 $(".phrase").trigger("input");
619 $(".network option[selected]").removeAttr("selected");
620 $(".network option[value=13]").prop("selected", true);
621 $(".network").trigger("change");
622 });
623 waitForGenerate(function() {
624 // check the address is generated correctly
625 // this value comes from
626 // https://www.myetherwallet.com/#view-wallet-info
49b21f12 627 // Unusual capitalization is due to checksum
24137d96
IC
628 var expected = "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
629 var actual = page.evaluate(function() {
630 return $(".address:first").text();
631 });
49b21f12 632 if (actual != expected) {
24137d96
IC
633 console.log("Ethereum address is incorrect");
634 console.log("Expected: " + expected);
635 console.log("Actual: " + actual);
636 fail();
637 }
638 // check the private key is correct
639 // this private key can be imported into
640 // https://www.myetherwallet.com/#view-wallet-info
641 // and it should correlate to the address above
642 var expected = "8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
643 var actual = page.evaluate(function() {
644 return $(".privkey:first").text();
645 });
646 if (actual != expected) {
647 console.log("Ethereum privkey is incorrect");
648 console.log("Expected: " + expected);
649 console.log("Actual: " + actual);
650 fail();
651 }
652 // check the public key is correct
653 // TODO
654 // don't have any third-party source to generate the expected value
655 //var expected = "?";
656 //var actual = page.evaluate(function() {
657 // return $(".pubkey:first").text();
658 //});
659 //if (actual != expected) {
660 // console.log("Ethereum privkey is incorrect");
661 // console.log("Expected: " + expected);
662 // console.log("Actual: " + actual);
663 // fail();
664 //}
665 next();
666 });
667});
668},
669
88e2cdaa 670// BIP39 seed is set from phrase
c196ad55
IC
671function() {
672page.open(url, function(status) {
673 // set the phrase
674 var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
675 page.evaluate(function() {
676 $(".phrase").val("abandon abandon ability");
677 $(".phrase").trigger("input");
678 });
679 // check the address is generated correctly
3eef9d0d 680 waitForGenerate(function() {
c196ad55
IC
681 var actual = page.evaluate(function() {
682 return $(".seed").val();
683 });
684 if (actual != expected) {
685 console.log("BIP39 seed is incorrectly generated from mnemonic");
686 console.log("Expected: " + expected);
687 console.log("Actual: " + actual);
688 fail();
689 }
690 next();
3eef9d0d 691 });
c196ad55
IC
692});
693},
694
88e2cdaa 695// BIP32 root key is set from phrase
ec60b662
IC
696function() {
697page.open(url, function(status) {
698 // set the phrase
699 var expected = "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
700 page.evaluate(function() {
701 $(".phrase").val("abandon abandon ability");
702 $(".phrase").trigger("input");
703 });
704 // check the address is generated correctly
3eef9d0d 705 waitForGenerate(function() {
ec60b662
IC
706 var actual = page.evaluate(function() {
707 return $(".root-key").val();
708 });
709 if (actual != expected) {
710 console.log("Root key is incorrectly generated from mnemonic");
711 console.log("Expected: " + expected);
712 console.log("Actual: " + actual);
713 fail();
714 }
715 next();
3eef9d0d 716 });
ec60b662
IC
717});
718},
719
88e2cdaa 720// Tabs show correct addresses when changed
cf7258fd
IC
721function() {
722page.open(url, function(status) {
723 // set the phrase
724 var expected = "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
725 page.evaluate(function() {
726 $(".phrase").val("abandon abandon ability");
727 $(".phrase").trigger("input");
728 });
729 // change tabs
730 waitForGenerate(function() {
731 page.evaluate(function() {
732 $("#bip32-tab a").click();
733 });
734 // check the address is generated correctly
735 waitForGenerate(function() {
736 var actual = page.evaluate(function() {
737 return $(".address:first").text();
738 });
739 if (actual != expected) {
740 console.log("Clicking tab generates incorrect address");
741 console.log("Expected: " + expected);
742 console.log("Actual: " + actual);
743 fail();
744 }
745 next();
746 });
747 });
748});
749},
88e2cdaa
IC
750
751// BIP44 derivation path is shown
d077e1e7
IC
752function() {
753page.open(url, function(status) {
754 // set the phrase
755 var expected = "m/44'/0'/0'/0";
756 page.evaluate(function() {
757 $(".phrase").val("abandon abandon ability");
758 $(".phrase").trigger("input");
759 });
760 // check the derivation path of the first address
761 waitForGenerate(function() {
762 var actual = page.evaluate(function() {
763 return $("#bip44 .path").val();
764 });
765 if (actual != expected) {
766 console.log("BIP44 derivation path is incorrect");
767 console.log("Expected: " + expected);
768 console.log("Actual: " + actual);
769 fail();
770 }
771 next();
772 });
773});
774},
775
88e2cdaa 776// BIP44 extended private key is shown
4fd2925d
IC
777function() {
778page.open(url, function(status) {
779 // set the phrase
780 var expected = "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
781 page.evaluate(function() {
782 $(".phrase").val("abandon abandon ability");
783 $(".phrase").trigger("input");
784 });
06286adb 785 // check the BIP44 extended private key
4fd2925d
IC
786 waitForGenerate(function() {
787 var actual = page.evaluate(function() {
788 return $(".extended-priv-key").val();
789 });
790 if (actual != expected) {
791 console.log("BIP44 extended private key is incorrect");
792 console.log("Expected: " + expected);
793 console.log("Actual: " + actual);
794 fail();
795 }
796 next();
797 });
798});
799},
800
88e2cdaa 801// BIP44 extended public key is shown
39fd45bb
IC
802function() {
803page.open(url, function(status) {
804 // set the phrase
805 var expected = "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
806 page.evaluate(function() {
807 $(".phrase").val("abandon abandon ability");
808 $(".phrase").trigger("input");
809 });
06286adb 810 // check the BIP44 extended public key
39fd45bb
IC
811 waitForGenerate(function() {
812 var actual = page.evaluate(function() {
813 return $(".extended-pub-key").val();
814 });
815 if (actual != expected) {
816 console.log("BIP44 extended public key is incorrect");
817 console.log("Expected: " + expected);
818 console.log("Actual: " + actual);
819 fail();
820 }
821 next();
822 });
823});
824},
825
06286adb
IC
826// BIP44 purpose field changes address list
827function() {
828page.open(url, function(status) {
829 // set the phrase
830 var expected = "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
831 page.evaluate(function() {
832 $(".phrase").val("abandon abandon ability");
833 $(".phrase").trigger("input");
834 });
835 waitForGenerate(function() {
836 // change the bip44 purpose field to 45
837 page.evaluate(function() {
838 $("#bip44 .purpose").val("45");
839 $("#bip44 .purpose").trigger("input");
840 });
841 waitForGenerate(function() {
842 // check the address for the new derivation path
843 var actual = page.evaluate(function() {
844 return $(".address:first").text();
845 });
846 if (actual != expected) {
847 console.log("BIP44 purpose field generates incorrect address");
848 console.log("Expected: " + expected);
849 console.log("Actual: " + actual);
850 fail();
851 }
852 next();
853 });
854 });
855});
856},
857
88e2cdaa 858// BIP44 coin field changes address list
9eb72cdf
IC
859function() {
860page.open(url, function(status) {
861 // set the phrase
862 var expected = "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
863 page.evaluate(function() {
864 $(".phrase").val("abandon abandon ability");
865 $(".phrase").trigger("input");
866 });
867 waitForGenerate(function() {
868 // change the bip44 purpose field to 45
869 page.evaluate(function() {
870 $("#bip44 .coin").val("1");
871 $("#bip44 .coin").trigger("input");
872 });
873 waitForGenerate(function() {
874 // check the address for the new derivation path
875 var actual = page.evaluate(function() {
876 return $(".address:first").text();
877 });
878 if (actual != expected) {
879 console.log("BIP44 coin field generates incorrect address");
880 console.log("Expected: " + expected);
881 console.log("Actual: " + actual);
882 fail();
883 }
884 next();
885 });
886 });
887});
888},
889
88e2cdaa 890// BIP44 account field changes address list
048bc3e0
IC
891function() {
892page.open(url, function(status) {
893 // set the phrase
894 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
895 page.evaluate(function() {
896 $(".phrase").val("abandon abandon ability");
897 $(".phrase").trigger("input");
898 });
899 waitForGenerate(function() {
900 // change the bip44 purpose field to 45
901 page.evaluate(function() {
902 $("#bip44 .account").val("1");
903 $("#bip44 .account").trigger("input");
904 });
905 waitForGenerate(function() {
906 // check the address for the new derivation path
907 var actual = page.evaluate(function() {
908 return $(".address:first").text();
909 });
910 if (actual != expected) {
911 console.log("BIP44 account field generates incorrect address");
912 console.log("Expected: " + expected);
913 console.log("Actual: " + actual);
914 fail();
915 }
916 next();
917 });
918 });
919});
920},
921
fa4da086
IC
922// BIP44 change field changes address list
923function() {
924page.open(url, function(status) {
925 // set the phrase
926 var expected = "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
927 page.evaluate(function() {
928 $(".phrase").val("abandon abandon ability");
929 $(".phrase").trigger("input");
930 });
931 waitForGenerate(function() {
932 // change the bip44 purpose field to 45
933 page.evaluate(function() {
934 $("#bip44 .change").val("1");
935 $("#bip44 .change").trigger("input");
936 });
937 waitForGenerate(function() {
938 // check the address for the new derivation path
939 var actual = page.evaluate(function() {
940 return $(".address:first").text();
941 });
942 if (actual != expected) {
943 console.log("BIP44 change field generates incorrect address");
944 console.log("Expected: " + expected);
945 console.log("Actual: " + actual);
946 fail();
947 }
948 next();
949 });
950 });
951});
952},
048bc3e0 953
88e2cdaa 954// BIP32 derivation path can be set
651382a3
IC
955function() {
956page.open(url, function(status) {
957 // set the phrase
958 var expected = "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
959 page.evaluate(function() {
960 $(".phrase").val("abandon abandon ability");
961 $(".phrase").trigger("input");
962 });
963 // change tabs
964 waitForGenerate(function() {
965 page.evaluate(function() {
966 $("#bip32-tab a").click();
967 });
968 // set the derivation path to m/1
969 waitForGenerate(function() {
970 page.evaluate(function() {
971 $("#bip32 .path").val("m/1");
972 $("#bip32 .path").trigger("input");
973 });
974 // check the address is generated correctly
975 waitForGenerate(function() {
976 var actual = page.evaluate(function() {
977 return $(".address:first").text();
978 });
979 if (actual != expected) {
980 console.log("Custom BIP32 path generates incorrect address");
981 console.log("Expected: " + expected);
982 console.log("Actual: " + actual);
983 fail();
984 }
985 next();
986 });
987 });
988 });
989});
990},
991
88e2cdaa 992// BIP32 can use hardened derivation paths
651382a3
IC
993function() {
994page.open(url, function(status) {
995 // set the phrase
996 var expected = "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
997 page.evaluate(function() {
998 $(".phrase").val("abandon abandon ability");
999 $(".phrase").trigger("input");
1000 });
1001 // change tabs
1002 waitForGenerate(function() {
1003 page.evaluate(function() {
1004 $("#bip32-tab a").click();
1005 });
1006 // set the derivation path to m/0'
1007 waitForGenerate(function() {
1008 page.evaluate(function() {
1009 $("#bip32 .path").val("m/0'");
1010 $("#bip32 .path").trigger("input");
1011 });
1012 // check the address is generated correctly
1013 waitForGenerate(function() {
1014 var actual = page.evaluate(function() {
1015 return $(".address:first").text();
1016 });
1017 if (actual != expected) {
1018 console.log("Hardened BIP32 path generates incorrect address");
1019 console.log("Expected: " + expected);
1020 console.log("Actual: " + actual);
1021 fail();
1022 }
1023 next();
1024 });
1025 });
1026 });
1027});
1028},
1029
88e2cdaa 1030// BIP32 extended private key is shown
9e9dcfda
IC
1031function() {
1032page.open(url, function(status) {
1033 // set the phrase
1034 var expected = "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1035 page.evaluate(function() {
1036 $(".phrase").val("abandon abandon ability");
1037 $(".phrase").trigger("input");
1038 });
1039 // change tabs
1040 waitForGenerate(function() {
1041 page.evaluate(function() {
1042 $("#bip32-tab a").click();
1043 });
1044 // check the extended private key is generated correctly
1045 waitForGenerate(function() {
1046 var actual = page.evaluate(function() {
1047 return $(".extended-priv-key").val();
1048 });
1049 if (actual != expected) {
1050 console.log("BIP32 extended private key is incorrect");
1051 console.log("Expected: " + expected);
1052 console.log("Actual: " + actual);
1053 fail();
1054 }
1055 next();
1056 });
1057 });
1058});
1059},
1060
88e2cdaa 1061// BIP32 extended public key is shown
9e9dcfda
IC
1062function() {
1063page.open(url, function(status) {
1064 // set the phrase
1065 var expected = "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1066 page.evaluate(function() {
1067 $(".phrase").val("abandon abandon ability");
1068 $(".phrase").trigger("input");
1069 });
1070 // change tabs
1071 waitForGenerate(function() {
1072 page.evaluate(function() {
1073 $("#bip32-tab a").click();
1074 });
1075 // check the extended public key is generated correctly
1076 waitForGenerate(function() {
1077 var actual = page.evaluate(function() {
1078 return $(".extended-pub-key").val();
1079 });
1080 if (actual != expected) {
1081 console.log("BIP32 extended public key is incorrect");
1082 console.log("Expected: " + expected);
1083 console.log("Actual: " + actual);
1084 fail();
1085 }
1086 next();
1087 });
1088 });
1089});
1090},
88e2cdaa
IC
1091
1092// Derivation path is shown in table
5f844c62
IC
1093function() {
1094page.open(url, function(status) {
1095 // set the phrase
1096 var expected = "m/44'/0'/0'/0/0";
1097 page.evaluate(function() {
1098 $(".phrase").val("abandon abandon ability");
1099 $(".phrase").trigger("input");
1100 });
1101 // check for derivation path in table
1102 waitForGenerate(function() {
1103 var actual = page.evaluate(function() {
1104 return $(".index:first").text();
1105 });
1106 if (actual != expected) {
1107 console.log("Derivation path shown incorrectly in table");
1108 console.log("Expected: " + expected);
1109 console.log("Actual: " + actual);
1110 fail();
1111 }
1112 next();
1113 });
1114});
1115},
1116
88e2cdaa 1117// Derivation path for address can be hardened
4974fd7f
IC
1118function() {
1119page.open(url, function(status) {
1120 // set the phrase
1121 var expected = "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1122 page.evaluate(function() {
1123 $(".phrase").val("abandon abandon ability");
1124 $(".phrase").trigger("input");
1125 });
1126 // change tabs
1127 waitForGenerate(function() {
1128 page.evaluate(function() {
1129 $("#bip32-tab a").click();
1130 });
1131 waitForGenerate(function() {
1132 // select the hardened addresses option
1133 page.evaluate(function() {
1134 $(".hardened-addresses").prop("checked", true);
1135 $(".hardened-addresses").trigger("change");
1136 });
1137 waitForGenerate(function() {
1138 // check the generated address is hardened
1139 var actual = page.evaluate(function() {
1140 return $(".address:first").text();
1141 });
1142 if (actual != expected) {
1143 console.log("Hardened address is incorrect");
1144 console.log("Expected: " + expected);
1145 console.log("Actual: " + actual);
1146 fail();
1147 }
1148 next();
1149 });
1150 });
1151 });
1152});
1153},
1154
88e2cdaa 1155// Derivation path visibility can be toggled
a775b5c6
IC
1156function() {
1157page.open(url, function(status) {
1158 // set the phrase
1159 page.evaluate(function() {
1160 $(".phrase").val("abandon abandon ability");
1161 $(".phrase").trigger("input");
1162 });
a775b5c6
IC
1163 waitForGenerate(function() {
1164 // toggle path visibility
1165 page.evaluate(function() {
1166 $(".index-toggle").click();
1167 });
1168 // check the path is not visible
1169 var isInvisible = page.evaluate(function() {
1170 return $(".index:first span").hasClass("invisible");
1171 });
1172 if (!isInvisible) {
1173 console.log("Toggled derivation path is visible");
1174 fail();
1175 }
1176 next();
1177 });
1178});
1179},
1180
88e2cdaa 1181// Address is shown
06c4c6e3
IC
1182function() {
1183page.open(url, function(status) {
1184 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1185 // set the phrase
1186 page.evaluate(function() {
1187 $(".phrase").val("abandon abandon ability").trigger("input");
1188 });
1189 // get the address
1190 waitForGenerate(function() {
1191 var actual = page.evaluate(function() {
1192 return $(".address:first").text();
1193 });
1194 if (actual != expected) {
1195 console.log("Address is not shown");
1196 console.log("Expected: " + expected);
1197 console.log("Got: " + actual);
1198 fail();
1199 }
1200 next();
1201 });
1202});
1203},
1204
88e2cdaa 1205// Addresses are shown in order of derivation path
3f1faa4d
IC
1206function() {
1207page.open(url, function(status) {
1208 // set the phrase
1209 page.evaluate(function() {
1210 $(".phrase").val("abandon abandon ability").trigger("input");
1211 });
1212 // get the derivation paths
1213 waitForGenerate(function() {
1214 var paths = page.evaluate(function() {
1215 return $(".index").map(function(i, e) {
1216 return $(e).text();
1217 });
1218 });
1219 if (paths.length != 20) {
1220 console.log("Total paths is less than expected: " + paths.length);
1221 fail();
1222 }
1223 for (var i=0; i<paths.length; i++) {
1224 var expected = "m/44'/0'/0'/0/" + i;
1225 var actual = paths[i];
1226 if (actual != expected) {
1227 console.log("Path " + i + " is incorrect");
1228 console.log("Expected: " + expected);
1229 console.log("Actual: " + actual);
1230 fail();
1231 }
1232 }
1233 next();
1234 });
1235});
1236},
21372fab 1237
88e2cdaa 1238// Address visibility can be toggled
21372fab
IC
1239function() {
1240page.open(url, function(status) {
1241 // set the phrase
1242 page.evaluate(function() {
1243 $(".phrase").val("abandon abandon ability");
1244 $(".phrase").trigger("input");
1245 });
1246 waitForGenerate(function() {
1247 // toggle address visibility
1248 page.evaluate(function() {
1249 $(".address-toggle").click();
1250 });
1251 // check the address is not visible
1252 var isInvisible = page.evaluate(function() {
1253 return $(".address:first span").hasClass("invisible");
1254 });
1255 if (!isInvisible) {
1256 console.log("Toggled address is visible");
1257 fail();
1258 }
1259 next();
1260 });
1261});
1262},
1263
1b12b2f5
IC
1264// Public key is shown
1265function() {
1266page.open(url, function(status) {
1267 var expected = "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1268 // set the phrase
1269 page.evaluate(function() {
1270 $(".phrase").val("abandon abandon ability").trigger("input");
1271 });
1272 // get the address
1273 waitForGenerate(function() {
1274 var actual = page.evaluate(function() {
1275 return $(".pubkey:first").text();
1276 });
1277 if (actual != expected) {
1278 console.log("Public key is not shown");
1279 console.log("Expected: " + expected);
1280 console.log("Got: " + actual);
1281 fail();
1282 }
1283 next();
1284 });
1285});
1286},
1287
1288// Public key visibility can be toggled
1289function() {
1290page.open(url, function(status) {
1291 // set the phrase
1292 page.evaluate(function() {
1293 $(".phrase").val("abandon abandon ability");
1294 $(".phrase").trigger("input");
1295 });
1296 waitForGenerate(function() {
1297 // toggle public key visibility
1298 page.evaluate(function() {
1299 $(".public-key-toggle").click();
1300 });
1301 // check the public key is not visible
1302 var isInvisible = page.evaluate(function() {
1303 return $(".pubkey:first span").hasClass("invisible");
1304 });
1305 if (!isInvisible) {
1306 console.log("Toggled public key is visible");
1307 fail();
1308 }
1309 next();
1310 });
1311});
1312},
1313
88e2cdaa 1314// Private key is shown
8cd5e231
IC
1315function() {
1316page.open(url, function(status) {
1317 var expected = "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1318 // set the phrase
1319 page.evaluate(function() {
1320 $(".phrase").val("abandon abandon ability").trigger("input");
1321 });
1322 // get the address
1323 waitForGenerate(function() {
1324 var actual = page.evaluate(function() {
1325 return $(".privkey:first").text();
1326 });
1327 if (actual != expected) {
1328 console.log("Private key is not shown");
1329 console.log("Expected: " + expected);
1330 console.log("Got: " + actual);
1331 fail();
1332 }
1333 next();
1334 });
1335});
1336},
1337
88e2cdaa 1338// Private key visibility can be toggled
6848d03b
IC
1339function() {
1340page.open(url, function(status) {
1341 // set the phrase
1342 page.evaluate(function() {
1343 $(".phrase").val("abandon abandon ability");
1344 $(".phrase").trigger("input");
1345 });
1346 waitForGenerate(function() {
1347 // toggle private key visibility
1348 page.evaluate(function() {
1349 $(".private-key-toggle").click();
1350 });
1351 // check the private key is not visible
1352 var isInvisible = page.evaluate(function() {
1353 return $(".privkey:first span").hasClass("invisible");
1354 });
1355 if (!isInvisible) {
1356 console.log("Toggled private key is visible");
1357 fail();
1358 }
1359 next();
1360 });
1361});
1362},
88e2cdaa
IC
1363
1364// More addresses can be generated
35631659
IC
1365function() {
1366page.open(url, function(status) {
1367 // set the phrase
1368 page.evaluate(function() {
1369 $(".phrase").val("abandon abandon ability");
1370 $(".phrase").trigger("input");
1371 });
1372 waitForGenerate(function() {
1373 // generate more addresses
1374 page.evaluate(function() {
1375 $(".more").click();
1376 });
1377 waitForGenerate(function() {
1378 // check there are more addresses
1379 var addressCount = page.evaluate(function() {
1380 return $(".address").length;
1381 });
1382 if (addressCount != 40) {
1383 console.log("More addresses cannot be generated");
1384 fail();
1385 }
1386 next();
1387 });
1388 });
1389});
1390},
1391
88e2cdaa 1392// A custom number of additional addresses can be generated
8a89b9da
IC
1393function() {
1394page.open(url, function(status) {
1395 // set the phrase
1396 page.evaluate(function() {
1397 $(".phrase").val("abandon abandon ability");
1398 $(".phrase").trigger("input");
1399 });
1400 waitForGenerate(function() {
1401 // get the current number of addresses
1402 var oldAddressCount = page.evaluate(function() {
1403 return $(".address").length;
1404 });
1405 // set a custom number of additional addresses
1406 page.evaluate(function() {
1407 $(".rows-to-add").val(1);
1408 });
1409 // generate more addresses
1410 page.evaluate(function() {
1411 $(".more").click();
1412 });
1413 waitForGenerate(function() {
1414 // check there are the correct number of addresses
1415 var newAddressCount = page.evaluate(function() {
1416 return $(".address").length;
1417 });
1418 if (newAddressCount - oldAddressCount != 1) {
1419 console.log("Number of additional addresses cannot be customized");
1420 console.log(newAddressCount)
1421 console.log(oldAddressCount)
1422 fail();
1423 }
1424 next();
1425 });
1426 });
1427});
1428},
1429
88e2cdaa 1430// Additional addresses are shown in order of derivation path
4d387bf5
IC
1431function() {
1432page.open(url, function(status) {
1433 // set the phrase
1434 page.evaluate(function() {
1435 $(".phrase").val("abandon abandon ability").trigger("input");
1436 });
1437 waitForGenerate(function() {
1438 // generate more addresses
1439 page.evaluate(function() {
1440 $(".more").click();
1441 });
1442 // get the derivation paths
1443 waitForGenerate(function() {
1444 var paths = page.evaluate(function() {
1445 return $(".index").map(function(i, e) {
1446 return $(e).text();
1447 });
1448 });
1449 if (paths.length != 40) {
1450 console.log("Total additional paths is less than expected: " + paths.length);
1451 fail();
1452 }
1453 for (var i=0; i<paths.length; i++) {
1454 var expected = "m/44'/0'/0'/0/" + i;
1455 var actual = paths[i];
1456 if (actual != expected) {
1457 console.log("Path " + i + " is not in correct order");
1458 console.log("Expected: " + expected);
1459 console.log("Actual: " + actual);
1460 fail();
1461 }
1462 }
1463 next();
1464 });
1465 });
1466});
1467},
88e2cdaa
IC
1468
1469// BIP32 root key can be set by the user
61ed16a9
IC
1470function() {
1471page.open(url, function(status) {
1472 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1473 // set the root key
1474 page.evaluate(function() {
1475 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1476 });
1477 waitForGenerate(function() {
1478 var actual = page.evaluate(function() {
1479 return $(".address:first").text();
1480 });
1481 if (actual != expected) {
bc324fd2 1482 console.log("Setting BIP32 root key results in wrong address");
61ed16a9
IC
1483 console.log("Expected: " + expected);
1484 console.log("Actual: " + actual);
1485 fail();
1486 }
1487 next();
1488 });
1489});
1490},
1491
54563907 1492// Setting BIP32 root key clears the existing phrase, passphrase and seed
bc324fd2
IC
1493function() {
1494page.open(url, function(status) {
1495 var expected = "";
1496 // set a mnemonic
1497 page.evaluate(function() {
1498 $(".phrase").val("A non-blank but invalid value");
1499 });
1500 // Accept any confirm dialogs
1501 page.onConfirm = function() {
1502 return true;
1503 };
1504 // set the root key
1505 page.evaluate(function() {
1506 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1507 });
1508 waitForGenerate(function() {
1509 var actual = page.evaluate(function() {
1510 return $(".phrase").val();
1511 });
1512 if (actual != expected) {
1513 console.log("Phrase not cleared when setting BIP32 root key");
1514 console.log("Expected: " + expected);
1515 console.log("Actual: " + actual);
1516 fail();
1517 }
1518 next();
1519 });
1520});
1521},
1522
54563907 1523// Clearing of phrase, passphrase and seed can be cancelled by user
abfbe450
IC
1524function() {
1525page.open(url, function(status) {
1526 var expected = "abandon abandon ability";
1527 // set a mnemonic
1528 page.evaluate(function() {
1529 $(".phrase").val("abandon abandon ability");
1530 });
1531 // Cancel any confirm dialogs
1532 page.onConfirm = function() {
1533 return false;
1534 };
1535 // set the root key
1536 page.evaluate(function() {
1537 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1538 });
1539 var actual = page.evaluate(function() {
1540 return $(".phrase").val();
1541 });
1542 if (actual != expected) {
1543 console.log("Phrase not retained when cancelling changes to BIP32 root key");
1544 console.log("Expected: " + expected);
1545 console.log("Actual: " + actual);
1546 fail();
1547 }
1548 next();
1549});
1550},
7ff86d4c 1551
88e2cdaa 1552// Custom BIP32 root key is used when changing the derivation path
7ff86d4c
IC
1553function() {
1554page.open(url, function(status) {
1555 var expected = "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1556 // set the root key
1557 page.evaluate(function() {
1558 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1559 });
1560 waitForGenerate(function() {
1561 // change the derivation path
1562 page.evaluate(function() {
1563 $("#account").val("1").trigger("input");
1564 });
1565 // check the bip32 root key is used for derivation, not the blank phrase
1566 waitForGenerate(function() {
1567 var actual = page.evaluate(function() {
1568 return $(".address:first").text();
1569 });
1570 if (actual != expected) {
1571 console.log("Changing the derivation path does not use BIP32 root key");
1572 console.log("Expected: " + expected);
1573 console.log("Actual: " + actual);
1574 fail();
1575 }
1576 next();
1577 });
1578 });
1579});
1580},
88e2cdaa
IC
1581
1582// Incorrect mnemonic shows error
c3719b00
IC
1583function() {
1584page.open(url, function(status) {
1585 // set the root key
1586 page.evaluate(function() {
1587 $(".phrase").val("abandon abandon abandon").trigger("input");
1588 });
1589 waitForFeedback(function() {
1590 // check there is an error shown
1591 var feedback = page.evaluate(function() {
1592 return $(".feedback").text();
1593 });
1594 if (feedback.length <= 0) {
1595 console.log("Invalid mnemonic does not show error");
1596 fail();
1597 }
1598 next();
1599 });
1600});
1601},
1602
88e2cdaa 1603// Incorrect word shows suggested replacement
20f80cfa
IC
1604function() {
1605page.open(url, function(status) {
1606 // set the root key
1607 page.evaluate(function() {
1608 $(".phrase").val("abandon abandon abiliti").trigger("input");
1609 });
1610 // check there is a suggestion shown
1611 waitForFeedback(function() {
1612 var feedback = page.evaluate(function() {
1613 return $(".feedback").text();
1614 });
1615 if (feedback.indexOf("did you mean ability?") < 0) {
1616 console.log("Incorrect word does not show suggested replacement");
1617 console.log("Error: " + error);
1618 fail();
6ea15134
IC
1619 }
1620 next();
1621 });
1622});
1623},
1624
1625// Github pull request 48
1626// First four letters of word shows that word, not closest
1627// since first four letters gives unique word in BIP39 wordlist
1628// eg ille should show illegal, not idle
1629function() {
1630page.open(url, function(status) {
1631 // set the incomplete word
1632 page.evaluate(function() {
1633 $(".phrase").val("ille").trigger("input");
1634 });
1635 // check there is a suggestion shown
1636 waitForFeedback(function() {
1637 var feedback = page.evaluate(function() {
1638 return $(".feedback").text();
1639 });
1640 if (feedback.indexOf("did you mean illegal?") < 0) {
1641 console.log("Start of word does not show correct suggestion");
1642 console.log("Error: " + error);
1643 fail();
20f80cfa
IC
1644 }
1645 next();
1646 });
1647});
1648},
1649
88e2cdaa 1650// Incorrect BIP32 root key shows error
02f4a90e
IC
1651function() {
1652page.open(url, function(status) {
1653 // set the root key
1654 page.evaluate(function() {
1655 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1656 });
1657 // check there is an error shown
1658 waitForFeedback(function() {
1659 var feedback = page.evaluate(function() {
1660 return $(".feedback").text();
1661 });
1662 if (feedback != "Invalid root key") {
1663 console.log("Invalid root key does not show error");
1664 console.log("Error: " + error);
1665 fail();
1666 }
1667 next();
1668 });
1669});
1670},
1671
88e2cdaa 1672// Derivation path not starting with m shows error
f976b541
IC
1673function() {
1674page.open(url, function(status) {
1675 // set the mnemonic phrase
1676 page.evaluate(function() {
1677 $(".phrase").val("abandon abandon ability").trigger("input");
1678 });
1679 waitForGenerate(function() {
1680 // select the bip32 tab so custom derivation path can be set
1681 page.evaluate(function() {
1682 $("#bip32-tab a").click();
1683 });
1684 waitForGenerate(function() {
1685 // set the incorrect derivation path
1686 page.evaluate(function() {
1687 $("#bip32 .path").val("n/0").trigger("input");
1688 });
1689 waitForFeedback(function() {
1690 var feedback = page.evaluate(function() {
1691 return $(".feedback").text();
1692 });
1693 if (feedback != "First character must be 'm'") {
1694 console.log("Derivation path not starting with m should show error");
1695 console.log("Error: " + error);
1696 fail();
1697 }
1698 next();
1699 });
1700 });
1701 });
1702});
1703},
1704
88e2cdaa 1705// Derivation path containing invalid characters shows useful error
55c0ffcb
IC
1706function() {
1707page.open(url, function(status) {
1708 // set the mnemonic phrase
1709 page.evaluate(function() {
1710 $(".phrase").val("abandon abandon ability").trigger("input");
1711 });
1712 waitForGenerate(function() {
1713 // select the bip32 tab so custom derivation path can be set
1714 page.evaluate(function() {
1715 $("#bip32-tab a").click();
1716 });
1717 waitForGenerate(function() {
1718 // set the incorrect derivation path
1719 page.evaluate(function() {
1720 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1721 });
1722 waitForFeedback(function() {
1723 var feedback = page.evaluate(function() {
1724 return $(".feedback").text();
1725 });
1726 if (feedback != "Invalid characters 0wrong1 found at depth 2") {
1727 console.log("Derivation path with invalid characters should show error");
1728 console.log("Error: " + error);
1729 fail();
1730 }
1731 next();
1732 });
1733 });
1734 });
1735});
1736},
88e2cdaa
IC
1737
1738// Github Issue 11: Default word length is 15
b630f83d 1739// https://github.com/iancoleman/bip39/issues/11
ca976aa9
IC
1740function() {
1741page.open(url, function(status) {
1742 // get the word length
1743 var defaultLength = page.evaluate(function() {
1744 return $(".strength").val();
1745 });
1746 if (defaultLength != 15) {
1747 console.log("Default word length is not 15");
1748 fail();
1749 }
1750 next();
1751});
1752},
1753
88e2cdaa
IC
1754
1755// Github Issue 12: Generate more rows with private keys hidden
b630f83d 1756// https://github.com/iancoleman/bip39/issues/12
c97627fa
IC
1757function() {
1758page.open(url, function(status) {
1759 // set the phrase
1760 page.evaluate(function() {
1761 $(".phrase").val("abandon abandon ability");
1762 $(".phrase").trigger("input");
1763 });
1764 waitForGenerate(function() {
1765 // toggle private keys hidden, then generate more addresses
1766 page.evaluate(function() {
1767 $(".private-key-toggle").click();
1768 $(".more").click();
1769 });
1770 waitForGenerate(function() {
1771 // check more have been generated
1772 var expected = 40;
1773 var numPrivKeys = page.evaluate(function() {
1774 return $(".privkey").length;
1775 });
1776 if (numPrivKeys != expected) {
1777 console.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1778 console.log("Expected: " + expected);
1779 console.log("Actual: " + numPrivKeys);
1780 fail();
1781 }
1782 // check no private keys are shown
1783 var numHiddenPrivKeys = page.evaluate(function() {
1784 return $(".privkey span[class=invisible]").length;
1785 });
1786 if (numHiddenPrivKeys != expected) {
1787 console.log("Generating more does not retain hidden state of privkeys");
1788 console.log("Expected: " + expected);
1789 console.log("Actual: " + numHiddenPrivKeys);
1790 fail();
1791 }
1792 next();
1793 });
1794 });
1795});
1796},
88e2cdaa
IC
1797
1798// Github Issue 19: Mnemonic is not sensitive to whitespace
b630f83d 1799// https://github.com/iancoleman/bip39/issues/19
a7becc43
IC
1800function() {
1801page.open(url, function(status) {
1802 // set the phrase
1803 var expected = "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1804 page.evaluate(function() {
1805 var doubleSpace = " ";
1806 $(".phrase").val("urge cat" + doubleSpace + "bid");
1807 $(".phrase").trigger("input");
1808 });
1809 waitForGenerate(function() {
1810 // Check the bip32 root key is correct
1811 var actual = page.evaluate(function() {
1812 return $(".root-key").val();
1813 });
1814 if (actual != expected) {
1815 console.log("Mnemonic is sensitive to whitespace");
1816 console.log("Expected: " + expected);
1817 console.log("Actual: " + actual);
1818 fail();
1819 }
1820 next();
1821 });
1822});
1823},
88e2cdaa 1824
e3001539 1825// Github Issue 23: Part 1: Use correct derivation path when changing tabs
b630f83d 1826// https://github.com/iancoleman/bip39/issues/23
e3001539
IC
1827function() {
1828page.open(url, function(status) {
1829 // 1) and 2) set the phrase
1830 page.evaluate(function() {
1831 $(".phrase").val("abandon abandon ability").trigger("input");
1832 });
1833 waitForGenerate(function() {
1834 // 3) select bip32 tab
1835 page.evaluate(function() {
1836 $("#bip32-tab a").click();
1837 });
1838 waitForGenerate(function() {
1839 // 4) switch from bitcoin to litecoin
1840 page.evaluate(function() {
1841 $(".network").val("2").trigger("change");
1842 });
1843 waitForGenerate(function() {
1844 // 5) Check derivation path is displayed correctly
1845 var expected = "m/0/0";
1846 var actual = page.evaluate(function() {
1847 return $(".index:first").text();
1848 });
1849 if (actual != expected) {
1850 console.log("Github Issue 23 Part 1: derivation path display error");
1851 console.log("Expected: " + expected);
1852 console.log("Actual: " + actual);
1853 fail();
1854 }
1855 // 5) Check address is displayed correctly
1856 var expected = "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
1857 var actual = page.evaluate(function() {
1858 return $(".address:first").text();
1859 });
1860 if (actual != expected) {
1861 console.log("Github Issue 23 Part 1: address display error");
1862 console.log("Expected: " + expected);
1863 console.log("Actual: " + actual);
1864 fail();
1865 }
1866 next();
1867 });
1868 });
1869 });
1870});
1871},
1872
1873// Github Issue 23 Part 2: Coin selection in derivation path
b630f83d 1874// https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
af4fd3a2
IC
1875function() {
1876page.open(url, function(status) {
1877 // set the phrase
1878 page.evaluate(function() {
1879 $(".phrase").val("abandon abandon ability").trigger("input");
1880 });
1881 waitForGenerate(function() {
1882 // switch from bitcoin to clam
1883 page.evaluate(function() {
1884 $(".network").val("9").trigger("change");
1885 });
1886 waitForGenerate(function() {
1887 // check derivation path is displayed correctly
1888 var expected = "m/44'/23'/0'/0/0";
1889 var actual = page.evaluate(function() {
1890 return $(".index:first").text();
1891 });
1892 if (actual != expected) {
1893 console.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
1894 console.log("Expected: " + expected);
1895 console.log("Actual: " + actual);
1896 fail();
1897 }
1898 next();
1899 });
1900 });
1901});
1902},
88e2cdaa 1903
f3d0aca1 1904// Github Issue 26: When using a Root key derrived altcoins are incorrect
b630f83d 1905// https://github.com/iancoleman/bip39/issues/26
558ef9ac
IC
1906function() {
1907page.open(url, function(status) {
1908 // 1) 2) and 3) set the root key
1909 page.evaluate(function() {
1910 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1911 });
1912 waitForGenerate(function() {
1913 // 4) switch from bitcoin to viacoin
1914 page.evaluate(function() {
1915 $(".network").val("6").trigger("change");
1916 });
1917 waitForGenerate(function() {
1918 // 5) ensure the derived address is correct
1919 var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
1920 var actual = page.evaluate(function() {
1921 return $(".address:first").text();
1922 });
1923 if (actual != expected) {
1924 console.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
1925 console.log("Expected: " + expected);
1926 console.log("Actual: " + actual);
1927 fail();
1928 }
1929 next();
1930 });
1931 });
1932});
1933},
f3d0aca1 1934
e1bae843
IC
1935// Selecting a language with no existing phrase should generate a phrase in
1936// that language.
1937function() {
1938page.open(url, function(status) {
1939 // Select a language
1940 // Need to manually simulate hash being set due to quirk between
1941 // 'click' event triggered by javascript vs triggered by mouse.
1942 // Perhaps look into page.sendEvent
1943 // http://phantomjs.org/api/webpage/method/send-event.html
1944 page.evaluate(function() {
1945 window.location.hash = "#japanese";
1946 $("a[href='#japanese']").trigger("click");
1947 });
1948 waitForGenerate(function() {
1949 // Check the mnemonic is in Japanese
1950 var phrase = page.evaluate(function() {
1951 return $(".phrase").val();
1952 });
1953 if (phrase.length <= 0) {
1954 console.log("No Japanese phrase generated");
1955 fail();
1956 }
1957 if (phrase.charCodeAt(0) < 128) {
1958 console.log("First character of Japanese phrase is ascii");
1959 console.log("Phrase: " + phrase);
1960 fail();
1961 }
1962 next();
1963 });
1964});
1965},
1966
1967// Selecting a language with existing phrase should update the phrase to use
1968// that language.
1969function() {
1970page.open(url, function(status) {
1971 // Set the phrase to an English phrase.
1972 page.evaluate(function() {
1973 $(".phrase").val("abandon abandon ability").trigger("input");
1974 });
1975 waitForGenerate(function() {
1976 // Change to Italian
1977 // Need to manually simulate hash being set due to quirk between
1978 // 'click' event triggered by javascript vs triggered by mouse.
1979 // Perhaps look into page.sendEvent
1980 // http://phantomjs.org/api/webpage/method/send-event.html
1981 page.evaluate(function() {
1982 window.location.hash = "#italian";
1983 $("a[href='#italian']").trigger("click");
1984 });
1985 waitForGenerate(function() {
1986 // Check only the language changes, not the phrase
1987 var expected = "abaco abaco abbaglio";
1988 var actual = page.evaluate(function() {
1989 return $(".phrase").val();
1990 });
1991 if (actual != expected) {
1992 console.log("Changing language with existing phrase");
1993 console.log("Expected: " + expected);
1994 console.log("Actual: " + actual);
1995 fail();
1996 }
1997 // Check the address is correct
1998 var expected = "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
1999 var actual = page.evaluate(function() {
2000 return $(".address:first").text();
2001 });
2002 if (actual != expected) {
2003 console.log("Changing language generates incorrect address");
2004 console.log("Expected: " + expected);
2005 console.log("Actual: " + actual);
2006 fail();
2007 }
2008 next();
2009 });
2010 });
2011});
2012},
2013
2014// Suggested replacement for erroneous word in non-English language
2015function() {
2016page.open(url, function(status) {
2017 // Set an incorrect phrase in Italian
2018 page.evaluate(function() {
2019 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2020 });
2021 waitForFeedback(function() {
2022 // Check the suggestion is correct
2023 var feedback = page.evaluate(function() {
2024 return $(".feedback").text();
2025 });
2026 if (feedback.indexOf("did you mean abbaglio?") < 0) {
2027 console.log("Incorrect Italian word does not show suggested replacement");
2028 console.log("Error: " + error);
2029 fail();
2030 }
2031 next();
2032 });
2033});
2034},
2035
2036
2037// Japanese word does not break across lines.
2038// Point 2 from
2039// https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2040function() {
2041page.open(url, function(status) {
2042 hasWordBreakCss = page.content.indexOf("word-break: keep-all;") > -1;
2043 if (!hasWordBreakCss) {
2044 console.log("Japanese words can break across lines mid-word");
2045 console.log("Check CSS for '.phrase { word-break: keep-all; }'");
2046 fail();
2047 }
2048 // Run the next test
2049 next();
2050});
2051},
2052
2053// Language can be specified at page load using hash value in url
2054function() {
2055page.open(url, function(status) {
2056 // Set the page hash as if it were on a fresh page load
2057 page.evaluate(function() {
2058 window.location.hash = "#japanese";
2059 });
2060 // Generate a random phrase
2061 page.evaluate(function() {
2062 $(".generate").trigger("click");
2063 });
2064 waitForGenerate(function() {
2065 // Check the phrase is in Japanese
2066 var phrase = page.evaluate(function() {
2067 return $(".phrase").val();
2068 });
2069 if (phrase.length <= 0) {
2070 console.log("No phrase generated using url hash");
2071 fail();
2072 }
2073 if (phrase.charCodeAt(0) < 128) {
2074 console.log("Language not detected from url hash on page load.");
2075 console.log("Phrase: " + phrase);
2076 fail();
2077 }
2078 next();
2079 });
2080});
2081},
2082
c6624d51
IC
2083// Entropy unit tests
2084function() {
2085page.open(url, function(status) {
adc8ce12 2086 var response = page.evaluate(function() {
c6624d51
IC
2087 var e;
2088 // binary entropy is detected
adc8ce12
IC
2089 try {
2090 e = Entropy.fromString("01010101");
2091 if (e.base.str != "binary") {
2092 return "Binary entropy not detected correctly";
2093 }
2094 }
2095 catch (e) {
2096 return e.message;
c6624d51
IC
2097 }
2098 // base6 entropy is detected
adc8ce12
IC
2099 try {
2100 e = Entropy.fromString("012345012345");
2101 if (e.base.str != "base 6") {
2102 return "base6 entropy not detected correctly";
2103 }
2104 }
2105 catch (e) {
2106 return e.message;
c6624d51
IC
2107 }
2108 // dice entropy is detected
adc8ce12
IC
2109 try {
2110 e = Entropy.fromString("123456123456");
2111 if (e.base.str != "base 6 (dice)") {
2112 return "dice entropy not detected correctly";
2113 }
2114 }
2115 catch (e) {
2116 return e.message;
c6624d51
IC
2117 }
2118 // base10 entropy is detected
adc8ce12
IC
2119 try {
2120 e = Entropy.fromString("0123456789");
2121 if (e.base.str != "base 10") {
2122 return "base10 entropy not detected correctly";
2123 }
2124 }
2125 catch (e) {
2126 return e.message;
c6624d51
IC
2127 }
2128 // hex entropy is detected
adc8ce12
IC
2129 try {
2130 e = Entropy.fromString("0123456789ABCDEF");
2131 if (e.base.str != "hexadecimal") {
2132 return "hexadecimal entropy not detected correctly";
2133 }
2134 }
2135 catch (e) {
2136 return e.message;
2137 }
2138 // card entropy is detected
2139 try {
2140 e = Entropy.fromString("AC4DTHKS");
2141 if (e.base.str != "card") {
2142 return "card entropy not detected correctly";
2143 }
2144 }
2145 catch (e) {
2146 return e.message;
c6624d51
IC
2147 }
2148 // entropy is case insensitive
adc8ce12
IC
2149 try {
2150 e = Entropy.fromString("aBcDeF");
2151 if (e.cleanStr != "aBcDeF") {
2152 return "Entropy should not be case sensitive";
2153 }
2154 }
2155 catch (e) {
2156 return e.message;
c6624d51
IC
2157 }
2158 // dice entropy is converted to base6
adc8ce12
IC
2159 try {
2160 e = Entropy.fromString("123456");
425b75a9 2161 if (e.cleanStr != "123450") {
adc8ce12
IC
2162 return "Dice entropy is not automatically converted to base6";
2163 }
2164 }
2165 catch (e) {
2166 return e.message;
c6624d51
IC
2167 }
2168 // dice entropy is preferred to base6 if ambiguous
adc8ce12
IC
2169 try {
2170 e = Entropy.fromString("12345");
2171 if (e.base.str != "base 6 (dice)") {
2172 return "dice not used as default over base 6";
2173 }
2174 }
2175 catch (e) {
2176 return e.message;
c6624d51
IC
2177 }
2178 // unused characters are ignored
adc8ce12
IC
2179 try {
2180 e = Entropy.fromString("fghijkl");
2181 if (e.cleanStr != "f") {
2182 return "additional characters are not ignored";
2183 }
2184 }
2185 catch (e) {
2186 return e.message;
c6624d51
IC
2187 }
2188 // the lowest base is used by default
2189 // 7 could be decimal or hexadecimal, but should be detected as decimal
adc8ce12
IC
2190 try {
2191 e = Entropy.fromString("7");
2192 if (e.base.str != "base 10") {
2193 return "lowest base is not used";
2194 }
c6624d51 2195 }
adc8ce12
IC
2196 catch (e) {
2197 return e.message;
c6624d51
IC
2198 }
2199 // Leading zeros are retained
adc8ce12
IC
2200 try {
2201 e = Entropy.fromString("000A");
2202 if (e.cleanStr != "000A") {
2203 return "Leading zeros are not retained";
2204 }
2205 }
2206 catch (e) {
2207 return e.message;
c6624d51
IC
2208 }
2209 // Leading zeros are correctly preserved for hex in binary string
adc8ce12
IC
2210 try {
2211 e = Entropy.fromString("2A");
2212 if (e.binaryStr != "00101010") {
2213 return "Hex leading zeros are not correct in binary";
2214 }
2215 }
2216 catch (e) {
2217 return e.message;
2218 }
1cf1bbaf
IC
2219 // Leading zeros for base 6 as binary string
2220 // 20 = 2 events at 2.58 bits per event = 5 bits
2221 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2222 // so it needs 1 bit of padding to be the right bit length
adc8ce12 2223 try {
1cf1bbaf
IC
2224 e = Entropy.fromString("20");
2225 if (e.binaryStr != "01100") {
0d0f07f9
IC
2226 return "Base 6 as binary has leading zeros";
2227 }
2228 }
2229 catch (e) {
2230 return e.message;
2231 }
1cf1bbaf 2232 // Leading zeros for base 10 as binary string
0d0f07f9 2233 try {
1cf1bbaf
IC
2234 e = Entropy.fromString("17");
2235 if (e.binaryStr != "010001") {
0d0f07f9
IC
2236 return "Base 10 as binary has leading zeros";
2237 }
2238 }
2239 catch (e) {
2240 return e.message;
2241 }
87ad2c6e 2242 // Leading zeros for card entropy as binary string.
9d33c892 2243 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
0d0f07f9 2244 try {
9d33c892
IC
2245 e = Entropy.fromString("2c");
2246 if (e.binaryStr != "0010") {
0d0f07f9 2247 return "Card entropy as binary has leading zeros";
adc8ce12
IC
2248 }
2249 }
2250 catch (e) {
2251 return e.message;
c6624d51
IC
2252 }
2253 // Keyboard mashing results in weak entropy
2254 // Despite being a long string, it's less than 30 bits of entropy
adc8ce12
IC
2255 try {
2256 e = Entropy.fromString("aj;se ifj; ask,dfv js;ifj");
2257 if (e.binaryStr.length >= 30) {
2258 return "Keyboard mashing should produce weak entropy";
2259 }
c6624d51 2260 }
adc8ce12
IC
2261 catch (e) {
2262 return e.message;
2263 }
2264 // Card entropy is used if every pair could be a card
2265 try {
2266 e = Entropy.fromString("4c3c2c");
2267 if (e.base.str != "card") {
2268 return "Card entropy not used if all pairs are cards";
2269 }
2270 }
2271 catch (e) {
2272 return e.message;
2273 }
2274 // Card entropy uses base 52
2275 // [ cards, binary ]
2276 try {
2277 var cards = [
9d33c892
IC
2278 [ "ac", "0101" ],
2279 [ "acqs", "11011100" ],
2280 [ "acks", "01011100" ],
2281 [ "2cac", "11111000" ],
2282 [ "2c", "0010" ],
2283 [ "3d", "0001" ],
2284 [ "4h", "1001" ],
87ad2c6e 2285 [ "5s", "1001" ],
9d33c892
IC
2286 [ "6c", "0000" ],
2287 [ "7d", "0001" ],
87ad2c6e 2288 [ "8h", "1011" ],
9d33c892
IC
2289 [ "9s", "0010" ],
2290 [ "tc", "1001" ],
2291 [ "jd", "1111" ],
2292 [ "qh", "0010" ],
2293 [ "ks", "0101" ],
2294 [ "ks2c", "01010100" ],
2295 [ "KS2C", "01010100" ],
adc8ce12
IC
2296 ];
2297 for (var i=0; i<cards.length; i++) {
2298 var card = cards[i][0];
2299 var result = cards[i][1];
2300 e = Entropy.fromString(card);
2301 console.log(e.binary + " " + result);
2302 if (e.binaryStr !== result) {
886f06ee 2303 return "card entropy " + card + " not parsed correctly: " + result + " != " + e.binaryStr;
adc8ce12
IC
2304 }
2305 }
2306 }
2307 catch (e) {
2308 return e.message;
2309 }
2310 return "PASS";
c6624d51 2311 });
adc8ce12 2312 if (response != "PASS") {
c6624d51 2313 console.log("Entropy unit tests");
adc8ce12 2314 console.log(response);
c6624d51
IC
2315 fail();
2316 };
2317 next();
2318});
2319},
2320
2321// Entropy can be entered by the user
2322function() {
2323page.open(url, function(status) {
2324 expected = {
2325 mnemonic: "abandon abandon ability",
2326 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2327 }
2328 // use entropy
2329 page.evaluate(function() {
2330 $(".use-entropy").prop("checked", true).trigger("change");
2331 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2332 });
2333 // check the mnemonic is set and address is correct
2334 waitForGenerate(function() {
2335 var actual = page.evaluate(function() {
2336 return {
2337 address: $(".address:first").text(),
2338 mnemonic: $(".phrase").val(),
2339 }
2340 });
2341 if (actual.mnemonic != expected.mnemonic) {
2342 console.log("Entropy does not generate correct mnemonic");
2343 console.log("Expected: " + expected.mnemonic);
2344 console.log("Got: " + actual.mnemonic);
2345 fail();
2346 }
2347 if (actual.address != expected.address) {
2348 console.log("Entropy does not generate correct address");
2349 console.log("Expected: " + expected.address);
2350 console.log("Got: " + actual.address);
2351 fail();
2352 }
2353 next();
2354 });
2355});
2356},
2357
2358// A warning about entropy is shown to the user, with additional information
2359function() {
2360page.open(url, function(status) {
2361 // get text content from entropy sections of page
2362 var hasWarning = page.evaluate(function() {
2363 var entropyText = $(".entropy-container").text();
2364 var warning = "mnemonic may be insecure";
2365 if (entropyText.indexOf(warning) == -1) {
2366 return false;
2367 }
2368 var readMoreText = $("#entropy-notes").parent().text();
2369 var goodSources = "flipping a fair coin, rolling a fair dice, noise measurements etc";
2370 if (readMoreText.indexOf(goodSources) == -1) {
2371 return false;
2372 }
2373 return true;
2374 });
2375 // check the warnings and information are shown
2376 if (!hasWarning) {
2377 console.log("Page does not contain warning about using own entropy");
2378 fail();
2379 }
2380 next();
2381});
2382},
2383
2384// The types of entropy available are described to the user
2385function() {
2386page.open(url, function(status) {
2387 // get placeholder text for entropy field
2388 var placeholder = page.evaluate(function() {
2389 return $(".entropy").attr("placeholder");
2390 });
2391 var options = [
2392 "binary",
2393 "base 6",
2394 "dice",
2395 "base 10",
2396 "hexadecimal",
439f0e25 2397 "cards",
c6624d51
IC
2398 ];
2399 for (var i=0; i<options.length; i++) {
2400 var option = options[i];
2401 if (placeholder.indexOf(option) == -1) {
2402 console.log("Available entropy type is not shown to user: " + option);
2403 fail();
2404 }
2405 }
2406 next();
2407});
2408},
2409
2410// The actual entropy used is shown to the user
2411function() {
2412page.open(url, function(status) {
2413 // use entropy
2414 var badEntropySource = page.evaluate(function() {
2415 var entropy = "Not A Very Good Entropy Source At All";
2416 $(".use-entropy").prop("checked", true).trigger("change");
2417 $(".entropy").val(entropy).trigger("input");
2418 });
2419 // check the actual entropy being used is shown
057722b0 2420 waitForEntropyFeedback(function() {
c6624d51
IC
2421 var expectedText = "AedEceAA";
2422 var entropyText = page.evaluate(function() {
2423 return $(".entropy-container").text();
2424 });
2425 if (entropyText.indexOf(expectedText) == -1) {
2426 console.log("Actual entropy used is not shown");
2427 fail();
2428 }
2429 next();
2430 });
2431});
2432},
2433
2434// Binary entropy can be entered
2435function() {
2436page.open(url, function(status) {
2437 // use entropy
2438 page.evaluate(function() {
2439 $(".use-entropy").prop("checked", true).trigger("change");
2440 $(".entropy").val("01").trigger("input");
2441 });
2442 // check the entropy is shown to be the correct type
057722b0 2443 waitForEntropyFeedback(function() {
c6624d51
IC
2444 var entropyText = page.evaluate(function() {
2445 return $(".entropy-container").text();
2446 });
2447 if (entropyText.indexOf("binary") == -1) {
2448 console.log("Binary entropy is not detected and presented to user");
2449 fail();
2450 }
2451 next();
2452 });
2453});
2454},
2455
2456// Base 6 entropy can be entered
2457function() {
2458page.open(url, function(status) {
2459 // use entropy
2460 page.evaluate(function() {
2461 $(".use-entropy").prop("checked", true).trigger("change");
2462 $(".entropy").val("012345").trigger("input");
2463 });
2464 // check the entropy is shown to be the correct type
057722b0 2465 waitForEntropyFeedback(function() {
c6624d51
IC
2466 var entropyText = page.evaluate(function() {
2467 return $(".entropy-container").text();
2468 });
2469 if (entropyText.indexOf("base 6") == -1) {
2470 console.log("Base 6 entropy is not detected and presented to user");
2471 fail();
2472 }
2473 next();
2474 });
2475});
2476},
2477
2478// Base 6 dice entropy can be entered
2479function() {
2480page.open(url, function(status) {
2481 // use entropy
2482 page.evaluate(function() {
2483 $(".use-entropy").prop("checked", true).trigger("change");
2484 $(".entropy").val("123456").trigger("input");
2485 });
2486 // check the entropy is shown to be the correct type
057722b0 2487 waitForEntropyFeedback(function() {
c6624d51
IC
2488 var entropyText = page.evaluate(function() {
2489 return $(".entropy-container").text();
2490 });
2491 if (entropyText.indexOf("dice") == -1) {
2492 console.log("Dice entropy is not detected and presented to user");
2493 fail();
2494 }
2495 next();
2496 });
2497});
2498},
2499
2500// Base 10 entropy can be entered
2501function() {
2502page.open(url, function(status) {
2503 // use entropy
2504 page.evaluate(function() {
2505 $(".use-entropy").prop("checked", true).trigger("change");
2506 $(".entropy").val("789").trigger("input");
2507 });
2508 // check the entropy is shown to be the correct type
057722b0 2509 waitForEntropyFeedback(function() {
c6624d51
IC
2510 var entropyText = page.evaluate(function() {
2511 return $(".entropy-container").text();
2512 });
2513 if (entropyText.indexOf("base 10") == -1) {
2514 console.log("Base 10 entropy is not detected and presented to user");
2515 fail();
2516 }
2517 next();
2518 });
2519});
2520},
2521
2522// Hexadecimal entropy can be entered
2523function() {
2524page.open(url, function(status) {
2525 // use entropy
2526 page.evaluate(function() {
2527 $(".use-entropy").prop("checked", true).trigger("change");
2528 $(".entropy").val("abcdef").trigger("input");
2529 });
2530 // check the entropy is shown to be the correct type
057722b0 2531 waitForEntropyFeedback(function() {
c6624d51
IC
2532 var entropyText = page.evaluate(function() {
2533 return $(".entropy-container").text();
2534 });
2535 if (entropyText.indexOf("hexadecimal") == -1) {
2536 console.log("Hexadecimal entropy is not detected and presented to user");
2537 fail();
2538 }
2539 next();
2540 });
2541});
2542},
2543
2544// Dice entropy value is shown as the converted base 6 value
2545function() {
2546page.open(url, function(status) {
2547 // use entropy
2548 page.evaluate(function() {
2549 $(".use-entropy").prop("checked", true).trigger("change");
2550 $(".entropy").val("123456").trigger("input");
2551 });
2552 // check the entropy is shown as base 6, not as the original dice value
057722b0 2553 waitForEntropyFeedback(function() {
c6624d51
IC
2554 var entropyText = page.evaluate(function() {
2555 return $(".entropy-container").text();
2556 });
425b75a9 2557 if (entropyText.indexOf("123450") == -1) {
c6624d51
IC
2558 console.log("Dice entropy is not shown to user as base 6 value");
2559 fail();
2560 }
2561 if (entropyText.indexOf("123456") > -1) {
2562 console.log("Dice entropy value is shown instead of true base 6 value");
2563 fail();
2564 }
2565 next();
2566 });
2567});
2568},
2569
2570// The number of bits of entropy accumulated is shown
2571function() {
2572page.open(url, function(status) {
057722b0
IC
2573 //[ entropy, bits ]
2574 var tests = [
2575 [ "0000 0000 0000 0000 0000", "20" ],
2576 [ "0", "1" ],
2577 [ "0000", "4" ],
0d0f07f9
IC
2578 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2579 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
057722b0
IC
2580 [ "8", "4" ],
2581 [ "F", "4" ],
1cf1bbaf 2582 [ "29", "6" ],
057722b0
IC
2583 [ "0A", "8" ],
2584 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2585 [ "2A", "8" ],
2586 [ "4A", "8" ],
2587 [ "8A", "8" ],
2588 [ "FA", "8" ],
2589 [ "000A", "16" ],
0d0f07f9
IC
2590 [ "5555", "11" ],
2591 [ "6666", "10" ], // uses dice, so entropy is actually 0000 in base 6, which is 4 lots of 2.58 bits, which is 10.32 bits (rounded down to 10 bits)
1cf1bbaf 2592 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
057722b0
IC
2593 [ "222F", "16" ],
2594 [ "FFFF", "16" ],
1cf1bbaf 2595 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
6422c1cd 2596 [ "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks", "225" ], // cards are not replaced, so a full deck is not 52^52 entropy which is 296 bits, it's 52!, which is 225 bits
057722b0 2597 ]
c6624d51
IC
2598 // use entropy
2599 page.evaluate(function(e) {
2600 $(".use-entropy").prop("checked", true).trigger("change");
2601 });
2602 // Run each test
2603 var nextTest = function runNextTest(i) {
057722b0
IC
2604 var entropy = tests[i][0];
2605 var expected = tests[i][1];
c6624d51
IC
2606 // set entropy
2607 page.evaluate(function(e) {
c6624d51
IC
2608 $(".entropy").val(e).trigger("input");
2609 }, entropy);
2610 // check the number of bits of entropy is shown
057722b0 2611 waitForEntropyFeedback(function() {
c6624d51 2612 var entropyText = page.evaluate(function() {
dd944906 2613 return $(".entropy-container").text();
c6624d51 2614 });
1cf1bbaf 2615 if (entropyText.replace(/\s/g,"").indexOf("Bits" + expected) == -1) {
c6624d51
IC
2616 console.log("Accumulated entropy is not shown correctly for " + entropy);
2617 fail();
2618 }
057722b0 2619 var isLastTest = i == tests.length - 1;
c6624d51
IC
2620 if (isLastTest) {
2621 next();
2622 }
2623 else {
2624 runNextTest(i+1);
2625 }
2626 });
2627 }
2628 nextTest(0);
2629});
2630},
2631
fb353f9d 2632// There is feedback provided about the supplied entropy
c6624d51
IC
2633function() {
2634page.open(url, function(status) {
2635 var tests = [
2636 {
2637 entropy: "A",
fb353f9d
IC
2638 filtered: "A",
2639 type: "hexadecimal",
2640 events: 1,
2641 bits: 4,
c6624d51 2642 words: 0,
1cf1bbaf 2643 strength: "extremely weak",
c6624d51
IC
2644 },
2645 {
2646 entropy: "AAAAAAAA",
fb353f9d
IC
2647 filtered: "AAAAAAAA",
2648 type: "hexadecimal",
2649 events: 8,
2650 bits: 32,
c6624d51 2651 words: 3,
1cf1bbaf 2652 strength: "extremely weak",
c6624d51
IC
2653 },
2654 {
2655 entropy: "AAAAAAAA B",
fb353f9d
IC
2656 filtered: "AAAAAAAAB",
2657 type: "hexadecimal",
2658 events: 9,
2659 bits: 36,
c6624d51 2660 words: 3,
1cf1bbaf 2661 strength: "extremely weak",
c6624d51
IC
2662 },
2663 {
2664 entropy: "AAAAAAAA BBBBBBBB",
fb353f9d
IC
2665 filtered: "AAAAAAAABBBBBBBB",
2666 type: "hexadecimal",
2667 events: 16,
2668 bits: 64,
c6624d51 2669 words: 6,
1cf1bbaf 2670 strength: "very weak",
c6624d51
IC
2671 },
2672 {
2673 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
fb353f9d
IC
2674 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2675 type: "hexadecimal",
2676 events: 24,
2677 bits: 96,
c6624d51 2678 words: 9,
1cf1bbaf 2679 strength: "weak",
c6624d51
IC
2680 },
2681 {
2682 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
fb353f9d
IC
2683 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2684 type: "hexadecimal",
2685 events: 32,
2686 bits: 128,
c6624d51 2687 words: 12,
e6a799cc
IC
2688 strength: "easily cracked",
2689 },
2690 {
2691 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2692 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2693 type: "hexadecimal",
2694 events: 32,
2695 bits: 128,
2696 words: 12,
1cf1bbaf 2697 strength: "strong",
c6624d51
IC
2698 },
2699 {
e6a799cc
IC
2700 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2701 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
fb353f9d
IC
2702 type: "hexadecimal",
2703 events: 40,
2704 bits: 160,
c6624d51 2705 words: 15,
1cf1bbaf
IC
2706 strength: "very strong",
2707 },
2708 {
e6a799cc
IC
2709 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2710 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
fb353f9d
IC
2711 type: "hexadecimal",
2712 events: 48,
2713 bits: 192,
1cf1bbaf
IC
2714 words: 18,
2715 strength: "extremely strong",
fb353f9d 2716 },
391c7f26
IC
2717 {
2718 entropy: "7d",
2719 type: "card",
2720 events: 1,
2721 bits: 5,
2722 words: 0,
2723 strength: "extremely weak",
2724 },
2725 {
2726 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2727 type: "card (full deck)",
2728 events: 52,
6422c1cd
IC
2729 bits: 225,
2730 words: 21,
391c7f26
IC
2731 strength: "extremely strong",
2732 },
2733 {
2734 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2735 type: "card (full deck, 1 duplicate: 3d)",
2736 events: 53,
87ad2c6e 2737 bits: 254,
6422c1cd 2738 words: 21,
391c7f26
IC
2739 strength: "extremely strong",
2740 },
2741 {
2742 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
bbc29c80 2743 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
391c7f26 2744 events: 53,
87ad2c6e 2745 bits: 254,
6422c1cd 2746 words: 21,
391c7f26
IC
2747 strength: "extremely strong",
2748 },
2749 {
2750 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
bbc29c80 2751 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
391c7f26 2752 events: 53,
87ad2c6e
IC
2753 bits: 264,
2754 words: 24,
391c7f26
IC
2755 strength: "extremely strong",
2756 },
9bc39377 2757 // Next test was throwing uncaught error in zxcvbn
6422c1cd 2758 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
9bc39377
IC
2759 {
2760 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2761 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2762 events: 104,
87ad2c6e
IC
2763 bits: 499,
2764 words: 45,
9bc39377
IC
2765 strength: "extremely strong",
2766 },
5c653a12
IC
2767 // Case insensitivity to duplicate cards
2768 {
2769 entropy: "asAS",
2770 type: "card (1 duplicate: AS)",
2771 events: 2,
87ad2c6e 2772 bits: 9,
5c653a12
IC
2773 words: 0,
2774 strength: "extremely weak",
2775 },
2776 {
2777 entropy: "ASas",
2778 type: "card (1 duplicate: as)",
2779 events: 2,
87ad2c6e 2780 bits: 9,
5c653a12
IC
2781 words: 0,
2782 strength: "extremely weak",
2783 },
bbc29c80
IC
2784 // Missing cards are detected
2785 {
2786 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2787 type: "card (1 missing: 9C)",
2788 events: 51,
87ad2c6e
IC
2789 bits: 221,
2790 words: 18,
bbc29c80
IC
2791 strength: "extremely strong",
2792 },
2793 {
2794 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2795 type: "card (2 missing: 9C 5D)",
2796 events: 50,
87ad2c6e
IC
2797 bits: 216,
2798 words: 18,
bbc29c80
IC
2799 strength: "extremely strong",
2800 },
2801 {
2802 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2803 type: "card (4 missing: 9C 5D QD...)",
2804 events: 48,
87ad2c6e 2805 bits: 208,
6422c1cd 2806 words: 18,
bbc29c80
IC
2807 strength: "extremely strong",
2808 },
2809 // More than six missing cards does not show message
2810 {
2811 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2812 type: "card",
2813 events: 45,
87ad2c6e 2814 bits: 195,
6422c1cd 2815 words: 18,
bbc29c80
IC
2816 strength: "extremely strong",
2817 },
fc7c248f
IC
2818 // Multiple decks of cards increases bits per event
2819 {
2820 entropy: "3d",
2821 events: 1,
2822 bits: 4,
2823 bitsPerEvent: 4.34,
2824 },
2825 {
2826 entropy: "3d3d",
2827 events: 2,
2828 bits: 9,
2829 bitsPerEvent: 4.80,
2830 },
2831 {
2832 entropy: "3d3d3d",
2833 events: 3,
2834 bits: 15,
2835 bitsPerEvent: 5.01,
2836 },
2837 {
2838 entropy: "3d3d3d3d",
2839 events: 4,
2840 bits: 20,
2841 bitsPerEvent: 5.14,
2842 },
2843 {
2844 entropy: "3d3d3d3d3d",
2845 events: 5,
2846 bits: 26,
2847 bitsPerEvent: 5.22,
2848 },
2849 {
2850 entropy: "3d3d3d3d3d3d",
2851 events: 6,
2852 bits: 31,
2853 bitsPerEvent: 5.28,
2854 },
2855 {
2856 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
2857 events: 33,
2858 bits: 184,
2859 bitsPerEvent: 5.59,
2860 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
2861 },
c6624d51
IC
2862 ];
2863 // use entropy
2864 page.evaluate(function() {
2865 $(".use-entropy").prop("checked", true).trigger("change");
2866 });
2867 var nextTest = function runNextTest(i) {
fb353f9d 2868 function getFeedbackError(expected, actual) {
391c7f26 2869 if ("filtered" in expected && actual.indexOf(expected.filtered) == -1) {
fb353f9d
IC
2870 return "Filtered value not in feedback";
2871 }
fc7c248f 2872 if ("type" in expected && actual.indexOf(expected.type) == -1) {
fb353f9d
IC
2873 return "Entropy type not in feedback";
2874 }
fc7c248f 2875 if ("events" in expected && actual.indexOf(expected.events) == -1) {
fb353f9d
IC
2876 return "Event count not in feedback";
2877 }
fc7c248f 2878 if ("bits" in expected && actual.indexOf(expected.bits) == -1) {
fb353f9d
IC
2879 return "Bit count not in feedback";
2880 }
fc7c248f 2881 if ("strength" in expected && actual.indexOf(expected.strength) == -1) {
fb353f9d
IC
2882 return "Strength not in feedback";
2883 }
fc7c248f
IC
2884 if ("bitsPerEvent" in expected && actual.indexOf(expected.bitsPerEvent) == -1) {
2885 return "bitsPerEvent not in feedback";
2886 }
fb353f9d
IC
2887 return false;
2888 }
c6624d51
IC
2889 test = tests[i];
2890 page.evaluate(function(e) {
2891 $(".addresses").empty();
057722b0 2892 $(".phrase").val("");
c6624d51
IC
2893 $(".entropy").val(e).trigger("input");
2894 }, test.entropy);
fb353f9d 2895 waitForEntropyFeedback(function() {
c6624d51
IC
2896 var mnemonic = page.evaluate(function() {
2897 return $(".phrase").val();
2898 });
fb353f9d 2899 // Check mnemonic length
fc7c248f 2900 if ("words" in test && test.words == 0) {
fb353f9d
IC
2901 if (mnemonic.length > 0) {
2902 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2903 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2904 console.log("Mnemonic: " + mnemonic);
2905 fail();
2906 }
2907 }
fc7c248f 2908 else if ("words" in test) {
fb353f9d
IC
2909 if (mnemonic.split(" ").length != test.words) {
2910 console.log("Mnemonic length for " + test.strength + " strength is not " + test.words);
87ad2c6e 2911 console.log("Entropy: " + test.entropy);
fb353f9d
IC
2912 console.log("Mnemonic: " + mnemonic);
2913 fail();
2914 }
2915 }
2916 // check feedback
2917 var feedback = page.evaluate(function() {
dd944906 2918 return $(".entropy-container").text();
fb353f9d
IC
2919 });
2920 var feedbackError = getFeedbackError(test, feedback);
2921 if (feedbackError) {
2922 console.log("Entropy feedback for " + test.entropy + " returned error");
2923 console.log(feedbackError);
c6624d51
IC
2924 fail();
2925 }
fb353f9d 2926 // Run next test
c6624d51
IC
2927 var isLastTest = i == tests.length - 1;
2928 if (isLastTest) {
2929 next();
2930 }
2931 else {
2932 runNextTest(i+1);
2933 }
fb353f9d 2934 });
c6624d51
IC
2935 }
2936 nextTest(0);
2937});
2938},
2939
d6fd8ebf 2940// Entropy is truncated from the left
c6624d51
IC
2941function() {
2942page.open(url, function(status) {
d6fd8ebf 2943 var expected = "avocado zoo zone";
c6624d51
IC
2944 // use entropy
2945 page.evaluate(function() {
2946 $(".use-entropy").prop("checked", true).trigger("change");
2947 var entropy = "00000000 00000000 00000000 00000000";
d6fd8ebf 2948 entropy += "11111111 11111111 11111111 1111"; // Missing last byte
c6624d51
IC
2949 $(".entropy").val(entropy).trigger("input");
2950 });
2951 // check the entropy is truncated from the right
2952 waitForGenerate(function() {
2953 var actual = page.evaluate(function() {
2954 return $(".phrase").val();
2955 });
2956 if (actual != expected) {
2957 console.log("Entropy is not truncated from the right");
2958 console.log("Expected: " + expected);
2959 console.log("Got: " + actual);
2960 fail();
2961 }
2962 next();
2963 });
2964});
2965},
2966
2967// Very large entropy results in very long mnemonics
2968function() {
2969page.open(url, function(status) {
2970 // use entropy
2971 page.evaluate(function() {
2972 $(".use-entropy").prop("checked", true).trigger("change");
2973 var entropy = "";
2974 // Generate a very long entropy string
2975 for (var i=0; i<33; i++) {
2976 entropy += "AAAAAAAA"; // 3 words * 33 iterations = 99 words
2977 }
2978 $(".entropy").val(entropy).trigger("input");
2979 });
2980 // check the mnemonic is very long
2981 waitForGenerate(function() {
2982 var wordCount = page.evaluate(function() {
2983 return $(".phrase").val().split(" ").length;
2984 });
2985 if (wordCount != 99) {
2986 console.log("Large entropy does not generate long mnemonic");
2987 console.log("Expected 99 words, got " + wordCount);
2988 fail();
2989 }
2990 next();
2991 });
2992});
2993},
2994
2995// Is compatible with bip32jp entropy
2996// https://bip32jp.github.io/english/index.html
2997// NOTES:
2998// Is incompatible with:
c6624d51
IC
2999// base 20
3000function() {
3001page.open(url, function(status) {
0d0f07f9 3002 var expected = "train then jungle barely whip fiber purpose puppy eagle cloud clump hospital robot brave balcony utility detect estate old green desk skill multiply virus";
c6624d51
IC
3003 // use entropy
3004 page.evaluate(function() {
3005 $(".use-entropy").prop("checked", true).trigger("change");
0d0f07f9 3006 var entropy = "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
c6624d51
IC
3007 $(".entropy").val(entropy).trigger("input");
3008 });
3009 // check the mnemonic matches the expected value from bip32jp
3010 waitForGenerate(function() {
3011 var actual = page.evaluate(function() {
3012 return $(".phrase").val();
3013 });
3014 if (actual != expected) {
3015 console.log("Mnemonic does not match bip32jp for base 6 entropy");
3016 console.log("Expected: " + expected);
3017 console.log("Got: " + actual);
3018 fail();
3019 }
3020 next();
3021 });
3022});
3023},
3024
057722b0
IC
3025// Blank entropy does not generate mnemonic or addresses
3026function() {
3027page.open(url, function(status) {
3028 // use entropy
3029 page.evaluate(function() {
3030 $(".use-entropy").prop("checked", true).trigger("change");
3031 $(".entropy").val("").trigger("input");
3032 });
3033 waitForFeedback(function() {
3034 // check there is no mnemonic
3035 var phrase = page.evaluate(function() {
3036 return $(".phrase").val();
3037 });
3038 if (phrase != "") {
3039 console.log("Blank entropy does not result in blank mnemonic");
3040 console.log("Got: " + phrase);
3041 fail();
3042 }
3043 // check there are no addresses displayed
3044 var addresses = page.evaluate(function() {
3045 return $(".address").length;
3046 });
3047 if (addresses != 0) {
3048 console.log("Blank entropy does not result in zero addresses");
3049 fail();
3050 }
3051 // Check the feedback says 'blank entropy'
3052 var feedback = page.evaluate(function() {
3053 return $(".feedback").text();
3054 });
3055 if (feedback != "Blank entropy") {
3056 console.log("Blank entropy does not show feedback message");
3057 fail();
3058 }
3059 next();
3060 });
3061});
3062},
3063
3599674d
IC
3064// Mnemonic length can be selected even for weak entropy
3065function() {
3066page.open(url, function(status) {
3067 // use entropy
3068 page.evaluate(function() {
3069 $(".use-entropy").prop("checked", true).trigger("change");
3070 $(".entropy").val("012345");
3071 $(".mnemonic-length").val("18").trigger("change");
3072 });
3073 // check the mnemonic is the correct length
3074 waitForGenerate(function() {
3075 var phrase = page.evaluate(function() {
3076 return $(".phrase").val();
3077 });
3078 var numberOfWords = phrase.split(/\s/g).length;
3079 if (numberOfWords != 18) {
3080 console.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3081 console.log(phrase);
3082 fail();
3083 }
3084 next();
3085 });
3086});
3087},
3088
886f06ee
IC
3089// Github issue 33
3090// https://github.com/iancoleman/bip39/issues/33
3091// Final cards should contribute entropy
3092function() {
3093page.open(url, function(status) {
3094 // use entropy
3095 page.evaluate(function() {
3096 $(".use-entropy").prop("checked", true).trigger("change");
3097 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-6C TS TH 4H KC 5H 2H AH 2C 8D 3H 5D").trigger("input");
3098 });
3099 // get the mnemonic
3100 waitForGenerate(function() {
3101 var originalPhrase = page.evaluate(function() {
3102 return $(".phrase").val();
3103 });
3104 // Set the last 12 cards to be AS
3105 page.evaluate(function() {
3106 $(".addresses").empty();
3107 $(".entropy").val("7S 9H 9S QH 8C KS AS 7D 7C QD 4S 4D TC 2D 5S JS 3D 8S 8H 4C 3C AC 3S QC 9C JC 7H AD TD JD 6D KH 5C QS 2S 6S 6H JH KD 9D-AS AS AS AS AS AS AS AS AS AS AS AS").trigger("input");
3108 });
3109 // get the new mnemonic
3110 waitForGenerate(function() {
3111 var newPhrase = page.evaluate(function() {
3112 return $(".phrase").val();
3113 });
92e73fd9 3114 // check the phrase has changed
886f06ee
IC
3115 if (newPhrase == originalPhrase) {
3116 console.log("Changing last 12 cards does not change mnemonic");
3117 console.log("Original:");
3118 console.log(originalPhrase);
3119 console.log("New:");
3120 console.log(newPhrase);
3121 fail();
3122 }
3123 next();
3124 });
3125 });
3126});
3127},
3128
e00964cc
IC
3129// Github issue 35
3130// https://github.com/iancoleman/bip39/issues/35
3131// QR Code support
3132function() {
3133page.open(url, function(status) {
3134 // use entropy
3135 page.evaluate(function() {
3136 $(".generate").click();
3137 });
3138 waitForGenerate(function() {
3139 var p = page.evaluate(function() {
3140 // get position of mnemonic element
3141 return $(".phrase").offset();
3142 });
3143 p.top = Math.ceil(p.top);
3144 p.left = Math.ceil(p.left);
3145 // check the qr code shows
3146 page.sendEvent("mousemove", p.left+4, p.top+4);
3147 var qrShowing = page.evaluate(function() {
3148 return $(".qr-container").find("canvas").length > 0;
3149 });
3150 if (!qrShowing) {
3151 console.log("QR Code does not show");
3152 fail();
3153 }
3154 // check the qr code hides
3155 page.sendEvent("mousemove", p.left-4, p.top-4);
3156 var qrHidden = page.evaluate(function() {
3157 return $(".qr-container").find("canvas").length == 0;
3158 });
3159 if (!qrHidden) {
3160 console.log("QR Code does not hide");
3161 fail();
3162 }
3163 next();
3164 });
3165});
3166},
3167
c554e6ff
IC
3168// BIP44 account extendend private key is shown
3169// github issue 37 - compatibility with electrum
3170function() {
3171page.open(url, function(status) {
3172 // set the phrase
3173 var expected = "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3174 page.evaluate(function() {
3175 $(".phrase").val("abandon abandon ability");
3176 $(".phrase").trigger("input");
3177 });
3178 // check the BIP44 account extended private key
3179 waitForGenerate(function() {
3180 var actual = page.evaluate(function() {
3181 return $(".account-xprv").val();
3182 });
3183 if (actual != expected) {
3184 console.log("BIP44 account extended private key is incorrect");
3185 console.log("Expected: " + expected);
3186 console.log("Actual: " + actual);
3187 fail();
3188 }
3189 next();
3190 });
3191});
3192},
3193
3194// BIP44 account extendend public key is shown
3195// github issue 37 - compatibility with electrum
3196function() {
3197page.open(url, function(status) {
3198 // set the phrase
3199 var expected = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3200 page.evaluate(function() {
3201 $(".phrase").val("abandon abandon ability");
3202 $(".phrase").trigger("input");
3203 });
3204 // check the BIP44 account extended public key
3205 waitForGenerate(function() {
3206 var actual = page.evaluate(function() {
3207 return $(".account-xpub").val();
3208 });
3209 if (actual != expected) {
3210 console.log("BIP44 account extended public key is incorrect");
3211 console.log("Expected: " + expected);
ba3cb9ec
IC
3212 console.log("Actual: " + actual);
3213 fail();
3214 }
3215 next();
3216 });
3217});
3218},
3219
3220// github issue 40
3221// BIP32 root key can be set as an xpub
3222function() {
3223page.open(url, function(status) {
3224 // set the phrase
3225 page.evaluate(function() {
3226 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3227 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3228 $("#root-key").val(bip44AccountXpub);
3229 $("#root-key").trigger("input");
3230 });
3231 waitForFeedback(function() {
3232 page.evaluate(function() {
3233 // Use bip32 tab
3234 $("#bip32-tab a").click();
3235 });
3236 waitForGenerate(function() {
3237 page.evaluate(function() {
3238 // derive external addresses for this xpub
3239 var firstAccountDerivationPath = "m/0";
3240 $("#bip32-path").val(firstAccountDerivationPath);
3241 $("#bip32-path").trigger("input");
3242 });
3243 waitForGenerate(function() {
3244 // check the addresses are generated
3245 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3246 var actual = page.evaluate(function() {
3247 return $(".address:first").text();
3248 });
3249 if (actual != expected) {
3250 console.log("xpub key does not generate addresses in table");
3251 console.log("Expected: " + expected);
3252 console.log("Actual: " + actual);
3253 fail();
3254 }
3255 // check the xprv key is not set
3256 var expected = "NA";
3257 var actual = page.evaluate(function() {
3258 return $(".extended-priv-key").val();
3259 });
3260 if (actual != expected) {
3261 console.log("xpub key as root shows derived bip32 xprv key");
3262 console.log("Expected: " + expected);
3263 console.log("Actual: " + actual);
3264 fail();
3265 }
3266 // check the private key is not set
3267 var expected = "NA";
3268 var actual = page.evaluate(function() {
3269 return $(".privkey:first").text();
3270 });
3271 if (actual != expected) {
3272 console.log("xpub key generates private key in addresses table");
3273 console.log("Expected: " + expected);
3274 console.log("Actual: " + actual);
3275 fail();
3276 }
3277 next();
3278 });
3279 });
3280 });
3281});
3282},
3283
3284// github issue 40
3285// xpub for bip32 root key will not work with hardened derivation paths
3286function() {
3287page.open(url, function(status) {
3288 // set the phrase
3289 page.evaluate(function() {
3290 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3291 var bip44AccountXpub = "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3292 $("#root-key").val(bip44AccountXpub);
3293 $("#root-key").trigger("input");
3294 });
3295 waitForFeedback(function() {
3296 // Check feedback is correct
3297 var expected = "Hardened derivation path is invalid with xpub key";
3298 var actual = page.evaluate(function() {
3299 return $(".feedback").text();
3300 });
3301 if (actual != expected) {
3302 console.log("xpub key with hardened derivation path does not show feedback");
3303 console.log("Expected: " + expected);
3304 console.log("Actual: " + actual);
3305 fail();
3306 }
3307 // Check no addresses are shown
3308 var expected = 0;
3309 var actual = page.evaluate(function() {
3310 return $(".addresses tr").length;
3311 });
3312 if (actual != expected) {
3313 console.log("addresses still show after setting xpub key with hardened derivation path");
3314 console.log("Expected: " + expected);
c554e6ff
IC
3315 console.log("Actual: " + actual);
3316 fail();
3317 }
3318 next();
3319 });
3320});
3321},
3322
0a1f0259
IC
3323// github issue 39
3324// no root key shows feedback
3325function() {
3326page.open(url, function(status) {
3327 // click the bip32 tab on fresh page
3328 page.evaluate(function() {
3329 $("#bip32-tab a").click();
3330 });
3331 waitForFeedback(function() {
3332 // Check feedback is correct
3333 var expected = "No root key";
3334 var actual = page.evaluate(function() {
3335 return $(".feedback").text();
3336 });
3337 if (actual != expected) {
3338 console.log("Blank root key not detected");
3339 console.log("Expected: " + expected);
3340 console.log("Actual: " + actual);
3341 fail();
3342 }
3343 next();
3344 });
3345});
3346},
886f06ee 3347
40892aba
IC
3348// Github issue 44
3349// display error switching tabs while addresses are generating
3350function() {
3351page.open(url, function(status) {
3352 // set the phrase
3353 page.evaluate(function() {
3354 $(".phrase").val("abandon abandon ability").trigger("input");
3355 });
3356 waitForGenerate(function() {
3357 // set to generate 500 more addresses
3358 // generate more addresses
3359 // change tabs which should cancel the previous generating
3360 page.evaluate(function() {
3361 $(".rows-to-add").val("100");
3362 $(".more").click();
3363 $("#bip32-tab a").click();
3364 });
3365 // check the derivation paths are in order and of the right quantity
3366 waitForGenerate(function() {
3367 var paths = page.evaluate(function() {
3368 return $(".index").map(function(i, e) {
3369 return $(e).text();
3370 });
3371 });
3372 for (var i=0; i<paths.length; i++) {
3373 var expected = "m/0/" + i;
3374 var actual = paths[i];
3375 if (actual != expected) {
3376 console.log("Path " + i + " is not in correct order");
3377 console.log("Expected: " + expected);
3378 console.log("Actual: " + actual);
3379 fail();
3380 }
3381 }
3382 if (paths.length != 20) {
3383 console.log("Generation was not cancelled by new action");
3384 fail();
3385 }
3386 next();
3387 });
3388 });
3389});
3390},
3391
53aaab27
IC
3392// Github issue 49
3393// padding for binary should give length with multiple of 256
3394// hashed entropy 1111 is length 252, so requires 4 leading zeros
3395// prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3396function() {
3397page.open(url, function(status) {
3398 expected = "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3399 // use entropy
3400 page.evaluate(function() {
3401 $(".use-entropy").prop("checked", true).trigger("change");
5ed50bd9 3402 $(".mnemonic-length").val("15");
53aaab27
IC
3403 $(".entropy").val("1111").trigger("input");
3404 });
3405 waitForGenerate(function() {
3406 // get the mnemonic
3407 var actual = page.evaluate(function() {
3408 return $(".phrase").val();
3409 });
3410 // check the mnemonic is correct
3411 if (actual != expected) {
3412 console.log("Left padding error for entropy");
3413 console.log("Expected: " + expected);
3414 console.log("Actual: " + actual);
3415 fail();
3416 }
3417 next();
3418 });
3419});
3420},
3421
b0fb45b9
IC
3422// If you wish to add more tests, do so here...
3423
3424// Here is a blank test template
3425/*
3426
3427function() {
3428page.open(url, function(status) {
3429 // Do something on the page
3430 page.evaluate(function() {
3431 $(".phrase").val("abandon abandon ability").trigger("input");
3432 });
3433 waitForGenerate(function() {
3434 // Check the result of doing the thing
3435 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3436 var actual = page.evaluate(function() {
3437 return $(".address:first").text();
3438 });
3439 if (actual != expected) {
3440 console.log("A specific message about what failed");
3441 console.log("Expected: " + expected);
3442 console.log("Actual: " + actual);
3443 fail();
3444 }
3445 // Run the next test
3446 next();
3447 });
3448});
3449},
3450
3451*/
3452
88e2cdaa
IC
3453];
3454
3455console.log("Running tests...");
fb372687 3456tests = shuffle(tests);
88e2cdaa 3457next();