2 // $ phantomjs tests.js
5 var page
= require('webpage').create();
6 var url
= 'src/index.html';
7 var testMaxTime
= 20000;
14 page
.onResourceError = function(e
) {
15 console
.log("Error loading " + e
.url
);
20 console
.log("Failed");
24 function waitForGenerate(fn
, maxTime
) {
26 maxTime
= testMaxTime
;
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
;
36 var hasFinished
= addressCount
> 0 && addressCount
== prevAddressCount
;
37 prevAddressCount
= addressCount
;
41 else if (hasTimedOut
) {
42 console
.log("Test timed out");
46 setTimeout(keepWaiting
, 100);
52 function waitForFeedback(fn
, maxTime
) {
54 maxTime
= testMaxTime
;
56 var start
= new Date().getTime();
57 var wait
= function keepWaiting() {
58 var now
= new Date().getTime();
59 var hasTimedOut
= now
- start
> maxTime
;
61 console
.log("Test timed out");
65 var feedback
= page
.evaluate(function() {
66 var feedback
= $(".feedback");
67 if (feedback
.css("display") == "none") {
70 return feedback
.text();
72 var hasFinished
= feedback
.length
> 0 && feedback
!= "Calculating...";
77 setTimeout(keepWaiting
, 100);
83 function waitForEntropyFeedback(fn
, maxTime
) {
85 maxTime
= testMaxTime
;
87 var origFeedback
= page
.evaluate(function() {
88 return $(".entropy-container").text();
90 var start
= new Date().getTime();
91 var wait
= function keepWaiting() {
92 var now
= new Date().getTime();
93 var hasTimedOut
= now
- start
> maxTime
;
95 console
.log("Test timed out");
99 var feedback
= page
.evaluate(function() {
100 return $(".entropy-container").text();
102 var hasFinished
= feedback
!= origFeedback
;
107 setTimeout(keepWaiting
, 100);
114 if (tests
.length
> 0) {
115 var testsStr
= tests
.length
== 1 ? "test" : "tests";
116 console
.log(tests
.length
+ " " + testsStr
+ " remaining");
120 console
.log("Finished with 0 failures");
126 * Randomize array element order in-place.
127 * Using Durstenfeld shuffle algorithm.
128 * See http://stackoverflow.com/a/12646864
130 function shuffle(array
) {
131 for (var i
= array
.length
- 1; i
> 0; i
--) {
132 var j
= Math
.floor(Math
.random() * (i
+ 1));
142 // Page loads with status of 'success'
144 page
.open(url
, function(status
) {
145 if (status
!= "success") {
146 console
.log("Page did not load with status 'success'");
155 page
.open(url
, function(status
) {
156 var content
= page
.evaluate(function() {
157 return document
.body
.textContent
.trim();
160 console
.log("Page does not have text");
167 // Entering mnemonic generates addresses
169 page
.open(url
, function(status
) {
171 page
.evaluate(function() {
172 $(".phrase").val("abandon abandon ability").trigger("input");
175 waitForGenerate(function() {
176 var addressCount
= page
.evaluate(function() {
177 return $(".address").length
;
179 if (addressCount
!= 20) {
180 console
.log("Mnemonic did not generate addresses");
181 console
.log("Expected: " + expected
);
182 console
.log("Got: " + actual
);
190 // Random button generates random mnemonic
192 page
.open(url
, function(status
) {
193 // check initial phrase is empty
194 var phrase
= page
.evaluate(function() {
195 return $(".phrase").text();
198 console
.log("Initial phrase is not blank");
201 // press the 'generate' button
202 page
.evaluate(function() {
203 $(".generate").click();
205 // get the new phrase
206 waitForGenerate(function() {
207 var phrase
= page
.evaluate(function() {
208 return $(".phrase").val();
210 if (phrase
.length
<= 0) {
211 console
.log("Phrase not generated by pressing button");
219 // Mnemonic length can be customized
221 page
.open(url
, function(status
) {
222 // set the length to 6
223 var expectedLength
= "6";
224 page
.evaluate(function() {
225 $(".strength option[selected]").removeAttr("selected");
226 $(".strength option[value=6]").prop("selected", true);
228 // press the 'generate' button
229 page
.evaluate(function() {
230 $(".generate").click();
232 // check the new phrase is six words long
233 waitForGenerate(function() {
234 var actualLength
= page
.evaluate(function() {
235 var words
= $(".phrase").val().split(" ");
238 if (actualLength
!= expectedLength
) {
239 console
.log("Phrase not generated with correct length");
240 console
.log("Expected: " + expectedLength
);
241 console
.log("Actual: " + actualLength
);
249 // Passphrase can be set
251 page
.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");
258 // check the address is generated correctly
259 waitForGenerate(function() {
260 var actual
= page
.evaluate(function() {
261 return $(".address:first").text();
263 if (actual
!= expected
) {
264 console
.log("Passphrase results in wrong address");
265 console
.log("Expected: " + expected
);
266 console
.log("Actual: " + actual
);
274 // Network can be set to bitcoin testnet
276 page
.open(url
, function(status
) {
277 // set the phrase and coin
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").filter(function() {
284 return $(this).html() == "BTC - Bitcoin Testnet";
285 }).prop("selected", true);
286 $(".network").trigger("change");
288 // check the address is generated correctly
289 waitForGenerate(function() {
290 var actual
= page
.evaluate(function() {
291 return $(".address:first").text();
293 if (actual
!= expected
) {
294 console
.log("Bitcoin testnet address is incorrect");
295 console
.log("Expected: " + expected
);
296 console
.log("Actual: " + actual
);
304 // Network can be set to litecoin
306 page
.open(url
, function(status
) {
307 // set the phrase and coin
308 var expected
= "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
309 page
.evaluate(function() {
310 $(".phrase").val("abandon abandon ability");
311 $(".phrase").trigger("input");
312 $(".network option[selected]").removeAttr("selected");
313 $(".network option").filter(function() {
314 return $(this).html() == "LTC - Litecoin";
315 }).prop("selected", true);
316 $(".network").trigger("change");
318 // check the address is generated correctly
319 waitForGenerate(function() {
320 var actual
= page
.evaluate(function() {
321 return $(".address:first").text();
323 if (actual
!= expected
) {
324 console
.log("Litecoin address is incorrect");
325 console
.log("Expected: " + expected
);
326 console
.log("Actual: " + actual
);
334 // Network can be set to ripple
336 page
.open(url
, function(status
) {
337 // set the phrase and coin
338 var expected
= "rLTFnqbmCVPGx6VfaygdtuKWJgcN4v1zRS";
339 page
.evaluate(function() {
340 $(".phrase").val("ill clump only blind unit burden thing track silver cloth review awake useful craft whale all satisfy else trophy sunset walk vanish hope valve");
341 $(".phrase").trigger("input");
342 $(".network option[selected]").removeAttr("selected");
343 $(".network option").filter(function() {
344 return $(this).html() == "XRP - Ripple";
345 }).prop("selected", true);
346 $(".network").trigger("change");
348 // check the address is generated correctly
349 waitForGenerate(function() {
350 var actual
= page
.evaluate(function() {
351 return $(".address:first").text();
353 if (actual
!= expected
) {
354 console
.log("Ripple address is incorrect");
355 console
.log("Expected: " + expected
);
356 console
.log("Actual: " + actual
);
364 // Network can be set to dogecoin
366 page
.open(url
, function(status
) {
367 // set the phrase and coin
368 var expected
= "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
369 page
.evaluate(function() {
370 $(".phrase").val("abandon abandon ability");
371 $(".phrase").trigger("input");
372 $(".network option[selected]").removeAttr("selected");
373 $(".network option").filter(function() {
374 return $(this).html() == "DOGE - Dogecoin";
375 }).prop("selected", true);
376 $(".network").trigger("change");
378 // check the address is generated correctly
379 waitForGenerate(function() {
380 var actual
= page
.evaluate(function() {
381 return $(".address:first").text();
383 if (actual
!= expected
) {
384 console
.log("Dogecoin address is incorrect");
385 console
.log("Expected: " + expected
);
386 console
.log("Actual: " + actual
);
394 // Network can be set to shadowcash
396 page
.open(url
, function(status
) {
397 // set the phrase and coin
398 var expected
= "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
399 page
.evaluate(function() {
400 $(".phrase").val("abandon abandon ability");
401 $(".phrase").trigger("input");
402 $(".network option[selected]").removeAttr("selected");
403 $(".network option").filter(function() {
404 return $(this).html() == "SDC - ShadowCash";
405 }).prop("selected", true);
406 $(".network").trigger("change");
408 // check the address is generated correctly
409 waitForGenerate(function() {
410 var actual
= page
.evaluate(function() {
411 return $(".address:first").text();
413 if (actual
!= expected
) {
414 console
.log("Shadowcash address is incorrect");
415 console
.log("Expected: " + expected
);
416 console
.log("Actual: " + actual
);
424 // Network can be set to shadowcash testnet
426 page
.open(url
, function(status
) {
427 // set the phrase and coin
428 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
429 page
.evaluate(function() {
430 $(".phrase").val("abandon abandon ability");
431 $(".phrase").trigger("input");
432 $(".network option[selected]").removeAttr("selected");
433 $(".network option").filter(function() {
434 return $(this).html() == "SDC - ShadowCash Testnet";
435 }).prop("selected", true);
436 $(".network").trigger("change");
438 // check the address is generated correctly
439 waitForGenerate(function() {
440 var actual
= page
.evaluate(function() {
441 return $(".address:first").text();
443 if (actual
!= expected
) {
444 console
.log("Shadowcash testnet address is incorrect");
445 console
.log("Expected: " + expected
);
446 console
.log("Actual: " + actual
);
454 // Network can be set to viacoin
456 page
.open(url
, function(status
) {
457 // set the phrase and coin
458 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
459 page
.evaluate(function() {
460 $(".phrase").val("abandon abandon ability");
461 $(".phrase").trigger("input");
462 $(".network option[selected]").removeAttr("selected");
463 $(".network option").filter(function() {
464 return $(this).html() == "VIA - Viacoin";
465 }).prop("selected", true);
466 $(".network").trigger("change");
468 // check the address is generated correctly
469 waitForGenerate(function() {
470 var actual
= page
.evaluate(function() {
471 return $(".address:first").text();
473 if (actual
!= expected
) {
474 console
.log("Viacoin address is incorrect");
475 console
.log("Expected: " + expected
);
476 console
.log("Actual: " + actual
);
484 // Network can be set to viacoin testnet
486 page
.open(url
, function(status
) {
487 // set the phrase and coin
488 var expected
= "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
489 page
.evaluate(function() {
490 $(".phrase").val("abandon abandon ability");
491 $(".phrase").trigger("input");
492 $(".network option[selected]").removeAttr("selected");
493 $(".network option").filter(function() {
494 return $(this).html() == "VIA - Viacoin Testnet";
495 }).prop("selected", true);
496 $(".network").trigger("change");
498 // check the address is generated correctly
499 waitForGenerate(function() {
500 var actual
= page
.evaluate(function() {
501 return $(".address:first").text();
503 if (actual
!= expected
) {
504 console
.log("Viacoin testnet address is incorrect");
505 console
.log("Expected: " + expected
);
506 console
.log("Actual: " + actual
);
514 // Network can be set to jumbucks
516 page
.open(url
, function(status
) {
517 // set the phrase and coin
518 var expected
= "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
519 page
.evaluate(function() {
520 $(".phrase").val("abandon abandon ability");
521 $(".phrase").trigger("input");
522 $(".network option[selected]").removeAttr("selected");
523 $(".network option").filter(function() {
524 return $(this).html() == "JBS - Jumbucks";
525 }).prop("selected", true);
526 $(".network").trigger("change");
528 // check the address is generated correctly
529 waitForGenerate(function() {
530 var actual
= page
.evaluate(function() {
531 return $(".address:first").text();
533 if (actual
!= expected
) {
534 console
.log("Jumbucks address is incorrect");
535 console
.log("Expected: " + expected
);
536 console
.log("Actual: " + actual
);
544 // Network can be set to clam
546 page
.open(url
, function(status
) {
547 // set the phrase and coin
548 var expected
= "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
549 page
.evaluate(function() {
550 $(".phrase").val("abandon abandon ability");
551 $(".phrase").trigger("input");
552 $(".network option[selected]").removeAttr("selected");
553 $(".network option").filter(function() {
554 return $(this).html() == "CLAM - Clams";
555 }).prop("selected", true);
556 $(".network").trigger("change");
558 // check the address is generated correctly
559 waitForGenerate(function() {
560 var actual
= page
.evaluate(function() {
561 return $(".address:first").text();
563 if (actual
!= expected
) {
564 console
.log("CLAM address is incorrect");
565 console
.log("Expected: " + expected
);
566 console
.log("Actual: " + actual
);
574 // Network can be set to crown
576 page
.open(url
, function(status
) {
577 // set the phrase and coin
578 var expected
= "18pWSwSUAQdiwMHUfFZB1fM2xue9X1FqE5";
579 page
.evaluate(function() {
580 $(".phrase").val("abandon abandon ability");
581 $(".phrase").trigger("input");
582 $(".network option[selected]").removeAttr("selected");
583 $(".network option").filter(function() {
584 return $(this).html() == "CRW - Crown";
585 }).prop("selected", true);
586 $(".network").trigger("change");
588 // check the address is generated correctly
589 waitForGenerate(function() {
590 var actual
= page
.evaluate(function() {
591 return $(".address:first").text();
593 if (actual
!= expected
) {
594 console
.log("CRW address is incorrect");
595 console
.log("Expected: " + expected
);
596 console
.log("Actual: " + actual
);
604 // Network can be set to dash
606 page
.open(url
, function(status
) {
607 // set the phrase and coin
608 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
609 page
.evaluate(function() {
610 $(".phrase").val("abandon abandon ability");
611 $(".phrase").trigger("input");
612 $(".network option[selected]").removeAttr("selected");
613 $(".network option").filter(function() {
614 return $(this).html() == "DASH - Dash";
615 }).prop("selected", true);
616 $(".network").trigger("change");
618 // check the address is generated correctly
619 waitForGenerate(function() {
620 var actual
= page
.evaluate(function() {
621 return $(".address:first").text();
623 if (actual
!= expected
) {
624 console
.log("DASH address is incorrect");
625 console
.log("Expected: " + expected
);
626 console
.log("Actual: " + actual
);
635 page
.open(url
, function(status
) {
636 // set the phrase and coin
637 var expected
= "yaR52EN4oojdJfBgzWJTymC4uuCLPT29Gw";
638 page
.evaluate(function() {
639 $(".phrase").val("abandon abandon ability");
640 $(".phrase").trigger("input");
641 $(".network option[selected]").removeAttr("selected");
642 $(".network option").filter(function() {
643 return $(this).html() == "DASH - Dash Testnet";
644 }).prop("selected", true);
645 $(".network").trigger("change");
647 // check the address is generated correctly
648 waitForGenerate(function() {
649 var actual
= page
.evaluate(function() {
650 return $(".address:first").text();
652 if (actual
!= expected
) {
653 console
.log("DASH Testnet address is incorrect");
654 console
.log("Expected: " + expected
);
655 console
.log("Actual: " + actual
);
663 // Network can be set to game
665 page
.open(url
, function(status
) {
666 // set the phrase and coin
667 var expected
= "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
668 page
.evaluate(function() {
669 $(".phrase").val("abandon abandon ability");
670 $(".phrase").trigger("input");
671 $(".network option[selected]").removeAttr("selected");
672 $(".network option").filter(function() {
673 return $(this).html() == "GAME - GameCredits";
674 }).prop("selected", true);
675 $(".network").trigger("change");
677 // check the address is generated correctly
678 waitForGenerate(function() {
679 var actual
= page
.evaluate(function() {
680 return $(".address:first").text();
682 if (actual
!= expected
) {
683 console
.log("GAME address is incorrect");
684 console
.log("Expected: " + expected
);
685 console
.log("Actual: " + actual
);
693 // Network can be set to namecoin
695 page
.open(url
, function(status
) {
696 // set the phrase and coin
697 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
698 page
.evaluate(function() {
699 $(".phrase").val("abandon abandon ability");
700 $(".phrase").trigger("input");
701 $(".network option[selected]").removeAttr("selected");
702 $(".network option").filter(function() {
703 return $(this).html() == "NMC - Namecoin";
704 }).prop("selected", true);
705 $(".network").trigger("change");
707 // check the address is generated correctly
708 waitForGenerate(function() {
709 var actual
= page
.evaluate(function() {
710 return $(".address:first").text();
712 if (actual
!= expected
) {
713 console
.log("Namecoin address is incorrect");
714 console
.log("Expected: " + expected
);
715 console
.log("Actual: " + actual
);
723 // Network can be set to peercoin
725 page
.open(url
, function(status
) {
726 // set the phrase and coin
727 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
728 page
.evaluate(function() {
729 $(".phrase").val("abandon abandon ability");
730 $(".phrase").trigger("input");
731 $(".network option[selected]").removeAttr("selected");
732 $(".network option").filter(function() {
733 return $(this).html() == "PPC - Peercoin";
734 }).prop("selected", true);
735 $(".network").trigger("change");
737 // check the address is generated correctly
738 waitForGenerate(function() {
739 var actual
= page
.evaluate(function() {
740 return $(".address:first").text();
742 if (actual
!= expected
) {
743 console
.log("Peercoin address is incorrect");
744 console
.log("Expected: " + expected
);
745 console
.log("Actual: " + actual
);
753 // Network can be set to ethereum
756 page
.open(url
, function(status
) {
758 // set the phrase and coin
759 page
.evaluate(function() {
760 $(".phrase").val("abandon abandon ability");
761 $(".phrase").trigger("input");
762 $(".network option[selected]").removeAttr("selected");
763 $(".network option").filter(function() {
764 return $(this).html() == "ETH - Ethereum";
765 }).prop("selected", true);
766 $(".network").trigger("change");
768 waitForGenerate(function() {
769 // check the address is generated correctly
770 // this value comes from
771 // https://www.myetherwallet.com/#view-wallet-info
772 // Unusual capitalization is due to checksum
773 var expected
= "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
774 var actual
= page
.evaluate(function() {
775 return $(".address:first").text();
777 if (actual
!= expected
) {
778 console
.log("Ethereum address is incorrect");
779 console
.log("Expected: " + expected
);
780 console
.log("Actual: " + actual
);
783 // check the private key is correct
784 // this private key can be imported into
785 // https://www.myetherwallet.com/#view-wallet-info
786 // and it should correlate to the address above
787 var expected
= "0x8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
788 var actual
= page
.evaluate(function() {
789 return $(".privkey:first").text();
791 if (actual
!= expected
) {
792 console
.log("Ethereum privkey is incorrect");
793 console
.log("Expected: " + expected
);
794 console
.log("Actual: " + actual
);
797 // check the public key is correct
799 // don't have any third-party source to generate the expected value
800 //var expected = "?";
801 //var actual = page.evaluate(function() {
802 // return $(".pubkey:first").text();
804 //if (actual != expected) {
805 // console.log("Ethereum privkey is incorrect");
806 // console.log("Expected: " + expected);
807 // console.log("Actual: " + actual);
815 // Network can be set to Slimcoin
817 page
.open(url
, function(status
) {
818 // set the phrase and coin
819 var expected
= "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww";
820 page
.evaluate(function() {
821 $(".phrase").val("abandon abandon ability");
822 $(".phrase").trigger("input");
823 $(".network option[selected]").removeAttr("selected");
824 $(".network option").filter(function() {
825 return $(this).html() == "SLM - Slimcoin";
826 }).prop("selected", true);
827 $(".network").trigger("change");
829 // check the address is generated correctly
830 waitForGenerate(function() {
831 var actual
= page
.evaluate(function() {
832 return $(".address:first").text();
834 if (actual
!= expected
) {
835 console
.log("Slimcoin address is incorrect");
836 console
.log("Expected: " + expected
);
837 console
.log("Actual: " + actual
);
845 // Network can be set to Slimcointn
847 page
.open(url
, function(status
) {
848 // set the phrase and coin
849 var expected
= "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq";
850 page
.evaluate(function() {
851 $(".phrase").val("abandon abandon ability");
852 $(".phrase").trigger("input");
853 $(".network option[selected]").removeAttr("selected");
854 $(".network option").filter(function() {
855 return $(this).html() == "SLM - Slimcoin Testnet";
856 }).prop("selected", true);
857 $(".network").trigger("change");
859 // check the address is generated correctly
860 waitForGenerate(function() {
861 var actual
= page
.evaluate(function() {
862 return $(".address:first").text();
864 if (actual
!= expected
) {
865 console
.log("Slimcoin testnet address is incorrect");
866 console
.log("Expected: " + expected
);
867 console
.log("Actual: " + actual
);
875 // Network can be set to bitcoin cash
877 page
.open(url
, function(status
) {
878 // set the phrase and coin
879 var expected
= "1JKvb6wKtsjNoCRxpZ4DGrbniML7z5U16A";
880 page
.evaluate(function() {
881 $(".phrase").val("abandon abandon ability");
882 $(".phrase").trigger("input");
883 $(".network option[selected]").removeAttr("selected");
884 $(".network option").filter(function() {
885 return $(this).html() == "BCH - Bitcoin Cash";
886 }).prop("selected", true);
887 $(".network").trigger("change");
889 // check the address is generated correctly
890 waitForGenerate(function() {
891 var actual
= page
.evaluate(function() {
892 return $(".address:first").text();
894 if (actual
!= expected
) {
895 console
.log("Bitcoin Cash address is incorrect");
896 console
.log("Expected: " + expected
);
897 console
.log("Actual: " + actual
);
905 // Network can be set to myriadcoin
907 page
.open(url
, function(status
) {
908 // set the phrase and coin
909 var expected
= "MJEswvRR46wh9BoiVj9DzKYMBkCramhoBV";
910 page
.evaluate(function() {
911 $(".phrase").val("abandon abandon ability");
912 $(".phrase").trigger("input");
913 $(".network option[selected]").removeAttr("selected");
914 $(".network option").filter(function() {
915 return $(this).html() == "XMY - Myriadcoin";
916 }).prop("selected", true);
917 $(".network").trigger("change");
919 // check the address is generated correctly
920 waitForGenerate(function() {
921 var actual
= page
.evaluate(function() {
922 return $(".address:first").text();
924 if (actual
!= expected
) {
925 console
.log("Myriadcoin address is incorrect");
926 console
.log("Expected: " + expected
);
927 console
.log("Actual: " + actual
);
935 // BIP39 seed is set from phrase
937 page
.open(url
, function(status
) {
939 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
940 page
.evaluate(function() {
941 $(".phrase").val("abandon abandon ability");
942 $(".phrase").trigger("input");
944 // check the address is generated correctly
945 waitForGenerate(function() {
946 var actual
= page
.evaluate(function() {
947 return $(".seed").val();
949 if (actual
!= expected
) {
950 console
.log("BIP39 seed is incorrectly generated from mnemonic");
951 console
.log("Expected: " + expected
);
952 console
.log("Actual: " + actual
);
960 // BIP32 root key is set from phrase
962 page
.open(url
, function(status
) {
964 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
965 page
.evaluate(function() {
966 $(".phrase").val("abandon abandon ability");
967 $(".phrase").trigger("input");
969 // check the address is generated correctly
970 waitForGenerate(function() {
971 var actual
= page
.evaluate(function() {
972 return $(".root-key").val();
974 if (actual
!= expected
) {
975 console
.log("Root key is incorrectly generated from mnemonic");
976 console
.log("Expected: " + expected
);
977 console
.log("Actual: " + actual
);
985 // Tabs show correct addresses when changed
987 page
.open(url
, function(status
) {
989 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
990 page
.evaluate(function() {
991 $(".phrase").val("abandon abandon ability");
992 $(".phrase").trigger("input");
995 waitForGenerate(function() {
996 page
.evaluate(function() {
997 $("#bip32-tab a").click();
999 // check the address is generated correctly
1000 waitForGenerate(function() {
1001 var actual
= page
.evaluate(function() {
1002 return $(".address:first").text();
1004 if (actual
!= expected
) {
1005 console
.log("Clicking tab generates incorrect address");
1006 console
.log("Expected: " + expected
);
1007 console
.log("Actual: " + actual
);
1016 // BIP44 derivation path is shown
1018 page
.open(url
, function(status
) {
1020 var expected
= "m/44'/0'/0'/0";
1021 page
.evaluate(function() {
1022 $(".phrase").val("abandon abandon ability");
1023 $(".phrase").trigger("input");
1025 // check the derivation path of the first address
1026 waitForGenerate(function() {
1027 var actual
= page
.evaluate(function() {
1028 return $("#bip44 .path").val();
1030 if (actual
!= expected
) {
1031 console
.log("BIP44 derivation path is incorrect");
1032 console
.log("Expected: " + expected
);
1033 console
.log("Actual: " + actual
);
1041 // BIP44 extended private key is shown
1043 page
.open(url
, function(status
) {
1045 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
1046 page
.evaluate(function() {
1047 $(".phrase").val("abandon abandon ability");
1048 $(".phrase").trigger("input");
1050 // check the BIP44 extended private key
1051 waitForGenerate(function() {
1052 var actual
= page
.evaluate(function() {
1053 return $(".extended-priv-key").val();
1055 if (actual
!= expected
) {
1056 console
.log("BIP44 extended private key is incorrect");
1057 console
.log("Expected: " + expected
);
1058 console
.log("Actual: " + actual
);
1066 // BIP44 extended public key is shown
1068 page
.open(url
, function(status
) {
1070 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
1071 page
.evaluate(function() {
1072 $(".phrase").val("abandon abandon ability");
1073 $(".phrase").trigger("input");
1075 // check the BIP44 extended public key
1076 waitForGenerate(function() {
1077 var actual
= page
.evaluate(function() {
1078 return $(".extended-pub-key").val();
1080 if (actual
!= expected
) {
1081 console
.log("BIP44 extended public key is incorrect");
1082 console
.log("Expected: " + expected
);
1083 console
.log("Actual: " + actual
);
1091 // BIP44 account field changes address list
1093 page
.open(url
, function(status
) {
1095 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1096 page
.evaluate(function() {
1097 $(".phrase").val("abandon abandon ability");
1098 $(".phrase").trigger("input");
1100 waitForGenerate(function() {
1101 // change the bip44 purpose field to 45
1102 page
.evaluate(function() {
1103 $("#bip44 .account").val("1");
1104 $("#bip44 .account").trigger("input");
1106 waitForGenerate(function() {
1107 // check the address for the new derivation path
1108 var actual
= page
.evaluate(function() {
1109 return $(".address:first").text();
1111 if (actual
!= expected
) {
1112 console
.log("BIP44 account field generates incorrect address");
1113 console
.log("Expected: " + expected
);
1114 console
.log("Actual: " + actual
);
1123 // BIP44 change field changes address list
1125 page
.open(url
, function(status
) {
1127 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
1128 page
.evaluate(function() {
1129 $(".phrase").val("abandon abandon ability");
1130 $(".phrase").trigger("input");
1132 waitForGenerate(function() {
1133 // change the bip44 purpose field to 45
1134 page
.evaluate(function() {
1135 $("#bip44 .change").val("1");
1136 $("#bip44 .change").trigger("input");
1138 waitForGenerate(function() {
1139 // check the address for the new derivation path
1140 var actual
= page
.evaluate(function() {
1141 return $(".address:first").text();
1143 if (actual
!= expected
) {
1144 console
.log("BIP44 change field generates incorrect address");
1145 console
.log("Expected: " + expected
);
1146 console
.log("Actual: " + actual
);
1155 // BIP32 derivation path can be set
1157 page
.open(url
, function(status
) {
1159 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1160 page
.evaluate(function() {
1161 $(".phrase").val("abandon abandon ability");
1162 $(".phrase").trigger("input");
1165 waitForGenerate(function() {
1166 page
.evaluate(function() {
1167 $("#bip32-tab a").click();
1169 // set the derivation path to m/1
1170 waitForGenerate(function() {
1171 page
.evaluate(function() {
1172 $("#bip32 .path").val("m/1");
1173 $("#bip32 .path").trigger("input");
1175 // check the address is generated correctly
1176 waitForGenerate(function() {
1177 var actual
= page
.evaluate(function() {
1178 return $(".address:first").text();
1180 if (actual
!= expected
) {
1181 console
.log("Custom BIP32 path generates incorrect address");
1182 console
.log("Expected: " + expected
);
1183 console
.log("Actual: " + actual
);
1193 // BIP32 can use hardened derivation paths
1195 page
.open(url
, function(status
) {
1197 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1198 page
.evaluate(function() {
1199 $(".phrase").val("abandon abandon ability");
1200 $(".phrase").trigger("input");
1203 waitForGenerate(function() {
1204 page
.evaluate(function() {
1205 $("#bip32-tab a").click();
1207 // set the derivation path to m/0'
1208 waitForGenerate(function() {
1209 page
.evaluate(function() {
1210 $("#bip32 .path").val("m/0'");
1211 $("#bip32 .path").trigger("input");
1213 // check the address is generated correctly
1214 waitForGenerate(function() {
1215 var actual
= page
.evaluate(function() {
1216 return $(".address:first").text();
1218 if (actual
!= expected
) {
1219 console
.log("Hardened BIP32 path generates incorrect address");
1220 console
.log("Expected: " + expected
);
1221 console
.log("Actual: " + actual
);
1231 // BIP32 extended private key is shown
1233 page
.open(url
, function(status
) {
1235 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1236 page
.evaluate(function() {
1237 $(".phrase").val("abandon abandon ability");
1238 $(".phrase").trigger("input");
1241 waitForGenerate(function() {
1242 page
.evaluate(function() {
1243 $("#bip32-tab a").click();
1245 // check the extended private key is generated correctly
1246 waitForGenerate(function() {
1247 var actual
= page
.evaluate(function() {
1248 return $(".extended-priv-key").val();
1250 if (actual
!= expected
) {
1251 console
.log("BIP32 extended private key is incorrect");
1252 console
.log("Expected: " + expected
);
1253 console
.log("Actual: " + actual
);
1262 // BIP32 extended public key is shown
1264 page
.open(url
, function(status
) {
1266 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1267 page
.evaluate(function() {
1268 $(".phrase").val("abandon abandon ability");
1269 $(".phrase").trigger("input");
1272 waitForGenerate(function() {
1273 page
.evaluate(function() {
1274 $("#bip32-tab a").click();
1276 // check the extended public key is generated correctly
1277 waitForGenerate(function() {
1278 var actual
= page
.evaluate(function() {
1279 return $(".extended-pub-key").val();
1281 if (actual
!= expected
) {
1282 console
.log("BIP32 extended public key is incorrect");
1283 console
.log("Expected: " + expected
);
1284 console
.log("Actual: " + actual
);
1293 // Derivation path is shown in table
1295 page
.open(url
, function(status
) {
1297 var expected
= "m/44'/0'/0'/0/0";
1298 page
.evaluate(function() {
1299 $(".phrase").val("abandon abandon ability");
1300 $(".phrase").trigger("input");
1302 // check for derivation path in table
1303 waitForGenerate(function() {
1304 var actual
= page
.evaluate(function() {
1305 return $(".index:first").text();
1307 if (actual
!= expected
) {
1308 console
.log("Derivation path shown incorrectly in table");
1309 console
.log("Expected: " + expected
);
1310 console
.log("Actual: " + actual
);
1318 // Derivation path for address can be hardened
1320 page
.open(url
, function(status
) {
1322 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1323 page
.evaluate(function() {
1324 $(".phrase").val("abandon abandon ability");
1325 $(".phrase").trigger("input");
1328 waitForGenerate(function() {
1329 page
.evaluate(function() {
1330 $("#bip32-tab a").click();
1332 waitForGenerate(function() {
1333 // select the hardened addresses option
1334 page
.evaluate(function() {
1335 $(".hardened-addresses").prop("checked", true);
1336 $(".hardened-addresses").trigger("change");
1338 waitForGenerate(function() {
1339 // check the generated address is hardened
1340 var actual
= page
.evaluate(function() {
1341 return $(".address:first").text();
1343 if (actual
!= expected
) {
1344 console
.log("Hardened address is incorrect");
1345 console
.log("Expected: " + expected
);
1346 console
.log("Actual: " + actual
);
1356 // Derivation path visibility can be toggled
1358 page
.open(url
, function(status
) {
1360 page
.evaluate(function() {
1361 $(".phrase").val("abandon abandon ability");
1362 $(".phrase").trigger("input");
1364 waitForGenerate(function() {
1365 // toggle path visibility
1366 page
.evaluate(function() {
1367 $(".index-toggle").click();
1369 // check the path is not visible
1370 var isInvisible
= page
.evaluate(function() {
1371 return $(".index:first span").hasClass("invisible");
1374 console
.log("Toggled derivation path is visible");
1384 page
.open(url
, function(status
) {
1385 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1387 page
.evaluate(function() {
1388 $(".phrase").val("abandon abandon ability").trigger("input");
1391 waitForGenerate(function() {
1392 var actual
= page
.evaluate(function() {
1393 return $(".address:first").text();
1395 if (actual
!= expected
) {
1396 console
.log("Address is not shown");
1397 console
.log("Expected: " + expected
);
1398 console
.log("Got: " + actual
);
1406 // Addresses are shown in order of derivation path
1408 page
.open(url
, function(status
) {
1410 page
.evaluate(function() {
1411 $(".phrase").val("abandon abandon ability").trigger("input");
1413 // get the derivation paths
1414 waitForGenerate(function() {
1415 var paths
= page
.evaluate(function() {
1416 return $(".index").map(function(i
, e
) {
1420 if (paths
.length
!= 20) {
1421 console
.log("Total paths is less than expected: " + paths
.length
);
1424 for (var i
=0; i
<paths
.length
; i
++) {
1425 var expected
= "m/44'/0'/0'/0/" + i
;
1426 var actual
= paths
[i
];
1427 if (actual
!= expected
) {
1428 console
.log("Path " + i
+ " is incorrect");
1429 console
.log("Expected: " + expected
);
1430 console
.log("Actual: " + actual
);
1439 // Address visibility can be toggled
1441 page
.open(url
, function(status
) {
1443 page
.evaluate(function() {
1444 $(".phrase").val("abandon abandon ability");
1445 $(".phrase").trigger("input");
1447 waitForGenerate(function() {
1448 // toggle address visibility
1449 page
.evaluate(function() {
1450 $(".address-toggle").click();
1452 // check the address is not visible
1453 var isInvisible
= page
.evaluate(function() {
1454 return $(".address:first span").hasClass("invisible");
1457 console
.log("Toggled address is visible");
1465 // Public key is shown
1467 page
.open(url
, function(status
) {
1468 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1470 page
.evaluate(function() {
1471 $(".phrase").val("abandon abandon ability").trigger("input");
1474 waitForGenerate(function() {
1475 var actual
= page
.evaluate(function() {
1476 return $(".pubkey:first").text();
1478 if (actual
!= expected
) {
1479 console
.log("Public key is not shown");
1480 console
.log("Expected: " + expected
);
1481 console
.log("Got: " + actual
);
1489 // Public key visibility can be toggled
1491 page
.open(url
, function(status
) {
1493 page
.evaluate(function() {
1494 $(".phrase").val("abandon abandon ability");
1495 $(".phrase").trigger("input");
1497 waitForGenerate(function() {
1498 // toggle public key visibility
1499 page
.evaluate(function() {
1500 $(".public-key-toggle").click();
1502 // check the public key is not visible
1503 var isInvisible
= page
.evaluate(function() {
1504 return $(".pubkey:first span").hasClass("invisible");
1507 console
.log("Toggled public key is visible");
1515 // Private key is shown
1517 page
.open(url
, function(status
) {
1518 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1520 page
.evaluate(function() {
1521 $(".phrase").val("abandon abandon ability").trigger("input");
1524 waitForGenerate(function() {
1525 var actual
= page
.evaluate(function() {
1526 return $(".privkey:first").text();
1528 if (actual
!= expected
) {
1529 console
.log("Private key is not shown");
1530 console
.log("Expected: " + expected
);
1531 console
.log("Got: " + actual
);
1539 // Private key visibility can be toggled
1541 page
.open(url
, function(status
) {
1543 page
.evaluate(function() {
1544 $(".phrase").val("abandon abandon ability");
1545 $(".phrase").trigger("input");
1547 waitForGenerate(function() {
1548 // toggle private key visibility
1549 page
.evaluate(function() {
1550 $(".private-key-toggle").click();
1552 // check the private key is not visible
1553 var isInvisible
= page
.evaluate(function() {
1554 return $(".privkey:first span").hasClass("invisible");
1557 console
.log("Toggled private key is visible");
1565 // More addresses can be generated
1567 page
.open(url
, function(status
) {
1569 page
.evaluate(function() {
1570 $(".phrase").val("abandon abandon ability");
1571 $(".phrase").trigger("input");
1573 waitForGenerate(function() {
1574 // generate more addresses
1575 page
.evaluate(function() {
1578 waitForGenerate(function() {
1579 // check there are more addresses
1580 var addressCount
= page
.evaluate(function() {
1581 return $(".address").length
;
1583 if (addressCount
!= 40) {
1584 console
.log("More addresses cannot be generated");
1593 // A custom number of additional addresses can be generated
1595 page
.open(url
, function(status
) {
1597 page
.evaluate(function() {
1598 $(".phrase").val("abandon abandon ability");
1599 $(".phrase").trigger("input");
1601 waitForGenerate(function() {
1602 // get the current number of addresses
1603 var oldAddressCount
= page
.evaluate(function() {
1604 return $(".address").length
;
1606 // set a custom number of additional addresses
1607 page
.evaluate(function() {
1608 $(".rows-to-add").val(1);
1610 // generate more addresses
1611 page
.evaluate(function() {
1614 waitForGenerate(function() {
1615 // check there are the correct number of addresses
1616 var newAddressCount
= page
.evaluate(function() {
1617 return $(".address").length
;
1619 if (newAddressCount
- oldAddressCount
!= 1) {
1620 console
.log("Number of additional addresses cannot be customized");
1621 console
.log(newAddressCount
)
1622 console
.log(oldAddressCount
)
1631 // Additional addresses are shown in order of derivation path
1633 page
.open(url
, function(status
) {
1635 page
.evaluate(function() {
1636 $(".phrase").val("abandon abandon ability").trigger("input");
1638 waitForGenerate(function() {
1639 // generate more addresses
1640 page
.evaluate(function() {
1643 // get the derivation paths
1644 waitForGenerate(function() {
1645 var paths
= page
.evaluate(function() {
1646 return $(".index").map(function(i
, e
) {
1650 if (paths
.length
!= 40) {
1651 console
.log("Total additional paths is less than expected: " + paths
.length
);
1654 for (var i
=0; i
<paths
.length
; i
++) {
1655 var expected
= "m/44'/0'/0'/0/" + i
;
1656 var actual
= paths
[i
];
1657 if (actual
!= expected
) {
1658 console
.log("Path " + i
+ " is not in correct order");
1659 console
.log("Expected: " + expected
);
1660 console
.log("Actual: " + actual
);
1670 // BIP32 root key can be set by the user
1672 page
.open(url
, function(status
) {
1673 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1675 page
.evaluate(function() {
1676 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1678 waitForGenerate(function() {
1679 var actual
= page
.evaluate(function() {
1680 return $(".address:first").text();
1682 if (actual
!= expected
) {
1683 console
.log("Setting BIP32 root key results in wrong address");
1684 console
.log("Expected: " + expected
);
1685 console
.log("Actual: " + actual
);
1693 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1695 page
.open(url
, function(status
) {
1698 page
.evaluate(function() {
1699 $(".phrase").val("A non-blank but invalid value");
1701 // Accept any confirm dialogs
1702 page
.onConfirm = function() {
1706 page
.evaluate(function() {
1707 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1709 waitForGenerate(function() {
1710 var actual
= page
.evaluate(function() {
1711 return $(".phrase").val();
1713 if (actual
!= expected
) {
1714 console
.log("Phrase not cleared when setting BIP32 root key");
1715 console
.log("Expected: " + expected
);
1716 console
.log("Actual: " + actual
);
1724 // Clearing of phrase, passphrase and seed can be cancelled by user
1726 page
.open(url
, function(status
) {
1727 var expected
= "abandon abandon ability";
1729 page
.evaluate(function() {
1730 $(".phrase").val("abandon abandon ability");
1732 // Cancel any confirm dialogs
1733 page
.onConfirm = function() {
1737 page
.evaluate(function() {
1738 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1740 var actual
= page
.evaluate(function() {
1741 return $(".phrase").val();
1743 if (actual
!= expected
) {
1744 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1745 console
.log("Expected: " + expected
);
1746 console
.log("Actual: " + actual
);
1753 // Custom BIP32 root key is used when changing the derivation path
1755 page
.open(url
, function(status
) {
1756 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1758 page
.evaluate(function() {
1759 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1761 waitForGenerate(function() {
1762 // change the derivation path
1763 page
.evaluate(function() {
1764 $("#account").val("1").trigger("input");
1766 // check the bip32 root key is used for derivation, not the blank phrase
1767 waitForGenerate(function() {
1768 var actual
= page
.evaluate(function() {
1769 return $(".address:first").text();
1771 if (actual
!= expected
) {
1772 console
.log("Changing the derivation path does not use BIP32 root key");
1773 console
.log("Expected: " + expected
);
1774 console
.log("Actual: " + actual
);
1783 // Incorrect mnemonic shows error
1785 page
.open(url
, function(status
) {
1787 page
.evaluate(function() {
1788 $(".phrase").val("abandon abandon abandon").trigger("input");
1790 waitForFeedback(function() {
1791 // check there is an error shown
1792 var feedback
= page
.evaluate(function() {
1793 return $(".feedback").text();
1795 if (feedback
.length
<= 0) {
1796 console
.log("Invalid mnemonic does not show error");
1804 // Incorrect word shows suggested replacement
1806 page
.open(url
, function(status
) {
1808 page
.evaluate(function() {
1809 $(".phrase").val("abandon abandon abiliti").trigger("input");
1811 // check there is a suggestion shown
1812 waitForFeedback(function() {
1813 var feedback
= page
.evaluate(function() {
1814 return $(".feedback").text();
1816 if (feedback
.indexOf("did you mean ability?") < 0) {
1817 console
.log("Incorrect word does not show suggested replacement");
1818 console
.log("Error: " + error
);
1826 // Github pull request 48
1827 // First four letters of word shows that word, not closest
1828 // since first four letters gives unique word in BIP39 wordlist
1829 // eg ille should show illegal, not idle
1831 page
.open(url
, function(status
) {
1832 // set the incomplete word
1833 page
.evaluate(function() {
1834 $(".phrase").val("ille").trigger("input");
1836 // check there is a suggestion shown
1837 waitForFeedback(function() {
1838 var feedback
= page
.evaluate(function() {
1839 return $(".feedback").text();
1841 if (feedback
.indexOf("did you mean illegal?") < 0) {
1842 console
.log("Start of word does not show correct suggestion");
1843 console
.log("Error: " + error
);
1851 // Incorrect BIP32 root key shows error
1853 page
.open(url
, function(status
) {
1855 page
.evaluate(function() {
1856 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1858 // check there is an error shown
1859 waitForFeedback(function() {
1860 var feedback
= page
.evaluate(function() {
1861 return $(".feedback").text();
1863 if (feedback
!= "Invalid root key") {
1864 console
.log("Invalid root key does not show error");
1865 console
.log("Error: " + error
);
1873 // Derivation path not starting with m shows error
1875 page
.open(url
, function(status
) {
1876 // set the mnemonic phrase
1877 page
.evaluate(function() {
1878 $(".phrase").val("abandon abandon ability").trigger("input");
1880 waitForGenerate(function() {
1881 // select the bip32 tab so custom derivation path can be set
1882 page
.evaluate(function() {
1883 $("#bip32-tab a").click();
1885 waitForGenerate(function() {
1886 // set the incorrect derivation path
1887 page
.evaluate(function() {
1888 $("#bip32 .path").val("n/0").trigger("input");
1890 waitForFeedback(function() {
1891 var feedback
= page
.evaluate(function() {
1892 return $(".feedback").text();
1894 if (feedback
!= "First character must be 'm'") {
1895 console
.log("Derivation path not starting with m should show error");
1896 console
.log("Error: " + error
);
1906 // Derivation path containing invalid characters shows useful error
1908 page
.open(url
, function(status
) {
1909 // set the mnemonic phrase
1910 page
.evaluate(function() {
1911 $(".phrase").val("abandon abandon ability").trigger("input");
1913 waitForGenerate(function() {
1914 // select the bip32 tab so custom derivation path can be set
1915 page
.evaluate(function() {
1916 $("#bip32-tab a").click();
1918 waitForGenerate(function() {
1919 // set the incorrect derivation path
1920 page
.evaluate(function() {
1921 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1923 waitForFeedback(function() {
1924 var feedback
= page
.evaluate(function() {
1925 return $(".feedback").text();
1927 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1928 console
.log("Derivation path with invalid characters should show error");
1929 console
.log("Error: " + error
);
1939 // Github Issue 11: Default word length is 15
1940 // https://github.com/iancoleman/bip39/issues/11
1942 page
.open(url
, function(status
) {
1943 // get the word length
1944 var defaultLength
= page
.evaluate(function() {
1945 return $(".strength").val();
1947 if (defaultLength
!= 15) {
1948 console
.log("Default word length is not 15");
1956 // Github Issue 12: Generate more rows with private keys hidden
1957 // https://github.com/iancoleman/bip39/issues/12
1959 page
.open(url
, function(status
) {
1961 page
.evaluate(function() {
1962 $(".phrase").val("abandon abandon ability");
1963 $(".phrase").trigger("input");
1965 waitForGenerate(function() {
1966 // toggle private keys hidden, then generate more addresses
1967 page
.evaluate(function() {
1968 $(".private-key-toggle").click();
1971 waitForGenerate(function() {
1972 // check more have been generated
1974 var numPrivKeys
= page
.evaluate(function() {
1975 return $(".privkey").length
;
1977 if (numPrivKeys
!= expected
) {
1978 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1979 console
.log("Expected: " + expected
);
1980 console
.log("Actual: " + numPrivKeys
);
1983 // check no private keys are shown
1984 var numHiddenPrivKeys
= page
.evaluate(function() {
1985 return $(".privkey span[class=invisible]").length
;
1987 if (numHiddenPrivKeys
!= expected
) {
1988 console
.log("Generating more does not retain hidden state of privkeys");
1989 console
.log("Expected: " + expected
);
1990 console
.log("Actual: " + numHiddenPrivKeys
);
1999 // Github Issue 19: Mnemonic is not sensitive to whitespace
2000 // https://github.com/iancoleman/bip39/issues/19
2002 page
.open(url
, function(status
) {
2004 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
2005 page
.evaluate(function() {
2006 var doubleSpace
= " ";
2007 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
2008 $(".phrase").trigger("input");
2010 waitForGenerate(function() {
2011 // Check the bip32 root key is correct
2012 var actual
= page
.evaluate(function() {
2013 return $(".root-key").val();
2015 if (actual
!= expected
) {
2016 console
.log("Mnemonic is sensitive to whitespace");
2017 console
.log("Expected: " + expected
);
2018 console
.log("Actual: " + actual
);
2026 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
2027 // https://github.com/iancoleman/bip39/issues/23
2029 page
.open(url
, function(status
) {
2030 // 1) and 2) set the phrase
2031 page
.evaluate(function() {
2032 $(".phrase").val("abandon abandon ability").trigger("input");
2034 waitForGenerate(function() {
2035 // 3) select bip32 tab
2036 page
.evaluate(function() {
2037 $("#bip32-tab a").click();
2039 waitForGenerate(function() {
2040 // 4) switch from bitcoin to litecoin
2041 page
.evaluate(function() {
2042 $(".network option").filter(function() {
2043 return $(this).html() == "LTC - Litecoin";
2044 }).prop("selected", true);
2045 $(".network").trigger("change");
2047 waitForGenerate(function() {
2048 // 5) Check derivation path is displayed correctly
2049 var expected
= "m/0/0";
2050 var actual
= page
.evaluate(function() {
2051 return $(".index:first").text();
2053 if (actual
!= expected
) {
2054 console
.log("Github Issue 23 Part 1: derivation path display error");
2055 console
.log("Expected: " + expected
);
2056 console
.log("Actual: " + actual
);
2059 // 5) Check address is displayed correctly
2060 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
2061 var actual
= page
.evaluate(function() {
2062 return $(".address:first").text();
2064 if (actual
!= expected
) {
2065 console
.log("Github Issue 23 Part 1: address display error");
2066 console
.log("Expected: " + expected
);
2067 console
.log("Actual: " + actual
);
2077 // Github Issue 23 Part 2: Coin selection in derivation path
2078 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
2080 page
.open(url
, function(status
) {
2082 page
.evaluate(function() {
2083 $(".phrase").val("abandon abandon ability").trigger("input");
2085 waitForGenerate(function() {
2086 // switch from bitcoin to clam
2087 page
.evaluate(function() {
2088 $(".network option").filter(function() {
2089 return $(this).html() == "CLAM - Clams";
2090 }).prop("selected", true);
2091 $(".network").trigger("change");
2093 waitForGenerate(function() {
2094 // check derivation path is displayed correctly
2095 var expected
= "m/44'/23'/0'/0/0";
2096 var actual
= page
.evaluate(function() {
2097 return $(".index:first").text();
2099 if (actual
!= expected
) {
2100 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
2101 console
.log("Expected: " + expected
);
2102 console
.log("Actual: " + actual
);
2111 // Github Issue 26: When using a Root key derrived altcoins are incorrect
2112 // https://github.com/iancoleman/bip39/issues/26
2114 page
.open(url
, function(status
) {
2115 // 1) 2) and 3) set the root key
2116 page
.evaluate(function() {
2117 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
2119 waitForGenerate(function() {
2120 // 4) switch from bitcoin to viacoin
2121 page
.evaluate(function() {
2122 $(".network option").filter(function() {
2123 return $(this).html() == "VIA - Viacoin";
2124 }).prop("selected", true);
2125 $(".network").trigger("change");
2127 waitForGenerate(function() {
2128 // 5) ensure the derived address is correct
2129 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
2130 var actual
= page
.evaluate(function() {
2131 return $(".address:first").text();
2133 if (actual
!= expected
) {
2134 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
2135 console
.log("Expected: " + expected
);
2136 console
.log("Actual: " + actual
);
2145 // Selecting a language with no existing phrase should generate a phrase in
2148 page
.open(url
, function(status
) {
2149 // Select a language
2150 // Need to manually simulate hash being set due to quirk between
2151 // 'click' event triggered by javascript vs triggered by mouse.
2152 // Perhaps look into page.sendEvent
2153 // http://phantomjs.org/api/webpage/method/send-event.html
2154 page
.evaluate(function() {
2155 window
.location
.hash
= "#japanese";
2156 $("a[href='#japanese']").trigger("click");
2158 waitForGenerate(function() {
2159 // Check the mnemonic is in Japanese
2160 var phrase
= page
.evaluate(function() {
2161 return $(".phrase").val();
2163 if (phrase
.length
<= 0) {
2164 console
.log("No Japanese phrase generated");
2167 if (phrase
.charCodeAt(0) < 128) {
2168 console
.log("First character of Japanese phrase is ascii");
2169 console
.log("Phrase: " + phrase
);
2177 // Selecting a language with existing phrase should update the phrase to use
2180 page
.open(url
, function(status
) {
2181 // Set the phrase to an English phrase.
2182 page
.evaluate(function() {
2183 $(".phrase").val("abandon abandon ability").trigger("input");
2185 waitForGenerate(function() {
2186 // Change to Italian
2187 // Need to manually simulate hash being set due to quirk between
2188 // 'click' event triggered by javascript vs triggered by mouse.
2189 // Perhaps look into page.sendEvent
2190 // http://phantomjs.org/api/webpage/method/send-event.html
2191 page
.evaluate(function() {
2192 window
.location
.hash
= "#italian";
2193 $("a[href='#italian']").trigger("click");
2195 waitForGenerate(function() {
2196 // Check only the language changes, not the phrase
2197 var expected
= "abaco abaco abbaglio";
2198 var actual
= page
.evaluate(function() {
2199 return $(".phrase").val();
2201 if (actual
!= expected
) {
2202 console
.log("Changing language with existing phrase");
2203 console
.log("Expected: " + expected
);
2204 console
.log("Actual: " + actual
);
2207 // Check the address is correct
2208 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2209 var actual
= page
.evaluate(function() {
2210 return $(".address:first").text();
2212 if (actual
!= expected
) {
2213 console
.log("Changing language generates incorrect address");
2214 console
.log("Expected: " + expected
);
2215 console
.log("Actual: " + actual
);
2224 // Suggested replacement for erroneous word in non-English language
2226 page
.open(url
, function(status
) {
2227 // Set an incorrect phrase in Italian
2228 page
.evaluate(function() {
2229 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2231 waitForFeedback(function() {
2232 // Check the suggestion is correct
2233 var feedback
= page
.evaluate(function() {
2234 return $(".feedback").text();
2236 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
2237 console
.log("Incorrect Italian word does not show suggested replacement");
2238 console
.log("Error: " + error
);
2247 // Japanese word does not break across lines.
2249 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2251 page
.open(url
, function(status
) {
2252 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
2253 if (!hasWordBreakCss
) {
2254 console
.log("Japanese words can break across lines mid-word");
2255 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
2258 // Run the next test
2263 // Language can be specified at page load using hash value in url
2265 page
.open(url
, function(status
) {
2266 // Set the page hash as if it were on a fresh page load
2267 page
.evaluate(function() {
2268 window
.location
.hash
= "#japanese";
2270 // Generate a random phrase
2271 page
.evaluate(function() {
2272 $(".generate").trigger("click");
2274 waitForGenerate(function() {
2275 // Check the phrase is in Japanese
2276 var phrase
= page
.evaluate(function() {
2277 return $(".phrase").val();
2279 if (phrase
.length
<= 0) {
2280 console
.log("No phrase generated using url hash");
2283 if (phrase
.charCodeAt(0) < 128) {
2284 console
.log("Language not detected from url hash on page load.");
2285 console
.log("Phrase: " + phrase
);
2293 // Entropy unit tests
2295 page
.open(url
, function(status
) {
2296 var response
= page
.evaluate(function() {
2298 // binary entropy is detected
2300 e
= Entropy
.fromString("01010101");
2301 if (e
.base
.str
!= "binary") {
2302 return "Binary entropy not detected correctly";
2308 // base6 entropy is detected
2310 e
= Entropy
.fromString("012345012345");
2311 if (e
.base
.str
!= "base 6") {
2312 return "base6 entropy not detected correctly";
2318 // dice entropy is detected
2320 e
= Entropy
.fromString("123456123456");
2321 if (e
.base
.str
!= "base 6 (dice)") {
2322 return "dice entropy not detected correctly";
2328 // base10 entropy is detected
2330 e
= Entropy
.fromString("0123456789");
2331 if (e
.base
.str
!= "base 10") {
2332 return "base10 entropy not detected correctly";
2338 // hex entropy is detected
2340 e
= Entropy
.fromString("0123456789ABCDEF");
2341 if (e
.base
.str
!= "hexadecimal") {
2342 return "hexadecimal entropy not detected correctly";
2348 // card entropy is detected
2350 e
= Entropy
.fromString("AC4DTHKS");
2351 if (e
.base
.str
!= "card") {
2352 return "card entropy not detected correctly";
2358 // entropy is case insensitive
2360 e
= Entropy
.fromString("aBcDeF");
2361 if (e
.cleanStr
!= "aBcDeF") {
2362 return "Entropy should not be case sensitive";
2368 // dice entropy is converted to base6
2370 e
= Entropy
.fromString("123456");
2371 if (e
.cleanStr
!= "123450") {
2372 return "Dice entropy is not automatically converted to base6";
2378 // dice entropy is preferred to base6 if ambiguous
2380 e
= Entropy
.fromString("12345");
2381 if (e
.base
.str
!= "base 6 (dice)") {
2382 return "dice not used as default over base 6";
2388 // unused characters are ignored
2390 e
= Entropy
.fromString("fghijkl");
2391 if (e
.cleanStr
!= "f") {
2392 return "additional characters are not ignored";
2398 // the lowest base is used by default
2399 // 7 could be decimal or hexadecimal, but should be detected as decimal
2401 e
= Entropy
.fromString("7");
2402 if (e
.base
.str
!= "base 10") {
2403 return "lowest base is not used";
2409 // Leading zeros are retained
2411 e
= Entropy
.fromString("000A");
2412 if (e
.cleanStr
!= "000A") {
2413 return "Leading zeros are not retained";
2419 // Leading zeros are correctly preserved for hex in binary string
2421 e
= Entropy
.fromString("2A");
2422 if (e
.binaryStr
!= "00101010") {
2423 return "Hex leading zeros are not correct in binary";
2429 // Leading zeros for base 6 as binary string
2430 // 20 = 2 events at 2.58 bits per event = 5 bits
2431 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2432 // so it needs 1 bit of padding to be the right bit length
2434 e
= Entropy
.fromString("20");
2435 if (e
.binaryStr
!= "01100") {
2436 return "Base 6 as binary has leading zeros";
2442 // Leading zeros for base 10 as binary string
2444 e
= Entropy
.fromString("17");
2445 if (e
.binaryStr
!= "010001") {
2446 return "Base 10 as binary has leading zeros";
2452 // Leading zeros for card entropy as binary string.
2453 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
2455 e
= Entropy
.fromString("2c");
2456 if (e
.binaryStr
!= "0010") {
2457 return "Card entropy as binary has leading zeros";
2463 // Keyboard mashing results in weak entropy
2464 // Despite being a long string, it's less than 30 bits of entropy
2466 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2467 if (e
.binaryStr
.length
>= 30) {
2468 return "Keyboard mashing should produce weak entropy";
2474 // Card entropy is used if every pair could be a card
2476 e
= Entropy
.fromString("4c3c2c");
2477 if (e
.base
.str
!= "card") {
2478 return "Card entropy not used if all pairs are cards";
2484 // Card entropy uses base 52
2485 // [ cards, binary ]
2489 [ "acqs", "11011100" ],
2490 [ "acks", "01011100" ],
2491 [ "2cac", "11111000" ],
2504 [ "ks2c", "01010100" ],
2505 [ "KS2C", "01010100" ],
2507 for (var i
=0; i
<cards
.length
; i
++) {
2508 var card
= cards
[i
][0];
2509 var result
= cards
[i
][1];
2510 e
= Entropy
.fromString(card
);
2511 console
.log(e
.binary
+ " " + result
);
2512 if (e
.binaryStr
!== result
) {
2513 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2522 if (response
!= "PASS") {
2523 console
.log("Entropy unit tests");
2524 console
.log(response
);
2531 // Entropy can be entered by the user
2533 page
.open(url
, function(status
) {
2535 mnemonic: "abandon abandon ability",
2536 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2539 page
.evaluate(function() {
2540 $(".use-entropy").prop("checked", true).trigger("change");
2541 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2543 // check the mnemonic is set and address is correct
2544 waitForGenerate(function() {
2545 var actual
= page
.evaluate(function() {
2547 address: $(".address:first").text(),
2548 mnemonic: $(".phrase").val(),
2551 if (actual
.mnemonic
!= expected
.mnemonic
) {
2552 console
.log("Entropy does not generate correct mnemonic");
2553 console
.log("Expected: " + expected
.mnemonic
);
2554 console
.log("Got: " + actual
.mnemonic
);
2557 if (actual
.address
!= expected
.address
) {
2558 console
.log("Entropy does not generate correct address");
2559 console
.log("Expected: " + expected
.address
);
2560 console
.log("Got: " + actual
.address
);
2568 // A warning about entropy is shown to the user, with additional information
2570 page
.open(url
, function(status
) {
2571 // get text content from entropy sections of page
2572 var hasWarning
= page
.evaluate(function() {
2573 var entropyText
= $(".entropy-container").text();
2574 var warning
= "mnemonic may be insecure";
2575 if (entropyText
.indexOf(warning
) == -1) {
2578 var readMoreText
= $("#entropy-notes").parent().text();
2579 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2580 if (readMoreText
.indexOf(goodSources
) == -1) {
2585 // check the warnings and information are shown
2587 console
.log("Page does not contain warning about using own entropy");
2594 // The types of entropy available are described to the user
2596 page
.open(url
, function(status
) {
2597 // get placeholder text for entropy field
2598 var placeholder
= page
.evaluate(function() {
2599 return $(".entropy").attr("placeholder");
2609 for (var i
=0; i
<options
.length
; i
++) {
2610 var option
= options
[i
];
2611 if (placeholder
.indexOf(option
) == -1) {
2612 console
.log("Available entropy type is not shown to user: " + option
);
2620 // The actual entropy used is shown to the user
2622 page
.open(url
, function(status
) {
2624 var badEntropySource
= page
.evaluate(function() {
2625 var entropy
= "Not A Very Good Entropy Source At All";
2626 $(".use-entropy").prop("checked", true).trigger("change");
2627 $(".entropy").val(entropy
).trigger("input");
2629 // check the actual entropy being used is shown
2630 waitForEntropyFeedback(function() {
2631 var expectedText
= "AedEceAA";
2632 var entropyText
= page
.evaluate(function() {
2633 return $(".entropy-container").text();
2635 if (entropyText
.indexOf(expectedText
) == -1) {
2636 console
.log("Actual entropy used is not shown");
2644 // Binary entropy can be entered
2646 page
.open(url
, function(status
) {
2648 page
.evaluate(function() {
2649 $(".use-entropy").prop("checked", true).trigger("change");
2650 $(".entropy").val("01").trigger("input");
2652 // check the entropy is shown to be the correct type
2653 waitForEntropyFeedback(function() {
2654 var entropyText
= page
.evaluate(function() {
2655 return $(".entropy-container").text();
2657 if (entropyText
.indexOf("binary") == -1) {
2658 console
.log("Binary entropy is not detected and presented to user");
2666 // Base 6 entropy can be entered
2668 page
.open(url
, function(status
) {
2670 page
.evaluate(function() {
2671 $(".use-entropy").prop("checked", true).trigger("change");
2672 $(".entropy").val("012345").trigger("input");
2674 // check the entropy is shown to be the correct type
2675 waitForEntropyFeedback(function() {
2676 var entropyText
= page
.evaluate(function() {
2677 return $(".entropy-container").text();
2679 if (entropyText
.indexOf("base 6") == -1) {
2680 console
.log("Base 6 entropy is not detected and presented to user");
2688 // Base 6 dice entropy can be entered
2690 page
.open(url
, function(status
) {
2692 page
.evaluate(function() {
2693 $(".use-entropy").prop("checked", true).trigger("change");
2694 $(".entropy").val("123456").trigger("input");
2696 // check the entropy is shown to be the correct type
2697 waitForEntropyFeedback(function() {
2698 var entropyText
= page
.evaluate(function() {
2699 return $(".entropy-container").text();
2701 if (entropyText
.indexOf("dice") == -1) {
2702 console
.log("Dice entropy is not detected and presented to user");
2710 // Base 10 entropy can be entered
2712 page
.open(url
, function(status
) {
2714 page
.evaluate(function() {
2715 $(".use-entropy").prop("checked", true).trigger("change");
2716 $(".entropy").val("789").trigger("input");
2718 // check the entropy is shown to be the correct type
2719 waitForEntropyFeedback(function() {
2720 var entropyText
= page
.evaluate(function() {
2721 return $(".entropy-container").text();
2723 if (entropyText
.indexOf("base 10") == -1) {
2724 console
.log("Base 10 entropy is not detected and presented to user");
2732 // Hexadecimal entropy can be entered
2734 page
.open(url
, function(status
) {
2736 page
.evaluate(function() {
2737 $(".use-entropy").prop("checked", true).trigger("change");
2738 $(".entropy").val("abcdef").trigger("input");
2740 // check the entropy is shown to be the correct type
2741 waitForEntropyFeedback(function() {
2742 var entropyText
= page
.evaluate(function() {
2743 return $(".entropy-container").text();
2745 if (entropyText
.indexOf("hexadecimal") == -1) {
2746 console
.log("Hexadecimal entropy is not detected and presented to user");
2754 // Dice entropy value is shown as the converted base 6 value
2756 page
.open(url
, function(status
) {
2758 page
.evaluate(function() {
2759 $(".use-entropy").prop("checked", true).trigger("change");
2760 $(".entropy").val("123456").trigger("input");
2762 // check the entropy is shown as base 6, not as the original dice value
2763 waitForEntropyFeedback(function() {
2764 var entropyText
= page
.evaluate(function() {
2765 return $(".entropy-container").text();
2767 if (entropyText
.indexOf("123450") == -1) {
2768 console
.log("Dice entropy is not shown to user as base 6 value");
2771 if (entropyText
.indexOf("123456") > -1) {
2772 console
.log("Dice entropy value is shown instead of true base 6 value");
2780 // The number of bits of entropy accumulated is shown
2782 page
.open(url
, function(status
) {
2785 [ "0000 0000 0000 0000 0000", "20" ],
2788 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2789 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2794 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2801 [ "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)
2802 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2805 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2806 [ "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
2809 page
.evaluate(function(e
) {
2810 $(".use-entropy").prop("checked", true).trigger("change");
2813 var nextTest
= function runNextTest(i
) {
2814 var entropy
= tests
[i
][0];
2815 var expected
= tests
[i
][1];
2817 page
.evaluate(function(e
) {
2818 $(".entropy").val(e
).trigger("input");
2820 // check the number of bits of entropy is shown
2821 waitForEntropyFeedback(function() {
2822 var entropyText
= page
.evaluate(function() {
2823 return $(".entropy-container").text();
2825 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2826 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2829 var isLastTest
= i
== tests
.length
- 1;
2842 // There is feedback provided about the supplied entropy
2844 page
.open(url
, function(status
) {
2849 type: "hexadecimal",
2853 strength: "less than a second",
2856 entropy: "AAAAAAAA",
2857 filtered: "AAAAAAAA",
2858 type: "hexadecimal",
2862 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2865 entropy: "AAAAAAAA B",
2866 filtered: "AAAAAAAAB",
2867 type: "hexadecimal",
2871 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2874 entropy: "AAAAAAAA BBBBBBBB",
2875 filtered: "AAAAAAAABBBBBBBB",
2876 type: "hexadecimal",
2880 strength: "less than a second - Repeats like \"aaa\" are easy to guess",
2883 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2884 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2885 type: "hexadecimal",
2889 strength: "less than a second",
2892 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2893 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2894 type: "hexadecimal",
2898 strength: "2 minutes",
2901 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2902 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2903 type: "hexadecimal",
2910 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2911 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2912 type: "hexadecimal",
2916 strength: "3 years",
2919 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2920 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2921 type: "hexadecimal",
2925 strength: "centuries",
2933 strength: "less than a second",
2936 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2937 type: "card (full deck)",
2941 strength: "centuries",
2944 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2945 type: "card (full deck, 1 duplicate: 3d)",
2949 strength: "centuries",
2952 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2953 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2957 strength: "centuries",
2960 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2961 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2965 strength: "centuries",
2967 // Next test was throwing uncaught error in zxcvbn
2968 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2970 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2971 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2975 strength: "centuries",
2977 // Case insensitivity to duplicate cards
2980 type: "card (1 duplicate: AS)",
2984 strength: "less than a second",
2988 type: "card (1 duplicate: as)",
2992 strength: "less than a second",
2994 // Missing cards are detected
2996 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2997 type: "card (1 missing: 9C)",
3001 strength: "centuries",
3004 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
3005 type: "card (2 missing: 9C 5D)",
3009 strength: "centuries",
3012 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
3013 type: "card (4 missing: 9C 5D QD...)",
3017 strength: "centuries",
3019 // More than six missing cards does not show message
3021 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
3026 strength: "centuries",
3028 // Multiple decks of cards increases bits per event
3048 entropy: "3d3d3d3d",
3054 entropy: "3d3d3d3d3d",
3060 entropy: "3d3d3d3d3d3d",
3066 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
3070 strength: 'less than a second - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
3074 page
.evaluate(function() {
3075 $(".use-entropy").prop("checked", true).trigger("change");
3077 var nextTest
= function runNextTest(i
) {
3078 function getFeedbackError(expected
, actual
) {
3079 if ("filtered" in expected
&& actual
.indexOf(expected
.filtered
) == -1) {
3080 return "Filtered value not in feedback";
3082 if ("type" in expected
&& actual
.indexOf(expected
.type
) == -1) {
3083 return "Entropy type not in feedback";
3085 if ("events" in expected
&& actual
.indexOf(expected
.events
) == -1) {
3086 return "Event count not in feedback";
3088 if ("bits" in expected
&& actual
.indexOf(expected
.bits
) == -1) {
3089 return "Bit count not in feedback";
3091 if ("strength" in expected
&& actual
.indexOf(expected
.strength
) == -1) {
3092 return "Strength not in feedback";
3094 if ("bitsPerEvent" in expected
&& actual
.indexOf(expected
.bitsPerEvent
) == -1) {
3095 return "bitsPerEvent not in feedback";
3100 page
.evaluate(function(e
) {
3101 $(".addresses").empty();
3102 $(".phrase").val("");
3103 $(".entropy").val(e
).trigger("input");
3105 waitForEntropyFeedback(function() {
3106 var mnemonic
= page
.evaluate(function() {
3107 return $(".phrase").val();
3109 // Check mnemonic length
3110 if ("words" in test
&& test
.words
== 0) {
3111 if (mnemonic
.length
> 0) {
3112 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3113 console
.log("Entropy: " + test
.entropy
);
3114 console
.log("Mnemonic: " + mnemonic
);
3118 else if ("words" in test
) {
3119 if (mnemonic
.split(" ").length
!= test
.words
) {
3120 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3121 console
.log("Entropy: " + test
.entropy
);
3122 console
.log("Mnemonic: " + mnemonic
);
3127 var feedback
= page
.evaluate(function() {
3128 return $(".entropy-container").text();
3130 var feedbackError
= getFeedbackError(test
, feedback
);
3131 if (feedbackError
) {
3132 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
3133 console
.log(feedbackError
);
3137 var isLastTest
= i
== tests
.length
- 1;
3150 // Entropy is truncated from the left
3152 page
.open(url
, function(status
) {
3153 var expected
= "avocado zoo zone";
3155 page
.evaluate(function() {
3156 $(".use-entropy").prop("checked", true).trigger("change");
3157 var entropy
= "00000000 00000000 00000000 00000000";
3158 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
3159 $(".entropy").val(entropy
).trigger("input");
3161 // check the entropy is truncated from the right
3162 waitForGenerate(function() {
3163 var actual
= page
.evaluate(function() {
3164 return $(".phrase").val();
3166 if (actual
!= expected
) {
3167 console
.log("Entropy is not truncated from the right");
3168 console
.log("Expected: " + expected
);
3169 console
.log("Got: " + actual
);
3177 // Very large entropy results in very long mnemonics
3179 page
.open(url
, function(status
) {
3181 page
.evaluate(function() {
3182 $(".use-entropy").prop("checked", true).trigger("change");
3184 // Generate a very long entropy string
3185 for (var i
=0; i
<33; i
++) {
3186 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3188 $(".entropy").val(entropy
).trigger("input");
3190 // check the mnemonic is very long
3191 waitForGenerate(function() {
3192 var wordCount
= page
.evaluate(function() {
3193 return $(".phrase").val().split(" ").length
;
3195 if (wordCount
!= 99) {
3196 console
.log("Large entropy does not generate long mnemonic");
3197 console
.log("Expected 99 words, got " + wordCount
);
3205 // Is compatible with bip32jp entropy
3206 // https://bip32jp.github.io/english/index.html
3208 // Is incompatible with:
3211 page
.open(url
, function(status
) {
3212 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";
3214 page
.evaluate(function() {
3215 $(".use-entropy").prop("checked", true).trigger("change");
3216 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
3217 $(".entropy").val(entropy
).trigger("input");
3219 // check the mnemonic matches the expected value from bip32jp
3220 waitForGenerate(function() {
3221 var actual
= page
.evaluate(function() {
3222 return $(".phrase").val();
3224 if (actual
!= expected
) {
3225 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
3226 console
.log("Expected: " + expected
);
3227 console
.log("Got: " + actual
);
3235 // Blank entropy does not generate mnemonic or addresses
3237 page
.open(url
, function(status
) {
3239 page
.evaluate(function() {
3240 $(".use-entropy").prop("checked", true).trigger("change");
3241 $(".entropy").val("").trigger("input");
3243 waitForFeedback(function() {
3244 // check there is no mnemonic
3245 var phrase
= page
.evaluate(function() {
3246 return $(".phrase").val();
3249 console
.log("Blank entropy does not result in blank mnemonic");
3250 console
.log("Got: " + phrase
);
3253 // check there are no addresses displayed
3254 var addresses
= page
.evaluate(function() {
3255 return $(".address").length
;
3257 if (addresses
!= 0) {
3258 console
.log("Blank entropy does not result in zero addresses");
3261 // Check the feedback says 'blank entropy'
3262 var feedback
= page
.evaluate(function() {
3263 return $(".feedback").text();
3265 if (feedback
!= "Blank entropy") {
3266 console
.log("Blank entropy does not show feedback message");
3274 // Mnemonic length can be selected even for weak entropy
3276 page
.open(url
, function(status
) {
3278 page
.evaluate(function() {
3279 $(".use-entropy").prop("checked", true).trigger("change");
3280 $(".entropy").val("012345");
3281 $(".mnemonic-length").val("18").trigger("change");
3283 // check the mnemonic is the correct length
3284 waitForGenerate(function() {
3285 var phrase
= page
.evaluate(function() {
3286 return $(".phrase").val();
3288 var numberOfWords
= phrase
.split(/\s/g).length
;
3289 if (numberOfWords
!= 18) {
3290 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3291 console
.log(phrase
);
3300 // https://github.com/iancoleman/bip39/issues/33
3301 // Final cards should contribute entropy
3303 page
.open(url
, function(status
) {
3305 page
.evaluate(function() {
3306 $(".use-entropy").prop("checked", true).trigger("change");
3307 $(".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");
3310 waitForGenerate(function() {
3311 var originalPhrase
= page
.evaluate(function() {
3312 return $(".phrase").val();
3314 // Set the last 12 cards to be AS
3315 page
.evaluate(function() {
3316 $(".addresses").empty();
3317 $(".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");
3319 // get the new mnemonic
3320 waitForGenerate(function() {
3321 var newPhrase
= page
.evaluate(function() {
3322 return $(".phrase").val();
3324 // check the phrase has changed
3325 if (newPhrase
== originalPhrase
) {
3326 console
.log("Changing last 12 cards does not change mnemonic");
3327 console
.log("Original:");
3328 console
.log(originalPhrase
);
3329 console
.log("New:");
3330 console
.log(newPhrase
);
3340 // https://github.com/iancoleman/bip39/issues/35
3343 page
.open(url
, function(status
) {
3345 page
.evaluate(function() {
3346 $(".generate").click();
3348 waitForGenerate(function() {
3349 var p
= page
.evaluate(function() {
3350 // get position of mnemonic element
3351 return $(".phrase").offset();
3353 p
.top
= Math
.ceil(p
.top
);
3354 p
.left
= Math
.ceil(p
.left
);
3355 // check the qr code shows
3356 page
.sendEvent("mousemove", p
.left
+4, p
.top
+4);
3357 var qrShowing
= page
.evaluate(function() {
3358 return $(".qr-container").find("canvas").length
> 0;
3361 console
.log("QR Code does not show");
3364 // check the qr code hides
3365 page
.sendEvent("mousemove", p
.left
-4, p
.top
-4);
3366 var qrHidden
= page
.evaluate(function() {
3367 return $(".qr-container").find("canvas").length
== 0;
3370 console
.log("QR Code does not hide");
3378 // BIP44 account extendend private key is shown
3379 // github issue 37 - compatibility with electrum
3381 page
.open(url
, function(status
) {
3383 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3384 page
.evaluate(function() {
3385 $(".phrase").val("abandon abandon ability");
3386 $(".phrase").trigger("input");
3388 // check the BIP44 account extended private key
3389 waitForGenerate(function() {
3390 var actual
= page
.evaluate(function() {
3391 return $("#bip44 .account-xprv").val();
3393 if (actual
!= expected
) {
3394 console
.log("BIP44 account extended private key is incorrect");
3395 console
.log("Expected: " + expected
);
3396 console
.log("Actual: " + actual
);
3404 // BIP44 account extendend public key is shown
3405 // github issue 37 - compatibility with electrum
3407 page
.open(url
, function(status
) {
3409 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3410 page
.evaluate(function() {
3411 $(".phrase").val("abandon abandon ability");
3412 $(".phrase").trigger("input");
3414 // check the BIP44 account extended public key
3415 waitForGenerate(function() {
3416 var actual
= page
.evaluate(function() {
3417 return $("#bip44 .account-xpub").val();
3419 if (actual
!= expected
) {
3420 console
.log("BIP44 account extended public key is incorrect");
3421 console
.log("Expected: " + expected
);
3422 console
.log("Actual: " + actual
);
3431 // BIP32 root key can be set as an xpub
3433 page
.open(url
, function(status
) {
3435 page
.evaluate(function() {
3436 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3437 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3438 $("#root-key").val(bip44AccountXpub
);
3439 $("#root-key").trigger("input");
3441 waitForFeedback(function() {
3442 page
.evaluate(function() {
3444 $("#bip32-tab a").click();
3446 waitForGenerate(function() {
3447 page
.evaluate(function() {
3448 // derive external addresses for this xpub
3449 var firstAccountDerivationPath
= "m/0";
3450 $("#bip32-path").val(firstAccountDerivationPath
);
3451 $("#bip32-path").trigger("input");
3453 waitForGenerate(function() {
3454 // check the addresses are generated
3455 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3456 var actual
= page
.evaluate(function() {
3457 return $(".address:first").text();
3459 if (actual
!= expected
) {
3460 console
.log("xpub key does not generate addresses in table");
3461 console
.log("Expected: " + expected
);
3462 console
.log("Actual: " + actual
);
3465 // check the xprv key is not set
3466 var expected
= "NA";
3467 var actual
= page
.evaluate(function() {
3468 return $(".extended-priv-key").val();
3470 if (actual
!= expected
) {
3471 console
.log("xpub key as root shows derived bip32 xprv key");
3472 console
.log("Expected: " + expected
);
3473 console
.log("Actual: " + actual
);
3476 // check the private key is not set
3477 var expected
= "NA";
3478 var actual
= page
.evaluate(function() {
3479 return $(".privkey:first").text();
3481 if (actual
!= expected
) {
3482 console
.log("xpub key generates private key in addresses table");
3483 console
.log("Expected: " + expected
);
3484 console
.log("Actual: " + actual
);
3495 // xpub for bip32 root key will not work with hardened derivation paths
3497 page
.open(url
, function(status
) {
3499 page
.evaluate(function() {
3500 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3501 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3502 $("#root-key").val(bip44AccountXpub
);
3503 $("#root-key").trigger("input");
3505 waitForFeedback(function() {
3506 // Check feedback is correct
3507 var expected
= "Hardened derivation path is invalid with xpub key";
3508 var actual
= page
.evaluate(function() {
3509 return $(".feedback").text();
3511 if (actual
!= expected
) {
3512 console
.log("xpub key with hardened derivation path does not show feedback");
3513 console
.log("Expected: " + expected
);
3514 console
.log("Actual: " + actual
);
3517 // Check no addresses are shown
3519 var actual
= page
.evaluate(function() {
3520 return $(".addresses tr").length
;
3522 if (actual
!= expected
) {
3523 console
.log("addresses still show after setting xpub key with hardened derivation path");
3524 console
.log("Expected: " + expected
);
3525 console
.log("Actual: " + actual
);
3534 // no root key shows feedback
3536 page
.open(url
, function(status
) {
3537 // click the bip32 tab on fresh page
3538 page
.evaluate(function() {
3539 $("#bip32-tab a").click();
3541 waitForFeedback(function() {
3542 // Check feedback is correct
3543 var expected
= "No root key";
3544 var actual
= page
.evaluate(function() {
3545 return $(".feedback").text();
3547 if (actual
!= expected
) {
3548 console
.log("Blank root key not detected");
3549 console
.log("Expected: " + expected
);
3550 console
.log("Actual: " + actual
);
3559 // display error switching tabs while addresses are generating
3561 page
.open(url
, function(status
) {
3563 page
.evaluate(function() {
3564 $(".phrase").val("abandon abandon ability").trigger("input");
3566 waitForGenerate(function() {
3567 // set to generate 500 more addresses
3568 // generate more addresses
3569 // change tabs which should cancel the previous generating
3570 page
.evaluate(function() {
3571 $(".rows-to-add").val("100");
3573 $("#bip32-tab a").click();
3575 // check the derivation paths are in order and of the right quantity
3576 waitForGenerate(function() {
3577 var paths
= page
.evaluate(function() {
3578 return $(".index").map(function(i
, e
) {
3582 for (var i
=0; i
<paths
.length
; i
++) {
3583 var expected
= "m/0/" + i
;
3584 var actual
= paths
[i
];
3585 if (actual
!= expected
) {
3586 console
.log("Path " + i
+ " is not in correct order");
3587 console
.log("Expected: " + expected
);
3588 console
.log("Actual: " + actual
);
3592 if (paths
.length
!= 20) {
3593 console
.log("Generation was not cancelled by new action");
3603 // padding for binary should give length with multiple of 256
3604 // hashed entropy 1111 is length 252, so requires 4 leading zeros
3605 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3607 page
.open(url
, function(status
) {
3608 expected
= "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3610 page
.evaluate(function() {
3611 $(".use-entropy").prop("checked", true).trigger("change");
3612 $(".mnemonic-length").val("15");
3613 $(".entropy").val("1111").trigger("input");
3615 waitForGenerate(function() {
3617 var actual
= page
.evaluate(function() {
3618 return $(".phrase").val();
3620 // check the mnemonic is correct
3621 if (actual
!= expected
) {
3622 console
.log("Left padding error for entropy");
3623 console
.log("Expected: " + expected
);
3624 console
.log("Actual: " + actual
);
3632 // Github pull request 55
3633 // https://github.com/iancoleman/bip39/pull/55
3636 page
.open(url
, function(status
) {
3637 // set mnemonic and select bip32 tab
3638 page
.evaluate(function() {
3639 $("#bip32-tab a").click();
3640 $(".phrase").val("abandon abandon ability").trigger("input");
3642 waitForGenerate(function() {
3644 // set bip32 client to bitcoin core
3645 page
.evaluate(function() {
3646 var bitcoinCoreIndex
= "0";
3647 $("#bip32-client").val(bitcoinCoreIndex
).trigger("change");
3649 waitForGenerate(function() {
3650 // get the derivation path
3651 var actual
= page
.evaluate(function() {
3652 return $("#bip32-path").val();
3654 // check the derivation path is correct
3655 expected
= "m/0'/0'"
3656 if (actual
!= expected
) {
3657 console
.log("Selecting Bitcoin Core client does not set correct derivation path");
3658 console
.log("Expected: " + expected
);
3659 console
.log("Actual: " + actual
);
3662 // get hardened addresses
3663 var usesHardenedAddresses
= page
.evaluate(function() {
3664 return $(".hardened-addresses").prop("checked");
3666 // check hardened addresses is selected
3667 if(!usesHardenedAddresses
) {
3668 console
.log("Selecting Bitcoin Core client does not use hardened addresses");
3671 // check input is readonly
3672 var pathIsReadonly
= page
.evaluate(function() {
3673 return $("#bip32-path").prop("readonly");
3675 if (!pathIsReadonly
) {
3676 console
.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3680 // set bip32 client to multibit
3681 page
.evaluate(function() {
3682 var multibitIndex
= "2";
3683 $("#bip32-client").val(multibitIndex
).trigger("change");
3685 waitForGenerate(function() {
3686 // get the derivation path
3687 var actual
= page
.evaluate(function() {
3688 return $("#bip32-path").val();
3690 // check the derivation path is correct
3692 if (actual
!= expected
) {
3693 console
.log("Selecting Multibit client does not set correct derivation path");
3694 console
.log("Expected: " + expected
);
3695 console
.log("Actual: " + actual
);
3698 // get hardened addresses
3699 var usesHardenedAddresses
= page
.evaluate(function() {
3700 return $(".hardened-addresses").prop("checked");
3702 // check hardened addresses is selected
3703 if(usesHardenedAddresses
) {
3704 console
.log("Selecting Multibit client does not uncheck hardened addresses");
3707 // CUSTOM DERIVATION PATH
3708 // check input is not readonly
3709 page
.evaluate(function() {
3710 $("#bip32-client").val("custom").trigger("change");
3712 // do not wait for generate, since there is no change to the
3713 // derivation path there is no new generation performed
3714 var pathIsReadonly
= page
.evaluate(function() {
3715 return $("#bip32-path").prop("readonly");
3717 if (pathIsReadonly
) {
3718 console
.log("Selecting Custom Derivation Path does not allow derivation path input");
3729 // https://github.com/iancoleman/bip39/issues/58
3730 // bip32 derivation is correct, does not drop leading zeros
3732 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3734 page
.open(url
, function(status
) {
3735 // set the phrase and passphrase
3736 var expected
= "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3737 // Note that bitcore generates an incorrect address
3738 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3739 // see the medium.com link above for more details
3740 page
.evaluate(function() {
3741 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3742 $(".passphrase").val("banana").trigger("input");
3744 // check the address is generated correctly
3745 waitForGenerate(function() {
3746 var actual
= page
.evaluate(function() {
3747 return $(".address:first").text();
3749 if (actual
!= expected
) {
3750 console
.log("BIP32 derivation is incorrect");
3751 console
.log("Expected: " + expected
);
3752 console
.log("Actual: " + actual
);
3762 // Japanese mnemonics generate incorrect bip32 seed
3763 // BIP39 seed is set from phrase
3765 page
.open(url
, function(status
) {
3767 var expected
= "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3768 page
.evaluate(function() {
3769 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3770 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3771 $("#passphrase").trigger("input");
3773 // check the seed is generated correctly
3774 waitForGenerate(function() {
3775 var actual
= page
.evaluate(function() {
3776 return $(".seed").val();
3778 if (actual
!= expected
) {
3779 console
.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3780 console
.log("Expected: " + expected
);
3781 console
.log("Actual: " + actual
);
3789 // BIP49 official test vectors
3790 // https://github.com/bitcoin/bips/blob/master/bip-0049.mediawiki#test-vectors
3792 page
.open(url
, function(status
) {
3793 // set the phrase and select bitcoin testnet
3794 var expected
= "2Mww8dCYPUpKHofjgcXcBCEGmniw9CoaiD2";
3795 page
.evaluate(function() {
3796 $("#bip49-tab a").click();
3797 $(".phrase").val("abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about");
3798 $(".network option[selected]").removeAttr("selected");
3799 $(".network option").filter(function() {
3800 return $(this).html() == "BTC - Bitcoin Testnet";
3801 }).prop("selected", true);
3802 $(".network").trigger("change");
3803 $(".phrase").trigger("input");
3805 // check the first address
3806 waitForGenerate(function() {
3807 var actual
= page
.evaluate(function() {
3808 return $(".address:first").text();
3810 if (actual
!= expected
) {
3811 console
.log("BIP49 address is incorrect");
3812 console
.log("Expected: " + expected
);
3813 console
.log("Actual: " + actual
);
3821 // BIP49 derivation path is shown
3823 page
.open(url
, function(status
) {
3825 var expected
= "m/49'/0'/0'/0";
3826 page
.evaluate(function() {
3827 $("#bip49-tab a").click();
3828 $(".phrase").val("abandon abandon ability").trigger("input");
3830 // check the derivation path of the first address
3831 waitForGenerate(function() {
3832 var actual
= page
.evaluate(function() {
3833 return $("#bip49 .path").val();
3835 if (actual
!= expected
) {
3836 console
.log("BIP49 derivation path is incorrect");
3837 console
.log("Expected: " + expected
);
3838 console
.log("Actual: " + actual
);
3846 // BIP49 extended private key is shown
3848 page
.open(url
, function(status
) {
3850 var expected
= "xprvA1hukYsW7QfX9CVsaDAKde4eryajKa4DKWb6m9YjSnqkiZHrahFwwTJfEQTwBQ5kptWT5pZMkkusT1oK8dc1efQ8VFfq4SLSPAWd7Cpt423";
3851 page
.evaluate(function() {
3852 $("#bip49-tab a").click();
3853 $(".phrase").val("abandon abandon ability").trigger("input");
3855 // check the BIP49 extended private key
3856 waitForGenerate(function() {
3857 var actual
= page
.evaluate(function() {
3858 return $(".extended-priv-key").val();
3860 if (actual
!= expected
) {
3861 console
.log("BIP49 extended private key is incorrect");
3862 console
.log("Expected: " + expected
);
3863 console
.log("Actual: " + actual
);
3871 // BIP49 extended public key is shown
3873 page
.open(url
, function(status
) {
3875 var expected
= "xpub6EhGA4QPwnDpMgaLgEhKzn1PR1RDj2n4gjWhZXxM18NjbMd18EaCVFd95gkLARJaBD2rXAYJED2gdkUbGn1KkrSzCKR554AdABUELoainnt";
3876 page
.evaluate(function() {
3877 $("#bip49-tab a").click();
3878 $(".phrase").val("abandon abandon ability").trigger("input");
3880 // check the BIP49 extended public key
3881 waitForGenerate(function() {
3882 var actual
= page
.evaluate(function() {
3883 return $(".extended-pub-key").val();
3885 if (actual
!= expected
) {
3886 console
.log("BIP49 extended public key is incorrect");
3887 console
.log("Expected: " + expected
);
3888 console
.log("Actual: " + actual
);
3896 // BIP49 account field changes address list
3898 page
.open(url
, function(status
) {
3900 var expected
= "381wg1GGN4rP88rNC9v7QWsiww63yLVPsn";
3901 page
.evaluate(function() {
3902 $("#bip49-tab a").click();
3903 $(".phrase").val("abandon abandon ability").trigger("input");
3905 waitForGenerate(function() {
3906 // change the bip49 account field to 1
3907 page
.evaluate(function() {
3908 $("#bip49 .account").val("1");
3909 $("#bip49 .account").trigger("input");
3911 waitForGenerate(function() {
3912 // check the address for the new derivation path
3913 var actual
= page
.evaluate(function() {
3914 return $(".address:first").text();
3916 if (actual
!= expected
) {
3917 console
.log("BIP49 account field generates incorrect address");
3918 console
.log("Expected: " + expected
);
3919 console
.log("Actual: " + actual
);
3928 // BIP49 change field changes address list
3930 page
.open(url
, function(status
) {
3932 var expected
= "3PEM7MiKed5konBoN66PQhK8r3hjGhy9dT";
3933 page
.evaluate(function() {
3934 $("#bip49-tab a").click();
3935 $(".phrase").val("abandon abandon ability").trigger("input");
3937 waitForGenerate(function() {
3938 // change the bip49 change field to 1
3939 page
.evaluate(function() {
3940 $("#bip49 .change").val("1");
3941 $("#bip49 .change").trigger("input");
3943 waitForGenerate(function() {
3944 // check the address for the new derivation path
3945 var actual
= page
.evaluate(function() {
3946 return $(".address:first").text();
3948 if (actual
!= expected
) {
3949 console
.log("BIP49 change field generates incorrect address");
3950 console
.log("Expected: " + expected
);
3951 console
.log("Actual: " + actual
);
3960 // BIP49 account extendend private key is shown
3962 page
.open(url
, function(status
) {
3964 var expected
= "xprv9y3uhgQbfQZbj3o98nfgLDwGGuCJjUn7GKArSAZXjKgMjSdYHjQmTyf78s22g6jsGrxXvHB6HJeFyvFSPkuYZajeTGMZVXV6aNLWw2fagCn";
3965 page
.evaluate(function() {
3966 $("#bip49-tab a").click();
3967 $(".phrase").val("abandon abandon ability");
3968 $(".phrase").trigger("input");
3970 // check the BIP49 account extended private key
3971 waitForGenerate(function() {
3972 var actual
= page
.evaluate(function() {
3973 return $("#bip49 .account-xprv").val();
3975 if (actual
!= expected
) {
3976 console
.log("BIP49 account extended private key is incorrect");
3977 console
.log("Expected: " + expected
);
3978 console
.log("Actual: " + actual
);
3986 // BIP49 account extendend public key is shown
3988 page
.open(url
, function(status
) {
3990 var expected
= "xpub6C3G7BwVVn7twXscEpCghMszpw2o8wVxdY6TEYy9HfDLcExgqGj21myazAiq6HSmW2F1cBiFqJa3D1cqcDpSh8pbZF5x4iqpd4PyJvd3gjB";
3991 page
.evaluate(function() {
3992 $("#bip49-tab a").click();
3993 $(".phrase").val("abandon abandon ability");
3994 $(".phrase").trigger("input");
3996 // check the BIP49 account extended public key
3997 waitForGenerate(function() {
3998 var actual
= page
.evaluate(function() {
3999 return $("#bip49 .account-xpub").val();
4001 if (actual
!= expected
) {
4002 console
.log("BIP49 account extended public key is incorrect");
4003 console
.log("Expected: " + expected
);
4004 console
.log("Actual: " + actual
);
4012 // Test selecting coin where bip49 is unavailable (eg CLAM)
4014 page
.open(url
, function(status
) {
4016 page
.evaluate(function() {
4017 $("#bip49-tab a").click();
4018 $(".phrase").val("abandon abandon ability");
4019 $(".phrase").trigger("input");
4021 waitForGenerate(function() {
4022 // select non-bip49 network, ie CLAM network
4023 page
.evaluate(function() {
4024 $(".network option[selected]").removeAttr("selected");
4025 $(".network option").filter(function() {
4026 return $(this).html() == "CLAM - Clams";
4027 }).prop("selected", true);
4028 $(".network").trigger("change");
4030 // check the BIP49 error is shown
4031 var bip49ErrorShown
= page
.evaluate(function() {
4032 var bip49hidden
= $("#bip49 .available").hasClass("hidden");
4033 bip49hidden
= bip49hidden
&& !($("#bip49 .unavailable").hasClass("hidden"));
4036 if (!bip49ErrorShown
) {
4037 console
.log("BIP49 error not shown for non-bip49 network");
4040 // check there are no addresses shown
4041 var addressCount
= page
.evaluate(function() {
4042 return $(".address").length
;
4044 if (addressCount
!= 0) {
4045 console
.log("BIP49 address count for non-bip49 network is " + addressCount
);
4048 // check the derived keys are blank
4049 var areBlank
= page
.evaluate(function() {
4050 var prvKeyIsBlank
= $(".extended-priv-key").val().length
== 0;
4051 var pubKeyIsBlank
= $(".extended-pub-key").val().length
== 0;
4052 return prvKeyIsBlank
&& pubKeyIsBlank
;
4055 console
.log("BIP49 extended keys for non-bip49 network are not blank ");
4063 // If you wish to add more tests, do so here...
4065 // Here is a blank test template
4069 page.open(url, function(status) {
4070 // Do something on the page
4071 page.evaluate(function() {
4072 $(".phrase").val("abandon abandon ability").trigger("input");
4074 waitForGenerate(function() {
4075 // Check the result of doing the thing
4076 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
4077 var actual = page.evaluate(function() {
4078 return $(".address:first").text();
4080 if (actual != expected) {
4081 console.log("A specific message about what failed");
4082 console.log("Expected: " + expected);
4083 console.log("Actual: " + actual);
4086 // Run the next test
4096 console
.log("Running tests...");
4097 tests
= shuffle(tests
);