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