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