diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/index.html | 34 | ||||
-rw-r--r-- | src/js/index.js | 109 |
2 files changed, 120 insertions, 23 deletions
diff --git a/src/index.html b/src/index.html index 6708675..ea7096c 100644 --- a/src/index.html +++ b/src/index.html | |||
@@ -67,14 +67,14 @@ | |||
67 | <div class="col-sm-10"> | 67 | <div class="col-sm-10"> |
68 | <div class="input-group"> | 68 | <div class="input-group"> |
69 | <select id="strength" class="strength form-control"> | 69 | <select id="strength" class="strength form-control"> |
70 | <option val="3">3</option> | 70 | <option value="3">3</option> |
71 | <option val="6">6</option> | 71 | <option value="6">6</option> |
72 | <option val="9">9</option> | 72 | <option value="9">9</option> |
73 | <option val="12">12</option> | 73 | <option value="12">12</option> |
74 | <option val="15" selected>15</option> | 74 | <option value="15" selected>15</option> |
75 | <option val="18">18</option> | 75 | <option value="18">18</option> |
76 | <option val="21">21</option> | 76 | <option value="21">21</option> |
77 | <option val="24">24</option> | 77 | <option value="24">24</option> |
78 | </select> | 78 | </select> |
79 | <span class="input-group-btn"> | 79 | <span class="input-group-btn"> |
80 | <button class="btn generate">Generate Random Mnemonic</button> | 80 | <button class="btn generate">Generate Random Mnemonic</button> |
@@ -105,7 +105,7 @@ | |||
105 | <div class="form-group"> | 105 | <div class="form-group"> |
106 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> | 106 | <label for="root-key" class="col-sm-2 control-label">BIP32 Root Key</label> |
107 | <div class="col-sm-10"> | 107 | <div class="col-sm-10"> |
108 | <textarea id="root-key" class="root-key form-control" readonly="readonly"></textarea> | 108 | <textarea id="root-key" class="root-key form-control"></textarea> |
109 | </div> | 109 | </div> |
110 | </div> | 110 | </div> |
111 | </form> | 111 | </form> |
@@ -187,6 +187,13 @@ | |||
187 | </div> | 187 | </div> |
188 | </div> | 188 | </div> |
189 | <div class="form-group"> | 189 | <div class="form-group"> |
190 | <div class="col-sm-2"></div> | ||
191 | <label class="col-sm-10"> | ||
192 | <input class="hardened-addresses" type="checkbox"> | ||
193 | Use hardened addresses | ||
194 | </label> | ||
195 | </div> | ||
196 | <div class="form-group"> | ||
190 | <label class="col-sm-2 control-label">Hive Wallet</label> | 197 | <label class="col-sm-2 control-label">Hive Wallet</label> |
191 | <div class="col-sm-10"> | 198 | <div class="col-sm-10"> |
192 | <p class="form-control no-border"> | 199 | <p class="form-control no-border"> |
@@ -204,6 +211,15 @@ | |||
204 | </p> | 211 | </p> |
205 | </div> | 212 | </div> |
206 | </div> | 213 | </div> |
214 | <div class="form-group"> | ||
215 | <label for="core-path" class="col-sm-2 control-label">Bitcoin Core</label> | ||
216 | <div class="col-sm-10"> | ||
217 | <p class="form-control no-border"> | ||
218 | Use path <code>m/0'/0'</code> with hardened addresses. | ||
219 | For more info see the <a href="https://github.com/bitcoin/bitcoin/pull/8035" target="_blank">Bitcoin Core BIP32 implementation</a> | ||
220 | </p> | ||
221 | </div> | ||
222 | </div> | ||
207 | </form> | 223 | </form> |
208 | </div> | 224 | </div> |
209 | </div> | 225 | </div> |
diff --git a/src/js/index.js b/src/js/index.js index f3582b0..8f7d658 100644 --- a/src/js/index.js +++ b/src/js/index.js | |||
@@ -12,6 +12,7 @@ | |||
12 | var showPrivKey = true; | 12 | var showPrivKey = true; |
13 | 13 | ||
14 | var phraseChangeTimeoutEvent = null; | 14 | var phraseChangeTimeoutEvent = null; |
15 | var rootKeyChangedTimeoutEvent = null; | ||
15 | 16 | ||
16 | var DOM = {}; | 17 | var DOM = {}; |
17 | DOM.network = $(".network"); | 18 | DOM.network = $(".network"); |
@@ -34,6 +35,7 @@ | |||
34 | DOM.bip44account = $("#bip44 .account"); | 35 | DOM.bip44account = $("#bip44 .account"); |
35 | DOM.bip44change = $("#bip44 .change"); | 36 | DOM.bip44change = $("#bip44 .change"); |
36 | DOM.strength = $(".strength"); | 37 | DOM.strength = $(".strength"); |
38 | DOM.hardenedAddresses = $(".hardened-addresses"); | ||
37 | DOM.addresses = $(".addresses"); | 39 | DOM.addresses = $(".addresses"); |
38 | DOM.rowsToAdd = $(".rows-to-add"); | 40 | DOM.rowsToAdd = $(".rows-to-add"); |
39 | DOM.more = $(".more"); | 41 | DOM.more = $(".more"); |
@@ -50,12 +52,14 @@ | |||
50 | DOM.passphrase.on("input", delayedPhraseChanged); | 52 | DOM.passphrase.on("input", delayedPhraseChanged); |
51 | DOM.generate.on("click", generateClicked); | 53 | DOM.generate.on("click", generateClicked); |
52 | DOM.more.on("click", showMore); | 54 | DOM.more.on("click", showMore); |
53 | DOM.bip32path.on("input", delayedPhraseChanged); | 55 | DOM.rootKey.on("input", delayedRootKeyChanged); |
54 | DOM.bip44purpose.on("input", delayedPhraseChanged); | 56 | DOM.bip32path.on("input", calcForDerivationPath); |
55 | DOM.bip44coin.on("input", delayedPhraseChanged); | 57 | DOM.bip44purpose.on("input", calcForDerivationPath); |
56 | DOM.bip44account.on("input", delayedPhraseChanged); | 58 | DOM.bip44coin.on("input", calcForDerivationPath); |
57 | DOM.bip44change.on("input", delayedPhraseChanged); | 59 | DOM.bip44account.on("input", calcForDerivationPath); |
58 | DOM.tab.on("click", delayedPhraseChanged); | 60 | DOM.bip44change.on("input", calcForDerivationPath); |
61 | DOM.tab.on("shown.bs.tab", calcForDerivationPath); | ||
62 | DOM.hardenedAddresses.on("change", calcForDerivationPath); | ||
59 | DOM.indexToggle.on("click", toggleIndexes); | 63 | DOM.indexToggle.on("click", toggleIndexes); |
60 | DOM.addressToggle.on("click", toggleAddresses); | 64 | DOM.addressToggle.on("click", toggleAddresses); |
61 | DOM.privateKeyToggle.on("click", togglePrivateKeys); | 65 | DOM.privateKeyToggle.on("click", togglePrivateKeys); |
@@ -68,9 +72,14 @@ | |||
68 | // Event handlers | 72 | // Event handlers |
69 | 73 | ||
70 | function networkChanged(e) { | 74 | function networkChanged(e) { |
71 | var network = e.target.value; | 75 | var networkIndex = e.target.value; |
72 | networks[network].onSelect(); | 76 | networks[networkIndex].onSelect(); |
73 | delayedPhraseChanged(); | 77 | if (seed != null) { |
78 | phraseChanged(); | ||
79 | } | ||
80 | else { | ||
81 | rootKeyChanged(); | ||
82 | } | ||
74 | } | 83 | } |
75 | 84 | ||
76 | function delayedPhraseChanged() { | 85 | function delayedPhraseChanged() { |
@@ -87,12 +96,57 @@ | |||
87 | hideValidationError(); | 96 | hideValidationError(); |
88 | // Get the mnemonic phrase | 97 | // Get the mnemonic phrase |
89 | var phrase = DOM.phrase.val(); | 98 | var phrase = DOM.phrase.val(); |
90 | var passphrase = DOM.passphrase.val(); | ||
91 | var errorText = findPhraseErrors(phrase); | 99 | var errorText = findPhraseErrors(phrase); |
92 | if (errorText) { | 100 | if (errorText) { |
93 | showValidationError(errorText); | 101 | showValidationError(errorText); |
94 | return; | 102 | return; |
95 | } | 103 | } |
104 | // Calculate and display | ||
105 | var passphrase = DOM.passphrase.val(); | ||
106 | calcBip32RootKeyFromSeed(phrase, passphrase); | ||
107 | calcForDerivationPath(); | ||
108 | hidePending(); | ||
109 | } | ||
110 | |||
111 | function delayedRootKeyChanged() { | ||
112 | // Warn if there is an existing mnemonic or passphrase. | ||
113 | if (DOM.phrase.val().length > 0 || DOM.passphrase.val().length > 0) { | ||
114 | if (!confirm("This will clear existing mnemonic and passphrase")) { | ||
115 | DOM.rootKey.val(bip32RootKey); | ||
116 | return | ||
117 | } | ||
118 | } | ||
119 | hideValidationError(); | ||
120 | showPending(); | ||
121 | // Clear existing mnemonic and passphrase | ||
122 | DOM.phrase.val(""); | ||
123 | DOM.passphrase.val(""); | ||
124 | seed = null; | ||
125 | if (rootKeyChangedTimeoutEvent != null) { | ||
126 | clearTimeout(rootKeyChangedTimeoutEvent); | ||
127 | } | ||
128 | rootKeyChangedTimeoutEvent = setTimeout(rootKeyChanged, 400); | ||
129 | } | ||
130 | |||
131 | function rootKeyChanged() { | ||
132 | showPending(); | ||
133 | hideValidationError(); | ||
134 | // Validate the root key TODO | ||
135 | var rootKeyBase58 = DOM.rootKey.val(); | ||
136 | var errorText = validateRootKey(rootKeyBase58); | ||
137 | if (errorText) { | ||
138 | showValidationError(errorText); | ||
139 | return; | ||
140 | } | ||
141 | // Calculate and display | ||
142 | calcBip32RootKeyFromBase58(rootKeyBase58); | ||
143 | calcForDerivationPath(); | ||
144 | hidePending(); | ||
145 | } | ||
146 | |||
147 | function calcForDerivationPath() { | ||
148 | showPending(); | ||
149 | hideValidationError(); | ||
96 | // Get the derivation path | 150 | // Get the derivation path |
97 | var derivationPath = getDerivationPath(); | 151 | var derivationPath = getDerivationPath(); |
98 | var errorText = findDerivationPathErrors(derivationPath); | 152 | var errorText = findDerivationPathErrors(derivationPath); |
@@ -100,8 +154,7 @@ | |||
100 | showValidationError(errorText); | 154 | showValidationError(errorText); |
101 | return; | 155 | return; |
102 | } | 156 | } |
103 | // Calculate and display | 157 | calcBip32ExtendedKey(derivationPath); |
104 | calcBip32Seed(phrase, passphrase, derivationPath); | ||
105 | displayBip32Info(); | 158 | displayBip32Info(); |
106 | hidePending(); | 159 | hidePending(); |
107 | } | 160 | } |
@@ -148,9 +201,16 @@ | |||
148 | return words; | 201 | return words; |
149 | } | 202 | } |
150 | 203 | ||
151 | function calcBip32Seed(phrase, passphrase, path) { | 204 | function calcBip32RootKeyFromSeed(phrase, passphrase) { |
152 | seed = mnemonic.toSeed(phrase, passphrase); | 205 | seed = mnemonic.toSeed(phrase, passphrase); |
153 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); | 206 | bip32RootKey = bitcoin.HDNode.fromSeedHex(seed, network); |
207 | } | ||
208 | |||
209 | function calcBip32RootKeyFromBase58(rootKeyBase58) { | ||
210 | bip32RootKey = bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
211 | } | ||
212 | |||
213 | function calcBip32ExtendedKey(path) { | ||
154 | bip32ExtendedKey = bip32RootKey; | 214 | bip32ExtendedKey = bip32RootKey; |
155 | // Derive the key from the path | 215 | // Derive the key from the path |
156 | var pathBits = path.split("/"); | 216 | var pathBits = path.split("/"); |
@@ -213,6 +273,16 @@ | |||
213 | return false; | 273 | return false; |
214 | } | 274 | } |
215 | 275 | ||
276 | function validateRootKey(rootKeyBase58) { | ||
277 | try { | ||
278 | bitcoin.HDNode.fromBase58(rootKeyBase58); | ||
279 | } | ||
280 | catch (e) { | ||
281 | return "Invalid root key"; | ||
282 | } | ||
283 | return ""; | ||
284 | } | ||
285 | |||
216 | function getDerivationPath() { | 286 | function getDerivationPath() { |
217 | if (DOM.bip44tab.hasClass("active")) { | 287 | if (DOM.bip44tab.hasClass("active")) { |
218 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); | 288 | var purpose = parseIntNoNaN(DOM.bip44purpose.val(), 44); |
@@ -299,16 +369,27 @@ | |||
299 | 369 | ||
300 | function TableRow(index) { | 370 | function TableRow(index) { |
301 | 371 | ||
372 | var useHardenedAddresses = DOM.hardenedAddresses.prop("checked"); | ||
373 | |||
302 | function init() { | 374 | function init() { |
303 | calculateValues(); | 375 | calculateValues(); |
304 | } | 376 | } |
305 | 377 | ||
306 | function calculateValues() { | 378 | function calculateValues() { |
307 | setTimeout(function() { | 379 | setTimeout(function() { |
308 | var key = bip32ExtendedKey.derive(index); | 380 | var key = ""; |
381 | if (useHardenedAddresses) { | ||
382 | key = bip32ExtendedKey.deriveHardened(index); | ||
383 | } | ||
384 | else { | ||
385 | key = bip32ExtendedKey.derive(index); | ||
386 | } | ||
309 | var address = key.getAddress().toString(); | 387 | var address = key.getAddress().toString(); |
310 | var privkey = key.privKey.toWIF(network); | 388 | var privkey = key.privKey.toWIF(network); |
311 | var indexText = getDerivationPath() + "/" + index; | 389 | var indexText = getDerivationPath() + "/" + index; |
390 | if (useHardenedAddresses) { | ||
391 | indexText = indexText + "'"; | ||
392 | } | ||
312 | addAddressToList(indexText, address, privkey); | 393 | addAddressToList(indexText, address, privkey); |
313 | }, 50) | 394 | }, 50) |
314 | } | 395 | } |