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