Merge branch '2.0'
[electrum-nvc.git] / lib / wallet.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 import sys
20 import base64
21 import os
22 import re
23 import hashlib
24 import copy
25 import operator
26 import ast
27 import threading
28 import random
29 import aes
30 import Queue
31 import time
32 import math
33
34 from util import print_msg, print_error, format_satoshis
35 from bitcoin import *
36 from account import *
37 from transaction import Transaction
38 from plugins import run_hook
39
40 COINBASE_MATURITY = 100
41 DUST_THRESHOLD = 5430
42
43 # AES encryption
44 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
45 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
46
47 def pw_encode(s, password):
48     if password:
49         secret = Hash(password)
50         return EncodeAES(secret, s)
51     else:
52         return s
53
54 def pw_decode(s, password):
55     if password is not None:
56         secret = Hash(password)
57         try:
58             d = DecodeAES(secret, s)
59         except Exception:
60             raise Exception('Invalid password')
61         return d
62     else:
63         return s
64
65
66
67
68
69 from version import *
70
71
72 class WalletStorage:
73
74     def __init__(self, config):
75         self.lock = threading.Lock()
76         self.config = config
77         self.data = {}
78         self.file_exists = False
79         self.path = self.init_path(config)
80         print_error( "wallet path", self.path )
81         if self.path:
82             self.read(self.path)
83
84
85     def init_path(self, config):
86         """Set the path of the wallet."""
87
88         # command line -w option
89         path = config.get('wallet_path')
90         if path:
91             return path
92
93         # path in config file
94         path = config.get('default_wallet_path')
95         if path:
96             return path
97
98         # default path
99         dirpath = os.path.join(config.path, "wallets")
100         if not os.path.exists(dirpath):
101             os.mkdir(dirpath)
102
103         new_path = os.path.join(config.path, "wallets", "default_wallet")
104
105         # default path in pre 1.9 versions
106         old_path = os.path.join(config.path, "electrum.dat")
107         if os.path.exists(old_path) and not os.path.exists(new_path):
108             os.rename(old_path, new_path)
109
110         return new_path
111
112
113     def read(self, path):
114         """Read the contents of the wallet file."""
115         try:
116             with open(self.path, "r") as f:
117                 data = f.read()
118         except IOError:
119             return
120         try:
121             d = ast.literal_eval( data )  #parse raw data from reading wallet file
122         except Exception:
123             raise IOError("Cannot read wallet file.")
124
125         self.data = d
126         self.file_exists = True
127
128
129     def get(self, key, default=None):
130         v = self.data.get(key)
131         if v is None: 
132             v = default
133         return v
134
135     def put(self, key, value, save = True):
136
137         with self.lock:
138             if value is not None:
139                 self.data[key] = value
140             else:
141                 self.data.pop(key)
142             if save: 
143                 self.write()
144
145     def write(self):
146         s = repr(self.data)
147         f = open(self.path,"w")
148         f.write( s )
149         f.close()
150         if 'ANDROID_DATA' not in os.environ:
151             import stat
152             os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
153
154
155
156     
157
158 class NewWallet:
159
160     def __init__(self, storage):
161
162         self.storage = storage
163         self.electrum_version = ELECTRUM_VERSION
164         self.gap_limit_for_change = 3 # constant
165
166         # saved fields
167         self.seed_version          = storage.get('seed_version', NEW_SEED_VERSION)
168
169         self.gap_limit             = storage.get('gap_limit', 5)
170         self.use_change            = storage.get('use_change',True)
171         self.use_encryption        = storage.get('use_encryption', False)
172         self.seed                  = storage.get('seed', '')               # encrypted
173         self.labels                = storage.get('labels', {})
174         self.frozen_addresses      = storage.get('frozen_addresses',[])
175         self.addressbook           = storage.get('contacts', [])
176
177         self.imported_keys         = storage.get('imported_keys',{})
178         self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
179
180         self.fee                   = int(storage.get('fee_per_kb',20000))
181
182         self.master_public_keys = storage.get('master_public_keys',{})
183         self.master_private_keys = storage.get('master_private_keys', {})
184
185         self.next_addresses = storage.get('next_addresses',{})
186
187
188         # This attribute is set when wallet.start_threads is called.
189         self.synchronizer = None
190
191         self.load_accounts()
192
193         self.transactions = {}
194         tx_list = self.storage.get('transactions',{})
195         for k,v in tx_list.items():
196             try:
197                 tx = Transaction(v)
198             except Exception:
199                 print_msg("Warning: Cannot deserialize transactions. skipping")
200                 continue
201
202             self.add_extra_addresses(tx)
203             self.transactions[k] = tx
204
205         for h,tx in self.transactions.items():
206             if not self.check_new_tx(h, tx):
207                 print_error("removing unreferenced tx", h)
208                 self.transactions.pop(h)
209
210
211         # not saved
212         self.prevout_values = {}     # my own transaction outputs
213         self.spent_outputs = []
214
215         # spv
216         self.verifier = None
217
218         # there is a difference between wallet.up_to_date and interface.is_up_to_date()
219         # interface.is_up_to_date() returns true when all requests have been answered and processed
220         # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
221         
222         self.up_to_date = False
223         self.lock = threading.Lock()
224         self.transaction_lock = threading.Lock()
225         self.tx_event = threading.Event()
226
227         for tx_hash, tx in self.transactions.items():
228             self.update_tx_outputs(tx_hash)
229
230
231     def add_extra_addresses(self, tx):
232         h = tx.hash()
233         # find the address corresponding to pay-to-pubkey inputs
234         tx.add_extra_addresses(self.transactions)
235         for o in tx.d.get('outputs'):
236             if o.get('is_pubkey'):
237                 for tx2 in self.transactions.values():
238                     tx2.add_extra_addresses({h:tx})
239
240             
241
242
243     def set_up_to_date(self,b):
244         with self.lock: self.up_to_date = b
245
246
247     def is_up_to_date(self):
248         with self.lock: return self.up_to_date
249
250
251     def update(self):
252         self.up_to_date = False
253         while not self.is_up_to_date(): 
254             time.sleep(0.1)
255
256
257     def import_key(self, sec, password):
258         # check password
259         seed = self.get_seed(password)
260         try:
261             address = address_from_private_key(sec)
262         except Exception:
263             raise Exception('Invalid private key')
264
265         if self.is_mine(address):
266             raise Exception('Address already in wallet')
267         
268         # store the originally requested keypair into the imported keys table
269         self.imported_keys[address] = pw_encode(sec, password )
270         self.storage.put('imported_keys', self.imported_keys, True)
271         if self.synchronizer:
272             self.synchronizer.subscribe_to_addresses([address])
273         return address
274         
275     def delete_imported_key(self, addr):
276         if addr in self.imported_keys:
277             self.imported_keys.pop(addr)
278             self.storage.put('imported_keys', self.imported_keys, True)
279
280
281     def make_seed(self):
282         import mnemonic, ecdsa
283         entropy = ecdsa.util.randrange( pow(2,160) )
284         nonce = 0
285         while True:
286             ss = "%040x"%(entropy+nonce)
287             s = hashlib.sha256(ss.decode('hex')).digest().encode('hex')
288             # we keep only 13 words, that's approximately 139 bits of entropy
289             words = mnemonic.mn_encode(s)[0:13] 
290             seed = ' '.join(words)
291             if is_seed(seed):
292                 break  # this will remove 8 bits of entropy
293             nonce += 1
294
295         return seed
296
297
298     def init_seed(self, seed):
299         import mnemonic, unicodedata
300         
301         if self.seed: 
302             raise Exception("a seed exists")
303
304         self.seed_version = NEW_SEED_VERSION
305
306         if not seed:
307             self.seed = self.make_seed()
308             return
309
310         self.seed = unicodedata.normalize('NFC', unicode(seed.strip()))
311
312             
313
314     def save_seed(self, password):
315         if password: 
316             self.seed = pw_encode( self.seed, password)
317             self.use_encryption = True
318         self.storage.put('seed', self.seed, True)
319         self.storage.put('seed_version', self.seed_version, True)
320         self.storage.put('use_encryption', self.use_encryption,True)
321         self.create_accounts(password)
322
323
324     def create_watching_only_wallet(self, K0, c0):
325         cK0 = "" #FIXME
326         self.master_public_keys = {
327             "m/0'/": (c0, K0, cK0),
328             }
329         self.storage.put('master_public_keys', self.master_public_keys, True)
330         self.storage.put('seed_version', self.seed_version, True)
331         self.create_account('1of1','Main account')
332
333
334     def create_accounts(self, password):
335         seed = pw_decode(self.seed, password)
336         # create default account
337         self.create_master_keys('1of1', password)
338         self.create_account('1of1','Main account')
339
340
341     def create_master_keys(self, account_type, password):
342         master_k, master_c, master_K, master_cK = bip32_init(self.get_seed(password))
343         if account_type == '1of1':
344             k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
345             self.master_public_keys["m/0'/"] = (c0, K0, cK0)
346             self.master_private_keys["m/0'/"] = pw_encode(k0, password)
347         elif account_type == '2of2':
348             k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
349             k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
350             self.master_public_keys["m/1'/"] = (c1, K1, cK1)
351             self.master_public_keys["m/2'/"] = (c2, K2, cK2)
352             self.master_private_keys["m/1'/"] = pw_encode(k1, password)
353             self.master_private_keys["m/2'/"] = pw_encode(k2, password)
354         elif account_type == '2of3':
355             k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
356             k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
357             k5, c5, K5, cK5 = bip32_private_derivation(master_k, master_c, "m/", "m/5'/")
358             self.master_public_keys["m/3'/"] = (c3, K3, cK3)
359             self.master_public_keys["m/4'/"] = (c4, K4, cK4)
360             self.master_public_keys["m/5'/"] = (c5, K5, cK5)
361             self.master_private_keys["m/3'/"] = pw_encode(k3, password)
362             self.master_private_keys["m/4'/"] = pw_encode(k4, password)
363             self.master_private_keys["m/5'/"] = pw_encode(k5, password)
364
365         self.storage.put('master_public_keys', self.master_public_keys, True)
366         self.storage.put('master_private_keys', self.master_private_keys, True)
367
368     def has_master_public_keys(self, account_type):
369         if account_type == '1of1':
370             return "m/0'/" in self.master_public_keys
371         elif account_type == '2of2':
372             return set(["m/1'/", "m/2'/"]) <= set(self.master_public_keys.keys())
373         elif account_type == '2of3':
374             return set(["m/3'/", "m/4'/", "m/5'/"]) <= set(self.master_public_keys.keys())
375
376     def find_root_by_master_key(self, c, K):
377         for key, v in self.master_public_keys.items():
378             if key == "m/":continue
379             cc, KK, _ = v
380             if (c == cc) and (K == KK):
381                 return key
382
383     def deseed_root(self, seed, password):
384         # for safety, we ask the user to enter their seed
385         assert seed == self.get_seed(password)
386         self.seed = ''
387         self.storage.put('seed', '', True)
388
389
390     def deseed_branch(self, k):
391         # check that parent has no seed
392         # assert self.seed == ''
393         self.master_private_keys.pop(k)
394         self.storage.put('master_private_keys', self.master_private_keys, True)
395
396
397     def is_watching_only(self):
398         return (self.seed == '') and (self.master_private_keys == {})
399
400
401
402     def account_id(self, account_type, i):
403         if account_type == '1of1':
404             return "m/0'/%d"%i
405         elif account_type == '2of2':
406             return "m/1'/%d & m/2'/%d"%(i,i)
407         elif account_type == '2of3':
408             return "m/3'/%d & m/4'/%d & m/5'/%d"%(i,i,i)
409         else:
410             raise Exception('unknown account type')
411
412
413     def num_accounts(self, account_type):
414         keys = self.accounts.keys()
415         i = 0
416         while True:
417             account_id = self.account_id(account_type, i)
418             if account_id not in keys: break
419             i += 1
420         return i
421
422
423     def new_account_address(self, account_type = '1of1'):
424         i = self.num_accounts(account_type)
425         k = self.account_id(account_type,i)
426
427         addr = self.next_addresses.get(k)
428         if not addr: 
429             account_id, account = self.next_account(account_type)
430             addr = account.first_address()
431             self.next_addresses[k] = addr
432             self.storage.put('next_addresses',self.next_addresses)
433
434         return k, addr
435
436
437     def next_account(self, account_type = '1of1'):
438
439         i = self.num_accounts(account_type)
440         account_id = self.account_id(account_type,i)
441
442         if account_type is '1of1':
443             master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
444             c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
445             account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
446
447         elif account_type == '2of2':
448             master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
449             c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
450             master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
451             c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
452             account = BIP32_Account_2of2({ 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
453
454         elif account_type == '2of3':
455             master_c3, master_K3, _ = self.master_public_keys["m/3'/"]
456             c3, K3, cK3 = bip32_public_derivation(master_c3.decode('hex'), master_K3.decode('hex'), "m/3'/", "m/3'/%d"%i)
457             master_c4, master_K4, _ = self.master_public_keys["m/4'/"]
458             c4, K4, cK4 = bip32_public_derivation(master_c4.decode('hex'), master_K4.decode('hex'), "m/4'/", "m/4'/%d"%i)
459             master_c5, master_K5, _ = self.master_public_keys["m/5'/"]
460             c5, K5, cK5 = bip32_public_derivation(master_c5.decode('hex'), master_K5.decode('hex'), "m/5'/", "m/5'/%d"%i)
461             account = BIP32_Account_2of3({ 'c':c3, 'K':K3, 'cK':cK3, 'c2':c4, 'K2':K4, 'cK2':cK4, 'c3':c5, 'K3':K5, 'cK3':cK5 })
462
463         return account_id, account
464
465
466     def set_label(self, name, text = None):
467         changed = False
468         old_text = self.labels.get(name)
469         if text:
470             if old_text != text:
471                 self.labels[name] = text
472                 changed = True
473         else:
474             if old_text:
475                 self.labels.pop(name)
476                 changed = True
477
478         if changed:
479             self.storage.put('labels', self.labels, True)
480
481         run_hook('set_label', name, text, changed)
482         return changed
483
484
485
486     def create_account(self, account_type = '1of1', name = None):
487         k, account = self.next_account(account_type)
488         if k in self.pending_accounts:
489             self.pending_accounts.pop(k)
490             self.storage.put('pending_accounts', self.pending_accounts)
491
492         self.accounts[k] = account
493         self.save_accounts()
494         if name:
495             self.set_label(k, name)
496
497
498     def save_accounts(self):
499         d = {}
500         for k, v in self.accounts.items():
501             d[k] = v.dump()
502         self.storage.put('accounts', d, True)
503
504     
505
506     def load_accounts(self):
507         d = self.storage.get('accounts', {})
508         self.accounts = {}
509         for k, v in d.items():
510             if k == 0:
511                 v['mpk'] = self.storage.get('master_public_key')
512                 self.accounts[k] = OldAccount(v)
513             elif '&' in k:
514                 self.accounts[k] = BIP32_Account_2of2(v)
515             else:
516                 self.accounts[k] = BIP32_Account(v)
517
518         self.pending_accounts = self.storage.get('pending_accounts',{})
519
520
521     def delete_pending_account(self, k):
522         self.pending_accounts.pop(k)
523         self.storage.put('pending_accounts', self.pending_accounts)
524
525     def account_is_pending(self, k):
526         return k in self.pending_accounts
527
528     def create_pending_account(self, acct_type, name):
529         k, addr = self.new_account_address(acct_type)
530         self.set_label(k, name)
531         self.pending_accounts[k] = addr
532         self.storage.put('pending_accounts', self.pending_accounts)
533
534     def get_pending_accounts(self):
535         return self.pending_accounts.items()
536
537
538     def addresses(self, include_change = True, _next=True):
539         o = self.get_account_addresses(-1, include_change)
540         for a in self.accounts.keys():
541             o += self.get_account_addresses(a, include_change)
542
543         if _next:
544             for addr in self.next_addresses.values():
545                 if addr not in o:
546                     o += [addr]
547         return o
548
549
550     def is_mine(self, address):
551         return address in self.addresses(True)
552
553
554     def is_change(self, address):
555         if not self.is_mine(address): return False
556         if address in self.imported_keys.keys(): return False
557         acct, s = self.get_address_index(address)
558         if s is None: return False
559         return s[0] == 1
560
561     def get_master_public_key(self):
562         c, K, cK = self.storage.get("master_public_keys")["m/0'/"]
563         return repr((c, K))
564
565     def get_master_private_key(self, account, password):
566         k = self.master_private_keys.get(account)
567         if not k: return
568         master_k = pw_decode( k, password)
569         master_c, master_K, master_Kc = self.master_public_keys[account]
570         try:
571             K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
572             assert K.encode('hex') == master_K
573         except Exception:
574             raise Exception("Invalid password")
575         return master_k
576
577
578     def get_address_index(self, address):
579         if address in self.imported_keys.keys():
580             return -1, None
581
582         for account in self.accounts.keys():
583             for for_change in [0,1]:
584                 addresses = self.accounts[account].get_addresses(for_change)
585                 for addr in addresses:
586                     if address == addr:
587                         return account, (for_change, addresses.index(addr))
588
589         for k,v in self.next_addresses.items():
590             if v == address:
591                 return k, (0,0)
592
593         raise Exception("Address not found", address)
594
595
596     def get_roots(self, account):
597         roots = []
598         for a in account.split('&'):
599             s = a.strip()
600             m = re.match("(m/\d+'/)(\d+)", s)
601             roots.append( m.group(1) )
602         return roots
603
604     def is_seeded(self, account):
605         if type(account) is int:
606             return self.seed is not None
607
608         for root in self.get_roots(account):
609             if root not in self.master_private_keys.keys(): 
610                 return False
611         return True
612
613     def rebase_sequence(self, account, sequence):
614         c, i = sequence
615         dd = []
616         for a in account.split('&'):
617             s = a.strip()
618             m = re.match("(m/\d+'/)(\d+)", s)
619             root = m.group(1)
620             num = int(m.group(2))
621             dd.append( (root, [num,c,i] ) )
622         return dd
623         
624
625     def get_keyID(self, account, sequence):
626         if account == 0:
627             a, b = sequence
628             mpk = self.storage.get('master_public_key')
629             return 'old(%s,%d,%d)'%(mpk,a,b)
630
631         rs = self.rebase_sequence(account, sequence)
632         dd = []
633         for root, public_sequence in rs:
634             c, K, _ = self.master_public_keys[root]
635             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
636             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
637         return '&'.join(dd)
638
639
640     def get_seed(self, password):
641         s = pw_decode(self.seed, password)
642         seed = mnemonic_to_seed(s,'').encode('hex')
643         return seed
644
645
646     def get_mnemonic(self, password):
647         return pw_decode(self.seed, password)
648         
649
650     def get_private_key(self, address, password):
651         if self.is_watching_only():
652             return []
653
654         # first check the provided password
655         seed = self.get_seed(password)
656         
657         out = []
658         if address in self.imported_keys.keys():
659             out.append( pw_decode( self.imported_keys[address], password ) )
660         else:
661             account, sequence = self.get_address_index(address)
662             if account == 0:
663                 pk = self.accounts[account].get_private_key(seed, sequence)
664                 out.append(pk)
665                 return out
666
667             # assert address == self.accounts[account].get_address(*sequence)
668             rs = self.rebase_sequence( account, sequence)
669             for root, public_sequence in rs:
670
671                 if root not in self.master_private_keys.keys(): continue
672                 master_k = self.get_master_private_key(root, password)
673                 master_c, _, _ = self.master_public_keys[root]
674                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
675                 out.append(pk)
676                     
677         return out
678
679
680     def add_keypairs_from_wallet(self, tx, keypairs, password):
681         for txin in tx.inputs:
682             address = txin['address']
683             if not self.is_mine(address):
684                 continue
685             private_keys = self.get_private_key(address, password)
686             for sec in private_keys:
687                 pubkey = public_key_from_private_key(sec)
688                 keypairs[ pubkey ] = sec
689                 if address in self.imported_keys.keys():
690                     txin['redeemPubkey'] = pubkey
691
692
693     def add_keypairs_from_KeyID(self, tx, keypairs, password):
694         # first check the provided password
695         seed = self.get_seed(password)
696
697         for txin in tx.inputs:
698             keyid = txin.get('KeyID')
699             if keyid:
700                 roots = []
701                 for s in keyid.split('&'):
702                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
703                     if not m: continue
704                     c = m.group(1)
705                     K = m.group(2)
706                     sequence = m.group(3)
707                     root = self.find_root_by_master_key(c,K)
708                     if not root: continue
709                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
710                     root = root + '%d'%sequence[0]
711                     sequence = sequence[1:]
712                     roots.append((root,sequence)) 
713
714                 account_id = " & ".join( map(lambda x:x[0], roots) )
715                 account = self.accounts.get(account_id)
716                 if not account: continue
717                 addr = account.get_address(*sequence)
718                 txin['address'] = addr # fixme: side effect
719                 pk = self.get_private_key(addr, password)
720                 for sec in pk:
721                     pubkey = public_key_from_private_key(sec)
722                     keypairs[pubkey] = sec
723
724
725
726     def signrawtransaction(self, tx, input_info, private_keys, password):
727
728         # check that the password is correct
729         seed = self.get_seed(password)
730
731         # add input info
732         tx.add_input_info(input_info)
733
734         # add redeem script for coins that are in the wallet
735         # FIXME: add redeemPubkey too!
736         unspent_coins = self.get_unspent_coins()
737         for txin in tx.inputs:
738             for item in unspent_coins:
739                 if txin['prevout_hash'] == item['prevout_hash'] and txin['prevout_n'] == item['prevout_n']:
740                     print_error( "tx input is in unspent coins" )
741                     txin['scriptPubKey'] = item['scriptPubKey']
742                     account, sequence = self.get_address_index(item['address'])
743                     if account != -1:
744                         txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
745                         print_error("added redeemScript", txin['redeemScript'])
746                     break
747
748
749         # build a list of public/private keys
750         keypairs = {}
751
752         # add private keys from parameter
753         for sec in private_keys:
754             pubkey = public_key_from_private_key(sec)
755             keypairs[ pubkey ] = sec
756
757         # add private_keys from KeyID
758         self.add_keypairs_from_KeyID(tx, keypairs, password)
759
760         # add private keys from wallet
761         self.add_keypairs_from_wallet(tx, keypairs, password)
762         self.sign_transaction(tx, keypairs)
763
764
765     def sign_message(self, address, message, password):
766         keys = self.get_private_key(address, password)
767         assert len(keys) == 1
768         sec = keys[0]
769         key = regenerate_key(sec)
770         compressed = is_compressed(sec)
771         return key.sign_message(message, compressed, address)
772
773
774     def change_gap_limit(self, value):
775         if value >= self.gap_limit:
776             self.gap_limit = value
777             self.storage.put('gap_limit', self.gap_limit, True)
778             #self.interface.poke('synchronizer')
779             return True
780
781         elif value >= self.min_acceptable_gap():
782             for key, account in self.accounts.items():
783                 addresses = account[0]
784                 k = self.num_unused_trailing_addresses(addresses)
785                 n = len(addresses) - k + value
786                 addresses = addresses[0:n]
787                 self.accounts[key][0] = addresses
788
789             self.gap_limit = value
790             self.storage.put('gap_limit', self.gap_limit, True)
791             self.save_accounts()
792             return True
793         else:
794             return False
795
796     def num_unused_trailing_addresses(self, addresses):
797         k = 0
798         for a in addresses[::-1]:
799             if self.history.get(a):break
800             k = k + 1
801         return k
802
803     def min_acceptable_gap(self):
804         # fixme: this assumes wallet is synchronized
805         n = 0
806         nmax = 0
807
808         for account in self.accounts.values():
809             addresses = account.get_addresses(0)
810             k = self.num_unused_trailing_addresses(addresses)
811             for a in addresses[0:-k]:
812                 if self.history.get(a):
813                     n = 0
814                 else:
815                     n += 1
816                     if n > nmax: nmax = n
817         return nmax + 1
818
819
820     def address_is_old(self, address):
821         age = -1
822         h = self.history.get(address, [])
823         if h == ['*']:
824             return True
825         for tx_hash, tx_height in h:
826             if tx_height == 0:
827                 tx_age = 0
828             else: 
829                 tx_age = self.verifier.blockchain.height() - tx_height + 1
830             if tx_age > age:
831                 age = tx_age
832         return age > 2
833
834
835     def synchronize_sequence(self, account, for_change):
836         limit = self.gap_limit_for_change if for_change else self.gap_limit
837         new_addresses = []
838         while True:
839             addresses = account.get_addresses(for_change)
840             if len(addresses) < limit:
841                 address = account.create_new_address(for_change)
842                 self.history[address] = []
843                 new_addresses.append( address )
844                 continue
845
846             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
847                 break
848             else:
849                 address = account.create_new_address(for_change)
850                 self.history[address] = []
851                 new_addresses.append( address )
852
853         return new_addresses
854         
855
856
857     def create_pending_accounts(self):
858         for account_type in ['1of1','2of2','2of3']:
859             if not self.has_master_public_keys(account_type):
860                 continue
861             k, a = self.new_account_address(account_type)
862             if self.address_is_old(a):
863                 print_error( "creating account", a )
864                 self.create_account(account_type)
865                 self.next_addresses.pop(k)
866
867
868     def synchronize_account(self, account):
869         new = []
870         new += self.synchronize_sequence(account, 0)
871         new += self.synchronize_sequence(account, 1)
872         return new
873
874
875     def synchronize(self):
876         if self.master_public_keys:
877             self.create_pending_accounts()
878         new = []
879         for account in self.accounts.values():
880             new += self.synchronize_account(account)
881         if new:
882             self.save_accounts()
883             self.storage.put('addr_history', self.history, True)
884         return new
885
886
887     def is_found(self):
888         return self.history.values() != [[]] * len(self.history) 
889
890
891     def add_contact(self, address, label=None):
892         self.addressbook.append(address)
893         self.storage.put('contacts', self.addressbook, True)
894         if label:  
895             self.set_label(address, label)
896
897
898     def delete_contact(self, addr):
899         if addr in self.addressbook:
900             self.addressbook.remove(addr)
901             self.storage.put('addressbook', self.addressbook, True)
902
903
904     def fill_addressbook(self):
905         for tx_hash, tx in self.transactions.items():
906             is_relevant, is_send, _, _ = self.get_tx_value(tx)
907             if is_send:
908                 for addr, v in tx.outputs:
909                     if not self.is_mine(addr) and addr not in self.addressbook:
910                         self.addressbook.append(addr)
911         # redo labels
912         # self.update_tx_labels()
913
914     def get_num_tx(self, address):
915         n = 0 
916         for tx in self.transactions.values():
917             if address in map(lambda x:x[0], tx.outputs): n += 1
918         return n
919
920
921     def get_address_flags(self, addr):
922         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
923         flags += "F" if addr in self.frozen_addresses else "-"
924         return flags
925         
926
927     def get_tx_value(self, tx, account=None):
928         domain = self.get_account_addresses(account)
929         return tx.get_value(domain, self.prevout_values)
930
931     
932     def update_tx_outputs(self, tx_hash):
933         tx = self.transactions.get(tx_hash)
934
935         for i, (addr, value) in enumerate(tx.outputs):
936             key = tx_hash+ ':%d'%i
937             self.prevout_values[key] = value
938
939         for item in tx.inputs:
940             if self.is_mine(item.get('address')):
941                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
942                 self.spent_outputs.append(key)
943
944
945     def get_addr_balance(self, address):
946         assert self.is_mine(address)
947         h = self.history.get(address,[])
948         if h == ['*']: return 0,0
949         c = u = 0
950         received_coins = []   # list of coins received at address
951
952         for tx_hash, tx_height in h:
953             tx = self.transactions.get(tx_hash)
954             if not tx: continue
955
956             for i, (addr, value) in enumerate(tx.outputs):
957                 if addr == address:
958                     key = tx_hash + ':%d'%i
959                     received_coins.append(key)
960
961         for tx_hash, tx_height in h:
962             tx = self.transactions.get(tx_hash)
963             if not tx: continue
964             v = 0
965
966             for item in tx.inputs:
967                 addr = item.get('address')
968                 if addr == address:
969                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
970                     value = self.prevout_values.get( key )
971                     if key in received_coins: 
972                         v -= value
973
974             for i, (addr, value) in enumerate(tx.outputs):
975                 key = tx_hash + ':%d'%i
976                 if addr == address:
977                     v += value
978
979             if tx_height:
980                 c += v
981             else:
982                 u += v
983         return c, u
984
985
986     def get_account_name(self, k):
987         default = "Unnamed account"
988         m = re.match("m/0'/(\d+)", k)
989         if m:
990             num = m.group(1)
991             if num == '0':
992                 default = "Main account"
993             else:
994                 default = "Account %s"%num
995                     
996         m = re.match("m/1'/(\d+) & m/2'/(\d+)", k)
997         if m:
998             num = m.group(1)
999             default = "2of2 account %s"%num
1000         name = self.labels.get(k, default)
1001         return name
1002
1003
1004     def get_account_names(self):
1005         accounts = {}
1006         for k, account in self.accounts.items():
1007             accounts[k] = self.get_account_name(k)
1008         if self.imported_keys:
1009             accounts[-1] = 'Imported keys'
1010         return accounts
1011
1012
1013     def get_account_addresses(self, a, include_change=True):
1014         if a is None:
1015             o = self.addresses(True)
1016         elif a == -1:
1017             o = self.imported_keys.keys()
1018         else:
1019             ac = self.accounts[a]
1020             o = ac.get_addresses(0)
1021             if include_change: o += ac.get_addresses(1)
1022         return o
1023
1024     def get_imported_balance(self):
1025         return self.get_balance(self.imported_keys.keys())
1026
1027     def get_account_balance(self, account):
1028         return self.get_balance(self.get_account_addresses(account))
1029
1030     def get_frozen_balance(self):
1031         return self.get_balance(self.frozen_addresses)
1032         
1033     def get_balance(self, domain=None):
1034         if domain is None: domain = self.addresses(True)
1035         cc = uu = 0
1036         for addr in domain:
1037             c, u = self.get_addr_balance(addr)
1038             cc += c
1039             uu += u
1040         return cc, uu
1041
1042
1043     def get_unspent_coins(self, domain=None):
1044         coins = []
1045         if domain is None: domain = self.addresses(True)
1046         for addr in domain:
1047             h = self.history.get(addr, [])
1048             if h == ['*']: continue
1049             for tx_hash, tx_height in h:
1050                 tx = self.transactions.get(tx_hash)
1051                 if tx is None: raise Exception("Wallet not synchronized")
1052                 is_coinbase = tx.inputs[0].get('prevout_hash') == '0'*64
1053                 for o in tx.d.get('outputs'):
1054                     output = o.copy()
1055                     if output.get('address') != addr: continue
1056                     key = tx_hash + ":%d" % output.get('prevout_n')
1057                     if key in self.spent_outputs: continue
1058                     output['prevout_hash'] = tx_hash
1059                     output['height'] = tx_height
1060                     output['coinbase'] = is_coinbase
1061                     coins.append((tx_height, output))
1062
1063         # sort by age
1064         if coins:
1065             coins = sorted(coins)
1066             if coins[-1][0] != 0:
1067                 while coins[0][0] == 0: 
1068                     coins = coins[1:] + [ coins[0] ]
1069         return [x[1] for x in coins]
1070
1071
1072     def choose_tx_inputs( self, amount, fixed_fee, domain = None ):
1073         """ todo: minimize tx size """
1074         total = 0
1075         fee = self.fee if fixed_fee is None else fixed_fee
1076         if domain is None:
1077             domain = self.addresses(True)
1078
1079         for i in self.frozen_addresses:
1080             if i in domain: domain.remove(i)
1081
1082         coins = self.get_unspent_coins(domain)
1083         inputs = []
1084
1085         for item in coins:
1086             if item.get('coinbase') and item.get('height') + COINBASE_MATURITY > self.network.blockchain.height:
1087                 continue
1088             addr = item.get('address')
1089             v = item.get('value')
1090             total += v
1091             inputs.append(item)
1092             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
1093             if total >= amount + fee: break
1094         else:
1095             inputs = []
1096
1097         return inputs, total, fee
1098
1099
1100     def set_fee(self, fee):
1101         if self.fee != fee:
1102             self.fee = fee
1103             self.storage.put('fee_per_kb', self.fee, True)
1104         
1105     def estimated_fee(self, inputs):
1106         estimated_size =  len(inputs) * 180 + 80    # this assumes non-compressed keys
1107         fee = self.fee * int(math.ceil(estimated_size/1000.))
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