fixes for android: restore from seed
[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         account_id, account = self.next_account(account_type)
429         self.accounts[account_id] = account
430         self.save_accounts()
431         if name:
432             self.set_label(account_id, name)
433
434
435     def create_old_account(self):
436         mpk = OldAccount.mpk_from_seed(self.seed)
437         self.storage.put('master_public_key', mpk, True)
438         self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
439         self.save_accounts()
440
441
442     def save_accounts(self):
443         d = {}
444         for k, v in self.accounts.items():
445             d[k] = v.dump()
446         self.storage.put('accounts', d, True)
447
448     
449
450     def load_accounts(self):
451         d = self.storage.get('accounts', {})
452         self.accounts = {}
453         for k, v in d.items():
454             if k == 0:
455                 v['mpk'] = self.storage.get('master_public_key')
456                 self.accounts[k] = OldAccount(v)
457             elif '&' in k:
458                 self.accounts[k] = BIP32_Account_2of2(v)
459             else:
460                 self.accounts[k] = BIP32_Account(v)
461
462
463     def addresses(self, include_change = True, _next=True):
464         o = self.get_account_addresses(-1, include_change)
465         for a in self.accounts.keys():
466             o += self.get_account_addresses(a, include_change)
467
468         if _next:
469             for addr in self.next_addresses.values():
470                 if addr not in o:
471                     o += [addr]
472         return o
473
474
475     def is_mine(self, address):
476         return address in self.addresses(True)
477
478
479     def is_change(self, address):
480         if not self.is_mine(address): return False
481         if address in self.imported_keys.keys(): return False
482         acct, s = self.get_address_index(address)
483         if s is None: return False
484         return s[0] == 1
485
486     def get_master_public_key(self):
487         if self.seed_version == 4:
488             return self.storage.get("master_public_key")
489         else:
490             c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
491             return repr((c, K))
492
493     def get_master_private_key(self, account, password):
494         master_k = pw_decode( self.master_private_keys[account], password)
495         master_c, master_K, master_Kc = self.master_public_keys[account]
496         try:
497             K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
498             assert K.encode('hex') == master_K
499         except:
500             raise BaseException("Invalid password")
501         return master_k
502
503
504     def get_address_index(self, address):
505         if address in self.imported_keys.keys():
506             return -1, None
507
508         for account in self.accounts.keys():
509             for for_change in [0,1]:
510                 addresses = self.accounts[account].get_addresses(for_change)
511                 for addr in addresses:
512                     if address == addr:
513                         return account, (for_change, addresses.index(addr))
514
515         for k,v in self.next_addresses.items():
516             if v == address:
517                 return k, (0,0)
518
519         raise BaseException("Address not found", address)
520
521
522     def get_roots(self, account):
523         roots = []
524         for a in account.split('&'):
525             s = a.strip()
526             m = re.match("(m/\d+'/)(\d+)", s)
527             roots.append( m.group(1) )
528         return roots
529
530     def is_seeded(self, account):
531         if type(account) is int:
532             return self.seed is not None
533
534         for root in self.get_roots(account):
535             if root not in self.master_private_keys.keys(): 
536                 return False
537         return True
538
539     def rebase_sequence(self, account, sequence):
540         c, i = sequence
541         dd = []
542         for a in account.split('&'):
543             s = a.strip()
544             m = re.match("(m/\d+'/)(\d+)", s)
545             root = m.group(1)
546             num = int(m.group(2))
547             dd.append( (root, [num,c,i] ) )
548         return dd
549         
550
551     def get_keyID(self, account, sequence):
552         if account == 0:
553             return 'old'
554
555         rs = self.rebase_sequence(account, sequence)
556         dd = []
557         for root, public_sequence in rs:
558             c, K, _ = self.master_public_keys[root]
559             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
560             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
561         return '&'.join(dd)
562
563
564
565     def decode_seed(self, password):
566         seed = pw_decode(self.seed, password)
567         #todo:  #self.sequences[0].check_seed(seed)
568         return seed
569         
570
571     def get_private_key(self, address, password):
572         out = []
573         if address in self.imported_keys.keys():
574             out.append( pw_decode( self.imported_keys[address], password ) )
575         else:
576             account, sequence = self.get_address_index(address)
577             if account == 0:
578                 seed = self.decode_seed(password)
579                 pk = self.accounts[account].get_private_key(seed, sequence)
580                 out.append(pk)
581                 return out
582
583             # assert address == self.accounts[account].get_address(*sequence)
584             rs = self.rebase_sequence( account, sequence)
585             for root, public_sequence in rs:
586
587                 if root not in self.master_private_keys.keys(): continue
588                 master_k = self.get_master_private_key(root, password)
589                 master_c, _, _ = self.master_public_keys[root]
590                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
591                 out.append(pk)
592                     
593         return out
594
595
596     def add_keypairs_from_wallet(self, tx, keypairs, password):
597         for txin in tx.inputs:
598             address = txin['address']
599             private_keys = self.get_private_key(address, password)
600             for sec in private_keys:
601                 pubkey = public_key_from_private_key(sec)
602                 keypairs[ pubkey ] = sec
603
604
605     def add_keypairs_from_KeyID(self, tx, keypairs, password):
606         for txin in tx.inputs:
607             keyid = txin.get('KeyID')
608             if keyid:
609                 roots = []
610                 for s in keyid.split('&'):
611                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
612                     if not m: continue
613                     c = m.group(1)
614                     K = m.group(2)
615                     sequence = m.group(3)
616                     root = self.find_root_by_master_key(c,K)
617                     if not root: continue
618                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
619                     root = root + '%d'%sequence[0]
620                     sequence = sequence[1:]
621                     roots.append((root,sequence)) 
622
623                 account_id = " & ".join( map(lambda x:x[0], roots) )
624                 account = self.accounts.get(account_id)
625                 if not account: continue
626                 addr = account.get_address(*sequence)
627                 txin['address'] = addr # fixme: side effect
628                 pk = self.get_private_key(addr, password)
629                 for sec in pk:
630                     pubkey = public_key_from_private_key(sec)
631                     keypairs[pubkey] = sec
632
633
634
635     def signrawtransaction(self, tx, input_info, private_keys, password):
636
637         # check that the password is correct
638         seed = self.decode_seed(password)
639
640         # add input info
641         tx.add_input_info(input_info)
642
643         # add redeem script for coins that are in the wallet
644         # FIXME: add redeemPubkey too!
645         unspent_coins = self.get_unspent_coins()
646         for txin in tx.inputs:
647             for item in unspent_coins:
648                 if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
649                     print_error( "tx input is in unspent coins" )
650                     txin['scriptPubKey'] = item['scriptPubKey']
651                     account, sequence = self.get_address_index(item['address'])
652                     if account != -1:
653                         txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
654                         print_error("added redeemScript", txin['redeemScript'])
655                     break
656
657
658         # build a list of public/private keys
659         keypairs = {}
660
661         # add private keys from parameter
662         for sec in private_keys:
663             pubkey = public_key_from_private_key(sec)
664             keypairs[ pubkey ] = sec
665
666         # add private_keys from KeyID
667         self.add_keypairs_from_KeyID(tx, keypairs, password)
668
669         # add private keys from wallet
670         self.add_keypairs_from_wallet(tx, keypairs, password)
671         self.sign_transaction(tx, keypairs)
672
673
674     def sign_message(self, address, message, password):
675         keys = self.get_private_key(address, password)
676         assert len(keys) == 1
677         sec = keys[0]
678         key = regenerate_key(sec)
679         compressed = is_compressed(sec)
680         return key.sign_message(message, compressed, address)
681
682
683     def change_gap_limit(self, value):
684         if value >= self.gap_limit:
685             self.gap_limit = value
686             self.storage.put('gap_limit', self.gap_limit, True)
687             #self.interface.poke('synchronizer')
688             return True
689
690         elif value >= self.min_acceptable_gap():
691             for key, account in self.accounts.items():
692                 addresses = account[0]
693                 k = self.num_unused_trailing_addresses(addresses)
694                 n = len(addresses) - k + value
695                 addresses = addresses[0:n]
696                 self.accounts[key][0] = addresses
697
698             self.gap_limit = value
699             self.storage.put('gap_limit', self.gap_limit, True)
700             self.save_accounts()
701             return True
702         else:
703             return False
704
705     def num_unused_trailing_addresses(self, addresses):
706         k = 0
707         for a in addresses[::-1]:
708             if self.history.get(a):break
709             k = k + 1
710         return k
711
712     def min_acceptable_gap(self):
713         # fixme: this assumes wallet is synchronized
714         n = 0
715         nmax = 0
716
717         for account in self.accounts.values():
718             addresses = account.get_addresses(0)
719             k = self.num_unused_trailing_addresses(addresses)
720             for a in addresses[0:-k]:
721                 if self.history.get(a):
722                     n = 0
723                 else:
724                     n += 1
725                     if n > nmax: nmax = n
726         return nmax + 1
727
728
729     def address_is_old(self, address):
730         age = -1
731         h = self.history.get(address, [])
732         if h == ['*']:
733             return True
734         for tx_hash, tx_height in h:
735             if tx_height == 0:
736                 tx_age = 0
737             else: 
738                 tx_age = self.verifier.blockchain.height - tx_height + 1
739             if tx_age > age:
740                 age = tx_age
741         return age > 2
742
743
744     def synchronize_sequence(self, account, for_change):
745         limit = self.gap_limit_for_change if for_change else self.gap_limit
746         new_addresses = []
747         while True:
748             addresses = account.get_addresses(for_change)
749             if len(addresses) < limit:
750                 address = account.create_new_address(for_change)
751                 self.history[address] = []
752                 new_addresses.append( address )
753                 continue
754
755             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
756                 break
757             else:
758                 address = account.create_new_address(for_change)
759                 self.history[address] = []
760                 new_addresses.append( address )
761
762         return new_addresses
763         
764
765
766     def create_pending_accounts(self):
767         for account_type in ['1','2of2','2of3']:
768             if not self.has_master_public_keys(account_type):
769                 continue
770             k, a = self.new_account_address(account_type)
771             if self.address_is_old(a):
772                 print_error( "creating account", a )
773                 self.create_account(account_type)
774                 self.next_addresses.pop(k)
775
776
777     def synchronize_account(self, account):
778         new = []
779         new += self.synchronize_sequence(account, 0)
780         new += self.synchronize_sequence(account, 1)
781         return new
782
783
784     def synchronize(self):
785         if self.master_public_keys:
786             self.create_pending_accounts()
787         new = []
788         for account in self.accounts.values():
789             new += self.synchronize_account(account)
790         if new:
791             self.save_accounts()
792             self.storage.put('addr_history', self.history, True)
793         return new
794
795
796     def is_found(self):
797         return self.history.values() != [[]] * len(self.history) 
798
799
800     def add_contact(self, address, label=None):
801         self.addressbook.append(address)
802         self.storage.put('contacts', self.addressbook, True)
803         if label:  
804             self.set_label(address, label)
805
806
807     def delete_contact(self, addr):
808         if addr in self.addressbook:
809             self.addressbook.remove(addr)
810             self.storage.put('addressbook', self.addressbook, True)
811
812
813     def fill_addressbook(self):
814         for tx_hash, tx in self.transactions.items():
815             is_relevant, is_send, _, _ = self.get_tx_value(tx)
816             if is_send:
817                 for addr, v in tx.outputs:
818                     if not self.is_mine(addr) and addr not in self.addressbook:
819                         self.addressbook.append(addr)
820         # redo labels
821         # self.update_tx_labels()
822
823     def get_num_tx(self, address):
824         n = 0 
825         for tx in self.transactions.values():
826             if address in map(lambda x:x[0], tx.outputs): n += 1
827         return n
828
829
830     def get_address_flags(self, addr):
831         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
832         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
833         return flags
834         
835
836     def get_tx_value(self, tx, account=None):
837         domain = self.get_account_addresses(account)
838         return tx.get_value(domain, self.prevout_values)
839
840     
841     def update_tx_outputs(self, tx_hash):
842         tx = self.transactions.get(tx_hash)
843
844         for i, (addr, value) in enumerate(tx.outputs):
845             key = tx_hash+ ':%d'%i
846             self.prevout_values[key] = value
847
848         for item in tx.inputs:
849             if self.is_mine(item.get('address')):
850                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
851                 self.spent_outputs.append(key)
852
853
854     def get_addr_balance(self, address):
855         assert self.is_mine(address)
856         h = self.history.get(address,[])
857         if h == ['*']: return 0,0
858         c = u = 0
859         received_coins = []   # list of coins received at address
860
861         for tx_hash, tx_height in h:
862             tx = self.transactions.get(tx_hash)
863             if not tx: continue
864
865             for i, (addr, value) in enumerate(tx.outputs):
866                 if addr == address:
867                     key = tx_hash + ':%d'%i
868                     received_coins.append(key)
869
870         for tx_hash, tx_height in h:
871             tx = self.transactions.get(tx_hash)
872             if not tx: continue
873             v = 0
874
875             for item in tx.inputs:
876                 addr = item.get('address')
877                 if addr == address:
878                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
879                     value = self.prevout_values.get( key )
880                     if key in received_coins: 
881                         v -= value
882
883             for i, (addr, value) in enumerate(tx.outputs):
884                 key = tx_hash + ':%d'%i
885                 if addr == address:
886                     v += value
887
888             if tx_height:
889                 c += v
890             else:
891                 u += v
892         return c, u
893
894
895     def get_account_name(self, k):
896         if k == 0:
897             if self.seed_version == 4: 
898                 name = 'Main account'
899             else:
900                 name = 'Old account'
901         else:
902             default = "Unnamed account"
903             m = re.match("m/0'/(\d+)", k)
904             if m:
905                 num = m.group(1)
906                 if num == '0':
907                     default = "Main account"
908                 else:
909                     default = "Account %s"%num
910                     
911             m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
912             if m:
913                 num = m.group(1)
914                 default = "2of2 account %s"%num
915             name = self.labels.get(k, default)
916
917         return name
918
919     def get_account_names(self):
920         accounts = {}
921         for k, account in self.accounts.items():
922             accounts[k] = self.get_account_name(k)
923         if self.imported_keys:
924             accounts[-1] = 'Imported keys'
925         return accounts
926
927     def get_account_addresses(self, a, include_change=True):
928         if a is None:
929             o = self.addresses(True)
930         elif a == -1:
931             o = self.imported_keys.keys()
932         else:
933             ac = self.accounts[a]
934             o = ac.get_addresses(0)
935             if include_change: o += ac.get_addresses(1)
936         return o
937
938     def get_imported_balance(self):
939         return self.get_balance(self.imported_keys.keys())
940
941     def get_account_balance(self, account):
942         return self.get_balance(self.get_account_addresses(account))
943
944     def get_frozen_balance(self):
945         return self.get_balance(self.frozen_addresses)
946         
947     def get_balance(self, domain=None):
948         if domain is None: domain = self.addresses(True)
949         cc = uu = 0
950         for addr in domain:
951             c, u = self.get_addr_balance(addr)
952             cc += c
953             uu += u
954         return cc, uu
955
956
957     def get_unspent_coins(self, domain=None):
958         coins = []
959         if domain is None: domain = self.addresses(True)
960         for addr in domain:
961             h = self.history.get(addr, [])
962             if h == ['*']: continue
963             for tx_hash, tx_height in h:
964                 tx = self.transactions.get(tx_hash)
965                 if tx is None: raise BaseException("Wallet not synchronized")
966                 is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
967                 for output in tx.d.get('outputs'):
968                     if output.get('address') != addr: continue
969                     key = tx_hash + ":%d" % output.get('prevout_n')
970                     if key in self.spent_outputs: continue
971                     output['prevout_hash'] = tx_hash
972                     output['height'] = tx_height
973                     output['coinbase'] = is_coinbase
974                     coins.append((tx_height, output))
975
976         # sort by age
977         if coins:
978             coins = sorted(coins)
979             if coins[-1][0] != 0:
980                 while coins[0][0] == 0: 
981                     coins = coins[1:] + [ coins[0] ]
982         return [x[1] for x in coins]
983
984
985
986     def choose_tx_inputs_from_account( self, amount, fixed_fee, account ):
987         domain = self.get_account_addresses(account) if account else None
988         return self.choose_tx_inputs( amount, fixed_fee, domain )
989
990
991     def choose_tx_inputs( self, amount, fixed_fee, domain = None ):
992         """ todo: minimize tx size """
993         total = 0
994         fee = self.fee if fixed_fee is None else fixed_fee
995         if domain is None:
996             domain = self.addresses(True)
997
998         for i in self.frozen_addresses:
999             if i in domain: domain.remove(i)
1000
1001         prioritized = []
1002         for i in self.prioritized_addresses:
1003             if i in domain:
1004                 domain.remove(i)
1005                 prioritized.append(i)
1006
1007         coins = self.get_unspent_coins(domain)
1008         prioritized_coins = self.get_unspent_coins(prioritized)
1009
1010         inputs = []
1011         coins = prioritized_coins + coins
1012
1013         for item in coins:
1014             if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.blockchain.height:
1015                 continue
1016             addr = item.get('address')
1017             v = item.get('value')
1018             total += v
1019             inputs.append(item)
1020             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
1021             if total >= amount + fee: break
1022         else:
1023             inputs = []
1024
1025         return inputs, total, fee
1026
1027
1028     def set_fee(self, fee):
1029         if self.fee != fee:
1030             self.fee = fee
1031             self.storage.put('fee_per_kb', self.fee, True)
1032         
1033     def estimated_fee(self, inputs):
1034         estimated_size =  len(inputs) * 180 + 80     # this assumes non-compressed keys
1035         fee = self.fee * int(round(estimated_size/1024.))
1036         if fee == 0: fee = self.fee
1037         return fee
1038
1039
1040     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None):
1041         "add change to a transaction"
1042         change_amount = total - ( amount + fee )
1043         if change_amount != 0:
1044             if not change_addr:
1045
1046                 # send change to one of the accounts involved in the tx
1047                 address = inputs[0].get('address')
1048                 account, _ = self.get_address_index(address)
1049
1050                 if not self.use_change or account == -1:
1051                     change_addr = inputs[-1]['address']
1052                 else:
1053                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
1054
1055             # Insert the change output at a random position in the outputs
1056             posn = random.randint(0, len(outputs))
1057             outputs[posn:posn] = [( change_addr,  change_amount)]
1058         return outputs
1059
1060
1061     def get_history(self, address):
1062         with self.lock:
1063             return self.history.get(address)
1064
1065
1066     def get_status(self, h):
1067         if not h: return None
1068         if h == ['*']: return '*'
1069         status = ''
1070         for tx_hash, height in h:
1071             status += tx_hash + ':%d:' % height
1072         return hashlib.sha256( status ).digest().encode('hex')
1073
1074
1075     def receive_tx_callback(self, tx_hash, tx, tx_height):
1076
1077         with self.transaction_lock:
1078             self.add_extra_addresses(tx)
1079             if not self.check_new_tx(tx_hash, tx):
1080                 # may happen due to pruning
1081                 print_error("received transaction that is no longer referenced in history", tx_hash)
1082                 return
1083             self.transactions[tx_hash] = tx
1084             self.network.interface.pending_transactions_for_notifications.append(tx)
1085             self.save_transactions()
1086             if self.verifier and tx_height>0: 
1087                 self.verifier.add(tx_hash, tx_height)
1088             self.update_tx_outputs(tx_hash)
1089
1090
1091     def save_transactions(self):
1092         tx = {}
1093         for k,v in self.transactions.items():
1094             tx[k] = str(v)
1095         self.storage.put('transactions', tx, True)
1096
1097     def receive_history_callback(self, addr, hist):
1098
1099         if not self.check_new_history(addr, hist):
1100             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
1101             
1102         with self.lock:
1103             self.history[addr] = hist
1104             self.storage.put('addr_history', self.history, True)
1105
1106         if hist != ['*']:
1107             for tx_hash, tx_height in hist:
1108                 if tx_height>0:
1109                     # add it in case it was previously unconfirmed
1110                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1111
1112
1113     def get_tx_history(self, account=None):
1114         with self.transaction_lock:
1115             history = self.transactions.items()
1116             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1117             result = []
1118     
1119             balance = 0
1120             for tx_hash, tx in history:
1121                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1122                 if v is not None: balance += v
1123
1124             c, u = self.get_account_balance(account)
1125
1126             if balance != c+u:
1127                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1128
1129             balance = c + u - balance
1130             for tx_hash, tx in history:
1131                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1132                 if not is_relevant:
1133                     continue
1134                 if value is not None:
1135                     balance += value
1136
1137                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1138                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1139
1140         return result
1141
1142
1143     def get_label(self, tx_hash):
1144         label = self.labels.get(tx_hash)
1145         is_default = (label == '') or (label is None)
1146         if is_default: label = self.get_default_label(tx_hash)
1147         return label, is_default
1148
1149
1150     def get_default_label(self, tx_hash):
1151         tx = self.transactions.get(tx_hash)
1152         default_label = ''
1153         if tx:
1154             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1155             if is_mine:
1156                 for o in tx.outputs:
1157                     o_addr, _ = o
1158                     if not self.is_mine(o_addr):
1159                         try:
1160                             default_label = self.labels[o_addr]
1161                         except KeyError:
1162                             default_label = o_addr
1163                         break
1164                 else:
1165                     default_label = '(internal)'
1166             else:
1167                 for o in tx.outputs:
1168                     o_addr, _ = o
1169                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1170                         break
1171                 else:
1172                     for o in tx.outputs:
1173                         o_addr, _ = o
1174                         if self.is_mine(o_addr):
1175                             break
1176                     else:
1177                         o_addr = None
1178
1179                 if o_addr:
1180                     dest_label = self.labels.get(o_addr)
1181                     try:
1182                         default_label = self.labels[o_addr]
1183                     except KeyError:
1184                         default_label = o_addr
1185
1186         return default_label
1187
1188
1189     def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None ):
1190         for address, x in outputs:
1191             assert is_valid(address)
1192         amount = sum( map(lambda x:x[1], outputs) )
1193         inputs, total, fee = self.choose_tx_inputs( amount, fee, domain )
1194         if not inputs:
1195             raise ValueError("Not enough funds")
1196         self.add_input_info(inputs)
1197         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr)
1198         return Transaction.from_io(inputs, outputs)
1199
1200
1201     def mktx_from_account(self, outputs, password, fee=None, change_addr=None, account=None):
1202         domain = self.get_account_addresses(account) if account else None
1203         return self.mktx(outputs, password, fee, change_addr, domain)
1204
1205
1206     def mktx(self, outputs, password, fee=None, change_addr=None, domain= None ):
1207         tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain)
1208         keypairs = {}
1209         self.add_keypairs_from_wallet(tx, keypairs, password)
1210         self.sign_transaction(tx, keypairs)
1211         return tx
1212
1213
1214     def add_input_info(self, inputs):
1215         for txin in inputs:
1216             address = txin['address']
1217             account, sequence = self.get_address_index(address)
1218             txin['KeyID'] = self.get_keyID(account, sequence)
1219             redeemScript = self.accounts[account].redeem_script(sequence)
1220             if redeemScript: 
1221                 txin['redeemScript'] = redeemScript
1222             else:
1223                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1224
1225
1226     def sign_transaction(self, tx, keypairs):
1227         tx.sign(keypairs)
1228         run_hook('sign_transaction', tx)
1229
1230
1231     def sendtx(self, tx):
1232         # synchronous
1233         h = self.send_tx(tx)
1234         self.tx_event.wait()
1235         return self.receive_tx(h)
1236
1237     def send_tx(self, tx):
1238         # asynchronous
1239         self.tx_event.clear()
1240         self.network.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
1241         return tx.hash()
1242
1243     def on_broadcast(self, i, r):
1244         self.tx_result = r.get('result')
1245         self.tx_event.set()
1246
1247     def receive_tx(self,tx_hash):
1248         out = self.tx_result 
1249         if out != tx_hash:
1250             return False, "error: " + out
1251         return True, out
1252
1253
1254
1255     def update_password(self, seed, old_password, new_password):
1256         if new_password == '': new_password = None
1257         # this will throw an exception if unicode cannot be converted
1258         self.seed = pw_encode( seed, new_password)
1259         self.storage.put('seed', self.seed, True)
1260         self.use_encryption = (new_password != None)
1261         self.storage.put('use_encryption', self.use_encryption,True)
1262         for k in self.imported_keys.keys():
1263             a = self.imported_keys[k]
1264             b = pw_decode(a, old_password)
1265             c = pw_encode(b, new_password)
1266             self.imported_keys[k] = c
1267         self.storage.put('imported_keys', self.imported_keys, True)
1268
1269         for k, v in self.master_private_keys.items():
1270             b = pw_decode(v, old_password)
1271             c = pw_encode(b, new_password)
1272             self.master_private_keys[k] = c
1273         self.storage.put('master_private_keys', self.master_private_keys, True)
1274
1275
1276     def freeze(self,addr):
1277         if self.is_mine(addr) and addr not in self.frozen_addresses:
1278             self.unprioritize(addr)
1279             self.frozen_addresses.append(addr)
1280             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1281             return True
1282         else:
1283             return False
1284
1285     def unfreeze(self,addr):
1286         if self.is_mine(addr) and addr in self.frozen_addresses:
1287             self.frozen_addresses.remove(addr)
1288             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1289             return True
1290         else:
1291             return False
1292
1293     def prioritize(self,addr):
1294         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1295             self.unfreeze(addr)
1296             self.prioritized_addresses.append(addr)
1297             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1298             return True
1299         else:
1300             return False
1301
1302     def unprioritize(self,addr):
1303         if self.is_mine(addr) and addr in self.prioritized_addresses:
1304             self.prioritized_addresses.remove(addr)
1305             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1306             return True
1307         else:
1308             return False
1309
1310
1311     def set_verifier(self, verifier):
1312         self.verifier = verifier
1313
1314         # review transactions that are in the history
1315         for addr, hist in self.history.items():
1316             if hist == ['*']: continue
1317             for tx_hash, tx_height in hist:
1318                 if tx_height>0:
1319                     # add it in case it was previously unconfirmed
1320                     self.verifier.add(tx_hash, tx_height)
1321
1322
1323         # if we are on a pruning server, remove unverified transactions
1324         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1325         for tx_hash in self.transactions.keys():
1326             if tx_hash not in vr:
1327                 self.transactions.pop(tx_hash)
1328
1329
1330
1331     def check_new_history(self, addr, hist):
1332         
1333         # check that all tx in hist are relevant
1334         if hist != ['*']:
1335             for tx_hash, height in hist:
1336                 tx = self.transactions.get(tx_hash)
1337                 if not tx: continue
1338                 if not tx.has_address(addr):
1339                     return False
1340
1341         # check that we are not "orphaning" a transaction
1342         old_hist = self.history.get(addr,[])
1343         if old_hist == ['*']: return True
1344
1345         for tx_hash, height in old_hist:
1346             if tx_hash in map(lambda x:x[0], hist): continue
1347             found = False
1348             for _addr, _hist in self.history.items():
1349                 if _addr == addr: continue
1350                 if _hist == ['*']: continue
1351                 _tx_hist = map(lambda x:x[0], _hist)
1352                 if tx_hash in _tx_hist:
1353                     found = True
1354                     break
1355
1356             if not found:
1357                 tx = self.transactions.get(tx_hash)
1358                 # tx might not be there
1359                 if not tx: continue
1360                 
1361                 # already verified?
1362                 if self.verifier.get_height(tx_hash):
1363                     continue
1364                 # unconfirmed tx
1365                 print_error("new history is orphaning transaction:", tx_hash)
1366                 # check that all outputs are not mine, request histories
1367                 ext_requests = []
1368                 for _addr, _v in tx.outputs:
1369                     # assert not self.is_mine(_addr)
1370                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1371
1372                 ext_h = self.network.synchronous_get(ext_requests)
1373                 print_error("sync:", ext_requests, ext_h)
1374                 height = None
1375                 for h in ext_h:
1376                     if h == ['*']: continue
1377                     for item in h:
1378                         if item.get('tx_hash') == tx_hash:
1379                             height = item.get('height')
1380                 if height:
1381                     print_error("found height for", tx_hash, height)
1382                     self.verifier.add(tx_hash, height)
1383                 else:
1384                     print_error("removing orphaned tx from history", tx_hash)
1385                     self.transactions.pop(tx_hash)
1386
1387         return True
1388
1389
1390
1391     def check_new_tx(self, tx_hash, tx):
1392         # 1 check that tx is referenced in addr_history. 
1393         addresses = []
1394         for addr, hist in self.history.items():
1395             if hist == ['*']:continue
1396             for txh, height in hist:
1397                 if txh == tx_hash: 
1398                     addresses.append(addr)
1399
1400         if not addresses:
1401             return False
1402
1403         # 2 check that referencing addresses are in the tx
1404         for addr in addresses:
1405             if not tx.has_address(addr):
1406                 return False
1407
1408         return True
1409
1410
1411     def start_threads(self, network):
1412         from verifier import TxVerifier
1413         self.network = network
1414         self.verifier = TxVerifier(self.network, self.storage)
1415         self.verifier.start()
1416         self.set_verifier(self.verifier)
1417         self.synchronizer = WalletSynchronizer(self, network)
1418         self.synchronizer.start()
1419
1420     def stop_threads(self):
1421         self.verifier.stop()
1422         self.synchronizer.stop()
1423
1424
1425
1426     def restore(self, callback):
1427         from i18n import _
1428         def wait_for_wallet():
1429             self.set_up_to_date(False)
1430             while not self.is_up_to_date():
1431                 msg = "%s\n%s %d\n%s %.1f"%(
1432                     _("Please wait..."),
1433                     _("Addresses generated:"),
1434                     len(self.addresses(True)),_("Kilobytes received:"), 
1435                     self.network.interface.bytes_received/1024.)
1436
1437                 apply(callback, (msg,))
1438                 time.sleep(0.1)
1439
1440         def wait_for_network():
1441             while not self.network.interface.is_connected:
1442                 msg = "%s \n" % (_("Connecting..."))
1443                 apply(callback, (msg,))
1444                 time.sleep(0.1)
1445
1446         # wait until we are connected, because the user might have selected another server
1447         wait_for_network()
1448
1449         # try to restore old account
1450         self.create_old_account()
1451         wait_for_wallet()
1452
1453         if self.is_found():
1454             self.seed_version = 4
1455             self.storage.put('seed_version', wallet.seed_version, True)
1456         else:
1457             self.accounts.pop(0)
1458             self.create_accounts()
1459             wait_for_wallet()
1460
1461
1462
1463
1464 class WalletSynchronizer(threading.Thread):
1465
1466
1467     def __init__(self, wallet, network):
1468         threading.Thread.__init__(self)
1469         self.daemon = True
1470         self.wallet = wallet
1471         self.network = network
1472         self.was_updated = True
1473         self.running = False
1474         self.lock = threading.Lock()
1475         self.queue = Queue.Queue()
1476
1477     def stop(self):
1478         with self.lock: self.running = False
1479
1480     def is_running(self):
1481         with self.lock: return self.running
1482
1483     
1484     def subscribe_to_addresses(self, addresses):
1485         messages = []
1486         for addr in addresses:
1487             messages.append(('blockchain.address.subscribe', [addr]))
1488         self.network.subscribe( messages, lambda i,r: self.queue.put(r))
1489
1490
1491     def run(self):
1492         with self.lock:
1493             self.running = True
1494
1495         while self.is_running():
1496             
1497             if not self.network.is_connected():
1498                 print_error("synchronizer: waiting for interface")
1499                 self.network.wait_until_connected()
1500                 
1501             self.run_interface(self.network.interface)
1502
1503
1504     def run_interface(self, interface):
1505
1506         print_error("synchronizer: connected to", interface.server)
1507
1508         requested_tx = []
1509         missing_tx = []
1510         requested_histories = {}
1511
1512         # request any missing transactions
1513         for history in self.wallet.history.values():
1514             if history == ['*']: continue
1515             for tx_hash, tx_height in history:
1516                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1517                     missing_tx.append( (tx_hash, tx_height) )
1518
1519         if missing_tx:
1520             print_error("missing tx", missing_tx)
1521
1522         # subscriptions
1523         self.subscribe_to_addresses(self.wallet.addresses(True))
1524
1525         while self.is_running():
1526             # 1. create new addresses
1527             new_addresses = self.wallet.synchronize()
1528
1529             # request missing addresses
1530             if new_addresses:
1531                 self.subscribe_to_addresses(new_addresses)
1532
1533             # request missing transactions
1534             for tx_hash, tx_height in missing_tx:
1535                 if (tx_hash, tx_height) not in requested_tx:
1536                     interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
1537                     requested_tx.append( (tx_hash, tx_height) )
1538             missing_tx = []
1539
1540             # detect if situation has changed
1541             if interface.is_up_to_date() and self.queue.empty():
1542                 if not self.wallet.is_up_to_date():
1543                     self.wallet.set_up_to_date(True)
1544                     self.was_updated = True
1545             else:
1546                 if self.wallet.is_up_to_date():
1547                     self.wallet.set_up_to_date(False)
1548                     self.was_updated = True
1549
1550             if self.was_updated:
1551                 self.wallet.network.trigger_callback('updated')
1552                 self.was_updated = False
1553
1554             # 2. get a response
1555             try:
1556                 r = self.queue.get(block=True, timeout=1)
1557             except Queue.Empty:
1558                 continue
1559
1560             if interface != self.network.interface:
1561                 break
1562             
1563             if not r:
1564                 continue
1565
1566             # 3. handle response
1567             method = r['method']
1568             params = r['params']
1569             result = r.get('result')
1570             error = r.get('error')
1571             if error:
1572                 print "error", r
1573                 continue
1574
1575             if method == 'blockchain.address.subscribe':
1576                 addr = params[0]
1577                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1578                     if requested_histories.get(addr) is None:
1579                         interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
1580                         requested_histories[addr] = result
1581
1582             elif method == 'blockchain.address.get_history':
1583                 addr = params[0]
1584                 print_error("receiving history", addr, result)
1585                 if result == ['*']:
1586                     assert requested_histories.pop(addr) == '*'
1587                     self.wallet.receive_history_callback(addr, result)
1588                 else:
1589                     hist = []
1590                     # check that txids are unique
1591                     txids = []
1592                     for item in result:
1593                         tx_hash = item['tx_hash']
1594                         if tx_hash not in txids:
1595                             txids.append(tx_hash)
1596                             hist.append( (tx_hash, item['height']) )
1597
1598                     if len(hist) != len(result):
1599                         raise BaseException("error: server sent history with non-unique txid", result)
1600
1601                     # check that the status corresponds to what was announced
1602                     rs = requested_histories.pop(addr)
1603                     if self.wallet.get_status(hist) != rs:
1604                         raise BaseException("error: status mismatch: %s"%addr)
1605                 
1606                     # store received history
1607                     self.wallet.receive_history_callback(addr, hist)
1608
1609                     # request transactions that we don't have 
1610                     for tx_hash, tx_height in hist:
1611                         if self.wallet.transactions.get(tx_hash) is None:
1612                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1613                                 missing_tx.append( (tx_hash, tx_height) )
1614
1615             elif method == 'blockchain.transaction.get':
1616                 tx_hash = params[0]
1617                 tx_height = params[1]
1618                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1619                 tx = Transaction(result)
1620                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1621                 self.was_updated = True
1622                 requested_tx.remove( (tx_hash, tx_height) )
1623                 print_error("received tx:", tx_hash, len(tx.raw))
1624
1625             else:
1626                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1627
1628             if self.was_updated and not requested_tx:
1629                 self.wallet.network.trigger_callback('updated')
1630                 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
1631
1632                 self.was_updated = False