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