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