fix
[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                 import transaction
623                 _, addr = transaction.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
624             txin['address'] = addr
625
626             # add private keys that are in the wallet
627             pk = self.get_private_key(addr, password)
628             for sec in pk:
629                 pubkey = public_key_from_private_key(sec)
630                 keypairs[pubkey] = sec
631                 if not redeem_script:
632                     txin['redeemPubkey'] = pubkey
633
634             print txin
635
636         tx.sign( keypairs )
637
638     def sign_message(self, address, message, password):
639         keys = self.get_private_key(address, password)
640         assert len(keys) == 1
641         sec = keys[0]
642         key = regenerate_key(sec)
643         compressed = is_compressed(sec)
644         return key.sign_message(message, compressed, address)
645
646     def verify_message(self, address, signature, message):
647         try:
648             EC_KEY.verify_message(address, signature, message)
649             return True
650         except BaseException as e:
651             print_error("Verification error: {0}".format(e))
652             return False
653
654
655     def change_gap_limit(self, value):
656         if value >= self.gap_limit:
657             self.gap_limit = value
658             self.storage.put('gap_limit', self.gap_limit, True)
659             #self.interface.poke('synchronizer')
660             return True
661
662         elif value >= self.min_acceptable_gap():
663             for key, account in self.accounts.items():
664                 addresses = account[0]
665                 k = self.num_unused_trailing_addresses(addresses)
666                 n = len(addresses) - k + value
667                 addresses = addresses[0:n]
668                 self.accounts[key][0] = addresses
669
670             self.gap_limit = value
671             self.storage.put('gap_limit', self.gap_limit, True)
672             self.save_accounts()
673             return True
674         else:
675             return False
676
677     def num_unused_trailing_addresses(self, addresses):
678         k = 0
679         for a in addresses[::-1]:
680             if self.history.get(a):break
681             k = k + 1
682         return k
683
684     def min_acceptable_gap(self):
685         # fixme: this assumes wallet is synchronized
686         n = 0
687         nmax = 0
688
689         for account in self.accounts.values():
690             addresses = account.get_addresses(0)
691             k = self.num_unused_trailing_addresses(addresses)
692             for a in addresses[0:-k]:
693                 if self.history.get(a):
694                     n = 0
695                 else:
696                     n += 1
697                     if n > nmax: nmax = n
698         return nmax + 1
699
700
701     def address_is_old(self, address):
702         age = -1
703         h = self.history.get(address, [])
704         if h == ['*']:
705             return True
706         for tx_hash, tx_height in h:
707             if tx_height == 0:
708                 tx_age = 0
709             else: 
710                 tx_age = self.verifier.blockchain.height - tx_height + 1
711             if tx_age > age:
712                 age = tx_age
713         return age > 2
714
715
716     def synchronize_sequence(self, account, for_change):
717         limit = self.gap_limit_for_change if for_change else self.gap_limit
718         new_addresses = []
719         while True:
720             addresses = account.get_addresses(for_change)
721             if len(addresses) < limit:
722                 address = account.create_new_address(for_change)
723                 self.history[address] = []
724                 new_addresses.append( address )
725                 continue
726
727             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
728                 break
729             else:
730                 address = account.create_new_address(for_change)
731                 self.history[address] = []
732                 new_addresses.append( address )
733
734         return new_addresses
735         
736
737
738     def create_pending_accounts(self):
739         for account_type in ['1','2of2','2of3']:
740             a = self.new_account_address(account_type)
741             if self.address_is_old(a):
742                 print_error( "creating account", a )
743                 self.create_account(account_type)
744
745
746     def synchronize_account(self, account):
747         new = []
748         new += self.synchronize_sequence(account, 0)
749         new += self.synchronize_sequence(account, 1)
750         return new
751
752
753     def synchronize(self):
754         if self.master_public_keys:
755             self.create_pending_accounts()
756         new = []
757         for account in self.accounts.values():
758             new += self.synchronize_account(account)
759         if new:
760             self.save_accounts()
761             self.storage.put('addr_history', self.history, True)
762         return new
763
764
765     def is_found(self):
766         return self.history.values() != [[]] * len(self.history) 
767
768
769     def add_contact(self, address, label=None):
770         self.addressbook.append(address)
771         self.storage.put('contacts', self.addressbook, True)
772         if label:  
773             self.set_label(address, label)
774
775
776     def delete_contact(self, addr):
777         if addr in self.addressbook:
778             self.addressbook.remove(addr)
779             self.storage.put('addressbook', self.addressbook, True)
780
781
782     def fill_addressbook(self):
783         for tx_hash, tx in self.transactions.items():
784             is_relevant, is_send, _, _ = self.get_tx_value(tx)
785             if is_send:
786                 for addr, v in tx.outputs:
787                     if not self.is_mine(addr) and addr not in self.addressbook:
788                         self.addressbook.append(addr)
789         # redo labels
790         # self.update_tx_labels()
791
792     def get_num_tx(self, address):
793         n = 0 
794         for tx in self.transactions.values():
795             if address in map(lambda x:x[0], tx.outputs): n += 1
796         return n
797
798
799     def get_address_flags(self, addr):
800         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
801         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
802         return flags
803         
804
805     def get_tx_value(self, tx, account=None):
806         domain = self.get_account_addresses(account)
807         return tx.get_value(domain, self.prevout_values)
808
809     
810     def update_tx_outputs(self, tx_hash):
811         tx = self.transactions.get(tx_hash)
812
813         for i, (addr, value) in enumerate(tx.outputs):
814             key = tx_hash+ ':%d'%i
815             self.prevout_values[key] = value
816
817         for item in tx.inputs:
818             if self.is_mine(item.get('address')):
819                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
820                 self.spent_outputs.append(key)
821
822
823     def get_addr_balance(self, address):
824         assert self.is_mine(address)
825         h = self.history.get(address,[])
826         if h == ['*']: return 0,0
827         c = u = 0
828         received_coins = []   # list of coins received at address
829
830         for tx_hash, tx_height in h:
831             tx = self.transactions.get(tx_hash)
832             if not tx: continue
833
834             for i, (addr, value) in enumerate(tx.outputs):
835                 if addr == address:
836                     key = tx_hash + ':%d'%i
837                     received_coins.append(key)
838
839         for tx_hash, tx_height in h:
840             tx = self.transactions.get(tx_hash)
841             if not tx: continue
842             v = 0
843
844             for item in tx.inputs:
845                 addr = item.get('address')
846                 if addr == address:
847                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
848                     value = self.prevout_values.get( key )
849                     if key in received_coins: 
850                         v -= value
851
852             for i, (addr, value) in enumerate(tx.outputs):
853                 key = tx_hash + ':%d'%i
854                 if addr == address:
855                     v += value
856
857             if tx_height:
858                 c += v
859             else:
860                 u += v
861         return c, u
862
863
864     def get_account_name(self, k):
865         if k == 0:
866             if self.seed_version == 4: 
867                 name = 'Main account'
868             else:
869                 name = 'Old account'
870         else:
871             name = self.labels.get(k, 'Unnamed account')
872         return name
873
874     def get_account_names(self):
875         accounts = {}
876         for k, account in self.accounts.items():
877             accounts[k] = self.get_account_name(k)
878         if self.imported_keys:
879             accounts[-1] = 'Imported keys'
880         return accounts
881
882     def get_account_addresses(self, a, include_change=True):
883         if a is None:
884             o = self.addresses(True)
885         elif a == -1:
886             o = self.imported_keys.keys()
887         else:
888             ac = self.accounts[a]
889             o = ac.get_addresses(0)
890             if include_change: o += ac.get_addresses(1)
891         return o
892
893     def get_imported_balance(self):
894         cc = uu = 0
895         for addr in self.imported_keys.keys():
896             c, u = self.get_addr_balance(addr)
897             cc += c
898             uu += u
899         return cc, uu
900
901     def get_account_balance(self, account):
902         if account is None:
903             return self.get_balance()
904         elif account == -1:
905             return self.get_imported_balance()
906         
907         conf = unconf = 0
908         for addr in self.get_account_addresses(account): 
909             c, u = self.get_addr_balance(addr)
910             conf += c
911             unconf += u
912         return conf, unconf
913
914     def get_frozen_balance(self):
915         conf = unconf = 0
916         for addr in self.frozen_addresses:
917             c, u = self.get_addr_balance(addr)
918             conf += c
919             unconf += u
920         return conf, unconf
921
922         
923     def get_balance(self):
924         cc = uu = 0
925         for a in self.accounts.keys():
926             c, u = self.get_account_balance(a)
927             cc += c
928             uu += u
929         c, u = self.get_imported_balance()
930         cc += c
931         uu += u
932         return cc, uu
933
934
935     def get_unspent_coins(self, domain=None):
936         coins = []
937         if domain is None: domain = self.addresses(True)
938         for addr in domain:
939             h = self.history.get(addr, [])
940             if h == ['*']: continue
941             for tx_hash, tx_height in h:
942                 tx = self.transactions.get(tx_hash)
943                 if tx is None: raise BaseException("Wallet not synchronized")
944                 for output in tx.d.get('outputs'):
945                     if output.get('address') != addr: continue
946                     key = tx_hash + ":%d" % output.get('index')
947                     if key in self.spent_outputs: continue
948                     output['tx_hash'] = tx_hash
949                     coins.append(output)
950         return coins
951
952
953
954     def choose_tx_inputs( self, amount, fixed_fee, account = None ):
955         """ todo: minimize tx size """
956         total = 0
957         fee = self.fee if fixed_fee is None else fixed_fee
958         domain = self.get_account_addresses(account)
959         coins = []
960         prioritized_coins = []
961         for i in self.frozen_addresses:
962             if i in domain: domain.remove(i)
963
964         for i in self.prioritized_addresses:
965             if i in domain: domain.remove(i)
966
967         coins = self.get_unspent_coins(domain)
968         prioritized_coins = self.get_unspent_coins(self.prioritized_addresses)
969
970         inputs = []
971         coins = prioritized_coins + coins
972
973         for item in coins: 
974             addr = item.get('address')
975             v = item.get('value')
976             total += v
977             inputs.append( item )
978             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
979             if total >= amount + fee: break
980         else:
981             inputs = []
982
983         return inputs, total, fee
984
985
986     def set_fee(self, fee):
987         if self.fee != fee:
988             self.fee = fee
989             self.storage.put('fee_per_kb', self.fee, True)
990         
991     def estimated_fee(self, inputs):
992         estimated_size =  len(inputs) * 180 + 80     # this assumes non-compressed keys
993         fee = self.fee * int(round(estimated_size/1024.))
994         if fee == 0: fee = self.fee
995         return fee
996
997
998     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None, account=0 ):
999         "add change to a transaction"
1000         change_amount = total - ( amount + fee )
1001         if change_amount != 0:
1002             if not change_addr:
1003                 if account is None: 
1004                     # send change to one of the accounts involved in the tx
1005                     address = inputs[0].get('address')
1006                     account, _ = self.get_address_index(address)
1007
1008                 if not self.use_change or account == -1:
1009                     change_addr = inputs[-1]['address']
1010                 else:
1011                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
1012
1013             # Insert the change output at a random position in the outputs
1014             posn = random.randint(0, len(outputs))
1015             outputs[posn:posn] = [( change_addr,  change_amount)]
1016         return outputs
1017
1018
1019     def get_history(self, address):
1020         with self.lock:
1021             return self.history.get(address)
1022
1023
1024     def get_status(self, h):
1025         if not h: return None
1026         if h == ['*']: return '*'
1027         status = ''
1028         for tx_hash, height in h:
1029             status += tx_hash + ':%d:' % height
1030         return hashlib.sha256( status ).digest().encode('hex')
1031
1032
1033     def receive_tx_callback(self, tx_hash, tx, tx_height):
1034
1035         with self.transaction_lock:
1036             self.add_extra_addresses(tx)
1037             if not self.check_new_tx(tx_hash, tx):
1038                 # may happen due to pruning
1039                 print_error("received transaction that is no longer referenced in history", tx_hash)
1040                 return
1041             self.transactions[tx_hash] = tx
1042             self.network.interface.pending_transactions_for_notifications.append(tx)
1043             self.save_transactions()
1044             if self.verifier and tx_height>0: 
1045                 self.verifier.add(tx_hash, tx_height)
1046             self.update_tx_outputs(tx_hash)
1047
1048
1049     def save_transactions(self):
1050         tx = {}
1051         for k,v in self.transactions.items():
1052             tx[k] = str(v)
1053         self.storage.put('transactions', tx, True)
1054
1055     def receive_history_callback(self, addr, hist):
1056
1057         if not self.check_new_history(addr, hist):
1058             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
1059             
1060         with self.lock:
1061             self.history[addr] = hist
1062             self.storage.put('addr_history', self.history, True)
1063
1064         if hist != ['*']:
1065             for tx_hash, tx_height in hist:
1066                 if tx_height>0:
1067                     # add it in case it was previously unconfirmed
1068                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1069
1070
1071     def get_tx_history(self, account=None):
1072         with self.transaction_lock:
1073             history = self.transactions.items()
1074             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1075             result = []
1076     
1077             balance = 0
1078             for tx_hash, tx in history:
1079                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1080                 if v is not None: balance += v
1081
1082             c, u = self.get_account_balance(account)
1083
1084             if balance != c+u:
1085                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1086
1087             balance = c + u - balance
1088             for tx_hash, tx in history:
1089                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1090                 if not is_relevant:
1091                     continue
1092                 if value is not None:
1093                     balance += value
1094
1095                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1096                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1097
1098         return result
1099
1100
1101     def get_label(self, tx_hash):
1102         label = self.labels.get(tx_hash)
1103         is_default = (label == '') or (label is None)
1104         if is_default: label = self.get_default_label(tx_hash)
1105         return label, is_default
1106
1107
1108     def get_default_label(self, tx_hash):
1109         tx = self.transactions.get(tx_hash)
1110         default_label = ''
1111         if tx:
1112             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1113             if is_mine:
1114                 for o in tx.outputs:
1115                     o_addr, _ = o
1116                     if not self.is_mine(o_addr):
1117                         try:
1118                             default_label = self.labels[o_addr]
1119                         except KeyError:
1120                             default_label = o_addr
1121                         break
1122                 else:
1123                     default_label = '(internal)'
1124             else:
1125                 for o in tx.outputs:
1126                     o_addr, _ = o
1127                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1128                         break
1129                 else:
1130                     for o in tx.outputs:
1131                         o_addr, _ = o
1132                         if self.is_mine(o_addr):
1133                             break
1134                     else:
1135                         o_addr = None
1136
1137                 if o_addr:
1138                     dest_label = self.labels.get(o_addr)
1139                     try:
1140                         default_label = self.labels[o_addr]
1141                     except KeyError:
1142                         default_label = o_addr
1143
1144         return default_label
1145
1146
1147     def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, account=None ):
1148         for address, x in outputs:
1149             assert is_valid(address)
1150         amount = sum( map(lambda x:x[1], outputs) )
1151         inputs, total, fee = self.choose_tx_inputs( amount, fee, account )
1152         if not inputs:
1153             raise ValueError("Not enough funds")
1154         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
1155         return Transaction.from_io(inputs, outputs)
1156
1157
1158     def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
1159         tx = self.make_unsigned_transaction(outputs, fee, change_addr, account)
1160         self.sign_transaction(tx, password)
1161         return tx
1162
1163
1164     def sign_transaction(self, tx, password):
1165         keypairs = {}
1166         for i, txin in enumerate(tx.inputs):
1167             address = txin['address']
1168             account, sequence = self.get_address_index(address)
1169             txin['KeyID'] = self.get_keyID(account, sequence)
1170             redeemScript = self.accounts[account].redeem_script(sequence)
1171             if redeemScript: 
1172                 txin['redeemScript'] = redeemScript
1173             else:
1174                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1175             private_keys = self.get_private_key(address, password)
1176             for sec in private_keys:
1177                 pubkey = public_key_from_private_key(sec)
1178                 keypairs[ pubkey ] = sec
1179         tx.sign(keypairs)
1180
1181
1182     def sendtx(self, tx):
1183         # synchronous
1184         h = self.send_tx(tx)
1185         self.tx_event.wait()
1186         return self.receive_tx(h)
1187
1188     def send_tx(self, tx):
1189         # asynchronous
1190         self.tx_event.clear()
1191         self.network.interface.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
1192         return tx.hash()
1193
1194     def on_broadcast(self, i, r):
1195         self.tx_result = r.get('result')
1196         self.tx_event.set()
1197
1198     def receive_tx(self,tx_hash):
1199         out = self.tx_result 
1200         if out != tx_hash:
1201             return False, "error: " + out
1202         return True, out
1203
1204
1205
1206     def update_password(self, seed, old_password, new_password):
1207         if new_password == '': new_password = None
1208         # this will throw an exception if unicode cannot be converted
1209         self.seed = pw_encode( seed, new_password)
1210         self.storage.put('seed', self.seed, True)
1211         self.use_encryption = (new_password != None)
1212         self.storage.put('use_encryption', self.use_encryption,True)
1213         for k in self.imported_keys.keys():
1214             a = self.imported_keys[k]
1215             b = pw_decode(a, old_password)
1216             c = pw_encode(b, new_password)
1217             self.imported_keys[k] = c
1218         self.storage.put('imported_keys', self.imported_keys, True)
1219
1220         for k, v in self.master_private_keys.items():
1221             b = pw_decode(v, old_password)
1222             c = pw_encode(b, new_password)
1223             self.master_private_keys[k] = c
1224         self.storage.put('master_private_keys', self.master_private_keys, True)
1225
1226
1227     def freeze(self,addr):
1228         if self.is_mine(addr) and addr not in self.frozen_addresses:
1229             self.unprioritize(addr)
1230             self.frozen_addresses.append(addr)
1231             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1232             return True
1233         else:
1234             return False
1235
1236     def unfreeze(self,addr):
1237         if self.is_mine(addr) and addr in self.frozen_addresses:
1238             self.frozen_addresses.remove(addr)
1239             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1240             return True
1241         else:
1242             return False
1243
1244     def prioritize(self,addr):
1245         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1246             self.unfreeze(addr)
1247             self.prioritized_addresses.append(addr)
1248             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1249             return True
1250         else:
1251             return False
1252
1253     def unprioritize(self,addr):
1254         if self.is_mine(addr) and addr in self.prioritized_addresses:
1255             self.prioritized_addresses.remove(addr)
1256             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1257             return True
1258         else:
1259             return False
1260
1261
1262     def set_verifier(self, verifier):
1263         self.verifier = verifier
1264
1265         # review transactions that are in the history
1266         for addr, hist in self.history.items():
1267             if hist == ['*']: continue
1268             for tx_hash, tx_height in hist:
1269                 if tx_height>0:
1270                     # add it in case it was previously unconfirmed
1271                     self.verifier.add(tx_hash, tx_height)
1272
1273
1274         # if we are on a pruning server, remove unverified transactions
1275         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1276         for tx_hash in self.transactions.keys():
1277             if tx_hash not in vr:
1278                 self.transactions.pop(tx_hash)
1279
1280
1281
1282     def check_new_history(self, addr, hist):
1283         
1284         # check that all tx in hist are relevant
1285         if hist != ['*']:
1286             for tx_hash, height in hist:
1287                 tx = self.transactions.get(tx_hash)
1288                 if not tx: continue
1289                 if not tx.has_address(addr):
1290                     return False
1291
1292         # check that we are not "orphaning" a transaction
1293         old_hist = self.history.get(addr,[])
1294         if old_hist == ['*']: return True
1295
1296         for tx_hash, height in old_hist:
1297             if tx_hash in map(lambda x:x[0], hist): continue
1298             found = False
1299             for _addr, _hist in self.history.items():
1300                 if _addr == addr: continue
1301                 if _hist == ['*']: continue
1302                 _tx_hist = map(lambda x:x[0], _hist)
1303                 if tx_hash in _tx_hist:
1304                     found = True
1305                     break
1306
1307             if not found:
1308                 tx = self.transactions.get(tx_hash)
1309                 # tx might not be there
1310                 if not tx: continue
1311                 
1312                 # already verified?
1313                 if self.verifier.get_height(tx_hash):
1314                     continue
1315                 # unconfirmed tx
1316                 print_error("new history is orphaning transaction:", tx_hash)
1317                 # check that all outputs are not mine, request histories
1318                 ext_requests = []
1319                 for _addr, _v in tx.outputs:
1320                     # assert not self.is_mine(_addr)
1321                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1322
1323                 ext_h = self.network.interface.synchronous_get(ext_requests)
1324                 print_error("sync:", ext_requests, ext_h)
1325                 height = None
1326                 for h in ext_h:
1327                     if h == ['*']: continue
1328                     for item in h:
1329                         if item.get('tx_hash') == tx_hash:
1330                             height = item.get('height')
1331                 if height:
1332                     print_error("found height for", tx_hash, height)
1333                     self.verifier.add(tx_hash, height)
1334                 else:
1335                     print_error("removing orphaned tx from history", tx_hash)
1336                     self.transactions.pop(tx_hash)
1337
1338         return True
1339
1340
1341
1342     def check_new_tx(self, tx_hash, tx):
1343         # 1 check that tx is referenced in addr_history. 
1344         addresses = []
1345         for addr, hist in self.history.items():
1346             if hist == ['*']:continue
1347             for txh, height in hist:
1348                 if txh == tx_hash: 
1349                     addresses.append(addr)
1350
1351         if not addresses:
1352             return False
1353
1354         # 2 check that referencing addresses are in the tx
1355         for addr in addresses:
1356             if not tx.has_address(addr):
1357                 return False
1358
1359         return True
1360
1361
1362     def start_threads(self, network):
1363         from verifier import TxVerifier
1364         self.network = network
1365         self.verifier = TxVerifier(self.network, self.storage)
1366         self.verifier.start()
1367         self.set_verifier(self.verifier)
1368         self.synchronizer = WalletSynchronizer(self)
1369         self.synchronizer.start()
1370
1371     def stop_threads(self):
1372         self.verifier.stop()
1373         self.synchronizer.stop()
1374
1375
1376
1377
1378
1379 class WalletSynchronizer(threading.Thread):
1380
1381
1382     def __init__(self, wallet):
1383         threading.Thread.__init__(self)
1384         self.daemon = True
1385         self.wallet = wallet
1386         wallet.synchronizer = self
1387         self.network = self.wallet.network
1388         #self.wallet.network.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1389         self.was_updated = True
1390         self.running = False
1391         self.lock = threading.Lock()
1392         self.queue = Queue.Queue()
1393
1394     def stop(self):
1395         with self.lock: self.running = False
1396
1397     def is_running(self):
1398         with self.lock: return self.running
1399
1400     
1401     def subscribe_to_addresses(self, addresses):
1402         messages = []
1403         for addr in addresses:
1404             messages.append(('blockchain.address.subscribe', [addr]))
1405         self.network.interface.send( messages, lambda i,r: self.queue.put(r))
1406
1407
1408     def run(self):
1409         with self.lock:
1410             self.running = True
1411
1412         while self.is_running():
1413             interface = self.network.interface
1414             if not interface.is_connected:
1415                 print_error("synchronizer: waiting for interface")
1416                 interface.connect_event.wait()
1417                 
1418             self.run_interface(interface)
1419
1420
1421     def run_interface(self, interface):
1422
1423         print_error("synchronizer: connected to", interface.server)
1424
1425         requested_tx = []
1426         missing_tx = []
1427         requested_histories = {}
1428
1429         # request any missing transactions
1430         for history in self.wallet.history.values():
1431             if history == ['*']: continue
1432             for tx_hash, tx_height in history:
1433                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1434                     missing_tx.append( (tx_hash, tx_height) )
1435
1436         if missing_tx:
1437             print_error("missing tx", missing_tx)
1438
1439         # subscriptions
1440         self.subscribe_to_addresses(self.wallet.addresses(True, next=True))
1441
1442         while self.is_running():
1443             # 1. create new addresses
1444             new_addresses = self.wallet.synchronize()
1445
1446             # request missing addresses
1447             if new_addresses:
1448                 self.subscribe_to_addresses(new_addresses)
1449
1450             # request missing transactions
1451             for tx_hash, tx_height in missing_tx:
1452                 if (tx_hash, tx_height) not in requested_tx:
1453                     interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
1454                     requested_tx.append( (tx_hash, tx_height) )
1455             missing_tx = []
1456
1457             # detect if situation has changed
1458             if interface.is_up_to_date() and self.queue.empty():
1459                 if not self.wallet.is_up_to_date():
1460                     self.wallet.set_up_to_date(True)
1461                     self.was_updated = True
1462             else:
1463                 if self.wallet.is_up_to_date():
1464                     self.wallet.set_up_to_date(False)
1465                     self.was_updated = True
1466
1467             if self.was_updated:
1468                 self.wallet.network.trigger_callback('updated')
1469                 self.was_updated = False
1470
1471             # 2. get a response
1472             try:
1473                 r = self.queue.get(block=True, timeout=1)
1474             except Queue.Empty:
1475                 continue
1476
1477             if interface != self.network.interface:
1478                 break
1479             
1480             if not r:
1481                 continue
1482
1483             # 3. handle response
1484             method = r['method']
1485             params = r['params']
1486             result = r.get('result')
1487             error = r.get('error')
1488             if error:
1489                 print "error", r
1490                 continue
1491
1492             if method == 'blockchain.address.subscribe':
1493                 addr = params[0]
1494                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1495                     if requested_histories.get(addr) is None:
1496                         interface.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
1497                         requested_histories[addr] = result
1498
1499             elif method == 'blockchain.address.get_history':
1500                 addr = params[0]
1501                 print_error("receiving history", addr, result)
1502                 if result == ['*']:
1503                     assert requested_histories.pop(addr) == '*'
1504                     self.wallet.receive_history_callback(addr, result)
1505                 else:
1506                     hist = []
1507                     # check that txids are unique
1508                     txids = []
1509                     for item in result:
1510                         tx_hash = item['tx_hash']
1511                         if tx_hash not in txids:
1512                             txids.append(tx_hash)
1513                             hist.append( (tx_hash, item['height']) )
1514
1515                     if len(hist) != len(result):
1516                         raise BaseException("error: server sent history with non-unique txid", result)
1517
1518                     # check that the status corresponds to what was announced
1519                     rs = requested_histories.pop(addr)
1520                     if self.wallet.get_status(hist) != rs:
1521                         raise BaseException("error: status mismatch: %s"%addr)
1522                 
1523                     # store received history
1524                     self.wallet.receive_history_callback(addr, hist)
1525
1526                     # request transactions that we don't have 
1527                     for tx_hash, tx_height in hist:
1528                         if self.wallet.transactions.get(tx_hash) is None:
1529                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1530                                 missing_tx.append( (tx_hash, tx_height) )
1531
1532             elif method == 'blockchain.transaction.get':
1533                 tx_hash = params[0]
1534                 tx_height = params[1]
1535                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1536                 tx = Transaction(result)
1537                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1538                 self.was_updated = True
1539                 requested_tx.remove( (tx_hash, tx_height) )
1540                 print_error("received tx:", tx_hash, len(tx.raw))
1541
1542             else:
1543                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1544
1545             if self.was_updated and not requested_tx:
1546                 self.wallet.network.trigger_callback('updated')
1547                 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
1548
1549                 self.was_updated = False