From 329798f482b466d79809e6537eba181b02b116be Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sun, 3 Jan 2016 14:24:06 -0800 Subject: Wallet and registration process updates --- bower.json | 3 +- public/img/village_books.png | Bin 0 -> 121526 bytes public/js/calculator.coffee | 2 +- public/js/calculator.js | 2 +- public/js/register.coffee | 16 +-- public/js/register.js | 22 +--- public/js/wallet.coffee | 257 ++++++++++++++++++++++++------------- public/js/wallet.js | 296 +++++++++++++++++++++++++++++-------------- routes/users.js | 5 +- src/routes/users.coffee | 5 +- views/layout.html | 1 + views/sessions/new.html | 3 +- views/users/edit.html | 18 --- views/users/new.html | 7 +- views/users/profile.html | 2 - views/users/wallet.html | 85 ++++++------- 16 files changed, 440 insertions(+), 284 deletions(-) create mode 100644 public/img/village_books.png diff --git a/bower.json b/bower.json index e6109873..992dd8e4 100644 --- a/bower.json +++ b/bower.json @@ -32,6 +32,7 @@ "bootstrap-validator": "~0.9.0", "bootstrap3-dialog": "~1.34.7", "jquery-serialize-object": "~2.5.0", - "crypto-js": "~3.1.5" + "crypto-js": "~3.1.5", + "bootbox.js": "bootbox#~4.4.0" } } diff --git a/public/img/village_books.png b/public/img/village_books.png new file mode 100644 index 00000000..3743f069 Binary files /dev/null and b/public/img/village_books.png differ diff --git a/public/js/calculator.coffee b/public/js/calculator.coffee index 27606325..08a0a0c5 100644 --- a/public/js/calculator.coffee +++ b/public/js/calculator.coffee @@ -112,7 +112,7 @@ listen = -> clear(SOCKET_FAIL) g.blockchain.send('{"op":"addr_sub", "addr":"' + g.user.address + '"}') else - setTimeout(g.blockchain.onopen, 12000) + setTimeout(g.blockchain.onopen, 12000 * g.attempts) g.blockchain.onerror = -> fail(SOCKET_FAIL) diff --git a/public/js/calculator.js b/public/js/calculator.js index 39b47598..840d389b 100644 --- a/public/js/calculator.js +++ b/public/js/calculator.js @@ -130,7 +130,7 @@ clear(SOCKET_FAIL); return g.blockchain.send('{"op":"addr_sub", "addr":"' + g.user.address + '"}'); } else { - return setTimeout(g.blockchain.onopen, 12000); + return setTimeout(g.blockchain.onopen, 12000 * g.attempts); } }; g.blockchain.onerror = function() { diff --git a/public/js/register.coffee b/public/js/register.coffee index 0963d9d1..b012713f 100644 --- a/public/js/register.coffee +++ b/public/js/register.coffee @@ -4,17 +4,6 @@ $(-> $('#username').focus() $('#password').pwstrength(showVerdicts: false) - mnemonic = bip39.generateMnemonic() - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) - $('#pubkey').val(key.neutered().toString()) - privkey = key.toString() - - $('#password').keyup((e) -> - return if e.keyCode is 9 or e.keyCode is 16 - enc_privkey = CryptoJS.AES.encrypt(privkey, $(this).val()).toString() - enc_privkey = privkey if $(this).val() == '' - $('#privkey').val(enc_privkey) - ) $('#username').blur(-> $(this).parent().next('.alert').remove() @@ -62,5 +51,10 @@ $(-> if $('.has-error').length > 0 $('.has-error').effect('shake', 500) return false + else + mnemonic = bip39.generateMnemonic() + key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) + $('#pubkey').val(key.neutered().toString()) + $('#privkey').val(CryptoJS.AES.encrypt(key.toString(), $('#password').val()).toString()) ) ) diff --git a/public/js/register.js b/public/js/register.js index 02041163..5872896c 100644 --- a/public/js/register.js +++ b/public/js/register.js @@ -5,26 +5,10 @@ g = this; $(function() { - var key, mnemonic, privkey; $('#username').focus(); $('#password').pwstrength({ showVerdicts: false }); - mnemonic = bip39.generateMnemonic(); - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0); - $('#pubkey').val(key.neutered().toString()); - privkey = key.toString(); - $('#password').keyup(function(e) { - var enc_privkey; - if (e.keyCode === 9 || e.keyCode === 16) { - return; - } - enc_privkey = CryptoJS.AES.encrypt(privkey, $(this).val()).toString(); - if ($(this).val() === '') { - enc_privkey = privkey; - } - return $('#privkey').val(enc_privkey); - }); $('#username').blur(function() { $(this).parent().next('.alert').remove(); if (/^[a-z]+$/.test($(this).val()) && $(this).val().length > 2) { @@ -68,10 +52,16 @@ } }); return $('#register').submit(function() { + var key, mnemonic; $('.form-control').blur(); if ($('.has-error').length > 0) { $('.has-error').effect('shake', 500); return false; + } else { + mnemonic = bip39.generateMnemonic(); + key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0); + $('#pubkey').val(key.neutered().toString()); + return $('#privkey').val(CryptoJS.AES.encrypt(key.toString(), $('#password').val()).toString()); } }); }); diff --git a/public/js/wallet.coffee b/public/js/wallet.coffee index 32688150..97221e00 100644 --- a/public/js/wallet.coffee +++ b/public/js/wallet.coffee @@ -12,16 +12,39 @@ validators = password: (e) -> return true if e.val() is '' - g.master = null + g.key = null + + try + s = CryptoJS.AES.decrypt($('#privkey').val(), e.val()).toString(CryptoJS.enc.Utf8) + if s[0] is 'x' + g.key = bitcoin.HDNode.fromBase58(s) + else + g.key = bitcoin.ECPair.fromWIF(s) + catch + return false + + $('#invalid_keys').fadeOut() + + phrase: (e) -> try - g.master = bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt($('#privkey').val(), e.val()).toString(CryptoJS.enc.Utf8)) - $('#invalid_keys').fadeOut() + s = CryptoJS.AES.decrypt(g.user.privkey, e.val()).toString(CryptoJS.enc.Utf8) + if s[0] is 'x' + bitcoin.HDNode.fromBase58(s) + else + bitcoin.ECPair.fromWIF(s) catch return false + $('#invalid_keys').fadeOut() + + key: (e) -> + $('#keytype').val() != 'unknown' + errors = address: 'Invalid address.' password: 'Wrong password.' + phrase: 'Wrong password.' + key: 'Could not detect key type.' $(-> getUser() @@ -38,79 +61,108 @@ $(-> .popover(html: true) .on("show.bs.popover", -> $(this).data("bs.popover").tip().css(minWidth: "400px")) - $('form').validator(custom: validators, errors: errors) + $('form').validator(custom: validators, errors: errors, delay: 1200) $('[data-toggle=tooltip]').tooltip(trigger: 'hover') - $('#pubkey').keyup(-> + $('#key').keyup(-> val = $(this).val() - $('#type').val('unknown') + + $('#keytype').val('unknown') + $('#phrase').closest('.form-group').show() + switch val[0] when '1' try bitcoin.address.fromBase58Check(val) - $('#type').val('address') + $('#keytype').val('address') + $('#phrase').closest('.form-group').hide() when '5', \ 'L', \ 'K' try bitcoin.ECPair.fromWIF(val) - $('#type').val('private_key') + $('#keytype').val('wif') when 'U' try - CryptoJS.AES.decrypt(val, $('#new_password').val()) - $('#type').val('aes') + if CryptoJS.AES.decrypt(val, $('#phrase').val()).toString(CryptoJS.enc.Utf8) + $('#keytype').val('aes') when '6' - try - key = bip38().decrypt(val, $('#new_password').val()) - bitcoin.ECPair.fromWIF(key) - $('#type').val('bip38') + if bip38().verify(val) + $('#keytype').val('bip38') when 'x' if $(this).val()[3] is 'b' try bitcoin.HDNode.fromBase58(val) - $('#type').val('xpub') + $('#keytype').val('xpub') + $('#phrase').closest('.form-group').hide() else try bitcoin.HDNode.fromBase58(val) - $('#type').val('xprv') + $('#keytype').val('xprv') else - if val.split(' ').length in [12, 15, 18, 21, 24] - if bip39.validateMnemonic(val) - $('#type').val('bip39') + if val.split(' ').length in [12, 15, 18, 21, 24] and bip39.validateMnemonic(val) + $('#keytype').val('bip39') + ) + + $('#key').blur(-> + $('#keys form').validator('validate') ) - $('#keys form input[type=button]').click(-> + $('#save').click(-> $('.form-control').blur() if $('#keys .has-error').length > 0 $('#keys .has-error').effect('shake', 500) return - $('#withdraw').click() - $('#keys_updated').fadeIn() - - $.post("/#{g.user.username}", $('#keys form').serializeObject(), -> - $.ajax( - url: "#{g.api}/wallets/hd/#{g.user.username}" - type: 'DELETE' - ).done(-> - setTimeout(-> - data = - name: g.user.username - extended_public_key: $('#pubkey').val() - subchain_indexes: [0,1] - - $.post("#{g.api}/wallets/hd", JSON.stringify(data)).always(-> - $.post("#{g.api}/wallets/hd/#{g.user.username}/addresses/derive").always(-> - getBalance() - ) - ) - , 300) - ) - ) - - return false + key = $('#key').val() + proceed = true + + switch $('#keytype').val() + when 'address' + $('#pubkey').val(key) + $('#privkey').val('') + when 'wif' + pubkey = bitcoin.ECPair.fromWIF(key).getAddress() + privkey = CryptoJS.AES.encrypt(key, $('#phrase').val()) + $('#pubkey').val(pubkey) + $('#privkey').val(privkey) + when 'aes' + try + pubkey = bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt(key, $('#phrase').val()).toString(CryptoJS.enc.Utf8)).neutered().toString() + $('#pubkey').val(pubkey) + $('#privkey').val(key) + catch + try + pubkey = bitcoin.ECPair.fromWIF(CryptoJS.AES.decrypt(key, $('#phrase').val()).toString(CryptoJS.enc.Utf8)).getAddress() + $('#pubkey').val(pubkey) + $('#privkey').val(key) + catch + proceed = false + when 'bip38' + wif = bip38().decrypt(key, $('#phrase').val()) + pubkey = bitcoin.ECPair.fromWIF(wif).getAddress() + privkey = CryptoJS.AES.encrypt(wif, $('#phrase').val()) + $('#pubkey').val(pubkey) + $('#privkey, #key').val(privkey) + when 'xpub' + $('#pubkey').val(key) + $('#privkey').val('') + when 'xprv' + pubkey = bitcoin.HDNode.fromBase58(key).neutered().toString() + privkey = CryptoJS.AES.encrypt(key, $('#phrase').val()) + $('#pubkey').val(pubkey) + $('#privkey').val(privkey) + when 'bip39' + master = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(key)).deriveHardened(44).deriveHardened(0) + pubkey = master.neutered().toString() + privkey = CryptoJS.AES.encrypt(master.toString(), $('#phrase').val()) + $('#pubkey').val(pubkey) + $('#privkey').val(privkey) + + updateUser() if proceed ) + $('#withdrawal form input[type=button]').click(sendTransaction) $('#currency_toggle').click(-> @@ -147,46 +199,49 @@ $(-> $(this).val($(this).attr('max')) ) - $('#password').keyup(-> - ) - - $('#new_password').keyup(-> - $('#privkey').val(CryptoJS.AES.encrypt(g.privkey, $(this).val())) - + $('#phrase').keyup(-> try - bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt(g.user.privkey, $(this).val()).toString(CryptoJS.enc.Utf8)) - catch + CryptoJS.AES.decrypt(g.user.privkey, $(this).val()).toString(CryptoJS.enc.Utf8) + $('#key').keyup() ) - + $('#manage').click(-> $('#withdrawal').hide() $('#keys').show() - $('#withdraw').toggle(g.balance > 0) - $('#manage').hide() + $('#withdraw').hide().toggle(g.balance > 0 and g.user.privkey) $('#privkey').val(g.user.privkey) ) $('#withdraw').click(-> - $('#keys, #withdrawal').toggle() - $('#withdraw, #manage').toggle() + $('#withdrawal').show() + $('#amount').focus() + $('#keys').hide() + $('#withdraw').hide().toggle(g.balance > 0 and g.user.privkey) + $('#withdrawal form').validator('destroy') + $('#withdrawal form').validator(custom: validators, errors: errors, delay: 1200) + ) + + $('#cancel').click(-> + $('#keys').hide() ) $('#backup').click(-> url = 'data:application/json;base64,' + btoa(JSON.stringify(g.user.privkey)) - pom = document.createElement('a') - pom.setAttribute('href', url) - pom.setAttribute('download', 'coinos-wallet.aes.json') - pom.click() + a = document.createElement('a') + a.setAttribute('href', url) + a.setAttribute('download', 'coinos-wallet.aes.json') + a.click() ) $('#generate').click(-> - mnemonic = bip39.generateMnemonic() - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) - g.privkey = key.toString() - $('#pubkey').val(key.neutered().toString()).effect('highlight', {}, 2000).keyup() - $('#privkey').val('') - $('#new_password').parent().show() - $('#new_password').effect('shake', 500).focus() + bootbox.confirm('

Are you sure?

This will overwrite your existing wallet so make sure that you have the backup we sent to your email in case you want to restore it.

', (result) -> + if result + mnemonic = bip39.generateMnemonic() + key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0) + $('#key').val(key.toString()).effect('highlight', {}, 2000).keyup() + $('#phrase').parent().show() + $('#phrase').effect('shake', 500).focus() + ) ) $('.close').on('click', -> $(this).closest('.alert').fadeOut()) @@ -195,7 +250,9 @@ $(-> getUser = -> $.getJSON("/#{$('#username').val()}.json", (user) -> g.user = user - $('#pubkey').val(user.pubkey).keyup() + g.privkey = user.privkey + $('#key').val(user.privkey or user.pubkey).keyup() + $('#pubkey').val(user.pubkey) $('#privkey').val(user.privkey) $('#address').val(user.address) $('#unit').html(user.unit) @@ -216,17 +273,16 @@ createWallet = -> if g.user.username in data.wallet_names getBalance() else + params = name: g.user.username + if isBip32(g.user.pubkey) - data = - name: g.user.username - extended_public_key: g.user.pubkey - subchain_indexes: [0,1] + params.extended_public_key = g.user.pubkey + params.subchain_indexes = [0,1] - $.post("#{g.api}/wallets/hd", JSON.stringify(data)).always(getBalance) + $.post("#{g.api}/wallets/hd", JSON.stringify(params)).done(getBalance).fail(-> $('.wallet').fadeIn()) else - # TODO implement this properly - $('#balance').html(99) - $('#amount').val(99) + params.addresses = [g.user.pubkey] + $.post("#{g.api}/wallets", JSON.stringify(params)).done(getBalance).fail(-> $('.wallet').fadeIn()) ) @@ -240,15 +296,38 @@ getBalance = -> $('#amount').attr('max', g.balance) $('.wallet').fadeIn() - if g.balance > 0 + if g.balance > 0 and g.user.privkey + $('#keys').hide() + $('#withdrawal form').validator('destroy') + $('#withdrawal form').validator(custom: validators, errors: errors, delay: 1200) $('#withdrawal').show() $('#amount').focus() else - $('#manage').click() + $('#withdraw').hide() + ) + +updateUser = -> + data = $('#keys form').serializeObject() + delete data['key'] + delete data['phrase'] + + $.post("/#{g.user.username}", data, -> + $.ajax( + url: "#{g.api}/wallets/hd/#{g.user.username}" + type: 'DELETE' + ).always(-> + $.ajax( + url: "#{g.api}/wallets/#{g.user.username}" + type: 'DELETE' + ).always(-> + $('#keys').hide() + createWallet() + ) + ) ) sendTransaction = -> - if !g.master or typeof g.master.keyPair.d is 'undefined' + if !g.key or (typeof g.key.d is 'undefined' and typeof g.key.keyPair.d is 'undefined') $('#invalid_keys').fadeIn() $('#password').focus() else @@ -286,12 +365,18 @@ sendTransaction = -> $.post("#{g.api}/txs/new", JSON.stringify(params)).done((data) -> data.pubkeys = [] - data.signatures = data.tosign.map((tosign, i) -> - path = data.tx.inputs[i].hd_path.split('/') - key = g.master.derive(path[1]).derive(path[2]) - data.pubkeys.push(key.keyPair.getPublicKeyBuffer().toString('hex')) - return key.keyPair.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex") - ) + if g.key instanceof bitcoin.ECPair + data.signatures = data.tosign.map((tosign, i) -> + data.pubkeys.push(g.key.getPublicKeyBuffer().toString('hex')) + g.key.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex") + ) + else + data.signatures = data.tosign.map((tosign, i) -> + path = data.tx.inputs[i].hd_path.split('/') + key = g.key.derive(path[1]).derive(path[2]) + data.pubkeys.push(key.keyPair.getPublicKeyBuffer().toString('hex')) + key.keyPair.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex") + ) g.data = data diff --git a/public/js/wallet.js b/public/js/wallet.js index 9a2d1ff5..15dfab03 100644 --- a/public/js/wallet.js +++ b/public/js/wallet.js @@ -1,6 +1,6 @@ ;(function(e,t,n){function i(n,s){if(!t[n]){if(!e[n]){var o=typeof require=="function"&&require;if(!s&&o)return o(n,!0);if(r)return r(n,!0);throw new Error("Cannot find module '"+n+"'")}var u=t[n]={exports:{}};e[n][0](function(t){var r=e[n][1][t];return i(r?r:t)},u,u.exports)}return t[n].exports}var r=typeof require=="function"&&require;for(var s=0;s 0) { $('#keys .has-error').effect('shake', 500); return; } - $('#withdraw').click(); - $('#keys_updated').fadeIn(); - $.post("/" + g.user.username, $('#keys form').serializeObject(), function() { - return $.ajax({ - url: "" + g.api + "/wallets/hd/" + g.user.username, - type: 'DELETE' - }).done(function() { - return setTimeout(function() { - var data; - data = { - name: g.user.username, - extended_public_key: $('#pubkey').val(), - subchain_indexes: [0, 1] - }; - return $.post("" + g.api + "/wallets/hd", JSON.stringify(data)).always(function() { - return $.post("" + g.api + "/wallets/hd/" + g.user.username + "/addresses/derive").always(function() { - return getBalance(); - }); - }); - }, 300); - }); - }); - return false; + key = $('#key').val(); + proceed = true; + switch ($('#keytype').val()) { + case 'address': + $('#pubkey').val(key); + $('#privkey').val(''); + break; + case 'wif': + pubkey = bitcoin.ECPair.fromWIF(key).getAddress(); + privkey = CryptoJS.AES.encrypt(key, $('#phrase').val()); + $('#pubkey').val(pubkey); + $('#privkey').val(privkey); + break; + case 'aes': + try { + pubkey = bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt(key, $('#phrase').val()).toString(CryptoJS.enc.Utf8)).neutered().toString(); + $('#pubkey').val(pubkey); + $('#privkey').val(key); + } catch (_error) { + try { + pubkey = bitcoin.ECPair.fromWIF(CryptoJS.AES.decrypt(key, $('#phrase').val()).toString(CryptoJS.enc.Utf8)).getAddress(); + $('#pubkey').val(pubkey); + $('#privkey').val(key); + } catch (_error) { + proceed = false; + } + } + break; + case 'bip38': + wif = bip38().decrypt(key, $('#phrase').val()); + pubkey = bitcoin.ECPair.fromWIF(wif).getAddress(); + privkey = CryptoJS.AES.encrypt(wif, $('#phrase').val()); + $('#pubkey').val(pubkey); + $('#privkey, #key').val(privkey); + break; + case 'xpub': + $('#pubkey').val(key); + $('#privkey').val(''); + break; + case 'xprv': + pubkey = bitcoin.HDNode.fromBase58(key).neutered().toString(); + privkey = CryptoJS.AES.encrypt(key, $('#phrase').val()); + $('#pubkey').val(pubkey); + $('#privkey').val(privkey); + break; + case 'bip39': + master = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(key)).deriveHardened(44).deriveHardened(0); + pubkey = master.neutered().toString(); + privkey = CryptoJS.AES.encrypt(master.toString(), $('#phrase').val()); + $('#pubkey').val(pubkey); + $('#privkey').val(privkey); + } + if (proceed) { + return updateUser(); + } }); $('#withdrawal form input[type=button]').click(sendTransaction); $('#currency_toggle').click(function() { @@ -175,43 +237,52 @@ return $(this).val($(this).attr('max')); } }); - $('#password').keyup(function() {}); - $('#new_password').keyup(function() { - $('#privkey').val(CryptoJS.AES.encrypt(g.privkey, $(this).val())); + $('#phrase').keyup(function() { try { - return bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt(g.user.privkey, $(this).val()).toString(CryptoJS.enc.Utf8)); - } catch (_error) { - - } + CryptoJS.AES.decrypt(g.user.privkey, $(this).val()).toString(CryptoJS.enc.Utf8); + return $('#key').keyup(); + } catch (_error) {} }); $('#manage').click(function() { $('#withdrawal').hide(); $('#keys').show(); - $('#withdraw').toggle(g.balance > 0); - $('#manage').hide(); + $('#withdraw').hide().toggle(g.balance > 0 && g.user.privkey); return $('#privkey').val(g.user.privkey); }); $('#withdraw').click(function() { - $('#keys, #withdrawal').toggle(); - return $('#withdraw, #manage').toggle(); + $('#withdrawal').show(); + $('#amount').focus(); + $('#keys').hide(); + $('#withdraw').hide().toggle(g.balance > 0 && g.user.privkey); + $('#withdrawal form').validator('destroy'); + return $('#withdrawal form').validator({ + custom: validators, + errors: errors, + delay: 1200 + }); + }); + $('#cancel').click(function() { + return $('#keys').hide(); }); $('#backup').click(function() { - var pom, url; + var a, url; url = 'data:application/json;base64,' + btoa(JSON.stringify(g.user.privkey)); - pom = document.createElement('a'); - pom.setAttribute('href', url); - pom.setAttribute('download', 'coinos-wallet.aes.json'); - return pom.click(); + a = document.createElement('a'); + a.setAttribute('href', url); + a.setAttribute('download', 'coinos-wallet.aes.json'); + return a.click(); }); $('#generate').click(function() { - var key, mnemonic; - mnemonic = bip39.generateMnemonic(); - key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0); - g.privkey = key.toString(); - $('#pubkey').val(key.neutered().toString()).effect('highlight', {}, 2000).keyup(); - $('#privkey').val(''); - $('#new_password').parent().show(); - return $('#new_password').effect('shake', 500).focus(); + return bootbox.confirm('

Are you sure?

This will overwrite your existing wallet so make sure that you have the backup we sent to your email in case you want to restore it.

', function(result) { + var key, mnemonic; + if (result) { + mnemonic = bip39.generateMnemonic(); + key = bitcoin.HDNode.fromSeedBuffer(bip39.mnemonicToSeed(mnemonic)).deriveHardened(44).deriveHardened(0); + $('#key').val(key.toString()).effect('highlight', {}, 2000).keyup(); + $('#phrase').parent().show(); + return $('#phrase').effect('shake', 500).focus(); + } + }); }); return $('.close').on('click', function() { return $(this).closest('.alert').fadeOut(); @@ -221,7 +292,9 @@ getUser = function() { return $.getJSON("/" + ($('#username').val()) + ".json", function(user) { g.user = user; - $('#pubkey').val(user.pubkey).keyup(); + g.privkey = user.privkey; + $('#key').val(user.privkey || user.pubkey).keyup(); + $('#pubkey').val(user.pubkey); $('#privkey').val(user.privkey); $('#address').val(user.address); $('#unit').html(user.unit); @@ -240,20 +313,24 @@ createWallet = function() { return $.get("" + g.api + "/wallets", function(data) { - var _ref; + var params, _ref; if (_ref = g.user.username, __indexOf.call(data.wallet_names, _ref) >= 0) { return getBalance(); } else { + params = { + name: g.user.username + }; if (isBip32(g.user.pubkey)) { - data = { - name: g.user.username, - extended_public_key: g.user.pubkey, - subchain_indexes: [0, 1] - }; - return $.post("" + g.api + "/wallets/hd", JSON.stringify(data)).always(getBalance); + params.extended_public_key = g.user.pubkey; + params.subchain_indexes = [0, 1]; + return $.post("" + g.api + "/wallets/hd", JSON.stringify(params)).done(getBalance).fail(function() { + return $('.wallet').fadeIn(); + }); } else { - $('#balance').html(99); - return $('#amount').val(99); + params.addresses = [g.user.pubkey]; + return $.post("" + g.api + "/wallets", JSON.stringify(params)).done(getBalance).fail(function() { + return $('.wallet').fadeIn(); + }); } } }); @@ -269,18 +346,46 @@ $('#fiat').html("" + fiat + " " + g.user.currency); $('#amount').attr('max', g.balance); $('.wallet').fadeIn(); - if (g.balance > 0) { + if (g.balance > 0 && g.user.privkey) { + $('#keys').hide(); + $('#withdrawal form').validator('destroy'); + $('#withdrawal form').validator({ + custom: validators, + errors: errors, + delay: 1200 + }); $('#withdrawal').show(); return $('#amount').focus(); } else { - return $('#manage').click(); + return $('#withdraw').hide(); } }); }; + updateUser = function() { + var data; + data = $('#keys form').serializeObject(); + delete data['key']; + delete data['phrase']; + return $.post("/" + g.user.username, data, function() { + return $.ajax({ + url: "" + g.api + "/wallets/hd/" + g.user.username, + type: 'DELETE' + }).always(function() { + return $.ajax({ + url: "" + g.api + "/wallets/" + g.user.username, + type: 'DELETE' + }).always(function() { + $('#keys').hide(); + return createWallet(); + }); + }); + }); + }; + sendTransaction = function() { var dialog, params; - if (!g.master || typeof g.master.keyPair.d === 'undefined') { + if (!g.key || (typeof g.key.d === 'undefined' && typeof g.key.keyPair.d === 'undefined')) { $('#invalid_keys').fadeIn(); return $('#password').focus(); } else { @@ -332,13 +437,20 @@ return $.post("" + g.api + "/txs/new", JSON.stringify(params)).done(function(data) { var fee, total; data.pubkeys = []; - data.signatures = data.tosign.map(function(tosign, i) { - var key, path; - path = data.tx.inputs[i].hd_path.split('/'); - key = g.master.derive(path[1]).derive(path[2]); - data.pubkeys.push(key.keyPair.getPublicKeyBuffer().toString('hex')); - return key.keyPair.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex"); - }); + if (g.key instanceof bitcoin.ECPair) { + data.signatures = data.tosign.map(function(tosign, i) { + data.pubkeys.push(g.key.getPublicKeyBuffer().toString('hex')); + return g.key.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex"); + }); + } else { + data.signatures = data.tosign.map(function(tosign, i) { + var key, path; + path = data.tx.inputs[i].hd_path.split('/'); + key = g.key.derive(path[1]).derive(path[2]); + data.pubkeys.push(key.keyPair.getPublicKeyBuffer().toString('hex')); + return key.keyPair.sign(new buffer.Buffer(tosign, "hex")).toDER().toString("hex"); + }); + } g.data = data; amount = data.tx.outputs[0].value; fee = data.tx.fees; diff --git a/routes/users.js b/routes/users.js index 5348ee67..0eb97557 100644 --- a/routes/users.js +++ b/routes/users.js @@ -121,6 +121,8 @@ username: req.body.username, password: hash, email: req.body.email, + commission: req.body.commission, + unit: req.body.unit, pubkey: req.body.pubkey, privkey: req.body.privkey }, function() { @@ -192,9 +194,6 @@ if (req.body.password === '') { delete req.body.password; } - if (req.body.privkey === '') { - delete req.body.privkey; - } db.hmset("user:" + req.params.user, req.body, function() { if (req.body.password != null) { return bcrypt.hash(req.body.password, 12, function(err, hash) { diff --git a/src/routes/users.coffee b/src/routes/users.coffee index 8ff7f17a..4ae1200e 100644 --- a/src/routes/users.coffee +++ b/src/routes/users.coffee @@ -86,6 +86,8 @@ module.exports = (sessions) -> username: req.body.username, password: hash, email: req.body.email, + commission: req.body.commission + unit: req.body.unit, pubkey: req.body.pubkey, privkey: req.body.privkey , -> @@ -145,9 +147,6 @@ module.exports = (sessions) -> if req.body.password is '' delete req.body.password - if req.body.privkey is '' - delete req.body.privkey - db.hmset("user:"+req.params.user, req.body, -> if req.body.password? bcrypt.hash(req.body.password, 12, (err, hash) -> diff --git a/views/layout.html b/views/layout.html index a77f562f..f4a4e9c9 100644 --- a/views/layout.html +++ b/views/layout.html @@ -13,6 +13,7 @@ + diff --git a/views/sessions/new.html b/views/sessions/new.html index 97bfce17..686ee8ed 100644 --- a/views/sessions/new.html +++ b/views/sessions/new.html @@ -1,6 +1,5 @@ {{#yield-scripts}} - {{/yield-scripts}} @@ -8,7 +7,7 @@ {{#notice}}
-

Looks like you're trying to access a page or account that doesn't exist. We had an issue with our server in May that caused us to lose account data. Please feel free to contact us at info@coinos.io and we can help you get set up again or find what you're looking for.

+

Looks like you're trying to access a page or account that doesn't exist. Please contact us at info@coinos.io so we can help you get set up again or find what you're looking for.

{{/notice}} diff --git a/views/users/edit.html b/views/users/edit.html index 80b1e3c7..1a391b49 100644 --- a/views/users/edit.html +++ b/views/users/edit.html @@ -1,23 +1,5 @@ {{#yield-scripts}} - - - - - - - - - - - - - - - - - - {{/yield-scripts}} diff --git a/views/users/new.html b/views/users/new.html index c2b038d8..9bd22a25 100644 --- a/views/users/new.html +++ b/views/users/new.html @@ -1,8 +1,8 @@ {{#yield-scripts}} - + - + {{/yield-scripts}} @@ -34,6 +34,9 @@ + + + diff --git a/views/users/profile.html b/views/users/profile.html index 9fcdc03e..70f9acfc 100644 --- a/views/users/profile.html +++ b/views/users/profile.html @@ -1,6 +1,4 @@ {{#yield-scripts}} - - {{/yield-scripts}} diff --git a/views/users/wallet.html b/views/users/wallet.html index e2a1140e..0a546864 100644 --- a/views/users/wallet.html +++ b/views/users/wallet.html @@ -13,92 +13,86 @@ - -
0 (0 CAD)
- -
+ + + +