set label
[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.data = {}
72         self.file_exists = False
73         self.init_path(config)
74         print_error( "wallet path", self.path )
75         if self.path:
76             self.read(self.path)
77
78
79     def init_path(self, config):
80         """Set the path of the wallet."""
81
82         path = config.get('wallet_path')
83         if not path:
84             path = config.get('default_wallet_path')
85         if path is not None:
86             self.path = path
87             return
88
89         self.path = os.path.join(config.path, "electrum.dat")
90
91
92     def read(self, path):
93         """Read the contents of the wallet file."""
94         try:
95             with open(self.path, "r") as f:
96                 data = f.read()
97         except IOError:
98             return
99         try:
100             d = ast.literal_eval( data )  #parse raw data from reading wallet file
101         except:
102             raise IOError("Cannot read wallet file.")
103
104         self.data = d
105         self.file_exists = True
106
107
108     def get(self, key, default=None):
109         return self.data.get(key, default)
110
111     def put(self, key, value, save = True):
112
113         if self.data.get(key) is not None:
114             self.data[key] = value
115         else:
116             # add key to wallet config
117             self.data[key] = value
118
119         if save: 
120             self.write()
121
122
123     def write(self):
124         s = repr(self.data)
125         f = open(self.path,"w")
126         f.write( s )
127         f.close()
128         if self.get('gui') != 'android':
129             import stat
130             os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
131
132
133 class Wallet:
134
135     def __init__(self, storage):
136
137         self.storage = storage
138         self.electrum_version = ELECTRUM_VERSION
139         self.gap_limit_for_change = 3 # constant
140
141         # saved fields
142         self.seed_version          = storage.get('seed_version', SEED_VERSION)
143
144         self.gap_limit             = storage.get('gap_limit', 5)
145         self.use_change            = storage.get('use_change',True)
146         self.use_encryption        = storage.get('use_encryption', False)
147         self.seed                  = storage.get('seed', '')               # encrypted
148         self.labels                = storage.get('labels', {})
149         self.frozen_addresses      = storage.get('frozen_addresses',[])
150         self.prioritized_addresses = storage.get('prioritized_addresses',[])
151         self.addressbook           = storage.get('contacts', [])
152
153         self.imported_keys         = storage.get('imported_keys',{})
154         self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
155
156         self.fee                   = int(storage.get('fee_per_kb',20000))
157
158         self.master_public_keys = storage.get('master_public_keys',{})
159         self.master_private_keys = storage.get('master_private_keys', {})
160
161         self.next_addresses = storage.get('next_addresses',{})
162
163         if self.seed_version < 4:
164             raise ValueError("This wallet seed is deprecated.")
165
166         self.load_accounts()
167
168         self.transactions = {}
169         tx_list = self.storage.get('transactions',{})
170         for k,v in tx_list.items():
171             try:
172                 tx = Transaction(v)
173             except:
174                 print_msg("Warning: Cannot deserialize transactions. skipping")
175                 continue
176
177             self.add_extra_addresses(tx)
178             self.transactions[k] = tx
179
180         for h,tx in self.transactions.items():
181             if not self.check_new_tx(h, tx):
182                 print_error("removing unreferenced tx", h)
183                 self.transactions.pop(h)
184
185
186         # not saved
187         self.prevout_values = {}     # my own transaction outputs
188         self.spent_outputs = []
189
190         # spv
191         self.verifier = None
192
193         # there is a difference between wallet.up_to_date and interface.is_up_to_date()
194         # interface.is_up_to_date() returns true when all requests have been answered and processed
195         # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
196         
197         self.up_to_date = False
198         self.lock = threading.Lock()
199         self.transaction_lock = threading.Lock()
200         self.tx_event = threading.Event()
201
202         for tx_hash, tx in self.transactions.items():
203             self.update_tx_outputs(tx_hash)
204
205
206     def add_extra_addresses(self, tx):
207         h = tx.hash()
208         # find the address corresponding to pay-to-pubkey inputs
209         tx.add_extra_addresses(self.transactions)
210         for o in tx.d.get('outputs'):
211             if o.get('is_pubkey'):
212                 for tx2 in self.transactions.values():
213                     tx2.add_extra_addresses({h:tx})
214
215             
216
217
218     def set_up_to_date(self,b):
219         with self.lock: self.up_to_date = b
220
221
222     def is_up_to_date(self):
223         with self.lock: return self.up_to_date
224
225
226     def update(self):
227         self.up_to_date = False
228         while not self.is_up_to_date(): 
229             time.sleep(0.1)
230
231
232     def import_key(self, sec, password):
233         # check password
234         seed = self.decode_seed(password)
235         try:
236             address = address_from_private_key(sec)
237         except:
238             raise BaseException('Invalid private key')
239
240         if self.is_mine(address):
241             raise BaseException('Address already in wallet')
242         
243         # store the originally requested keypair into the imported keys table
244         self.imported_keys[address] = pw_encode(sec, password )
245         self.storage.put('imported_keys', self.imported_keys, True)
246         if self.synchronizer:
247             self.synchronizer.subscribe_to_addresses([address])
248         return address
249         
250     def delete_imported_key(self, addr):
251         if addr in self.imported_keys:
252             self.imported_keys.pop(addr)
253             self.storage.put('imported_keys', self.imported_keys, True)
254
255
256     def init_seed(self, seed):
257         if self.seed: raise BaseException("a seed exists")
258         if not seed: 
259             seed = random_seed(128)
260         self.seed = seed
261
262
263     def save_seed(self):
264         self.storage.put('seed', self.seed, True)
265         self.storage.put('seed_version', self.seed_version, True)
266
267     def create_watching_only_wallet(self, c0, K0):
268         cK0 = ""
269         self.master_public_keys = {
270             "m/0'/": (c0, K0, cK0),
271             }
272         self.storage.put('master_public_keys', self.master_public_keys, True)
273         self.create_account('1','Main account')
274
275     def create_accounts(self):
276
277         master_k, master_c, master_K, master_cK = bip32_init(self.seed)
278         
279         # normal accounts
280         k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
281         # p2sh 2of2
282         k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
283         k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
284         # p2sh 2of3
285         k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
286         k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
287         k5, c5, K5, cK5 = bip32_private_derivation(master_k, master_c, "m/", "m/5'/")
288
289         self.master_public_keys = {
290             "m/0'/": (c0, K0, cK0),
291             "m/1'/": (c1, K1, cK1),
292             "m/2'/": (c2, K2, cK2),
293             "m/3'/": (c3, K3, cK3),
294             "m/4'/": (c4, K4, cK4),
295             "m/5'/": (c5, K5, cK5)
296             }
297         
298         self.master_private_keys = {
299             "m/0'/": k0,
300             "m/1'/": k1,
301             "m/2'/": k2,
302             "m/3'/": k3,
303             "m/4'/": k4,
304             "m/5'/": k5
305             }
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         # create default account
311         self.create_account('1','Main account')
312
313
314     def find_root_by_master_key(self, c, K):
315         for key, v in self.master_public_keys.items():
316             if key == "m/":continue
317             cc, KK, _ = v
318             if (c == cc) and (K == KK):
319                 return key
320
321     def deseed_root(self, seed, password):
322         # for safety, we ask the user to enter their seed
323         assert seed == self.decode_seed(password)
324         self.seed = ''
325         self.storage.put('seed', '', True)
326
327
328     def deseed_branch(self, k):
329         # check that parent has no seed
330         assert self.seed == ''
331         self.master_private_keys.pop(k)
332         self.storage.put('master_private_keys', self.master_private_keys, True)
333
334
335     def account_id(self, account_type, i):
336         if account_type == '1':
337             return "m/0'/%d"%i
338         elif account_type == '2of2':
339             return "m/1'/%d & m/2'/%d"%(i,i)
340         elif account_type == '2of3':
341             return "m/3'/%d & m/4'/%d & m/5'/%d"%(i,i,i)
342         else:
343             raise BaseException('unknown account type')
344
345
346     def num_accounts(self, account_type):
347         keys = self.accounts.keys()
348         i = 0
349         while True:
350             account_id = self.account_id(account_type, i)
351             if account_id not in keys: break
352             i += 1
353         return i
354
355
356     def new_account_address(self, account_type = '1'):
357         i = self.num_accounts(account_type)
358         k = self.account_id(account_type,i)
359
360         addr = self.next_addresses.get(k)
361         if not addr: 
362             account_id, account = self.next_account(account_type)
363             addr = account.first_address()
364             self.next_addresses[k] = addr
365             self.storage.put('next_addresses',self.next_addresses)
366
367         return addr
368
369
370     def next_account(self, account_type = '1'):
371
372         i = self.num_accounts(account_type)
373         account_id = self.account_id(account_type,i)
374
375         if account_type is '1':
376             master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
377             c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
378             account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
379
380         elif account_type == '2of2':
381             master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
382             c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
383             master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
384             c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
385             account = BIP32_Account_2of2({ 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
386
387         elif account_type == '2of3':
388             master_c3, master_K3, _ = self.master_public_keys["m/3'/"]
389             c3, K3, cK3 = bip32_public_derivation(master_c3.decode('hex'), master_K3.decode('hex'), "m/3'/", "m/3'/%d"%i)
390             master_c4, master_K4, _ = self.master_public_keys["m/4'/"]
391             c4, K4, cK4 = bip32_public_derivation(master_c4.decode('hex'), master_K4.decode('hex'), "m/4'/", "m/4'/%d"%i)
392             master_c5, master_K5, _ = self.master_public_keys["m/5'/"]
393             c5, K5, cK5 = bip32_public_derivation(master_c5.decode('hex'), master_K5.decode('hex'), "m/5'/", "m/5'/%d"%i)
394             account = BIP32_Account_2of3({ 'c':c3, 'K':K3, 'cK':cK3, 'c2':c4, 'K2':K4, 'cK2':cK4, 'c3':c5, 'K3':K5, 'cK3':cK5 })
395
396         return account_id, account
397
398
399     def set_label(self, name, text = None):
400         changed = False
401         old_text = self.labels.get(name)
402         if text:
403             if old_text != text:
404                 self.labels[name] = text
405                 changed = True
406         else:
407             if old_text:
408                 self.labels.pop(name)
409                 changed = True
410
411         if changed:
412             self.storage.put('labels', self.labels, True)
413
414         run_hook('set_label', name, text, changed)
415         return changed
416
417
418
419     def create_account(self, account_type = '1', name = None):
420         account_id, account = self.next_account(account_type)
421         self.accounts[account_id] = account
422         self.save_accounts()
423         if name:
424             self.set_label(account_id, name)
425
426
427     def create_old_account(self):
428         mpk = OldAccount.mpk_from_seed(self.seed)
429         self.storage.put('master_public_key', mpk, True)
430         self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
431         self.save_accounts()
432
433
434     def save_accounts(self):
435         d = {}
436         for k, v in self.accounts.items():
437             d[k] = v.dump()
438         self.storage.put('accounts', d, True)
439
440     
441
442     def load_accounts(self):
443         d = self.storage.get('accounts', {})
444         self.accounts = {}
445         for k, v in d.items():
446             if k == 0:
447                 v['mpk'] = self.storage.get('master_public_key')
448                 self.accounts[k] = OldAccount(v)
449             elif '&' in k:
450                 self.accounts[k] = BIP32_Account_2of2(v)
451             else:
452                 self.accounts[k] = BIP32_Account(v)
453
454
455     def addresses(self, include_change = True, next=False):
456         o = self.get_account_addresses(-1, include_change)
457         for a in self.accounts.keys():
458             o += self.get_account_addresses(a, include_change)
459
460         if next:
461             for addr in self.next_addresses.values():
462                 if addr not in o:
463                     o += [addr]
464         return o
465
466
467     def is_mine(self, address):
468         return address in self.addresses(True)
469
470
471     def is_change(self, address):
472         if not self.is_mine(address): return False
473         if address in self.imported_keys.keys(): return False
474         acct, s = self.get_address_index(address)
475         if s is None: return False
476         return s[0] == 1
477
478     def get_master_public_key(self):
479         if self.seed_version == 4:
480             return self.storage.get("master_public_key")
481         else:
482             c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
483             return repr((c, K))
484
485     def get_master_private_key(self, account, password):
486         master_k = pw_decode( self.master_private_keys[account], password)
487         master_c, master_K, master_Kc = self.master_public_keys[account]
488         try:
489             K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
490             assert K.encode('hex') == master_K
491         except:
492             raise BaseException("Invalid password")
493         return master_k
494
495
496     def get_address_index(self, address):
497         if address in self.imported_keys.keys():
498             return -1, None
499
500         for account in self.accounts.keys():
501             for for_change in [0,1]:
502                 addresses = self.accounts[account].get_addresses(for_change)
503                 for addr in addresses:
504                     if address == addr:
505                         return account, (for_change, addresses.index(addr))
506
507         raise BaseException("Address not found", address)
508
509
510     def get_roots(self, account):
511         roots = []
512         for a in account.split('&'):
513             s = a.strip()
514             m = re.match("(m/\d+'/)(\d+)", s)
515             roots.append( m.group(1) )
516         return roots
517
518     def is_seeded(self, account):
519         if type(account) is int:
520             return self.seed is not None
521
522         for root in self.get_roots(account):
523             if root not in self.master_private_keys.keys(): 
524                 return False
525         return True
526
527     def rebase_sequence(self, account, sequence):
528         c, i = sequence
529         dd = []
530         for a in account.split('&'):
531             s = a.strip()
532             m = re.match("(m/\d+'/)(\d+)", s)
533             root = m.group(1)
534             num = int(m.group(2))
535             dd.append( (root, [num,c,i] ) )
536         return dd
537         
538
539     def get_keyID(self, account, sequence):
540         if account == 0:
541             return 'old'
542
543         rs = self.rebase_sequence(account, sequence)
544         dd = []
545         for root, public_sequence in rs:
546             c, K, _ = self.master_public_keys[root]
547             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
548             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
549         return '&'.join(dd)
550
551
552     def get_public_key(self, address):
553         account, sequence = self.get_address_index(address)
554         return self.accounts[account].get_pubkey( *sequence )
555
556
557     def decode_seed(self, password):
558         seed = pw_decode(self.seed, password)
559         #todo:  #self.sequences[0].check_seed(seed)
560         return seed
561         
562
563     def get_private_key(self, address, password):
564         out = []
565         if address in self.imported_keys.keys():
566             out.append( pw_decode( self.imported_keys[address], password ) )
567         else:
568             account, sequence = self.get_address_index(address)
569             if account == 0:
570                 seed = self.decode_seed(password)
571                 pk = self.accounts[account].get_private_key(seed, sequence)
572                 out.append(pk)
573                 return out
574
575             # assert address == self.accounts[account].get_address(*sequence)
576             rs = self.rebase_sequence( account, sequence)
577             for root, public_sequence in rs:
578
579                 if root not in self.master_private_keys.keys(): continue
580                 master_k = self.get_master_private_key(root, password)
581                 master_c, _, _ = self.master_public_keys[root]
582                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
583                 out.append(pk)
584                     
585         return out
586
587
588
589
590     def signrawtransaction(self, tx, input_info, private_keys, password):
591
592         unspent_coins = self.get_unspent_coins()
593         seed = self.decode_seed(password)
594
595         # build a list of public/private keys
596         keypairs = {}
597         for sec in private_keys:
598             pubkey = public_key_from_private_key(sec)
599             keypairs[ pubkey ] = sec
600
601
602         for txin in tx.inputs:
603             # convert to own format
604             txin['tx_hash'] = txin['prevout_hash']
605             txin['index'] = txin['prevout_n']
606
607             for item in input_info:
608                 if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']:
609                     txin['raw_output_script'] = item['scriptPubKey']
610                     txin['redeemScript'] = item.get('redeemScript')
611                     txin['KeyID'] = item.get('KeyID')
612                     break
613             else:
614                 for item in unspent_coins:
615                     if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
616                         print_error( "tx input is in unspent coins" )
617                         txin['raw_output_script'] = item['raw_output_script']
618                         account, sequence = self.get_address_index(item['address'])
619                         if account != -1:
620                             txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
621                         break
622                 else:
623                     raise BaseException("Unknown transaction input. Please provide the 'input_info' parameter, or synchronize this wallet")
624
625             # if available, derive private_keys from KeyID
626             keyid = txin.get('KeyID')
627             if keyid:
628                 roots = []
629                 for s in keyid.split('&'):
630                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
631                     if not m: continue
632                     c = m.group(1)
633                     K = m.group(2)
634                     sequence = m.group(3)
635                     root = self.find_root_by_master_key(c,K)
636                     if not root: continue
637                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
638                     root = root + '%d'%sequence[0]
639                     sequence = sequence[1:]
640                     roots.append((root,sequence)) 
641
642                 account_id = " & ".join( map(lambda x:x[0], roots) )
643                 account = self.accounts.get(account_id)
644                 if not account: continue
645                 addr = account.get_address(*sequence)
646                 txin['address'] = addr
647                 pk = self.get_private_key(addr, password)
648                 for sec in pk:
649                     pubkey = public_key_from_private_key(sec)
650                     keypairs[pubkey] = sec
651
652             redeem_script = txin.get("redeemScript")
653             print_error( "p2sh:", "yes" if redeem_script else "no")
654             if redeem_script:
655                 addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5)
656             else:
657                 import transaction
658                 _, addr = transaction.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
659             txin['address'] = addr
660
661             # add private keys that are in the wallet
662             pk = self.get_private_key(addr, password)
663             for sec in pk:
664                 pubkey = public_key_from_private_key(sec)
665                 keypairs[pubkey] = sec
666                 if not redeem_script:
667                     txin['redeemPubkey'] = pubkey
668
669             print txin
670
671         self.sign_tx(tx, keypairs)
672
673
674     def sign_message(self, address, message, password):
675         keys = self.get_private_key(address, password)
676         assert len(keys) == 1
677         sec = keys[0]
678         key = regenerate_key(sec)
679         compressed = is_compressed(sec)
680         return key.sign_message(message, compressed, address)
681
682     def verify_message(self, address, signature, message):
683         try:
684             EC_KEY.verify_message(address, signature, message)
685             return True
686         except BaseException as e:
687             print_error("Verification error: {0}".format(e))
688             return False
689
690
691     def change_gap_limit(self, value):
692         if value >= self.gap_limit:
693             self.gap_limit = value
694             self.storage.put('gap_limit', self.gap_limit, True)
695             #self.interface.poke('synchronizer')
696             return True
697
698         elif value >= self.min_acceptable_gap():
699             for key, account in self.accounts.items():
700                 addresses = account[0]
701                 k = self.num_unused_trailing_addresses(addresses)
702                 n = len(addresses) - k + value
703                 addresses = addresses[0:n]
704                 self.accounts[key][0] = addresses
705
706             self.gap_limit = value
707             self.storage.put('gap_limit', self.gap_limit, True)
708             self.save_accounts()
709             return True
710         else:
711             return False
712
713     def num_unused_trailing_addresses(self, addresses):
714         k = 0
715         for a in addresses[::-1]:
716             if self.history.get(a):break
717             k = k + 1
718         return k
719
720     def min_acceptable_gap(self):
721         # fixme: this assumes wallet is synchronized
722         n = 0
723         nmax = 0
724
725         for account in self.accounts.values():
726             addresses = account.get_addresses(0)
727             k = self.num_unused_trailing_addresses(addresses)
728             for a in addresses[0:-k]:
729                 if self.history.get(a):
730                     n = 0
731                 else:
732                     n += 1
733                     if n > nmax: nmax = n
734         return nmax + 1
735
736
737     def address_is_old(self, address):
738         age = -1
739         h = self.history.get(address, [])
740         if h == ['*']:
741             return True
742         for tx_hash, tx_height in h:
743             if tx_height == 0:
744                 tx_age = 0
745             else: 
746                 tx_age = self.verifier.blockchain.height - tx_height + 1
747             if tx_age > age:
748                 age = tx_age
749         return age > 2
750
751
752     def synchronize_sequence(self, account, for_change):
753         limit = self.gap_limit_for_change if for_change else self.gap_limit
754         new_addresses = []
755         while True:
756             addresses = account.get_addresses(for_change)
757             if len(addresses) < limit:
758                 address = account.create_new_address(for_change)
759                 self.history[address] = []
760                 new_addresses.append( address )
761                 continue
762
763             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
764                 break
765             else:
766                 address = account.create_new_address(for_change)
767                 self.history[address] = []
768                 new_addresses.append( address )
769
770         return new_addresses
771         
772
773
774     def create_pending_accounts(self):
775         for account_type in ['1','2of2','2of3']:
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('index')
983                     if key in self.spent_outputs: continue
984                     output['tx_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         self.sign_transaction(tx, password)
1214         return tx
1215
1216
1217     def add_input_info(self, inputs):
1218         for txin in inputs:
1219             address = txin['address']
1220             account, sequence = self.get_address_index(address)
1221             txin['KeyID'] = self.get_keyID(account, sequence)
1222             redeemScript = self.accounts[account].redeem_script(sequence)
1223             if redeemScript: 
1224                 txin['redeemScript'] = redeemScript
1225             else:
1226                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1227
1228
1229     def sign_transaction(self, tx, password):
1230         keypairs = {}
1231         for i, txin in enumerate(tx.inputs):
1232             address = txin['address']
1233             private_keys = self.get_private_key(address, password)
1234             for sec in private_keys:
1235                 pubkey = public_key_from_private_key(sec)
1236                 keypairs[ pubkey ] = sec
1237
1238         self.sign_tx(tx, keypairs)
1239
1240
1241     def sign_tx(self, tx, keypairs):
1242         tx.sign(keypairs)
1243         run_hook('sign_tx', tx)
1244
1245
1246     def sendtx(self, tx):
1247         # synchronous
1248         h = self.send_tx(tx)
1249         self.tx_event.wait()
1250         return self.receive_tx(h)
1251
1252     def send_tx(self, tx):
1253         # asynchronous
1254         self.tx_event.clear()
1255         self.network.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
1256         return tx.hash()
1257
1258     def on_broadcast(self, i, r):
1259         self.tx_result = r.get('result')
1260         self.tx_event.set()
1261
1262     def receive_tx(self,tx_hash):
1263         out = self.tx_result 
1264         if out != tx_hash:
1265             return False, "error: " + out
1266         return True, out
1267
1268
1269
1270     def update_password(self, seed, old_password, new_password):
1271         if new_password == '': new_password = None
1272         # this will throw an exception if unicode cannot be converted
1273         self.seed = pw_encode( seed, new_password)
1274         self.storage.put('seed', self.seed, True)
1275         self.use_encryption = (new_password != None)
1276         self.storage.put('use_encryption', self.use_encryption,True)
1277         for k in self.imported_keys.keys():
1278             a = self.imported_keys[k]
1279             b = pw_decode(a, old_password)
1280             c = pw_encode(b, new_password)
1281             self.imported_keys[k] = c
1282         self.storage.put('imported_keys', self.imported_keys, True)
1283
1284         for k, v in self.master_private_keys.items():
1285             b = pw_decode(v, old_password)
1286             c = pw_encode(b, new_password)
1287             self.master_private_keys[k] = c
1288         self.storage.put('master_private_keys', self.master_private_keys, True)
1289
1290
1291     def freeze(self,addr):
1292         if self.is_mine(addr) and addr not in self.frozen_addresses:
1293             self.unprioritize(addr)
1294             self.frozen_addresses.append(addr)
1295             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1296             return True
1297         else:
1298             return False
1299
1300     def unfreeze(self,addr):
1301         if self.is_mine(addr) and addr in self.frozen_addresses:
1302             self.frozen_addresses.remove(addr)
1303             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1304             return True
1305         else:
1306             return False
1307
1308     def prioritize(self,addr):
1309         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1310             self.unfreeze(addr)
1311             self.prioritized_addresses.append(addr)
1312             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1313             return True
1314         else:
1315             return False
1316
1317     def unprioritize(self,addr):
1318         if self.is_mine(addr) and addr in self.prioritized_addresses:
1319             self.prioritized_addresses.remove(addr)
1320             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1321             return True
1322         else:
1323             return False
1324
1325
1326     def set_verifier(self, verifier):
1327         self.verifier = verifier
1328
1329         # review transactions that are in the history
1330         for addr, hist in self.history.items():
1331             if hist == ['*']: continue
1332             for tx_hash, tx_height in hist:
1333                 if tx_height>0:
1334                     # add it in case it was previously unconfirmed
1335                     self.verifier.add(tx_hash, tx_height)
1336
1337
1338         # if we are on a pruning server, remove unverified transactions
1339         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1340         for tx_hash in self.transactions.keys():
1341             if tx_hash not in vr:
1342                 self.transactions.pop(tx_hash)
1343
1344
1345
1346     def check_new_history(self, addr, hist):
1347         
1348         # check that all tx in hist are relevant
1349         if hist != ['*']:
1350             for tx_hash, height in hist:
1351                 tx = self.transactions.get(tx_hash)
1352                 if not tx: continue
1353                 if not tx.has_address(addr):
1354                     return False
1355
1356         # check that we are not "orphaning" a transaction
1357         old_hist = self.history.get(addr,[])
1358         if old_hist == ['*']: return True
1359
1360         for tx_hash, height in old_hist:
1361             if tx_hash in map(lambda x:x[0], hist): continue
1362             found = False
1363             for _addr, _hist in self.history.items():
1364                 if _addr == addr: continue
1365                 if _hist == ['*']: continue
1366                 _tx_hist = map(lambda x:x[0], _hist)
1367                 if tx_hash in _tx_hist:
1368                     found = True
1369                     break
1370
1371             if not found:
1372                 tx = self.transactions.get(tx_hash)
1373                 # tx might not be there
1374                 if not tx: continue
1375                 
1376                 # already verified?
1377                 if self.verifier.get_height(tx_hash):
1378                     continue
1379                 # unconfirmed tx
1380                 print_error("new history is orphaning transaction:", tx_hash)
1381                 # check that all outputs are not mine, request histories
1382                 ext_requests = []
1383                 for _addr, _v in tx.outputs:
1384                     # assert not self.is_mine(_addr)
1385                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1386
1387                 ext_h = self.network.interface.synchronous_get(ext_requests)
1388                 print_error("sync:", ext_requests, ext_h)
1389                 height = None
1390                 for h in ext_h:
1391                     if h == ['*']: continue
1392                     for item in h:
1393                         if item.get('tx_hash') == tx_hash:
1394                             height = item.get('height')
1395                 if height:
1396                     print_error("found height for", tx_hash, height)
1397                     self.verifier.add(tx_hash, height)
1398                 else:
1399                     print_error("removing orphaned tx from history", tx_hash)
1400                     self.transactions.pop(tx_hash)
1401
1402         return True
1403
1404
1405
1406     def check_new_tx(self, tx_hash, tx):
1407         # 1 check that tx is referenced in addr_history. 
1408         addresses = []
1409         for addr, hist in self.history.items():
1410             if hist == ['*']:continue
1411             for txh, height in hist:
1412                 if txh == tx_hash: 
1413                     addresses.append(addr)
1414
1415         if not addresses:
1416             return False
1417
1418         # 2 check that referencing addresses are in the tx
1419         for addr in addresses:
1420             if not tx.has_address(addr):
1421                 return False
1422
1423         return True
1424
1425
1426     def start_threads(self, network):
1427         from verifier import TxVerifier
1428         self.network = network
1429         self.verifier = TxVerifier(self.network, self.storage)
1430         self.verifier.start()
1431         self.set_verifier(self.verifier)
1432         self.synchronizer = WalletSynchronizer(self)
1433         self.synchronizer.start()
1434
1435     def stop_threads(self):
1436         self.verifier.stop()
1437         self.synchronizer.stop()
1438
1439
1440
1441     def restore(self, callback):
1442         from i18n import _
1443         def wait_for_wallet():
1444             self.set_up_to_date(False)
1445             while not self.is_up_to_date():
1446                 msg = "%s\n%s %d\n%s %.1f"%(
1447                     _("Please wait..."),
1448                     _("Addresses generated:"),
1449                     len(self.addresses(True)),_("Kilobytes received:"), 
1450                     self.network.interface.bytes_received/1024.)
1451
1452                 apply(callback, (msg,))
1453                 time.sleep(0.1)
1454
1455         def wait_for_network():
1456             while not self.network.interface.is_connected:
1457                 msg = "%s \n" % (_("Connecting..."))
1458                 apply(callback, (msg,))
1459                 time.sleep(0.1)
1460
1461         # wait until we are connected, because the user might have selected another server
1462         wait_for_network()
1463
1464         # try to restore old account
1465         self.create_old_account()
1466         wait_for_wallet()
1467
1468         if self.is_found():
1469             self.seed_version = 4
1470             self.storage.put('seed_version', wallet.seed_version, True)
1471         else:
1472             self.accounts.pop(0)
1473             self.create_accounts()
1474             wait_for_wallet()
1475
1476
1477
1478
1479 class WalletSynchronizer(threading.Thread):
1480
1481
1482     def __init__(self, wallet):
1483         threading.Thread.__init__(self)
1484         self.daemon = True
1485         self.wallet = wallet
1486         wallet.synchronizer = self
1487         self.network = self.wallet.network
1488         #self.wallet.network.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1489         self.was_updated = True
1490         self.running = False
1491         self.lock = threading.Lock()
1492         self.queue = Queue.Queue()
1493
1494     def stop(self):
1495         with self.lock: self.running = False
1496
1497     def is_running(self):
1498         with self.lock: return self.running
1499
1500     
1501     def subscribe_to_addresses(self, addresses):
1502         messages = []
1503         for addr in addresses:
1504             messages.append(('blockchain.address.subscribe', [addr]))
1505         self.network.interface.send( messages, lambda i,r: self.queue.put(r))
1506
1507
1508     def run(self):
1509         with self.lock:
1510             self.running = True
1511
1512         while self.is_running():
1513             interface = self.network.interface
1514             if not interface.is_connected:
1515                 print_error("synchronizer: waiting for interface")
1516                 interface.connect_event.wait()
1517                 
1518             self.run_interface(interface)
1519
1520
1521     def run_interface(self, interface):
1522
1523         print_error("synchronizer: connected to", interface.server)
1524
1525         requested_tx = []
1526         missing_tx = []
1527         requested_histories = {}
1528
1529         # request any missing transactions
1530         for history in self.wallet.history.values():
1531             if history == ['*']: continue
1532             for tx_hash, tx_height in history:
1533                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1534                     missing_tx.append( (tx_hash, tx_height) )
1535
1536         if missing_tx:
1537             print_error("missing tx", missing_tx)
1538
1539         # subscriptions
1540         self.subscribe_to_addresses(self.wallet.addresses(True, next=True))
1541
1542         while self.is_running():
1543             # 1. create new addresses
1544             new_addresses = self.wallet.synchronize()
1545
1546             # request missing addresses
1547             if new_addresses:
1548                 self.subscribe_to_addresses(new_addresses)
1549
1550             # request missing transactions
1551             for tx_hash, tx_height in missing_tx:
1552                 if (tx_hash, tx_height) not in requested_tx:
1553                     interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
1554                     requested_tx.append( (tx_hash, tx_height) )
1555             missing_tx = []
1556
1557             # detect if situation has changed
1558             if interface.is_up_to_date() and self.queue.empty():
1559                 if not self.wallet.is_up_to_date():
1560                     self.wallet.set_up_to_date(True)
1561                     self.was_updated = True
1562             else:
1563                 if self.wallet.is_up_to_date():
1564                     self.wallet.set_up_to_date(False)
1565                     self.was_updated = True
1566
1567             if self.was_updated:
1568                 self.wallet.network.trigger_callback('updated')
1569                 self.was_updated = False
1570
1571             # 2. get a response
1572             try:
1573                 r = self.queue.get(block=True, timeout=1)
1574             except Queue.Empty:
1575                 continue
1576
1577             if interface != self.network.interface:
1578                 break
1579             
1580             if not r:
1581                 continue
1582
1583             # 3. handle response
1584             method = r['method']
1585             params = r['params']
1586             result = r.get('result')
1587             error = r.get('error')
1588             if error:
1589                 print "error", r
1590                 continue
1591
1592             if method == 'blockchain.address.subscribe':
1593                 addr = params[0]
1594                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1595                     if requested_histories.get(addr) is None:
1596                         interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
1597                         requested_histories[addr] = result
1598
1599             elif method == 'blockchain.address.get_history':
1600                 addr = params[0]
1601                 print_error("receiving history", addr, result)
1602                 if result == ['*']:
1603                     assert requested_histories.pop(addr) == '*'
1604                     self.wallet.receive_history_callback(addr, result)
1605                 else:
1606                     hist = []
1607                     # check that txids are unique
1608                     txids = []
1609                     for item in result:
1610                         tx_hash = item['tx_hash']
1611                         if tx_hash not in txids:
1612                             txids.append(tx_hash)
1613                             hist.append( (tx_hash, item['height']) )
1614
1615                     if len(hist) != len(result):
1616                         raise BaseException("error: server sent history with non-unique txid", result)
1617
1618                     # check that the status corresponds to what was announced
1619                     rs = requested_histories.pop(addr)
1620                     if self.wallet.get_status(hist) != rs:
1621                         raise BaseException("error: status mismatch: %s"%addr)
1622                 
1623                     # store received history
1624                     self.wallet.receive_history_callback(addr, hist)
1625
1626                     # request transactions that we don't have 
1627                     for tx_hash, tx_height in hist:
1628                         if self.wallet.transactions.get(tx_hash) is None:
1629                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1630                                 missing_tx.append( (tx_hash, tx_height) )
1631
1632             elif method == 'blockchain.transaction.get':
1633                 tx_hash = params[0]
1634                 tx_height = params[1]
1635                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1636                 tx = Transaction(result)
1637                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1638                 self.was_updated = True
1639                 requested_tx.remove( (tx_hash, tx_height) )
1640                 print_error("received tx:", tx_hash, len(tx.raw))
1641
1642             else:
1643                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1644
1645             if self.was_updated and not requested_tx:
1646                 self.wallet.network.trigger_callback('updated')
1647                 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
1648
1649                 self.was_updated = False