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