restore get_master_public_key function. fixes #668
[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 import math
33
34 from util import print_msg, print_error, format_satoshis
35 from bitcoin import *
36 from account import *
37 from transaction import Transaction
38 from plugins import run_hook
39
40 COINBASE_MATURITY = 100
41 DUST_THRESHOLD = 5430
42
43 # AES encryption
44 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
45 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
46
47 def pw_encode(s, password):
48     if password:
49         secret = Hash(password)
50         return EncodeAES(secret, s)
51     else:
52         return s
53
54 def pw_decode(s, password):
55     if password is not None:
56         secret = Hash(password)
57         try:
58             d = DecodeAES(secret, s)
59         except Exception:
60             raise Exception('Invalid password')
61         return d
62     else:
63         return s
64
65
66
67
68
69 from version import *
70
71
72 class WalletStorage:
73
74     def __init__(self, config):
75         self.lock = threading.Lock()
76         self.config = config
77         self.data = {}
78         self.file_exists = False
79         self.path = self.init_path(config)
80         print_error( "wallet path", self.path )
81         if self.path:
82             self.read(self.path)
83
84
85     def init_path(self, config):
86         """Set the path of the wallet."""
87
88         # command line -w option
89         path = config.get('wallet_path')
90         if path:
91             return path
92
93         # path in config file
94         path = config.get('default_wallet_path')
95         if path:
96             return path
97
98         # default path
99         dirpath = os.path.join(config.path, "wallets")
100         if not os.path.exists(dirpath):
101             os.mkdir(dirpath)
102
103         new_path = os.path.join(config.path, "wallets", "default_wallet")
104
105         # default path in pre 1.9 versions
106         old_path = os.path.join(config.path, "electrum.dat")
107         if os.path.exists(old_path) and not os.path.exists(new_path):
108             os.rename(old_path, new_path)
109
110         return new_path
111
112
113     def read(self, path):
114         """Read the contents of the wallet file."""
115         try:
116             with open(self.path, "r") as f:
117                 data = f.read()
118         except IOError:
119             return
120         try:
121             d = ast.literal_eval( data )  #parse raw data from reading wallet file
122         except Exception:
123             raise IOError("Cannot read wallet file.")
124
125         self.data = d
126         self.file_exists = True
127
128
129     def get(self, key, default=None):
130         v = self.data.get(key)
131         if v is None: 
132             v = default
133         return v
134
135     def put(self, key, value, save = True):
136
137         with self.lock:
138             if value is not None:
139                 self.data[key] = value
140             else:
141                 self.data.pop(key)
142             if save: 
143                 self.write()
144
145     def write(self):
146         s = repr(self.data)
147         f = open(self.path,"w")
148         f.write( s )
149         f.close()
150         if 'ANDROID_DATA' not in os.environ:
151             import stat
152             os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
153
154
155
156     
157
158 class NewWallet:
159
160     def __init__(self, storage):
161
162         self.storage = storage
163         self.electrum_version = ELECTRUM_VERSION
164         self.gap_limit_for_change = 3 # constant
165
166         # saved fields
167         self.seed_version          = storage.get('seed_version', NEW_SEED_VERSION)
168
169         self.gap_limit             = storage.get('gap_limit', 5)
170         self.use_change            = storage.get('use_change',True)
171         self.use_encryption        = storage.get('use_encryption', False)
172         self.seed                  = storage.get('seed', '')               # encrypted
173         self.labels                = storage.get('labels', {})
174         self.frozen_addresses      = storage.get('frozen_addresses',[])
175         self.addressbook           = storage.get('contacts', [])
176
177         self.imported_keys         = storage.get('imported_keys',{})
178         self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
179
180         self.fee                   = int(storage.get('fee_per_kb', 10000))
181
182         self.master_public_keys = storage.get('master_public_keys',{})
183         self.master_private_keys = storage.get('master_private_keys', {})
184
185         self.next_addresses = storage.get('next_addresses',{})
186
187
188         # This attribute is set when wallet.start_threads is called.
189         self.synchronizer = None
190
191         self.load_accounts()
192
193         self.transactions = {}
194         tx_list = self.storage.get('transactions',{})
195         for k,v in tx_list.items():
196             try:
197                 tx = Transaction(v)
198             except Exception:
199                 print_msg("Warning: Cannot deserialize transactions. skipping")
200                 continue
201
202             self.add_extra_addresses(tx)
203             self.transactions[k] = tx
204
205         for h,tx in self.transactions.items():
206             if not self.check_new_tx(h, tx):
207                 print_error("removing unreferenced tx", h)
208                 self.transactions.pop(h)
209
210
211         # not saved
212         self.prevout_values = {}     # my own transaction outputs
213         self.spent_outputs = []
214
215         # spv
216         self.verifier = None
217
218         # there is a difference between wallet.up_to_date and interface.is_up_to_date()
219         # interface.is_up_to_date() returns true when all requests have been answered and processed
220         # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
221         
222         self.up_to_date = False
223         self.lock = threading.Lock()
224         self.transaction_lock = threading.Lock()
225         self.tx_event = threading.Event()
226
227         for tx_hash, tx in self.transactions.items():
228             self.update_tx_outputs(tx_hash)
229
230
231     def add_extra_addresses(self, tx):
232         h = tx.hash()
233         # find the address corresponding to pay-to-pubkey inputs
234         tx.add_extra_addresses(self.transactions)
235         for o in tx.d.get('outputs'):
236             if o.get('is_pubkey'):
237                 for tx2 in self.transactions.values():
238                     tx2.add_extra_addresses({h:tx})
239
240
241     def get_action(self):
242         pass
243
244             
245     def can_create_accounts(self):
246         return not self.is_watching_only()
247
248
249     def set_up_to_date(self,b):
250         with self.lock: self.up_to_date = b
251
252
253     def is_up_to_date(self):
254         with self.lock: return self.up_to_date
255
256
257     def update(self):
258         self.up_to_date = False
259         while not self.is_up_to_date(): 
260             time.sleep(0.1)
261
262
263     def import_key(self, sec, password):
264         # check password
265         seed = self.get_seed(password)
266         try:
267             address = address_from_private_key(sec)
268         except Exception:
269             raise Exception('Invalid private key')
270
271         if self.is_mine(address):
272             raise Exception('Address already in wallet')
273         
274         # store the originally requested keypair into the imported keys table
275         self.imported_keys[address] = pw_encode(sec, password )
276         self.storage.put('imported_keys', self.imported_keys, True)
277         if self.synchronizer:
278             self.synchronizer.subscribe_to_addresses([address])
279         return address
280         
281     def delete_imported_key(self, addr):
282         if addr in self.imported_keys:
283             self.imported_keys.pop(addr)
284             self.storage.put('imported_keys', self.imported_keys, True)
285
286
287     def make_seed(self):
288         import mnemonic, ecdsa
289         entropy = ecdsa.util.randrange( pow(2,160) )
290         nonce = 0
291         while True:
292             ss = "%040x"%(entropy+nonce)
293             s = hashlib.sha256(ss.decode('hex')).digest().encode('hex')
294             # we keep only 13 words, that's approximately 139 bits of entropy
295             words = mnemonic.mn_encode(s)[0:13] 
296             seed = ' '.join(words)
297             if is_new_seed(seed):
298                 break  # this will remove 8 bits of entropy
299             nonce += 1
300
301         return seed
302
303
304     def prepare_seed(self, seed):
305         import unicodedata
306         return NEW_SEED_VERSION, unicodedata.normalize('NFC', unicode(seed.strip()))
307
308
309
310     def add_seed(self, seed, password):
311         if self.seed: 
312             raise Exception("a seed exists")
313         
314         self.seed_version, self.seed = self.prepare_seed(seed)
315         if password: 
316             self.seed = pw_encode( self.seed, password)
317             self.use_encryption = True
318         else:
319             self.use_encryption = False
320
321         self.storage.put('seed', self.seed, True)
322         self.storage.put('seed_version', self.seed_version, True)
323         self.storage.put('use_encryption', self.use_encryption,True)
324         self.create_master_keys(password)
325
326
327     def create_watching_only_wallet(self, xpub):
328         self.storage.put('seed_version', self.seed_version, True)
329         self.add_master_public_key("m/", xpub)
330         account = BIP32_Account({'xpub':xpub})
331         self.add_account("m/", account)
332
333
334     def create_accounts(self, password):
335         seed = pw_decode(self.seed, password)
336         self.create_account('Main account', password)
337
338
339     def add_master_public_key(self, name, mpk):
340         self.master_public_keys[name] = mpk
341         self.storage.put('master_public_keys', self.master_public_keys, True)
342
343
344     def add_master_private_key(self, name, xpriv, password):
345         self.master_private_keys[name] = pw_encode(xpriv, password)
346         self.storage.put('master_private_keys', self.master_private_keys, True)
347
348
349     def add_master_keys(self, root, account_id, password):
350         x = self.master_private_keys.get(root)
351         if x: 
352             master_xpriv = pw_decode(x, password )
353             xpriv, xpub = bip32_private_derivation(master_xpriv, root, account_id)
354             self.add_master_public_key(account_id, xpub)
355             self.add_master_private_key(account_id, xpriv, password)
356         else:
357             master_xpub = self.master_public_keys[root]
358             xpub = bip32_public_derivation(master_xpub, root, account_id)
359             self.add_master_public_key(account_id, xpub)
360         return xpub
361
362
363     def create_master_keys(self, password):
364         xpriv, xpub = bip32_root(self.get_seed(password))
365         self.add_master_public_key("m/", xpub)
366         self.add_master_private_key("m/", xpriv, password)
367
368
369     def find_root_by_master_key(self, xpub):
370         for key, xpub2 in self.master_public_keys.items():
371             if key == "m/":continue
372             if xpub == xpub2:
373                 return key
374
375     def is_watching_only(self):
376         return (self.seed == '') and (self.master_private_keys == {})
377
378
379     def num_accounts(self):
380         keys = self.accounts.keys()
381         i = 0
382         while True:
383             account_id = self.account_id(i)
384             if account_id not in keys: break
385             i += 1
386         return i
387
388
389     def next_account_address(self, password):
390         i = self.num_accounts()
391         account_id = self.account_id(i)
392
393         addr = self.next_addresses.get(account_id)
394         if not addr: 
395             account = self.make_account(account_id, password)
396             addr = account.first_address()
397             self.next_addresses[account_id] = addr
398             self.storage.put('next_addresses', self.next_addresses)
399
400         return account_id, addr
401
402     def account_id(self, i):
403         return "m/%d'"%i
404
405     def make_account(self, account_id, password):
406         """Creates and saves the master keys, but does not save the account"""
407         xpub = self.add_master_keys("m/", account_id, password)
408         account = BIP32_Account({'xpub':xpub})
409         return account
410
411
412     def set_label(self, name, text = None):
413         changed = False
414         old_text = self.labels.get(name)
415         if text:
416             if old_text != text:
417                 self.labels[name] = text
418                 changed = True
419         else:
420             if old_text:
421                 self.labels.pop(name)
422                 changed = True
423
424         if changed:
425             self.storage.put('labels', self.labels, True)
426
427         run_hook('set_label', name, text, changed)
428         return changed
429
430
431     def create_account(self, name, password):
432         i = self.num_accounts()
433         account_id = self.account_id(i)
434         account = self.make_account(account_id, password)
435         self.add_account(account_id, account)
436         if name:
437             self.set_label(account_id, name)
438
439         # add address of the next account
440         _, _ = self.next_account_address(password)
441
442
443     def add_account(self, account_id, account):
444         self.accounts[account_id] = account
445         if account_id in self.pending_accounts:
446             self.pending_accounts.pop(account_id)
447             self.storage.put('pending_accounts', self.pending_accounts)
448         self.save_accounts()
449
450
451     def save_accounts(self):
452         d = {}
453         for k, v in self.accounts.items():
454             d[k] = v.dump()
455         self.storage.put('accounts', d, True)
456
457     
458
459     def load_accounts(self):
460         d = self.storage.get('accounts', {})
461         self.accounts = {}
462         for k, v in d.items():
463             if k == 0:
464                 v['mpk'] = self.storage.get('master_public_key')
465                 self.accounts[k] = OldAccount(v)
466             elif v.get('xpub3'):
467                 self.accounts[k] = BIP32_Account_2of3(v)
468             elif v.get('xpub2'):
469                 self.accounts[k] = BIP32_Account_2of2(v)
470             elif v.get('xpub'):
471                 self.accounts[k] = BIP32_Account(v)
472             else:
473                 raise
474
475         self.pending_accounts = self.storage.get('pending_accounts',{})
476
477
478     def delete_pending_account(self, k):
479         self.pending_accounts.pop(k)
480         self.storage.put('pending_accounts', self.pending_accounts)
481
482     def account_is_pending(self, k):
483         return k in self.pending_accounts
484
485     def create_pending_account(self, name, password):
486         account_id, addr = self.next_account_address(password)
487         self.set_label(account_id, name)
488         self.pending_accounts[account_id] = addr
489         self.storage.put('pending_accounts', self.pending_accounts)
490
491     def get_pending_accounts(self):
492         return self.pending_accounts.items()
493
494
495     def addresses(self, include_change = True, _next=True):
496         o = self.get_account_addresses(-1, include_change)
497         for a in self.accounts.keys():
498             o += self.get_account_addresses(a, include_change)
499
500         if _next:
501             for addr in self.next_addresses.values():
502                 if addr not in o:
503                     o += [addr]
504         return o
505
506
507     def is_mine(self, address):
508         return address in self.addresses(True)
509
510
511     def is_change(self, address):
512         if not self.is_mine(address): return False
513         if address in self.imported_keys.keys(): return False
514         acct, s = self.get_address_index(address)
515         if s is None: return False
516         return s[0] == 1
517
518     def get_master_public_key(self):
519         return self.master_public_keys["m/"]
520
521     def get_master_public_keys(self):
522         out = {}
523         for k, account in self.accounts.items():
524             name = self.get_account_name(k)
525             mpk_text = '\n\n'.join( account.get_master_pubkeys() )
526             out[name] = mpk_text
527         return out
528
529     def get_master_private_key(self, account, password):
530         k = self.master_private_keys.get(account)
531         if not k: return
532         xpriv = pw_decode( k, password)
533         return xpriv
534
535
536     def get_address_index(self, address):
537         if address in self.imported_keys.keys():
538             return -1, None
539
540         for account in self.accounts.keys():
541             for for_change in [0,1]:
542                 addresses = self.accounts[account].get_addresses(for_change)
543                 for addr in addresses:
544                     if address == addr:
545                         return account, (for_change, addresses.index(addr))
546
547         for k,v in self.next_addresses.items():
548             if v == address:
549                 return k, (0,0)
550
551         raise Exception("Address not found", address)
552
553
554     def getpubkeys(self, addr):
555         assert is_valid(addr) and self.is_mine(addr)
556         account, sequence = self.get_address_index(addr)
557         if account != -1:
558             a = self.accounts[account]
559             return a.get_pubkeys( sequence )
560
561
562     def get_roots(self, account):
563         roots = []
564         for a in account.split('&'):
565             s = a.strip()
566             m = re.match("m/(\d+')", s)
567             roots.append( m.group(1) )
568         return roots
569
570
571     def is_seeded(self, account):
572         return True
573
574
575         for root in self.get_roots(account):
576             if root not in self.master_private_keys.keys(): 
577                 return False
578         return True
579
580     def rebase_sequence(self, account, sequence):
581         # account has one or more xpub
582         # sequence is a sequence of public derivations
583         c, i = sequence
584         dd = []
585         for a in account.split('&'):
586             s = a.strip()
587             m = re.match("m/(\d+)'", s)
588             root = "m/"
589             num = int(m.group(1))
590             dd.append( (root, [num,c,i] ) )
591         return dd
592         
593
594
595
596
597     def get_seed(self, password):
598         s = pw_decode(self.seed, password)
599         seed = mnemonic_to_seed(s,'').encode('hex')
600         return seed
601
602
603     def get_mnemonic(self, password):
604         return pw_decode(self.seed, password)
605         
606
607     def get_private_key(self, address, password):
608         if self.is_watching_only():
609             return []
610
611         # first check the provided password
612         seed = self.get_seed(password)
613
614         out = []
615         if address in self.imported_keys.keys():
616             out.append( pw_decode( self.imported_keys[address], password ) )
617         else:
618             account_id, sequence = self.get_address_index(address)
619             account = self.accounts[account_id]
620             xpubs = account.get_master_pubkeys()
621             roots = [k for k, v in self.master_public_keys.iteritems() if v in xpubs]
622             for root in roots:
623                 xpriv = self.get_master_private_key(root, password)
624                 if not xpriv:
625                     continue
626                 _, _, _, c, k = deserialize_xkey(xpriv)
627                 pk = bip32_private_key( sequence, k, c )
628                 out.append(pk)
629                     
630         return out
631
632
633     def get_public_keys(self, address):
634         account_id, sequence = self.get_address_index(address)
635         return self.accounts[account_id].get_pubkeys(sequence)
636
637
638     def add_keypairs_from_wallet(self, tx, keypairs, password):
639         for txin in tx.inputs:
640             address = txin['address']
641             if not self.is_mine(address):
642                 continue
643             private_keys = self.get_private_key(address, password)
644             for sec in private_keys:
645                 pubkey = public_key_from_private_key(sec)
646                 keypairs[ pubkey ] = sec
647                 if address in self.imported_keys.keys():
648                     txin['redeemPubkey'] = pubkey
649
650
651     def add_keypairs_from_KeyID(self, tx, keypairs, password):
652         # first check the provided password
653         seed = self.get_seed(password)
654
655         for txin in tx.inputs:
656             keyid = txin.get('KeyID')
657             if keyid:
658                 roots = []
659                 for s in keyid.split('&'):
660                     m = re.match("bip32\((.*),(/\d+/\d+)\)", s)
661                     if not m: continue
662                     xpub = m.group(1)
663                     sequence = m.group(2)
664                     root = self.find_root_by_master_key(xpub)
665                     if not root: continue
666                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
667                     root = root + '%d'%sequence[0]
668                     sequence = sequence[1:]
669                     roots.append((root,sequence)) 
670
671                 account_id = " & ".join( map(lambda x:x[0], roots) )
672                 account = self.accounts.get(account_id)
673                 if not account: continue
674                 addr = account.get_address(*sequence)
675                 txin['address'] = addr # fixme: side effect
676                 pk = self.get_private_key(addr, password)
677                 for sec in pk:
678                     pubkey = public_key_from_private_key(sec)
679                     keypairs[pubkey] = sec
680
681
682
683     def signrawtransaction(self, tx, input_info, private_keys, password):
684
685         # check that the password is correct
686         seed = self.get_seed(password)
687
688         # if input_info is not known, build it using wallet UTXOs
689         if not input_info:
690             input_info = []
691             unspent_coins = self.get_unspent_coins()
692             for txin in tx.inputs:
693                 for item in unspent_coins:
694                     if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
695                         info = { 'address':item['address'], 'scriptPubKey':item['scriptPubKey'] }
696                         self.add_input_info(info)
697                         input_info.append(info)
698                         break
699                 else:
700                     print_error( "input not in UTXOs" )
701                     input_info.append(None)
702
703         # add input_info to the transaction
704         print_error("input_info", input_info)
705         tx.add_input_info(input_info)
706
707         # build a list of public/private keys
708         keypairs = {}
709
710         # add private keys from parameter
711         for sec in private_keys:
712             pubkey = public_key_from_private_key(sec)
713             keypairs[ pubkey ] = sec
714
715         # add private_keys from KeyID
716         self.add_keypairs_from_KeyID(tx, keypairs, password)
717         # add private keys from wallet
718         self.add_keypairs_from_wallet(tx, keypairs, password)
719         # sign the transaction
720         self.sign_transaction(tx, keypairs, password)
721
722
723     def sign_message(self, address, message, password):
724         keys = self.get_private_key(address, password)
725         assert len(keys) == 1
726         sec = keys[0]
727         key = regenerate_key(sec)
728         compressed = is_compressed(sec)
729         return key.sign_message(message, compressed, address)
730
731
732
733     def decrypt_message(self, pubkey, message, password):
734         address = public_key_to_bc_address(pubkey.decode('hex'))
735         keys = self.get_private_key(address, password)
736         secret = keys[0]
737         ec = regenerate_key(secret)
738         decrypted = ec.decrypt_message(message)
739         return decrypted[0]
740
741
742     def change_gap_limit(self, value):
743         if value >= self.gap_limit:
744             self.gap_limit = value
745             self.storage.put('gap_limit', self.gap_limit, True)
746             #self.interface.poke('synchronizer')
747             return True
748
749         elif value >= self.min_acceptable_gap():
750             for key, account in self.accounts.items():
751                 addresses = account[0]
752                 k = self.num_unused_trailing_addresses(addresses)
753                 n = len(addresses) - k + value
754                 addresses = addresses[0:n]
755                 self.accounts[key][0] = addresses
756
757             self.gap_limit = value
758             self.storage.put('gap_limit', self.gap_limit, True)
759             self.save_accounts()
760             return True
761         else:
762             return False
763
764     def num_unused_trailing_addresses(self, addresses):
765         k = 0
766         for a in addresses[::-1]:
767             if self.history.get(a):break
768             k = k + 1
769         return k
770
771     def min_acceptable_gap(self):
772         # fixme: this assumes wallet is synchronized
773         n = 0
774         nmax = 0
775
776         for account in self.accounts.values():
777             addresses = account.get_addresses(0)
778             k = self.num_unused_trailing_addresses(addresses)
779             for a in addresses[0:-k]:
780                 if self.history.get(a):
781                     n = 0
782                 else:
783                     n += 1
784                     if n > nmax: nmax = n
785         return nmax + 1
786
787
788     def address_is_old(self, address):
789         age = -1
790         h = self.history.get(address, [])
791         if h == ['*']:
792             return True
793         for tx_hash, tx_height in h:
794             if tx_height == 0:
795                 tx_age = 0
796             else:
797                 tx_age = self.network.get_local_height() - tx_height + 1
798             if tx_age > age:
799                 age = tx_age
800         return age > 2
801
802
803     def synchronize_sequence(self, account, for_change):
804         limit = self.gap_limit_for_change if for_change else self.gap_limit
805         new_addresses = []
806         while True:
807             addresses = account.get_addresses(for_change)
808             if len(addresses) < limit:
809                 address = account.create_new_address(for_change)
810                 self.history[address] = []
811                 new_addresses.append( address )
812                 continue
813
814             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
815                 break
816             else:
817                 address = account.create_new_address(for_change)
818                 self.history[address] = []
819                 new_addresses.append( address )
820
821         return new_addresses
822         
823
824     def check_pending_accounts(self):
825         for account_id, addr in self.next_addresses.items():
826             if self.address_is_old(addr):
827                 print_error( "creating account", account_id )
828                 xpub = self.master_public_keys[account_id]
829                 account = BIP32_Account({'xpub':xpub})
830                 self.add_account(account_id, account)
831                 self.next_addresses.pop(account_id)
832
833
834     def synchronize_account(self, account):
835         new = []
836         new += self.synchronize_sequence(account, 0)
837         new += self.synchronize_sequence(account, 1)
838         return new
839
840
841     def synchronize(self):
842         self.check_pending_accounts()
843         new = []
844         for account in self.accounts.values():
845             new += self.synchronize_account(account)
846         if new:
847             self.save_accounts()
848             self.storage.put('addr_history', self.history, True)
849         return new
850
851
852     def is_found(self):
853         return self.history.values() != [[]] * len(self.history) 
854
855
856     def add_contact(self, address, label=None):
857         self.addressbook.append(address)
858         self.storage.put('contacts', self.addressbook, True)
859         if label:  
860             self.set_label(address, label)
861
862
863     def delete_contact(self, addr):
864         if addr in self.addressbook:
865             self.addressbook.remove(addr)
866             self.storage.put('addressbook', self.addressbook, True)
867
868
869     def fill_addressbook(self):
870         for tx_hash, tx in self.transactions.items():
871             is_relevant, is_send, _, _ = self.get_tx_value(tx)
872             if is_send:
873                 for addr, v in tx.outputs:
874                     if not self.is_mine(addr) and addr not in self.addressbook:
875                         self.addressbook.append(addr)
876         # redo labels
877         # self.update_tx_labels()
878
879     def get_num_tx(self, address):
880         n = 0 
881         for tx in self.transactions.values():
882             if address in map(lambda x:x[0], tx.outputs): n += 1
883         return n
884
885
886     def get_address_flags(self, addr):
887         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
888         flags += "F" if addr in self.frozen_addresses else "-"
889         return flags
890         
891
892     def get_tx_value(self, tx, account=None):
893         domain = self.get_account_addresses(account)
894         return tx.get_value(domain, self.prevout_values)
895
896     
897     def update_tx_outputs(self, tx_hash):
898         tx = self.transactions.get(tx_hash)
899
900         for i, (addr, value) in enumerate(tx.outputs):
901             key = tx_hash+ ':%d'%i
902             self.prevout_values[key] = value
903
904         for item in tx.inputs:
905             if self.is_mine(item.get('address')):
906                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
907                 self.spent_outputs.append(key)
908
909
910     def get_addr_balance(self, address):
911         assert self.is_mine(address)
912         h = self.history.get(address,[])
913         if h == ['*']: return 0,0
914         c = u = 0
915         received_coins = []   # list of coins received at address
916
917         for tx_hash, tx_height in h:
918             tx = self.transactions.get(tx_hash)
919             if not tx: continue
920
921             for i, (addr, value) in enumerate(tx.outputs):
922                 if addr == address:
923                     key = tx_hash + ':%d'%i
924                     received_coins.append(key)
925
926         for tx_hash, tx_height in h:
927             tx = self.transactions.get(tx_hash)
928             if not tx: continue
929             v = 0
930
931             for item in tx.inputs:
932                 addr = item.get('address')
933                 if addr == address:
934                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
935                     value = self.prevout_values.get( key )
936                     if key in received_coins: 
937                         v -= value
938
939             for i, (addr, value) in enumerate(tx.outputs):
940                 key = tx_hash + ':%d'%i
941                 if addr == address:
942                     v += value
943
944             if tx_height:
945                 c += v
946             else:
947                 u += v
948         return c, u
949
950
951     def get_account_name(self, k):
952         default = "Unnamed account"
953         m = re.match("m/0'/(\d+)", k)
954         if m:
955             num = m.group(1)
956             if num == '0':
957                 default = "Main account"
958             else:
959                 default = "Account %s"%num
960                     
961         m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
962         if m:
963             num = m.group(1)
964             default = "2of2 account %s"%num
965         name = self.labels.get(k, default)
966         return name
967
968
969     def get_account_names(self):
970         accounts = {}
971         for k, account in self.accounts.items():
972             accounts[k] = self.get_account_name(k)
973         if self.imported_keys:
974             accounts[-1] = 'Imported keys'
975         return accounts
976
977
978     def get_account_addresses(self, a, include_change=True):
979         if a is None:
980             o = self.addresses(True)
981         elif a == -1:
982             o = self.imported_keys.keys()
983         else:
984             ac = self.accounts[a]
985             o = ac.get_addresses(0)
986             if include_change: o += ac.get_addresses(1)
987         return o
988
989     def get_imported_balance(self):
990         return self.get_balance(self.imported_keys.keys())
991
992     def get_account_balance(self, account):
993         return self.get_balance(self.get_account_addresses(account))
994
995     def get_frozen_balance(self):
996         return self.get_balance(self.frozen_addresses)
997         
998     def get_balance(self, domain=None):
999         if domain is None: domain = self.addresses(True)
1000         cc = uu = 0
1001         for addr in domain:
1002             c, u = self.get_addr_balance(addr)
1003             cc += c
1004             uu += u
1005         return cc, uu
1006
1007
1008     def get_unspent_coins(self, domain=None):
1009         coins = []
1010         if domain is None: domain = self.addresses(True)
1011         for addr in domain:
1012             h = self.history.get(addr, [])
1013             if h == ['*']: continue
1014             for tx_hash, tx_height in h:
1015                 tx = self.transactions.get(tx_hash)
1016                 if tx is None: raise Exception("Wallet not synchronized")
1017                 is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
1018                 for o in tx.d.get('outputs'):
1019                     output = o.copy()
1020                     if output.get('address') != addr: continue
1021                     key = tx_hash + ":%d" % output.get('prevout_n')
1022                     if key in self.spent_outputs: continue
1023                     output['prevout_hash'] = tx_hash
1024                     output['height'] = tx_height
1025                     output['coinbase'] = is_coinbase
1026                     coins.append((tx_height, output))
1027
1028         # sort by age
1029         if coins:
1030             coins = sorted(coins)
1031             if coins[-1][0] != 0:
1032                 while coins[0][0] == 0: 
1033                     coins = coins[1:] + [ coins[0] ]
1034         return [x[1] for x in coins]
1035
1036
1037     def choose_tx_inputs( self, amount, fixed_fee, num_outputs, domain = None ):
1038         """ todo: minimize tx size """
1039         total = 0
1040         fee = self.fee if fixed_fee is None else fixed_fee
1041         if domain is None:
1042             domain = self.addresses(True)
1043
1044         for i in self.frozen_addresses:
1045             if i in domain: domain.remove(i)
1046
1047         coins = self.get_unspent_coins(domain)
1048         inputs = []
1049
1050         for item in coins:
1051             if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.get_local_height():
1052                 continue
1053             addr = item.get('address')
1054             v = item.get('value')
1055             total += v
1056             inputs.append(item)
1057             fee = self.estimated_fee(inputs, num_outputs) if fixed_fee is None else fixed_fee
1058             if total >= amount + fee: break
1059         else:
1060             inputs = []
1061
1062         return inputs, total, fee
1063
1064
1065     def set_fee(self, fee):
1066         if self.fee != fee:
1067             self.fee = fee
1068             self.storage.put('fee_per_kb', self.fee, True)
1069         
1070     def estimated_fee(self, inputs, num_outputs):
1071         estimated_size =  len(inputs) * 180 + num_outputs * 34    # this assumes non-compressed keys
1072         fee = self.fee * int(math.ceil(estimated_size/1000.))
1073         return fee
1074
1075
1076     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None):
1077         "add change to a transaction"
1078         change_amount = total - ( amount + fee )
1079         if change_amount > DUST_THRESHOLD:
1080             if not change_addr:
1081
1082                 # send change to one of the accounts involved in the tx
1083                 address = inputs[0].get('address')
1084                 account, _ = self.get_address_index(address)
1085
1086                 if not self.use_change or account == -1:
1087                     change_addr = inputs[-1]['address']
1088                 else:
1089                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
1090
1091             # Insert the change output at a random position in the outputs
1092             posn = random.randint(0, len(outputs))
1093             outputs[posn:posn] = [( change_addr,  change_amount)]
1094         return outputs
1095
1096
1097     def get_history(self, address):
1098         with self.lock:
1099             return self.history.get(address)
1100
1101
1102     def get_status(self, h):
1103         if not h: return None
1104         if h == ['*']: return '*'
1105         status = ''
1106         for tx_hash, height in h:
1107             status += tx_hash + ':%d:' % height
1108         return hashlib.sha256( status ).digest().encode('hex')
1109
1110
1111     def receive_tx_callback(self, tx_hash, tx, tx_height):
1112
1113         with self.transaction_lock:
1114             self.add_extra_addresses(tx)
1115             if not self.check_new_tx(tx_hash, tx):
1116                 # may happen due to pruning
1117                 print_error("received transaction that is no longer referenced in history", tx_hash)
1118                 return
1119             self.transactions[tx_hash] = tx
1120             self.network.pending_transactions_for_notifications.append(tx)
1121             self.save_transactions()
1122             if self.verifier and tx_height>0: 
1123                 self.verifier.add(tx_hash, tx_height)
1124             self.update_tx_outputs(tx_hash)
1125
1126
1127     def save_transactions(self):
1128         tx = {}
1129         for k,v in self.transactions.items():
1130             tx[k] = str(v)
1131         self.storage.put('transactions', tx, True)
1132
1133     def receive_history_callback(self, addr, hist):
1134
1135         if not self.check_new_history(addr, hist):
1136             raise Exception("error: received history for %s is not consistent with known transactions"%addr)
1137             
1138         with self.lock:
1139             self.history[addr] = hist
1140             self.storage.put('addr_history', self.history, True)
1141
1142         if hist != ['*']:
1143             for tx_hash, tx_height in hist:
1144                 if tx_height>0:
1145                     # add it in case it was previously unconfirmed
1146                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1147
1148
1149     def get_tx_history(self, account=None):
1150         if not self.verifier:
1151             return []
1152
1153         with self.transaction_lock:
1154             history = self.transactions.items()
1155             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1156             result = []
1157     
1158             balance = 0
1159             for tx_hash, tx in history:
1160                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1161                 if v is not None: balance += v
1162
1163             c, u = self.get_account_balance(account)
1164
1165             if balance != c+u:
1166                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1167
1168             balance = c + u - balance
1169             for tx_hash, tx in history:
1170                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1171                 if not is_relevant:
1172                     continue
1173                 if value is not None:
1174                     balance += value
1175
1176                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1177                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1178
1179         return result
1180
1181
1182     def get_label(self, tx_hash):
1183         label = self.labels.get(tx_hash)
1184         is_default = (label == '') or (label is None)
1185         if is_default: label = self.get_default_label(tx_hash)
1186         return label, is_default
1187
1188
1189     def get_default_label(self, tx_hash):
1190         tx = self.transactions.get(tx_hash)
1191         default_label = ''
1192         if tx:
1193             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1194             if is_mine:
1195                 for o in tx.outputs:
1196                     o_addr, _ = o
1197                     if not self.is_mine(o_addr):
1198                         try:
1199                             default_label = self.labels[o_addr]
1200                         except KeyError:
1201                             default_label = '>' + o_addr
1202                         break
1203                 else:
1204                     default_label = '(internal)'
1205             else:
1206                 for o in tx.outputs:
1207                     o_addr, _ = o
1208                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1209                         break
1210                 else:
1211                     for o in tx.outputs:
1212                         o_addr, _ = o
1213                         if self.is_mine(o_addr):
1214                             break
1215                     else:
1216                         o_addr = None
1217
1218                 if o_addr:
1219                     dest_label = self.labels.get(o_addr)
1220                     try:
1221                         default_label = self.labels[o_addr]
1222                     except KeyError:
1223                         default_label = '<' + o_addr
1224
1225         return default_label
1226
1227
1228     def make_unsigned_transaction(self, outputs, fee=None, change_addr=None, domain=None ):
1229         for address, x in outputs:
1230             assert is_valid(address), "Address " + address + " is invalid!"
1231         amount = sum( map(lambda x:x[1], outputs) )
1232         inputs, total, fee = self.choose_tx_inputs( amount, fee, len(outputs), domain )
1233         if not inputs:
1234             raise ValueError("Not enough funds")
1235         for txin in inputs:
1236             self.add_input_info(txin)
1237         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr)
1238         return Transaction.from_io(inputs, outputs)
1239
1240
1241     def mktx(self, outputs, password, fee=None, change_addr=None, domain= None ):
1242         tx = self.make_unsigned_transaction(outputs, fee, change_addr, domain)
1243         keypairs = {}
1244         self.add_keypairs_from_wallet(tx, keypairs, password)
1245         if keypairs:
1246             self.sign_transaction(tx, keypairs, password)
1247         return tx
1248
1249
1250     def add_input_info(self, txin):
1251         address = txin['address']
1252         if address in self.imported_keys.keys():
1253             return
1254         account_id, sequence = self.get_address_index(address)
1255         account = self.accounts[account_id]
1256         txin['KeyID'] = account.get_keyID(sequence)
1257         redeemScript = account.redeem_script(sequence)
1258         if redeemScript: 
1259             txin['redeemScript'] = redeemScript
1260         else:
1261             txin['redeemPubkey'] = account.get_pubkey(*sequence)
1262
1263
1264     def sign_transaction(self, tx, keypairs, password):
1265         tx.sign(keypairs)
1266         run_hook('sign_transaction', tx, password)
1267
1268
1269     def sendtx(self, tx):
1270         # synchronous
1271         h = self.send_tx(tx)
1272         self.tx_event.wait()
1273         return self.receive_tx(h, tx)
1274
1275     def send_tx(self, tx):
1276         # asynchronous
1277         self.tx_event.clear()
1278         self.network.send([('blockchain.transaction.broadcast', [str(tx)])], self.on_broadcast)
1279         return tx.hash()
1280
1281     def on_broadcast(self, i, r):
1282         self.tx_result = r.get('result')
1283         self.tx_event.set()
1284
1285     def receive_tx(self, tx_hash, tx):
1286         out = self.tx_result 
1287         if out != tx_hash:
1288             return False, "error: " + out
1289         run_hook('receive_tx', tx, self)
1290         return True, out
1291
1292
1293
1294     def update_password(self, old_password, new_password):
1295         if new_password == '': new_password = None
1296         decoded = self.get_seed(old_password)
1297         self.seed = pw_encode( decoded, new_password)
1298         self.storage.put('seed', self.seed, True)
1299         self.use_encryption = (new_password != None)
1300         self.storage.put('use_encryption', self.use_encryption,True)
1301         for k in self.imported_keys.keys():
1302             a = self.imported_keys[k]
1303             b = pw_decode(a, old_password)
1304             c = pw_encode(b, new_password)
1305             self.imported_keys[k] = c
1306         self.storage.put('imported_keys', self.imported_keys, True)
1307
1308         for k, v in self.master_private_keys.items():
1309             b = pw_decode(v, old_password)
1310             c = pw_encode(b, new_password)
1311             self.master_private_keys[k] = c
1312         self.storage.put('master_private_keys', self.master_private_keys, True)
1313
1314
1315     def freeze(self,addr):
1316         if self.is_mine(addr) and addr not in self.frozen_addresses:
1317             self.frozen_addresses.append(addr)
1318             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1319             return True
1320         else:
1321             return False
1322
1323     def unfreeze(self,addr):
1324         if self.is_mine(addr) and addr in self.frozen_addresses:
1325             self.frozen_addresses.remove(addr)
1326             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1327             return True
1328         else:
1329             return False
1330
1331
1332     def set_verifier(self, verifier):
1333         self.verifier = verifier
1334
1335         # review transactions that are in the history
1336         for addr, hist in self.history.items():
1337             if hist == ['*']: continue
1338             for tx_hash, tx_height in hist:
1339                 if tx_height>0:
1340                     # add it in case it was previously unconfirmed
1341                     self.verifier.add(tx_hash, tx_height)
1342
1343
1344         # if we are on a pruning server, remove unverified transactions
1345         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1346         for tx_hash in self.transactions.keys():
1347             if tx_hash not in vr:
1348                 self.transactions.pop(tx_hash)
1349
1350
1351
1352     def check_new_history(self, addr, hist):
1353         
1354         # check that all tx in hist are relevant
1355         if hist != ['*']:
1356             for tx_hash, height in hist:
1357                 tx = self.transactions.get(tx_hash)
1358                 if not tx: continue
1359                 if not tx.has_address(addr):
1360                     return False
1361
1362         # check that we are not "orphaning" a transaction
1363         old_hist = self.history.get(addr,[])
1364         if old_hist == ['*']: return True
1365
1366         for tx_hash, height in old_hist:
1367             if tx_hash in map(lambda x:x[0], hist): continue
1368             found = False
1369             for _addr, _hist in self.history.items():
1370                 if _addr == addr: continue
1371                 if _hist == ['*']: continue
1372                 _tx_hist = map(lambda x:x[0], _hist)
1373                 if tx_hash in _tx_hist:
1374                     found = True
1375                     break
1376
1377             if not found:
1378                 tx = self.transactions.get(tx_hash)
1379                 # tx might not be there
1380                 if not tx: continue
1381                 
1382                 # already verified?
1383                 if self.verifier.get_height(tx_hash):
1384                     continue
1385                 # unconfirmed tx
1386                 print_error("new history is orphaning transaction:", tx_hash)
1387                 # check that all outputs are not mine, request histories
1388                 ext_requests = []
1389                 for _addr, _v in tx.outputs:
1390                     # assert not self.is_mine(_addr)
1391                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1392
1393                 ext_h = self.network.synchronous_get(ext_requests)
1394                 print_error("sync:", ext_requests, ext_h)
1395                 height = None
1396                 for h in ext_h:
1397                     if h == ['*']: continue
1398                     for item in h:
1399                         if item.get('tx_hash') == tx_hash:
1400                             height = item.get('height')
1401                 if height:
1402                     print_error("found height for", tx_hash, height)
1403                     self.verifier.add(tx_hash, height)
1404                 else:
1405                     print_error("removing orphaned tx from history", tx_hash)
1406                     self.transactions.pop(tx_hash)
1407
1408         return True
1409
1410
1411
1412     def check_new_tx(self, tx_hash, tx):
1413         # 1 check that tx is referenced in addr_history. 
1414         addresses = []
1415         for addr, hist in self.history.items():
1416             if hist == ['*']:continue
1417             for txh, height in hist:
1418                 if txh == tx_hash: 
1419                     addresses.append(addr)
1420
1421         if not addresses:
1422             return False
1423
1424         # 2 check that referencing addresses are in the tx
1425         for addr in addresses:
1426             if not tx.has_address(addr):
1427                 return False
1428
1429         return True
1430
1431
1432     def start_threads(self, network):
1433         from verifier import TxVerifier
1434         self.network = network
1435         if self.network is not None:
1436             self.verifier = TxVerifier(self.network, self.storage)
1437             self.verifier.start()
1438             self.set_verifier(self.verifier)
1439             self.synchronizer = WalletSynchronizer(self, network)
1440             self.synchronizer.start()
1441         else:
1442             self.verifier = None
1443             self.synchronizer =None
1444
1445     def stop_threads(self):
1446         if self.network:
1447             self.verifier.stop()
1448             self.synchronizer.stop()
1449
1450
1451     def restore(self, callback):
1452         from i18n import _
1453         def wait_for_wallet():
1454             self.set_up_to_date(False)
1455             while not self.is_up_to_date():
1456                 msg = "%s\n%s %d\n%s %.1f"%(
1457                     _("Please wait..."),
1458                     _("Addresses generated:"),
1459                     len(self.addresses(True)), 
1460                     _("Kilobytes received:"), 
1461                     self.network.interface.bytes_received/1024.)
1462
1463                 apply(callback, (msg,))
1464                 time.sleep(0.1)
1465
1466         def wait_for_network():
1467             while not self.network.is_connected():
1468                 msg = "%s \n" % (_("Connecting..."))
1469                 apply(callback, (msg,))
1470                 time.sleep(0.1)
1471
1472         # wait until we are connected, because the user might have selected another server
1473         if self.network:
1474             wait_for_network()
1475             wait_for_wallet()
1476         else:
1477             self.synchronize()
1478             
1479         self.fill_addressbook()
1480
1481
1482
1483 class Wallet_2of2(NewWallet):
1484
1485     def __init__(self, storage):
1486         NewWallet.__init__(self, storage)
1487         self.storage.put('wallet_type', '2of2', True)
1488
1489     def can_create_accounts(self):
1490         return False
1491
1492     def create_account(self):
1493         xpub1 = self.master_public_keys.get("m/")
1494         xpub2 = self.master_public_keys.get("cold/")
1495         account = BIP32_Account_2of2({'xpub':xpub1, 'xpub2':xpub2})
1496         self.add_account('m/', account)
1497
1498     def get_master_public_keys(self):
1499         xpub1 = self.master_public_keys.get("m/")
1500         xpub2 = self.master_public_keys.get("cold/")
1501         return {'hot':xpub1, 'cold':xpub2}
1502
1503     def get_action(self):
1504         xpub1 = self.master_public_keys.get("m/")
1505         xpub2 = self.master_public_keys.get("cold/")
1506         if xpub1 is None:
1507             return 'create_2of2_1'
1508         if xpub2 is None:
1509             return 'create_2of2_2'
1510
1511
1512
1513 class Wallet_2of3(Wallet_2of2):
1514
1515     def __init__(self, storage):
1516         NewWallet.__init__(self, storage)
1517         self.storage.put('wallet_type', '2of3', True)
1518
1519     def create_account(self):
1520         xpub1 = self.master_public_keys.get("m/")
1521         xpub2 = self.master_public_keys.get("cold/")
1522         xpub3 = self.master_public_keys.get("remote/")
1523         account = BIP32_Account_2of3({'xpub':xpub1, 'xpub2':xpub2, 'xpub3':xpub3})
1524         self.add_account('m/', account)
1525
1526     def get_master_public_keys(self):
1527         xpub1 = self.master_public_keys.get("m/")
1528         xpub2 = self.master_public_keys.get("cold/")
1529         xpub3 = self.master_public_keys.get("remote/")
1530         return {'hot':xpub1, 'cold':xpub2, 'remote':xpub3}
1531
1532     def get_action(self):
1533         xpub1 = self.master_public_keys.get("m/")
1534         xpub2 = self.master_public_keys.get("cold/")
1535         xpub3 = self.master_public_keys.get("remote/")
1536         if xpub2 is None:
1537             return 'create_2of3_1'
1538         if xpub1 is None:
1539             return 'create_2of3_2'
1540         if xpub3 is None:
1541             return 'create_2of3_3'
1542
1543
1544 class WalletSynchronizer(threading.Thread):
1545
1546
1547     def __init__(self, wallet, network):
1548         threading.Thread.__init__(self)
1549         self.daemon = True
1550         self.wallet = wallet
1551         self.network = network
1552         self.was_updated = True
1553         self.running = False
1554         self.lock = threading.Lock()
1555         self.queue = Queue.Queue()
1556
1557     def stop(self):
1558         with self.lock: self.running = False
1559
1560     def is_running(self):
1561         with self.lock: return self.running
1562
1563     
1564     def subscribe_to_addresses(self, addresses):
1565         messages = []
1566         for addr in addresses:
1567             messages.append(('blockchain.address.subscribe', [addr]))
1568         self.network.subscribe( messages, lambda i,r: self.queue.put(r))
1569
1570
1571     def run(self):
1572         with self.lock:
1573             self.running = True
1574
1575         while self.is_running():
1576
1577             if not self.network.is_connected():
1578                 self.network.wait_until_connected()
1579                 
1580             self.run_interface()
1581
1582
1583     def run_interface(self):
1584
1585         print_error("synchronizer: connected to", self.network.main_server())
1586
1587         requested_tx = []
1588         missing_tx = []
1589         requested_histories = {}
1590
1591         # request any missing transactions
1592         for history in self.wallet.history.values():
1593             if history == ['*']: continue
1594             for tx_hash, tx_height in history:
1595                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1596                     missing_tx.append( (tx_hash, tx_height) )
1597
1598         if missing_tx:
1599             print_error("missing tx", missing_tx)
1600
1601         # subscriptions
1602         self.subscribe_to_addresses(self.wallet.addresses(True))
1603
1604         while self.is_running():
1605             # 1. create new addresses
1606             new_addresses = self.wallet.synchronize()
1607
1608             # request missing addresses
1609             if new_addresses:
1610                 self.subscribe_to_addresses(new_addresses)
1611
1612             # request missing transactions
1613             for tx_hash, tx_height in missing_tx:
1614                 if (tx_hash, tx_height) not in requested_tx:
1615                     self.network.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], lambda i,r: self.queue.put(r))
1616                     requested_tx.append( (tx_hash, tx_height) )
1617             missing_tx = []
1618
1619             # detect if situation has changed
1620             if self.network.is_up_to_date() and self.queue.empty():
1621                 if not self.wallet.is_up_to_date():
1622                     self.wallet.set_up_to_date(True)
1623                     self.was_updated = True
1624             else:
1625                 if self.wallet.is_up_to_date():
1626                     self.wallet.set_up_to_date(False)
1627                     self.was_updated = True
1628
1629             if self.was_updated:
1630                 self.network.trigger_callback('updated')
1631                 self.was_updated = False
1632
1633             # 2. get a response
1634             try:
1635                 r = self.queue.get(block=True, timeout=1)
1636             except Queue.Empty:
1637                 continue
1638
1639             # see if it changed
1640             #if interface != self.network.interface:
1641             #    break
1642             
1643             if not r:
1644                 continue
1645
1646             # 3. handle response
1647             method = r['method']
1648             params = r['params']
1649             result = r.get('result')
1650             error = r.get('error')
1651             if error:
1652                 print "error", r
1653                 continue
1654
1655             if method == 'blockchain.address.subscribe':
1656                 addr = params[0]
1657                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1658                     if requested_histories.get(addr) is None:
1659                         self.network.send([('blockchain.address.get_history', [addr])], lambda i,r:self.queue.put(r))
1660                         requested_histories[addr] = result
1661
1662             elif method == 'blockchain.address.get_history':
1663                 addr = params[0]
1664                 print_error("receiving history", addr, result)
1665                 if result == ['*']:
1666                     assert requested_histories.pop(addr) == '*'
1667                     self.wallet.receive_history_callback(addr, result)
1668                 else:
1669                     hist = []
1670                     # check that txids are unique
1671                     txids = []
1672                     for item in result:
1673                         tx_hash = item['tx_hash']
1674                         if tx_hash not in txids:
1675                             txids.append(tx_hash)
1676                             hist.append( (tx_hash, item['height']) )
1677
1678                     if len(hist) != len(result):
1679                         raise Exception("error: server sent history with non-unique txid", result)
1680
1681                     # check that the status corresponds to what was announced
1682                     rs = requested_histories.pop(addr)
1683                     if self.wallet.get_status(hist) != rs:
1684                         raise Exception("error: status mismatch: %s"%addr)
1685                 
1686                     # store received history
1687                     self.wallet.receive_history_callback(addr, hist)
1688
1689                     # request transactions that we don't have 
1690                     for tx_hash, tx_height in hist:
1691                         if self.wallet.transactions.get(tx_hash) is None:
1692                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1693                                 missing_tx.append( (tx_hash, tx_height) )
1694
1695             elif method == 'blockchain.transaction.get':
1696                 tx_hash = params[0]
1697                 tx_height = params[1]
1698                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1699                 tx = Transaction(result)
1700                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1701                 self.was_updated = True
1702                 requested_tx.remove( (tx_hash, tx_height) )
1703                 print_error("received tx:", tx_hash, len(tx.raw))
1704
1705             else:
1706                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1707
1708             if self.was_updated and not requested_tx:
1709                 self.network.trigger_callback('updated')
1710                 # Updated gets called too many times from other places as well; if we use that signal we get the notification three times
1711                 self.network.trigger_callback("new_transaction") 
1712                 self.was_updated = False
1713
1714
1715
1716
1717 class OldWallet(NewWallet):
1718
1719     def can_create_accounts(self):
1720         return False
1721
1722     def make_seed(self):
1723         import mnemonic
1724         seed = random_seed(128)
1725         return ' '.join(mnemonic.mn_encode(seed))
1726
1727     def prepare_seed(self, seed):
1728         import mnemonic
1729         # see if seed was entered as hex
1730         seed = seed.strip()
1731         try:
1732             assert seed
1733             seed.decode('hex')
1734             return OLD_SEED_VERSION, str(seed)
1735         except Exception:
1736             pass
1737
1738         words = seed.split()
1739         seed = mnemonic.mn_decode(words)
1740         if not seed:
1741             raise Exception("Invalid seed")
1742             
1743         return OLD_SEED_VERSION, seed
1744
1745
1746     def create_master_keys(self, password):
1747         seed = pw_decode(self.seed, password)
1748         mpk = OldAccount.mpk_from_seed(seed)
1749         self.storage.put('master_public_key', mpk, True)
1750
1751     def get_master_public_key(self):
1752         return self.storage.get("master_public_key")
1753
1754     def get_master_public_keys(self):
1755         return {'Main Account':self.get_master_public_key()}
1756
1757     def create_accounts(self, password):
1758         mpk = self.storage.get("master_public_key")
1759         self.create_account(mpk)
1760
1761     def create_account(self, mpk):
1762         self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
1763         self.save_accounts()
1764
1765     def create_watching_only_wallet(self, mpk):
1766         self.seed_version = OLD_SEED_VERSION
1767         self.storage.put('seed_version', self.seed_version, True)
1768         self.storage.put('master_public_key', mpk, True)
1769         self.create_account(mpk)
1770
1771     def get_seed(self, password):
1772         seed = pw_decode(self.seed, password)
1773         self.accounts[0].check_seed(seed)
1774         return seed
1775
1776     def get_mnemonic(self, password):
1777         import mnemonic
1778         s = pw_decode(self.seed, password)
1779         return ' '.join(mnemonic.mn_encode(s))
1780
1781
1782     def add_keypairs_from_KeyID(self, tx, keypairs, password):
1783         # first check the provided password
1784         seed = self.get_seed(password)
1785         for txin in tx.inputs:
1786             keyid = txin.get('KeyID')
1787             if keyid:
1788                 m = re.match("old\(([0-9a-f]+),(\d+),(\d+)", keyid)
1789                 if not m: continue
1790                 mpk = m.group(1)
1791                 if mpk != self.storage.get('master_public_key'): continue 
1792                 for_change = int(m.group(2))
1793                 num = int(m.group(3))
1794                 account = self.accounts[0]
1795                 addr = account.get_address(for_change, num)
1796                 txin['address'] = addr # fixme: side effect
1797                 pk = account.get_private_key(seed, (for_change, num))
1798                 pubkey = public_key_from_private_key(pk)
1799                 keypairs[pubkey] = pk
1800
1801
1802     def get_account_name(self, k):
1803         assert k == 0
1804         return 'Main account'
1805
1806     def is_seeded(self, account):
1807         return self.seed is not None
1808
1809     def get_private_key(self, address, password):
1810         if self.is_watching_only():
1811             return []
1812
1813         # first check the provided password
1814         seed = self.get_seed(password)
1815         
1816         out = []
1817         if address in self.imported_keys.keys():
1818             out.append( pw_decode( self.imported_keys[address], password ) )
1819         else:
1820             account_id, sequence = self.get_address_index(address)
1821             pk = self.accounts[0].get_private_key(seed, sequence)
1822             out.append(pk)
1823         return out
1824
1825     def check_pending_accounts(self):
1826         pass
1827
1828
1829 # former WalletFactory
1830 class Wallet(object):
1831
1832     def __new__(self, storage):
1833         config = storage.config
1834         if config.get('bitkey', False):
1835             # if user requested support for Bitkey device,
1836             # import Bitkey driver
1837             from wallet_bitkey import WalletBitkey
1838             return WalletBitkey(config)
1839
1840         if storage.get('wallet_type') == '2of2':
1841             return Wallet_2of2(storage)
1842
1843         if storage.get('wallet_type') == '2of3':
1844             return Wallet_2of3(storage)
1845
1846
1847         if not storage.file_exists:
1848             seed_version = NEW_SEED_VERSION if config.get('bip32') is True else OLD_SEED_VERSION
1849         else:
1850             seed_version = storage.get('seed_version')
1851             if not seed_version:
1852                 seed_version = OLD_SEED_VERSION if len(storage.get('master_public_key')) == 128 else NEW_SEED_VERSION
1853
1854         if seed_version == OLD_SEED_VERSION:
1855             return OldWallet(storage)
1856         elif seed_version == NEW_SEED_VERSION:
1857             return NewWallet(storage)
1858         else:
1859             msg = "This wallet seed is not supported."
1860             if seed_version in [5]:
1861                 msg += "\nTo open this wallet, try 'git checkout seed_v%d'"%seed_version
1862             print msg
1863             sys.exit(1)
1864
1865
1866
1867     @classmethod
1868     def is_seed(self, seed):
1869         if not seed:
1870             return False
1871         elif is_old_seed(seed):
1872             return True
1873         elif is_new_seed(seed):
1874             return True
1875         else: 
1876             return False
1877
1878     @classmethod
1879     def is_mpk(self, mpk):
1880         try:
1881             int(mpk, 16)
1882             old = True
1883         except:
1884             old = False
1885             
1886         if old:
1887             return len(mpk) == 128
1888         else:
1889             try:
1890                 deserialize_xkey(mpk)
1891                 return True
1892             except:
1893                 return False
1894                 
1895
1896     @classmethod
1897     def from_seed(self, seed, storage):
1898         if is_old_seed(seed):
1899             klass = OldWallet
1900         elif is_new_seed(seed):
1901             klass = NewWallet
1902         w = klass(storage)
1903         return w
1904
1905     @classmethod
1906     def from_mpk(self, mpk, storage):
1907
1908         try:
1909             int(mpk, 16)
1910             old = True
1911         except:
1912             old = False
1913
1914         if old:
1915             w = OldWallet(storage)
1916             w.seed = ''
1917             w.create_watching_only_wallet(mpk)
1918         else:
1919             w = NewWallet(storage)
1920             w.create_watching_only_wallet(mpk)
1921
1922         return w