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