wallet.make_unsigned_transaction()
[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 make_unsigned_transaction(self, outputs, fee=None, change_addr=None, account=None ):
1114         for address, x in outputs:
1115             assert is_valid(address)
1116         amount = sum( map(lambda x:x[1], outputs) )
1117         inputs, total, fee = self.choose_tx_inputs( amount, fee, account )
1118         if not inputs:
1119             raise ValueError("Not enough funds")
1120         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
1121         return Transaction.from_io(inputs, outputs)
1122
1123
1124     def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
1125         tx = self.make_unsigned_transaction(outputs, fee, change_addr, account)
1126         self.sign_transaction(tx, password)
1127         return tx
1128
1129
1130     def sign_transaction(self, tx, password):
1131         keypairs = {}
1132         for i, txin in enumerate(tx.inputs):
1133             address = txin['address']
1134             account, sequence = self.get_address_index(address)
1135             txin['KeyID'] = self.get_keyID(account, sequence)
1136             redeemScript = self.accounts[account].redeem_script(sequence)
1137             if redeemScript: 
1138                 txin['redeemScript'] = redeemScript
1139             else:
1140                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1141             private_keys = self.get_private_key(address, password)
1142             for sec in private_keys:
1143                 pubkey = public_key_from_private_key(sec)
1144                 keypairs[ pubkey ] = sec
1145         tx.sign(keypairs)
1146
1147
1148     def sendtx(self, tx):
1149         # synchronous
1150         h = self.send_tx(tx)
1151         self.tx_event.wait()
1152         return self.receive_tx(h)
1153
1154     def send_tx(self, tx):
1155         # asynchronous
1156         self.tx_event.clear()
1157         self.interface.send([('blockchain.transaction.broadcast', [str(tx)])], 'synchronizer')
1158         return tx.hash()
1159
1160     def receive_tx(self,tx_hash):
1161         out = self.tx_result 
1162         if out != tx_hash:
1163             return False, "error: " + out
1164         return True, out
1165
1166
1167
1168     def update_password(self, seed, old_password, new_password):
1169         if new_password == '': new_password = None
1170         # this will throw an exception if unicode cannot be converted
1171         self.seed = pw_encode( seed, new_password)
1172         self.storage.put('seed', self.seed, True)
1173         self.use_encryption = (new_password != None)
1174         self.storage.put('use_encryption', self.use_encryption,True)
1175         for k in self.imported_keys.keys():
1176             a = self.imported_keys[k]
1177             b = pw_decode(a, old_password)
1178             c = pw_encode(b, new_password)
1179             self.imported_keys[k] = c
1180         self.storage.put('imported_keys', self.imported_keys, True)
1181
1182         for k, v in self.master_private_keys.items():
1183             b = pw_decode(v, old_password)
1184             c = pw_encode(b, new_password)
1185             self.master_private_keys[k] = c
1186         self.storage.put('master_private_keys', self.master_private_keys, True)
1187
1188
1189     def freeze(self,addr):
1190         if self.is_mine(addr) and addr not in self.frozen_addresses:
1191             self.unprioritize(addr)
1192             self.frozen_addresses.append(addr)
1193             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1194             return True
1195         else:
1196             return False
1197
1198     def unfreeze(self,addr):
1199         if self.is_mine(addr) and addr in self.frozen_addresses:
1200             self.frozen_addresses.remove(addr)
1201             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1202             return True
1203         else:
1204             return False
1205
1206     def prioritize(self,addr):
1207         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1208             self.unfreeze(addr)
1209             self.prioritized_addresses.append(addr)
1210             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1211             return True
1212         else:
1213             return False
1214
1215     def unprioritize(self,addr):
1216         if self.is_mine(addr) and addr in self.prioritized_addresses:
1217             self.prioritized_addresses.remove(addr)
1218             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1219             return True
1220         else:
1221             return False
1222
1223
1224     def set_verifier(self, verifier):
1225         self.verifier = verifier
1226
1227         # review transactions that are in the history
1228         for addr, hist in self.history.items():
1229             if hist == ['*']: continue
1230             for tx_hash, tx_height in hist:
1231                 if tx_height>0:
1232                     # add it in case it was previously unconfirmed
1233                     self.verifier.add(tx_hash, tx_height)
1234
1235
1236         # if we are on a pruning server, remove unverified transactions
1237         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1238         for tx_hash in self.transactions.keys():
1239             if tx_hash not in vr:
1240                 self.transactions.pop(tx_hash)
1241
1242
1243
1244     def check_new_history(self, addr, hist):
1245         
1246         # check that all tx in hist are relevant
1247         if hist != ['*']:
1248             for tx_hash, height in hist:
1249                 tx = self.transactions.get(tx_hash)
1250                 if not tx: continue
1251                 if not tx.has_address(addr):
1252                     return False
1253
1254         # check that we are not "orphaning" a transaction
1255         old_hist = self.history.get(addr,[])
1256         if old_hist == ['*']: return True
1257
1258         for tx_hash, height in old_hist:
1259             if tx_hash in map(lambda x:x[0], hist): continue
1260             found = False
1261             for _addr, _hist in self.history.items():
1262                 if _addr == addr: continue
1263                 if _hist == ['*']: continue
1264                 _tx_hist = map(lambda x:x[0], _hist)
1265                 if tx_hash in _tx_hist:
1266                     found = True
1267                     break
1268
1269             if not found:
1270                 tx = self.transactions.get(tx_hash)
1271                 # tx might not be there
1272                 if not tx: continue
1273                 
1274                 # already verified?
1275                 if self.verifier.get_height(tx_hash):
1276                     continue
1277                 # unconfirmed tx
1278                 print_error("new history is orphaning transaction:", tx_hash)
1279                 # check that all outputs are not mine, request histories
1280                 ext_requests = []
1281                 for _addr, _v in tx.outputs:
1282                     # assert not self.is_mine(_addr)
1283                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1284
1285                 ext_h = self.interface.synchronous_get(ext_requests)
1286                 print_error("sync:", ext_requests, ext_h)
1287                 height = None
1288                 for h in ext_h:
1289                     if h == ['*']: continue
1290                     for item in h:
1291                         if item.get('tx_hash') == tx_hash:
1292                             height = item.get('height')
1293                 if height:
1294                     print_error("found height for", tx_hash, height)
1295                     self.verifier.add(tx_hash, height)
1296                 else:
1297                     print_error("removing orphaned tx from history", tx_hash)
1298                     self.transactions.pop(tx_hash)
1299
1300         return True
1301
1302
1303
1304     def check_new_tx(self, tx_hash, tx):
1305         # 1 check that tx is referenced in addr_history. 
1306         addresses = []
1307         for addr, hist in self.history.items():
1308             if hist == ['*']:continue
1309             for txh, height in hist:
1310                 if txh == tx_hash: 
1311                     addresses.append(addr)
1312
1313         if not addresses:
1314             return False
1315
1316         # 2 check that referencing addresses are in the tx
1317         for addr in addresses:
1318             if not tx.has_address(addr):
1319                 return False
1320
1321         return True
1322
1323
1324     def start_threads(self, interface, blockchain):
1325         from verifier import TxVerifier
1326         self.interface = interface
1327         self.verifier = TxVerifier(interface, blockchain, self.storage)
1328         self.verifier.start()
1329         self.set_verifier(self.verifier)
1330         self.synchronizer = WalletSynchronizer(self)
1331         self.synchronizer.start()
1332
1333     def stop_threads(self):
1334         self.verifier.stop()
1335         self.synchronizer.stop()
1336
1337
1338
1339
1340
1341 class WalletSynchronizer(threading.Thread):
1342
1343
1344     def __init__(self, wallet):
1345         threading.Thread.__init__(self)
1346         self.daemon = True
1347         self.wallet = wallet
1348         wallet.synchronizer = self
1349         self.interface = self.wallet.interface
1350         self.interface.register_channel('synchronizer')
1351         self.wallet.interface.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1352         self.was_updated = True
1353         self.running = False
1354         self.lock = threading.Lock()
1355
1356     def stop(self):
1357         with self.lock: self.running = False
1358         self.interface.poke('synchronizer')
1359
1360     def is_running(self):
1361         with self.lock: return self.running
1362
1363     
1364     def subscribe_to_addresses(self, addresses):
1365         messages = []
1366         for addr in addresses:
1367             messages.append(('blockchain.address.subscribe', [addr]))
1368         self.interface.send( messages, 'synchronizer')
1369
1370
1371     def run(self):
1372         if not self.interface.is_connected:
1373             print_error( "synchronizer: waiting for interface")
1374             self.interface.connect_event.wait()
1375
1376         with self.lock: self.running = True
1377
1378         requested_tx = []
1379         missing_tx = []
1380         requested_histories = {}
1381
1382         # request any missing transactions
1383         for history in self.wallet.history.values():
1384             if history == ['*']: continue
1385             for tx_hash, tx_height in history:
1386                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1387                     missing_tx.append( (tx_hash, tx_height) )
1388         print_error("missing tx", missing_tx)
1389
1390         # wait until we are connected, in case the user is not connected
1391         while not self.interface.is_connected:
1392             time.sleep(1)
1393         
1394         # subscriptions
1395         self.subscribe_to_addresses(self.wallet.addresses(True))
1396
1397         while self.is_running():
1398             # 1. create new addresses
1399             new_addresses = self.wallet.synchronize()
1400
1401             # request missing addresses
1402             if new_addresses:
1403                 self.subscribe_to_addresses(new_addresses)
1404
1405             # request missing transactions
1406             for tx_hash, tx_height in missing_tx:
1407                 if (tx_hash, tx_height) not in requested_tx:
1408                     self.interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], 'synchronizer')
1409                     requested_tx.append( (tx_hash, tx_height) )
1410             missing_tx = []
1411
1412             # detect if situation has changed
1413             if not self.interface.is_up_to_date('synchronizer'):
1414                 if self.wallet.is_up_to_date():
1415                     self.wallet.set_up_to_date(False)
1416                     self.was_updated = True
1417             else:
1418                 if not self.wallet.is_up_to_date():
1419                     self.wallet.set_up_to_date(True)
1420                     self.was_updated = True
1421
1422             if self.was_updated:
1423                 self.interface.trigger_callback('updated')
1424                 self.was_updated = False
1425
1426             # 2. get a response
1427             r = self.interface.get_response('synchronizer')
1428
1429             # poke sends None. (needed during stop)
1430             if not r: continue
1431
1432             # 3. handle response
1433             method = r['method']
1434             params = r['params']
1435             result = r.get('result')
1436             error = r.get('error')
1437             if error:
1438                 print "error", r
1439                 continue
1440
1441             if method == 'blockchain.address.subscribe':
1442                 addr = params[0]
1443                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1444                     if requested_histories.get(addr) is None:
1445                         self.interface.send([('blockchain.address.get_history', [addr])], 'synchronizer')
1446                         requested_histories[addr] = result
1447
1448             elif method == 'blockchain.address.get_history':
1449                 addr = params[0]
1450                 print_error("receiving history", addr, result)
1451                 if result == ['*']:
1452                     assert requested_histories.pop(addr) == '*'
1453                     self.wallet.receive_history_callback(addr, result)
1454                 else:
1455                     hist = []
1456                     # check that txids are unique
1457                     txids = []
1458                     for item in result:
1459                         tx_hash = item['tx_hash']
1460                         if tx_hash not in txids:
1461                             txids.append(tx_hash)
1462                             hist.append( (tx_hash, item['height']) )
1463
1464                     if len(hist) != len(result):
1465                         raise BaseException("error: server sent history with non-unique txid", result)
1466
1467                     # check that the status corresponds to what was announced
1468                     rs = requested_histories.pop(addr)
1469                     if self.wallet.get_status(hist) != rs:
1470                         raise BaseException("error: status mismatch: %s"%addr)
1471                 
1472                     # store received history
1473                     self.wallet.receive_history_callback(addr, hist)
1474
1475                     # request transactions that we don't have 
1476                     for tx_hash, tx_height in hist:
1477                         if self.wallet.transactions.get(tx_hash) is None:
1478                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1479                                 missing_tx.append( (tx_hash, tx_height) )
1480
1481             elif method == 'blockchain.transaction.get':
1482                 tx_hash = params[0]
1483                 tx_height = params[1]
1484                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1485                 tx = Transaction(result)
1486                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1487                 self.was_updated = True
1488                 requested_tx.remove( (tx_hash, tx_height) )
1489                 print_error("received tx:", tx_hash, len(tx.raw))
1490
1491             elif method == 'blockchain.transaction.broadcast':
1492                 self.wallet.tx_result = result
1493                 self.wallet.tx_event.set()
1494
1495             else:
1496                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1497
1498             if self.was_updated and not requested_tx:
1499                 self.interface.trigger_callback('updated')
1500                 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
1501                 
1502
1503                 self.was_updated = False