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