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