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