]>
git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/BIP39.git/blob - src/js/index.js
3 var mnemonic
= new Mnemonic("english");
4 var bip32RootKey
= null;
5 var bip32ExtendedKey
= null;
6 var network
= bitcoin
.networks
.bitcoin
;
7 var addressRowTemplate
= $("#address-row-template");
10 var showAddress
= true;
11 var showPrivKey
= true;
13 var phraseChangeTimeoutEvent
= null;
16 DOM
.network
= $(".network");
17 DOM
.phraseNetwork
= $("#network-phrase");
18 DOM
.phrase
= $(".phrase");
19 DOM
.passphrase
= $(".passphrase");
20 DOM
.generate
= $(".generate");
21 DOM
.rootKey
= $(".root-key");
22 DOM
.extendedPrivKey
= $(".extended-priv-key");
23 DOM
.extendedPubKey
= $(".extended-pub-key");
24 DOM
.bip32tab
= $("#bip32-tab");
25 DOM
.bip44tab
= $("#bip44-tab");
26 DOM
.bip32panel
= $("#bip32");
27 DOM
.bip44panel
= $("#bip44");
28 DOM
.bip32path
= $("#bip32-path");
29 DOM
.bip44path
= $("#bip44-path");
30 DOM
.bip44purpose
= $("#bip44 .purpose");
31 DOM
.bip44coin
= $("#bip44 .coin");
32 DOM
.bip44account
= $("#bip44 .account");
33 DOM
.bip44change
= $("#bip44 .change");
34 DOM
.strength
= $(".strength");
35 DOM
.addresses
= $(".addresses");
36 DOM
.rowsToAdd
= $(".rows-to-add");
37 DOM
.more
= $(".more");
38 DOM
.feedback
= $(".feedback");
39 DOM
.tab
= $(".derivation-type a");
40 DOM
.indexToggle
= $(".index-toggle");
41 DOM
.addressToggle
= $(".address-toggle");
42 DOM
.privateKeyToggle
= $(".private-key-toggle");
44 var derivationPath
= $(".tab-pane.active .path").val();
48 DOM
.network
.on("change", networkChanged
);
49 DOM
.phrase
.on("input", delayedPhraseChanged
);
50 DOM
.passphrase
.on("input", delayedPhraseChanged
);
51 DOM
.generate
.on("click", generateClicked
);
52 DOM
.more
.on("click", showMore
);
53 DOM
.bip32path
.on("input", bip32Changed
);
54 DOM
.bip44purpose
.on("input", bip44Changed
);
55 DOM
.bip44coin
.on("input", bip44Changed
);
56 DOM
.bip44account
.on("input", bip44Changed
);
57 DOM
.bip44change
.on("input", bip44Changed
);
58 DOM
.tab
.on("click", tabClicked
);
59 DOM
.indexToggle
.on("click", toggleIndexes
);
60 DOM
.addressToggle
.on("click", toggleAddresses
);
61 DOM
.privateKeyToggle
.on("click", togglePrivateKeys
);
64 hideValidationError();
65 populateNetworkSelect();
70 function networkChanged(e
) {
71 var network
= e
.target
.value
;
72 networks
[network
].onSelect();
73 setBip44DerivationPath();
74 delayedPhraseChanged();
77 function delayedPhraseChanged() {
78 hideValidationError();
80 if (phraseChangeTimeoutEvent
!= null) {
81 clearTimeout(phraseChangeTimeoutEvent
);
83 phraseChangeTimeoutEvent
= setTimeout(phraseChanged
, 400);
86 function phraseChanged() {
88 hideValidationError();
89 // Get the mnemonic phrase
90 var phrase
= DOM
.phrase
.val();
91 var passphrase
= DOM
.passphrase
.val();
92 var errorText
= findPhraseErrors(phrase
);
94 showValidationError(errorText
);
97 // Get the derivation path
98 var errorText
= findDerivationPathErrors();
100 showValidationError(errorText
);
103 // Calculate and display
104 calcBip32Seed(phrase
, passphrase
, derivationPath
);
109 function generateClicked() {
112 setTimeout(function() {
113 var phrase
= generateRandomPhrase();
121 function tabClicked(e
) {
122 var activePath
= $(e
.target
.getAttribute("href") + " .path");
123 derivationPath
= activePath
.val();
127 function derivationChanged() {
128 delayedPhraseChanged();
131 function bip32Changed() {
132 derivationPath
= DOM
.bip32path
.val();
136 function bip44Changed() {
137 setBip44DerivationPath();
141 function toggleIndexes() {
142 showIndex
= !showIndex
;
143 $("td.index span").toggleClass("invisible");
146 function toggleAddresses() {
147 showAddress
= !showAddress
;
148 $("td.address span").toggleClass("invisible");
151 function togglePrivateKeys() {
152 showPrivKey
= !showPrivKey
;
153 $("td.privkey span").toggleClass("invisible");
158 function generateRandomPhrase() {
159 if (!hasStrongRandom()) {
160 var errorText
= "This browser does not support strong randomness";
161 showValidationError(errorText
);
164 var numWords
= parseInt(DOM
.strength
.val());
165 // Check strength is an integer
166 if (isNaN(numWords
)) {
167 DOM
.strength
.val("12");
170 // Check strength is a multiple of 32, if not round it down
171 if (numWords
% 3 != 0) {
172 numWords
= Math
.floor(numWords
/ 3) * 3;
173 DOM
.strength
.val(numWords
);
175 // Check strength is at least 32
178 DOM
.strength
.val(numWords
);
180 var strength
= numWords
/ 3 * 32;
181 var words
= mnemonic
.generate(strength
);
182 DOM
.phrase
.val(words
);
186 function calcBip32Seed(phrase
, passphrase
, path
) {
187 var seed
= mnemonic
.toSeed(phrase
, passphrase
);
188 bip32RootKey
= bitcoin
.HDNode
.fromSeedHex(seed
, network
);
189 bip32ExtendedKey
= bip32RootKey
;
190 // Derive the key from the path
191 var pathBits
= path
.split("/");
192 for (var i
=0; i
<pathBits
.length
; i
++) {
193 var bit
= pathBits
[i
];
194 var index
= parseInt(bit
);
198 var hardened
= bit
[bit
.length
-1] == "'";
200 bip32ExtendedKey
= bip32ExtendedKey
.deriveHardened(index
);
203 bip32ExtendedKey
= bip32ExtendedKey
.derive(index
);
208 function showValidationError(errorText
) {
214 function hideValidationError() {
220 function findPhraseErrors(phrase
) {
221 // TODO make this right
222 // Preprocess the words
223 phrase
= mnemonic
.normalizeString(phrase
);
224 var parts
= phrase
.split(" ");
226 for (var i
=0; i
<parts
.length
; i
++) {
228 if (part
.length
> 0) {
229 // TODO check that lowercasing is always valid to do
230 proper
.push(part
.toLowerCase());
233 // TODO some levenstein on the words
234 var properPhrase
= proper
.join(' ');
235 // Check the words are valid
236 var isValid
= mnemonic
.check(properPhrase
);
238 return "Invalid mnemonic";
243 function findDerivationPathErrors(path
) {
248 function displayBip32Info() {
250 var rootKey
= bip32RootKey
.toBase58();
251 DOM
.rootKey
.val(rootKey
);
252 var extendedPrivKey
= bip32ExtendedKey
.toBase58();
253 DOM
.extendedPrivKey
.val(extendedPrivKey
);
254 var extendedPubKey
= bip32ExtendedKey
.toBase58(false);
255 DOM
.extendedPubKey
.val(extendedPubKey
);
256 // Display the addresses and privkeys
257 clearAddressesList();
258 displayAddresses(0, 20);
261 function displayAddresses(start
, total
) {
262 for (var i
=0; i
<total
; i
++) {
263 var index
= i
+ start
;
268 function TableRow(index
) {
274 function calculateValues() {
275 setTimeout(function() {
276 var key
= bip32ExtendedKey
.derive(index
);
277 var address
= key
.getAddress().toString();
278 var privkey
= key
.privKey
.toWIF(network
);
279 addAddressToList(index
, address
, privkey
);
287 function showMore() {
288 var start
= DOM
.addresses
.children().length
;
289 var rowsToAdd
= parseInt(DOM
.rowsToAdd
.val());
290 if (isNaN(rowsToAdd
)) {
292 DOM
.rowsToAdd
.val("20");
294 if (rowsToAdd
> 200) {
295 var msg
= "Generating " + rowsToAdd
+ " rows could take a while. ";
296 msg
+= "Do you want to continue?";
301 displayAddresses(start
, rowsToAdd
);
304 function clearDisplay() {
305 clearAddressesList();
307 hideValidationError();
310 function clearAddressesList() {
311 DOM
.addresses
.empty();
314 function clearKey() {
316 DOM
.extendedPrivKey
.val("");
317 DOM
.extendedPubKey
.val("");
320 function addAddressToList(index
, address
, privkey
) {
321 var row
= $(addressRowTemplate
.html());
323 var indexCell
= row
.find(".index span");
324 var addressCell
= row
.find(".address span");
325 var privkeyCell
= row
.find(".privkey span");
327 var indexText
= derivationPath
+ "/" + index
;
328 indexCell
.text(indexText
);
329 addressCell
.text(address
);
330 privkeyCell
.text(privkey
);
333 indexCell
.addClass("invisible");
336 addressCell
.addClass("invisible");
339 privkeCell
.addClass("invisible");
341 DOM
.addresses
.append(row
);
344 function hasStrongRandom() {
345 return 'crypto' in window
&& window
['crypto'] !== null;
348 function disableForms() {
349 $("form").on("submit", function(e
) {
354 function setBip44DerivationPath() {
355 var purpose
= parseIntNoNaN(DOM
.bip44purpose
.val(), 44);
356 var coin
= parseIntNoNaN(DOM
.bip44coin
.val(), 0);
357 var account
= parseIntNoNaN(DOM
.bip44account
.val(), 0);
358 var change
= parseIntNoNaN(DOM
.bip44change
.val(), 0);
360 path
+= purpose
+ "'/";
362 path
+= account
+ "'/";
364 DOM
.bip44path
.val(path
);
365 derivationPath
= DOM
.bip44path
.val();
368 function parseIntNoNaN(val
, defaultVal
) {
369 var v
= parseInt(val
);
376 function showPending() {
378 .text("Calculating...")
382 function hidePending() {
388 function populateNetworkSelect() {
389 for (var i
=0; i
<networks
.length
; i
++) {
390 var network
= networks
[i
];
391 var option
= $("<option>");
392 option
.attr("value", i
);
393 option
.text(network
.name
);
394 DOM
.phraseNetwork
.append(option
);
401 onSelect: function() {
402 network
= bitcoin
.networks
.bitcoin
;
403 DOM
.bip44coin
.val(0);
407 name: "Bitcoin Testnet",
408 onSelect: function() {
409 network
= bitcoin
.networks
.testnet
;
410 DOM
.bip44coin
.val(1);
415 onSelect: function() {
416 network
= bitcoin
.networks
.litecoin
;
417 DOM
.bip44coin
.val(2);
422 onSelect: function() {
423 network
= bitcoin
.networks
.dogecoin
;
424 DOM
.bip44coin
.val(3);
429 onSelect: function() {
430 network
= bitcoin
.networks
.shadow
;
431 DOM
.bip44coin
.val(35);
435 name: "ShadowCash Testnet",
436 onSelect: function() {
437 network
= bitcoin
.networks
.shadowtn
;
438 DOM
.bip44coin
.val(1);
443 onSelect: function() {
444 network
= bitcoin
.networks
.viacoin
;
445 DOM
.bip44coin
.val(14);
449 name: "Viacoin Testnet",
450 onSelect: function() {
451 network
= bitcoin
.networks
.viacointestnet
;
452 DOM
.bip44coin
.val(1);
457 onSelect: function() {
458 network
= bitcoin
.networks
.jumbucks
;
459 DOM
.bip44coin
.val(26);