bug fix: seed_version
[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 aes
30 import Queue
31 import time
32
33 from util import print_msg, print_error, format_satoshis
34 from bitcoin import *
35 from account import *
36 from transaction import Transaction
37 from plugins import run_hook
38
39 COINBASE_MATURITY = 100
40
41 # AES encryption
42 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
43 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
44
45 def pw_encode(s, password):
46     if password:
47         secret = Hash(password)
48         return EncodeAES(secret, s)
49     else:
50         return s
51
52 def pw_decode(s, password):
53     if password is not None:
54         secret = Hash(password)
55         try:
56             d = DecodeAES(secret, s)
57         except:
58             raise BaseException('Invalid password')
59         return d
60     else:
61         return s
62
63
64
65
66
67 from version import ELECTRUM_VERSION, SEED_VERSION
68
69
70 class WalletStorage:
71
72     def __init__(self, config):
73         self.lock = threading.Lock()
74         self.data = {}
75         self.file_exists = False
76         self.init_path(config)
77         print_error( "wallet path", self.path )
78         if self.path:
79             self.read(self.path)
80
81
82     def init_path(self, config):
83         """Set the path of the wallet."""
84
85         path = config.get('wallet_path')
86         if not path:
87             path = config.get('default_wallet_path')
88         if path is not None:
89             self.path = path
90             return
91
92         self.path = os.path.join(config.path, "electrum.dat")
93
94
95     def read(self, path):
96         """Read the contents of the wallet file."""
97         try:
98             with open(self.path, "r") as f:
99                 data = f.read()
100         except IOError:
101             return
102         try:
103             d = ast.literal_eval( data )  #parse raw data from reading wallet file
104         except:
105             raise IOError("Cannot read wallet file.")
106
107         self.data = d
108         self.file_exists = True
109
110
111     def get(self, key, default=None):
112         return self.data.get(key, default)
113
114     def put(self, key, value, save = True):
115
116         with self.lock:
117             if value is not None:
118                 self.data[key] = value
119             else:
120                 self.data.pop[key]
121             if save: 
122                 self.write()
123
124     def write(self):
125         s = repr(self.data)
126         f = open(self.path,"w")
127         f.write( s )
128         f.close()
129         if 'ANDROID_DATA' not in os.environ:
130             import stat
131             os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
132
133
134 class Wallet:
135
136     def __init__(self, storage):
137
138         self.storage = storage
139         self.electrum_version = ELECTRUM_VERSION
140         self.gap_limit_for_change = 3 # constant
141
142         # saved fields
143         self.seed_version          = storage.get('seed_version', SEED_VERSION)
144
145         self.gap_limit             = storage.get('gap_limit', 5)
146         self.use_change            = storage.get('use_change',True)
147         self.use_encryption        = storage.get('use_encryption', False)
148         self.seed                  = storage.get('seed', '')               # encrypted
149         self.labels                = storage.get('labels', {})
150         self.frozen_addresses      = storage.get('frozen_addresses',[])
151         self.prioritized_addresses = storage.get('prioritized_addresses',[])
152         self.addressbook           = storage.get('contacts', [])
153
154         self.imported_keys         = storage.get('imported_keys',{})
155         self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
156
157         self.fee                   = int(storage.get('fee_per_kb',20000))
158
159         self.master_public_keys = storage.get('master_public_keys',{})
160         self.master_private_keys = storage.get('master_private_keys', {})
161
162         self.next_addresses = storage.get('next_addresses',{})
163
164         if self.seed_version < 4:
165             raise ValueError("This wallet seed is deprecated.")
166
167         self.load_accounts()
168
169         self.transactions = {}
170         tx_list = self.storage.get('transactions',{})
171         for k,v in tx_list.items():
172             try:
173                 tx = Transaction(v)
174             except:
175                 print_msg("Warning: Cannot deserialize transactions. skipping")
176                 continue
177
178             self.add_extra_addresses(tx)
179             self.transactions[k] = tx
180
181         for h,tx in self.transactions.items():
182             if not self.check_new_tx(h, tx):
183                 print_error("removing unreferenced tx", h)
184                 self.transactions.pop(h)
185
186
187         # not saved
188         self.prevout_values = {}     # my own transaction outputs
189         self.spent_outputs = []
190
191         # spv
192         self.verifier = None
193
194         # there is a difference between wallet.up_to_date and interface.is_up_to_date()
195         # interface.is_up_to_date() returns true when all requests have been answered and processed
196         # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
197         
198         self.up_to_date = False
199         self.lock = threading.Lock()
200         self.transaction_lock = threading.Lock()
201         self.tx_event = threading.Event()
202
203         for tx_hash, tx in self.transactions.items():
204             self.update_tx_outputs(tx_hash)
205
206
207     def add_extra_addresses(self, tx):
208         h = tx.hash()
209         # find the address corresponding to pay-to-pubkey inputs
210         tx.add_extra_addresses(self.transactions)
211         for o in tx.d.get('outputs'):
212             if o.get('is_pubkey'):
213                 for tx2 in self.transactions.values():
214                     tx2.add_extra_addresses({h:tx})
215
216             
217
218
219     def set_up_to_date(self,b):
220         with self.lock: self.up_to_date = b
221
222
223     def is_up_to_date(self):
224         with self.lock: return self.up_to_date
225
226
227     def update(self):
228         self.up_to_date = False
229         while not self.is_up_to_date(): 
230             time.sleep(0.1)
231
232
233     def import_key(self, sec, password):
234         # check password
235         seed = self.decode_seed(password)
236         try:
237             address = address_from_private_key(sec)
238         except:
239             raise BaseException('Invalid private key')
240
241         if self.is_mine(address):
242             raise BaseException('Address already in wallet')
243         
244         # store the originally requested keypair into the imported keys table
245         self.imported_keys[address] = pw_encode(sec, password )
246         self.storage.put('imported_keys', self.imported_keys, True)
247         if self.synchronizer:
248             self.synchronizer.subscribe_to_addresses([address])
249         return address
250         
251     def delete_imported_key(self, addr):
252         if addr in self.imported_keys:
253             self.imported_keys.pop(addr)
254             self.storage.put('imported_keys', self.imported_keys, True)
255
256
257     def init_seed(self, seed):
258         if self.seed: raise BaseException("a seed exists")
259         if not seed: 
260             seed = random_seed(128)
261         self.seed = seed
262
263
264     def save_seed(self):
265         self.storage.put('seed', self.seed, True)
266         self.storage.put('seed_version', self.seed_version, True)
267
268     def create_watching_only_wallet(self, c0, K0):
269         cK0 = ""
270         self.master_public_keys = {
271             "m/0'/": (c0, K0, cK0),
272             }
273         self.storage.put('master_public_keys', self.master_public_keys, True)
274         self.create_account('1','Main account')
275
276
277     def create_accounts(self): 
278         # create default account
279         self.create_master_keys('1', self.seed)
280         self.create_account('1','Main account')
281
282
283     def create_master_keys(self, account_type, seed):
284         master_k, master_c, master_K, master_cK = bip32_init(self.seed)
285         if account_type == '1':
286             k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
287             self.master_public_keys["m/0'/"] = (c0, K0, cK0)
288             self.master_private_keys["m/0'/"] = k0
289         elif account_type == '2of2':
290             k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
291             k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
292             self.master_public_keys["m/1'/"] = (c1, K1, cK1)
293             self.master_public_keys["m/2'/"] = (c2, K2, cK2)
294             self.master_private_keys["m/1'/"] = k1
295             self.master_private_keys["m/2'/"] = k2
296         elif account_type == '2of3':
297             k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
298             k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
299             k5, c5, K5, cK5 = bip32_private_derivation(master_k, master_c, "m/", "m/5'/")
300             self.master_public_keys["m/3'/"] = (c3, K3, cK3)
301             self.master_public_keys["m/4'/"] = (c4, K4, cK4)
302             self.master_public_keys["m/5'/"] = (c5, K5, cK5)
303             self.master_private_keys["m/3'/"] = k3
304             self.master_private_keys["m/4'/"] = k4
305             self.master_private_keys["m/5'/"] = k5
306
307         self.storage.put('master_public_keys', self.master_public_keys, True)
308         self.storage.put('master_private_keys', self.master_private_keys, True)
309
310     def has_master_public_keys(self, account_type):
311         if account_type == '1':
312             return "m/0'/" in self.master_public_keys
313         elif account_type == '2of2':
314             return set(["m/1'/", "m/2'/"]) <= set(self.master_public_keys.keys())
315         elif account_type == '2of3':
316             return set(["m/3'/", "m/4'/", "m/5'/"]) <= set(self.master_public_keys.keys())
317
318     def find_root_by_master_key(self, c, K):
319         for key, v in self.master_public_keys.items():
320             if key == "m/":continue
321             cc, KK, _ = v
322             if (c == cc) and (K == KK):
323                 return key
324
325     def deseed_root(self, seed, password):
326         # for safety, we ask the user to enter their seed
327         assert seed == self.decode_seed(password)
328         self.seed = ''
329         self.storage.put('seed', '', True)
330
331
332     def deseed_branch(self, k):
333         # check that parent has no seed
334         assert self.seed == ''
335         self.master_private_keys.pop(k)
336         self.storage.put('master_private_keys', self.master_private_keys, True)
337
338     def is_watching_only(self):
339         return (self.seed == '') and (self.master_private_keys == {})
340
341
342
343     def account_id(self, account_type, i):
344         if account_type == '1':
345             return "m/0'/%d"%i
346         elif account_type == '2of2':
347             return "m/1'/%d & m/2'/%d"%(i,i)
348         elif account_type == '2of3':
349             return "m/3'/%d & m/4'/%d & m/5'/%d"%(i,i,i)
350         else:
351             raise BaseException('unknown account type')
352
353
354     def num_accounts(self, account_type):
355         keys = self.accounts.keys()
356         i = 0
357         while True:
358             account_id = self.account_id(account_type, i)
359             if account_id not in keys: break
360             i += 1
361         return i
362
363
364     def new_account_address(self, account_type = '1'):
365         i = self.num_accounts(account_type)
366         k = self.account_id(account_type,i)
367
368         addr = self.next_addresses.get(k)
369         if not addr: 
370             account_id, account = self.next_account(account_type)
371             addr = account.first_address()
372             self.next_addresses[k] = addr
373             self.storage.put('next_addresses',self.next_addresses)
374
375         return k, addr
376
377
378     def next_account(self, account_type = '1'):
379
380         i = self.num_accounts(account_type)
381         account_id = self.account_id(account_type,i)
382
383         if account_type is '1':
384             master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
385             c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
386             account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
387
388         elif account_type == '2of2':
389             master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
390             c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
391             master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
392             c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
393             account = BIP32_Account_2of2({ 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
394
395         elif account_type == '2of3':
396             master_c3, master_K3, _ = self.master_public_keys["m/3'/"]
397             c3, K3, cK3 = bip32_public_derivation(master_c3.decode('hex'), master_K3.decode('hex'), "m/3'/", "m/3'/%d"%i)
398             master_c4, master_K4, _ = self.master_public_keys["m/4'/"]
399             c4, K4, cK4 = bip32_public_derivation(master_c4.decode('hex'), master_K4.decode('hex'), "m/4'/", "m/4'/%d"%i)
400             master_c5, master_K5, _ = self.master_public_keys["m/5'/"]
401             c5, K5, cK5 = bip32_public_derivation(master_c5.decode('hex'), master_K5.decode('hex'), "m/5'/", "m/5'/%d"%i)
402             account = BIP32_Account_2of3({ 'c':c3, 'K':K3, 'cK':cK3, 'c2':c4, 'K2':K4, 'cK2':cK4, 'c3':c5, 'K3':K5, 'cK3':cK5 })
403
404         return account_id, account
405
406
407     def set_label(self, name, text = None):
408         changed = False
409         old_text = self.labels.get(name)
410         if text:
411             if old_text != text:
412                 self.labels[name] = text
413                 changed = True
414         else:
415             if old_text:
416                 self.labels.pop(name)
417                 changed = True
418
419         if changed:
420             self.storage.put('labels', self.labels, True)
421
422         run_hook('set_label', name, text, changed)
423         return changed
424
425
426
427     def create_account(self, account_type = '1', name = None):
428         k, account = self.next_account(account_type)
429         if k in self.pending_accounts:
430             self.pending_accounts.pop(k)
431             self.storage.put('pending_accounts', self.pending_accounts)
432
433         self.accounts[k] = account
434         self.save_accounts()
435         if name:
436             self.set_label(k, name)
437
438
439     def create_old_account(self):
440         mpk = OldAccount.mpk_from_seed(self.seed)
441         self.storage.put('master_public_key', mpk, True)
442         self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
443         self.save_accounts()
444
445
446     def save_accounts(self):
447         d = {}
448         for k, v in self.accounts.items():
449             d[k] = v.dump()
450         self.storage.put('accounts', d, True)
451
452     
453
454     def load_accounts(self):
455         d = self.storage.get('accounts', {})
456         self.accounts = {}
457         for k, v in d.items():
458             if k == 0:
459                 v['mpk'] = self.storage.get('master_public_key')
460                 self.accounts[k] = OldAccount(v)
461             elif '&' in k:
462                 self.accounts[k] = BIP32_Account_2of2(v)
463             else:
464                 self.accounts[k] = BIP32_Account(v)
465
466         self.pending_accounts = self.storage.get('pending_accounts',{})
467
468
469     def delete_pending_account(self, k):
470         self.pending_accounts.pop(k)
471         self.storage.put('pending_accounts', self.pending_accounts)
472
473     def account_is_pending(self, k):
474         return k in self.pending_accounts
475
476     def create_pending_account(self, acct_type, name):
477         k, addr = self.new_account_address(acct_type)
478         self.set_label(k, name)
479         self.pending_accounts[k] = addr
480         self.storage.put('pending_accounts', self.pending_accounts)
481
482     def get_pending_accounts(self):
483         return self.pending_accounts.items()
484
485
486     def addresses(self, include_change = True, _next=True):
487         o = self.get_account_addresses(-1, include_change)
488         for a in self.accounts.keys():
489             o += self.get_account_addresses(a, include_change)
490
491         if _next:
492             for addr in self.next_addresses.values():
493                 if addr not in o:
494                     o += [addr]
495         return o
496
497
498     def is_mine(self, address):
499         return address in self.addresses(True)
500
501
502     def is_change(self, address):
503         if not self.is_mine(address): return False
504         if address in self.imported_keys.keys(): return False
505         acct, s = self.get_address_index(address)
506         if s is None: return False
507         return s[0] == 1
508
509     def get_master_public_key(self):
510         if self.seed_version == 4:
511             return self.storage.get("master_public_key")
512         else:
513             c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
514             return repr((c, K))
515
516     def get_master_private_key(self, account, password):
517         master_k = pw_decode( self.master_private_keys[account], password)
518         master_c, master_K, master_Kc = self.master_public_keys[account]
519         try:
520             K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
521             assert K.encode('hex') == master_K
522         except:
523             raise BaseException("Invalid password")
524         return master_k
525
526
527     def get_address_index(self, address):
528         if address in self.imported_keys.keys():
529             return -1, None
530
531         for account in self.accounts.keys():
532             for for_change in [0,1]:
533                 addresses = self.accounts[account].get_addresses(for_change)
534                 for addr in addresses:
535                     if address == addr:
536                         return account, (for_change, addresses.index(addr))
537
538         for k,v in self.next_addresses.items():
539             if v == address:
540                 return k, (0,0)
541
542         raise BaseException("Address not found", address)
543
544
545     def get_roots(self, account):
546         roots = []
547         for a in account.split('&'):
548             s = a.strip()
549             m = re.match("(m/\d+'/)(\d+)", s)
550             roots.append( m.group(1) )
551         return roots
552
553     def is_seeded(self, account):
554         if type(account) is int:
555             return self.seed is not None
556
557         for root in self.get_roots(account):
558             if root not in self.master_private_keys.keys(): 
559                 return False
560         return True
561
562     def rebase_sequence(self, account, sequence):
563         c, i = sequence
564         dd = []
565         for a in account.split('&'):
566             s = a.strip()
567             m = re.match("(m/\d+'/)(\d+)", s)
568             root = m.group(1)
569             num = int(m.group(2))
570             dd.append( (root, [num,c,i] ) )
571         return dd
572         
573
574     def get_keyID(self, account, sequence):
575         if account == 0:
576             return 'old'
577
578         rs = self.rebase_sequence(account, sequence)
579         dd = []
580         for root, public_sequence in rs:
581             c, K, _ = self.master_public_keys[root]
582             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
583             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
584         return '&'.join(dd)
585
586
587
588     def decode_seed(self, password):
589         seed = pw_decode(self.seed, password)
590         #todo:  #self.sequences[0].check_seed(seed)
591         return seed
592         
593
594     def get_private_key(self, address, password):
595         out = []
596         if address in self.imported_keys.keys():
597             out.append( pw_decode( self.imported_keys[address], password ) )
598         else:
599             account, sequence = self.get_address_index(address)
600             if account == 0:
601                 seed = self.decode_seed(password)
602                 pk = self.accounts[account].get_private_key(seed, sequence)
603                 out.append(pk)
604                 return out
605
606             # assert address == self.accounts[account].get_address(*sequence)
607             rs = self.rebase_sequence( account, sequence)
608             for root, public_sequence in rs:
609
610                 if root not in self.master_private_keys.keys(): continue
611                 master_k = self.get_master_private_key(root, password)
612                 master_c, _, _ = self.master_public_keys[root]
613                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
614                 out.append(pk)
615                     
616         return out
617
618
619     def add_keypairs_from_wallet(self, tx, keypairs, password):
620         for txin in tx.inputs:
621             address = txin['address']
622             private_keys = self.get_private_key(address, password)
623             for sec in private_keys:
624                 pubkey = public_key_from_private_key(sec)
625                 keypairs[ pubkey ] = sec
626
627
628     def add_keypairs_from_KeyID(self, tx, keypairs, password):
629         for txin in tx.inputs:
630             keyid = txin.get('KeyID')
631             if keyid:
632                 roots = []
633                 for s in keyid.split('&'):
634                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
635                     if not m: continue
636                     c = m.group(1)
637                     K = m.group(2)
638                     sequence = m.group(3)
639                     root = self.find_root_by_master_key(c,K)
640                     if not root: continue
641                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
642                     root = root + '%d'%sequence[0]
643                     sequence = sequence[1:]
644                     roots.append((root,sequence)) 
645
646                 account_id = " & ".join( map(lambda x:x[0], roots) )
647                 account = self.accounts.get(account_id)
648                 if not account: continue
649                 addr = account.get_address(*sequence)
650                 txin['address'] = addr # fixme: side effect
651                 pk = self.get_private_key(addr, password)
652                 for sec in pk:
653                     pubkey = public_key_from_private_key(sec)
654                     keypairs[pubkey] = sec
655
656
657
658     def signrawtransaction(self, tx, input_info, private_keys, password):
659
660         # check that the password is correct
661         seed = self.decode_seed(password)
662
663         # add input info
664         tx.add_input_info(input_info)
665
666         # add redeem script for coins that are in the wallet
667         # FIXME: add redeemPubkey too!
668         unspent_coins = self.get_unspent_coins()
669         for txin in tx.inputs:
670             for item in unspent_coins:
671                 if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
672                     print_error( "tx input is in unspent coins" )
673                     txin['scriptPubKey'] = item['scriptPubKey']
674                     account, sequence = self.get_address_index(item['address'])
675                     if account != -1:
676                         txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
677                         print_error("added redeemScript", txin['redeemScript'])
678                     break
679
680
681         # build a list of public/private keys
682         keypairs = {}
683
684         # add private keys from parameter
685         for sec in private_keys:
686             pubkey = public_key_from_private_key(sec)
687             keypairs[ pubkey ] = sec
688
689         # add private_keys from KeyID
690         self.add_keypairs_from_KeyID(tx, keypairs, password)
691
692         # add private keys from wallet
693         self.add_keypairs_from_wallet(tx, keypairs, password)
694         self.sign_transaction(tx, keypairs)
695
696
697     def sign_message(self, address, message, password):
698         keys = self.get_private_key(address, password)
699         assert len(keys) == 1
700         sec = keys[0]
701         key = regenerate_key(sec)
702         compressed = is_compressed(sec)
703         return key.sign_message(message, compressed, address)
704
705
706     def change_gap_limit(self, value):
707         if value >= self.gap_limit:
708             self.gap_limit = value
709             self.storage.put('gap_limit', self.gap_limit, True)
710             #self.interface.poke('synchronizer')
711             return True
712
713         elif value >= self.min_acceptable_gap():
714             for key, account in self.accounts.items():
715                 addresses = account[0]
716                 k = self.num_unused_trailing_addresses(addresses)
717                 n = len(addresses) - k + value
718                 addresses = addresses[0:n]
719                 self.accounts[key][0] = addresses
720
721             self.gap_limit = value
722             self.storage.put('gap_limit', self.gap_limit, True)
723             self.save_accounts()
724             return True
725         else:
726             return False
727
728     def num_unused_trailing_addresses(self, addresses):
729         k = 0
730         for a in addresses[::-1]:
731             if self.history.get(a):break
732             k = k + 1
733         return k
734
735     def min_acceptable_gap(self):
736         # fixme: this assumes wallet is synchronized
737         n = 0
738         nmax = 0
739
740         for account in self.accounts.values():
741             addresses = account.get_addresses(0)
742             k = self.num_unused_trailing_addresses(addresses)
743             for a in addresses[0:-k]:
744                 if self.history.get(a):
745                     n = 0
746                 else:
747                     n += 1
748                     if n > nmax: nmax = n
749         return nmax + 1
750
751
752     def address_is_old(self, address):
753         age = -1
754         h = self.history.get(address, [])
755         if h == ['*']:
756             return True
757         for tx_hash, tx_height in h:
758             if tx_height == 0:
759                 tx_age = 0
760             else: 
761                 tx_age = self.verifier.blockchain.height() - tx_height + 1
762             if tx_age > age:
763                 age = tx_age
764         return age > 2
765
766
767     def synchronize_sequence(self, account, for_change):
768         limit = self.gap_limit_for_change if for_change else self.gap_limit
769         new_addresses = []
770         while True:
771             addresses = account.get_addresses(for_change)
772             if len(addresses) < limit:
773                 address = account.create_new_address(for_change)
774                 self.history[address] = []
775                 new_addresses.append( address )
776                 continue
777
778             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
779                 break
780             else:
781                 address = account.create_new_address(for_change)
782                 self.history[address] = []
783                 new_addresses.append( address )
784
785         return new_addresses
786         
787
788
789     def create_pending_accounts(self):
790         for account_type in ['1','2of2','2of3']:
791             if not self.has_master_public_keys(account_type):
792                 continue
793             k, a = self.new_account_address(account_type)
794             if self.address_is_old(a):
795                 print_error( "creating account", a )
796                 self.create_account(account_type)
797                 self.next_addresses.pop(k)
798
799
800     def synchronize_account(self, account):
801         new = []
802         new += self.synchronize_sequence(account, 0)
803         new += self.synchronize_sequence(account, 1)
804         return new
805
806
807     def synchronize(self):
808         if self.master_public_keys:
809             self.create_pending_accounts()
810         new = []
811         for account in self.accounts.values():
812             new += self.synchronize_account(account)
813         if new:
814             self.save_accounts()
815             self.storage.put('addr_history', self.history, True)
816         return new
817
818
819     def is_found(self):
820         return self.history.values() != [[]] * len(self.history) 
821
822
823     def add_contact(self, address, label=None):
824         self.addressbook.append(address)
825         self.storage.put('contacts', self.addressbook, True)
826         if label:  
827             self.set_label(address, label)
828
829
830     def delete_contact(self, addr):
831         if addr in self.addressbook:
832             self.addressbook.remove(addr)
833             self.storage.put('addressbook', self.addressbook, True)
834
835
836     def fill_addressbook(self):
837         for tx_hash, tx in self.transactions.items():
838             is_relevant, is_send, _, _ = self.get_tx_value(tx)
839             if is_send:
840                 for addr, v in tx.outputs:
841                     if not self.is_mine(addr) and addr not in self.addressbook:
842                         self.addressbook.append(addr)
843         # redo labels
844         # self.update_tx_labels()
845
846     def get_num_tx(self, address):
847         n = 0 
848         for tx in self.transactions.values():
849             if address in map(lambda x:x[0], tx.outputs): n += 1
850         return n
851
852
853     def get_address_flags(self, addr):
854         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
855         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
856         return flags
857         
858
859     def get_tx_value(self, tx, account=None):
860         domain = self.get_account_addresses(account)
861         return tx.get_value(domain, self.prevout_values)
862
863     
864     def update_tx_outputs(self, tx_hash):
865         tx = self.transactions.get(tx_hash)
866
867         for i, (addr, value) in enumerate(tx.outputs):
868             key = tx_hash+ ':%d'%i
869             self.prevout_values[key] = value
870
871         for item in tx.inputs:
872             if self.is_mine(item.get('address')):
873                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
874                 self.spent_outputs.append(key)
875
876
877     def get_addr_balance(self, address):
878         assert self.is_mine(address)
879         h = self.history.get(address,[])
880         if h == ['*']: return 0,0
881         c = u = 0
882         received_coins = []   # list of coins received at address
883
884         for tx_hash, tx_height in h:
885             tx = self.transactions.get(tx_hash)
886             if not tx: continue
887
888             for i, (addr, value) in enumerate(tx.outputs):
889                 if addr == address:
890                     key = tx_hash + ':%d'%i
891                     received_coins.append(key)
892
893         for tx_hash, tx_height in h:
894             tx = self.transactions.get(tx_hash)
895             if not tx: continue
896             v = 0
897
898             for item in tx.inputs:
899                 addr = item.get('address')
900                 if addr == address:
901                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
902                     value = self.prevout_values.get( key )
903                     if key in received_coins: 
904                         v -= value
905
906             for i, (addr, value) in enumerate(tx.outputs):
907                 key = tx_hash + ':%d'%i
908                 if addr == address:
909                     v += value
910
911             if tx_height:
912                 c += v
913             else:
914                 u += v
915         return c, u
916
917
918     def get_account_name(self, k):
919         if k == 0:
920             if self.seed_version == 4: 
921                 name = 'Main account'
922             else:
923                 name = 'Old account'
924         else:
925             default = "Unnamed account"
926             m = re.match("m/0'/(\d+)", k)
927             if m:
928                 num = m.group(1)
929                 if num == '0':
930                     default = "Main account"
931                 else:
932                     default = "Account %s"%num
933                     
934             m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
935             if m:
936                 num = m.group(1)
937                 default = "2of2 account %s"%num
938             name = self.labels.get(k, default)
939
940         return name
941
942     def get_account_names(self):
943         accounts = {}
944         for k, account in self.accounts.items():
945             accounts[k] = self.get_account_name(k)
946         if self.imported_keys:
947             accounts[-1] = 'Imported keys'
948         return accounts
949
950     def get_account_addresses(self, a, include_change=True):
951         if a is None:
952             o = self.addresses(True)
953         elif a == -1:
954             o = self.imported_keys.keys()
955         else:
956             ac = self.accounts[a]
957             o = ac.get_addresses(0)
958             if include_change: o += ac.get_addresses(1)
959         return o
960
961     def get_imported_balance(self):
962         return self.get_balance(self.imported_keys.keys())
963
964     def get_account_balance(self, account):
965         return self.get_balance(self.get_account_addresses(account))
966
967     def get_frozen_balance(self):
968         return self.get_balance(self.frozen_addresses)
969         
970     def get_balance(self, domain=None):
971         if domain is None: domain = self.addresses(True)
972         cc = uu = 0
973         for addr in domain:
974             c, u = self.get_addr_balance(addr)
975             cc += c
976             uu += u
977         return cc, uu
978
979
980     def get_unspent_coins(self, domain=None):
981         coins = []
982         if domain is None: domain = self.addresses(True)
983         for addr in domain:
984             h = self.history.get(addr, [])
985             if h == ['*']: continue
986             for tx_hash, tx_height in h:
987                 tx = self.transactions.get(tx_hash)
988                 if tx is None: raise BaseException("Wallet not synchronized")
989                 is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
990                 for output in tx.d.get('outputs'):
991                     if output.get('address') != addr: continue
992                     key = tx_hash + ":%d" % output.get('prevout_n')
993                     if key in self.spent_outputs: continue
994                     output['prevout_hash'] = tx_hash
995                     output['height'] = tx_height
996                     output['coinbase'] = is_coinbase
997                     coins.append((tx_height, output))
998
999         # sort by age
1000         if coins:
1001             coins = sorted(coins)
1002             if coins[-1][0] != 0:
1003                 while coins[0][0] == 0: 
1004                     coins = coins[1:] + [ coins[0] ]
1005         return [x[1] for x in coins]
1006
1007
1008
1009     def choose_tx_inputs_from_account( self, amount, fixed_fee, account ):
1010         domain = self.get_account_addresses(account) if account else None
1011         return self.choose_tx_inputs( amount, fixed_fee, domain )
1012
1013
1014     def choose_tx_inputs( self, amount, fixed_fee, domain = None ):
1015         """ todo: minimize tx size """
1016         total = 0
1017         fee = self.fee if fixed_fee is None else fixed_fee
1018         if domain is None:
1019             domain = self.addresses(True)
1020
1021         for i in self.frozen_addresses:
1022             if i in domain: domain.remove(i)
1023
1024         prioritized = []
1025         for i in self.prioritized_addresses:
1026             if i in domain:
1027                 domain.remove(i)
1028                 prioritized.append(i)
1029
1030         coins = self.get_unspent_coins(domain)
1031         prioritized_coins = self.get_unspent_coins(prioritized)
1032
1033         inputs = []
1034         coins = prioritized_coins + coins
1035
1036         for item in coins:
1037             if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.blockchain.height:
1038                 continue
1039             addr = item.get('address')
1040             v = item.get('value')
1041             total += v
1042             inputs.append(item)
1043             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
1044             if total >= amount + fee: break
1045         else:
1046             inputs = []
1047
1048         return inputs, total, fee
1049
1050
1051     def set_fee(self, fee):
1052         if self.fee != fee:
1053             self.fee = fee
1054             self.storage.put('fee_per_kb', self.fee, True)
1055         
1056     def estimated_fee(self, inputs):
1057         estimated_size =  len(inputs) * 180 + 80     # this assumes non-compressed keys
1058         fee = self.fee * int(round(estimated_size/1024.))
1059         if fee == 0: fee = self.fee
1060         return fee
1061
1062
1063     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None):
1064         "add change to a transaction"
1065         change_amount = total - ( amount + fee )
1066         if change_amount != 0:
1067             if not change_addr:
1068
1069                 # send change to one of the accounts involved in the tx
1070                 address = inputs[0].get('address')
1071                 account, _ = self.get_address_index(address)
1072
1073                 if not self.use_change or account == -1:
1074                     change_addr = inputs[-1]['address']
1075                 else:
1076                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
1077
1078             # Insert the change output at a random position in the outputs
1079             posn = random.randint(0, len(outputs))
1080             outputs[posn:posn] = [( change_addr,  change_amount)]
1081         return outputs
1082
1083
1084     def get_history(self, address):
1085         with self.lock:
1086             return self.history.get(address)
1087
1088
1089     def get_status(self, h):
1090         if not h: return None
1091         if h == ['*']: return '*'
1092         status = ''
1093         for tx_hash, height in h:
1094             status += tx_hash + ':%d:' % height
1095         return hashlib.sha256( status ).digest().encode('hex')
1096
1097
1098     def receive_tx_callback(self, tx_hash, tx, tx_height):
1099
1100         with self.transaction_lock:
1101             self.add_extra_addresses(tx)
1102             if not self.check_new_tx(tx_hash, tx):
1103                 # may happen due to pruning
1104                 print_error("received transaction that is no longer referenced in history", tx_hash)
1105                 return
1106             self.transactions[tx_hash] = tx
1107             self.network.interface.pending_transactions_for_notifications.append(tx)
1108             self.save_transactions()
1109             if self.verifier and tx_height>0: 
1110                 self.verifier.add(tx_hash, tx_height)
1111             self.update_tx_outputs(tx_hash)
1112
1113
1114     def save_transactions(self):
1115         tx = {}
1116         for k,v in self.transactions.items():
1117             tx[k] = str(v)
1118         self.storage.put('transactions', tx, True)
1119
1120     def receive_history_callback(self, addr, hist):
1121
1122         if not self.check_new_history(addr, hist):
1123             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
1124             
1125         with self.lock:
1126             self.history[addr] = hist
1127             self.storage.put('addr_history', self.history, True)
1128
1129         if hist != ['*']:
1130             for tx_hash, tx_height in hist:
1131                 if tx_height>0:
1132                     # add it in case it was previously unconfirmed
1133                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1134
1135
1136     def get_tx_history(self, account=None):
1137         with self.transaction_lock:
1138             history = self.transactions.items()
1139             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1140             result = []
1141     
1142             balance = 0
1143             for tx_hash, tx in history:
1144                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1145                 if v is not None: balance += v
1146
1147             c, u = self.get_account_balance(account)
1148
1149             if balance != c+u:
1150                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1151
1152             balance = c + u - balance
1153             for tx_hash, tx in history:
1154                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1155                 if not is_relevant:
1156                     continue
1157                 if value is not None:
1158                     balance += value
1159
1160                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1161                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1162
1163         return result
1164
1165
1166     def get_label(self, tx_hash):
1167         label = self.labels.get(tx_hash)
1168         is_default = (label == '') or (label is None)
1169         if is_default: label = self.get_default_label(tx_hash)
1170         return label, is_default
1171
1172
1173     def get_default_label(self, tx_hash):
1174         tx = self.transactions.get(tx_hash)
1175         default_label = ''
1176         if tx:
1177             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1178             if is_mine:
1179                 for o in tx.outputs:
1180                     o_addr, _ = o
1181                     if not self.is_mine(o_addr):
1182                         try:
1183                             default_label = self.labels[o_addr]
1184                         except KeyError:
1185                             default_label = o_addr
1186                         break
1187                 else:
1188                     default_label = '(internal)'
1189             else:
1190                 for o in tx.outputs:
1191                     o_addr, _ = o
1192                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1193                         break
1194                 else:
1195                     for o in tx.outputs:
1196                         o_addr, _ = o
1197                         if self.is_mine(o_addr):
1198                             break
1199                     else:
1200                         o_addr = None
1201
1202                 if o_addr:
1203                     dest_label = self.labels.get(o_addr)
1204                     try:
1205                         default_label = self.labels[o_addr]
1206                     except KeyError:
1207                         default_label = o_addr
1208
1209         return default_label
1210
1211
1212     def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None ):
1213         for address, x in outputs:
1214             assert is_valid(address)
1215         amount = sum( map(lambda x:x[1], outputs) )
1216         inputs, total, fee = self.choose_tx_inputs( amount, fee, domain )
1217         if not inputs:
1218             raise ValueError("Not enough funds")
1219         self.add_input_info(inputs)
1220         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr)
1221         return Transaction.from_io(inputs, outputs)
1222
1223
1224     def mktx_from_account(self, outputs, password, fee=None, account=None):
1225         domain = self.get_account_addresses(account) if account else None
1226         return self.mktx(outputs, password, fee, change_addr=None, domain=domain)
1227
1228
1229     def mktx(self, outputs, password, fee=None, change_addr=None, domain= None ):
1230         tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain)
1231         keypairs = {}
1232         self.add_keypairs_from_wallet(tx, keypairs, password)
1233         if keypairs:
1234             self.sign_transaction(tx, keypairs)
1235         return tx
1236
1237
1238     def add_input_info(self, inputs):
1239         for txin in inputs:
1240             address = txin['address']
1241             account, sequence = self.get_address_index(address)
1242             txin['KeyID'] = self.get_keyID(account, sequence)
1243             redeemScript = self.accounts[account].redeem_script(sequence)
1244             if redeemScript: 
1245                 txin['redeemScript'] = redeemScript
1246             else:
1247                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1248
1249
1250     def sign_transaction(self, tx, keypairs):
1251         tx.sign(keypairs)
1252         run_hook('sign_transaction', tx)
1253
1254
1255     def sendtx(self, tx):
1256         # synchronous
1257         h = self.send_tx(tx)
1258         self.tx_event.wait()
1259         return self.receive_tx(h)
1260
1261     def send_tx(self, tx):
1262         # asynchronous
1263         self.tx_event.clear()
1264         self.network.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
1265         return tx.hash()
1266
1267     def on_broadcast(self, i, r):
1268         self.tx_result = r.get('result')
1269         self.tx_event.set()
1270
1271     def receive_tx(self,tx_hash):
1272         out = self.tx_result 
1273         if out != tx_hash:
1274             return False, "error: " + out
1275         return True, out
1276
1277
1278
1279     def update_password(self, seed, old_password, new_password):
1280         if new_password == '': new_password = None
1281         # this will throw an exception if unicode cannot be converted
1282         self.seed = pw_encode( seed, new_password)
1283         self.storage.put('seed', self.seed, True)
1284         self.use_encryption = (new_password != None)
1285         self.storage.put('use_encryption', self.use_encryption,True)
1286         for k in self.imported_keys.keys():
1287             a = self.imported_keys[k]
1288             b = pw_decode(a, old_password)
1289             c = pw_encode(b, new_password)
1290             self.imported_keys[k] = c
1291         self.storage.put('imported_keys', self.imported_keys, True)
1292
1293         for k, v in self.master_private_keys.items():
1294             b = pw_decode(v, old_password)
1295             c = pw_encode(b, new_password)
1296             self.master_private_keys[k] = c
1297         self.storage.put('master_private_keys', self.master_private_keys, True)
1298
1299
1300     def freeze(self,addr):
1301         if self.is_mine(addr) and addr not in self.frozen_addresses:
1302             self.unprioritize(addr)
1303             self.frozen_addresses.append(addr)
1304             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1305             return True
1306         else:
1307             return False
1308
1309     def unfreeze(self,addr):
1310         if self.is_mine(addr) and addr in self.frozen_addresses:
1311             self.frozen_addresses.remove(addr)
1312             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1313             return True
1314         else:
1315             return False
1316
1317     def prioritize(self,addr):
1318         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1319             self.unfreeze(addr)
1320             self.prioritized_addresses.append(addr)
1321             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1322             return True
1323         else:
1324             return False
1325
1326     def unprioritize(self,addr):
1327         if self.is_mine(addr) and addr in self.prioritized_addresses:
1328             self.prioritized_addresses.remove(addr)
1329             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1330             return True
1331         else:
1332             return False
1333
1334
1335     def set_verifier(self, verifier):
1336         self.verifier = verifier
1337
1338         # review transactions that are in the history
1339         for addr, hist in self.history.items():
1340             if hist == ['*']: continue
1341             for tx_hash, tx_height in hist:
1342                 if tx_height>0:
1343                     # add it in case it was previously unconfirmed
1344                     self.verifier.add(tx_hash, tx_height)
1345
1346
1347         # if we are on a pruning server, remove unverified transactions
1348         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1349         for tx_hash in self.transactions.keys():
1350             if tx_hash not in vr:
1351                 self.transactions.pop(tx_hash)
1352
1353
1354
1355     def check_new_history(self, addr, hist):
1356         
1357         # check that all tx in hist are relevant
1358         if hist != ['*']:
1359             for tx_hash, height in hist:
1360                 tx = self.transactions.get(tx_hash)
1361                 if not tx: continue
1362                 if not tx.has_address(addr):
1363                     return False
1364
1365         # check that we are not "orphaning" a transaction
1366         old_hist = self.history.get(addr,[])
1367         if old_hist == ['*']: return True
1368
1369         for tx_hash, height in old_hist:
1370             if tx_hash in map(lambda x:x[0], hist): continue
1371             found = False
1372             for _addr, _hist in self.history.items():
1373                 if _addr == addr: continue
1374                 if _hist == ['*']: continue
1375                 _tx_hist = map(lambda x:x[0], _hist)
1376                 if tx_hash in _tx_hist:
1377                     found = True
1378                     break
1379
1380             if not found:
1381                 tx = self.transactions.get(tx_hash)
1382                 # tx might not be there
1383                 if not tx: continue
1384                 
1385                 # already verified?
1386                 if self.verifier.get_height(tx_hash):
1387                     continue
1388                 # unconfirmed tx
1389                 print_error("new history is orphaning transaction:", tx_hash)
1390                 # check that all outputs are not mine, request histories
1391                 ext_requests = []
1392                 for _addr, _v in tx.outputs:
1393                     # assert not self.is_mine(_addr)
1394                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1395
1396                 ext_h = self.network.synchronous_get(ext_requests)
1397                 print_error("sync:", ext_requests, ext_h)
1398                 height = None
1399                 for h in ext_h:
1400                     if h == ['*']: continue
1401                     for item in h:
1402                         if item.get('tx_hash') == tx_hash:
1403                             height = item.get('height')
1404                 if height:
1405                     print_error("found height for", tx_hash, height)
1406                     self.verifier.add(tx_hash, height)
1407                 else:
1408                     print_error("removing orphaned tx from history", tx_hash)
1409                     self.transactions.pop(tx_hash)
1410
1411         return True
1412
1413
1414
1415     def check_new_tx(self, tx_hash, tx):
1416         # 1 check that tx is referenced in addr_history. 
1417         addresses = []
1418         for addr, hist in self.history.items():
1419             if hist == ['*']:continue
1420             for txh, height in hist:
1421                 if txh == tx_hash: 
1422                     addresses.append(addr)
1423
1424         if not addresses:
1425             return False
1426
1427         # 2 check that referencing addresses are in the tx
1428         for addr in addresses:
1429             if not tx.has_address(addr):
1430                 return False
1431
1432         return True
1433
1434
1435     def start_threads(self, network):
1436         from verifier import TxVerifier
1437         self.network = network
1438         self.verifier = TxVerifier(self.network, self.storage)
1439         self.verifier.start()
1440         self.set_verifier(self.verifier)
1441         self.synchronizer = WalletSynchronizer(self, network)
1442         self.synchronizer.start()
1443
1444     def stop_threads(self):
1445         self.verifier.stop()
1446         self.synchronizer.stop()
1447
1448
1449
1450     def restore(self, callback):
1451         from i18n import _
1452         def wait_for_wallet():
1453             self.set_up_to_date(False)
1454             while not self.is_up_to_date():
1455                 msg = "%s\n%s %d\n%s %.1f"%(
1456                     _("Please wait..."),
1457                     _("Addresses generated:"),
1458                     len(self.addresses(True)),_("Kilobytes received:"), 
1459                     self.network.interface.bytes_received/1024.)
1460
1461                 apply(callback, (msg,))
1462                 time.sleep(0.1)
1463
1464         def wait_for_network():
1465             while not self.network.interface.is_connected:
1466                 msg = "%s \n" % (_("Connecting..."))
1467                 apply(callback, (msg,))
1468                 time.sleep(0.1)
1469
1470         # wait until we are connected, because the user might have selected another server
1471         wait_for_network()
1472
1473         # try to restore old account
1474         self.create_old_account()
1475         wait_for_wallet()
1476
1477         if self.is_found():
1478             self.seed_version = 4
1479             self.storage.put('seed_version', self.seed_version, True)
1480         else:
1481             self.accounts.pop(0)
1482             self.create_accounts()
1483             wait_for_wallet()
1484
1485
1486
1487
1488 class WalletSynchronizer(threading.Thread):
1489
1490
1491     def __init__(self, wallet, network):
1492         threading.Thread.__init__(self)
1493         self.daemon = True
1494         self.wallet = wallet
1495         self.network = network
1496         self.was_updated = True
1497         self.running = False
1498         self.lock = threading.Lock()
1499         self.queue = Queue.Queue()
1500
1501     def stop(self):
1502         with self.lock: self.running = False
1503
1504     def is_running(self):
1505         with self.lock: return self.running
1506
1507     
1508     def subscribe_to_addresses(self, addresses):
1509         messages = []
1510         for addr in addresses:
1511             messages.append(('blockchain.address.subscribe', [addr]))
1512         self.network.subscribe( messages, lambda i,r: self.queue.put(r))
1513
1514
1515     def run(self):
1516         with self.lock:
1517             self.running = True
1518
1519         while self.is_running():
1520             
1521             if not self.network.is_connected():
1522                 print_error("synchronizer: waiting for interface")
1523                 self.network.wait_until_connected()
1524                 
1525             self.run_interface(self.network.interface)
1526
1527
1528     def run_interface(self, interface):
1529
1530         print_error("synchronizer: connected to", interface.server)
1531
1532         requested_tx = []
1533         missing_tx = []
1534         requested_histories = {}
1535
1536         # request any missing transactions
1537         for history in self.wallet.history.values():
1538             if history == ['*']: continue
1539             for tx_hash, tx_height in history:
1540                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1541                     missing_tx.append( (tx_hash, tx_height) )
1542
1543         if missing_tx:
1544             print_error("missing tx", missing_tx)
1545
1546         # subscriptions
1547         self.subscribe_to_addresses(self.wallet.addresses(True))
1548
1549         while self.is_running():
1550             # 1. create new addresses
1551             new_addresses = self.wallet.synchronize()
1552
1553             # request missing addresses
1554             if new_addresses:
1555                 self.subscribe_to_addresses(new_addresses)
1556
1557             # request missing transactions
1558             for tx_hash, tx_height in missing_tx:
1559                 if (tx_hash, tx_height) not in requested_tx:
1560                     interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
1561                     requested_tx.append( (tx_hash, tx_height) )
1562             missing_tx = []
1563
1564             # detect if situation has changed
1565             if interface.is_up_to_date() and self.queue.empty():
1566                 if not self.wallet.is_up_to_date():
1567                     self.wallet.set_up_to_date(True)
1568                     self.was_updated = True
1569             else:
1570                 if self.wallet.is_up_to_date():
1571                     self.wallet.set_up_to_date(False)
1572                     self.was_updated = True
1573
1574             if self.was_updated:
1575                 self.wallet.network.trigger_callback('updated')
1576                 self.was_updated = False
1577
1578             # 2. get a response
1579             try:
1580                 r = self.queue.get(block=True, timeout=1)
1581             except Queue.Empty:
1582                 continue
1583
1584             if interface != self.network.interface:
1585                 break
1586             
1587             if not r:
1588                 continue
1589
1590             # 3. handle response
1591             method = r['method']
1592             params = r['params']
1593             result = r.get('result')
1594             error = r.get('error')
1595             if error:
1596                 print "error", r
1597                 continue
1598
1599             if method == 'blockchain.address.subscribe':
1600                 addr = params[0]
1601                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1602                     if requested_histories.get(addr) is None:
1603                         interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
1604                         requested_histories[addr] = result
1605
1606             elif method == 'blockchain.address.get_history':
1607                 addr = params[0]
1608                 print_error("receiving history", addr, result)
1609                 if result == ['*']:
1610                     assert requested_histories.pop(addr) == '*'
1611                     self.wallet.receive_history_callback(addr, result)
1612                 else:
1613                     hist = []
1614                     # check that txids are unique
1615                     txids = []
1616                     for item in result:
1617                         tx_hash = item['tx_hash']
1618                         if tx_hash not in txids:
1619                             txids.append(tx_hash)
1620                             hist.append( (tx_hash, item['height']) )
1621
1622                     if len(hist) != len(result):
1623                         raise BaseException("error: server sent history with non-unique txid", result)
1624
1625                     # check that the status corresponds to what was announced
1626                     rs = requested_histories.pop(addr)
1627                     if self.wallet.get_status(hist) != rs:
1628                         raise BaseException("error: status mismatch: %s"%addr)
1629                 
1630                     # store received history
1631                     self.wallet.receive_history_callback(addr, hist)
1632
1633                     # request transactions that we don't have 
1634                     for tx_hash, tx_height in hist:
1635                         if self.wallet.transactions.get(tx_hash) is None:
1636                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1637                                 missing_tx.append( (tx_hash, tx_height) )
1638
1639             elif method == 'blockchain.transaction.get':
1640                 tx_hash = params[0]
1641                 tx_height = params[1]
1642                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1643                 tx = Transaction(result)
1644                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1645                 self.was_updated = True
1646                 requested_tx.remove( (tx_hash, tx_height) )
1647                 print_error("received tx:", tx_hash, len(tx.raw))
1648
1649             else:
1650                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1651
1652             if self.was_updated and not requested_tx:
1653                 self.wallet.network.trigger_callback('updated')
1654                 self.wallet.network.trigger_callback("new_transaction") # Updated gets called too many times from other places as well; if we use that signal we get the notification three times
1655
1656                 self.was_updated = False