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