From 30e6b044cf292b2630967156bd5f9bb1f987dcb9 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 19 Jan 2012 17:25:25 +0100 Subject: [PATCH 01/48] move urldecode to the executable file --- client/electrum | 6 ++++++ client/wallet.py | 3 --- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/client/electrum b/client/electrum index 67a9f4c56..26c2d2696 100755 --- a/client/electrum +++ b/client/electrum @@ -23,6 +23,12 @@ from optparse import OptionParser from wallet import Wallet from interface import Interface + +# URL decode +_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) +urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) + + if __name__ == '__main__': known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import'] diff --git a/client/wallet.py b/client/wallet.py index 52ec655ab..9d3773ade 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -151,9 +151,6 @@ def int_to_hex(i, length=1): return s.decode('hex')[::-1].encode('hex') -# URL decode -_ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) -urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) # AES EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) From de19da75d264105505b32d7142d6115d2b62d178 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 19 Jan 2012 17:39:24 +0100 Subject: [PATCH 02/48] minor fix --- client/wallet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/wallet.py b/client/wallet.py index 9d3773ade..ddf6c2045 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -284,7 +284,7 @@ class Wallet: seed = "%032x"%ecdsa.util.randrange( pow(2,128) ) self.init_mpk(seed) # encrypt - self.seed = wallet.pw_encode( seed, password ) + self.seed = self.pw_encode( seed, password ) def init_mpk(self,seed): # public key From 58d73c66e06e4637810ed1bac736f2fc481c8ed0 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 20 Jan 2012 17:21:29 +0100 Subject: [PATCH 03/48] bugfix + verifymessage --- client/electrum | 9 +++++++-- client/wallet.py | 14 +++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/client/electrum b/client/electrum index 26c2d2696..858226845 100755 --- a/client/electrum +++ b/client/electrum @@ -66,15 +66,20 @@ if __name__ == '__main__': else: params = [] cmd = 'gui' - amount = '' - label = '' + amount = label = signature = signer = '' for p in params: k,v = p.split('=') v = urldecode(v) if k=='amount': amount = v elif k=='label': label = v + elif k =='signature': signature = v + elif k =='signer': signer = v else: print k,v + if signature: + message = p.replace('signer=%s'%signer,'').replace('signature=%s'%signature,'') + wallet.verify_message(signer, signature,message) + gui.set_send_tab(address, amount, label) gui.main() diff --git a/client/wallet.py b/client/wallet.py index ddf6c2045..ada85a8ba 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -345,6 +345,10 @@ class Wallet: return pk + def verify_message(self, signing_address, signature, message): + """ recover public key from signature; """ + pass + def create_new_address2(self, for_change): """ Publickey(type,n) = Master_public_key + H(n|S|type)*point """ @@ -631,19 +635,19 @@ class Wallet: def mktx(self, to_address, amount, label, password, fee=None): if not self.is_valid(to_address): raise BaseException("Invalid address") - inputs, total, fee = wallet.choose_tx_inputs( amount, fee ) + inputs, total, fee = self.choose_tx_inputs( amount, fee ) if not inputs: raise BaseException("Not enough funds") - outputs = wallet.choose_tx_outputs( to_address, amount, fee, total ) - s_inputs = wallet.sign_inputs( inputs, outputs, password ) + outputs = self.choose_tx_outputs( to_address, amount, fee, total ) + s_inputs = self.sign_inputs( inputs, outputs, password ) tx = filter( raw_tx( s_inputs, outputs ) ) if to_address not in self.addressbook: self.addressbook.append(to_address) if label: tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex') - wallet.labels[tx_hash] = label - wallet.save() + self.labels[tx_hash] = label + self.save() return tx def sendtx(self, tx): From 2038de230208ab26b3fa372d72d2a779d615e1d6 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 21 Jan 2012 09:44:15 +0100 Subject: [PATCH 04/48] do not save wallet during recovery dialog --- client/gui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index 420fcb4df..843f3562f 100644 --- a/client/gui.py +++ b/client/gui.py @@ -124,6 +124,7 @@ def init_wallet(wallet): else: # ask for the server. + wallet.interface.get_servers() run_network_dialog( wallet, parent=None ) # ask for seed and gap. @@ -370,8 +371,9 @@ def run_network_dialog( wallet, parent ): if host!= wallet.interface.host or port!=wallet.interface.port: wallet.interface.host = host wallet.interface.set_port( port ) - wallet.save() wallet.interface.is_connected = False + if parent: + wallet.save() From 4788c6e31b591c2f3a27a57477d10b1ffc906456 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 1 Feb 2012 20:27:03 +0100 Subject: [PATCH 05/48] new commands: signmessage and verifymessage. --- client/electrum | 33 +++++++++++++-------- client/wallet.py | 74 +++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 88 insertions(+), 19 deletions(-) diff --git a/client/electrum b/client/electrum index 858226845..c16b8ae2c 100755 --- a/client/electrum +++ b/client/electrum @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re,sys +import re, sys, getpass from optparse import OptionParser @@ -30,7 +30,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -65,20 +65,21 @@ if __name__ == '__main__': params = o[1].split('&') else: params = [] - cmd = 'gui' + amount = label = signature = signer = '' for p in params: k,v = p.split('=') - v = urldecode(v) - if k=='amount': amount = v - elif k=='label': label = v - elif k =='signature': signature = v - elif k =='signer': signer = v + uv = urldecode(v) + if k=='amount': amount = uv + elif k=='label': label = uv + elif k =='signature': signature = uv + elif k =='signer': signer = uv else: print k,v - + if k in ['signer','signature']: + cmd = cmd.replace('&%s=%s'%(k,v),'') + if signature: - message = p.replace('signer=%s'%signer,'').replace('signature=%s'%signature,'') - wallet.verify_message(signer, signature,message) + wallet.verify_message(signer, signature, cmd ) gui.set_send_tab(address, amount, label) @@ -153,7 +154,7 @@ if __name__ == '__main__': wallet.save() # commands needing password - if cmd in ['payto', 'password', 'mktx', 'seed', 'import' ] or ( cmd=='addresses' and options.show_keys): + if cmd in ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): password = getpass.getpass('Password:') if wallet.use_encryption else None # check password try: @@ -334,3 +335,11 @@ if __name__ == '__main__': else: print "error: mismatch" + elif cmd == 'signmessage': + address, message = args[1:3] + print wallet.sign_message(address, message, password) + + elif cmd == 'verifymessage': + address, signature, message = args[1:4] + print wallet.verify_message(address, signature, message) + diff --git a/client/wallet.py b/client/wallet.py index ada85a8ba..eb8abc58a 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -17,8 +17,7 @@ # along with this program. If not, see . -import sys, base64, os, re, hashlib, socket, getpass, copy, operator, ast, random -from decimal import Decimal +import sys, base64, os, re, hashlib, copy, operator, ast try: import ecdsa @@ -151,7 +150,6 @@ def int_to_hex(i, length=1): return s.decode('hex')[::-1].encode('hex') - # AES EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e)) @@ -343,11 +341,73 @@ class Wallet: pk = number_to_string(secexp,order) return pk + + def sign_message(self, address, message, password): + private_key = ecdsa.SigningKey.from_string( self.get_private_key2(address, password), curve = SECP256k1 ) + public_key = private_key.get_verifying_key() + signature = private_key.sign_digest( Hash( message ), sigencode = ecdsa.util.sigencode_string ) + assert public_key.verify_digest( signature, Hash( message ), sigdecode = ecdsa.util.sigdecode_string) + for i in range(4): + sig = base64.b64encode( chr(27+i) + signature ) + if self.verify_message( address, sig, message): + return sig + else: + raise BaseException("error: cannot sign message") + - - def verify_message(self, signing_address, signature, message): - """ recover public key from signature; """ - pass + def verify_message(self, address, signature, message): + """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """ + + from ecdsa import numbertheory, ellipticcurve, util + import msqr + curve = curve_secp256k1 + G = generator_secp256k1 + order = G.order() + + sig = base64.b64decode(signature) + if len(sig) != 65: raise BaseException("error") + recid = ord(sig[0]) - 27 + # print "recid", recid + + # extract r,s from signature + r,s = util.sigdecode_string(sig[1:], order) + + # 1.1 + x = r + (recid/2) * order + + # 1.3 + alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p() + beta = msqr.modular_sqrt(alpha, curve.p()) + y = beta if (beta - recid) % 2 == 0 else curve.p() - beta + + # 1.4 the constructor checks that nR is at infinity + try: + R = ellipticcurve.Point(curve, x, y, order) + except: + print "not in curve" + return False + + # 1.5 compute e from message: + h = Hash(message) + e = string_to_number(h) + minus_e = -e % order + + # 1.6 compute Q = r^-1 (sR - eG) + inv_r = numbertheory.inverse_mod(r,order) + Q = inv_r * ( s * R + minus_e * G ) + public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 ) + + # check that Q is the public key + try: + public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) + except: + print "wrong key" + return False + + # check that we get the original signing address + addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) + # print addr + return address == addr def create_new_address2(self, for_change): From 900c2c8557c08c1cca0dd5951d33122fa67b8228 Mon Sep 17 00:00:00 2001 From: thomasv Date: Wed, 1 Feb 2012 21:02:19 +0100 Subject: [PATCH 06/48] square root modulo --- client/msqr.py | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 client/msqr.py diff --git a/client/msqr.py b/client/msqr.py new file mode 100644 index 000000000..da3c6fdef --- /dev/null +++ b/client/msqr.py @@ -0,0 +1,94 @@ +# from http://eli.thegreenplace.net/2009/03/07/computing-modular-square-roots-in-python/ + +def modular_sqrt(a, p): + """ Find a quadratic residue (mod p) of 'a'. p + must be an odd prime. + + Solve the congruence of the form: + x^2 = a (mod p) + And returns x. Note that p - x is also a root. + + 0 is returned is no square root exists for + these a and p. + + The Tonelli-Shanks algorithm is used (except + for some simple cases in which the solution + is known from an identity). This algorithm + runs in polynomial time (unless the + generalized Riemann hypothesis is false). + """ + # Simple cases + # + if legendre_symbol(a, p) != 1: + return 0 + elif a == 0: + return 0 + elif p == 2: + return p + elif p % 4 == 3: + return pow(a, (p + 1) / 4, p) + + # Partition p-1 to s * 2^e for an odd s (i.e. + # reduce all the powers of 2 from p-1) + # + s = p - 1 + e = 0 + while s % 2 == 0: + s /= 2 + e += 1 + + # Find some 'n' with a legendre symbol n|p = -1. + # Shouldn't take long. + # + n = 2 + while legendre_symbol(n, p) != -1: + n += 1 + + # Here be dragons! + # Read the paper "Square roots from 1; 24, 51, + # 10 to Dan Shanks" by Ezra Brown for more + # information + # + + # x is a guess of the square root that gets better + # with each iteration. + # b is the "fudge factor" - by how much we're off + # with the guess. The invariant x^2 = ab (mod p) + # is maintained throughout the loop. + # g is used for successive powers of n to update + # both a and b + # r is the exponent - decreases with each update + # + x = pow(a, (s + 1) / 2, p) + b = pow(a, s, p) + g = pow(n, s, p) + r = e + + while True: + t = b + m = 0 + for m in xrange(r): + if t == 1: + break + t = pow(t, 2, p) + + if m == 0: + return x + + gs = pow(g, 2 ** (r - m - 1), p) + g = (gs * gs) % p + x = (x * gs) % p + b = (b * g) % p + r = m + +def legendre_symbol(a, p): + """ Compute the Legendre symbol a|p using + Euler's criterion. p is a prime, a is + relatively prime to p (if p divides + a, then a|p = 0) + + Returns 1 if a has a square root modulo + p, -1 otherwise. + """ + ls = pow(a, (p - 1) / 2, p) + return -1 if ls == p - 1 else ls From e1ccfe1b13a91c7f70f2b46dca5fe3d13aaaa4a9 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Thu, 2 Feb 2012 00:13:35 +0100 Subject: [PATCH 07/48] no session for sign/verify message --- client/electrum | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/electrum b/client/electrum index c16b8ae2c..dde7e6bc5 100755 --- a/client/electrum +++ b/client/electrum @@ -148,7 +148,7 @@ if __name__ == '__main__': cmd = 'help' # open session - if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress']: + if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage']: wallet.interface.new_session(wallet.all_addresses(), wallet.electrum_version) wallet.update() wallet.save() From a6fa869a26ac32c21c6e45471ade5ac0d59a092e Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 15:26:10 +0100 Subject: [PATCH 08/48] add magic header for compatibility with the official Bitcoin client --- client/wallet.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/client/wallet.py b/client/wallet.py index eb8abc58a..43e8bdac1 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -218,7 +218,6 @@ from version import ELECTRUM_VERSION, SEED_VERSION - class Wallet: def __init__(self, interface): @@ -342,11 +341,14 @@ class Wallet: pk = number_to_string(secexp,order) return pk + def msg_magic(self, message): + return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message + def sign_message(self, address, message, password): private_key = ecdsa.SigningKey.from_string( self.get_private_key2(address, password), curve = SECP256k1 ) public_key = private_key.get_verifying_key() - signature = private_key.sign_digest( Hash( message ), sigencode = ecdsa.util.sigencode_string ) - assert public_key.verify_digest( signature, Hash( message ), sigdecode = ecdsa.util.sigdecode_string) + signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string ) + assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string) for i in range(4): sig = base64.b64encode( chr(27+i) + signature ) if self.verify_message( address, sig, message): @@ -357,20 +359,17 @@ class Wallet: def verify_message(self, address, signature, message): """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """ - from ecdsa import numbertheory, ellipticcurve, util import msqr curve = curve_secp256k1 G = generator_secp256k1 order = G.order() + # extract r,s from signature sig = base64.b64decode(signature) if len(sig) != 65: raise BaseException("error") - recid = ord(sig[0]) - 27 - # print "recid", recid - - # extract r,s from signature r,s = util.sigdecode_string(sig[1:], order) + recid = ord(sig[0]) - 27 # 1.1 x = r + (recid/2) * order @@ -388,7 +387,7 @@ class Wallet: return False # 1.5 compute e from message: - h = Hash(message) + h = Hash( self.msg_magic( message ) ) e = string_to_number(h) minus_e = -e % order From 46d5f7dd6e941af22cde869f68999ad8975afb34 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 17:21:02 +0100 Subject: [PATCH 09/48] sig warning + lookup from default url --- client/electrum | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/client/electrum b/client/electrum index c16b8ae2c..ccecfd091 100755 --- a/client/electrum +++ b/client/electrum @@ -73,15 +73,25 @@ if __name__ == '__main__': if k=='amount': amount = uv elif k=='label': label = uv elif k =='signature': signature = uv - elif k =='signer': signer = uv + elif k =='signer': + signer = uv + if not wallet.is_valid(signer): + print signer + print "trying canonical URL" + import urllib + url = 'http://'+signer+'/bitcoin.id' + print url + signer = urllib.urlopen(url).read().strip() + print repr(signer) + else: print k,v if k in ['signer','signature']: cmd = cmd.replace('&%s=%s'%(k,v),'') - if signature: - wallet.verify_message(signer, signature, cmd ) - gui.set_send_tab(address, amount, label) + if signature: + if not wallet.verify_message(signer, signature, cmd ): + gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nPay at your own risks!') gui.main() wallet.save() From 85c16d10d7b9ba5405e47ff0c1abd2a390547946 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 17:42:00 +0100 Subject: [PATCH 10/48] error handling --- client/electrum | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/electrum b/client/electrum index 19622532b..781dbbb2e 100755 --- a/client/electrum +++ b/client/electrum @@ -76,13 +76,13 @@ if __name__ == '__main__': elif k =='signer': signer = uv if not wallet.is_valid(signer): - print signer - print "trying canonical URL" import urllib url = 'http://'+signer+'/bitcoin.id' - print url - signer = urllib.urlopen(url).read().strip() - print repr(signer) + try: + signer = urllib.urlopen(url).read().strip() + except: + # no need to display something, the signature verification will fail anyway + pass else: print k,v if k in ['signer','signature']: From 9d722db1f410b267526426b15c7c4c9e0c50423e Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 17:44:08 +0100 Subject: [PATCH 11/48] s/signer/identity --- client/electrum | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/client/electrum b/client/electrum index 781dbbb2e..1a19d29f3 100755 --- a/client/electrum +++ b/client/electrum @@ -66,31 +66,31 @@ if __name__ == '__main__': else: params = [] - amount = label = signature = signer = '' + amount = label = signature = identity = '' for p in params: k,v = p.split('=') uv = urldecode(v) if k=='amount': amount = uv elif k=='label': label = uv elif k =='signature': signature = uv - elif k =='signer': - signer = uv - if not wallet.is_valid(signer): + elif k =='identity': + identity = uv + if not wallet.is_valid(identity): import urllib - url = 'http://'+signer+'/bitcoin.id' + url = 'http://'+identity+'/bitcoin.id' try: - signer = urllib.urlopen(url).read().strip() + identity = urllib.urlopen(url).read().strip() except: # no need to display something, the signature verification will fail anyway pass else: print k,v - if k in ['signer','signature']: + if k in ['identity','signature']: cmd = cmd.replace('&%s=%s'%(k,v),'') gui.set_send_tab(address, amount, label) if signature: - if not wallet.verify_message(signer, signature, cmd ): + if not wallet.verify_message(identity, signature, cmd ): gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nPay at your own risks!') gui.main() From 662501b68ff33657fb8c93e481b00327dca5fcaf Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 18:44:50 +0100 Subject: [PATCH 12/48] add payto identity field to gui --- client/electrum | 14 ++++++++------ client/gui.py | 37 +++++++++++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/client/electrum b/client/electrum index 1a19d29f3..9731e130b 100755 --- a/client/electrum +++ b/client/electrum @@ -75,22 +75,24 @@ if __name__ == '__main__': elif k =='signature': signature = uv elif k =='identity': identity = uv - if not wallet.is_valid(identity): + if wallet.is_valid(identity): + signing_address = identity + else: import urllib url = 'http://'+identity+'/bitcoin.id' try: - identity = urllib.urlopen(url).read().strip() + signing_address = urllib.urlopen(url).read().strip() except: - # no need to display something, the signature verification will fail anyway - pass + # no need to display anything since verification will fail + signing_address = '' else: print k,v if k in ['identity','signature']: cmd = cmd.replace('&%s=%s'%(k,v),'') - gui.set_send_tab(address, amount, label) + gui.set_send_tab(address, amount, label, identity) if signature: - if not wallet.verify_message(identity, signature, cmd ): + if not wallet.verify_message(signing_address, signature, cmd ): gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nPay at your own risks!') gui.main() diff --git a/client/gui.py b/client/gui.py index 843f3562f..16736c76f 100644 --- a/client/gui.py +++ b/client/gui.py @@ -646,6 +646,8 @@ class BitcoinGUI: payto_label.set_size_request(100,10) payto_label.show() payto.pack_start(payto_label, False) + payto_id = gtk.Label('') + payto.pack_start(payto_id, False) payto_entry = gtk.Entry() payto_entry.set_size_request(350, 26) payto_entry.show() @@ -674,10 +676,6 @@ class BitcoinGUI: amount_box.pack_start(amount_entry, False) vbox.pack_start(amount_box, False, False, 5) - send_button = gtk.Button("Send") - send_button.show() - amount_box.pack_start(send_button, False, False, 5) - self.fee_box = fee_box = gtk.HBox() fee_label = gtk.Label('Fee:') fee_label.set_size_request(100,10) @@ -687,9 +685,22 @@ class BitcoinGUI: fee_entry.set_has_frame(False) fee_entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee")) fee_box.pack_start(fee_entry, False) + vbox.pack_start(fee_box, False, False, 5) + + end_box = gtk.HBox() + empty_label = gtk.Label('') + empty_label.set_size_request(100,10) + end_box.pack_start(empty_label, False) + send_button = gtk.Button("Send") + send_button.show() + end_box.pack_start(send_button, False, False, 5) + clear_button = gtk.Button("Clear") + clear_button.show() + end_box.pack_start(clear_button, False, False, 5) send_button.connect("clicked", self.do_send, (payto_entry, label_entry, amount_entry, fee_entry)) - vbox.pack_start(fee_box, False, False, 5) + clear_button.connect("clicked", self.do_clear, (payto_entry, label_entry, amount_entry, fee_entry)) + vbox.pack_start(end_box, False, False, 5) self.user_fee = False @@ -718,13 +729,18 @@ class BitcoinGUI: fee_entry.connect('changed', entry_changed, True) self.payto_entry = payto_entry + self.payto_fee_entry = fee_entry + self.payto_id = payto_id self.payto_amount_entry = amount_entry self.payto_label_entry = label_entry self.add_tab(page, 'Send') - def set_send_tab(self, address, amount, label): + def set_send_tab(self, address, amount, label, identity): self.notebook.set_current_page(1) self.payto_entry.set_text(address) + if identity: + self.payto_id.set_text(address + '@' + identity) + self.payto_entry.set_visible(False) self.payto_label_entry.set_text(label) self.payto_amount_entry.set_text(amount) @@ -741,6 +757,15 @@ class BitcoinGUI: self.info = tv.get_buffer() self.add_tab(page, 'Wall') + def do_clear(self, w, data): + self.payto_entry.set_text('') + self.payto_entry.set_visible(True) + self.payto_id.set_visible(False) + self.payto_label_entry.set_text('') + self.payto_amount_entry.set_text('') + self.payto_fee_entry.set_text('') + + def do_send(self, w, data): payto_entry, label_entry, amount_entry, fee_entry = data From 53cddb7af0ffa15784ef20af7e82fa462c3aadb7 Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 18:56:57 +0100 Subject: [PATCH 13/48] move fee box --- client/gui.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/client/gui.py b/client/gui.py index 16736c76f..aca1a1aa1 100644 --- a/client/gui.py +++ b/client/gui.py @@ -678,14 +678,14 @@ class BitcoinGUI: self.fee_box = fee_box = gtk.HBox() fee_label = gtk.Label('Fee:') - fee_label.set_size_request(100,10) - fee_box.pack_start(fee_label, False) + fee_box.set_size_request(220,10) fee_entry = gtk.Entry() - fee_entry.set_size_request(120, 26) + fee_entry.set_size_request(60, 26) fee_entry.set_has_frame(False) fee_entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee")) - fee_box.pack_start(fee_entry, False) - vbox.pack_start(fee_box, False, False, 5) + fee_box.pack_end(fee_entry, False) + fee_box.pack_end(fee_label, False) + amount_box.pack_start(fee_box, False, False, 5) end_box = gtk.HBox() empty_label = gtk.Label('') From d71d8d80aefd6e7d237ccd78d1ce5fa03ae83bae Mon Sep 17 00:00:00 2001 From: thomasv Date: Thu, 2 Feb 2012 19:02:48 +0100 Subject: [PATCH 14/48] syntax --- client/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index aca1a1aa1..b399e5d39 100644 --- a/client/gui.py +++ b/client/gui.py @@ -739,7 +739,7 @@ class BitcoinGUI: self.notebook.set_current_page(1) self.payto_entry.set_text(address) if identity: - self.payto_id.set_text(address + '@' + identity) + self.payto_id.set_text(identity + ' [' + address + ']') self.payto_entry.set_visible(False) self.payto_label_entry.set_text(label) self.payto_amount_entry.set_text(amount) From 382abc54a86c3e5d48a51ac707c2b7bfdc020763 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 3 Feb 2012 08:02:12 +0100 Subject: [PATCH 15/48] aliases --- client/electrum | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) diff --git a/client/electrum b/client/electrum index 9731e130b..7ed6dfcba 100755 --- a/client/electrum +++ b/client/electrum @@ -19,7 +19,6 @@ import re, sys, getpass from optparse import OptionParser - from wallet import Wallet from interface import Interface @@ -29,6 +28,26 @@ _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) +def alias(x): + import urllib + if wallet.is_valid(x): + xx = x + else: + m = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', x) + if m: + url = 'http://' + m.group(2) + '/bitcoin.id/' + m.group(1) + else: + url = 'http://' + x + '/bitcoin.id' + print url + try: + xx = urllib.urlopen(url).read().strip() + except: + xx = '' + if not wallet.is_valid(xx): + xx = '' + return xx + + if __name__ == '__main__': known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage'] @@ -58,9 +77,11 @@ if __name__ == '__main__': import gui gui.init_wallet(wallet) gui = gui.BitcoinGUI(wallet) + if re.match('^bitcoin:', cmd): + o = cmd[8:].split('?') - address = o[0] + address = alias(o[0]) if len(o)>1: params = o[1].split('&') else: @@ -70,23 +91,15 @@ if __name__ == '__main__': for p in params: k,v = p.split('=') uv = urldecode(v) - if k=='amount': amount = uv - elif k=='label': label = uv - elif k =='signature': signature = uv - elif k =='identity': + if k == 'amount': amount = uv + elif k == 'label': label = uv + elif k == 'signature': signature = uv + elif k == 'identity': identity = uv - if wallet.is_valid(identity): - signing_address = identity - else: - import urllib - url = 'http://'+identity+'/bitcoin.id' - try: - signing_address = urllib.urlopen(url).read().strip() - except: - # no need to display anything since verification will fail - signing_address = '' - - else: print k,v + signing_address = alias(identity) + else: + print k,v + if k in ['identity','signature']: cmd = cmd.replace('&%s=%s'%(k,v),'') From ac9d9048dd4f9b3429840f4b91b283935f6f55f5 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 11:48:09 +0100 Subject: [PATCH 16/48] simplify signatures --- client/electrum | 26 ++++++++++------- client/gui.py | 76 +++++++++++++++++++++++++++++++----------------- client/wallet.py | 27 +++++------------ 3 files changed, 73 insertions(+), 56 deletions(-) diff --git a/client/electrum b/client/electrum index 7ed6dfcba..4b756d66a 100755 --- a/client/electrum +++ b/client/electrum @@ -93,20 +93,21 @@ if __name__ == '__main__': uv = urldecode(v) if k == 'amount': amount = uv elif k == 'label': label = uv - elif k == 'signature': signature = uv - elif k == 'identity': - identity = uv + elif k == 'signature': + identity, signature = uv.split(':') signing_address = alias(identity) + cmd = cmd.replace('&%s=%s'%(k,v),'') else: print k,v - if k in ['identity','signature']: - cmd = cmd.replace('&%s=%s'%(k,v),'') - - gui.set_send_tab(address, amount, label, identity) if signature: - if not wallet.verify_message(signing_address, signature, cmd ): - gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nPay at your own risks!') + try: + wallet.verify_message(signing_address, signature, cmd ) + except: + gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') + signature = identity = '' + + gui.set_send_tab(address, amount, label, identity) gui.main() wallet.save() @@ -366,5 +367,10 @@ if __name__ == '__main__': elif cmd == 'verifymessage': address, signature, message = args[1:4] - print wallet.verify_message(address, signature, message) + try: + wallet.verify_message(address, signature, message) + print True + except: + print False + diff --git a/client/gui.py b/client/gui.py index b399e5d39..be88042ed 100644 --- a/client/gui.py +++ b/client/gui.py @@ -570,7 +570,7 @@ class BitcoinGUI: self.window.add(vbox) self.window.show_all() - self.fee_box.hide() + #self.fee_box.hide() self.context_id = self.status_bar.get_context_id("statusbar") self.update_status_bar() @@ -637,26 +637,29 @@ class BitcoinGUI: def create_send_tab(self): + + # cases: + # no alias bitcoin:address + # alias bitcoin:name@domain + # alias + signature bitcoin:address?id=name@domain&sig=signature page = vbox = gtk.VBox() page.show() payto = gtk.HBox() payto_label = gtk.Label('Pay to:') - payto_label.set_size_request(100,10) + payto_label.set_size_request(100,-1) payto_label.show() payto.pack_start(payto_label, False) - payto_id = gtk.Label('') - payto.pack_start(payto_id, False) payto_entry = gtk.Entry() payto_entry.set_size_request(350, 26) payto_entry.show() payto.pack_start(payto_entry, False) vbox.pack_start(payto, False, False, 5) - + label = gtk.HBox() label_label = gtk.Label('Label:') - label_label.set_size_request(100,10) + label_label.set_size_request(100,-1) label_label.show() label.pack_start(label_label, False) label_entry = gtk.Entry() @@ -678,18 +681,17 @@ class BitcoinGUI: self.fee_box = fee_box = gtk.HBox() fee_label = gtk.Label('Fee:') - fee_box.set_size_request(220,10) + fee_label.set_size_request(100,-1) + fee_box.pack_start(fee_label, False) fee_entry = gtk.Entry() fee_entry.set_size_request(60, 26) - fee_entry.set_has_frame(False) - fee_entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee")) - fee_box.pack_end(fee_entry, False) - fee_box.pack_end(fee_label, False) - amount_box.pack_start(fee_box, False, False, 5) + fee_box.pack_start(fee_entry, False) end_box = gtk.HBox() + end_box.pack_start(fee_box, False, False, 5) + empty_label = gtk.Label('') - empty_label.set_size_request(100,10) + empty_label.set_size_request(100,-1) end_box.pack_start(empty_label, False) send_button = gtk.Button("Send") send_button.show() @@ -697,11 +699,18 @@ class BitcoinGUI: clear_button = gtk.Button("Clear") clear_button.show() end_box.pack_start(clear_button, False, False, 5) - send_button.connect("clicked", self.do_send, (payto_entry, label_entry, amount_entry, fee_entry)) clear_button.connect("clicked", self.do_clear, (payto_entry, label_entry, amount_entry, fee_entry)) + vbox.pack_start(end_box, False, False, 5) + # display this line only if there is a signature + payto_sig = gtk.HBox() + payto_sig_id = gtk.Label('') + payto_sig.pack_start(payto_sig_id, False) + vbox.pack_start(payto_sig, True, True, 5) + + self.user_fee = False def entry_changed( entry, is_fee ): @@ -709,7 +718,8 @@ class BitcoinGUI: fee = numbify(fee_entry) if not is_fee: fee = None if amount is None: - self.fee_box.hide(); return + #self.fee_box.hide(); + return inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee ) if not is_fee: fee_entry.set_text( str( Decimal( fee ) / 100000000 ) ) @@ -730,19 +740,35 @@ class BitcoinGUI: self.payto_entry = payto_entry self.payto_fee_entry = fee_entry - self.payto_id = payto_id + self.payto_sig_id = payto_sig_id + self.payto_sig = payto_sig self.payto_amount_entry = amount_entry self.payto_label_entry = label_entry self.add_tab(page, 'Send') + def set_frozen(self,entry,frozen): + if frozen: + entry.set_editable(False) + entry.set_has_frame(False) + entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#eeeeee")) + else: + entry.set_editable(True) + entry.set_has_frame(True) + entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) + + def set_send_tab(self, address, amount, label, identity): self.notebook.set_current_page(1) self.payto_entry.set_text(address) - if identity: - self.payto_id.set_text(identity + ' [' + address + ']') - self.payto_entry.set_visible(False) self.payto_label_entry.set_text(label) self.payto_amount_entry.set_text(amount) + if identity: + self.set_frozen(self.payto_entry,True) + self.set_frozen(self.payto_amount_entry,True) + self.set_frozen(self.payto_label_entry,True) + self.payto_sig_id.set_text( ' This transaction URI was signed by ' + identity ) + else: + self.payto_sig.set_visible(False) def create_about_tab(self): page = gtk.VBox() @@ -758,12 +784,10 @@ class BitcoinGUI: self.add_tab(page, 'Wall') def do_clear(self, w, data): - self.payto_entry.set_text('') - self.payto_entry.set_visible(True) - self.payto_id.set_visible(False) - self.payto_label_entry.set_text('') - self.payto_amount_entry.set_text('') - self.payto_fee_entry.set_text('') + self.payto_sig.set_visible(False) + for entry in [self.payto_entry,self.payto_amount_entry,self.payto_label_entry]: + self.set_frozen(entry,False) + entry.set_text('') def do_send(self, w, data): @@ -809,7 +833,7 @@ class BitcoinGUI: label_entry.set_text("") amount_entry.set_text("") fee_entry.set_text("") - self.fee_box.hide() + #self.fee_box.hide() self.update_sending_tab() else: self.show_message( msg ) diff --git a/client/wallet.py b/client/wallet.py index 43e8bdac1..e34e85bcc 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -351,8 +351,11 @@ class Wallet: assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string) for i in range(4): sig = base64.b64encode( chr(27+i) + signature ) - if self.verify_message( address, sig, message): + try: + self.verify_message( address, sig, message) return sig + except: + continue else: raise BaseException("error: cannot sign message") @@ -364,49 +367,33 @@ class Wallet: curve = curve_secp256k1 G = generator_secp256k1 order = G.order() - # extract r,s from signature sig = base64.b64decode(signature) if len(sig) != 65: raise BaseException("error") r,s = util.sigdecode_string(sig[1:], order) recid = ord(sig[0]) - 27 - # 1.1 x = r + (recid/2) * order - # 1.3 alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p() beta = msqr.modular_sqrt(alpha, curve.p()) y = beta if (beta - recid) % 2 == 0 else curve.p() - beta - # 1.4 the constructor checks that nR is at infinity - try: - R = ellipticcurve.Point(curve, x, y, order) - except: - print "not in curve" - return False - + R = ellipticcurve.Point(curve, x, y, order) # 1.5 compute e from message: h = Hash( self.msg_magic( message ) ) e = string_to_number(h) minus_e = -e % order - # 1.6 compute Q = r^-1 (sR - eG) inv_r = numbertheory.inverse_mod(r,order) Q = inv_r * ( s * R + minus_e * G ) public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 ) - # check that Q is the public key - try: - public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) - except: - print "wrong key" - return False - + public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) # check that we get the original signing address addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) # print addr - return address == addr + assert address == addr def create_new_address2(self, for_change): From a041b48f4d26420c2e312599c74bf15e6515fcaa Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 11:51:57 +0100 Subject: [PATCH 17/48] minor --- client/electrum | 5 +++-- client/gui.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/client/electrum b/client/electrum index 4b756d66a..a25eb6733 100755 --- a/client/electrum +++ b/client/electrum @@ -105,9 +105,10 @@ if __name__ == '__main__': wallet.verify_message(signing_address, signature, cmd ) except: gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') - signature = identity = '' + address = amount = label = identity = '' - gui.set_send_tab(address, amount, label, identity) + if address: + gui.set_send_tab(address, amount, label, identity) gui.main() wallet.save() diff --git a/client/gui.py b/client/gui.py index be88042ed..cb0dee5ee 100644 --- a/client/gui.py +++ b/client/gui.py @@ -766,7 +766,7 @@ class BitcoinGUI: self.set_frozen(self.payto_entry,True) self.set_frozen(self.payto_amount_entry,True) self.set_frozen(self.payto_label_entry,True) - self.payto_sig_id.set_text( ' This transaction URI was signed by ' + identity ) + self.payto_sig_id.set_text( ' The bitcoin URI was signed by ' + identity ) else: self.payto_sig.set_visible(False) From 25e3993a5367769de5211f1745d598497e9d1b31 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 15:45:41 +0100 Subject: [PATCH 18/48] perform get_alias in a thread --- client/electrum | 32 ++-------------------------- client/gui.py | 51 ++++++++++++++++++++++++++++++++++----------- client/interface.py | 3 +++ client/wallet.py | 23 ++++++++++++++++++++ 4 files changed, 67 insertions(+), 42 deletions(-) diff --git a/client/electrum b/client/electrum index a25eb6733..6048cce00 100755 --- a/client/electrum +++ b/client/electrum @@ -28,25 +28,6 @@ _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) -def alias(x): - import urllib - if wallet.is_valid(x): - xx = x - else: - m = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', x) - if m: - url = 'http://' + m.group(2) + '/bitcoin.id/' + m.group(1) - else: - url = 'http://' + x + '/bitcoin.id' - print url - try: - xx = urllib.urlopen(url).read().strip() - except: - xx = '' - if not wallet.is_valid(xx): - xx = '' - return xx - if __name__ == '__main__': known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage'] @@ -81,7 +62,7 @@ if __name__ == '__main__': if re.match('^bitcoin:', cmd): o = cmd[8:].split('?') - address = alias(o[0]) + address = o[0] if len(o)>1: params = o[1].split('&') else: @@ -95,20 +76,11 @@ if __name__ == '__main__': elif k == 'label': label = uv elif k == 'signature': identity, signature = uv.split(':') - signing_address = alias(identity) cmd = cmd.replace('&%s=%s'%(k,v),'') else: print k,v - if signature: - try: - wallet.verify_message(signing_address, signature, cmd ) - except: - gui.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') - address = amount = label = identity = '' - - if address: - gui.set_send_tab(address, amount, label, identity) + gui.set_send_tab(address, amount, label, identity, signature, cmd) gui.main() wallet.save() diff --git a/client/gui.py b/client/gui.py index cb0dee5ee..a619370f8 100644 --- a/client/gui.py +++ b/client/gui.py @@ -17,7 +17,7 @@ # along with this program. If not, see . import datetime -import thread, time, ast, sys +import thread, time, ast, sys, re import socket, traceback import pygtk pygtk.require('2.0') @@ -580,6 +580,21 @@ class BitcoinGUI: gobject.idle_add( self.update_status_bar ) time.sleep(0.5) + + def check_recipient_thread(): + old_r = '' + while True: + time.sleep(0.5) + r = self.payto_entry.get_text() + if r != old_r: + old_r = r + if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): + to_address = self.wallet.get_alias(r) + if to_address: + s = r+ ' <'+to_address+'>' + gobject.idle_add( lambda: self.payto_entry.set_text(s) ) + + def update_wallet_thread(): while True: try: @@ -627,6 +642,7 @@ class BitcoinGUI: thread.start_new_thread(update_wallet_thread, ()) thread.start_new_thread(update_status_bar_thread, ()) + thread.start_new_thread(check_recipient_thread, ()) self.notebook.set_current_page(0) @@ -649,10 +665,10 @@ class BitcoinGUI: payto = gtk.HBox() payto_label = gtk.Label('Pay to:') payto_label.set_size_request(100,-1) - payto_label.show() + #payto_label.show() payto.pack_start(payto_label, False) payto_entry = gtk.Entry() - payto_entry.set_size_request(350, 26) + payto_entry.set_size_request(450, 26) payto_entry.show() payto.pack_start(payto_entry, False) vbox.pack_start(payto, False, False, 5) @@ -663,7 +679,7 @@ class BitcoinGUI: label_label.show() label.pack_start(label_label, False) label_entry = gtk.Entry() - label_entry.set_size_request(350, 26) + label_entry.set_size_request(450, 26) label_entry.show() label.pack_start(label_entry, False) vbox.pack_start(label, False, False, 5) @@ -736,7 +752,7 @@ class BitcoinGUI: self.error = 'Not enough funds' amount_entry.connect('changed', entry_changed, False) - fee_entry.connect('changed', entry_changed, True) + fee_entry.connect('changed', entry_changed, True) self.payto_entry = payto_entry self.payto_fee_entry = fee_entry @@ -757,9 +773,17 @@ class BitcoinGUI: entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) - def set_send_tab(self, address, amount, label, identity): + def set_send_tab(self, payto, amount, label, identity, signature, cmd): + if signature: + try: + signing_address = self.wallet.get_alias(identity) + self.wallet.verify_message(signing_address, signature, cmd ) + except: + self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') + address = amount = label = identity = '' + self.notebook.set_current_page(1) - self.payto_entry.set_text(address) + self.payto_entry.set_text(payto) self.payto_label_entry.set_text(label) self.payto_amount_entry.set_text(amount) if identity: @@ -788,16 +812,19 @@ class BitcoinGUI: for entry in [self.payto_entry,self.payto_amount_entry,self.payto_label_entry]: self.set_frozen(entry,False) entry.set_text('') - + def do_send(self, w, data): payto_entry, label_entry, amount_entry, fee_entry = data - label = label_entry.get_text() - - to_address = payto_entry.get_text() + r = payto_entry.get_text() + m = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) + if m: + to_address = m.group(5) + else: + to_address = r if not self.wallet.is_valid(to_address): - self.show_message( "invalid bitcoin address") + self.show_message( "invalid bitcoin address:\n"+to_address) return try: diff --git a/client/interface.py b/client/interface.py index 0ba948174..b9aa2979a 100644 --- a/client/interface.py +++ b/client/interface.py @@ -19,6 +19,9 @@ import random, socket, ast + + + class Interface: def __init__(self): self.servers = ['ecdsa.org','electrum.novit.ro'] # list of default servers diff --git a/client/wallet.py b/client/wallet.py index e34e85bcc..c8881c1a8 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -218,6 +218,7 @@ from version import ELECTRUM_VERSION, SEED_VERSION + class Wallet: def __init__(self, interface): @@ -703,3 +704,25 @@ class Wallet: return False, "error: " + out return True, out + def get_alias(self, x): + # this might not be the right place for this function. + import urllib + if self.is_valid(x): + return x + else: + m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', x) + m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', x) + if m1: + url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) + elif m2: + url = 'http://' + x + '/bitcoin.id' + else: + return '' + try: + print url + xx = urllib.urlopen(url).read().strip() + except: + return '' + if not self.is_valid(xx): + return '' + return xx From ea96658a73512735e1f7bb8d5a9b95793490ac44 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 15:57:45 +0100 Subject: [PATCH 19/48] do not act during focus --- client/gui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/gui.py b/client/gui.py index a619370f8..53e7a0f5b 100644 --- a/client/gui.py +++ b/client/gui.py @@ -585,6 +585,8 @@ class BitcoinGUI: old_r = '' while True: time.sleep(0.5) + if self.payto_entry.is_focus(): + continue r = self.payto_entry.get_text() if r != old_r: old_r = r From 0b1e56f51afe6f81c4ad6731d0d304793fd16c14 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 16:08:28 +0100 Subject: [PATCH 20/48] strip r --- client/gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index 53e7a0f5b..64226cbaa 100644 --- a/client/gui.py +++ b/client/gui.py @@ -590,10 +590,11 @@ class BitcoinGUI: r = self.payto_entry.get_text() if r != old_r: old_r = r + r = r.strip() if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): to_address = self.wallet.get_alias(r) if to_address: - s = r+ ' <'+to_address+'>' + s = r + ' <' + to_address + '>' gobject.idle_add( lambda: self.payto_entry.set_text(s) ) From 8de53f7f453b31900689142242478eb8cf46510f Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 16:39:27 +0100 Subject: [PATCH 21/48] check aliases versus stored value --- client/gui.py | 17 ++++++++++++++++- client/wallet.py | 12 ++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index 64226cbaa..c8b689772 100644 --- a/client/gui.py +++ b/client/gui.py @@ -592,7 +592,10 @@ class BitcoinGUI: old_r = r r = r.strip() if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): - to_address = self.wallet.get_alias(r) + try: + to_address = self.wallet.get_alias(r) + except: + continue if to_address: s = r + ' <' + to_address + '>' gobject.idle_add( lambda: self.payto_entry.set_text(s) ) @@ -780,6 +783,9 @@ class BitcoinGUI: if signature: try: signing_address = self.wallet.get_alias(identity) + except: + self.show_message('Warning: the key of the recipient has changed since last visit.\nContinue at your own risks!') + try: self.wallet.verify_message(signing_address, signature, cmd ) except: self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') @@ -821,6 +827,15 @@ class BitcoinGUI: payto_entry, label_entry, amount_entry, fee_entry = data label = label_entry.get_text() r = payto_entry.get_text() + + r = r.strip() + if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): + try: + to_address = self.wallet.get_alias(r) + except: + self.show_message('Warning: the key of the recipient has changed since last visit.') + return + m = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) if m: to_address = m.group(5) diff --git a/client/wallet.py b/client/wallet.py index c8881c1a8..f984c8ce5 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -237,6 +237,7 @@ class Wallet: self.status = {} # current status of addresses self.history = {} self.labels = {} # labels for addresses and transactions + self.aliases = {} # aliases for addresses self.addressbook = [] # outgoing addresses, for payments # not saved @@ -475,6 +476,7 @@ class Wallet: 'labels':self.labels, 'contacts':self.addressbook, 'imported_keys':self.imported_keys, + 'aliases':self.aliases, } f = open(self.path,"w") f.write( repr(s) ) @@ -505,6 +507,7 @@ class Wallet: self.labels = d.get('labels') self.addressbook = d.get('contacts') self.imported_keys = d.get('imported_keys',{}) + self.aliases = d.get('aliases',{}) except: raise BaseException(upgrade_msg) @@ -725,4 +728,13 @@ class Wallet: return '' if not self.is_valid(xx): return '' + self.labels[xx] = x + + s = self.aliases.get(x) + if s: + if s != xx: + raise BaseException("error:alias does not match previous value") + else: + self.aliases[x] = xx + return xx From 3112385dd1603f4a0e3cfda57fc1158a4bf0aa00 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 17:17:33 +0100 Subject: [PATCH 22/48] dialog for updating aliases --- client/gui.py | 50 ++++++++++++++++++++++++++++++++---------------- client/wallet.py | 2 +- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/client/gui.py b/client/gui.py index c8b689772..f8839f211 100644 --- a/client/gui.py +++ b/client/gui.py @@ -781,15 +781,14 @@ class BitcoinGUI: def set_send_tab(self, payto, amount, label, identity, signature, cmd): if signature: - try: - signing_address = self.wallet.get_alias(identity) - except: - self.show_message('Warning: the key of the recipient has changed since last visit.\nContinue at your own risks!') + signing_address = self.get_alias(identity) + if not signing_address: + return try: self.wallet.verify_message(signing_address, signature, cmd ) except: - self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.\nContinue at your own risks!') - address = amount = label = identity = '' + self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') + payto = amount = label = identity = '' self.notebook.set_current_page(1) self.payto_entry.set_text(payto) @@ -823,24 +822,43 @@ class BitcoinGUI: entry.set_text('') + def get_alias(self, r): + try: + to_address = self.wallet.get_alias(r) + except BaseException, e: + to_address = None + msg = "Warning: the key corresponding to %s does not match its previously known value.\nDo you wish to accept the new key?"%r + dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg) + dialog.show() + result = dialog.run() + dialog.destroy() + if result != gtk.RESPONSE_CANCEL: + print e.message + to_address = e.message + self.wallet.aliases[r] = to_address + + return to_address + + + def do_send(self, w, data): payto_entry, label_entry, amount_entry, fee_entry = data label = label_entry.get_text() r = payto_entry.get_text() - r = r.strip() - if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): - try: - to_address = self.wallet.get_alias(r) - except: - self.show_message('Warning: the key of the recipient has changed since last visit.') - return - m = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) - if m: - to_address = m.group(5) + m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r) + m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) + + if m1: + to_address = self.get_alias(r) + if not to_address: + return + elif m2: + to_address = m2.group(5) else: to_address = r + if not self.wallet.is_valid(to_address): self.show_message( "invalid bitcoin address:\n"+to_address) return diff --git a/client/wallet.py b/client/wallet.py index f984c8ce5..bd46aaaca 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -733,7 +733,7 @@ class Wallet: s = self.aliases.get(x) if s: if s != xx: - raise BaseException("error:alias does not match previous value") + raise BaseException( xx ) else: self.aliases[x] = xx From 90cd03e09306dff8519de673cbd1f515c3a23398 Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 17:38:43 +0100 Subject: [PATCH 23/48] receipts. 'list' command to list variables. --- client/electrum | 9 +++++++-- client/gui.py | 1 + client/wallet.py | 7 +++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/client/electrum b/client/electrum index 6048cce00..0166c3916 100755 --- a/client/electrum +++ b/client/electrum @@ -30,7 +30,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','list'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -147,7 +147,7 @@ if __name__ == '__main__': cmd = 'help' # open session - if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage']: + if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'list']: wallet.interface.new_session(wallet.all_addresses(), wallet.electrum_version) wallet.update() wallet.save() @@ -237,6 +237,11 @@ if __name__ == '__main__': for addr in wallet.addressbook: print addr, " ", wallet.labels.get(addr) + elif cmd == 'list': + objname = args[1] + obj = eval("wallet."+objname) + print obj + elif cmd in [ 'addresses']: for addr in wallet.all_addresses(): if options.show_all or not wallet.is_change(addr): diff --git a/client/gui.py b/client/gui.py index f8839f211..b8b8b7a0f 100644 --- a/client/gui.py +++ b/client/gui.py @@ -786,6 +786,7 @@ class BitcoinGUI: return try: self.wallet.verify_message(signing_address, signature, cmd ) + self.wallet.receipt = (signing_address, signature, cmd) except: self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') payto = amount = label = identity = '' diff --git a/client/wallet.py b/client/wallet.py index bd46aaaca..0be15b308 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -238,6 +238,8 @@ class Wallet: self.history = {} self.labels = {} # labels for addresses and transactions self.aliases = {} # aliases for addresses + self.receipts = {} # signed URIs + self.receipt = None # next receipt self.addressbook = [] # outgoing addresses, for payments # not saved @@ -477,6 +479,7 @@ class Wallet: 'contacts':self.addressbook, 'imported_keys':self.imported_keys, 'aliases':self.aliases, + 'receipts':self.receipts, } f = open(self.path,"w") f.write( repr(s) ) @@ -508,6 +511,7 @@ class Wallet: self.addressbook = d.get('contacts') self.imported_keys = d.get('imported_keys',{}) self.aliases = d.get('aliases',{}) + self.receipts = d.get('receipts',{}) except: raise BaseException(upgrade_msg) @@ -705,6 +709,9 @@ class Wallet: out = self.interface.send_tx(tx) if out != tx_hash: return False, "error: " + out + if self.receipt: + self.receipts[tx_hash] = self.receipt + self.receipt = None return True, out def get_alias(self, x): From 4485e7dd4b6d5a6199d99cdc9a852ff551fc384b Mon Sep 17 00:00:00 2001 From: thomasv Date: Fri, 3 Feb 2012 17:53:04 +0100 Subject: [PATCH 24/48] bump version number --- client/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/version.py b/client/version.py index a28557508..a92528f75 100644 --- a/client/version.py +++ b/client/version.py @@ -1,2 +1,2 @@ -ELECTRUM_VERSION = "0.37" +ELECTRUM_VERSION = "0.38" SEED_VERSION = 4 # bump this everytime the seed generation is modified From 7ae9752ba266132cc3ed7ad3b186908252774def Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 3 Feb 2012 22:25:50 +0100 Subject: [PATCH 25/48] message != label --- client/electrum | 5 +++-- client/gui.py | 59 +++++++++++++++++++++---------------------------- 2 files changed, 28 insertions(+), 36 deletions(-) diff --git a/client/electrum b/client/electrum index 0166c3916..a0a607476 100755 --- a/client/electrum +++ b/client/electrum @@ -68,11 +68,12 @@ if __name__ == '__main__': else: params = [] - amount = label = signature = identity = '' + amount = label = message = signature = identity = '' for p in params: k,v = p.split('=') uv = urldecode(v) if k == 'amount': amount = uv + elif k == 'message': message = uv elif k == 'label': label = uv elif k == 'signature': identity, signature = uv.split(':') @@ -80,7 +81,7 @@ if __name__ == '__main__': else: print k,v - gui.set_send_tab(address, amount, label, identity, signature, cmd) + gui.set_send_tab(address, amount, message, label, identity, signature, cmd) gui.main() wallet.save() diff --git a/client/gui.py b/client/gui.py index b8b8b7a0f..2c373f532 100644 --- a/client/gui.py +++ b/client/gui.py @@ -660,44 +660,33 @@ class BitcoinGUI: def create_send_tab(self): - # cases: - # no alias bitcoin:address - # alias bitcoin:name@domain - # alias + signature bitcoin:address?id=name@domain&sig=signature - page = vbox = gtk.VBox() page.show() payto = gtk.HBox() payto_label = gtk.Label('Pay to:') payto_label.set_size_request(100,-1) - #payto_label.show() payto.pack_start(payto_label, False) payto_entry = gtk.Entry() payto_entry.set_size_request(450, 26) - payto_entry.show() payto.pack_start(payto_entry, False) vbox.pack_start(payto, False, False, 5) - label = gtk.HBox() - label_label = gtk.Label('Label:') - label_label.set_size_request(100,-1) - label_label.show() - label.pack_start(label_label, False) - label_entry = gtk.Entry() - label_entry.set_size_request(450, 26) - label_entry.show() - label.pack_start(label_entry, False) - vbox.pack_start(label, False, False, 5) + message = gtk.HBox() + message_label = gtk.Label('Message:') + message_label.set_size_request(100,-1) + message.pack_start(message_label, False) + message_entry = gtk.Entry() + message_entry.set_size_request(450, 26) + message.pack_start(message_entry, False) + vbox.pack_start(message, False, False, 5) amount_box = gtk.HBox() amount_label = gtk.Label('Amount:') amount_label.set_size_request(100,-1) - amount_label.show() amount_box.pack_start(amount_label, False) amount_entry = gtk.Entry() amount_entry.set_size_request(120, -1) - amount_entry.show() amount_box.pack_start(amount_entry, False) vbox.pack_start(amount_box, False, False, 5) @@ -708,21 +697,20 @@ class BitcoinGUI: fee_entry = gtk.Entry() fee_entry.set_size_request(60, 26) fee_box.pack_start(fee_entry, False) + vbox.pack_start(fee_box, False, False, 5) end_box = gtk.HBox() - end_box.pack_start(fee_box, False, False, 5) - empty_label = gtk.Label('') empty_label.set_size_request(100,-1) end_box.pack_start(empty_label, False) send_button = gtk.Button("Send") send_button.show() - end_box.pack_start(send_button, False, False, 5) + end_box.pack_start(send_button, False, False, 0) clear_button = gtk.Button("Clear") clear_button.show() - end_box.pack_start(clear_button, False, False, 5) - send_button.connect("clicked", self.do_send, (payto_entry, label_entry, amount_entry, fee_entry)) - clear_button.connect("clicked", self.do_clear, (payto_entry, label_entry, amount_entry, fee_entry)) + end_box.pack_start(clear_button, False, False, 15) + send_button.connect("clicked", self.do_send, (payto_entry, message_entry, amount_entry, fee_entry)) + clear_button.connect("clicked", self.do_clear, (payto_entry, message_entry, amount_entry, fee_entry)) vbox.pack_start(end_box, False, False, 5) @@ -764,8 +752,8 @@ class BitcoinGUI: self.payto_fee_entry = fee_entry self.payto_sig_id = payto_sig_id self.payto_sig = payto_sig - self.payto_amount_entry = amount_entry - self.payto_label_entry = label_entry + self.amount_entry = amount_entry + self.message_entry = message_entry self.add_tab(page, 'Send') def set_frozen(self,entry,frozen): @@ -779,7 +767,7 @@ class BitcoinGUI: entry.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse("#ffffff")) - def set_send_tab(self, payto, amount, label, identity, signature, cmd): + def set_send_tab(self, payto, amount, message, label, identity, signature, cmd): if signature: signing_address = self.get_alias(identity) if not signing_address: @@ -791,14 +779,17 @@ class BitcoinGUI: self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') payto = amount = label = identity = '' + if label and payto: + self.labels[payto] = label + self.notebook.set_current_page(1) self.payto_entry.set_text(payto) - self.payto_label_entry.set_text(label) - self.payto_amount_entry.set_text(amount) + self.message_entry.set_text(message) + self.amount_entry.set_text(amount) if identity: self.set_frozen(self.payto_entry,True) - self.set_frozen(self.payto_amount_entry,True) - self.set_frozen(self.payto_label_entry,True) + self.set_frozen(self.amount_entry,True) + self.set_frozen(self.message_entry,True) self.payto_sig_id.set_text( ' The bitcoin URI was signed by ' + identity ) else: self.payto_sig.set_visible(False) @@ -818,7 +809,7 @@ class BitcoinGUI: def do_clear(self, w, data): self.payto_sig.set_visible(False) - for entry in [self.payto_entry,self.payto_amount_entry,self.payto_label_entry]: + for entry in [self.payto_entry,self.amount_entry,self.message_entry]: self.set_frozen(entry,False) entry.set_text('') @@ -1108,7 +1099,7 @@ class BitcoinGUI: address = liststore.get_value( liststore.get_iter(path), 0) self.payto_entry.set_text( address ) self.notebook.set_current_page(1) - self.payto_amount_entry.grab_focus() + self.amount_entry.grab_focus() button.connect("clicked", payto, treeview, liststore) button.show() From ec46a812408c275cb09ce10bf5c7a755ce9b6ef8 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Fri, 3 Feb 2012 22:27:41 +0100 Subject: [PATCH 26/48] do not handle labels --- client/gui.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/client/gui.py b/client/gui.py index 2c373f532..ed0a6c1ff 100644 --- a/client/gui.py +++ b/client/gui.py @@ -779,8 +779,9 @@ class BitcoinGUI: self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') payto = amount = label = identity = '' - if label and payto: - self.labels[payto] = label + # redundant with aliases + #if label and payto: + # self.labels[payto] = label self.notebook.set_current_page(1) self.payto_entry.set_text(payto) @@ -797,9 +798,6 @@ class BitcoinGUI: def create_about_tab(self): page = gtk.VBox() page.show() - #self.info = gtk.Label('') - #self.info.set_selectable(True) - #page.pack_start(self.info) tv = gtk.TextView() tv.set_editable(False) tv.set_cursor_visible(False) From 9d396e6a56314666bc91b1f3abc7c14658d0433e Mon Sep 17 00:00:00 2001 From: ThomasV Date: Sat, 4 Feb 2012 08:47:46 +0100 Subject: [PATCH 27/48] message->label --- client/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index ed0a6c1ff..5b35987af 100644 --- a/client/gui.py +++ b/client/gui.py @@ -673,7 +673,7 @@ class BitcoinGUI: vbox.pack_start(payto, False, False, 5) message = gtk.HBox() - message_label = gtk.Label('Message:') + message_label = gtk.Label('Label:') message_label.set_size_request(100,-1) message.pack_start(message_label, False) message_entry = gtk.Entry() From 072bd8c6ccaac29b6f352994471802bb584cb5db Mon Sep 17 00:00:00 2001 From: thomasv Date: Sat, 4 Feb 2012 13:29:46 +0100 Subject: [PATCH 28/48] Description --- client/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui.py b/client/gui.py index 5b35987af..bdee9cb59 100644 --- a/client/gui.py +++ b/client/gui.py @@ -673,7 +673,7 @@ class BitcoinGUI: vbox.pack_start(payto, False, False, 5) message = gtk.HBox() - message_label = gtk.Label('Label:') + message_label = gtk.Label('Description:') message_label.set_size_request(100,-1) message.pack_start(message_label, False) message_entry = gtk.Entry() @@ -931,7 +931,7 @@ class BitcoinGUI: tvcolumn.pack_start(cell, False) tvcolumn.add_attribute(cell, 'text', 2) - tvcolumn = gtk.TreeViewColumn('Label') + tvcolumn = gtk.TreeViewColumn('Description') treeview.append_column(tvcolumn) cell = gtk.CellRendererText() cell.set_property('foreground', 'grey') From 69ab0aeb324fb2fe7322cda27e603ffef8adfa9b Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 6 Feb 2012 07:48:52 +0100 Subject: [PATCH 29/48] use decimals in text mode --- client/electrum | 22 +++++++++++----------- client/gui.py | 11 +++-------- client/wallet.py | 12 +++++++++++- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/client/electrum b/client/electrum index a0a607476..6b7688c93 100755 --- a/client/electrum +++ b/client/electrum @@ -21,13 +21,14 @@ import re, sys, getpass from optparse import OptionParser from wallet import Wallet from interface import Interface - +from decimal import Decimal # URL decode _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE) urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) +from wallet import format_satoshis if __name__ == '__main__': known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','list'] @@ -110,7 +111,7 @@ if __name__ == '__main__': host = raw_input("server (default:%s):"%wallet.interface.host) port = raw_input("port (default:%d):"%wallet.interface.port) - fee = raw_input("fee (default:%f):"%(wallet.fee*1e-8)) + fee = raw_input("fee (default:%s):"%( str(Decimal(wallet.fee)/100000000)) ) if fee: wallet.fee = float(fee) if host: wallet.interface.host = host if port: wallet.interface.port = int(port) @@ -223,16 +224,16 @@ if __name__ == '__main__': if addrs == []: c, u = wallet.get_balance() if u: - print c*1e-8, u*1e-8 + print Decimal( c ) / 100000000 , Decimal( u ) / 100000000 else: - print c*1e-8 + print Decimal( c ) / 100000000 else: for addr in addrs: c, u = wallet.get_addr_balance(addr) if u: - print "%s %s, %s" % (addr, c*1e-8, u*1e-8) + print "%s %s, %s" % (addr, str(Decimal(c)/100000000), str(Decimal(u)/100000000)) else: - print "%s %s" % (addr, c*1e-8) + print "%s %s" % (addr, str(Decimal(c)/100000000)) elif cmd in [ 'contacts']: for addr in wallet.addressbook: @@ -257,7 +258,7 @@ if __name__ == '__main__': for item in h: if item['is_in']: ni += 1 else: no += 1 - b = "%d %d %f"%(no, ni, wallet.get_addr_balance(addr)[0]*1e-8) + b = "%d %d %s"%(no, ni, str(Decimal(wallet.get_addr_balance(addr)[0])/100000000)) else: b='' if options.show_keys: pk = wallet.get_private_key2(addr, password) @@ -269,9 +270,8 @@ if __name__ == '__main__': b = 0 for line in lines: import datetime - v = 1.*line['value']/1e8 + v = line['value'] b += v - v_str = "%f"%v if v<0 else "+%f"%v try: time_str = datetime.datetime.fromtimestamp( line['nTime']) except: @@ -281,8 +281,8 @@ if __name__ == '__main__': if not label: label = line['tx_hash'] else: label = label + ' '*(64 - len(label) ) - print time_str, " ", label, " ", v_str, " ", "%f"%b - print "# balance: ", b + print time_str , " " + label + " " + format_satoshis(v)+ " "+ format_satoshis(b) + print "# balance: ", format_satoshis(b) elif cmd == 'label': try: diff --git a/client/gui.py b/client/gui.py index 5b35987af..d1eff1a0c 100644 --- a/client/gui.py +++ b/client/gui.py @@ -28,12 +28,7 @@ from decimal import Decimal gtk.gdk.threads_init() APP_NAME = "Electrum" -def format_satoshis(x): - s = str( Decimal(x) /100000000 ) - if not '.' in s: s += '.' - p = s.find('.') - s += " "*( 9 - ( len(s) - p )) - return s +from wallet import format_satoshis def numbify(entry, is_int = False): text = entry.get_text().strip() @@ -1117,7 +1112,7 @@ class BitcoinGUI: self.status_image.set_from_stock(gtk.STOCK_NO, gtk.ICON_SIZE_MENU) self.network_button.set_tooltip_text("Trying to contact %s.\n%d blocks"%(self.wallet.interface.host, self.wallet.interface.blocks)) text = "Balance: %s "%( format_satoshis(c) ) - if u: text += "[+ %s unconfirmed]"%( format_satoshis(u) ) + if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) ) if self.error: text = self.error self.status_bar.pop(self.context_id) self.status_bar.push(self.context_id, text) @@ -1175,7 +1170,7 @@ class BitcoinGUI: details+= "Outputs:\n-"+ '\n-'.join(tx['outputs']) self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label, - ('+' if v>0 else '') + format_satoshis(v), format_satoshis(balance), tooltip, details] ) + format_satoshis(v,True), format_satoshis(balance), tooltip, details] ) if cursor: self.history_treeview.set_cursor( cursor ) diff --git a/client/wallet.py b/client/wallet.py index 0be15b308..c0759adb6 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -213,9 +213,19 @@ def raw_tx( inputs, outputs, for_sig = None ): -from version import ELECTRUM_VERSION, SEED_VERSION +def format_satoshis(x, is_diff=False): + from decimal import Decimal + s = str( Decimal(x) /100000000 ) + if is_diff and x>0: + s = "+" + s + if not '.' in s: s += '.' + p = s.find('.') + s += " "*( 9 - ( len(s) - p )) + s = " "*( 5 - ( p )) + s + return s +from version import ELECTRUM_VERSION, SEED_VERSION From a590699d92b4006f0be991ba3928f071fa519bf9 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 17:59:31 +0100 Subject: [PATCH 30/48] introducing signed aliases --- client/gui.py | 47 ++++++++++++++++++++++++++----------- client/wallet.py | 60 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/client/gui.py b/client/gui.py index 397c0b477..8182dfc8d 100644 --- a/client/gui.py +++ b/client/gui.py @@ -588,7 +588,7 @@ class BitcoinGUI: r = r.strip() if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): try: - to_address = self.wallet.get_alias(r) + to_address = self.wallet.read_alias(r)[0] except: continue if to_address: @@ -807,22 +807,43 @@ class BitcoinGUI: entry.set_text('') + def question(self,msg): + dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg) + dialog.show() + result = dialog.run() + dialog.destroy() + return result == gtk.RESPONSE_OK + def get_alias(self, r): try: - to_address = self.wallet.get_alias(r) + target, signing_address, auth_name = self.wallet.read_alias(r) except BaseException, e: - to_address = None - msg = "Warning: the key corresponding to %s does not match its previously known value.\nDo you wish to accept the new key?"%r - dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg) - dialog.show() - result = dialog.run() - dialog.destroy() - if result != gtk.RESPONSE_CANCEL: - print e.message - to_address = e.message - self.wallet.aliases[r] = to_address + # raise exception if verify fails (verify the chain) + self.show_message("Alias error: " + e.message) + return - return to_address + if auth_name is None: + a = self.wallet.aliases.get(r) + if not a: + if self.question( "Warning: the alias is self-signed. Do you want to trust address %s ?"%to_address ): + self.wallet.aliases[r] = signing_address + else: + target = None + else: + if signing_address != a: + if self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%r ): + self.wallet.aliases[r] = signing_address + else: + target = None + else: + if signing_address not in self.wallet.authorities.keys(): + if self.question( "Warning: the alias '%s' was signed by %s [%s].\n\nDo you want to add this key to your list of trusted keys?"\ + %(r,auth_name,signing_address)): + self.wallet.authorities[signing_address] = auth_name + else: + target = None + + return target diff --git a/client/wallet.py b/client/wallet.py index c0759adb6..7b68e725c 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -248,6 +248,8 @@ class Wallet: self.history = {} self.labels = {} # labels for addresses and transactions self.aliases = {} # aliases for addresses + self.authorities = {} # trusted addresses + self.receipts = {} # signed URIs self.receipt = None # next receipt self.addressbook = [] # outgoing addresses, for payments @@ -383,7 +385,7 @@ class Wallet: order = G.order() # extract r,s from signature sig = base64.b64decode(signature) - if len(sig) != 65: raise BaseException("error") + if len(sig) != 65: raise BaseException("Wrong encoding") r,s = util.sigdecode_string(sig[1:], order) recid = ord(sig[0]) - 27 # 1.1 @@ -407,7 +409,10 @@ class Wallet: # check that we get the original signing address addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) # print addr - assert address == addr + try: + assert address == addr + except: + raise BaseException("Bad signature") def create_new_address2(self, for_change): @@ -489,6 +494,7 @@ class Wallet: 'contacts':self.addressbook, 'imported_keys':self.imported_keys, 'aliases':self.aliases, + 'authorities':self.authorities, 'receipts':self.receipts, } f = open(self.path,"w") @@ -521,6 +527,7 @@ class Wallet: self.addressbook = d.get('contacts') self.imported_keys = d.get('imported_keys',{}) self.aliases = d.get('aliases',{}) + self.authorities = d.get('authorities',{}) self.receipts = d.get('receipts',{}) except: raise BaseException(upgrade_msg) @@ -724,34 +731,47 @@ class Wallet: self.receipt = None return True, out - def get_alias(self, x): + + def read_alias(self, alias): # this might not be the right place for this function. import urllib - if self.is_valid(x): - return x + if self.is_valid(alias): + return alias else: - m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', x) - m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', x) + m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias) + m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias) if m1: url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) elif m2: - url = 'http://' + x + '/bitcoin.id' + url = 'http://' + alias + '/bitcoin.id' else: return '' try: - print url - xx = urllib.urlopen(url).read().strip() + lines = urllib.urlopen(url).readlines() except: return '' - if not self.is_valid(xx): - return '' - self.labels[xx] = x - s = self.aliases.get(x) - if s: - if s != xx: - raise BaseException( xx ) + # line 0 + line = lines[0].strip().split(':') + if len(line) == 1: + auth_name = None + target = signing_addr = line[0] else: - self.aliases[x] = xx - - return xx + target, auth_name, signing_addr, signature = line + msg = "alias:%s:%s:%s"%(alias,target,auth_name) + print msg, signature + self.verify_message(signing_addr, signature, msg) + + # other lines are signed updates + for line in lines[1:]: + line = line.strip() + if not line: continue + line = line.split(':') + previous = target + print repr(line) + target, signature = line + self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) + + assert self.is_valid(target) + + return target, signing_addr, auth_name From fd019addd07205cb6ecb03d2736726ee3a50b484 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 18:01:35 +0100 Subject: [PATCH 31/48] add parent window to password dialog --- client/gui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/gui.py b/client/gui.py index 8182dfc8d..4dd1311a2 100644 --- a/client/gui.py +++ b/client/gui.py @@ -396,8 +396,8 @@ def password_line(label): password.show() return password, password_entry -def password_dialog(): - dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, +def password_dialog(parent): + dialog = gtk.MessageDialog( parent, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, "Please enter your password.") dialog.get_image().set_visible(False) current_pw, current_pw_entry = password_line('Password:') @@ -526,7 +526,7 @@ class BitcoinGUI: def seedb(w, wallet): if wallet.use_encryption: - password = password_dialog() + password = password_dialog(self.window) if not password: return else: password = None show_seed_dialog(wallet, password, self.window) @@ -881,7 +881,7 @@ class BitcoinGUI: return if self.wallet.use_encryption: - password = password_dialog() + password = password_dialog(self.window) if not password: return else: From d978a8fbcdac4070e0386d14c9ca3328e730f156 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 18:07:01 +0100 Subject: [PATCH 32/48] raise exceptions with messages --- client/wallet.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/client/wallet.py b/client/wallet.py index 7b68e725c..16fcc1a6c 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -409,9 +409,7 @@ class Wallet: # check that we get the original signing address addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) # print addr - try: - assert address == addr - except: + if address != addr: raise BaseException("Bad signature") @@ -772,6 +770,7 @@ class Wallet: target, signature = line self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) - assert self.is_valid(target) + if not self.is_valid(target): + raise BaseException("Invalid bitcoin address") return target, signing_addr, auth_name From 95014bd4b7c3df869164e2532340d4fe056b71b7 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 18:10:30 +0100 Subject: [PATCH 33/48] rename methods --- client/wallet.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/client/wallet.py b/client/wallet.py index 16fcc1a6c..fbe275ed8 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -333,7 +333,7 @@ class Wallet: def get_sequence(self,n,for_change): return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key ) ) - def get_private_key2(self, address, password): + def get_private_key(self, address, password): """ Privatekey(type,n) = Master_private_key + H(n|S|type) """ order = generator_secp256k1.order() @@ -361,7 +361,7 @@ class Wallet: return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message def sign_message(self, address, message, password): - private_key = ecdsa.SigningKey.from_string( self.get_private_key2(address, password), curve = SECP256k1 ) + private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 ) public_key = private_key.get_verifying_key() signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string ) assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string) @@ -413,7 +413,7 @@ class Wallet: raise BaseException("Bad signature") - def create_new_address2(self, for_change): + def create_new_address(self, for_change): """ Publickey(type,n) = Master_public_key + H(n|S|type)*point """ curve = SECP256k1 n = len(self.change_addresses) if for_change else len(self.addresses) @@ -438,12 +438,12 @@ class Wallet: is_new = False while True: if self.change_addresses == []: - self.create_new_address2(True) + self.create_new_address(True) is_new = True continue a = self.change_addresses[-1] if self.history.get(a): - self.create_new_address2(True) + self.create_new_address(True) is_new = True else: break @@ -451,13 +451,13 @@ class Wallet: n = self.gap_limit while True: if len(self.addresses) < n: - self.create_new_address2(False) + self.create_new_address(False) is_new = True continue if map( lambda a: self.history.get(a), self.addresses[-n:] ) == n*[[]]: break else: - self.create_new_address2(False) + self.create_new_address(False) is_new = True @@ -543,7 +543,7 @@ class Wallet: if not self.history.get(addr): n = n + 1 if n < self.gap_limit: - new_address = self.create_new_address2(False) + new_address = self.create_new_address(False) self.history[new_address] = [] #get from server return True, new_address else: @@ -629,7 +629,7 @@ class Wallet: s_inputs = [] for i in range(len(inputs)): addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i] - private_key = ecdsa.SigningKey.from_string( self.get_private_key2(addr, password), curve = SECP256k1 ) + private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 ) public_key = private_key.get_verifying_key() pubkey = public_key.to_string() tx = filter( raw_tx( inputs, outputs, for_sig = i ) ) From 9156d5aafe657e01b68928cb4b87d4bb44113ac4 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 18:13:33 +0100 Subject: [PATCH 34/48] rename 'list' as 'eval' --- client/electrum | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/client/electrum b/client/electrum index 6b7688c93..7893bd66f 100755 --- a/client/electrum +++ b/client/electrum @@ -31,7 +31,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) from wallet import format_satoshis if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','list'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage','eval'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -149,7 +149,7 @@ if __name__ == '__main__': cmd = 'help' # open session - if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'list']: + if cmd not in ['password', 'mktx', 'history', 'label', 'contacts', 'help', 'validateaddress', 'signmessage', 'verifymessage', 'eval']: wallet.interface.new_session(wallet.all_addresses(), wallet.electrum_version) wallet.update() wallet.save() @@ -206,6 +206,8 @@ if __name__ == '__main__': print "syntax: mktx [label]" elif cmd2 == 'seed': print "show generation seed of your wallet. password protected." + elif cmd2 == 'eval': + print "run python eval on an object" elif cmd == 'seed': import mnemonic @@ -239,10 +241,8 @@ if __name__ == '__main__': for addr in wallet.addressbook: print addr, " ", wallet.labels.get(addr) - elif cmd == 'list': - objname = args[1] - obj = eval("wallet."+objname) - print obj + elif cmd == 'eval': + print eval(args[1]) elif cmd in [ 'addresses']: for addr in wallet.all_addresses(): From d7132e5e9a99c16110ba24fd706d48d0151fb813 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 18:55:25 +0100 Subject: [PATCH 35/48] interactive flag for get_alias --- client/electrum | 1 + client/gui.py | 24 +++++++++++++----------- client/wallet.py | 1 + 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/client/electrum b/client/electrum index 7893bd66f..29ff64514 100755 --- a/client/electrum +++ b/client/electrum @@ -243,6 +243,7 @@ if __name__ == '__main__': elif cmd == 'eval': print eval(args[1]) + wallet.save() elif cmd in [ 'addresses']: for addr in wallet.all_addresses(): diff --git a/client/gui.py b/client/gui.py index 4dd1311a2..743912d1d 100644 --- a/client/gui.py +++ b/client/gui.py @@ -588,7 +588,7 @@ class BitcoinGUI: r = r.strip() if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): try: - to_address = self.wallet.read_alias(r)[0] + to_address = self.get_alias(r, interactive=False) except: continue if to_address: @@ -764,7 +764,7 @@ class BitcoinGUI: def set_send_tab(self, payto, amount, message, label, identity, signature, cmd): if signature: - signing_address = self.get_alias(identity) + signing_address = self.get_alias(identity, interactive = True) if not signing_address: return try: @@ -814,31 +814,33 @@ class BitcoinGUI: dialog.destroy() return result == gtk.RESPONSE_OK - def get_alias(self, r): + def get_alias(self, alias, interactive = False): try: - target, signing_address, auth_name = self.wallet.read_alias(r) + target, signing_address, auth_name = self.wallet.read_alias(alias) except BaseException, e: # raise exception if verify fails (verify the chain) self.show_message("Alias error: " + e.message) return + print target, signing_address, auth_name + if auth_name is None: - a = self.wallet.aliases.get(r) + a = self.wallet.aliases.get(alias) if not a: - if self.question( "Warning: the alias is self-signed. Do you want to trust address %s ?"%to_address ): + if interactive and self.question( "Warning: the alias is self-signed. Do you want to trust address %s ?"%to_address ): self.wallet.aliases[r] = signing_address else: target = None else: if signing_address != a: - if self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%r ): - self.wallet.aliases[r] = signing_address + if interactive and self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%alias ): + self.wallet.aliases[alias] = signing_address else: target = None else: if signing_address not in self.wallet.authorities.keys(): - if self.question( "Warning: the alias '%s' was signed by %s [%s].\n\nDo you want to add this key to your list of trusted keys?"\ - %(r,auth_name,signing_address)): + if interactive and self.question( "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"\ + %(alias,target,auth_name,signing_address)): self.wallet.authorities[signing_address] = auth_name else: target = None @@ -857,7 +859,7 @@ class BitcoinGUI: m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r) if m1: - to_address = self.get_alias(r) + to_address = self.get_alias(r, interactive = True) if not to_address: return elif m2: diff --git a/client/wallet.py b/client/wallet.py index fbe275ed8..a8f657503 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -410,6 +410,7 @@ class Wallet: addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) # print addr if address != addr: + print "bad signature" raise BaseException("Bad signature") From 3c3e18056fa6b989e691462c5f3c0403e6a83ccb Mon Sep 17 00:00:00 2001 From: ThomasV Date: Mon, 6 Feb 2012 23:45:21 +0100 Subject: [PATCH 36/48] fixes --- client/gui.py | 11 +++++-- client/wallet.py | 76 +++++++++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 42 deletions(-) diff --git a/client/gui.py b/client/gui.py index 743912d1d..98d92fc7c 100644 --- a/client/gui.py +++ b/client/gui.py @@ -764,7 +764,12 @@ class BitcoinGUI: def set_send_tab(self, payto, amount, message, label, identity, signature, cmd): if signature: - signing_address = self.get_alias(identity, interactive = True) + if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity): + signing_address = self.get_alias(identity, interactive = True) + elif self.wallet.is_valid(identity): + signing_address = identity + else: + signing_address = None if not signing_address: return try: @@ -827,8 +832,8 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - if interactive and self.question( "Warning: the alias is self-signed. Do you want to trust address %s ?"%to_address ): - self.wallet.aliases[r] = signing_address + if interactive and self.question( "Warning: the alias '%s' is self-signed. Do you want to trust address %s ?"%(alias,signing_address) ): + self.wallet.aliases[alias] = signing_address else: target = None else: diff --git a/client/wallet.py b/client/wallet.py index a8f657503..ea133b615 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -734,44 +734,42 @@ class Wallet: def read_alias(self, alias): # this might not be the right place for this function. import urllib - if self.is_valid(alias): - return alias + + m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias) + m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias) + if m1: + url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) + elif m2: + url = 'http://' + alias + '/bitcoin.id' else: - m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias) - m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias) - if m1: - url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) - elif m2: - url = 'http://' + alias + '/bitcoin.id' - else: - return '' - try: - lines = urllib.urlopen(url).readlines() - except: - return '' + return '' + try: + lines = urllib.urlopen(url).readlines() + except: + return '' - # line 0 - line = lines[0].strip().split(':') - if len(line) == 1: - auth_name = None - target = signing_addr = line[0] - else: - target, auth_name, signing_addr, signature = line - msg = "alias:%s:%s:%s"%(alias,target,auth_name) - print msg, signature - self.verify_message(signing_addr, signature, msg) - - # other lines are signed updates - for line in lines[1:]: - line = line.strip() - if not line: continue - line = line.split(':') - previous = target - print repr(line) - target, signature = line - self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) - - if not self.is_valid(target): - raise BaseException("Invalid bitcoin address") - - return target, signing_addr, auth_name + # line 0 + line = lines[0].strip().split(':') + if len(line) == 1: + auth_name = None + target = signing_addr = line[0] + else: + target, auth_name, signing_addr, signature = line + msg = "alias:%s:%s:%s"%(alias,target,auth_name) + print msg, signature + self.verify_message(signing_addr, signature, msg) + + # other lines are signed updates + for line in lines[1:]: + line = line.strip() + if not line: continue + line = line.split(':') + previous = target + print repr(line) + target, signature = line + self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) + + if not self.is_valid(target): + raise BaseException("Invalid bitcoin address") + + return target, signing_addr, auth_name From 4cab1db01d6f1ca020e24cb00c9020df09b66f6b Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 11:14:48 +0100 Subject: [PATCH 37/48] add accepted aliases to the list of labels --- client/gui.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/gui.py b/client/gui.py index 98d92fc7c..0efcb7acd 100644 --- a/client/gui.py +++ b/client/gui.py @@ -763,6 +763,8 @@ class BitcoinGUI: def set_send_tab(self, payto, amount, message, label, identity, signature, cmd): + self.notebook.set_current_page(1) + if signature: if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity): signing_address = self.get_alias(identity, interactive = True) @@ -777,13 +779,15 @@ class BitcoinGUI: self.wallet.receipt = (signing_address, signature, cmd) except: self.show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') - payto = amount = label = identity = '' + payto = amount = label = identity = message = '' # redundant with aliases #if label and payto: # self.labels[payto] = label + payto_address = self.get_alias(payto, interactive=True) + if payto_address: + payto = payto + ' <' + payto_address + '>' - self.notebook.set_current_page(1) self.payto_entry.set_text(payto) self.message_entry.set_text(message) self.amount_entry.set_text(amount) @@ -807,6 +811,7 @@ class BitcoinGUI: def do_clear(self, w, data): self.payto_sig.set_visible(False) + self.payto_fee_entry.set_text('') for entry in [self.payto_entry,self.amount_entry,self.message_entry]: self.set_frozen(entry,False) entry.set_text('') @@ -832,14 +837,16 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - if interactive and self.question( "Warning: the alias '%s' is self-signed. Do you want to trust address %s ?"%(alias,signing_address) ): + if interactive and self.question( "Warning: the alias '%s' is unsigned. Do you want to trust the address %s ?"%(alias,signing_address) ): self.wallet.aliases[alias] = signing_address + self.wallet.labels[target] = alias else: target = None else: if signing_address != a: if interactive and self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%alias ): self.wallet.aliases[alias] = signing_address + self.wallet.labels[target] = alias else: target = None else: @@ -847,6 +854,7 @@ class BitcoinGUI: if interactive and self.question( "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"\ %(alias,target,auth_name,signing_address)): self.wallet.authorities[signing_address] = auth_name + self.wallet.labels[target] = alias else: target = None From dac60f9b9141a1517b0f23f32d07c0cd3717c490 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 11:40:04 +0100 Subject: [PATCH 38/48] update messages --- client/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui.py b/client/gui.py index 0efcb7acd..6c8793450 100644 --- a/client/gui.py +++ b/client/gui.py @@ -837,14 +837,14 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - if interactive and self.question( "Warning: the alias '%s' is unsigned. Do you want to trust the address %s ?"%(alias,signing_address) ): + if interactive and self.question( "Warning: the alias '%s' is unsigned. Do you want to trust the address %s for this alias?"%(alias,signing_address) ): self.wallet.aliases[alias] = signing_address self.wallet.labels[target] = alias else: target = None else: if signing_address != a: - if interactive and self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%alias ): + if interactive and self.question( "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias ): self.wallet.aliases[alias] = signing_address self.wallet.labels[target] = alias else: From a26c6ca2e1e5acd1296300df47f3afa3fcfea339 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 12:50:28 +0100 Subject: [PATCH 39/48] fix --- client/gui.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/gui.py b/client/gui.py index 6c8793450..21ce8c910 100644 --- a/client/gui.py +++ b/client/gui.py @@ -784,9 +784,10 @@ class BitcoinGUI: # redundant with aliases #if label and payto: # self.labels[payto] = label - payto_address = self.get_alias(payto, interactive=True) - if payto_address: - payto = payto + ' <' + payto_address + '>' + if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', payto): + payto_address = self.get_alias(payto, interactive=True) + if payto_address: + payto = payto + ' <' + payto_address + '>' self.payto_entry.set_text(payto) self.message_entry.set_text(message) From 71e3c78dd450046de5d7ad030b36f9edb5cd767a Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 13:22:54 +0100 Subject: [PATCH 40/48] labels --- client/gui.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/client/gui.py b/client/gui.py index 21ce8c910..a4e911404 100644 --- a/client/gui.py +++ b/client/gui.py @@ -838,26 +838,31 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - if interactive and self.question( "Warning: the alias '%s' is unsigned. Do you want to trust the address %s for this alias?"%(alias,signing_address) ): + msg = "Warning: the alias '%s' is unsigned. Do you want to trust the address %s for this alias?"%(alias,signing_address) + if interactive and self.question( msg ): self.wallet.aliases[alias] = signing_address - self.wallet.labels[target] = alias else: target = None else: if signing_address != a: - if interactive and self.question( "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias ): + msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias + if interactive and self.question( msg ): self.wallet.aliases[alias] = signing_address - self.wallet.labels[target] = alias else: target = None else: if signing_address not in self.wallet.authorities.keys(): - if interactive and self.question( "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"\ - %(alias,target,auth_name,signing_address)): + msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address) + if interactive and self.question( msg ): self.wallet.authorities[signing_address] = auth_name - self.wallet.labels[target] = alias else: target = None + + if target: + # do this only if there's a tx + if target not in self.wallet.addressbook: + self.wallet.addressbook.append(target) + self.wallet.labels[target] = alias return target From 378de10581331a37f32f0e9b2a8e506772697464 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 13:24:46 +0100 Subject: [PATCH 41/48] unsigned->self-signed --- client/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index a4e911404..ae4f95c33 100644 --- a/client/gui.py +++ b/client/gui.py @@ -838,7 +838,7 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - msg = "Warning: the alias '%s' is unsigned. Do you want to trust the address %s for this alias?"%(alias,signing_address) + msg = "Warning: the alias '%s' is self-signed. Do you want to trust the address %s for this alias?"%(alias,signing_address) if interactive and self.question( msg ): self.wallet.aliases[alias] = signing_address else: From eb209a24729a026b9dad940bb9c322d31483deb2 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 15:00:12 +0100 Subject: [PATCH 42/48] gui for aliases --- client/gui.py | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/client/gui.py b/client/gui.py index ae4f95c33..d2d882185 100644 --- a/client/gui.py +++ b/client/gui.py @@ -840,14 +840,14 @@ class BitcoinGUI: if not a: msg = "Warning: the alias '%s' is self-signed. Do you want to trust the address %s for this alias?"%(alias,signing_address) if interactive and self.question( msg ): - self.wallet.aliases[alias] = signing_address + self.wallet.aliases[alias] = (signing_address, target) else: target = None else: - if signing_address != a: + if signing_address != a[0]: msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias if interactive and self.question( msg ): - self.wallet.aliases[alias] = signing_address + self.wallet.aliases[alias] = (signing_address, target) else: target = None else: @@ -859,10 +859,9 @@ class BitcoinGUI: target = None if target: - # do this only if there's a tx - if target not in self.wallet.addressbook: - self.wallet.addressbook.append(target) - self.wallet.labels[target] = alias + self.wallet.aliases[alias] = (signing_address, target) + self.update_sending_tab() + return target @@ -932,8 +931,14 @@ class BitcoinGUI: def treeview_button_press(self, treeview, event): if event.type == gtk.gdk._2BUTTON_PRESS: c = treeview.get_cursor()[0] - tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8) - self.show_message(tx_details) + if treeview == self.history_treeview: + tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8) + self.show_message(tx_details) + elif treeview == self.contacts_treeview: + m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0) + a = self.wallet.aliases.get(m) + if a: self.show_message('Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + a[0]) + def treeview_key_press(self, treeview, event): c = treeview.get_cursor()[0] @@ -941,9 +946,15 @@ class BitcoinGUI: if c and c[0] == 0: treeview.parent.grab_focus() treeview.set_cursor((0,)) - elif event.keyval == gtk.keysyms.Return and treeview == self.history_treeview: - tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8) - self.show_message(tx_details) + elif event.keyval == gtk.keysyms.Return: + if treeview == self.history_treeview: + tx_details = self.history_list.get_value( self.history_list.get_iter(c), 8) + self.show_message(tx_details) + elif treeview == self.contacts_treeview: + m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0) + a = self.wallet.aliases.get(m) + if a: self.show_message('Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + a[0]) + return False def create_history_tab(self): @@ -1033,7 +1044,10 @@ class BitcoinGUI: liststore = self.recv_list if is_recv else self.addressbook_list treeview = gtk.TreeView(model= liststore) treeview.connect('key-press-event', self.treeview_key_press) + treeview.connect('button-press-event', self.treeview_button_press) treeview.show() + if not is_recv: + self.contacts_treeview = treeview tvcolumn = gtk.TreeViewColumn('Address') treeview.append_column(tvcolumn) @@ -1174,6 +1188,11 @@ class BitcoinGUI: def update_sending_tab(self): # detect addresses that are not mine in history, add them here... self.addressbook_list.clear() + for alias, v in self.wallet.aliases.items(): + s, target = v + label = self.wallet.labels.get(alias) + self.addressbook_list.append((alias, label, '-')) + for address in self.wallet.addressbook: label = self.wallet.labels.get(address) n = 0 From 4d7ff560b11f7f62e511c1b6ad316fbee17f6528 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 15:19:48 +0100 Subject: [PATCH 43/48] gui for aliases --- client/gui.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/client/gui.py b/client/gui.py index d2d882185..2067c2d97 100644 --- a/client/gui.py +++ b/client/gui.py @@ -937,7 +937,13 @@ class BitcoinGUI: elif treeview == self.contacts_treeview: m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0) a = self.wallet.aliases.get(m) - if a: self.show_message('Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + a[0]) + if a: + if a[0] in self.wallet.authorities.keys(): + s = self.wallet.authorities.get(a[0]) + else: + s = "self" + msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0] + self.show_message(msg) def treeview_key_press(self, treeview, event): @@ -953,7 +959,13 @@ class BitcoinGUI: elif treeview == self.contacts_treeview: m = self.addressbook_list.get_value( self.addressbook_list.get_iter(c), 0) a = self.wallet.aliases.get(m) - if a: self.show_message('Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + a[0]) + if a: + if a[0] in self.wallet.authorities.keys(): + s = self.wallet.authorities.get(a[0]) + else: + s = "self" + msg = 'Alias:'+ m + '\n\nTarget: '+ a[1] + '\nSigned by: ' + s + '\nSigning address:' + a[0] + self.show_message(msg) return False From 8e5b385f773be6a3bd3875532c97ab61460891ac Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 15:27:36 +0100 Subject: [PATCH 44/48] fix --- client/gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index 2067c2d97..a6c0705db 100644 --- a/client/gui.py +++ b/client/gui.py @@ -830,7 +830,8 @@ class BitcoinGUI: target, signing_address, auth_name = self.wallet.read_alias(alias) except BaseException, e: # raise exception if verify fails (verify the chain) - self.show_message("Alias error: " + e.message) + if interactive: + self.show_message("Alias error: " + e.message) return print target, signing_address, auth_name From deb6f80d092109249287b5e1c8060c8230689ad9 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 15:37:27 +0100 Subject: [PATCH 45/48] minor --- client/gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/gui.py b/client/gui.py index a6c0705db..ae59ea819 100644 --- a/client/gui.py +++ b/client/gui.py @@ -839,7 +839,7 @@ class BitcoinGUI: if auth_name is None: a = self.wallet.aliases.get(alias) if not a: - msg = "Warning: the alias '%s' is self-signed. Do you want to trust the address %s for this alias?"%(alias,signing_address) + msg = "Warning: the alias '%s' is self-signed. The signing address is %s. Do you want to trust this alias?"%(alias,signing_address) if interactive and self.question( msg ): self.wallet.aliases[alias] = (signing_address, target) else: From 30cc2846ae4f9ef21d2de00f085971417e80432b Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 17:09:14 +0100 Subject: [PATCH 46/48] show tx receipts --- client/gui.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/client/gui.py b/client/gui.py index ae59ea819..91aa13dc0 100644 --- a/client/gui.py +++ b/client/gui.py @@ -1235,13 +1235,20 @@ class BitcoinGUI: if is_default_label: label = tx['default_label'] tooltip = tx_hash + "\n%d confirmations"%conf - tx = self.wallet.tx_history.get(tx_hash) - details = "Transaction Details:\n\n" - details+= "Transaction ID:\n" + tx_hash + "\n\n" - details+= "Status: %d confirmations\n\n"%conf - details+= "Date: %s\n\n"%time_str - details+= "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" - details+= "Outputs:\n-"+ '\n-'.join(tx['outputs']) + # tx = self.wallet.tx_history.get(tx_hash) + details = "Transaction Details:\n\n" \ + + "Transaction ID:\n" + tx_hash + "\n\n" \ + + "Status: %d confirmations\n\n"%conf \ + + "Date: %s\n\n"%time_str \ + + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \ + + "Outputs:\n-"+ '\n-'.join(tx['outputs']) + r = self.wallet.receipts.get(tx_hash) + if r: + details += "\n_______________________________________" \ + + "\n\nSigned by: " + r[0] \ + + '\n\nSigned data: ' + r[2] \ + + '\n\nSignature: ' + r[1] + self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label, format_satoshis(v,True), format_satoshis(balance), tooltip, details] ) From 3b991c7b10c588fbece46bbf26e0f4e48239ec38 Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 17:12:05 +0100 Subject: [PATCH 47/48] minor --- client/gui.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/gui.py b/client/gui.py index 91aa13dc0..bb8813421 100644 --- a/client/gui.py +++ b/client/gui.py @@ -1245,10 +1245,10 @@ class BitcoinGUI: r = self.wallet.receipts.get(tx_hash) if r: details += "\n_______________________________________" \ - + "\n\nSigned by: " + r[0] \ - + '\n\nSigned data: ' + r[2] \ - + '\n\nSignature: ' + r[1] - + + "\n\nURI signed by: " + r[0] \ + + '\n\nSigned data: ' + r[2] \ + + '\n\nSignature: ' + r[1] + self.history_list.prepend( [tx_hash, conf_icon, time_str, label, is_default_label, format_satoshis(v,True), format_satoshis(balance), tooltip, details] ) From 6464684b3b86f5388e90884e0d0d44d7431167ad Mon Sep 17 00:00:00 2001 From: thomasv Date: Tue, 7 Feb 2012 17:13:38 +0100 Subject: [PATCH 48/48] minor --- client/gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/gui.py b/client/gui.py index bb8813421..d2db96500 100644 --- a/client/gui.py +++ b/client/gui.py @@ -1245,8 +1245,8 @@ class BitcoinGUI: r = self.wallet.receipts.get(tx_hash) if r: details += "\n_______________________________________" \ - + "\n\nURI signed by: " + r[0] \ - + '\n\nSigned data: ' + r[2] \ + + '\n\nSigned URI: ' + r[2] \ + + "\n\nSigned by: " + r[0] \ + '\n\nSignature: ' + r[1]