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