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