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