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