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