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