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