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