]>
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
.bip44Network
= $("#network-bip44");
19 DOM
.addressNetwork
= $("#network-address-type");
20 DOM
.phrase
= $(".phrase");
21 DOM
.passphrase
= $(".passphrase");
22 DOM
.generate
= $(".generate");
23 DOM
.rootKey
= $(".root-key");
24 DOM
.extendedPrivKey
= $(".extended-priv-key");
25 DOM
.extendedPubKey
= $(".extended-pub-key");
26 DOM
.bip32tab
= $("#bip32-tab");
27 DOM
.bip44tab
= $("#bip44-tab");
28 DOM
.bip32panel
= $("#bip32");
29 DOM
.bip44panel
= $("#bip44");
30 DOM
.bip32path
= $("#bip32-path");
31 DOM
.bip44path
= $("#bip44-path");
32 DOM
.bip44purpose
= $("#bip44 .purpose");
33 DOM
.bip44coin
= $("#bip44 .coin");
34 DOM
.bip44account
= $("#bip44 .account");
35 DOM
.bip44change
= $("#bip44 .change");
36 DOM
.strength
= $(".strength");
37 DOM
.addresses
= $(".addresses");
38 DOM
.rowsToAdd
= $(".rows-to-add");
39 DOM
.more
= $(".more");
40 DOM
.feedback
= $(".feedback");
41 DOM
.tab
= $(".derivation-type a");
42 DOM
.indexToggle
= $(".index-toggle");
43 DOM
.addressToggle
= $(".address-toggle");
44 DOM
.privateKeyToggle
= $(".private-key-toggle");
46 var derivationPath
= DOM
.bip44path
.val();
50 DOM
.network
.on("change", networkChanged
);
51 DOM
.phrase
.on("input", delayedPhraseChanged
);
52 DOM
.passphrase
.on("input", delayedPhraseChanged
);
53 DOM
.generate
.on("click", generateClicked
);
54 DOM
.more
.on("click", showMore
);
55 DOM
.bip32path
.on("input", bip32Changed
);
56 DOM
.bip44purpose
.on("input", bip44Changed
);
57 DOM
.bip44coin
.on("input", bip44Changed
);
58 DOM
.bip44account
.on("input", bip44Changed
);
59 DOM
.bip44change
.on("input", bip44Changed
);
60 DOM
.tab
.on("click", tabClicked
);
61 DOM
.indexToggle
.on("click", toggleIndexes
);
62 DOM
.addressToggle
.on("click", toggleAddresses
);
63 DOM
.privateKeyToggle
.on("click", togglePrivateKeys
);
66 hideValidationError();
71 function networkChanged(e
) {
72 var n
= e
.target
.value
;
74 network
= Bitcoin
.networks
.bitcoin
;
76 setBip44DerivationPath();
79 else if (n
== "bitcoin-testnet") {
80 network
= Bitcoin
.networks
.testnet
;
82 setBip44DerivationPath();
85 else if (n
== "litecoin") {
86 network
= Bitcoin
.networks
.litecoin
;
88 setBip44DerivationPath();
91 else if (n
== "dogecoin") {
92 network
= Bitcoin
.networks
.dogecoin
;
95 DOM
.phraseNetwork
.val(n
);
96 DOM
.bip44Network
.val(n
);
97 if(e
.target
!= DOM
.addressNetwork
.dom
){
98 DOM
.addressNetwork
.val(n
);
100 delayedPhraseChanged();
103 function delayedPhraseChanged() {
104 hideValidationError();
106 if (phraseChangeTimeoutEvent
!= null) {
107 clearTimeout(phraseChangeTimeoutEvent
);
109 phraseChangeTimeoutEvent
= setTimeout(phraseChanged
, 400);
112 function phraseChanged() {
114 hideValidationError();
115 // Get the mnemonic phrase
116 var phrase
= DOM
.phrase
.val();
117 var passphrase
= DOM
.passphrase
.val();
118 var errorText
= findPhraseErrors(phrase
);
120 showValidationError(errorText
);
123 // Get the derivation path
124 var errorText
= findDerivationPathErrors();
126 showValidationError(errorText
);
129 // Calculate and display
130 calcBip32Seed(phrase
, passphrase
, derivationPath
);
135 function generateClicked() {
138 setTimeout(function() {
139 var phrase
= generateRandomPhrase();
147 function tabClicked(e
) {
148 var activePath
= $(e
.target
.getAttribute("href") + " .path");
149 derivationPath
= activePath
.val();
153 function derivationChanged() {
154 delayedPhraseChanged();
157 function bip32Changed() {
158 derivationPath
= DOM
.bip32path
.val();
162 function bip44Changed() {
163 setBip44DerivationPath();
164 derivationPath
= DOM
.bip44path
.val();
168 function toggleIndexes() {
169 showIndex
= !showIndex
;
170 $("td.index span").toggleClass("invisible");
173 function toggleAddresses() {
174 showAddress
= !showAddress
;
175 $("td.address span").toggleClass("invisible");
178 function togglePrivateKeys() {
179 showPrivKey
= !showPrivKey
;
180 $("td.privkey span").toggleClass("invisible");
185 function generateRandomPhrase() {
186 if (!hasStrongRandom()) {
187 var errorText
= "This browser does not support strong randomness";
188 showValidationError(errorText
);
191 var numWords
= parseInt(DOM
.strength
.val());
192 // Check strength is an integer
193 if (isNaN(numWords
)) {
194 DOM
.strength
.val("12");
197 // Check strength is a multiple of 32, if not round it down
198 if (numWords
% 3 != 0) {
199 numWords
= Math
.floor(numWords
/ 3) * 3;
200 DOM
.strength
.val(numWords
);
202 // Check strength is at least 32
205 DOM
.strength
.val(numWords
);
207 var strength
= numWords
/ 3 * 32;
208 var words
= mnemonic
.generate(strength
);
209 DOM
.phrase
.val(words
);
213 function calcBip32Seed(phrase
, passphrase
, path
) {
214 var seed
= mnemonic
.toSeed(phrase
, passphrase
);
215 bip32RootKey
= Bitcoin
.HDNode
.fromSeedHex(seed
, network
);
216 bip32ExtendedKey
= bip32RootKey
;
217 // Derive the key from the path
218 var pathBits
= path
.split("/");
219 for (var i
=0; i
<pathBits
.length
; i
++) {
220 var bit
= pathBits
[i
];
221 var index
= parseInt(bit
);
225 var hardened
= bit
[bit
.length
-1] == "'";
227 bip32ExtendedKey
= bip32ExtendedKey
.deriveHardened(index
);
230 bip32ExtendedKey
= bip32ExtendedKey
.derive(index
);
235 function showValidationError(errorText
) {
241 function hideValidationError() {
247 function findPhraseErrors(phrase
) {
248 // TODO make this right
249 // Preprocess the words
250 phrase
= mnemonic
.normalizeString(phrase
);
251 var parts
= phrase
.split(" ");
253 for (var i
=0; i
<parts
.length
; i
++) {
255 if (part
.length
> 0) {
256 // TODO check that lowercasing is always valid to do
257 proper
.push(part
.toLowerCase());
260 // TODO some levenstein on the words
261 var properPhrase
= proper
.join(' ');
262 // Check the words are valid
263 var isValid
= mnemonic
.check(properPhrase
);
265 return "Invalid mnemonic";
270 function findDerivationPathErrors(path
) {
275 function displayBip32Info() {
277 var rootKey
= bip32RootKey
.toBase58();
278 DOM
.rootKey
.val(rootKey
);
279 var extendedPrivKey
= bip32ExtendedKey
.toBase58();
280 DOM
.extendedPrivKey
.val(extendedPrivKey
);
281 var extendedPubKey
= bip32ExtendedKey
.toBase58(false);
282 DOM
.extendedPubKey
.val(extendedPubKey
);
283 // Display the addresses and privkeys
284 clearAddressesList();
285 displayAddresses(0, 20);
288 function displayAddresses(start
, total
) {
289 for (var i
=0; i
<total
; i
++) {
290 var index
= i
+ start
;
295 function TableRow(index
) {
301 function calculateValues() {
302 setTimeout(function() {
303 var key
= bip32ExtendedKey
.derive(index
);
304 var address
= key
.getAddress().toString();
305 var privkey
= key
.privKey
.toWIF(network
);
306 addAddressToList(index
, address
, privkey
);
314 function showMore() {
315 var start
= DOM
.addresses
.children().length
;
316 var rowsToAdd
= parseInt(DOM
.rowsToAdd
.val());
317 if (isNaN(rowsToAdd
)) {
319 DOM
.rowsToAdd
.val("20");
321 if (rowsToAdd
> 200) {
322 var msg
= "Generating " + rowsToAdd
+ " rows could take a while. ";
323 msg
+= "Do you want to continue?";
328 displayAddresses(start
, rowsToAdd
);
331 function clearDisplay() {
332 clearAddressesList();
334 hideValidationError();
337 function clearAddressesList() {
338 DOM
.addresses
.empty();
341 function clearKey() {
343 DOM
.extendedPrivKey
.val("");
344 DOM
.extendedPubKey
.val("");
347 function addAddressToList(index
, address
, privkey
) {
348 var row
= $(addressRowTemplate
.html());
350 var indexCell
= row
.find(".index span");
351 var addressCell
= row
.find(".address span");
352 var privkeyCell
= row
.find(".privkey span");
354 indexCell
.text(index
);
355 addressCell
.text(address
);
356 privkeyCell
.text(privkey
);
359 indexCell
.addClass("invisible");
362 addressCell
.addClass("invisible");
365 privkeCell
.addClass("invisible");
367 DOM
.addresses
.append(row
);
370 function hasStrongRandom() {
371 return 'crypto' in window
&& window
['crypto'] !== null;
374 function disableForms() {
375 $("form").on("submit", function(e
) {
380 function setBip44DerivationPath() {
381 var purpose
= parseIntNoNaN(DOM
.bip44purpose
.val(), 44);
382 var coin
= parseIntNoNaN(DOM
.bip44coin
.val(), 0);
383 var account
= parseIntNoNaN(DOM
.bip44account
.val(), 0);
384 var change
= parseIntNoNaN(DOM
.bip44change
.val(), 0);
386 path
+= purpose
+ "'/";
388 path
+= account
+ "'/";
390 DOM
.bip44path
.val(path
);
393 function parseIntNoNaN(val
, defaultVal
) {
394 var v
= parseInt(val
);
401 function showPending() {
403 .text("Calculating...")
407 function hidePending() {
413 function enableBip44Tab() {
414 // show bip44 tab (but don't select it)
415 DOM
.bip44tab
.removeClass("hidden");
416 DOM
.bip44panel
.removeClass("hidden");
419 function disableBip44Tab() {
421 DOM
.bip44tab
.addClass("hidden");
422 DOM
.bip44tab
.removeClass("active");
424 DOM
.bip44panel
.addClass("hidden");
425 DOM
.bip44panel
.removeClass("active");
427 DOM
.bip32tab
.addClass("active");
429 DOM
.bip32panel
.addClass("active");
430 // set the derivation path
431 var activePath
= $("#bip32 .path");
432 derivationPath
= activePath
.val();