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