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