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