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