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