aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2016-01-03 14:24:06 -0800
committerAdam Soltys <[email protected]>2016-01-03 14:24:06 -0800
commit329798f482b466d79809e6537eba181b02b116be (patch)
tree426cb4fde002e24ebffc5a72eb0da6318a933c8b
parent365ec75087ebacbab4ed8e969542283079ffb4ae (diff)
downloadcoinos-server-329798f482b466d79809e6537eba181b02b116be.tar.gz
coinos-server-329798f482b466d79809e6537eba181b02b116be.zip
Wallet and registration process updates
-rw-r--r--bower.json3
-rw-r--r--public/img/village_books.pngbin0 -> 121526 bytes
-rw-r--r--public/js/calculator.coffee2
-rw-r--r--public/js/calculator.js2
-rw-r--r--public/js/register.coffee16
-rw-r--r--public/js/register.js22
-rw-r--r--public/js/wallet.coffee253
-rw-r--r--public/js/wallet.js296
-rw-r--r--routes/users.js5
-rw-r--r--src/routes/users.coffee5
-rw-r--r--views/layout.html1
-rw-r--r--views/sessions/new.html3
-rw-r--r--views/users/edit.html18
-rw-r--r--views/users/new.html7
-rw-r--r--views/users/profile.html2
-rw-r--r--views/users/wallet.html85
16 files changed, 438 insertions, 282 deletions
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
--- /dev/null
+++ b/public/img/village_books.png
Binary files 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()
+ key = $('#key').val()
+ proceed = true
- $.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)
- )
- )
+ 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)
- return false
+ 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('<h3>Are you sure?</h3> <p>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.</p>', (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<n.length;s++)i(n[s]);return i})({1:[function(require,module,exports){
(function(){(function() {
- var check_address, convertedAmount, createWallet, displayErrors, errors, g, getBalance, getExchangeRate, getUser, isBip32, multiplier, precision, sendTransaction, validators,
+ var check_address, convertedAmount, createWallet, displayErrors, errors, g, getBalance, getExchangeRate, getUser, isBip32, multiplier, precision, sendTransaction, updateUser, validators,
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
g = typeof exports !== "undefined" && exports !== null ? exports : this;
@@ -21,22 +21,47 @@
}
},
password: function(e) {
+ var s;
if (e.val() === '') {
return true;
}
- g.master = null;
+ g.key = null;
try {
- g.master = bitcoin.HDNode.fromBase58(CryptoJS.AES.decrypt($('#privkey').val(), e.val()).toString(CryptoJS.enc.Utf8));
- return $('#invalid_keys').fadeOut();
+ s = CryptoJS.AES.decrypt($('#privkey').val(), e.val()).toString(CryptoJS.enc.Utf8);
+ if (s[0] === 'x') {
+ g.key = bitcoin.HDNode.fromBase58(s);
+ } else {
+ g.key = bitcoin.ECPair.fromWIF(s);
+ }
} catch (_error) {
return false;
}
+ return $('#invalid_keys').fadeOut();
+ },
+ phrase: function(e) {
+ var s;
+ try {
+ s = CryptoJS.AES.decrypt(g.user.privkey, e.val()).toString(CryptoJS.enc.Utf8);
+ if (s[0] === 'x') {
+ bitcoin.HDNode.fromBase58(s);
+ } else {
+ bitcoin.ECPair.fromWIF(s);
+ }
+ } catch (_error) {
+ return false;
+ }
+ return $('#invalid_keys').fadeOut();
+ },
+ key: function(e) {
+ return $('#keytype').val() !== 'unknown';
}
};
errors = {
address: 'Invalid address.',
- password: 'Wrong password.'
+ password: 'Wrong password.',
+ phrase: 'Wrong password.',
+ key: 'Could not detect key type.'
};
$(function() {
@@ -50,20 +75,23 @@
});
$('form').validator({
custom: validators,
- errors: errors
+ errors: errors,
+ delay: 1200
});
$('[data-toggle=tooltip]').tooltip({
trigger: 'hover'
});
- $('#pubkey').keyup(function() {
- var key, val, _ref;
+ $('#key').keyup(function() {
+ var val, _ref;
val = $(this).val();
- $('#type').val('unknown');
+ $('#keytype').val('unknown');
+ $('#phrase').closest('.form-group').show();
switch (val[0]) {
case '1':
try {
bitcoin.address.fromBase58Check(val);
- return $('#type').val('address');
+ $('#keytype').val('address');
+ return $('#phrase').closest('.form-group').hide();
} catch (_error) {}
break;
case '5':
@@ -71,72 +99,106 @@
case 'K':
try {
bitcoin.ECPair.fromWIF(val);
- return $('#type').val('private_key');
+ return $('#keytype').val('wif');
} catch (_error) {}
break;
case 'U':
try {
- CryptoJS.AES.decrypt(val, $('#new_password').val());
- return $('#type').val('aes');
+ if (CryptoJS.AES.decrypt(val, $('#phrase').val()).toString(CryptoJS.enc.Utf8)) {
+ return $('#keytype').val('aes');
+ }
} catch (_error) {}
break;
case '6':
- try {
- key = bip38().decrypt(val, $('#new_password').val());
- bitcoin.ECPair.fromWIF(key);
- return $('#type').val('bip38');
- } catch (_error) {}
+ if (bip38().verify(val)) {
+ return $('#keytype').val('bip38');
+ }
break;
case 'x':
if ($(this).val()[3] === 'b') {
try {
bitcoin.HDNode.fromBase58(val);
- return $('#type').val('xpub');
+ $('#keytype').val('xpub');
+ return $('#phrase').closest('.form-group').hide();
} catch (_error) {}
} else {
try {
bitcoin.HDNode.fromBase58(val);
- return $('#type').val('xprv');
+ return $('#keytype').val('xprv');
} catch (_error) {}
}
break;
default:
- if ((_ref = val.split(' ').length) === 12 || _ref === 15 || _ref === 18 || _ref === 21 || _ref === 24) {
- if (bip39.validateMnemonic(val)) {
- return $('#type').val('bip39');
- }
+ if (((_ref = val.split(' ').length) === 12 || _ref === 15 || _ref === 18 || _ref === 21 || _ref === 24) && bip39.validateMnemonic(val)) {
+ return $('#keytype').val('bip39');
}
}
});
- $('#keys form input[type=button]').click(function() {
+ $('#key').blur(function() {
+ return $('#keys form').validator('validate');
+ });
+ $('#save').click(function() {
+ var key, master, privkey, proceed, pubkey, wif;
$('.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(), 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('<h3>Are you sure?</h3> <p>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.</p>', 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 @@
<script src="/bower/jquery-ui/jquery-ui.min.js"></script>
<script src="/bower/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="/bower/bootstrap-validator/dist/validator.min.js"></script>
+ <script src="/bower/bootbox.js/bootbox.js"></script>
<script src="/js/buffer.min.js"></script>
<script src="/js/bitcoinjs.min.js"></script>
<script src="/js/qrcode.js"></script>
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}}
<link rel="stylesheet" href="/css/signin.css" />
- <script src="/js/jquery-1.8.2.min.js"></script>
<script src="/js/signin.js"></script>
{{/yield-scripts}}
@@ -8,7 +7,7 @@
{{#notice}}
<div class="alert alert-danger">
- <p>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 <a href="mailto:[email protected]">[email protected]</a> and we can help you get set up again or find what you're looking for.</p>
+ <p>Looks like you're trying to access a page or account that doesn't exist. Please contact us at <a href="mailto:[email protected]">[email protected]</a> so we can help you get set up again or find what you're looking for.</p>
</div>
{{/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}}
-<script src="/js/jquery-1.8.2.min.js"></script>
-<script src="/js/jquery-ui.min.js"></script>
-<script src="/js/jquery.printElement.min.js"></script>
<script src="/js/check_password.js"></script>
-<script src="/js/bootstrap.min.js"></script>
-<script src="/js/jsbn.js"></script>
-<script src="/js/jsbn2.js"></script>
-<script src="/js/crypto-min.js"></script>
-<script src="/js/2.5.3-crypto-sha256.js"></script>
-<script src="/js/sha512.js"></script>
-<script src="/js/rfc1751.js"></script>
-<script src="/js/bitcoinjs-min-1.0.2.js"></script>
-<script src="/js/bitcoinjs-min.js"></script>
-<script src="/js/modsqrt.js"></script>
-<script src="/js/bip32.js"></script>
-<script src="/js/secure_random.js"></script>
-<script src="/js/qrcode.js"></script>
-<script src="/js/aes.js"></script>
-<script src="/js/key.js"></script>
<script src="/js/setup.js"></script>
{{/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}}
-<script src="/js/bip39.js"></script>
+<script src="/js/bip39.min.js"></script>
<script src="/js/check_email.js"></script>
<script src="/js/check_password.js"></script>
-<script src="/crypto-js/crypto-js.js"></script>
+<script src="/bower/crypto-js/crypto-js.js"></script>
<script src="/js/register.js"></script>
{{/yield-scripts}}
@@ -34,6 +34,9 @@
<input type="password" id="confirm" name="confirm" class="form-control" />
</div>
+ <input id="commission" name="commission" type="hidden" value="0" />
+ <input id="unit" name="unit" type="hidden" value="BTC" />
+
<input id="pubkey" name="pubkey" type="hidden" />
<input id="privkey" name="privkey" type="hidden" />
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}}
-<script src="/js/jquery-1.8.2.min.js"></script>
-<script src="/js/bootstrap.min.js"></script>
<script src="/js/profile.js"></script>
{{/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 @@
<input type="hidden" id="username" name="username" value="{{user}}" />
<input type="hidden" id="email" name="email" />
- <div id="keys_updated" class="alert alert-success" style="display: none">
- <button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- Wallet keys updated
- </div>
-
<div class="well">
<b id="balance" class="lead">0</b> <b id="unit" class="lead"></b>
(<b id="fiat" class="lead">0 CAD</b>)
<div class="pull-right">
- <button id="withdraw" type="button" class="btn btn-default" style="display: none" data-toggle="tooltip" data-placement="bottom" title="Withdraw">
+ <button id="withdraw" type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Withdraw">
<i class="fa fa-btc fa-lg"></i>
</button>
<button id="manage" type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Manage Keys">
- <i class="fa fa-key fa-lg"></i>
+ <i class="fa fa-cog fa-lg"></i>
</button>
- <button id="backup" type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Download Backup">
+ <button id="backup" type="button" class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Wallet Backup">
<i class="fa fa-download fa-lg"></i>
</button>
</div>
</div>
+ <div id="keys_updated" class="alert alert-success" style="display: none">
+ <button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ Wallet keys updated
+ </div>
+
+ <div id="transaction_sent" class="alert alert-success" style="display: none">
+ <button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
+ <p>Transaction sent!</p><br />
+ <button id="blockchain" class="btn btn-success">View on Blockchain</button>
+ </div>
+
<div id="keys" style="display: none">
- <h3>Keys</h3>
+ <h3>Settings</h3>
<form>
- <div class="form-group">
- <div class="input-group" style="width: 100%">
- <label for="new_password">Password:</label>
- <input id="new_password" name="new_password" type="password" class="form-control" />
- </div>
- </div>
-
<div class="form-group has-feedback">
<div class="input-group" style="width: 100%">
- <label for="pubkey">
+ <label for="key">
Address or Key:
- <button id="types" type="button" class="btn btn-default" title="Supported Key Types" data-toggle="popover" data-placement="right">
- <i class="fa fa-question"></i>
- </button>
</label>
- <textarea id="pubkey" name="pubkey" class="form-control" rows="3"></textarea>
+ <textarea id="key" name="key" class="form-control" rows="5" data-key="true"></textarea>
</div>
</div>
- <div class="form-group" style="display: none">
- <div class="input-group" style="width: 100%">
- <label for="privkey">Key:</label>
- <textarea id="privkey" name="privkey" class="form-control" rows="3"></textarea>
- </div>
- </div>
+ <input type="hidden" id="pubkey" name="pubkey" />
+ <input type="hidden" id="privkey" name="privkey" />
- <div class="form-group has-feedback">
+ <div class="form-group has-feedback" style="display: none">
<div class="input-group" style="width: 100%">
- <label for="type">Detected Type:</label>
- <select id="type" name="type" class="form-control" disabled>
+ <label for="keytype">Detected Type:</label>
+ <select id="keytype" name="keytype" class="form-control">
<option value="unknown">Unknown</option>
<option value="address">Bitcoin Address (starts with 1 or 3)</option>
- <option value="private_key">Private Key (starts with 5 or L)</option>
- <option value="xpub">HD Wallet pubkey (starts with xpub)</option>
- <option value="xprv">HD Wallet private key (starts with xprv)</option>
+ <option value="wif">Private Key (starts with 5 or L or K)</option>
+ <option value="xpub">HD Wallet Pubkey (starts with xpub)</option>
+ <option value="xprv">HD Wallet Private Key (starts with xprv)</option>
<option value="aes">AES Encrypted Private Key (starts with U)</option>
<option value="bip38">BIP38 Encrypted Private Key (starts with 6)</option>
- <option value="bip39">BIP39 Mnemonic (series of 12-24 english words)</option>
+ <option value="bip39">BIP39 Mnemonic (series of 12-24 words)</option>
</select>
</div>
</div>
- <button id="generate" class="btn btn-primary pull-right" type="button"><i class="fa fa-bolt"></i> Generate New HD Wallet</button>
- <br class="clear" />
- <input class="btn btn-primary" type="button" value="Save" />
+ <div class="form-group has-feedback">
+ <div class="input-group" style="width: 100%">
+ <label for="phrase">Passphrase:</label>
+ <input id="phrase" name="phrase" type="password" data-phrase="true" class="form-control" />
+ </div>
+ </div>
+
+ <button id="generate" class="btn btn-success pull-right" type="button"><i class="fa fa-bolt"></i> Create New Wallet</button>
+
+ <input id="save" class="btn btn-primary" type="button" value="Save" />
+ <input id="cancel" class="btn btn-default" type="button" value="Cancel" />
</form>
</div>
<div id="withdrawal" style="display: none">
<h3>Send Payment / Withdrawal</h3>
- <div id="transaction_sent" class="alert alert-success" style="display: none">
- <button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- <p>Transaction sent!</p><br />
- <button id="blockchain" class="btn btn-success">View on Blockchain</button>
- </div>
-
<div id="invalid_keys" class="alert alert-danger" style="display: none">
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></button>
- Invalid password or problem with private key
+ Invalid passphrase or problem with private key
</div>
<form>
@@ -137,7 +131,7 @@
<div class="form-group has-feedback">
<div class="input-group" style="width: 100%">
- <label for="password">Password:</label>
+ <label for="password">Passphrase:</label>
<input id="password" name="password" type="password" class="form-control" data-password="true" required />
</div>
<div class="help-block with-errors"></div>
@@ -154,5 +148,4 @@
<label>Fee:</label> <span class="fee"></span><br />
<label>To:</label> <span class="address"></span>
</div>
-
</div>