]>
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");
9 var phraseChangeTimeoutEvent
= null;
12 DOM
.phrase
= $(".phrase");
13 DOM
.passphrase
= $(".passphrase");
14 DOM
.generate
= $(".generate");
15 DOM
.rootKey
= $(".root-key");
16 DOM
.extendedPrivKey
= $(".extended-priv-key");
17 DOM
.extendedPubKey
= $(".extended-pub-key");
18 DOM
.bip32path
= $("#bip32-path");
19 DOM
.bip44path
= $("#bip44-path");
20 DOM
.bip44purpose
= $("#bip44 .purpose");
21 DOM
.bip44coin
= $("#bip44 .coin");
22 DOM
.bip44account
= $("#bip44 .account");
23 DOM
.bip44change
= $("#bip44 .change");
24 DOM
.strength
= $(".strength");
25 DOM
.addresses
= $(".addresses");
26 DOM
.rowsToAdd
= $(".rows-to-add");
27 DOM
.more
= $(".more");
28 DOM
.feedback
= $(".feedback");
29 DOM
.tab
= $(".derivation-type a");
30 DOM
.indexToggle
= $(".index-toggle");
31 DOM
.addressToggle
= $(".address-toggle");
32 DOM
.privateKeyToggle
= $(".private-key-toggle");
34 var derivationPath
= DOM
.bip44path
.val();
35 var currentPhrase
= DOM
.phrase
.val();
36 var currentPassphrase
= DOM
.passphrase
.val();
40 DOM
.phrase
.on("keyup", delayedPhraseChanged
);
41 DOM
.passphrase
.on("keyup", delayedPhraseChanged
);
42 DOM
.generate
.on("click", generateClicked
);
43 DOM
.more
.on("click", showMore
);
44 DOM
.bip32path
.on("keyup", bip32Changed
);
45 DOM
.bip44purpose
.on("keyup", bip44Changed
);
46 DOM
.bip44coin
.on("keyup", bip44Changed
);
47 DOM
.bip44account
.on("keyup", bip44Changed
);
48 DOM
.bip44change
.on("keyup", bip44Changed
);
49 DOM
.tab
.on("click", tabClicked
);
50 DOM
.indexToggle
.on("click", toggleIndexes
);
51 DOM
.addressToggle
.on("click", toggleAddresses
);
52 DOM
.privateKeyToggle
.on("click", togglePrivateKeys
);
55 hideValidationError();
60 function delayedPhraseChanged() {
64 hideValidationError();
66 if (phraseChangeTimeoutEvent
!= null) {
67 clearTimeout(phraseChangeTimeoutEvent
);
69 phraseChangeTimeoutEvent
= setTimeout(phraseChanged
, 400);
72 function phraseChanged() {
74 hideValidationError();
75 // Get the mnemonic phrase
76 var phrase
= DOM
.phrase
.val();
77 var passphrase
= DOM
.passphrase
.val();
78 var errorText
= findPhraseErrors(phrase
);
80 showValidationError(errorText
);
83 // Get the derivation path
84 var errorText
= findDerivationPathErrors();
86 showValidationError(errorText
);
89 // Calculate and display
90 calcBip32Seed(phrase
, passphrase
, derivationPath
);
93 // Set current state so we only update as needed
94 currentPhrase
= phrase
;
95 currentPassphrase
= passphrase
;
98 function generateClicked() {
101 setTimeout(function() {
102 var phrase
= generateRandomPhrase();
110 function tabClicked(e
) {
111 var activePath
= $(e
.target
.getAttribute("href") + " .path");
112 derivationPath
= activePath
.val();
116 function derivationChanged() {
117 hideValidationError();
119 setTimeout(phraseChanged
, 50);
122 function bip32Changed() {
123 derivationPath
= DOM
.bip32path
.val();
127 function bip44Changed() {
128 setBip44DerivationPath();
129 derivationPath
= DOM
.bip44path
.val();
133 function toggleIndexes() {
134 $("td.index span").toggleClass("invisible");
137 function toggleAddresses() {
138 $("td.address span").toggleClass("invisible");
141 function togglePrivateKeys() {
142 $("td.privkey span").toggleClass("invisible");
147 function generateRandomPhrase() {
148 if (!hasStrongRandom()) {
149 var errorText
= "This browser does not support strong randomness";
150 showValidationError(errorText
);
153 var numWords
= parseInt(DOM
.strength
.val());
154 // Check strength is an integer
155 if (isNaN(numWords
)) {
156 DOM
.strength
.val("12");
159 // Check strength is a multiple of 32, if not round it down
160 if (numWords
% 3 != 0) {
161 numWords
= Math
.floor(numWords
/ 3) * 3;
162 DOM
.strength
.val(numWords
);
164 // Check strength is at least 32
167 DOM
.strength
.val(numWords
);
169 var strength
= numWords
/ 3 * 32;
170 var words
= mnemonic
.generate(strength
);
171 DOM
.phrase
.val(words
);
175 function calcBip32Seed(phrase
, passphrase
, path
) {
176 var seed
= mnemonic
.toSeed(phrase
, passphrase
);
177 var seedHash
= Bitcoin
.crypto
.sha256(seed
).toString("hex");
178 bip32RootKey
= Bitcoin
.HDNode
.fromSeedHex(seedHash
, network
);
179 bip32ExtendedKey
= bip32RootKey
;
180 // Derive the key from the path
181 var pathBits
= path
.split("/");
182 for (var i
=0; i
<pathBits
.length
; i
++) {
183 var bit
= pathBits
[i
];
184 var index
= parseInt(bit
);
188 var hardened
= bit
[bit
.length
-1] == "'";
190 bip32ExtendedKey
= bip32ExtendedKey
.deriveHardened(index
);
193 bip32ExtendedKey
= bip32ExtendedKey
.derive(index
);
198 function showValidationError(errorText
) {
204 function hideValidationError() {
210 function findPhraseErrors(phrase
) {
211 // TODO make this right
212 // Preprocess the words
213 var parts
= phrase
.split(" ");
215 for (var i
=0; i
<parts
.length
; i
++) {
217 if (part
.length
> 0) {
218 // TODO check that lowercasing is always valid to do
219 proper
.push(part
.toLowerCase());
222 // TODO some levenstein on the words
223 var properPhrase
= proper
.join(' ');
224 // Check the words are valid
225 var isValid
= mnemonic
.check(properPhrase
);
227 return "Invalid mnemonic";
232 function findDerivationPathErrors(path
) {
237 function displayBip32Info() {
239 var rootKey
= bip32RootKey
.toBase58();
240 DOM
.rootKey
.val(rootKey
);
241 var extendedPrivKey
= bip32ExtendedKey
.toBase58();
242 DOM
.extendedPrivKey
.val(extendedPrivKey
);
243 var extendedPubKey
= bip32ExtendedKey
.toBase58(false);
244 DOM
.extendedPubKey
.val(extendedPubKey
);
245 // Display the addresses and privkeys
246 clearAddressesList();
247 displayAddresses(0, 20);
250 function displayAddresses(start
, total
) {
251 for (var i
=0; i
<total
; i
++) {
252 var index
= i
+ start
;
253 var key
= bip32ExtendedKey
.derive(index
);
254 var address
= key
.getAddress().toString();
255 var privkey
= key
.privKey
.toWIF();
256 addAddressToList(index
, address
, privkey
);
260 function showMore() {
261 var start
= DOM
.addresses
.children().length
;
262 var rowsToAdd
= parseInt(DOM
.rowsToAdd
.val());
263 if (isNaN(rowsToAdd
)) {
265 DOM
.rowsToAdd
.val("20");
267 if (rowsToAdd
> 200) {
268 var msg
= "Generating " + rowsToAdd
+ " rows could take a while. ";
269 msg
+= "Do you want to continue?";
275 setTimeout(function() {
276 displayAddresses(start
, rowsToAdd
);
281 function clearDisplay() {
282 clearAddressesList();
284 hideValidationError();
287 function clearAddressesList() {
288 DOM
.addresses
.empty();
291 function clearKey() {
293 DOM
.extendedPrivKey
.val("");
294 DOM
.extendedPubKey
.val("");
297 function addAddressToList(index
, address
, privkey
) {
298 var row
= $(addressRowTemplate
.html());
299 row
.find(".index span").text(index
);
300 row
.find(".address span").text(address
);
301 row
.find(".privkey span").text(privkey
);
302 DOM
.addresses
.append(row
);
305 function hasStrongRandom() {
306 return 'crypto' in window
&& window
['crypto'] !== null;
309 function disableForms() {
310 $("form").on("submit", function(e
) {
315 function setBip44DerivationPath() {
316 var purpose
= parseIntNoNaN(DOM
.bip44purpose
.val(), 44);
317 var coin
= parseIntNoNaN(DOM
.bip44coin
.val(), 0);
318 var account
= parseIntNoNaN(DOM
.bip44account
.val(), 0);
319 var change
= parseIntNoNaN(DOM
.bip44change
.val(), 0);
321 path
+= purpose
+ "'/";
323 path
+= account
+ "'/";
325 DOM
.bip44path
.val(path
);
328 function parseIntNoNaN(val
, defaultVal
) {
329 var v
= parseInt(val
);
336 function showPending() {
338 .text("Calculating...")
342 function hidePending() {
348 function hasChanged() {
349 var phraseChanged
= DOM
.phrase
.val() != currentPhrase
;
350 var passphraseChanged
= DOM
.passphrase
.val() != currentPassphrase
;
351 return phraseChanged
|| passphraseChanged
;