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