nanotube's patch for compressed keys
[electrum-nvc.git] / lib / wallet.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19
20 import sys, base64, os, re, hashlib, copy, operator, ast, threading, random
21 import aes, ecdsa
22 from ecdsa.util import string_to_number, number_to_string
23
24 ############ functions from pywallet ##################### 
25
26 addrtype = 0
27
28 def hash_160(public_key):
29     try:
30         md = hashlib.new('ripemd160')
31         md.update(hashlib.sha256(public_key).digest())
32         return md.digest()
33     except:
34         import ripemd
35         md = ripemd.new(hashlib.sha256(public_key).digest())
36         return md.digest()
37
38
39 def public_key_to_bc_address(public_key):
40     h160 = hash_160(public_key)
41     return hash_160_to_bc_address(h160)
42
43 def hash_160_to_bc_address(h160):
44     vh160 = chr(addrtype) + h160
45     h = Hash(vh160)
46     addr = vh160 + h[0:4]
47     return b58encode(addr)
48
49 def bc_address_to_hash_160(addr):
50     bytes = b58decode(addr, 25)
51     return bytes[1:21]
52
53 def encode_point(pubkey, compressed=False):
54     order = generator_secp256k1.order()
55     p = pubkey.pubkey.point
56     x_str = ecdsa.util.number_to_string(p.x(), order)
57     y_str = ecdsa.util.number_to_string(p.y(), order)
58     if compressed:
59         return chr(2 + (p.y() & 1)) + x_str
60     else:
61         return chr(4) + pubkey.to_string() #x_str + y_str
62
63 __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
64 __b58base = len(__b58chars)
65
66 def b58encode(v):
67     """ encode v, which is a string of bytes, to base58.                
68     """
69
70     long_value = 0L
71     for (i, c) in enumerate(v[::-1]):
72         long_value += (256**i) * ord(c)
73
74     result = ''
75     while long_value >= __b58base:
76         div, mod = divmod(long_value, __b58base)
77         result = __b58chars[mod] + result
78         long_value = div
79     result = __b58chars[long_value] + result
80
81     # Bitcoin does a little leading-zero-compression:
82     # leading 0-bytes in the input become leading-1s
83     nPad = 0
84     for c in v:
85         if c == '\0': nPad += 1
86         else: break
87
88     return (__b58chars[0]*nPad) + result
89
90 def b58decode(v, length):
91     """ decode v into a string of len bytes
92     """
93     long_value = 0L
94     for (i, c) in enumerate(v[::-1]):
95         long_value += __b58chars.find(c) * (__b58base**i)
96
97     result = ''
98     while long_value >= 256:
99         div, mod = divmod(long_value, 256)
100         result = chr(mod) + result
101         long_value = div
102     result = chr(long_value) + result
103
104     nPad = 0
105     for c in v:
106         if c == __b58chars[0]: nPad += 1
107         else: break
108
109     result = chr(0)*nPad + result
110     if length is not None and len(result) != length:
111         return None
112
113     return result
114
115
116 def Hash(data):
117     return hashlib.sha256(hashlib.sha256(data).digest()).digest()
118
119 def EncodeBase58Check(vchIn):
120     hash = Hash(vchIn)
121     return b58encode(vchIn + hash[0:4])
122
123 def DecodeBase58Check(psz):
124     vchRet = b58decode(psz, None)
125     key = vchRet[0:-4]
126     csum = vchRet[-4:]
127     hash = Hash(key)
128     cs32 = hash[0:4]
129     if cs32 != csum:
130         return None
131     else:
132         return key
133
134 def PrivKeyToSecret(privkey):
135     return privkey[9:9+32]
136
137 def SecretToASecret(secret):
138     vchIn = chr(addrtype+128) + secret
139     return EncodeBase58Check(vchIn)
140
141 def ASecretToSecret(key):
142     vch = DecodeBase58Check(key)
143     if vch and vch[0] == chr(addrtype+128):
144         return vch[1:]
145     else:
146         return False
147
148 ########### end pywallet functions #######################
149
150 # URL decode
151 _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
152 urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
153
154
155 def int_to_hex(i, length=1):
156     s = hex(i)[2:].rstrip('L')
157     s = "0"*(2*length - len(s)) + s
158     return s.decode('hex')[::-1].encode('hex')
159
160
161 # AES
162 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
163 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
164
165
166
167 # secp256k1, http://www.oid-info.com/get/1.3.132.0.10
168 _p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
169 _r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
170 _b = 0x0000000000000000000000000000000000000000000000000000000000000007L
171 _a = 0x0000000000000000000000000000000000000000000000000000000000000000L
172 _Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
173 _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
174 curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
175 generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
176 oid_secp256k1 = (1,3,132,0,10)
177 SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) 
178
179
180 def filter(s): 
181     out = re.sub('( [^\n]*|)\n','',s)
182     out = out.replace(' ','')
183     out = out.replace('\n','')
184     return out
185
186 def raw_tx( inputs, outputs, for_sig = None ):
187     s  = int_to_hex(1,4)                                     +   '     version\n' 
188     s += int_to_hex( len(inputs) )                           +   '     number of inputs\n'
189     for i in range(len(inputs)):
190         _, _, p_hash, p_index, p_script, pubkey, sig = inputs[i]
191         s += p_hash.decode('hex')[::-1].encode('hex')        +  '     prev hash\n'
192         s += int_to_hex(p_index,4)                           +  '     prev index\n'
193         if for_sig is None:
194             sig = sig + chr(1)                               # hashtype
195             script  = int_to_hex( len(sig))                  +  '     push %d bytes\n'%len(sig)
196             script += sig.encode('hex')                      +  '     sig\n'
197             pubkey = chr(4) + pubkey
198             script += int_to_hex( len(pubkey))               +  '     push %d bytes\n'%len(pubkey)
199             script += pubkey.encode('hex')                   +  '     pubkey\n'
200         elif for_sig==i:
201             script = p_script                                +  '     scriptsig \n'
202         else:
203             script=''
204         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
205         s += script
206         s += "ffffffff"                                      +  '     sequence\n'
207     s += int_to_hex( len(outputs) )                          +  '     number of outputs\n'
208     for output in outputs:
209         addr, amount = output
210         s += int_to_hex( amount, 8)                          +  '     amount: %d\n'%amount 
211         script = '76a9'                                      # op_dup, op_hash_160
212         script += '14'                                       # push 0x14 bytes
213         script += bc_address_to_hash_160(addr).encode('hex')
214         script += '88ac'                                     # op_equalverify, op_checksig
215         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
216         s += script                                          +  '     script \n'
217     s += int_to_hex(0,4)                                     # lock time
218     if for_sig is not None: s += int_to_hex(1, 4)            # hash type
219     return s
220
221
222
223
224 def format_satoshis(x, is_diff=False, num_zeros = 0):
225     from decimal import Decimal
226     s = Decimal(x)
227     sign, digits, exp = s.as_tuple()
228     digits = map(str, digits)
229     while len(digits) < 9:
230         digits.insert(0,'0')
231     digits.insert(-8,'.')
232     s = ''.join(digits).rstrip('0')
233     if sign: 
234         s = '-' + s
235     elif is_diff:
236         s = "+" + s
237
238     p = s.find('.')
239     s += "0"*( 1 + num_zeros - ( len(s) - p ))
240     s += " "*( 9 - ( len(s) - p ))
241     s = " "*( 5 - ( p )) + s
242     return s
243
244
245 from version import ELECTRUM_VERSION, SEED_VERSION
246 from interface import DEFAULT_SERVERS
247
248
249
250
251 class Wallet:
252     def __init__(self, gui_callback = lambda: None):
253
254         self.electrum_version = ELECTRUM_VERSION
255         self.seed_version = SEED_VERSION
256         self.gui_callback = gui_callback
257
258         self.gap_limit = 5           # configuration
259         self.fee = 100000
260         self.num_zeros = 0
261         self.master_public_key = ''
262
263         # saved fields
264         self.use_encryption = False
265         self.addresses = []          # receiving addresses visible for user
266         self.change_addresses = []   # addresses used as change
267         self.seed = ''               # encrypted
268         self.history = {}
269         self.labels = {}             # labels for addresses and transactions
270         self.aliases = {}            # aliases for addresses
271         self.authorities = {}        # trusted addresses
272         self.frozen_addresses = []
273         self.prioritized_addresses = []
274         self.expert_mode = False
275         
276         self.receipts = {}           # signed URIs
277         self.receipt = None          # next receipt
278         self.addressbook = []        # outgoing addresses, for payments
279
280         # not saved
281         self.tx_history = {}
282
283         self.imported_keys = {}
284         self.remote_url = None
285
286         self.was_updated = True
287         self.blocks = -1
288         self.banner = ''
289
290         # there is a difference between self.up_to_date and self.is_up_to_date()
291         # self.is_up_to_date() returns true when all requests have been answered and processed
292         # self.up_to_date is true when the wallet is synchronized (stronger requirement)
293         self.up_to_date_event = threading.Event()
294         self.up_to_date_event.clear()
295         self.up_to_date = False
296         self.lock = threading.Lock()
297         self.tx_event = threading.Event()
298
299         self.pick_random_server()
300
301
302
303     def pick_random_server(self):
304         self.server = random.choice( DEFAULT_SERVERS )         # random choice when the wallet is created
305
306     def is_up_to_date(self):
307         return self.interface.responses.empty() and not self.interface.unanswered_requests
308
309     def set_server(self, server):
310         # raise an error if the format isnt correct
311         a,b,c = server.split(':')
312         b = int(b)
313         assert c in ['t','h','n']
314         # set the server
315         if server != self.server:
316             self.server = server
317             self.save()
318             self.interface.is_connected = False  # this exits the polling loop
319             self.interface.poke()
320
321     def set_path(self, wallet_path):
322
323         if wallet_path is not None:
324             self.path = wallet_path
325         else:
326             # backward compatibility: look for wallet file in the default data directory
327             if "HOME" in os.environ:
328                 wallet_dir = os.path.join( os.environ["HOME"], '.electrum')
329             elif "LOCALAPPDATA" in os.environ:
330                 wallet_dir = os.path.join( os.environ["LOCALAPPDATA"], 'Electrum' )
331             elif "APPDATA" in os.environ:
332                 wallet_dir = os.path.join( os.environ["APPDATA"], 'Electrum' )
333             else:
334                 raise BaseException("No home directory found in environment variables.")
335
336             if not os.path.exists( wallet_dir ): os.mkdir( wallet_dir )
337             self.path = os.path.join( wallet_dir, 'electrum.dat' )
338
339     def import_key(self, keypair, password):
340         address, key = keypair.split(':')
341         if not self.is_valid(address):
342             raise BaseException('Invalid Bitcoin address')
343         if address in self.all_addresses():
344             raise BaseException('Address already in wallet')
345         b = ASecretToSecret( key )
346         if not b: 
347             raise BaseException('Unsupported key format')
348         secexp = int( b.encode('hex'), 16)
349         private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 )
350         # sanity check
351         public_key = private_key.get_verifying_key()
352         if not address == public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ):
353             raise BaseException('Address does not match private key')
354         self.imported_keys[address] = self.pw_encode( key, password )
355
356     def new_seed(self, password):
357         seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
358         #self.init_mpk(seed)
359         # encrypt
360         self.seed = self.pw_encode( seed, password )
361
362
363     def init_mpk(self,seed):
364         # public key
365         curve = SECP256k1
366         secexp = self.stretch_key(seed)
367         master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
368         self.master_public_key = master_private_key.get_verifying_key().to_string()
369
370     def all_addresses(self):
371         return self.addresses + self.change_addresses + self.imported_keys.keys()
372
373     def is_mine(self, address):
374         return address in self.all_addresses()
375
376     def is_change(self, address):
377         return address in self.change_addresses
378
379     def is_valid(self,addr):
380         ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z')
381         if not ADDRESS_RE.match(addr): return False
382         try:
383             h = bc_address_to_hash_160(addr)
384         except:
385             return False
386         return addr == hash_160_to_bc_address(h)
387
388     def stretch_key(self,seed):
389         oldseed = seed
390         for i in range(100000):
391             seed = hashlib.sha256(seed + oldseed).digest()
392         return string_to_number( seed )
393
394     def get_sequence(self,n,for_change):
395         return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key ) )
396
397     def get_private_key_base58(self, address, password):
398         pk = self.get_private_key(address, password)
399         if pk is None: return None
400         return SecretToASecret( pk )
401
402     def get_private_key(self, address, password):
403         """  Privatekey(type,n) = Master_private_key + H(n|S|type)  """
404         order = generator_secp256k1.order()
405         
406         if address in self.imported_keys.keys():
407             b = self.pw_decode( self.imported_keys[address], password )
408             if not b: return None
409             b = ASecretToSecret( b )
410             secexp = int( b.encode('hex'), 16)
411         else:
412             if address in self.addresses:
413                 n = self.addresses.index(address)
414                 for_change = False
415             elif address in self.change_addresses:
416                 n = self.change_addresses.index(address)
417                 for_change = True
418             else:
419                 raise BaseException("unknown address")
420             try:
421                 seed = self.pw_decode( self.seed, password)
422             except:
423                 raise BaseException("Invalid password")
424             if not seed: return None
425             secexp = self.stretch_key(seed)
426             secexp = ( secexp + self.get_sequence(n,for_change) ) % order
427
428         pk = number_to_string(secexp,order)
429         return pk
430
431     def msg_magic(self, message):
432         return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
433
434     def sign_message(self, address, message, password):
435         private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 )
436         public_key = private_key.get_verifying_key()
437         signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string )
438         assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string)
439         for i in range(4):
440             sig = base64.b64encode( chr(27+i) + signature )
441             try:
442                 self.verify_message( address, sig, message)
443                 return sig
444             except:
445                 continue
446         else:
447             raise BaseException("error: cannot sign message")
448
449
450     def verify_message(self, address, signature, message):
451         """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """
452         from ecdsa import numbertheory, ellipticcurve, util
453         import msqr
454         curve = curve_secp256k1
455         G = generator_secp256k1
456         order = G.order()
457         # extract r,s from signature
458         sig = base64.b64decode(signature)
459         if len(sig) != 65: raise BaseException("Wrong encoding")
460         r,s = util.sigdecode_string(sig[1:], order)
461         nV = ord(sig[0])
462         if nV < 27 or nV >= 35:
463             raise BaseException("Bad encoding")
464         if nV >= 31:
465             compressed = True
466             nV -= 4
467         else:
468             compressed = False
469
470         recid = nV - 27
471         # 1.1
472         x = r + (recid/2) * order
473         # 1.3
474         alpha = ( x * x * x  + curve.a() * x + curve.b() ) % curve.p()
475         beta = msqr.modular_sqrt(alpha, curve.p())
476         y = beta if (beta - recid) % 2 == 0 else curve.p() - beta
477         # 1.4 the constructor checks that nR is at infinity
478         R = ellipticcurve.Point(curve, x, y, order)
479         # 1.5 compute e from message:
480         h = Hash( self.msg_magic( message ) )
481         e = string_to_number(h)
482         minus_e = -e % order
483         # 1.6 compute Q = r^-1 (sR - eG)
484         inv_r = numbertheory.inverse_mod(r,order)
485         Q = inv_r * ( s * R + minus_e * G )
486         public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 )
487         # check that Q is the public key
488         public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
489         # check that we get the original signing address
490         addr = public_key_to_bc_address( encode_point(public_key, compressed) )
491         if address != addr:
492             raise BaseException("Bad signature")
493     
494
495     def create_new_address(self, for_change):
496         """   Publickey(type,n) = Master_public_key + H(n|S|type)*point  """
497         curve = SECP256k1
498         n = len(self.change_addresses) if for_change else len(self.addresses)
499         z = self.get_sequence(n,for_change)
500         master_public_key = ecdsa.VerifyingKey.from_string( self.master_public_key, curve = SECP256k1 )
501         pubkey_point = master_public_key.pubkey.point + z*curve.generator
502         public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
503         address = public_key_to_bc_address( '04'.decode('hex') + public_key2.to_string() )
504         if for_change:
505             self.change_addresses.append(address)
506         else:
507             self.addresses.append(address)
508
509         self.history[address] = []
510         print address
511         return address
512
513
514     def change_gap_limit(self, value):
515         if value >= self.gap_limit:
516             self.gap_limit = value
517             self.save()
518             self.interface.poke()
519             return True
520
521         elif value >= self.min_acceptable_gap():
522             k = self.num_unused_trailing_addresses()
523             n = len(self.addresses) - k + value
524             self.addresses = self.addresses[0:n]
525             self.gap_limit = value
526             self.save()
527             return True
528         else:
529             return False
530
531     def num_unused_trailing_addresses(self):
532         k = 0
533         for a in self.addresses[::-1]:
534             if self.history.get(a):break
535             k = k + 1
536         return k
537
538     def min_acceptable_gap(self):
539         # fixme: this assumes wallet is synchronized
540         n = 0
541         nmax = 0
542         k = self.num_unused_trailing_addresses()
543         for a in self.addresses[0:-k]:
544             if self.history.get(a):
545                 n = 0
546             else:
547                 n += 1
548                 if n > nmax: nmax = n
549         return nmax + 1
550
551
552     def synchronize(self):
553         if not self.master_public_key:
554             return []
555
556         new_addresses = []
557         while True:
558             if self.change_addresses == []:
559                 new_addresses.append( self.create_new_address(True) )
560                 continue
561             a = self.change_addresses[-1]
562             if self.history.get(a):
563                 new_addresses.append( self.create_new_address(True) )
564             else:
565                 break
566
567         n = self.gap_limit
568         while True:
569             if len(self.addresses) < n:
570                 new_addresses.append( self.create_new_address(False) )
571                 continue
572             if map( lambda a: self.history.get(a), self.addresses[-n:] ) == n*[[]]:
573                 break
574             else:
575                 new_addresses.append( self.create_new_address(False) )
576
577         if self.remote_url:
578             num = self.get_remote_number()
579             while len(self.addresses)<num:
580                 new_addresses.append( self.create_new_address(False) )
581
582         return new_addresses
583
584
585     def get_remote_number(self):
586         import jsonrpclib
587         server = jsonrpclib.Server(self.remote_url)
588         out = server.getnum()
589         return out
590
591     def get_remote_mpk(self):
592         import jsonrpclib
593         server = jsonrpclib.Server(self.remote_url)
594         out = server.getkey()
595         return out
596
597     def is_found(self):
598         return (len(self.change_addresses) > 1 ) or ( len(self.addresses) > self.gap_limit )
599
600     def fill_addressbook(self):
601         for tx in self.tx_history.values():
602             if tx['value']<0:
603                 for i in tx['outputs']:
604                     if not self.is_mine(i) and i not in self.addressbook:
605                         self.addressbook.append(i)
606         # redo labels
607         self.update_tx_labels()
608
609
610     def save(self):
611         s = {
612             'seed_version':self.seed_version,
613             'use_encryption':self.use_encryption,
614             'master_public_key': self.master_public_key.encode('hex'),
615             'fee':self.fee,
616             'server':self.server,
617             'seed':self.seed,
618             'addresses':self.addresses,
619             'change_addresses':self.change_addresses,
620             'history':self.history, 
621             'labels':self.labels,
622             'contacts':self.addressbook,
623             'imported_keys':self.imported_keys,
624             'aliases':self.aliases,
625             'authorities':self.authorities,
626             'receipts':self.receipts,
627             'num_zeros':self.num_zeros,
628             'frozen_addresses':self.frozen_addresses,
629             'prioritized_addresses':self.prioritized_addresses,
630             'expert_mode':self.expert_mode,
631             'gap_limit':self.gap_limit,
632             }
633         f = open(self.path,"w")
634         f.write( repr(s) )
635         f.close()
636         import stat
637         os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
638
639     def read(self):
640         import interface
641
642         upgrade_msg = """This wallet seed is deprecated. Please run upgrade.py for a diagnostic."""
643         self.file_exists = False
644         try:
645             f = open(self.path,"r")
646             data = f.read()
647             f.close()
648         except:
649             return
650         data = interface.old_to_new(data)
651         try:
652             d = ast.literal_eval( data )
653             self.seed_version = d.get('seed_version')
654             self.master_public_key = d.get('master_public_key').decode('hex')
655             self.use_encryption = d.get('use_encryption')
656             self.fee = int( d.get('fee') )
657             self.seed = d.get('seed')
658             self.server = d.get('server')
659             #blocks = d.get('blocks')
660             self.addresses = d.get('addresses')
661             self.change_addresses = d.get('change_addresses')
662             self.history = d.get('history')
663             self.labels = d.get('labels')
664             self.addressbook = d.get('contacts')
665             self.imported_keys = d.get('imported_keys',{})
666             self.aliases = d.get('aliases',{})
667             self.authorities = d.get('authorities',{})
668             self.receipts = d.get('receipts',{})
669             self.num_zeros = d.get('num_zeros',0)
670             self.frozen_addresses = d.get('frozen_addresses',[])
671             self.prioritized_addresses = d.get('prioritized_addresses',[])
672             self.expert_mode = d.get('expert_mode',False)
673             self.gap_limit = d.get('gap_limit',5)
674         except:
675             raise BaseException("cannot read wallet file")
676
677         self.update_tx_history()
678
679         if self.seed_version != SEED_VERSION:
680             raise BaseException(upgrade_msg)
681
682         if self.remote_url: assert self.master_public_key.encode('hex') == self.get_remote_mpk()
683
684         self.file_exists = True
685
686
687     def get_address_flags(self, addr):
688         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
689         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
690         return flags
691         
692
693     def get_addr_balance(self, addr):
694         assert self.is_mine(addr)
695         h = self.history.get(addr,[])
696         c = u = 0
697         for item in h:
698             v = item['value']
699             if item['height']:
700                 c += v
701             else:
702                 u += v
703         return c, u
704
705     def get_balance(self):
706         conf = unconf = 0
707         for addr in self.all_addresses(): 
708             c, u = self.get_addr_balance(addr)
709             conf += c
710             unconf += u
711         return conf, unconf
712
713
714     def choose_tx_inputs( self, amount, fixed_fee, from_addr = None ):
715         """ todo: minimize tx size """
716         total = 0
717         fee = self.fee if fixed_fee is None else fixed_fee
718
719         coins = []
720         prioritized_coins = []
721         domain = [from_addr] if from_addr else self.all_addresses()
722         for i in self.frozen_addresses:
723             if i in domain: domain.remove(i)
724
725         for i in self.prioritized_addresses:
726             if i in domain: domain.remove(i)
727
728         for addr in domain:
729             h = self.history.get(addr)
730             if h is None: continue
731             for item in h:
732                 if item.get('raw_output_script'):
733                     coins.append( (addr,item))
734
735         coins = sorted( coins, key = lambda x: x[1]['timestamp'] )
736
737         for addr in self.prioritized_addresses:
738             h = self.history.get(addr)
739             if h is None: continue
740             for item in h:
741                 if item.get('raw_output_script'):
742                     prioritized_coins.append( (addr,item))
743
744         prioritized_coins = sorted( prioritized_coins, key = lambda x: x[1]['timestamp'] )
745
746         inputs = []
747         coins = prioritized_coins + coins
748
749         for c in coins: 
750             addr, item = c
751             v = item.get('value')
752             total += v
753             inputs.append((addr, v, item['tx_hash'], item['index'], item['raw_output_script'], None, None) )
754             fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
755             if total >= amount + fee: break
756         else:
757             #print "not enough funds: %s %s"%(format_satoshis(total), format_satoshis(fee))
758             inputs = []
759         return inputs, total, fee
760
761     def choose_tx_outputs( self, to_addr, amount, fee, total, change_addr=None ):
762         outputs = [ (to_addr, amount) ]
763         change_amount = total - ( amount + fee )
764         if change_amount != 0:
765             # normally, the update thread should ensure that the last change address is unused
766             if not change_addr:
767                 change_addr = self.change_addresses[-1]
768             outputs.append( ( change_addr,  change_amount) )
769         return outputs
770
771     def sign_inputs( self, inputs, outputs, password ):
772         s_inputs = []
773         for i in range(len(inputs)):
774             addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
775             private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 )
776             public_key = private_key.get_verifying_key()
777             pubkey = public_key.to_string()
778             tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
779             sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
780             assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
781             s_inputs.append( (addr, v, p_hash, p_pos, p_scriptPubKey, pubkey, sig) )
782         return s_inputs
783
784     def pw_encode(self, s, password):
785         if password:
786             secret = Hash(password)
787             return EncodeAES(secret, s)
788         else:
789             return s
790
791     def pw_decode(self, s, password):
792         if password is not None:
793             secret = Hash(password)
794             d = DecodeAES(secret, s)
795             if s == self.seed:
796                 try:
797                     d.decode('hex')
798                 except:
799                     raise BaseException("Invalid password")
800             return d
801         else:
802             return s
803
804     def get_status(self, address):
805         h = self.history.get(address)
806         if not h:
807             status = None
808         else:
809             lastpoint = h[-1]
810             status = lastpoint['block_hash']
811             if status == 'mempool': 
812                 status = status + ':%d'% len(h)
813         return status
814
815     def receive_status_callback(self, addr, status):
816         with self.lock:
817             if self.get_status(addr) != status:
818                 #print "updating status for", addr, status
819                 self.interface.get_history(addr)
820
821     def receive_history_callback(self, addr, data): 
822         #print "updating history for", addr
823         with self.lock:
824             self.history[addr] = data
825             self.update_tx_history()
826             self.save()
827
828     def get_tx_history(self):
829         lines = self.tx_history.values()
830         lines = sorted(lines, key=operator.itemgetter("timestamp"))
831         return lines
832
833     def update_tx_history(self):
834         self.tx_history= {}
835         for addr in self.all_addresses():
836             h = self.history.get(addr)
837             if h is None: continue
838             for tx in h:
839                 tx_hash = tx['tx_hash']
840                 line = self.tx_history.get(tx_hash)
841                 if not line:
842                     self.tx_history[tx_hash] = copy.copy(tx)
843                     line = self.tx_history.get(tx_hash)
844                 else:
845                     line['value'] += tx['value']
846                 if line['height'] == 0:
847                     line['timestamp'] = 1e12
848         self.update_tx_labels()
849
850     def update_tx_labels(self):
851         for tx in self.tx_history.values():
852             default_label = ''
853             if tx['value']<0:
854                 for o_addr in tx['outputs']:
855                     if not self.is_mine(o_addr):
856                         dest_label = self.labels.get(o_addr)
857                         if dest_label:
858                             default_label = 'to: ' + dest_label
859                         else:
860                             default_label = 'to: ' + o_addr
861             else:
862                 for o_addr in tx['outputs']:
863                     if self.is_mine(o_addr) and not self.is_change(o_addr):
864                         break
865                 else:
866                     for o_addr in tx['outputs']:
867                         if self.is_mine(o_addr):
868                             break
869                     else:
870                         o_addr = None
871
872                 if o_addr:
873                     dest_label = self.labels.get(o_addr)
874                     if dest_label:
875                         default_label = 'at: ' + dest_label
876                     else:
877                         default_label = 'at: ' + o_addr
878
879             tx['default_label'] = default_label
880
881     def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, from_addr= None):
882         if not self.is_valid(to_address):
883             raise BaseException("Invalid address")
884         inputs, total, fee = self.choose_tx_inputs( amount, fee, from_addr )
885         if not inputs:
886             raise BaseException("Not enough funds")
887         outputs = self.choose_tx_outputs( to_address, amount, fee, total, change_addr )
888         s_inputs = self.sign_inputs( inputs, outputs, password )
889
890         tx = filter( raw_tx( s_inputs, outputs ) )
891         if to_address not in self.addressbook:
892             self.addressbook.append(to_address)
893         if label: 
894             tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
895             self.labels[tx_hash] = label
896
897         return tx
898
899     def sendtx(self, tx):
900         tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
901         self.tx_event.clear()
902         self.interface.send([('blockchain.transaction.broadcast', [tx])])
903         self.tx_event.wait()
904         out = self.tx_result 
905         if out != tx_hash:
906             return False, "error: " + out
907         if self.receipt:
908             self.receipts[tx_hash] = self.receipt
909             self.receipt = None
910         return True, out
911
912
913     def read_alias(self, alias):
914         # this might not be the right place for this function.
915         import urllib
916
917         m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
918         m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
919         if m1:
920             url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) 
921         elif m2:
922             url = 'http://' + alias + '/bitcoin.id'
923         else:
924             return ''
925         try:
926             lines = urllib.urlopen(url).readlines()
927         except:
928             return ''
929
930         # line 0
931         line = lines[0].strip().split(':')
932         if len(line) == 1:
933             auth_name = None
934             target = signing_addr = line[0]
935         else:
936             target, auth_name, signing_addr, signature = line
937             msg = "alias:%s:%s:%s"%(alias,target,auth_name)
938             print msg, signature
939             self.verify_message(signing_addr, signature, msg)
940         
941         # other lines are signed updates
942         for line in lines[1:]:
943             line = line.strip()
944             if not line: continue
945             line = line.split(':')
946             previous = target
947             print repr(line)
948             target, signature = line
949             self.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
950
951         if not self.is_valid(target):
952             raise BaseException("Invalid bitcoin address")
953
954         return target, signing_addr, auth_name
955
956     def update_password(self, seed, old_password, new_password):
957         if new_password == '': new_password = None
958         self.use_encryption = (new_password != None)
959         self.seed = self.pw_encode( seed, new_password)
960         for k in self.imported_keys.keys():
961             a = self.imported_keys[k]
962             b = self.pw_decode(a, old_password)
963             c = self.pw_encode(b, new_password)
964             self.imported_keys[k] = c
965         self.save()
966
967     def get_alias(self, alias, interactive = False, show_message=None, question = None):
968         try:
969             target, signing_address, auth_name = self.read_alias(alias)
970         except BaseException, e:
971             # raise exception if verify fails (verify the chain)
972             if interactive:
973                 show_message("Alias error: " + str(e))
974             return
975
976         print target, signing_address, auth_name
977
978         if auth_name is None:
979             a = self.aliases.get(alias)
980             if not a:
981                 msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
982                 if interactive and question( msg ):
983                     self.aliases[alias] = (signing_address, target)
984                 else:
985                     target = None
986             else:
987                 if signing_address != a[0]:
988                     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
989                     if interactive and question( msg ):
990                         self.aliases[alias] = (signing_address, target)
991                     else:
992                         target = None
993         else:
994             if signing_address not in self.authorities.keys():
995                 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)
996                 if interactive and question( msg ):
997                     self.authorities[signing_address] = auth_name
998                 else:
999                     target = None
1000
1001         if target:
1002             self.aliases[alias] = (signing_address, target)
1003             
1004         return target
1005
1006
1007     def parse_url(self, url, show_message, question):
1008         o = url[8:].split('?')
1009         address = o[0]
1010         if len(o)>1:
1011             params = o[1].split('&')
1012         else:
1013             params = []
1014
1015         amount = label = message = signature = identity = ''
1016         for p in params:
1017             k,v = p.split('=')
1018             uv = urldecode(v)
1019             if k == 'amount': amount = uv
1020             elif k == 'message': message = uv
1021             elif k == 'label': label = uv
1022             elif k == 'signature':
1023                 identity, signature = uv.split(':')
1024                 url = url.replace('&%s=%s'%(k,v),'')
1025             else: 
1026                 print k,v
1027
1028         if label and self.labels.get(address) != label:
1029             if question('Give label "%s" to address %s ?'%(label,address)):
1030                 if address not in self.addressbook and address not in self.all_addresses(): 
1031                     self.addressbook.append(address)
1032                 self.labels[address] = label
1033
1034         if signature:
1035             if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
1036                 signing_address = self.get_alias(identity, True, show_message, question)
1037             elif self.is_valid(identity):
1038                 signing_address = identity
1039             else:
1040                 signing_address = None
1041             if not signing_address:
1042                 return
1043             try:
1044                 self.verify_message(signing_address, signature, url )
1045                 self.receipt = (signing_address, signature, url)
1046             except:
1047                 show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
1048                 address = amount = label = identity = message = ''
1049
1050         if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', address):
1051             payto_address = self.get_alias(address, True, show_message, question)
1052             if payto_address:
1053                 address = address + ' <' + payto_address + '>'
1054
1055         return address, amount, label, message, signature, identity, url
1056
1057
1058     def update(self):
1059         self.interface.poke()
1060         self.up_to_date_event.wait(10000000000)
1061
1062
1063     def start_session(self, interface):
1064         self.interface = interface
1065         self.interface.send([('server.banner',[]), ('blockchain.numblocks.subscribe',[]), ('server.peers.subscribe',[])])
1066         self.interface.subscribe(self.all_addresses())
1067
1068
1069     def freeze(self,addr):
1070         if addr in self.all_addresses() and addr not in self.frozen_addresses:
1071             self.unprioritize(addr)
1072             self.frozen_addresses.append(addr)
1073             self.save()
1074             return True
1075         else:
1076             return False
1077
1078     def unfreeze(self,addr):
1079         if addr in self.all_addresses() and addr in self.frozen_addresses:
1080             self.frozen_addresses.remove(addr)
1081             self.save()
1082             return True
1083         else:
1084             return False
1085
1086     def prioritize(self,addr):
1087         if addr in self.all_addresses() and addr not in self.prioritized_addresses:
1088             self.unfreeze(addr)
1089             self.prioritized_addresses.append(addr)
1090             self.save()
1091             return True
1092         else:
1093             return False
1094
1095     def unprioritize(self,addr):
1096         if addr in self.all_addresses() and addr in self.prioritized_addresses:
1097             self.prioritized_addresses.remove(addr)
1098             self.save()
1099             return True
1100         else:
1101             return False