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