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