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