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