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