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