diff options
-rw-r--r-- | src/index.html | 83 | ||||
-rw-r--r-- | src/js/index.js | 56 | ||||
-rw-r--r-- | tests/spec/tests.js | 148 |
3 files changed, 285 insertions, 2 deletions
diff --git a/src/index.html b/src/index.html index 8d8f756..0d1a2c6 100644 --- a/src/index.html +++ b/src/index.html | |||
@@ -204,6 +204,9 @@ | |||
204 | <li id="bip49-tab"> | 204 | <li id="bip49-tab"> |
205 | <a href="#bip49" role="tab" data-toggle="tab">BIP49</a> | 205 | <a href="#bip49" role="tab" data-toggle="tab">BIP49</a> |
206 | </li> | 206 | </li> |
207 | <li id="bip84-tab"> | ||
208 | <a href="#bip84" role="tab" data-toggle="tab">BIP84</a> | ||
209 | </li> | ||
207 | <li id="bip141-tab"> | 210 | <li id="bip141-tab"> |
208 | <a href="#bip141" role="tab" data-toggle="tab">BIP141</a> | 211 | <a href="#bip141" role="tab" data-toggle="tab">BIP141</a> |
209 | </li> | 212 | </li> |
@@ -487,6 +490,86 @@ | |||
487 | </div> | 490 | </div> |
488 | </form> | 491 | </form> |
489 | </div> | 492 | </div> |
493 | <div id="bip84" class="tab-pane"> | ||
494 | <form class="form-horizontal" role="form"> | ||
495 | <br> | ||
496 | <div class="col-sm-2"></div> | ||
497 | <div class="col-sm-10"> | ||
498 | <p> | ||
499 | For more info see the | ||
500 | <a href="https://github.com/bitcoin/bips/blob/master/bip-0084.mediawiki" target="_blank">BIP84 spec</a>. | ||
501 | </p> | ||
502 | </div> | ||
503 | <div class="form-group"> | ||
504 | <label for="purpose" class="col-sm-2 control-label"> | ||
505 | Purpose | ||
506 | </label> | ||
507 | <div class="col-sm-10"> | ||
508 | <input id="purpose" type="text" class="purpose form-control" value="84" readonly> | ||
509 | </div> | ||
510 | </div> | ||
511 | <div class="form-group"> | ||
512 | <label for="coin" class="col-sm-2 control-label"> | ||
513 | Coin | ||
514 | </label> | ||
515 | <div class="col-sm-10"> | ||
516 | <input id="coin" type="text" class="coin form-control" value="0" readonly> | ||
517 | </div> | ||
518 | </div> | ||
519 | <div class="form-group"> | ||
520 | <label for="account" class="col-sm-2 control-label"> | ||
521 | Account | ||
522 | </label> | ||
523 | <div class="col-sm-10"> | ||
524 | <input id="account" type="text" class="account form-control" value="0"> | ||
525 | </div> | ||
526 | </div> | ||
527 | <div class="form-group"> | ||
528 | <label for="change" class="col-sm-2 control-label"> | ||
529 | External / Internal | ||
530 | </label> | ||
531 | <div class="col-sm-10"> | ||
532 | <input id="change" type="text" class="change form-control" value="0"> | ||
533 | </div> | ||
534 | </div> | ||
535 | <div class="form-group"> | ||
536 | <label class="col-sm-2 control-label"> | ||
537 | </label> | ||
538 | <div class="col-sm-10"> | ||
539 | <p>The account extended keys can be used for importing to most BIP84 compatible wallets.</p> | ||
540 | </div> | ||
541 | </div> | ||
542 | <div class="form-group"> | ||
543 | <label for="account-xprv" class="col-sm-2 control-label"> | ||
544 | <span>Account Extended Private Key</span> | ||
545 | </label> | ||
546 | <div class="col-sm-10"> | ||
547 | <textarea id="account-xprv" type="text" class="account-xprv form-control" readonly data-show-qr></textarea> | ||
548 | </div> | ||
549 | </div> | ||
550 | <div class="form-group"> | ||
551 | <label for="account-xpub" class="col-sm-2 control-label"> | ||
552 | <span>Account Extended Public Key</span> | ||
553 | </label> | ||
554 | <div class="col-sm-10"> | ||
555 | <textarea id="account-xpub" type="text" class="account-xpub form-control" readonly data-show-qr></textarea> | ||
556 | </div> | ||
557 | </div> | ||
558 | <div class="form-group"> | ||
559 | <label class="col-sm-2 control-label"> | ||
560 | </label> | ||
561 | <div class="col-sm-10"> | ||
562 | <p>The BIP32 derivation path and extended keys are the basis for the derived addresses.</p> | ||
563 | </div> | ||
564 | </div> | ||
565 | <div class="form-group"> | ||
566 | <label for="bip84-path" class="col-sm-2 control-label">BIP32 Derivation Path</label> | ||
567 | <div class="col-sm-10"> | ||
568 | <input id="bip84-path" type="text" class="path form-control" value="m/84'/0'/0'/0" readonly="readonly"> | ||
569 | </div> | ||
570 | </div> | ||
571 | </form> | ||
572 | </div> | ||
490 | </div> | 573 | </div> |
491 | <form class="form-horizontal" role="form"> | 574 | <form class="form-horizontal" role="form"> |
492 | <div class="form-group"> | 575 | <div class="form-group"> |
diff --git a/src/js/index.js b/src/js/index.js index 9c9ad58..e63c65f 100644 --- a/src/js/index.js +++ b/src/js/index.js | |||
@@ -53,6 +53,7 @@ | |||
53 | DOM.bip32tab = $("#bip32-tab"); | 53 | DOM.bip32tab = $("#bip32-tab"); |
54 | DOM.bip44tab = $("#bip44-tab"); | 54 | DOM.bip44tab = $("#bip44-tab"); |
55 | DOM.bip49tab = $("#bip49-tab"); | 55 | DOM.bip49tab = $("#bip49-tab"); |
56 | DOM.bip84tab = $("#bip84-tab"); | ||
56 | DOM.bip141tab = $("#bip141-tab"); | 57 | DOM.bip141tab = $("#bip141-tab"); |
57 | DOM.bip32panel = $("#bip32"); | 58 | DOM.bip32panel = $("#bip32"); |
58 | DOM.bip44panel = $("#bip44"); | 59 | DOM.bip44panel = $("#bip44"); |
@@ -74,6 +75,13 @@ | |||
74 | DOM.bip49accountXprv = $("#bip49 .account-xprv"); | 75 | DOM.bip49accountXprv = $("#bip49 .account-xprv"); |
75 | DOM.bip49accountXpub = $("#bip49 .account-xpub"); | 76 | DOM.bip49accountXpub = $("#bip49 .account-xpub"); |
76 | DOM.bip49change = $("#bip49 .change"); | 77 | DOM.bip49change = $("#bip49 .change"); |
78 | DOM.bip84path = $("#bip84-path"); | ||
79 | DOM.bip84purpose = $("#bip84 .purpose"); | ||
80 | DOM.bip84coin = $("#bip84 .coin"); | ||
81 | DOM.bip84account = $("#bip84 .account"); | ||
82 | DOM.bip84accountXprv = $("#bip84 .account-xprv"); | ||
83 | DOM.bip84accountXpub = $("#bip84 .account-xpub"); | ||
84 | DOM.bip84change = $("#bip84 .change"); | ||
77 | DOM.bip141unavailable = $("#bip141 .unavailable"); | 85 | DOM.bip141unavailable = $("#bip141 .unavailable"); |
78 | DOM.bip141available = $("#bip141 .available"); | 86 | DOM.bip141available = $("#bip141 .available"); |
79 | DOM.bip141path = $("#bip141-path"); | 87 | DOM.bip141path = $("#bip141-path"); |
@@ -117,6 +125,8 @@ | |||
117 | DOM.bip44change.on("input", calcForDerivationPath); | 125 | DOM.bip44change.on("input", calcForDerivationPath); |
118 | DOM.bip49account.on("input", calcForDerivationPath); | 126 | DOM.bip49account.on("input", calcForDerivationPath); |
119 | DOM.bip49change.on("input", calcForDerivationPath); | 127 | DOM.bip49change.on("input", calcForDerivationPath); |
128 | DOM.bip84account.on("input", calcForDerivationPath); | ||
129 | DOM.bip84change.on("input", calcForDerivationPath); | ||
120 | DOM.bip141path.on("input", calcForDerivationPath); | 130 | DOM.bip141path.on("input", calcForDerivationPath); |
121 | DOM.bip141semantics.on("change", tabChanged); | 131 | DOM.bip141semantics.on("change", tabChanged); |
122 | DOM.tab.on("shown.bs.tab", tabChanged); | 132 | DOM.tab.on("shown.bs.tab", tabChanged); |
@@ -357,6 +367,9 @@ | |||
357 | else if (bip49TabSelected()) { | 367 | else if (bip49TabSelected()) { |
358 | displayBip49Info(); | 368 | displayBip49Info(); |
359 | } | 369 | } |
370 | else if (bip84TabSelected()) { | ||
371 | displayBip84Info(); | ||
372 | } | ||
360 | displayBip32Info(); | 373 | displayBip32Info(); |
361 | } | 374 | } |
362 | 375 | ||
@@ -559,6 +572,21 @@ | |||
559 | console.log("Using derivation path from BIP49 tab: " + derivationPath); | 572 | console.log("Using derivation path from BIP49 tab: " + derivationPath); |
560 | return derivationPath; | 573 | return derivationPath; |
561 | } | 574 | } |
575 | else if (bip84TabSelected()) { | ||
576 | var purpose = parseIntNoNaN(DOM.bip84purpose.val(), 84); | ||
577 | var coin = parseIntNoNaN(DOM.bip84coin.val(), 0); | ||
578 | var account = parseIntNoNaN(DOM.bip84account.val(), 0); | ||
579 | var change = parseIntNoNaN(DOM.bip84change.val(), 0); | ||
580 | var path = "m/"; | ||
581 | path += purpose + "'/"; | ||
582 | path += coin + "'/"; | ||
583 | path += account + "'/"; | ||
584 | path += change; | ||
585 | DOM.bip84path.val(path); | ||
586 | var derivationPath = DOM.bip84path.val(); | ||
587 | console.log("Using derivation path from BIP84 tab: " + derivationPath); | ||
588 | return derivationPath; | ||
589 | } | ||
562 | else if (bip32TabSelected()) { | 590 | else if (bip32TabSelected()) { |
563 | var derivationPath = DOM.bip32path.val(); | 591 | var derivationPath = DOM.bip32path.val(); |
564 | console.log("Using derivation path from BIP32 tab: " + derivationPath); | 592 | console.log("Using derivation path from BIP32 tab: " + derivationPath); |
@@ -659,6 +687,24 @@ | |||
659 | DOM.bip49accountXpub.val(accountXpub); | 687 | DOM.bip49accountXpub.val(accountXpub); |
660 | } | 688 | } |
661 | 689 | ||
690 | function displayBip84Info() { | ||
691 | // Get the derivation path for the account | ||
692 | var purpose = parseIntNoNaN(DOM.bip84purpose.val(), 84); | ||
693 | var coin = parseIntNoNaN(DOM.bip84coin.val(), 0); | ||
694 | var account = parseIntNoNaN(DOM.bip84account.val(), 0); | ||
695 | var path = "m/"; | ||
696 | path += purpose + "'/"; | ||
697 | path += coin + "'/"; | ||
698 | path += account + "'/"; | ||
699 | // Calculate the account extended keys | ||
700 | var accountExtendedKey = calcBip32ExtendedKey(path); | ||
701 | var accountXprv = accountExtendedKey.toBase58(); | ||
702 | var accountXpub = accountExtendedKey.neutered().toBase58(); | ||
703 | // Display the extended keys | ||
704 | DOM.bip84accountXprv.val(accountXprv); | ||
705 | DOM.bip84accountXpub.val(accountXpub); | ||
706 | } | ||
707 | |||
662 | function displayBip32Info() { | 708 | function displayBip32Info() { |
663 | // Display the key | 709 | // Display the key |
664 | DOM.seed.val(seed); | 710 | DOM.seed.val(seed); |
@@ -699,11 +745,12 @@ | |||
699 | } | 745 | } |
700 | 746 | ||
701 | function segwitSelected() { | 747 | function segwitSelected() { |
702 | return bip49TabSelected() || bip141TabSelected(); | 748 | return bip49TabSelected() || bip84TabSelected() || bip141TabSelected(); |
703 | } | 749 | } |
704 | 750 | ||
705 | function p2wpkhSelected() { | 751 | function p2wpkhSelected() { |
706 | return bip141TabSelected() && DOM.bip141semantics.val() == "p2wpkh"; | 752 | return bip84TabSelected() || |
753 | bip141TabSelected() && DOM.bip141semantics.val() == "p2wpkh"; | ||
707 | } | 754 | } |
708 | 755 | ||
709 | function p2wpkhInP2shSelected() { | 756 | function p2wpkhInP2shSelected() { |
@@ -1284,6 +1331,10 @@ | |||
1284 | return DOM.bip49tab.hasClass("active"); | 1331 | return DOM.bip49tab.hasClass("active"); |
1285 | } | 1332 | } |
1286 | 1333 | ||
1334 | function bip84TabSelected() { | ||
1335 | return DOM.bip84tab.hasClass("active"); | ||
1336 | } | ||
1337 | |||
1287 | function bip141TabSelected() { | 1338 | function bip141TabSelected() { |
1288 | return DOM.bip141tab.hasClass("active"); | 1339 | return DOM.bip141tab.hasClass("active"); |
1289 | } | 1340 | } |
@@ -1291,6 +1342,7 @@ | |||
1291 | function setHdCoin(coinValue) { | 1342 | function setHdCoin(coinValue) { |
1292 | DOM.bip44coin.val(coinValue); | 1343 | DOM.bip44coin.val(coinValue); |
1293 | DOM.bip49coin.val(coinValue); | 1344 | DOM.bip49coin.val(coinValue); |
1345 | DOM.bip84coin.val(coinValue); | ||
1294 | } | 1346 | } |
1295 | 1347 | ||
1296 | function showSegwitAvailable() { | 1348 | function showSegwitAvailable() { |
diff --git a/tests/spec/tests.js b/tests/spec/tests.js index 7691fcc..07b7b1a 100644 --- a/tests/spec/tests.js +++ b/tests/spec/tests.js | |||
@@ -2684,4 +2684,152 @@ it('Shows the index of each word in the mnemonic', function(done) { | |||
2684 | }); | 2684 | }); |
2685 | }); | 2685 | }); |
2686 | 2686 | ||
2687 | it('Shows the derivation path for bip84 tab', function(done) { | ||
2688 | driver.findElement(By.css('#bip84-tab a')) | ||
2689 | .click() | ||
2690 | driver.findElement(By.css('.phrase')) | ||
2691 | .sendKeys('abandon abandon ability'); | ||
2692 | driver.sleep(generateDelay).then(function() { | ||
2693 | driver.findElement(By.css('#bip84 .path')) | ||
2694 | .getAttribute("value") | ||
2695 | .then(function(path) { | ||
2696 | expect(path).toBe("m/84'/0'/0'/0"); | ||
2697 | done(); | ||
2698 | }) | ||
2699 | }); | ||
2700 | }); | ||
2701 | |||
2702 | it('Shows the extended private key for bip84 tab', function(done) { | ||
2703 | driver.findElement(By.css('#bip84-tab a')) | ||
2704 | .click() | ||
2705 | driver.findElement(By.css('.phrase')) | ||
2706 | .sendKeys('abandon abandon ability'); | ||
2707 | driver.sleep(generateDelay).then(function() { | ||
2708 | driver.findElement(By.css('.extended-priv-key')) | ||
2709 | .getAttribute("value") | ||
2710 | .then(function(path) { | ||
2711 | expect(path).toBe("zprvAev3RKrZ3QVKiUFCfdeMRen1BPDJgdNt1XpxiDy8acSs4kkAGTCvq7HeRYRNNpo8EtEjCFQBWavJwtCUR29y4TUCH4X5RXMcyq48uN8y9BP"); | ||
2712 | done(); | ||
2713 | }) | ||
2714 | }); | ||
2715 | }); | ||
2716 | |||
2717 | it('Shows the extended public key for bip84 tab', function(done) { | ||
2718 | driver.findElement(By.css('#bip84-tab a')) | ||
2719 | .click() | ||
2720 | driver.findElement(By.css('.phrase')) | ||
2721 | .sendKeys('abandon abandon ability'); | ||
2722 | driver.sleep(generateDelay).then(function() { | ||
2723 | driver.findElement(By.css('.extended-pub-key')) | ||
2724 | .getAttribute("value") | ||
2725 | .then(function(path) { | ||
2726 | expect(path).toBe("zpub6suPpqPSsn3cvxKfmfBMnnijjR3o666jNkkZWcNk8wyqwZ5JozXBNuc8Gs7DB3uLwTDvGVTspVEAUQcEjKF3pZHgywVbubdTqbXTUg7usyx"); | ||
2727 | done(); | ||
2728 | }) | ||
2729 | }); | ||
2730 | }); | ||
2731 | |||
2732 | it('Changes the address list if bip84 account is changed', function(done) { | ||
2733 | driver.findElement(By.css('#bip84-tab a')) | ||
2734 | .click() | ||
2735 | driver.findElement(By.css('#bip84 .account')) | ||
2736 | .sendKeys('1'); | ||
2737 | driver.findElement(By.css('.phrase')) | ||
2738 | .sendKeys('abandon abandon ability'); | ||
2739 | driver.sleep(generateDelay).then(function() { | ||
2740 | getFirstAddress(function(address) { | ||
2741 | expect(address).toBe("bc1qp7vv669t2fy965jdzvqwrraana89ctd5ewc662"); | ||
2742 | done(); | ||
2743 | }); | ||
2744 | }); | ||
2745 | }); | ||
2746 | |||
2747 | it('Changes the address list if bip84 change is changed', function(done) { | ||
2748 | driver.findElement(By.css('#bip84-tab a')) | ||
2749 | .click() | ||
2750 | driver.findElement(By.css('#bip84 .change')) | ||
2751 | .sendKeys('1'); | ||
2752 | driver.findElement(By.css('.phrase')) | ||
2753 | .sendKeys('abandon abandon ability'); | ||
2754 | driver.sleep(generateDelay).then(function() { | ||
2755 | getFirstAddress(function(address) { | ||
2756 | expect(address).toBe("bc1qr39vj6rh06ff05m53uxq8uazehwhccswylhrs2"); | ||
2757 | done(); | ||
2758 | }); | ||
2759 | }); | ||
2760 | }); | ||
2761 | |||
2762 | it('Passes the official BIP84 test spec for rootpriv', function(done) { | ||
2763 | driver.findElement(By.css('#bip84-tab a')) | ||
2764 | .click() | ||
2765 | driver.findElement(By.css('.phrase')) | ||
2766 | .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'); | ||
2767 | driver.sleep(generateDelay).then(function() { | ||
2768 | driver.findElement(By.css(".root-key")) | ||
2769 | .getAttribute("value") | ||
2770 | .then(function(rootKey) { | ||
2771 | expect(rootKey).toBe("zprvAWgYBBk7JR8Gjrh4UJQ2uJdG1r3WNRRfURiABBE3RvMXYSrRJL62XuezvGdPvG6GFBZduosCc1YP5wixPox7zhZLfiUm8aunE96BBa4Kei5"); | ||
2772 | done(); | ||
2773 | }) | ||
2774 | }); | ||
2775 | }); | ||
2776 | |||
2777 | it('Passes the official BIP84 test spec for account 0 xprv', function(done) { | ||
2778 | driver.findElement(By.css('#bip84-tab a')) | ||
2779 | .click() | ||
2780 | driver.findElement(By.css('.phrase')) | ||
2781 | .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'); | ||
2782 | driver.sleep(generateDelay).then(function() { | ||
2783 | driver.findElement(By.css("#bip84 .account-xprv")) | ||
2784 | .getAttribute("value") | ||
2785 | .then(function(rootKey) { | ||
2786 | expect(rootKey).toBe("zprvAdG4iTXWBoARxkkzNpNh8r6Qag3irQB8PzEMkAFeTRXxHpbF9z4QgEvBRmfvqWvGp42t42nvgGpNgYSJA9iefm1yYNZKEm7z6qUWCroSQnE"); | ||
2787 | done(); | ||
2788 | }) | ||
2789 | }); | ||
2790 | }); | ||
2791 | |||
2792 | it('Passes the official BIP84 test spec for account 0 xpub', function(done) { | ||
2793 | driver.findElement(By.css('#bip84-tab a')) | ||
2794 | .click() | ||
2795 | driver.findElement(By.css('.phrase')) | ||
2796 | .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'); | ||
2797 | driver.sleep(generateDelay).then(function() { | ||
2798 | driver.findElement(By.css("#bip84 .account-xpub")) | ||
2799 | .getAttribute("value") | ||
2800 | .then(function(rootKey) { | ||
2801 | expect(rootKey).toBe("zpub6rFR7y4Q2AijBEqTUquhVz398htDFrtymD9xYYfG1m4wAcvPhXNfE3EfH1r1ADqtfSdVCToUG868RvUUkgDKf31mGDtKsAYz2oz2AGutZYs"); | ||
2802 | done(); | ||
2803 | }) | ||
2804 | }); | ||
2805 | }); | ||
2806 | |||
2807 | it('Passes the official BIP84 test spec for account 0 first address', function(done) { | ||
2808 | driver.findElement(By.css('#bip84-tab a')) | ||
2809 | .click() | ||
2810 | driver.findElement(By.css('.phrase')) | ||
2811 | .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'); | ||
2812 | driver.sleep(generateDelay).then(function() { | ||
2813 | getFirstAddress(function(address) { | ||
2814 | expect(address).toBe("bc1qcr8te4kr609gcawutmrza0j4xv80jy8z306fyu"); | ||
2815 | done(); | ||
2816 | }); | ||
2817 | }); | ||
2818 | }); | ||
2819 | |||
2820 | it('Passes the official BIP84 test spec for account 0 first change address', function(done) { | ||
2821 | driver.findElement(By.css('#bip84-tab a')) | ||
2822 | .click() | ||
2823 | driver.findElement(By.css('.phrase')) | ||
2824 | .sendKeys('abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about'); | ||
2825 | driver.findElement(By.css('#bip84 .change')) | ||
2826 | .sendKeys('1'); | ||
2827 | driver.sleep(generateDelay).then(function() { | ||
2828 | getFirstAddress(function(address) { | ||
2829 | expect(address).toBe("bc1q8c6fshw2dlwun7ekn9qwf37cu2rn755upcp6el"); | ||
2830 | done(); | ||
2831 | }); | ||
2832 | }); | ||
2833 | }); | ||
2834 | |||
2687 | }); | 2835 | }); |