perform check_new_tx inside add_transaction
[electrum-nvc.git] / lib / wallet.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 import sys
20 import base64
21 import os
22 import re
23 import hashlib
24 import copy
25 import operator
26 import ast
27 import threading
28 import random
29 import aes
30 import Queue
31 import time
32
33 from util import print_msg, print_error, format_satoshis
34 from bitcoin import *
35 from account import *
36 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             self.update_tx_outputs(tx_hash)
197
198
199     def add_transaction(self, tx):
200         h = tx.hash()
201         # find the address corresponding to pay-to-pubkey inputs
202         tx.add_extra_addresses(self.transactions)
203         for o in tx.d.get('outputs'):
204             if o.get('is_pubkey'):
205                 for tx2 in self.transactions.values():
206                     tx2.add_extra_addresses({h:tx})
207
208         if self.check_new_tx(h, tx):
209             self.transactions[h] = tx
210             return True
211         else:
212             print_error("unreferenced tx", tx_hash)
213             return False
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             if not self.add_transaction(tx):
1025                 # may happen due to pruning
1026                 print_error("received transaction that is no longer referenced in history", tx_hash)
1027                 return
1028
1029             self.interface.pending_transactions_for_notifications.append(tx)
1030             self.save_transactions()
1031             if self.verifier and tx_height>0: 
1032                 self.verifier.add(tx_hash, tx_height)
1033             self.update_tx_outputs(tx_hash)
1034
1035
1036     def save_transactions(self):
1037         tx = {}
1038         for k,v in self.transactions.items():
1039             tx[k] = str(v)
1040         self.storage.put('transactions', tx, True)
1041
1042     def receive_history_callback(self, addr, hist):
1043
1044         if not self.check_new_history(addr, hist):
1045             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
1046             
1047         with self.lock:
1048             self.history[addr] = hist
1049             self.storage.put('addr_history', self.history, True)
1050
1051         if hist != ['*']:
1052             for tx_hash, tx_height in hist:
1053                 if tx_height>0:
1054                     # add it in case it was previously unconfirmed
1055                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1056
1057
1058     def get_tx_history(self, account=None):
1059         with self.transaction_lock:
1060             history = self.transactions.items()
1061             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1062             result = []
1063     
1064             balance = 0
1065             for tx_hash, tx in history:
1066                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1067                 if v is not None: balance += v
1068
1069             c, u = self.get_account_balance(account)
1070
1071             if balance != c+u:
1072                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1073
1074             balance = c + u - balance
1075             for tx_hash, tx in history:
1076                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1077                 if not is_relevant:
1078                     continue
1079                 if value is not None:
1080                     balance += value
1081
1082                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1083                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1084
1085         return result
1086
1087
1088     def get_label(self, tx_hash):
1089         label = self.labels.get(tx_hash)
1090         is_default = (label == '') or (label is None)
1091         if is_default: label = self.get_default_label(tx_hash)
1092         return label, is_default
1093
1094
1095     def get_default_label(self, tx_hash):
1096         tx = self.transactions.get(tx_hash)
1097         default_label = ''
1098         if tx:
1099             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1100             if is_mine:
1101                 for o in tx.outputs:
1102                     o_addr, _ = o
1103                     if not self.is_mine(o_addr):
1104                         try:
1105                             default_label = self.labels[o_addr]
1106                         except KeyError:
1107                             default_label = o_addr
1108                         break
1109                 else:
1110                     default_label = '(internal)'
1111             else:
1112                 for o in tx.outputs:
1113                     o_addr, _ = o
1114                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1115                         break
1116                 else:
1117                     for o in tx.outputs:
1118                         o_addr, _ = o
1119                         if self.is_mine(o_addr):
1120                             break
1121                     else:
1122                         o_addr = None
1123
1124                 if o_addr:
1125                     dest_label = self.labels.get(o_addr)
1126                     try:
1127                         default_label = self.labels[o_addr]
1128                     except KeyError:
1129                         default_label = o_addr
1130
1131         return default_label
1132
1133
1134     def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, account=None ):
1135         for address, x in outputs:
1136             assert is_valid(address)
1137         amount = sum( map(lambda x:x[1], outputs) )
1138         inputs, total, fee = self.choose_tx_inputs( amount, fee, account )
1139         if not inputs:
1140             raise ValueError("Not enough funds")
1141         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
1142         return Transaction.from_io(inputs, outputs)
1143
1144
1145     def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
1146         tx = self.make_unsigned_transaction(outputs, fee, change_addr, account)
1147         self.sign_transaction(tx, password)
1148         return tx
1149
1150
1151     def sign_transaction(self, tx, password):
1152         keypairs = {}
1153         for i, txin in enumerate(tx.inputs):
1154             address = txin['address']
1155             account, sequence = self.get_address_index(address)
1156             txin['KeyID'] = self.get_keyID(account, sequence)
1157             redeemScript = self.accounts[account].redeem_script(sequence)
1158             if redeemScript: 
1159                 txin['redeemScript'] = redeemScript
1160             else:
1161                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1162             private_keys = self.get_private_key(address, password)
1163             for sec in private_keys:
1164                 pubkey = public_key_from_private_key(sec)
1165                 keypairs[ pubkey ] = sec
1166         tx.sign(keypairs)
1167
1168
1169     def sendtx(self, tx):
1170         # synchronous
1171         h = self.send_tx(tx)
1172         self.tx_event.wait()
1173         return self.receive_tx(h)
1174
1175     def send_tx(self, tx):
1176         # asynchronous
1177         self.tx_event.clear()
1178         self.interface.send([('blockchain.transaction.broadcast', [str(tx)])], 'synchronizer')
1179         return tx.hash()
1180
1181     def receive_tx(self,tx_hash):
1182         out = self.tx_result 
1183         if out != tx_hash:
1184             return False, "error: " + out
1185         return True, out
1186
1187
1188
1189     def update_password(self, seed, old_password, new_password):
1190         if new_password == '': new_password = None
1191         # this will throw an exception if unicode cannot be converted
1192         self.seed = pw_encode( seed, new_password)
1193         self.storage.put('seed', self.seed, True)
1194         self.use_encryption = (new_password != None)
1195         self.storage.put('use_encryption', self.use_encryption,True)
1196         for k in self.imported_keys.keys():
1197             a = self.imported_keys[k]
1198             b = pw_decode(a, old_password)
1199             c = pw_encode(b, new_password)
1200             self.imported_keys[k] = c
1201         self.storage.put('imported_keys', self.imported_keys, True)
1202
1203         for k, v in self.master_private_keys.items():
1204             b = pw_decode(v, old_password)
1205             c = pw_encode(b, new_password)
1206             self.master_private_keys[k] = c
1207         self.storage.put('master_private_keys', self.master_private_keys, True)
1208
1209
1210     def freeze(self,addr):
1211         if self.is_mine(addr) and addr not in self.frozen_addresses:
1212             self.unprioritize(addr)
1213             self.frozen_addresses.append(addr)
1214             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1215             return True
1216         else:
1217             return False
1218
1219     def unfreeze(self,addr):
1220         if self.is_mine(addr) and addr in self.frozen_addresses:
1221             self.frozen_addresses.remove(addr)
1222             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1223             return True
1224         else:
1225             return False
1226
1227     def prioritize(self,addr):
1228         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1229             self.unfreeze(addr)
1230             self.prioritized_addresses.append(addr)
1231             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1232             return True
1233         else:
1234             return False
1235
1236     def unprioritize(self,addr):
1237         if self.is_mine(addr) and addr in self.prioritized_addresses:
1238             self.prioritized_addresses.remove(addr)
1239             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1240             return True
1241         else:
1242             return False
1243
1244
1245     def set_verifier(self, verifier):
1246         self.verifier = verifier
1247
1248         # review transactions that are in the history
1249         for addr, hist in self.history.items():
1250             if hist == ['*']: continue
1251             for tx_hash, tx_height in hist:
1252                 if tx_height>0:
1253                     # add it in case it was previously unconfirmed
1254                     self.verifier.add(tx_hash, tx_height)
1255
1256
1257         # if we are on a pruning server, remove unverified transactions
1258         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1259         for tx_hash in self.transactions.keys():
1260             if tx_hash not in vr:
1261                 self.transactions.pop(tx_hash)
1262
1263
1264
1265     def check_new_history(self, addr, hist):
1266         
1267         # check that all tx in hist are relevant
1268         if hist != ['*']:
1269             for tx_hash, height in hist:
1270                 tx = self.transactions.get(tx_hash)
1271                 if not tx: continue
1272                 if not tx.has_address(addr):
1273                     return False
1274
1275         # check that we are not "orphaning" a transaction
1276         old_hist = self.history.get(addr,[])
1277         if old_hist == ['*']: return True
1278
1279         for tx_hash, height in old_hist:
1280             if tx_hash in map(lambda x:x[0], hist): continue
1281             found = False
1282             for _addr, _hist in self.history.items():
1283                 if _addr == addr: continue
1284                 if _hist == ['*']: continue
1285                 _tx_hist = map(lambda x:x[0], _hist)
1286                 if tx_hash in _tx_hist:
1287                     found = True
1288                     break
1289
1290             if not found:
1291                 tx = self.transactions.get(tx_hash)
1292                 # tx might not be there
1293                 if not tx: continue
1294                 
1295                 # already verified?
1296                 if self.verifier.get_height(tx_hash):
1297                     continue
1298                 # unconfirmed tx
1299                 print_error("new history is orphaning transaction:", tx_hash)
1300                 # check that all outputs are not mine, request histories
1301                 ext_requests = []
1302                 for _addr, _v in tx.outputs:
1303                     # assert not self.is_mine(_addr)
1304                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1305
1306                 ext_h = self.interface.synchronous_get(ext_requests)
1307                 print_error("sync:", ext_requests, ext_h)
1308                 height = None
1309                 for h in ext_h:
1310                     if h == ['*']: continue
1311                     for item in h:
1312                         if item.get('tx_hash') == tx_hash:
1313                             height = item.get('height')
1314                 if height:
1315                     print_error("found height for", tx_hash, height)
1316                     self.verifier.add(tx_hash, height)
1317                 else:
1318                     print_error("removing orphaned tx from history", tx_hash)
1319                     self.transactions.pop(tx_hash)
1320
1321         return True
1322
1323
1324
1325     def check_new_tx(self, tx_hash, tx):
1326         # 1 check that tx is referenced in addr_history. 
1327         addresses = []
1328         for addr, hist in self.history.items():
1329             if hist == ['*']:continue
1330             for txh, height in hist:
1331                 if txh == tx_hash: 
1332                     addresses.append(addr)
1333
1334         if not addresses:
1335             return False
1336
1337         # 2 check that referencing addresses are in the tx
1338         for addr in addresses:
1339             if not tx.has_address(addr):
1340                 print "z", addr, tx.inputs
1341                 return False
1342
1343         return True
1344
1345
1346     def start_threads(self, interface, blockchain):
1347         from verifier import TxVerifier
1348         self.interface = interface
1349         self.verifier = TxVerifier(interface, blockchain, self.storage)
1350         self.verifier.start()
1351         self.set_verifier(self.verifier)
1352         self.synchronizer = WalletSynchronizer(self)
1353         self.synchronizer.start()
1354
1355     def stop_threads(self):
1356         self.verifier.stop()
1357         self.synchronizer.stop()
1358
1359
1360
1361
1362
1363 class WalletSynchronizer(threading.Thread):
1364
1365
1366     def __init__(self, wallet):
1367         threading.Thread.__init__(self)
1368         self.daemon = True
1369         self.wallet = wallet
1370         wallet.synchronizer = self
1371         self.interface = self.wallet.interface
1372         self.interface.register_channel('synchronizer')
1373         self.wallet.interface.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1374         self.was_updated = True
1375         self.running = False
1376         self.lock = threading.Lock()
1377
1378     def stop(self):
1379         with self.lock: self.running = False
1380         self.interface.poke('synchronizer')
1381
1382     def is_running(self):
1383         with self.lock: return self.running
1384
1385     
1386     def subscribe_to_addresses(self, addresses):
1387         messages = []
1388         for addr in addresses:
1389             messages.append(('blockchain.address.subscribe', [addr]))
1390         self.interface.send( messages, 'synchronizer')
1391
1392
1393     def run(self):
1394         if not self.interface.is_connected:
1395             print_error( "synchronizer: waiting for interface")
1396             self.interface.connect_event.wait()
1397
1398         with self.lock: self.running = True
1399
1400         requested_tx = []
1401         missing_tx = []
1402         requested_histories = {}
1403
1404         # request any missing transactions
1405         for history in self.wallet.history.values():
1406             if history == ['*']: continue
1407             for tx_hash, tx_height in history:
1408                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1409                     missing_tx.append( (tx_hash, tx_height) )
1410         print_error("missing tx", missing_tx)
1411
1412         # wait until we are connected, in case the user is not connected
1413         while not self.interface.is_connected:
1414             time.sleep(1)
1415         
1416         # subscriptions
1417         self.subscribe_to_addresses(self.wallet.addresses(True))
1418
1419         while self.is_running():
1420             # 1. create new addresses
1421             new_addresses = self.wallet.synchronize()
1422
1423             # request missing addresses
1424             if new_addresses:
1425                 self.subscribe_to_addresses(new_addresses)
1426
1427             # request missing transactions
1428             for tx_hash, tx_height in missing_tx:
1429                 if (tx_hash, tx_height) not in requested_tx:
1430                     self.interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], 'synchronizer')
1431                     requested_tx.append( (tx_hash, tx_height) )
1432             missing_tx = []
1433
1434             # detect if situation has changed
1435             if not self.interface.is_up_to_date('synchronizer'):
1436                 if self.wallet.is_up_to_date():
1437                     self.wallet.set_up_to_date(False)
1438                     self.was_updated = True
1439             else:
1440                 if not self.wallet.is_up_to_date():
1441                     self.wallet.set_up_to_date(True)
1442                     self.was_updated = True
1443
1444             if self.was_updated:
1445                 self.interface.trigger_callback('updated')
1446                 self.was_updated = False
1447
1448             # 2. get a response
1449             r = self.interface.get_response('synchronizer')
1450
1451             # poke sends None. (needed during stop)
1452             if not r: continue
1453
1454             # 3. handle response
1455             method = r['method']
1456             params = r['params']
1457             result = r.get('result')
1458             error = r.get('error')
1459             if error:
1460                 print "error", r
1461                 continue
1462
1463             if method == 'blockchain.address.subscribe':
1464                 addr = params[0]
1465                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1466                     if requested_histories.get(addr) is None:
1467                         self.interface.send([('blockchain.address.get_history', [addr])], 'synchronizer')
1468                         requested_histories[addr] = result
1469
1470             elif method == 'blockchain.address.get_history':
1471                 addr = params[0]
1472                 print_error("receiving history", addr, result)
1473                 if result == ['*']:
1474                     assert requested_histories.pop(addr) == '*'
1475                     self.wallet.receive_history_callback(addr, result)
1476                 else:
1477                     hist = []
1478                     # check that txids are unique
1479                     txids = []
1480                     for item in result:
1481                         tx_hash = item['tx_hash']
1482                         if tx_hash not in txids:
1483                             txids.append(tx_hash)
1484                             hist.append( (tx_hash, item['height']) )
1485
1486                     if len(hist) != len(result):
1487                         raise BaseException("error: server sent history with non-unique txid", result)
1488
1489                     # check that the status corresponds to what was announced
1490                     rs = requested_histories.pop(addr)
1491                     if self.wallet.get_status(hist) != rs:
1492                         raise BaseException("error: status mismatch: %s"%addr)
1493                 
1494                     # store received history
1495                     self.wallet.receive_history_callback(addr, hist)
1496
1497                     # request transactions that we don't have 
1498                     for tx_hash, tx_height in hist:
1499                         if self.wallet.transactions.get(tx_hash) is None:
1500                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1501                                 missing_tx.append( (tx_hash, tx_height) )
1502
1503             elif method == 'blockchain.transaction.get':
1504                 tx_hash = params[0]
1505                 tx_height = params[1]
1506                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1507                 tx = Transaction(result)
1508                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1509                 self.was_updated = True
1510                 requested_tx.remove( (tx_hash, tx_height) )
1511                 print_error("received tx:", tx_hash, len(tx.raw))
1512
1513             elif method == 'blockchain.transaction.broadcast':
1514                 self.wallet.tx_result = result
1515                 self.wallet.tx_event.set()
1516
1517             else:
1518                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1519
1520             if self.was_updated and not requested_tx:
1521                 self.interface.trigger_callback('updated')
1522                 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
1523                 
1524
1525                 self.was_updated = False