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