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