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() == "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() == "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() == "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("Litecoin 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() == "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() == "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() == "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() == "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() == "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() == "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";
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 dash
576 page
.open(url
, function(status
) {
577 // set the phrase and coin
578 var expected
= "XdbhtMuGsPSkE6bPdNTHoFSszQKmK4S5LT";
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() == "DASH";
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("DASH address is incorrect");
595 console
.log("Expected: " + expected
);
596 console
.log("Actual: " + actual
);
604 // Network can be set to game
606 page
.open(url
, function(status
) {
607 // set the phrase and coin
608 var expected
= "GSMY9bAp36cMR4zyT4uGVS7GFjpdXbao5Q";
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() == "GAME";
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("GAME address is incorrect");
625 console
.log("Expected: " + expected
);
626 console
.log("Actual: " + actual
);
634 // Network can be set to namecoin
636 page
.open(url
, function(status
) {
637 // set the phrase and coin
638 var expected
= "Mw2vK2Bvex1yYtYF6sfbEg2YGoUc98YUD2";
639 page
.evaluate(function() {
640 $(".phrase").val("abandon abandon ability");
641 $(".phrase").trigger("input");
642 $(".network option[selected]").removeAttr("selected");
643 $(".network option").filter(function() {
644 return $(this).html() == "Namecoin";
645 }).prop("selected", true);
646 $(".network").trigger("change");
648 // check the address is generated correctly
649 waitForGenerate(function() {
650 var actual
= page
.evaluate(function() {
651 return $(".address:first").text();
653 if (actual
!= expected
) {
654 console
.log("Namecoin address is incorrect");
655 console
.log("Expected: " + expected
);
656 console
.log("Actual: " + actual
);
664 // Network can be set to peercoin
666 page
.open(url
, function(status
) {
667 // set the phrase and coin
668 var expected
= "PVAiioTaK2eDHSEo3tppT9AVdBYqxRTBAm";
669 page
.evaluate(function() {
670 $(".phrase").val("abandon abandon ability");
671 $(".phrase").trigger("input");
672 $(".network option[selected]").removeAttr("selected");
673 $(".network option").filter(function() {
674 return $(this).html() == "Peercoin";
675 }).prop("selected", true);
676 $(".network").trigger("change");
678 // check the address is generated correctly
679 waitForGenerate(function() {
680 var actual
= page
.evaluate(function() {
681 return $(".address:first").text();
683 if (actual
!= expected
) {
684 console
.log("Peercoin address is incorrect");
685 console
.log("Expected: " + expected
);
686 console
.log("Actual: " + actual
);
694 // Network can be set to ethereum
697 page
.open(url
, function(status
) {
699 // set the phrase and coin
700 page
.evaluate(function() {
701 $(".phrase").val("abandon abandon ability");
702 $(".phrase").trigger("input");
703 $(".network option[selected]").removeAttr("selected");
704 $(".network option").filter(function() {
705 return $(this).html() == "Ethereum";
706 }).prop("selected", true);
707 $(".network").trigger("change");
709 waitForGenerate(function() {
710 // check the address is generated correctly
711 // this value comes from
712 // https://www.myetherwallet.com/#view-wallet-info
713 // Unusual capitalization is due to checksum
714 var expected
= "0xe5815d5902Ad612d49283DEdEc02100Bd44C2772";
715 var actual
= page
.evaluate(function() {
716 return $(".address:first").text();
718 if (actual
!= expected
) {
719 console
.log("Ethereum address is incorrect");
720 console
.log("Expected: " + expected
);
721 console
.log("Actual: " + actual
);
724 // check the private key is correct
725 // this private key can be imported into
726 // https://www.myetherwallet.com/#view-wallet-info
727 // and it should correlate to the address above
728 var expected
= "8f253078b73d7498302bb78c171b23ce7a8fb511987d2b2702b731638a4a15e7";
729 var actual
= page
.evaluate(function() {
730 return $(".privkey:first").text();
732 if (actual
!= expected
) {
733 console
.log("Ethereum privkey is incorrect");
734 console
.log("Expected: " + expected
);
735 console
.log("Actual: " + actual
);
738 // check the public key is correct
740 // don't have any third-party source to generate the expected value
741 //var expected = "?";
742 //var actual = page.evaluate(function() {
743 // return $(".pubkey:first").text();
745 //if (actual != expected) {
746 // console.log("Ethereum privkey is incorrect");
747 // console.log("Expected: " + expected);
748 // console.log("Actual: " + actual);
756 // Network can be set to Slimcoin
758 page
.open(url
, function(status
) {
759 // set the phrase and coin
760 var expected
= "SNzPi1CafHFm3WWjRo43aMgiaEEj3ogjww";
761 page
.evaluate(function() {
762 $(".phrase").val("abandon abandon ability");
763 $(".phrase").trigger("input");
764 $(".network option[selected]").removeAttr("selected");
765 $(".network option").filter(function() {
766 return $(this).html() == "Slimcoin";
767 }).prop("selected", true);
768 $(".network").trigger("change");
770 // check the address is generated correctly
771 waitForGenerate(function() {
772 var actual
= page
.evaluate(function() {
773 return $(".address:first").text();
775 if (actual
!= expected
) {
776 console
.log("Slimcoin address is incorrect");
777 console
.log("Expected: " + expected
);
778 console
.log("Actual: " + actual
);
786 // Network can be set to Slimcointn
788 page
.open(url
, function(status
) {
789 // set the phrase and coin
790 var expected
= "n3nMgWufTek5QQAr6uwMhg5xbzj8xqc4Dq";
791 page
.evaluate(function() {
792 $(".phrase").val("abandon abandon ability");
793 $(".phrase").trigger("input");
794 $(".network option[selected]").removeAttr("selected");
795 $(".network option").filter(function() {
796 return $(this).html() == "Slimcoin Testnet";
797 }).prop("selected", true);
798 $(".network").trigger("change");
800 // check the address is generated correctly
801 waitForGenerate(function() {
802 var actual
= page
.evaluate(function() {
803 return $(".address:first").text();
805 if (actual
!= expected
) {
806 console
.log("Slimcoin testnet address is incorrect");
807 console
.log("Expected: " + expected
);
808 console
.log("Actual: " + actual
);
816 // BIP39 seed is set from phrase
818 page
.open(url
, function(status
) {
820 var expected
= "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
821 page
.evaluate(function() {
822 $(".phrase").val("abandon abandon ability");
823 $(".phrase").trigger("input");
825 // check the address is generated correctly
826 waitForGenerate(function() {
827 var actual
= page
.evaluate(function() {
828 return $(".seed").val();
830 if (actual
!= expected
) {
831 console
.log("BIP39 seed is incorrectly generated from mnemonic");
832 console
.log("Expected: " + expected
);
833 console
.log("Actual: " + actual
);
841 // BIP32 root key is set from phrase
843 page
.open(url
, function(status
) {
845 var expected
= "xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi";
846 page
.evaluate(function() {
847 $(".phrase").val("abandon abandon ability");
848 $(".phrase").trigger("input");
850 // check the address is generated correctly
851 waitForGenerate(function() {
852 var actual
= page
.evaluate(function() {
853 return $(".root-key").val();
855 if (actual
!= expected
) {
856 console
.log("Root key is incorrectly generated from mnemonic");
857 console
.log("Expected: " + expected
);
858 console
.log("Actual: " + actual
);
866 // Tabs show correct addresses when changed
868 page
.open(url
, function(status
) {
870 var expected
= "17uQ7s2izWPwBmEVFikTmZUjbBKWYdJchz";
871 page
.evaluate(function() {
872 $(".phrase").val("abandon abandon ability");
873 $(".phrase").trigger("input");
876 waitForGenerate(function() {
877 page
.evaluate(function() {
878 $("#bip32-tab a").click();
880 // check the address is generated correctly
881 waitForGenerate(function() {
882 var actual
= page
.evaluate(function() {
883 return $(".address:first").text();
885 if (actual
!= expected
) {
886 console
.log("Clicking tab generates incorrect address");
887 console
.log("Expected: " + expected
);
888 console
.log("Actual: " + actual
);
897 // BIP44 derivation path is shown
899 page
.open(url
, function(status
) {
901 var expected
= "m/44'/0'/0'/0";
902 page
.evaluate(function() {
903 $(".phrase").val("abandon abandon ability");
904 $(".phrase").trigger("input");
906 // check the derivation path of the first address
907 waitForGenerate(function() {
908 var actual
= page
.evaluate(function() {
909 return $("#bip44 .path").val();
911 if (actual
!= expected
) {
912 console
.log("BIP44 derivation path is incorrect");
913 console
.log("Expected: " + expected
);
914 console
.log("Actual: " + actual
);
922 // BIP44 extended private key is shown
924 page
.open(url
, function(status
) {
926 var expected
= "xprvA2DxxvPZcyRvYgZMGS53nadR32mVDeCyqQYyFhrCVbJNjPoxMeVf7QT5g7mQASbTf9Kp4cryvcXnu2qurjWKcrdsr91jXymdCDNxKgLFKJG";
927 page
.evaluate(function() {
928 $(".phrase").val("abandon abandon ability");
929 $(".phrase").trigger("input");
931 // check the BIP44 extended private key
932 waitForGenerate(function() {
933 var actual
= page
.evaluate(function() {
934 return $(".extended-priv-key").val();
936 if (actual
!= expected
) {
937 console
.log("BIP44 extended private key is incorrect");
938 console
.log("Expected: " + expected
);
939 console
.log("Actual: " + actual
);
947 // BIP44 extended public key is shown
949 page
.open(url
, function(status
) {
951 var expected
= "xpub6FDKNRvTTLzDmAdpNTc49ia9b4byd6vqCdUa46Fp3vqMcC96uBoufCmZXQLiN5AK3iSCJMhf9gT2sxkpyaPepRuA7W3MujV5tGmF5VfbueM";
952 page
.evaluate(function() {
953 $(".phrase").val("abandon abandon ability");
954 $(".phrase").trigger("input");
956 // check the BIP44 extended public key
957 waitForGenerate(function() {
958 var actual
= page
.evaluate(function() {
959 return $(".extended-pub-key").val();
961 if (actual
!= expected
) {
962 console
.log("BIP44 extended public key is incorrect");
963 console
.log("Expected: " + expected
);
964 console
.log("Actual: " + actual
);
972 // BIP44 purpose field changes address list
974 page
.open(url
, function(status
) {
976 var expected
= "1JbDzRJ2cDT8aat2xwKd6Pb2zzavow5MhF";
977 page
.evaluate(function() {
978 $(".phrase").val("abandon abandon ability");
979 $(".phrase").trigger("input");
981 waitForGenerate(function() {
982 // change the bip44 purpose field to 45
983 page
.evaluate(function() {
984 $("#bip44 .purpose").val("45");
985 $("#bip44 .purpose").trigger("input");
987 waitForGenerate(function() {
988 // check the address for the new derivation path
989 var actual
= page
.evaluate(function() {
990 return $(".address:first").text();
992 if (actual
!= expected
) {
993 console
.log("BIP44 purpose field generates incorrect address");
994 console
.log("Expected: " + expected
);
995 console
.log("Actual: " + actual
);
1004 // BIP44 coin field changes address list
1006 page
.open(url
, function(status
) {
1008 var expected
= "1F6dB2djQYrxoyfZZmfr6D5voH8GkJTghk";
1009 page
.evaluate(function() {
1010 $(".phrase").val("abandon abandon ability");
1011 $(".phrase").trigger("input");
1013 waitForGenerate(function() {
1014 // change the bip44 purpose field to 45
1015 page
.evaluate(function() {
1016 $("#bip44 .coin").val("1");
1017 $("#bip44 .coin").trigger("input");
1019 waitForGenerate(function() {
1020 // check the address for the new derivation path
1021 var actual
= page
.evaluate(function() {
1022 return $(".address:first").text();
1024 if (actual
!= expected
) {
1025 console
.log("BIP44 coin field generates incorrect address");
1026 console
.log("Expected: " + expected
);
1027 console
.log("Actual: " + actual
);
1036 // BIP44 account field changes address list
1038 page
.open(url
, function(status
) {
1040 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1041 page
.evaluate(function() {
1042 $(".phrase").val("abandon abandon ability");
1043 $(".phrase").trigger("input");
1045 waitForGenerate(function() {
1046 // change the bip44 purpose field to 45
1047 page
.evaluate(function() {
1048 $("#bip44 .account").val("1");
1049 $("#bip44 .account").trigger("input");
1051 waitForGenerate(function() {
1052 // check the address for the new derivation path
1053 var actual
= page
.evaluate(function() {
1054 return $(".address:first").text();
1056 if (actual
!= expected
) {
1057 console
.log("BIP44 account field generates incorrect address");
1058 console
.log("Expected: " + expected
);
1059 console
.log("Actual: " + actual
);
1068 // BIP44 change field changes address list
1070 page
.open(url
, function(status
) {
1072 var expected
= "1KAGfWgqfVbSSXY56fNQ7YnhyKuoskHtYo";
1073 page
.evaluate(function() {
1074 $(".phrase").val("abandon abandon ability");
1075 $(".phrase").trigger("input");
1077 waitForGenerate(function() {
1078 // change the bip44 purpose field to 45
1079 page
.evaluate(function() {
1080 $("#bip44 .change").val("1");
1081 $("#bip44 .change").trigger("input");
1083 waitForGenerate(function() {
1084 // check the address for the new derivation path
1085 var actual
= page
.evaluate(function() {
1086 return $(".address:first").text();
1088 if (actual
!= expected
) {
1089 console
.log("BIP44 change field generates incorrect address");
1090 console
.log("Expected: " + expected
);
1091 console
.log("Actual: " + actual
);
1100 // BIP32 derivation path can be set
1102 page
.open(url
, function(status
) {
1104 var expected
= "16pYQQdLD1hH4hwTGLXBaZ9Teboi1AGL8L";
1105 page
.evaluate(function() {
1106 $(".phrase").val("abandon abandon ability");
1107 $(".phrase").trigger("input");
1110 waitForGenerate(function() {
1111 page
.evaluate(function() {
1112 $("#bip32-tab a").click();
1114 // set the derivation path to m/1
1115 waitForGenerate(function() {
1116 page
.evaluate(function() {
1117 $("#bip32 .path").val("m/1");
1118 $("#bip32 .path").trigger("input");
1120 // check the address is generated correctly
1121 waitForGenerate(function() {
1122 var actual
= page
.evaluate(function() {
1123 return $(".address:first").text();
1125 if (actual
!= expected
) {
1126 console
.log("Custom BIP32 path generates incorrect address");
1127 console
.log("Expected: " + expected
);
1128 console
.log("Actual: " + actual
);
1138 // BIP32 can use hardened derivation paths
1140 page
.open(url
, function(status
) {
1142 var expected
= "14aXZeprXAE3UUKQc4ihvwBvww2LuEoHo4";
1143 page
.evaluate(function() {
1144 $(".phrase").val("abandon abandon ability");
1145 $(".phrase").trigger("input");
1148 waitForGenerate(function() {
1149 page
.evaluate(function() {
1150 $("#bip32-tab a").click();
1152 // set the derivation path to m/0'
1153 waitForGenerate(function() {
1154 page
.evaluate(function() {
1155 $("#bip32 .path").val("m/0'");
1156 $("#bip32 .path").trigger("input");
1158 // check the address is generated correctly
1159 waitForGenerate(function() {
1160 var actual
= page
.evaluate(function() {
1161 return $(".address:first").text();
1163 if (actual
!= expected
) {
1164 console
.log("Hardened BIP32 path generates incorrect address");
1165 console
.log("Expected: " + expected
);
1166 console
.log("Actual: " + actual
);
1176 // BIP32 extended private key is shown
1178 page
.open(url
, function(status
) {
1180 var expected
= "xprv9va99uTVE5aLiutUVLTyfxfe8v8aaXjSQ1XxZbK6SezYVuikA9MnjQVTA8rQHpNA5LKvyQBpLiHbBQiiccKiBDs7eRmBogsvq3THFeLHYbe";
1181 page
.evaluate(function() {
1182 $(".phrase").val("abandon abandon ability");
1183 $(".phrase").trigger("input");
1186 waitForGenerate(function() {
1187 page
.evaluate(function() {
1188 $("#bip32-tab a").click();
1190 // check the extended private key is generated correctly
1191 waitForGenerate(function() {
1192 var actual
= page
.evaluate(function() {
1193 return $(".extended-priv-key").val();
1195 if (actual
!= expected
) {
1196 console
.log("BIP32 extended private key is incorrect");
1197 console
.log("Expected: " + expected
);
1198 console
.log("Actual: " + actual
);
1207 // BIP32 extended public key is shown
1209 page
.open(url
, function(status
) {
1211 var expected
= "xpub69ZVZQzP4T8dwPxwbMzz36cNgwy4yzTHmETZMyihzzXXNi3thgg3HCow1RtY252wdw5rS8369xKnraN5Q93y3FkFfJp2XEHWUrkyXsjS93P";
1212 page
.evaluate(function() {
1213 $(".phrase").val("abandon abandon ability");
1214 $(".phrase").trigger("input");
1217 waitForGenerate(function() {
1218 page
.evaluate(function() {
1219 $("#bip32-tab a").click();
1221 // check the extended public key is generated correctly
1222 waitForGenerate(function() {
1223 var actual
= page
.evaluate(function() {
1224 return $(".extended-pub-key").val();
1226 if (actual
!= expected
) {
1227 console
.log("BIP32 extended public key is incorrect");
1228 console
.log("Expected: " + expected
);
1229 console
.log("Actual: " + actual
);
1238 // Derivation path is shown in table
1240 page
.open(url
, function(status
) {
1242 var expected
= "m/44'/0'/0'/0/0";
1243 page
.evaluate(function() {
1244 $(".phrase").val("abandon abandon ability");
1245 $(".phrase").trigger("input");
1247 // check for derivation path in table
1248 waitForGenerate(function() {
1249 var actual
= page
.evaluate(function() {
1250 return $(".index:first").text();
1252 if (actual
!= expected
) {
1253 console
.log("Derivation path shown incorrectly in table");
1254 console
.log("Expected: " + expected
);
1255 console
.log("Actual: " + actual
);
1263 // Derivation path for address can be hardened
1265 page
.open(url
, function(status
) {
1267 var expected
= "18exLzUv7kfpiXRzmCjFDoC9qwNLFyvwyd";
1268 page
.evaluate(function() {
1269 $(".phrase").val("abandon abandon ability");
1270 $(".phrase").trigger("input");
1273 waitForGenerate(function() {
1274 page
.evaluate(function() {
1275 $("#bip32-tab a").click();
1277 waitForGenerate(function() {
1278 // select the hardened addresses option
1279 page
.evaluate(function() {
1280 $(".hardened-addresses").prop("checked", true);
1281 $(".hardened-addresses").trigger("change");
1283 waitForGenerate(function() {
1284 // check the generated address is hardened
1285 var actual
= page
.evaluate(function() {
1286 return $(".address:first").text();
1288 if (actual
!= expected
) {
1289 console
.log("Hardened address is incorrect");
1290 console
.log("Expected: " + expected
);
1291 console
.log("Actual: " + actual
);
1301 // Derivation path visibility can be toggled
1303 page
.open(url
, function(status
) {
1305 page
.evaluate(function() {
1306 $(".phrase").val("abandon abandon ability");
1307 $(".phrase").trigger("input");
1309 waitForGenerate(function() {
1310 // toggle path visibility
1311 page
.evaluate(function() {
1312 $(".index-toggle").click();
1314 // check the path is not visible
1315 var isInvisible
= page
.evaluate(function() {
1316 return $(".index:first span").hasClass("invisible");
1319 console
.log("Toggled derivation path is visible");
1329 page
.open(url
, function(status
) {
1330 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1332 page
.evaluate(function() {
1333 $(".phrase").val("abandon abandon ability").trigger("input");
1336 waitForGenerate(function() {
1337 var actual
= page
.evaluate(function() {
1338 return $(".address:first").text();
1340 if (actual
!= expected
) {
1341 console
.log("Address is not shown");
1342 console
.log("Expected: " + expected
);
1343 console
.log("Got: " + actual
);
1351 // Addresses are shown in order of derivation path
1353 page
.open(url
, function(status
) {
1355 page
.evaluate(function() {
1356 $(".phrase").val("abandon abandon ability").trigger("input");
1358 // get the derivation paths
1359 waitForGenerate(function() {
1360 var paths
= page
.evaluate(function() {
1361 return $(".index").map(function(i
, e
) {
1365 if (paths
.length
!= 20) {
1366 console
.log("Total paths is less than expected: " + paths
.length
);
1369 for (var i
=0; i
<paths
.length
; i
++) {
1370 var expected
= "m/44'/0'/0'/0/" + i
;
1371 var actual
= paths
[i
];
1372 if (actual
!= expected
) {
1373 console
.log("Path " + i
+ " is incorrect");
1374 console
.log("Expected: " + expected
);
1375 console
.log("Actual: " + actual
);
1384 // Address visibility can be toggled
1386 page
.open(url
, function(status
) {
1388 page
.evaluate(function() {
1389 $(".phrase").val("abandon abandon ability");
1390 $(".phrase").trigger("input");
1392 waitForGenerate(function() {
1393 // toggle address visibility
1394 page
.evaluate(function() {
1395 $(".address-toggle").click();
1397 // check the address is not visible
1398 var isInvisible
= page
.evaluate(function() {
1399 return $(".address:first span").hasClass("invisible");
1402 console
.log("Toggled address is visible");
1410 // Public key is shown
1412 page
.open(url
, function(status
) {
1413 var expected
= "033f5aed5f6cfbafaf223188095b5980814897295f723815fea5d3f4b648d0d0b3";
1415 page
.evaluate(function() {
1416 $(".phrase").val("abandon abandon ability").trigger("input");
1419 waitForGenerate(function() {
1420 var actual
= page
.evaluate(function() {
1421 return $(".pubkey:first").text();
1423 if (actual
!= expected
) {
1424 console
.log("Public key is not shown");
1425 console
.log("Expected: " + expected
);
1426 console
.log("Got: " + actual
);
1434 // Public key visibility can be toggled
1436 page
.open(url
, function(status
) {
1438 page
.evaluate(function() {
1439 $(".phrase").val("abandon abandon ability");
1440 $(".phrase").trigger("input");
1442 waitForGenerate(function() {
1443 // toggle public key visibility
1444 page
.evaluate(function() {
1445 $(".public-key-toggle").click();
1447 // check the public key is not visible
1448 var isInvisible
= page
.evaluate(function() {
1449 return $(".pubkey:first span").hasClass("invisible");
1452 console
.log("Toggled public key is visible");
1460 // Private key is shown
1462 page
.open(url
, function(status
) {
1463 var expected
= "L26cVSpWFkJ6aQkPkKmTzLqTdLJ923e6CzrVh9cmx21QHsoUmrEE";
1465 page
.evaluate(function() {
1466 $(".phrase").val("abandon abandon ability").trigger("input");
1469 waitForGenerate(function() {
1470 var actual
= page
.evaluate(function() {
1471 return $(".privkey:first").text();
1473 if (actual
!= expected
) {
1474 console
.log("Private key is not shown");
1475 console
.log("Expected: " + expected
);
1476 console
.log("Got: " + actual
);
1484 // Private key visibility can be toggled
1486 page
.open(url
, function(status
) {
1488 page
.evaluate(function() {
1489 $(".phrase").val("abandon abandon ability");
1490 $(".phrase").trigger("input");
1492 waitForGenerate(function() {
1493 // toggle private key visibility
1494 page
.evaluate(function() {
1495 $(".private-key-toggle").click();
1497 // check the private key is not visible
1498 var isInvisible
= page
.evaluate(function() {
1499 return $(".privkey:first span").hasClass("invisible");
1502 console
.log("Toggled private key is visible");
1510 // More addresses can be generated
1512 page
.open(url
, function(status
) {
1514 page
.evaluate(function() {
1515 $(".phrase").val("abandon abandon ability");
1516 $(".phrase").trigger("input");
1518 waitForGenerate(function() {
1519 // generate more addresses
1520 page
.evaluate(function() {
1523 waitForGenerate(function() {
1524 // check there are more addresses
1525 var addressCount
= page
.evaluate(function() {
1526 return $(".address").length
;
1528 if (addressCount
!= 40) {
1529 console
.log("More addresses cannot be generated");
1538 // A custom number of additional addresses can be generated
1540 page
.open(url
, function(status
) {
1542 page
.evaluate(function() {
1543 $(".phrase").val("abandon abandon ability");
1544 $(".phrase").trigger("input");
1546 waitForGenerate(function() {
1547 // get the current number of addresses
1548 var oldAddressCount
= page
.evaluate(function() {
1549 return $(".address").length
;
1551 // set a custom number of additional addresses
1552 page
.evaluate(function() {
1553 $(".rows-to-add").val(1);
1555 // generate more addresses
1556 page
.evaluate(function() {
1559 waitForGenerate(function() {
1560 // check there are the correct number of addresses
1561 var newAddressCount
= page
.evaluate(function() {
1562 return $(".address").length
;
1564 if (newAddressCount
- oldAddressCount
!= 1) {
1565 console
.log("Number of additional addresses cannot be customized");
1566 console
.log(newAddressCount
)
1567 console
.log(oldAddressCount
)
1576 // Additional addresses are shown in order of derivation path
1578 page
.open(url
, function(status
) {
1580 page
.evaluate(function() {
1581 $(".phrase").val("abandon abandon ability").trigger("input");
1583 waitForGenerate(function() {
1584 // generate more addresses
1585 page
.evaluate(function() {
1588 // get the derivation paths
1589 waitForGenerate(function() {
1590 var paths
= page
.evaluate(function() {
1591 return $(".index").map(function(i
, e
) {
1595 if (paths
.length
!= 40) {
1596 console
.log("Total additional paths is less than expected: " + paths
.length
);
1599 for (var i
=0; i
<paths
.length
; i
++) {
1600 var expected
= "m/44'/0'/0'/0/" + i
;
1601 var actual
= paths
[i
];
1602 if (actual
!= expected
) {
1603 console
.log("Path " + i
+ " is not in correct order");
1604 console
.log("Expected: " + expected
);
1605 console
.log("Actual: " + actual
);
1615 // BIP32 root key can be set by the user
1617 page
.open(url
, function(status
) {
1618 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
1620 page
.evaluate(function() {
1621 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1623 waitForGenerate(function() {
1624 var actual
= page
.evaluate(function() {
1625 return $(".address:first").text();
1627 if (actual
!= expected
) {
1628 console
.log("Setting BIP32 root key results in wrong address");
1629 console
.log("Expected: " + expected
);
1630 console
.log("Actual: " + actual
);
1638 // Setting BIP32 root key clears the existing phrase, passphrase and seed
1640 page
.open(url
, function(status
) {
1643 page
.evaluate(function() {
1644 $(".phrase").val("A non-blank but invalid value");
1646 // Accept any confirm dialogs
1647 page
.onConfirm = function() {
1651 page
.evaluate(function() {
1652 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1654 waitForGenerate(function() {
1655 var actual
= page
.evaluate(function() {
1656 return $(".phrase").val();
1658 if (actual
!= expected
) {
1659 console
.log("Phrase not cleared when setting BIP32 root key");
1660 console
.log("Expected: " + expected
);
1661 console
.log("Actual: " + actual
);
1669 // Clearing of phrase, passphrase and seed can be cancelled by user
1671 page
.open(url
, function(status
) {
1672 var expected
= "abandon abandon ability";
1674 page
.evaluate(function() {
1675 $(".phrase").val("abandon abandon ability");
1677 // Cancel any confirm dialogs
1678 page
.onConfirm = function() {
1682 page
.evaluate(function() {
1683 $(".root-key").val("xprv9s21ZrQH143K3d3vzEDD3KpSKmxsZ3y7CqhAL1tinwtP6wqK4TKEKjpBuo6P2hUhB6ZENo7TTSRytiP857hBZVpBdk8PooFuRspE1eywwNZ").trigger("input");
1685 var actual
= page
.evaluate(function() {
1686 return $(".phrase").val();
1688 if (actual
!= expected
) {
1689 console
.log("Phrase not retained when cancelling changes to BIP32 root key");
1690 console
.log("Expected: " + expected
);
1691 console
.log("Actual: " + actual
);
1698 // Custom BIP32 root key is used when changing the derivation path
1700 page
.open(url
, function(status
) {
1701 var expected
= "1Nq2Wmu726XHCuGhctEtGmhxo3wzk5wZ1H";
1703 page
.evaluate(function() {
1704 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
1706 waitForGenerate(function() {
1707 // change the derivation path
1708 page
.evaluate(function() {
1709 $("#account").val("1").trigger("input");
1711 // check the bip32 root key is used for derivation, not the blank phrase
1712 waitForGenerate(function() {
1713 var actual
= page
.evaluate(function() {
1714 return $(".address:first").text();
1716 if (actual
!= expected
) {
1717 console
.log("Changing the derivation path does not use BIP32 root key");
1718 console
.log("Expected: " + expected
);
1719 console
.log("Actual: " + actual
);
1728 // Incorrect mnemonic shows error
1730 page
.open(url
, function(status
) {
1732 page
.evaluate(function() {
1733 $(".phrase").val("abandon abandon abandon").trigger("input");
1735 waitForFeedback(function() {
1736 // check there is an error shown
1737 var feedback
= page
.evaluate(function() {
1738 return $(".feedback").text();
1740 if (feedback
.length
<= 0) {
1741 console
.log("Invalid mnemonic does not show error");
1749 // Incorrect word shows suggested replacement
1751 page
.open(url
, function(status
) {
1753 page
.evaluate(function() {
1754 $(".phrase").val("abandon abandon abiliti").trigger("input");
1756 // check there is a suggestion shown
1757 waitForFeedback(function() {
1758 var feedback
= page
.evaluate(function() {
1759 return $(".feedback").text();
1761 if (feedback
.indexOf("did you mean ability?") < 0) {
1762 console
.log("Incorrect word does not show suggested replacement");
1763 console
.log("Error: " + error
);
1771 // Github pull request 48
1772 // First four letters of word shows that word, not closest
1773 // since first four letters gives unique word in BIP39 wordlist
1774 // eg ille should show illegal, not idle
1776 page
.open(url
, function(status
) {
1777 // set the incomplete word
1778 page
.evaluate(function() {
1779 $(".phrase").val("ille").trigger("input");
1781 // check there is a suggestion shown
1782 waitForFeedback(function() {
1783 var feedback
= page
.evaluate(function() {
1784 return $(".feedback").text();
1786 if (feedback
.indexOf("did you mean illegal?") < 0) {
1787 console
.log("Start of word does not show correct suggestion");
1788 console
.log("Error: " + error
);
1796 // Incorrect BIP32 root key shows error
1798 page
.open(url
, function(status
) {
1800 page
.evaluate(function() {
1801 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpj").trigger("input");
1803 // check there is an error shown
1804 waitForFeedback(function() {
1805 var feedback
= page
.evaluate(function() {
1806 return $(".feedback").text();
1808 if (feedback
!= "Invalid root key") {
1809 console
.log("Invalid root key does not show error");
1810 console
.log("Error: " + error
);
1818 // Derivation path not starting with m shows error
1820 page
.open(url
, function(status
) {
1821 // set the mnemonic phrase
1822 page
.evaluate(function() {
1823 $(".phrase").val("abandon abandon ability").trigger("input");
1825 waitForGenerate(function() {
1826 // select the bip32 tab so custom derivation path can be set
1827 page
.evaluate(function() {
1828 $("#bip32-tab a").click();
1830 waitForGenerate(function() {
1831 // set the incorrect derivation path
1832 page
.evaluate(function() {
1833 $("#bip32 .path").val("n/0").trigger("input");
1835 waitForFeedback(function() {
1836 var feedback
= page
.evaluate(function() {
1837 return $(".feedback").text();
1839 if (feedback
!= "First character must be 'm'") {
1840 console
.log("Derivation path not starting with m should show error");
1841 console
.log("Error: " + error
);
1851 // Derivation path containing invalid characters shows useful error
1853 page
.open(url
, function(status
) {
1854 // set the mnemonic phrase
1855 page
.evaluate(function() {
1856 $(".phrase").val("abandon abandon ability").trigger("input");
1858 waitForGenerate(function() {
1859 // select the bip32 tab so custom derivation path can be set
1860 page
.evaluate(function() {
1861 $("#bip32-tab a").click();
1863 waitForGenerate(function() {
1864 // set the incorrect derivation path
1865 page
.evaluate(function() {
1866 $("#bip32 .path").val("m/1/0wrong1/1").trigger("input");
1868 waitForFeedback(function() {
1869 var feedback
= page
.evaluate(function() {
1870 return $(".feedback").text();
1872 if (feedback
!= "Invalid characters 0wrong1 found at depth 2") {
1873 console
.log("Derivation path with invalid characters should show error");
1874 console
.log("Error: " + error
);
1884 // Github Issue 11: Default word length is 15
1885 // https://github.com/iancoleman/bip39/issues/11
1887 page
.open(url
, function(status
) {
1888 // get the word length
1889 var defaultLength
= page
.evaluate(function() {
1890 return $(".strength").val();
1892 if (defaultLength
!= 15) {
1893 console
.log("Default word length is not 15");
1901 // Github Issue 12: Generate more rows with private keys hidden
1902 // https://github.com/iancoleman/bip39/issues/12
1904 page
.open(url
, function(status
) {
1906 page
.evaluate(function() {
1907 $(".phrase").val("abandon abandon ability");
1908 $(".phrase").trigger("input");
1910 waitForGenerate(function() {
1911 // toggle private keys hidden, then generate more addresses
1912 page
.evaluate(function() {
1913 $(".private-key-toggle").click();
1916 waitForGenerate(function() {
1917 // check more have been generated
1919 var numPrivKeys
= page
.evaluate(function() {
1920 return $(".privkey").length
;
1922 if (numPrivKeys
!= expected
) {
1923 console
.log("Wrong number of addresses when clicking 'more' with hidden privkeys");
1924 console
.log("Expected: " + expected
);
1925 console
.log("Actual: " + numPrivKeys
);
1928 // check no private keys are shown
1929 var numHiddenPrivKeys
= page
.evaluate(function() {
1930 return $(".privkey span[class=invisible]").length
;
1932 if (numHiddenPrivKeys
!= expected
) {
1933 console
.log("Generating more does not retain hidden state of privkeys");
1934 console
.log("Expected: " + expected
);
1935 console
.log("Actual: " + numHiddenPrivKeys
);
1944 // Github Issue 19: Mnemonic is not sensitive to whitespace
1945 // https://github.com/iancoleman/bip39/issues/19
1947 page
.open(url
, function(status
) {
1949 var expected
= "xprv9s21ZrQH143K3isaZsWbKVoTtbvd34Y1ZGRugGdMeBGbM3AgBVzTH159mj1cbbtYSJtQr65w6L5xy5L9SFC7c9VJZWHxgAzpj4mun5LhrbC";
1950 page
.evaluate(function() {
1951 var doubleSpace
= " ";
1952 $(".phrase").val("urge cat" + doubleSpace
+ "bid");
1953 $(".phrase").trigger("input");
1955 waitForGenerate(function() {
1956 // Check the bip32 root key is correct
1957 var actual
= page
.evaluate(function() {
1958 return $(".root-key").val();
1960 if (actual
!= expected
) {
1961 console
.log("Mnemonic is sensitive to whitespace");
1962 console
.log("Expected: " + expected
);
1963 console
.log("Actual: " + actual
);
1971 // Github Issue 23: Part 1: Use correct derivation path when changing tabs
1972 // https://github.com/iancoleman/bip39/issues/23
1974 page
.open(url
, function(status
) {
1975 // 1) and 2) set the phrase
1976 page
.evaluate(function() {
1977 $(".phrase").val("abandon abandon ability").trigger("input");
1979 waitForGenerate(function() {
1980 // 3) select bip32 tab
1981 page
.evaluate(function() {
1982 $("#bip32-tab a").click();
1984 waitForGenerate(function() {
1985 // 4) switch from bitcoin to litecoin
1986 page
.evaluate(function() {
1987 $(".network option").filter(function() {
1988 return $(this).html() == "Litecoin";
1989 }).prop("selected", true);
1990 $(".network").trigger("change");
1992 waitForGenerate(function() {
1993 // 5) Check derivation path is displayed correctly
1994 var expected
= "m/0/0";
1995 var actual
= page
.evaluate(function() {
1996 return $(".index:first").text();
1998 if (actual
!= expected
) {
1999 console
.log("Github Issue 23 Part 1: derivation path display error");
2000 console
.log("Expected: " + expected
);
2001 console
.log("Actual: " + actual
);
2004 // 5) Check address is displayed correctly
2005 var expected
= "LS8MP5LZ5AdzSZveRrjm3aYVoPgnfFh5T5";
2006 var actual
= page
.evaluate(function() {
2007 return $(".address:first").text();
2009 if (actual
!= expected
) {
2010 console
.log("Github Issue 23 Part 1: address display error");
2011 console
.log("Expected: " + expected
);
2012 console
.log("Actual: " + actual
);
2022 // Github Issue 23 Part 2: Coin selection in derivation path
2023 // https://github.com/iancoleman/bip39/issues/23#issuecomment-238011920
2025 page
.open(url
, function(status
) {
2027 page
.evaluate(function() {
2028 $(".phrase").val("abandon abandon ability").trigger("input");
2030 waitForGenerate(function() {
2031 // switch from bitcoin to clam
2032 page
.evaluate(function() {
2033 $(".network option").filter(function() {
2034 return $(this).html() == "CLAM";
2035 }).prop("selected", true);
2036 $(".network").trigger("change");
2038 waitForGenerate(function() {
2039 // check derivation path is displayed correctly
2040 var expected
= "m/44'/23'/0'/0/0";
2041 var actual
= page
.evaluate(function() {
2042 return $(".index:first").text();
2044 if (actual
!= expected
) {
2045 console
.log("Github Issue 23 Part 2: Coin in BIP44 derivation path is incorrect");
2046 console
.log("Expected: " + expected
);
2047 console
.log("Actual: " + actual
);
2056 // Github Issue 26: When using a Root key derrived altcoins are incorrect
2057 // https://github.com/iancoleman/bip39/issues/26
2059 page
.open(url
, function(status
) {
2060 // 1) 2) and 3) set the root key
2061 page
.evaluate(function() {
2062 $(".root-key").val("xprv9s21ZrQH143K2jkGDCeTLgRewT9F2pH5JZs2zDmmjXes34geVnFiuNa8KTvY5WoYvdn4Ag6oYRoB6cXtc43NgJAEqDXf51xPm6fhiMCKwpi").trigger("input");
2064 waitForGenerate(function() {
2065 // 4) switch from bitcoin to viacoin
2066 page
.evaluate(function() {
2067 $(".network option").filter(function() {
2068 return $(this).html() == "Viacoin";
2069 }).prop("selected", true);
2070 $(".network").trigger("change");
2072 waitForGenerate(function() {
2073 // 5) ensure the derived address is correct
2074 var expected
= "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
2075 var actual
= page
.evaluate(function() {
2076 return $(".address:first").text();
2078 if (actual
!= expected
) {
2079 console
.log("Github Issue 26: address is incorrect when changing networks and using root-key to derive");
2080 console
.log("Expected: " + expected
);
2081 console
.log("Actual: " + actual
);
2090 // Selecting a language with no existing phrase should generate a phrase in
2093 page
.open(url
, function(status
) {
2094 // Select a language
2095 // Need to manually simulate hash being set due to quirk between
2096 // 'click' event triggered by javascript vs triggered by mouse.
2097 // Perhaps look into page.sendEvent
2098 // http://phantomjs.org/api/webpage/method/send-event.html
2099 page
.evaluate(function() {
2100 window
.location
.hash
= "#japanese";
2101 $("a[href='#japanese']").trigger("click");
2103 waitForGenerate(function() {
2104 // Check the mnemonic is in Japanese
2105 var phrase
= page
.evaluate(function() {
2106 return $(".phrase").val();
2108 if (phrase
.length
<= 0) {
2109 console
.log("No Japanese phrase generated");
2112 if (phrase
.charCodeAt(0) < 128) {
2113 console
.log("First character of Japanese phrase is ascii");
2114 console
.log("Phrase: " + phrase
);
2122 // Selecting a language with existing phrase should update the phrase to use
2125 page
.open(url
, function(status
) {
2126 // Set the phrase to an English phrase.
2127 page
.evaluate(function() {
2128 $(".phrase").val("abandon abandon ability").trigger("input");
2130 waitForGenerate(function() {
2131 // Change to Italian
2132 // Need to manually simulate hash being set due to quirk between
2133 // 'click' event triggered by javascript vs triggered by mouse.
2134 // Perhaps look into page.sendEvent
2135 // http://phantomjs.org/api/webpage/method/send-event.html
2136 page
.evaluate(function() {
2137 window
.location
.hash
= "#italian";
2138 $("a[href='#italian']").trigger("click");
2140 waitForGenerate(function() {
2141 // Check only the language changes, not the phrase
2142 var expected
= "abaco abaco abbaglio";
2143 var actual
= page
.evaluate(function() {
2144 return $(".phrase").val();
2146 if (actual
!= expected
) {
2147 console
.log("Changing language with existing phrase");
2148 console
.log("Expected: " + expected
);
2149 console
.log("Actual: " + actual
);
2152 // Check the address is correct
2153 var expected
= "1Dz5TgDhdki9spa6xbPFbBqv5sjMrx3xgV";
2154 var actual
= page
.evaluate(function() {
2155 return $(".address:first").text();
2157 if (actual
!= expected
) {
2158 console
.log("Changing language generates incorrect address");
2159 console
.log("Expected: " + expected
);
2160 console
.log("Actual: " + actual
);
2169 // Suggested replacement for erroneous word in non-English language
2171 page
.open(url
, function(status
) {
2172 // Set an incorrect phrase in Italian
2173 page
.evaluate(function() {
2174 $(".phrase").val("abaco abaco zbbaglio").trigger("input");
2176 waitForFeedback(function() {
2177 // Check the suggestion is correct
2178 var feedback
= page
.evaluate(function() {
2179 return $(".feedback").text();
2181 if (feedback
.indexOf("did you mean abbaglio?") < 0) {
2182 console
.log("Incorrect Italian word does not show suggested replacement");
2183 console
.log("Error: " + error
);
2192 // Japanese word does not break across lines.
2194 // https://github.com/bitcoin/bips/blob/master/bip-0039/bip-0039-wordlists.md#japanese
2196 page
.open(url
, function(status
) {
2197 hasWordBreakCss
= page
.content
.indexOf("word-break: keep-all;") > -1;
2198 if (!hasWordBreakCss
) {
2199 console
.log("Japanese words can break across lines mid-word");
2200 console
.log("Check CSS for '.phrase { word-break: keep-all; }'");
2203 // Run the next test
2208 // Language can be specified at page load using hash value in url
2210 page
.open(url
, function(status
) {
2211 // Set the page hash as if it were on a fresh page load
2212 page
.evaluate(function() {
2213 window
.location
.hash
= "#japanese";
2215 // Generate a random phrase
2216 page
.evaluate(function() {
2217 $(".generate").trigger("click");
2219 waitForGenerate(function() {
2220 // Check the phrase is in Japanese
2221 var phrase
= page
.evaluate(function() {
2222 return $(".phrase").val();
2224 if (phrase
.length
<= 0) {
2225 console
.log("No phrase generated using url hash");
2228 if (phrase
.charCodeAt(0) < 128) {
2229 console
.log("Language not detected from url hash on page load.");
2230 console
.log("Phrase: " + phrase
);
2238 // Entropy unit tests
2240 page
.open(url
, function(status
) {
2241 var response
= page
.evaluate(function() {
2243 // binary entropy is detected
2245 e
= Entropy
.fromString("01010101");
2246 if (e
.base
.str
!= "binary") {
2247 return "Binary entropy not detected correctly";
2253 // base6 entropy is detected
2255 e
= Entropy
.fromString("012345012345");
2256 if (e
.base
.str
!= "base 6") {
2257 return "base6 entropy not detected correctly";
2263 // dice entropy is detected
2265 e
= Entropy
.fromString("123456123456");
2266 if (e
.base
.str
!= "base 6 (dice)") {
2267 return "dice entropy not detected correctly";
2273 // base10 entropy is detected
2275 e
= Entropy
.fromString("0123456789");
2276 if (e
.base
.str
!= "base 10") {
2277 return "base10 entropy not detected correctly";
2283 // hex entropy is detected
2285 e
= Entropy
.fromString("0123456789ABCDEF");
2286 if (e
.base
.str
!= "hexadecimal") {
2287 return "hexadecimal entropy not detected correctly";
2293 // card entropy is detected
2295 e
= Entropy
.fromString("AC4DTHKS");
2296 if (e
.base
.str
!= "card") {
2297 return "card entropy not detected correctly";
2303 // entropy is case insensitive
2305 e
= Entropy
.fromString("aBcDeF");
2306 if (e
.cleanStr
!= "aBcDeF") {
2307 return "Entropy should not be case sensitive";
2313 // dice entropy is converted to base6
2315 e
= Entropy
.fromString("123456");
2316 if (e
.cleanStr
!= "123450") {
2317 return "Dice entropy is not automatically converted to base6";
2323 // dice entropy is preferred to base6 if ambiguous
2325 e
= Entropy
.fromString("12345");
2326 if (e
.base
.str
!= "base 6 (dice)") {
2327 return "dice not used as default over base 6";
2333 // unused characters are ignored
2335 e
= Entropy
.fromString("fghijkl");
2336 if (e
.cleanStr
!= "f") {
2337 return "additional characters are not ignored";
2343 // the lowest base is used by default
2344 // 7 could be decimal or hexadecimal, but should be detected as decimal
2346 e
= Entropy
.fromString("7");
2347 if (e
.base
.str
!= "base 10") {
2348 return "lowest base is not used";
2354 // Leading zeros are retained
2356 e
= Entropy
.fromString("000A");
2357 if (e
.cleanStr
!= "000A") {
2358 return "Leading zeros are not retained";
2364 // Leading zeros are correctly preserved for hex in binary string
2366 e
= Entropy
.fromString("2A");
2367 if (e
.binaryStr
!= "00101010") {
2368 return "Hex leading zeros are not correct in binary";
2374 // Leading zeros for base 6 as binary string
2375 // 20 = 2 events at 2.58 bits per event = 5 bits
2376 // 20 in base 6 = 12 in base 10 = 1100 in base 2
2377 // so it needs 1 bit of padding to be the right bit length
2379 e
= Entropy
.fromString("20");
2380 if (e
.binaryStr
!= "01100") {
2381 return "Base 6 as binary has leading zeros";
2387 // Leading zeros for base 10 as binary string
2389 e
= Entropy
.fromString("17");
2390 if (e
.binaryStr
!= "010001") {
2391 return "Base 10 as binary has leading zeros";
2397 // Leading zeros for card entropy as binary string.
2398 // Card entropy is hashed so 2c does not necessarily produce leading zeros.
2400 e
= Entropy
.fromString("2c");
2401 if (e
.binaryStr
!= "0010") {
2402 return "Card entropy as binary has leading zeros";
2408 // Keyboard mashing results in weak entropy
2409 // Despite being a long string, it's less than 30 bits of entropy
2411 e
= Entropy
.fromString("aj;se ifj; ask,dfv js;ifj");
2412 if (e
.binaryStr
.length
>= 30) {
2413 return "Keyboard mashing should produce weak entropy";
2419 // Card entropy is used if every pair could be a card
2421 e
= Entropy
.fromString("4c3c2c");
2422 if (e
.base
.str
!= "card") {
2423 return "Card entropy not used if all pairs are cards";
2429 // Card entropy uses base 52
2430 // [ cards, binary ]
2434 [ "acqs", "11011100" ],
2435 [ "acks", "01011100" ],
2436 [ "2cac", "11111000" ],
2449 [ "ks2c", "01010100" ],
2450 [ "KS2C", "01010100" ],
2452 for (var i
=0; i
<cards
.length
; i
++) {
2453 var card
= cards
[i
][0];
2454 var result
= cards
[i
][1];
2455 e
= Entropy
.fromString(card
);
2456 console
.log(e
.binary
+ " " + result
);
2457 if (e
.binaryStr
!== result
) {
2458 return "card entropy " + card
+ " not parsed correctly: " + result
+ " != " + e
.binaryStr
;
2467 if (response
!= "PASS") {
2468 console
.log("Entropy unit tests");
2469 console
.log(response
);
2476 // Entropy can be entered by the user
2478 page
.open(url
, function(status
) {
2480 mnemonic: "abandon abandon ability",
2481 address: "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug",
2484 page
.evaluate(function() {
2485 $(".use-entropy").prop("checked", true).trigger("change");
2486 $(".entropy").val("00000000 00000000 00000000 00000000").trigger("input");
2488 // check the mnemonic is set and address is correct
2489 waitForGenerate(function() {
2490 var actual
= page
.evaluate(function() {
2492 address: $(".address:first").text(),
2493 mnemonic: $(".phrase").val(),
2496 if (actual
.mnemonic
!= expected
.mnemonic
) {
2497 console
.log("Entropy does not generate correct mnemonic");
2498 console
.log("Expected: " + expected
.mnemonic
);
2499 console
.log("Got: " + actual
.mnemonic
);
2502 if (actual
.address
!= expected
.address
) {
2503 console
.log("Entropy does not generate correct address");
2504 console
.log("Expected: " + expected
.address
);
2505 console
.log("Got: " + actual
.address
);
2513 // A warning about entropy is shown to the user, with additional information
2515 page
.open(url
, function(status
) {
2516 // get text content from entropy sections of page
2517 var hasWarning
= page
.evaluate(function() {
2518 var entropyText
= $(".entropy-container").text();
2519 var warning
= "mnemonic may be insecure";
2520 if (entropyText
.indexOf(warning
) == -1) {
2523 var readMoreText
= $("#entropy-notes").parent().text();
2524 var goodSources
= "flipping a fair coin, rolling a fair dice, noise measurements etc";
2525 if (readMoreText
.indexOf(goodSources
) == -1) {
2530 // check the warnings and information are shown
2532 console
.log("Page does not contain warning about using own entropy");
2539 // The types of entropy available are described to the user
2541 page
.open(url
, function(status
) {
2542 // get placeholder text for entropy field
2543 var placeholder
= page
.evaluate(function() {
2544 return $(".entropy").attr("placeholder");
2554 for (var i
=0; i
<options
.length
; i
++) {
2555 var option
= options
[i
];
2556 if (placeholder
.indexOf(option
) == -1) {
2557 console
.log("Available entropy type is not shown to user: " + option
);
2565 // The actual entropy used is shown to the user
2567 page
.open(url
, function(status
) {
2569 var badEntropySource
= page
.evaluate(function() {
2570 var entropy
= "Not A Very Good Entropy Source At All";
2571 $(".use-entropy").prop("checked", true).trigger("change");
2572 $(".entropy").val(entropy
).trigger("input");
2574 // check the actual entropy being used is shown
2575 waitForEntropyFeedback(function() {
2576 var expectedText
= "AedEceAA";
2577 var entropyText
= page
.evaluate(function() {
2578 return $(".entropy-container").text();
2580 if (entropyText
.indexOf(expectedText
) == -1) {
2581 console
.log("Actual entropy used is not shown");
2589 // Binary entropy can be entered
2591 page
.open(url
, function(status
) {
2593 page
.evaluate(function() {
2594 $(".use-entropy").prop("checked", true).trigger("change");
2595 $(".entropy").val("01").trigger("input");
2597 // check the entropy is shown to be the correct type
2598 waitForEntropyFeedback(function() {
2599 var entropyText
= page
.evaluate(function() {
2600 return $(".entropy-container").text();
2602 if (entropyText
.indexOf("binary") == -1) {
2603 console
.log("Binary entropy is not detected and presented to user");
2611 // Base 6 entropy can be entered
2613 page
.open(url
, function(status
) {
2615 page
.evaluate(function() {
2616 $(".use-entropy").prop("checked", true).trigger("change");
2617 $(".entropy").val("012345").trigger("input");
2619 // check the entropy is shown to be the correct type
2620 waitForEntropyFeedback(function() {
2621 var entropyText
= page
.evaluate(function() {
2622 return $(".entropy-container").text();
2624 if (entropyText
.indexOf("base 6") == -1) {
2625 console
.log("Base 6 entropy is not detected and presented to user");
2633 // Base 6 dice entropy can be entered
2635 page
.open(url
, function(status
) {
2637 page
.evaluate(function() {
2638 $(".use-entropy").prop("checked", true).trigger("change");
2639 $(".entropy").val("123456").trigger("input");
2641 // check the entropy is shown to be the correct type
2642 waitForEntropyFeedback(function() {
2643 var entropyText
= page
.evaluate(function() {
2644 return $(".entropy-container").text();
2646 if (entropyText
.indexOf("dice") == -1) {
2647 console
.log("Dice entropy is not detected and presented to user");
2655 // Base 10 entropy can be entered
2657 page
.open(url
, function(status
) {
2659 page
.evaluate(function() {
2660 $(".use-entropy").prop("checked", true).trigger("change");
2661 $(".entropy").val("789").trigger("input");
2663 // check the entropy is shown to be the correct type
2664 waitForEntropyFeedback(function() {
2665 var entropyText
= page
.evaluate(function() {
2666 return $(".entropy-container").text();
2668 if (entropyText
.indexOf("base 10") == -1) {
2669 console
.log("Base 10 entropy is not detected and presented to user");
2677 // Hexadecimal entropy can be entered
2679 page
.open(url
, function(status
) {
2681 page
.evaluate(function() {
2682 $(".use-entropy").prop("checked", true).trigger("change");
2683 $(".entropy").val("abcdef").trigger("input");
2685 // check the entropy is shown to be the correct type
2686 waitForEntropyFeedback(function() {
2687 var entropyText
= page
.evaluate(function() {
2688 return $(".entropy-container").text();
2690 if (entropyText
.indexOf("hexadecimal") == -1) {
2691 console
.log("Hexadecimal entropy is not detected and presented to user");
2699 // Dice entropy value is shown as the converted base 6 value
2701 page
.open(url
, function(status
) {
2703 page
.evaluate(function() {
2704 $(".use-entropy").prop("checked", true).trigger("change");
2705 $(".entropy").val("123456").trigger("input");
2707 // check the entropy is shown as base 6, not as the original dice value
2708 waitForEntropyFeedback(function() {
2709 var entropyText
= page
.evaluate(function() {
2710 return $(".entropy-container").text();
2712 if (entropyText
.indexOf("123450") == -1) {
2713 console
.log("Dice entropy is not shown to user as base 6 value");
2716 if (entropyText
.indexOf("123456") > -1) {
2717 console
.log("Dice entropy value is shown instead of true base 6 value");
2725 // The number of bits of entropy accumulated is shown
2727 page
.open(url
, function(status
) {
2730 [ "0000 0000 0000 0000 0000", "20" ],
2733 [ "6", "2" ], // 6 in card is 0 in base 6, 0 in base 6 is 2.6 bits (rounded down to 2 bits)
2734 [ "7", "3" ], // 7 in base 10 is 111 in base 2, no leading zeros
2739 [ "1A", "8" ], // hex is always multiple of 4 bits of entropy
2746 [ "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)
2747 [ "2227", "13" ], // Uses base 10, which is 4 lots of 3.32 bits, which is 13.3 bits (rounded down to 13)
2750 [ "0000101017", "33" ], // 10 events at 3.32 bits per event
2751 [ "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
2754 page
.evaluate(function(e
) {
2755 $(".use-entropy").prop("checked", true).trigger("change");
2758 var nextTest
= function runNextTest(i
) {
2759 var entropy
= tests
[i
][0];
2760 var expected
= tests
[i
][1];
2762 page
.evaluate(function(e
) {
2763 $(".entropy").val(e
).trigger("input");
2765 // check the number of bits of entropy is shown
2766 waitForEntropyFeedback(function() {
2767 var entropyText
= page
.evaluate(function() {
2768 return $(".entropy-container").text();
2770 if (entropyText
.replace(/\s/g,"").indexOf("Bits" + expected
) == -1) {
2771 console
.log("Accumulated entropy is not shown correctly for " + entropy
);
2774 var isLastTest
= i
== tests
.length
- 1;
2787 // There is feedback provided about the supplied entropy
2789 page
.open(url
, function(status
) {
2794 type: "hexadecimal",
2798 strength: "extremely weak",
2801 entropy: "AAAAAAAA",
2802 filtered: "AAAAAAAA",
2803 type: "hexadecimal",
2807 strength: "extremely weak",
2810 entropy: "AAAAAAAA B",
2811 filtered: "AAAAAAAAB",
2812 type: "hexadecimal",
2816 strength: "extremely weak",
2819 entropy: "AAAAAAAA BBBBBBBB",
2820 filtered: "AAAAAAAABBBBBBBB",
2821 type: "hexadecimal",
2825 strength: "very weak",
2828 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC",
2829 filtered: "AAAAAAAABBBBBBBBCCCCCCCC",
2830 type: "hexadecimal",
2837 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD",
2838 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD",
2839 type: "hexadecimal",
2843 strength: "easily cracked",
2846 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA",
2847 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDA",
2848 type: "hexadecimal",
2855 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE",
2856 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEE",
2857 type: "hexadecimal",
2861 strength: "very strong",
2864 entropy: "AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDA EEEEEEEE FFFFFFFF",
2865 filtered: "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDAEEEEEEEEFFFFFFFF",
2866 type: "hexadecimal",
2870 strength: "extremely strong",
2878 strength: "extremely weak",
2881 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2882 type: "card (full deck)",
2886 strength: "extremely strong",
2889 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks3d",
2890 type: "card (full deck, 1 duplicate: 3d)",
2894 strength: "extremely strong",
2897 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d",
2898 type: "card (2 duplicates: 3d 4d, 1 missing: KS)",
2902 strength: "extremely strong",
2905 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqs3d4d5d6d",
2906 type: "card (4 duplicates: 3d 4d 5d..., 1 missing: KS)",
2910 strength: "extremely strong",
2912 // Next test was throwing uncaught error in zxcvbn
2913 // Also tests 451 bits, ie Math.log2(52!)*2 = 225.58 * 2
2915 entropy: "ac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsksac2c3c4c5c6c7c8c9ctcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2916 type: "card (full deck, 52 duplicates: ac 2c 3c...)",
2920 strength: "extremely strong",
2922 // Case insensitivity to duplicate cards
2925 type: "card (1 duplicate: AS)",
2929 strength: "extremely weak",
2933 type: "card (1 duplicate: as)",
2937 strength: "extremely weak",
2939 // Missing cards are detected
2941 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d5d6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2942 type: "card (1 missing: 9C)",
2946 strength: "extremely strong",
2949 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjdqdkdah2h3h4h5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2950 type: "card (2 missing: 9C 5D)",
2954 strength: "extremely strong",
2957 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d7d8d9dtdjd kdah2h3h 5h6h7h8h9hthjhqhkhas2s3s4s5s6s7s8s9stsjsqsks",
2958 type: "card (4 missing: 9C 5D QD...)",
2962 strength: "extremely strong",
2964 // More than six missing cards does not show message
2966 entropy: "ac2c3c4c5c6c7c8c tcjcqckcad2d3d4d 6d 8d9d jd kdah2h3h 5h6h7h8h9hthjhqhkh 2s3s4s5s6s7s8s9stsjsqsks",
2971 strength: "extremely strong",
2973 // Multiple decks of cards increases bits per event
2993 entropy: "3d3d3d3d",
2999 entropy: "3d3d3d3d3d",
3005 entropy: "3d3d3d3d3d3d",
3011 entropy: "3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d3d",
3015 strength: 'easily cracked - Repeats like "abcabcabc" are only slightly harder to guess than "abc"',
3019 page
.evaluate(function() {
3020 $(".use-entropy").prop("checked", true).trigger("change");
3022 var nextTest
= function runNextTest(i
) {
3023 function getFeedbackError(expected
, actual
) {
3024 if ("filtered" in expected
&& actual
.indexOf(expected
.filtered
) == -1) {
3025 return "Filtered value not in feedback";
3027 if ("type" in expected
&& actual
.indexOf(expected
.type
) == -1) {
3028 return "Entropy type not in feedback";
3030 if ("events" in expected
&& actual
.indexOf(expected
.events
) == -1) {
3031 return "Event count not in feedback";
3033 if ("bits" in expected
&& actual
.indexOf(expected
.bits
) == -1) {
3034 return "Bit count not in feedback";
3036 if ("strength" in expected
&& actual
.indexOf(expected
.strength
) == -1) {
3037 return "Strength not in feedback";
3039 if ("bitsPerEvent" in expected
&& actual
.indexOf(expected
.bitsPerEvent
) == -1) {
3040 return "bitsPerEvent not in feedback";
3045 page
.evaluate(function(e
) {
3046 $(".addresses").empty();
3047 $(".phrase").val("");
3048 $(".entropy").val(e
).trigger("input");
3050 waitForEntropyFeedback(function() {
3051 var mnemonic
= page
.evaluate(function() {
3052 return $(".phrase").val();
3054 // Check mnemonic length
3055 if ("words" in test
&& test
.words
== 0) {
3056 if (mnemonic
.length
> 0) {
3057 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3058 console
.log("Entropy: " + test
.entropy
);
3059 console
.log("Mnemonic: " + mnemonic
);
3063 else if ("words" in test
) {
3064 if (mnemonic
.split(" ").length
!= test
.words
) {
3065 console
.log("Mnemonic length for " + test
.strength
+ " strength is not " + test
.words
);
3066 console
.log("Entropy: " + test
.entropy
);
3067 console
.log("Mnemonic: " + mnemonic
);
3072 var feedback
= page
.evaluate(function() {
3073 return $(".entropy-container").text();
3075 var feedbackError
= getFeedbackError(test
, feedback
);
3076 if (feedbackError
) {
3077 console
.log("Entropy feedback for " + test
.entropy
+ " returned error");
3078 console
.log(feedbackError
);
3082 var isLastTest
= i
== tests
.length
- 1;
3095 // Entropy is truncated from the left
3097 page
.open(url
, function(status
) {
3098 var expected
= "avocado zoo zone";
3100 page
.evaluate(function() {
3101 $(".use-entropy").prop("checked", true).trigger("change");
3102 var entropy
= "00000000 00000000 00000000 00000000";
3103 entropy
+= "11111111 11111111 11111111 1111"; // Missing last byte
3104 $(".entropy").val(entropy
).trigger("input");
3106 // check the entropy is truncated from the right
3107 waitForGenerate(function() {
3108 var actual
= page
.evaluate(function() {
3109 return $(".phrase").val();
3111 if (actual
!= expected
) {
3112 console
.log("Entropy is not truncated from the right");
3113 console
.log("Expected: " + expected
);
3114 console
.log("Got: " + actual
);
3122 // Very large entropy results in very long mnemonics
3124 page
.open(url
, function(status
) {
3126 page
.evaluate(function() {
3127 $(".use-entropy").prop("checked", true).trigger("change");
3129 // Generate a very long entropy string
3130 for (var i
=0; i
<33; i
++) {
3131 entropy
+= "AAAAAAAA"; // 3 words * 33 iterations = 99 words
3133 $(".entropy").val(entropy
).trigger("input");
3135 // check the mnemonic is very long
3136 waitForGenerate(function() {
3137 var wordCount
= page
.evaluate(function() {
3138 return $(".phrase").val().split(" ").length
;
3140 if (wordCount
!= 99) {
3141 console
.log("Large entropy does not generate long mnemonic");
3142 console
.log("Expected 99 words, got " + wordCount
);
3150 // Is compatible with bip32jp entropy
3151 // https://bip32jp.github.io/english/index.html
3153 // Is incompatible with:
3156 page
.open(url
, function(status
) {
3157 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";
3159 page
.evaluate(function() {
3160 $(".use-entropy").prop("checked", true).trigger("change");
3161 var entropy
= "543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543210543";
3162 $(".entropy").val(entropy
).trigger("input");
3164 // check the mnemonic matches the expected value from bip32jp
3165 waitForGenerate(function() {
3166 var actual
= page
.evaluate(function() {
3167 return $(".phrase").val();
3169 if (actual
!= expected
) {
3170 console
.log("Mnemonic does not match bip32jp for base 6 entropy");
3171 console
.log("Expected: " + expected
);
3172 console
.log("Got: " + actual
);
3180 // Blank entropy does not generate mnemonic or addresses
3182 page
.open(url
, function(status
) {
3184 page
.evaluate(function() {
3185 $(".use-entropy").prop("checked", true).trigger("change");
3186 $(".entropy").val("").trigger("input");
3188 waitForFeedback(function() {
3189 // check there is no mnemonic
3190 var phrase
= page
.evaluate(function() {
3191 return $(".phrase").val();
3194 console
.log("Blank entropy does not result in blank mnemonic");
3195 console
.log("Got: " + phrase
);
3198 // check there are no addresses displayed
3199 var addresses
= page
.evaluate(function() {
3200 return $(".address").length
;
3202 if (addresses
!= 0) {
3203 console
.log("Blank entropy does not result in zero addresses");
3206 // Check the feedback says 'blank entropy'
3207 var feedback
= page
.evaluate(function() {
3208 return $(".feedback").text();
3210 if (feedback
!= "Blank entropy") {
3211 console
.log("Blank entropy does not show feedback message");
3219 // Mnemonic length can be selected even for weak entropy
3221 page
.open(url
, function(status
) {
3223 page
.evaluate(function() {
3224 $(".use-entropy").prop("checked", true).trigger("change");
3225 $(".entropy").val("012345");
3226 $(".mnemonic-length").val("18").trigger("change");
3228 // check the mnemonic is the correct length
3229 waitForGenerate(function() {
3230 var phrase
= page
.evaluate(function() {
3231 return $(".phrase").val();
3233 var numberOfWords
= phrase
.split(/\s/g).length
;
3234 if (numberOfWords
!= 18) {
3235 console
.log("Weak entropy cannot be overridden to give 18 word mnemonic");
3236 console
.log(phrase
);
3245 // https://github.com/iancoleman/bip39/issues/33
3246 // Final cards should contribute entropy
3248 page
.open(url
, function(status
) {
3250 page
.evaluate(function() {
3251 $(".use-entropy").prop("checked", true).trigger("change");
3252 $(".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");
3255 waitForGenerate(function() {
3256 var originalPhrase
= page
.evaluate(function() {
3257 return $(".phrase").val();
3259 // Set the last 12 cards to be AS
3260 page
.evaluate(function() {
3261 $(".addresses").empty();
3262 $(".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");
3264 // get the new mnemonic
3265 waitForGenerate(function() {
3266 var newPhrase
= page
.evaluate(function() {
3267 return $(".phrase").val();
3269 // check the phrase has changed
3270 if (newPhrase
== originalPhrase
) {
3271 console
.log("Changing last 12 cards does not change mnemonic");
3272 console
.log("Original:");
3273 console
.log(originalPhrase
);
3274 console
.log("New:");
3275 console
.log(newPhrase
);
3285 // https://github.com/iancoleman/bip39/issues/35
3288 page
.open(url
, function(status
) {
3290 page
.evaluate(function() {
3291 $(".generate").click();
3293 waitForGenerate(function() {
3294 var p
= page
.evaluate(function() {
3295 // get position of mnemonic element
3296 return $(".phrase").offset();
3298 p
.top
= Math
.ceil(p
.top
);
3299 p
.left
= Math
.ceil(p
.left
);
3300 // check the qr code shows
3301 page
.sendEvent("mousemove", p
.left
+4, p
.top
+4);
3302 var qrShowing
= page
.evaluate(function() {
3303 return $(".qr-container").find("canvas").length
> 0;
3306 console
.log("QR Code does not show");
3309 // check the qr code hides
3310 page
.sendEvent("mousemove", p
.left
-4, p
.top
-4);
3311 var qrHidden
= page
.evaluate(function() {
3312 return $(".qr-container").find("canvas").length
== 0;
3315 console
.log("QR Code does not hide");
3323 // BIP44 account extendend private key is shown
3324 // github issue 37 - compatibility with electrum
3326 page
.open(url
, function(status
) {
3328 var expected
= "xprv9yzrnt4zWVJUr1k2VxSPy9ettKz5PpeDMgaVG7UKedhqnw1tDkxP2UyYNhuNSumk2sLE5ctwKZs9vwjsq3e1vo9egCK6CzP87H2cVYXpfwQ";
3329 page
.evaluate(function() {
3330 $(".phrase").val("abandon abandon ability");
3331 $(".phrase").trigger("input");
3333 // check the BIP44 account extended private key
3334 waitForGenerate(function() {
3335 var actual
= page
.evaluate(function() {
3336 return $(".account-xprv").val();
3338 if (actual
!= expected
) {
3339 console
.log("BIP44 account extended private key is incorrect");
3340 console
.log("Expected: " + expected
);
3341 console
.log("Actual: " + actual
);
3349 // BIP44 account extendend public key is shown
3350 // github issue 37 - compatibility with electrum
3352 page
.open(url
, function(status
) {
3354 var expected
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3355 page
.evaluate(function() {
3356 $(".phrase").val("abandon abandon ability");
3357 $(".phrase").trigger("input");
3359 // check the BIP44 account extended public key
3360 waitForGenerate(function() {
3361 var actual
= page
.evaluate(function() {
3362 return $(".account-xpub").val();
3364 if (actual
!= expected
) {
3365 console
.log("BIP44 account extended public key is incorrect");
3366 console
.log("Expected: " + expected
);
3367 console
.log("Actual: " + actual
);
3376 // BIP32 root key can be set as an xpub
3378 page
.open(url
, function(status
) {
3380 page
.evaluate(function() {
3381 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3382 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3383 $("#root-key").val(bip44AccountXpub
);
3384 $("#root-key").trigger("input");
3386 waitForFeedback(function() {
3387 page
.evaluate(function() {
3389 $("#bip32-tab a").click();
3391 waitForGenerate(function() {
3392 page
.evaluate(function() {
3393 // derive external addresses for this xpub
3394 var firstAccountDerivationPath
= "m/0";
3395 $("#bip32-path").val(firstAccountDerivationPath
);
3396 $("#bip32-path").trigger("input");
3398 waitForGenerate(function() {
3399 // check the addresses are generated
3400 var expected
= "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3401 var actual
= page
.evaluate(function() {
3402 return $(".address:first").text();
3404 if (actual
!= expected
) {
3405 console
.log("xpub key does not generate addresses in table");
3406 console
.log("Expected: " + expected
);
3407 console
.log("Actual: " + actual
);
3410 // check the xprv key is not set
3411 var expected
= "NA";
3412 var actual
= page
.evaluate(function() {
3413 return $(".extended-priv-key").val();
3415 if (actual
!= expected
) {
3416 console
.log("xpub key as root shows derived bip32 xprv key");
3417 console
.log("Expected: " + expected
);
3418 console
.log("Actual: " + actual
);
3421 // check the private key is not set
3422 var expected
= "NA";
3423 var actual
= page
.evaluate(function() {
3424 return $(".privkey:first").text();
3426 if (actual
!= expected
) {
3427 console
.log("xpub key generates private key in addresses table");
3428 console
.log("Expected: " + expected
);
3429 console
.log("Actual: " + actual
);
3440 // xpub for bip32 root key will not work with hardened derivation paths
3442 page
.open(url
, function(status
) {
3444 page
.evaluate(function() {
3445 // set xpub for account 0 of bip44 for 'abandon abandon ability'
3446 var bip44AccountXpub
= "xpub6CzDCPbtLrrn4VpVbyyQLHbdSMpZoHN4iuW64VswCyEpfjM2mJGdaHJ2DyuZwtst96E16VvcERb8BBeJdHSCVmAq9RhtRQg6eAZFrTKCNqf";
3447 $("#root-key").val(bip44AccountXpub
);
3448 $("#root-key").trigger("input");
3450 waitForFeedback(function() {
3451 // Check feedback is correct
3452 var expected
= "Hardened derivation path is invalid with xpub key";
3453 var actual
= page
.evaluate(function() {
3454 return $(".feedback").text();
3456 if (actual
!= expected
) {
3457 console
.log("xpub key with hardened derivation path does not show feedback");
3458 console
.log("Expected: " + expected
);
3459 console
.log("Actual: " + actual
);
3462 // Check no addresses are shown
3464 var actual
= page
.evaluate(function() {
3465 return $(".addresses tr").length
;
3467 if (actual
!= expected
) {
3468 console
.log("addresses still show after setting xpub key with hardened derivation path");
3469 console
.log("Expected: " + expected
);
3470 console
.log("Actual: " + actual
);
3479 // no root key shows feedback
3481 page
.open(url
, function(status
) {
3482 // click the bip32 tab on fresh page
3483 page
.evaluate(function() {
3484 $("#bip32-tab a").click();
3486 waitForFeedback(function() {
3487 // Check feedback is correct
3488 var expected
= "No root key";
3489 var actual
= page
.evaluate(function() {
3490 return $(".feedback").text();
3492 if (actual
!= expected
) {
3493 console
.log("Blank root key not detected");
3494 console
.log("Expected: " + expected
);
3495 console
.log("Actual: " + actual
);
3504 // display error switching tabs while addresses are generating
3506 page
.open(url
, function(status
) {
3508 page
.evaluate(function() {
3509 $(".phrase").val("abandon abandon ability").trigger("input");
3511 waitForGenerate(function() {
3512 // set to generate 500 more addresses
3513 // generate more addresses
3514 // change tabs which should cancel the previous generating
3515 page
.evaluate(function() {
3516 $(".rows-to-add").val("100");
3518 $("#bip32-tab a").click();
3520 // check the derivation paths are in order and of the right quantity
3521 waitForGenerate(function() {
3522 var paths
= page
.evaluate(function() {
3523 return $(".index").map(function(i
, e
) {
3527 for (var i
=0; i
<paths
.length
; i
++) {
3528 var expected
= "m/0/" + i
;
3529 var actual
= paths
[i
];
3530 if (actual
!= expected
) {
3531 console
.log("Path " + i
+ " is not in correct order");
3532 console
.log("Expected: " + expected
);
3533 console
.log("Actual: " + actual
);
3537 if (paths
.length
!= 20) {
3538 console
.log("Generation was not cancelled by new action");
3548 // padding for binary should give length with multiple of 256
3549 // hashed entropy 1111 is length 252, so requires 4 leading zeros
3550 // prior to issue 49 it would only generate 2 leading zeros, ie missing 2
3552 page
.open(url
, function(status
) {
3553 expected
= "avocado valid quantum cross link predict excuse edit street able flame large galaxy ginger nuclear"
3555 page
.evaluate(function() {
3556 $(".use-entropy").prop("checked", true).trigger("change");
3557 $(".mnemonic-length").val("15");
3558 $(".entropy").val("1111").trigger("input");
3560 waitForGenerate(function() {
3562 var actual
= page
.evaluate(function() {
3563 return $(".phrase").val();
3565 // check the mnemonic is correct
3566 if (actual
!= expected
) {
3567 console
.log("Left padding error for entropy");
3568 console
.log("Expected: " + expected
);
3569 console
.log("Actual: " + actual
);
3577 // Github pull request 55
3578 // https://github.com/iancoleman/bip39/pull/55
3581 page
.open(url
, function(status
) {
3582 // set mnemonic and select bip32 tab
3583 page
.evaluate(function() {
3584 $("#bip32-tab a").click();
3585 $(".phrase").val("abandon abandon ability").trigger("input");
3587 waitForGenerate(function() {
3589 // set bip32 client to bitcoin core
3590 page
.evaluate(function() {
3591 var bitcoinCoreIndex
= "0";
3592 $("#bip32-client").val(bitcoinCoreIndex
).trigger("change");
3594 waitForGenerate(function() {
3595 // get the derivation path
3596 var actual
= page
.evaluate(function() {
3597 return $("#bip32-path").val();
3599 // check the derivation path is correct
3600 expected
= "m/0'/0'"
3601 if (actual
!= expected
) {
3602 console
.log("Selecting Bitcoin Core client does not set correct derivation path");
3603 console
.log("Expected: " + expected
);
3604 console
.log("Actual: " + actual
);
3607 // get hardened addresses
3608 var usesHardenedAddresses
= page
.evaluate(function() {
3609 return $(".hardened-addresses").prop("checked");
3611 // check hardened addresses is selected
3612 if(!usesHardenedAddresses
) {
3613 console
.log("Selecting Bitcoin Core client does not use hardened addresses");
3616 // check input is readonly
3617 var pathIsReadonly
= page
.evaluate(function() {
3618 return $("#bip32-path").prop("readonly");
3620 if (!pathIsReadonly
) {
3621 console
.log("Selecting Bitcoin Core client does not set derivation path to readonly");
3625 // set bip32 client to multibit
3626 page
.evaluate(function() {
3627 var multibitIndex
= "2";
3628 $("#bip32-client").val(multibitIndex
).trigger("change");
3630 waitForGenerate(function() {
3631 // get the derivation path
3632 var actual
= page
.evaluate(function() {
3633 return $("#bip32-path").val();
3635 // check the derivation path is correct
3637 if (actual
!= expected
) {
3638 console
.log("Selecting Multibit client does not set correct derivation path");
3639 console
.log("Expected: " + expected
);
3640 console
.log("Actual: " + actual
);
3643 // get hardened addresses
3644 var usesHardenedAddresses
= page
.evaluate(function() {
3645 return $(".hardened-addresses").prop("checked");
3647 // check hardened addresses is selected
3648 if(usesHardenedAddresses
) {
3649 console
.log("Selecting Multibit client does not uncheck hardened addresses");
3652 // CUSTOM DERIVATION PATH
3653 // check input is not readonly
3654 page
.evaluate(function() {
3655 $("#bip32-client").val("custom").trigger("change");
3657 // do not wait for generate, since there is no change to the
3658 // derivation path there is no new generation performed
3659 var pathIsReadonly
= page
.evaluate(function() {
3660 return $("#bip32-path").prop("readonly");
3662 if (pathIsReadonly
) {
3663 console
.log("Selecting Custom Derivation Path does not allow derivation path input");
3674 // https://github.com/iancoleman/bip39/issues/58
3675 // bip32 derivation is correct, does not drop leading zeros
3677 // https://medium.com/@alexberegszaszi/why-do-my-bip32-wallets-disagree-6f3254cc5846
3679 page
.open(url
, function(status
) {
3680 // set the phrase and passphrase
3681 var expected
= "17rxURoF96VhmkcEGCj5LNQkmN9HVhWb7F";
3682 // Note that bitcore generates an incorrect address
3683 // 13EuKhffWkBE2KUwcbkbELZb1MpzbimJ3Y
3684 // see the medium.com link above for more details
3685 page
.evaluate(function() {
3686 $(".phrase").val("fruit wave dwarf banana earth journey tattoo true farm silk olive fence");
3687 $(".passphrase").val("banana").trigger("input");
3689 // check the address is generated correctly
3690 waitForGenerate(function() {
3691 var actual
= page
.evaluate(function() {
3692 return $(".address:first").text();
3694 if (actual
!= expected
) {
3695 console
.log("BIP32 derivation is incorrect");
3696 console
.log("Expected: " + expected
);
3697 console
.log("Actual: " + actual
);
3707 // Japanese mnemonics generate incorrect bip32 seed
3708 // BIP39 seed is set from phrase
3710 page
.open(url
, function(status
) {
3712 var expected
= "a262d6fb6122ecf45be09c50492b31f92e9beb7d9a845987a02cefda57a15f9c467a17872029a9e92299b5cbdf306e3a0ee620245cbd508959b6cb7ca637bd55";
3713 page
.evaluate(function() {
3714 $(".phrase").val("あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あいこくしん あおぞら");
3715 $("#passphrase").val("メートルガバヴァぱばぐゞちぢ十人十色");
3716 $("#passphrase").trigger("input");
3718 // check the seed is generated correctly
3719 waitForGenerate(function() {
3720 var actual
= page
.evaluate(function() {
3721 return $(".seed").val();
3723 if (actual
!= expected
) {
3724 console
.log("BIP39 seed is incorrectly generated from Japanese mnemonic");
3725 console
.log("Expected: " + expected
);
3726 console
.log("Actual: " + actual
);
3734 // If you wish to add more tests, do so here...
3736 // Here is a blank test template
3740 page.open(url, function(status) {
3741 // Do something on the page
3742 page.evaluate(function() {
3743 $(".phrase").val("abandon abandon ability").trigger("input");
3745 waitForGenerate(function() {
3746 // Check the result of doing the thing
3747 var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
3748 var actual = page.evaluate(function() {
3749 return $(".address:first").text();
3751 if (actual != expected) {
3752 console.log("A specific message about what failed");
3753 console.log("Expected: " + expected);
3754 console.log("Actual: " + actual);
3757 // Run the next test
3767 console
.log("Running tests...");
3768 tests
= shuffle(tests
);