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