fix: synchronize generated duplicated addresses
[electrum-nvc.git] / lib / wallet.py
1 #!/usr/bin/env python
2 #
3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2011 thomasv@gitorious
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
19 import sys
20 import base64
21 import os
22 import re
23 import hashlib
24 import copy
25 import operator
26 import ast
27 import threading
28 import random
29 import aes
30 import Queue
31 import time
32
33 from util import print_msg, print_error, user_dir, format_satoshis
34 from bitcoin import *
35 from account import *
36
37 # AES encryption
38 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
39 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
40
41 def pw_encode(s, password):
42     if password:
43         secret = Hash(password)
44         return EncodeAES(secret, s)
45     else:
46         return s
47
48 def pw_decode(s, password):
49     if password is not None:
50         secret = Hash(password)
51         try:
52             d = DecodeAES(secret, s)
53         except:
54             raise BaseException('Invalid password')
55         return d
56     else:
57         return s
58
59
60
61
62
63 from version import ELECTRUM_VERSION, SEED_VERSION
64
65
66 class WalletStorage:
67
68     def __init__(self, config):
69         self.data = {}
70         self.file_exists = False
71         self.init_path(config)
72         print_error( "wallet path", self.path )
73         if self.path:
74             self.read(self.path)
75
76
77     def init_path(self, config):
78         """Set the path of the wallet."""
79
80         path = config.get('wallet_path')
81         if not path:
82             path = config.get('default_wallet_path')
83         if path is not None:
84             self.path = path
85             return
86
87         # Look for wallet file in the default data directory.
88         # Make wallet directory if it does not yet exist.
89         if not os.path.exists(self.user_dir):
90             os.mkdir(self.user_dir)
91
92         self.path = os.path.join(self.user_dir, "electrum.dat")
93
94
95     def read(self, path):
96         """Read the contents of the wallet file."""
97         try:
98             with open(self.path, "r") as f:
99                 data = f.read()
100         except IOError:
101             return
102         try:
103             d = ast.literal_eval( data )  #parse raw data from reading wallet file
104         except:
105             raise IOError("Cannot read wallet file.")
106
107         self.data = d
108         self.file_exists = True
109
110
111     def get(self, key, default=None):
112         return self.data.get(key, default)
113
114     def put(self, key, value, save = True):
115
116         if self.data.get(key) is not None:
117             self.data[key] = value
118         else:
119             # add key to wallet config
120             self.data[key] = value
121
122         if save: 
123             self.write()
124
125
126     def write(self):
127         s = repr(self.data)
128         f = open(self.path,"w")
129         f.write( s )
130         f.close()
131         if self.get('gui') != 'android':
132             import stat
133             os.chmod(self.path,stat.S_IREAD | stat.S_IWRITE)
134
135
136 class Wallet:
137
138     def __init__(self, storage):
139
140         self.storage = storage
141         self.electrum_version = ELECTRUM_VERSION
142         self.gap_limit_for_change = 3 # constant
143
144         # saved fields
145         self.seed_version          = storage.get('seed_version', SEED_VERSION)
146
147         self.gap_limit             = storage.get('gap_limit', 5)
148         self.use_change            = storage.get('use_change',True)
149         self.use_encryption        = storage.get('use_encryption', False)
150         self.seed                  = storage.get('seed', '')               # encrypted
151         self.labels                = storage.get('labels', {})
152         self.frozen_addresses      = storage.get('frozen_addresses',[])
153         self.prioritized_addresses = storage.get('prioritized_addresses',[])
154         self.addressbook           = storage.get('contacts', [])
155
156         self.imported_keys         = storage.get('imported_keys',{})
157         self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
158
159         self.fee                   = int(storage.get('fee_per_kb',20000))
160
161         self.master_public_keys = storage.get('master_public_keys',{})
162         self.master_private_keys = storage.get('master_private_keys', {})
163
164         self.first_addresses = storage.get('first_addresses',{})
165
166         #if self.seed_version != SEED_VERSION:
167         #    raise ValueError("This wallet seed is deprecated. Please restore from seed.")
168
169         self.load_accounts()
170
171         self.transactions = {}
172         tx = storage.get('transactions',{})
173         try:
174             for k,v in tx.items(): self.transactions[k] = Transaction(v)
175         except:
176             print_msg("Warning: Cannot deserialize transactions. skipping")
177         
178         # not saved
179         self.prevout_values = {}     # my own transaction outputs
180         self.spent_outputs = []
181
182         # spv
183         self.verifier = None
184
185         # there is a difference between wallet.up_to_date and interface.is_up_to_date()
186         # interface.is_up_to_date() returns true when all requests have been answered and processed
187         # wallet.up_to_date is true when the wallet is synchronized (stronger requirement)
188         
189         self.up_to_date = False
190         self.lock = threading.Lock()
191         self.transaction_lock = threading.Lock()
192         self.tx_event = threading.Event()
193
194         for tx_hash, tx in self.transactions.items():
195             if self.check_new_tx(tx_hash, tx):
196                 self.update_tx_outputs(tx_hash)
197             else:
198                 print_error("unreferenced tx", tx_hash)
199                 self.transactions.pop(tx_hash)
200
201
202     def set_up_to_date(self,b):
203         with self.lock: self.up_to_date = b
204
205     def is_up_to_date(self):
206         with self.lock: return self.up_to_date
207
208     def update(self):
209         self.up_to_date = False
210         self.interface.poke('synchronizer')
211         while not self.is_up_to_date(): time.sleep(0.1)
212
213     def import_key(self, sec, password):
214         # check password
215         seed = self.decode_seed(password)
216         try:
217             address = address_from_private_key(sec)
218         except:
219             raise BaseException('Invalid private key')
220
221         if self.is_mine(address):
222             raise BaseException('Address already in wallet')
223         
224         # store the originally requested keypair into the imported keys table
225         self.imported_keys[address] = pw_encode(sec, password )
226         self.storage.put('imported_keys', self.imported_keys, True)
227         return address
228         
229     def delete_imported_key(self, addr):
230         if addr in self.imported_keys:
231             self.imported_keys.pop(addr)
232             self.storage.put('imported_keys', self.imported_keys, True)
233
234
235     def init_seed(self, seed):
236         if self.seed: raise BaseException("a seed exists")
237         if not seed: 
238             seed = random_seed(128)
239         self.seed = seed
240
241
242     def save_seed(self):
243         self.storage.put('seed', self.seed, True)
244         self.storage.put('seed_version', self.seed_version, True)
245
246         master_k, master_c, master_K, master_cK = bip32_init(self.seed)
247         
248         # normal accounts
249         k0, c0, K0, cK0 = bip32_private_derivation(master_k, master_c, "m/", "m/0'/")
250         # p2sh 2of2
251         k1, c1, K1, cK1 = bip32_private_derivation(master_k, master_c, "m/", "m/1'/")
252         k2, c2, K2, cK2 = bip32_private_derivation(master_k, master_c, "m/", "m/2'/")
253         # p2sh 2of3
254         k3, c3, K3, cK3 = bip32_private_derivation(master_k, master_c, "m/", "m/3'/")
255         k4, c4, K4, cK4 = bip32_private_derivation(master_k, master_c, "m/", "m/4'/")
256         k5, c5, K5, cK5 = bip32_private_derivation(master_k, master_c, "m/", "m/5'/")
257
258         self.master_public_keys = {
259             "m/0'/": (c0, K0, cK0),
260             "m/1'/": (c1, K1, cK1),
261             "m/2'/": (c2, K2, cK2),
262             "m/3'/": (c3, K3, cK3),
263             "m/4'/": (c4, K4, cK4),
264             "m/5'/": (c5, K5, cK5)
265             }
266         
267         self.master_private_keys = {
268             "m/0'/": k0,
269             "m/1'/": k1,
270             "m/2'/": k2,
271             "m/3'/": k3,
272             "m/4'/": k4,
273             "m/5'/": k5
274             }
275         
276         self.storage.put('master_public_keys', self.master_public_keys, True)
277         self.storage.put('master_private_keys', self.master_private_keys, True)
278
279         # create default account
280         self.create_account('1','Main account')
281
282
283     def find_root_by_master_key(self, c, K):
284         for key, v in self.master_public_keys.items():
285             if key == "m/":continue
286             cc, KK, _ = v
287             if (c == cc) and (K == KK):
288                 return key
289
290     def deseed_root(self, seed, password):
291         # for safety, we ask the user to enter their seed
292         assert seed == self.decode_seed(password)
293         self.seed = ''
294         self.storage.put('seed', '', True)
295
296
297     def deseed_branch(self, k):
298         # check that parent has no seed
299         assert self.seed == ''
300         self.master_private_keys.pop(k)
301         self.storage.put('master_private_keys', self.master_private_keys, True)
302
303
304     def account_id(self, account_type, i):
305         if account_type == '1':
306             return "m/0'/%d"%i
307         elif account_type == '2of2':
308             return "m/1'/%d & m/2'/%d"%(i,i)
309         elif account_type == '2of3':
310             return "m/3'/%d & m/4'/%d & m/5'/%d"%(i,i,i)
311         else:
312             raise BaseException('unknown account type')
313
314
315     def num_accounts(self, account_type):
316         keys = self.accounts.keys()
317         i = 0
318         while True:
319             account_id = self.account_id(account_type, i)
320             if account_id not in keys: break
321             i += 1
322         return i
323
324
325     def new_account_address(self, account_type = '1'):
326         i = self.num_accounts(account_type)
327         k = self.account_id(account_type,i)
328
329         addr = self.first_addresses.get(k)
330         if not addr: 
331             account_id, account = self.next_account(account_type)
332             addr = account.first_address()
333             self.first_addresses[k] = addr
334             self.storage.put('first_addresses',self.first_addresses)
335
336         return addr
337
338
339     def next_account(self, account_type = '1'):
340
341         i = self.num_accounts(account_type)
342         account_id = self.account_id(account_type,i)
343
344         if account_type is '1':
345             master_c0, master_K0, _ = self.master_public_keys["m/0'/"]
346             c0, K0, cK0 = bip32_public_derivation(master_c0.decode('hex'), master_K0.decode('hex'), "m/0'/", "m/0'/%d"%i)
347             account = BIP32_Account({ 'c':c0, 'K':K0, 'cK':cK0 })
348
349         elif account_type == '2of2':
350             master_c1, master_K1, _ = self.master_public_keys["m/1'/"]
351             c1, K1, cK1 = bip32_public_derivation(master_c1.decode('hex'), master_K1.decode('hex'), "m/1'/", "m/1'/%d"%i)
352             master_c2, master_K2, _ = self.master_public_keys["m/2'/"]
353             c2, K2, cK2 = bip32_public_derivation(master_c2.decode('hex'), master_K2.decode('hex'), "m/2'/", "m/2'/%d"%i)
354             account = BIP32_Account_2of2({ 'c':c1, 'K':K1, 'cK':cK1, 'c2':c2, 'K2':K2, 'cK2':cK2 })
355
356         elif account_type == '2of3':
357             master_c3, master_K3, _ = self.master_public_keys["m/3'/"]
358             c3, K3, cK3 = bip32_public_derivation(master_c3.decode('hex'), master_K3.decode('hex'), "m/3'/", "m/3'/%d"%i)
359             master_c4, master_K4, _ = self.master_public_keys["m/4'/"]
360             c4, K4, cK4 = bip32_public_derivation(master_c4.decode('hex'), master_K4.decode('hex'), "m/4'/", "m/4'/%d"%i)
361             master_c5, master_K5, _ = self.master_public_keys["m/5'/"]
362             c5, K5, cK5 = bip32_public_derivation(master_c5.decode('hex'), master_K5.decode('hex'), "m/5'/", "m/5'/%d"%i)
363             account = BIP32_Account_2of3({ 'c':c3, 'K':K3, 'cK':cK3, 'c2':c4, 'K2':K4, 'cK2':cK4, 'c3':c5, 'K3':K5, 'cK3':cK5 })
364
365         return account_id, account
366
367
368     def create_account(self, account_type = '1', name = None):
369         account_id, account = self.next_account(account_type)
370         self.accounts[account_id] = account
371         self.save_accounts()
372         if name: 
373             self.labels[account_id] = name
374         self.storage.put('labels', self.labels, True)
375
376
377     def create_old_account(self):
378         print self.seed
379         mpk = OldAccount.mpk_from_seed(self.seed)
380         self.storage.put('master_public_key', mpk, True)
381         self.accounts[0] = OldAccount({'mpk':mpk, 0:[], 1:[]})
382         self.save_accounts()
383
384
385     def save_accounts(self):
386         d = {}
387         for k, v in self.accounts.items():
388             d[k] = v.dump()
389         self.storage.put('accounts', d, True)
390
391     
392
393     def load_accounts(self):
394         d = self.storage.get('accounts', {})
395         self.accounts = {}
396         for k, v in d.items():
397             if k == 0:
398                 v['mpk'] = self.storage.get('master_public_key')
399                 self.accounts[k] = OldAccount(v)
400             elif '&' in k:
401                 self.accounts[k] = BIP32_Account_2of2(v)
402             else:
403                 self.accounts[k] = BIP32_Account(v)
404
405
406     def addresses(self, include_change = True):
407         o = self.get_account_addresses(-1, include_change)
408         for a in self.accounts.keys():
409             o += self.get_account_addresses(a, include_change)
410         o += self.first_addresses.values()
411         return o
412
413
414     def is_mine(self, address):
415         return address in self.addresses(True)
416
417
418     def is_change(self, address):
419         if not self.is_mine(address): return False
420         if address in self.imported_keys.keys(): return False
421         acct, s = self.get_address_index(address)
422         return s[0] == 1
423
424     def get_master_public_key(self):
425         raise
426         return self.storage.get("master_public_key")
427
428     def get_master_private_key(self, account, password):
429         master_k = pw_decode( self.master_private_keys[account], password)
430         master_c, master_K, master_Kc = self.master_public_keys[account]
431         try:
432             K, Kc = get_pubkeys_from_secret(master_k.decode('hex'))
433             assert K.encode('hex') == master_K
434         except:
435             raise BaseException("Invalid password")
436         return master_k
437
438
439     def get_address_index(self, address):
440         if address in self.imported_keys.keys():
441             return -1, None
442
443         for account in self.accounts.keys():
444             for for_change in [0,1]:
445                 addresses = self.accounts[account].get_addresses(for_change)
446                 for addr in addresses:
447                     if address == addr:
448                         return account, (for_change, addresses.index(addr))
449
450         raise BaseException("not found")
451
452
453     def rebase_sequence(self, account, sequence):
454         c, i = sequence
455         dd = []
456         for a in account.split('&'):
457             s = a.strip()
458             m = re.match("(m/\d+'/)(\d+)", s)
459             root = m.group(1)
460             num = int(m.group(2))
461             dd.append( (root, [num,c,i] ) )
462         return dd
463         
464
465     def get_keyID(self, account, sequence):
466         if account == 0:
467             return 'old'
468
469         rs = self.rebase_sequence(account, sequence)
470         dd = []
471         for root, public_sequence in rs:
472             c, K, _ = self.master_public_keys[root]
473             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
474             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
475         return '&'.join(dd)
476
477
478     def get_public_key(self, address):
479         account, sequence = self.get_address_index(address)
480         return self.accounts[account].get_pubkey( *sequence )
481
482
483     def decode_seed(self, password):
484         seed = pw_decode(self.seed, password)
485         #todo:  #self.sequences[0].check_seed(seed)
486         return seed
487         
488
489     def get_private_key(self, address, password):
490         out = []
491         if address in self.imported_keys.keys():
492             out.append( pw_decode( self.imported_keys[address], password ) )
493         else:
494             account, sequence = self.get_address_index(address)
495             if account == 0:
496                 seed = self.decode_seed(password)
497                 pk = self.accounts[account].get_private_key(seed, sequence)
498                 out.append(pk)
499                 return out
500
501             # assert address == self.accounts[account].get_address(*sequence)
502             rs = self.rebase_sequence( account, sequence)
503             for root, public_sequence in rs:
504
505                 if root not in self.master_private_keys.keys(): continue
506                 master_k = self.get_master_private_key(root, password)
507                 master_c, _, _ = self.master_public_keys[root]
508                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
509                 out.append(pk)
510                     
511         return out
512
513
514
515
516     def signrawtransaction(self, tx, input_info, private_keys, password):
517         import deserialize
518         unspent_coins = self.get_unspent_coins()
519         seed = self.decode_seed(password)
520
521         # build a list of public/private keys
522         keypairs = {}
523         for sec in private_keys:
524             pubkey = public_key_from_private_key(sec)
525             keypairs[ pubkey ] = sec
526
527
528         for txin in tx.inputs:
529             # convert to own format
530             txin['tx_hash'] = txin['prevout_hash']
531             txin['index'] = txin['prevout_n']
532
533             for item in input_info:
534                 if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']:
535                     txin['raw_output_script'] = item['scriptPubKey']
536                     txin['redeemScript'] = item.get('redeemScript')
537                     txin['KeyID'] = item.get('KeyID')
538                     break
539             else:
540                 for item in unspent_coins:
541                     if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
542                         print_error( "tx input is in unspent coins" )
543                         txin['raw_output_script'] = item['raw_output_script']
544                         account, sequence = self.get_address_index(item['address'])
545                         if account != -1:
546                             txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
547                         break
548                 else:
549                     raise BaseException("Unknown transaction input. Please provide the 'input_info' parameter, or synchronize this wallet")
550
551             # if available, derive private_keys from KeyID
552             keyid = txin.get('KeyID')
553             if keyid:
554                 roots = []
555                 for s in keyid.split('&'):
556                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
557                     if not m: continue
558                     c = m.group(1)
559                     K = m.group(2)
560                     sequence = m.group(3)
561                     root = self.find_root_by_master_key(c,K)
562                     if not root: continue
563                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
564                     root = root + '%d'%sequence[0]
565                     sequence = sequence[1:]
566                     roots.append((root,sequence)) 
567
568                 account_id = " & ".join( map(lambda x:x[0], roots) )
569                 account = self.accounts.get(account_id)
570                 if not account: continue
571                 addr = account.get_address(*sequence)
572                 txin['address'] = addr
573                 pk = self.get_private_key(addr, password)
574                 for sec in pk:
575                     pubkey = public_key_from_private_key(sec)
576                     keypairs[pubkey] = sec
577
578             redeem_script = txin.get("redeemScript")
579             print_error( "p2sh:", "yes" if redeem_script else "no")
580             if redeem_script:
581                 addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5)
582             else:
583                 addr = deserialize.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
584             txin['address'] = addr
585
586             # add private keys that are in the wallet
587             pk = self.get_private_key(addr, password)
588             for sec in pk:
589                 pubkey = public_key_from_private_key(sec)
590                 keypairs[pubkey] = sec
591                 if not redeem_script:
592                     txin['redeemPubkey'] = pubkey
593
594             print txin
595
596         tx.sign( keypairs )
597
598     def sign_message(self, address, message, password):
599         sec = self.get_private_key(address, password)
600         key = regenerate_key(sec)
601         compressed = is_compressed(sec)
602         return key.sign_message(message, compressed, address)
603
604     def verify_message(self, address, signature, message):
605         try:
606             EC_KEY.verify_message(address, signature, message)
607             return True
608         except BaseException as e:
609             print_error("Verification error: {0}".format(e))
610             return False
611
612
613     def change_gap_limit(self, value):
614         if value >= self.gap_limit:
615             self.gap_limit = value
616             self.storage.put('gap_limit', self.gap_limit, True)
617             self.interface.poke('synchronizer')
618             return True
619
620         elif value >= self.min_acceptable_gap():
621             for key, account in self.accounts.items():
622                 addresses = account[0]
623                 k = self.num_unused_trailing_addresses(addresses)
624                 n = len(addresses) - k + value
625                 addresses = addresses[0:n]
626                 self.accounts[key][0] = addresses
627
628             self.gap_limit = value
629             self.storage.put('gap_limit', self.gap_limit, True)
630             self.save_accounts()
631             return True
632         else:
633             return False
634
635     def num_unused_trailing_addresses(self, addresses):
636         k = 0
637         for a in addresses[::-1]:
638             if self.history.get(a):break
639             k = k + 1
640         return k
641
642     def min_acceptable_gap(self):
643         # fixme: this assumes wallet is synchronized
644         n = 0
645         nmax = 0
646
647         for account in self.accounts.values():
648             addresses = account.get_addresses(0)
649             k = self.num_unused_trailing_addresses(addresses)
650             for a in addresses[0:-k]:
651                 if self.history.get(a):
652                     n = 0
653                 else:
654                     n += 1
655                     if n > nmax: nmax = n
656         return nmax + 1
657
658
659     def address_is_old(self, address):
660         age = -1
661         h = self.history.get(address, [])
662         if h == ['*']:
663             return True
664         for tx_hash, tx_height in h:
665             if tx_height == 0:
666                 tx_age = 0
667             else: 
668                 tx_age = self.verifier.blockchain.height - tx_height + 1
669             if tx_age > age:
670                 age = tx_age
671         return age > 2
672
673
674     def synchronize_sequence(self, account, for_change):
675         limit = self.gap_limit_for_change if for_change else self.gap_limit
676         new_addresses = []
677         while True:
678             addresses = account.get_addresses(for_change)
679             if len(addresses) < limit:
680                 address = account.create_new_address(for_change)
681                 self.history[address] = []
682                 new_addresses.append( address )
683                 continue
684
685             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
686                 break
687             else:
688                 address = account.create_new_address(for_change)
689                 self.history[address] = []
690                 new_addresses.append( address )
691
692         return new_addresses
693         
694
695
696     def create_pending_accounts(self):
697         for account_type in ['1','2of2','2of3']:
698             a = self.new_account_address(account_type)
699             if self.address_is_old(a):
700                 print "creating account", a
701                 self.create_account(account_type)
702
703
704     def synchronize_account(self, account):
705         new = []
706         new += self.synchronize_sequence(account, 0)
707         new += self.synchronize_sequence(account, 1)
708         return new
709
710
711     def synchronize(self):
712         if self.master_public_keys:
713             self.create_pending_accounts()
714         new = []
715         for account in self.accounts.values():
716             new += self.synchronize_account(account)
717         if new:
718             self.save_accounts()
719             self.storage.put('addr_history', self.history, True)
720         return new
721
722
723     def is_found(self):
724         return self.history.values() != [[]] * len(self.history) 
725
726
727     def add_contact(self, address, label=None):
728         self.addressbook.append(address)
729         self.storage.put('contacts', self.addressbook, True)
730         if label:  
731             self.labels[address] = label
732             self.storage.put('labels', self.labels, True)
733
734     def delete_contact(self, addr):
735         if addr in self.addressbook:
736             self.addressbook.remove(addr)
737             self.storage.put('addressbook', self.addressbook, True)
738
739
740     def fill_addressbook(self):
741         for tx_hash, tx in self.transactions.items():
742             is_relevant, is_send, _, _ = self.get_tx_value(tx)
743             if is_send:
744                 for addr, v in tx.outputs:
745                     if not self.is_mine(addr) and addr not in self.addressbook:
746                         self.addressbook.append(addr)
747         # redo labels
748         # self.update_tx_labels()
749
750     def get_num_tx(self, address):
751         n = 0 
752         for tx in self.transactions.values():
753             if address in map(lambda x:x[0], tx.outputs): n += 1
754         return n
755
756
757     def get_address_flags(self, addr):
758         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
759         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
760         return flags
761         
762
763     def get_tx_value(self, tx, account=None):
764         domain = self.get_account_addresses(account)
765         return tx.get_value(domain, self.prevout_values)
766
767     
768     def update_tx_outputs(self, tx_hash):
769         tx = self.transactions.get(tx_hash)
770
771         for i, (addr, value) in enumerate(tx.outputs):
772             key = tx_hash+ ':%d'%i
773             self.prevout_values[key] = value
774
775         for item in tx.inputs:
776             if self.is_mine(item.get('address')):
777                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
778                 self.spent_outputs.append(key)
779
780
781     def get_addr_balance(self, address):
782         assert self.is_mine(address)
783         h = self.history.get(address,[])
784         if h == ['*']: return 0,0
785         c = u = 0
786         received_coins = []   # list of coins received at address
787
788         for tx_hash, tx_height in h:
789             tx = self.transactions.get(tx_hash)
790             if not tx: continue
791
792             for i, (addr, value) in enumerate(tx.outputs):
793                 if addr == address:
794                     key = tx_hash + ':%d'%i
795                     received_coins.append(key)
796
797         for tx_hash, tx_height in h:
798             tx = self.transactions.get(tx_hash)
799             if not tx: continue
800             v = 0
801
802             for item in tx.inputs:
803                 addr = item.get('address')
804                 if addr == address:
805                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
806                     value = self.prevout_values.get( key )
807                     if key in received_coins: 
808                         v -= value
809
810             for i, (addr, value) in enumerate(tx.outputs):
811                 key = tx_hash + ':%d'%i
812                 if addr == address:
813                     v += value
814
815             if tx_height:
816                 c += v
817             else:
818                 u += v
819         return c, u
820
821
822     def get_accounts(self):
823         accounts = {}
824         for k, account in self.accounts.items():
825             accounts[k] = self.labels.get(k, 'unnamed')
826         if self.imported_keys:
827             accounts[-1] = 'Imported keys'
828         return accounts
829
830     def get_account_addresses(self, a, include_change=True):
831         if a is None:
832             o = self.addresses(True)
833         elif a == -1:
834             o = self.imported_keys.keys()
835         else:
836             ac = self.accounts[a]
837             o = ac.get_addresses(0)
838             if include_change: o += ac.get_addresses(1)
839         return o
840
841     def get_imported_balance(self):
842         cc = uu = 0
843         for addr in self.imported_keys.keys():
844             c, u = self.get_addr_balance(addr)
845             cc += c
846             uu += u
847         return cc, uu
848
849     def get_account_balance(self, account):
850         if account is None:
851             return self.get_balance()
852         elif account == -1:
853             return self.get_imported_balance()
854         
855         conf = unconf = 0
856         for addr in self.get_account_addresses(account): 
857             c, u = self.get_addr_balance(addr)
858             conf += c
859             unconf += u
860         return conf, unconf
861
862     def get_frozen_balance(self):
863         conf = unconf = 0
864         for addr in self.frozen_addresses:
865             c, u = self.get_addr_balance(addr)
866             conf += c
867             unconf += u
868         return conf, unconf
869
870         
871     def get_balance(self):
872         cc = uu = 0
873         for a in self.accounts.keys():
874             c, u = self.get_account_balance(a)
875             cc += c
876             uu += u
877         c, u = self.get_imported_balance()
878         cc += c
879         uu += u
880         return cc, uu
881
882
883     def get_unspent_coins(self, domain=None):
884         coins = []
885         if domain is None: domain = self.addresses(True)
886         for addr in domain:
887             h = self.history.get(addr, [])
888             if h == ['*']: continue
889             for tx_hash, tx_height in h:
890                 tx = self.transactions.get(tx_hash)
891                 if tx is None: raise BaseException("Wallet not synchronized")
892                 for output in tx.d.get('outputs'):
893                     if output.get('address') != addr: continue
894                     key = tx_hash + ":%d" % output.get('index')
895                     if key in self.spent_outputs: continue
896                     output['tx_hash'] = tx_hash
897                     coins.append(output)
898         return coins
899
900
901
902     def choose_tx_inputs( self, amount, fixed_fee, account = None ):
903         """ todo: minimize tx size """
904         total = 0
905         fee = self.fee if fixed_fee is None else fixed_fee
906         domain = self.get_account_addresses(account)
907         coins = []
908         prioritized_coins = []
909         for i in self.frozen_addresses:
910             if i in domain: domain.remove(i)
911
912         for i in self.prioritized_addresses:
913             if i in domain: domain.remove(i)
914
915         coins = self.get_unspent_coins(domain)
916         prioritized_coins = self.get_unspent_coins(self.prioritized_addresses)
917
918         inputs = []
919         coins = prioritized_coins + coins
920
921         for item in coins: 
922             addr = item.get('address')
923             v = item.get('value')
924             total += v
925             inputs.append( item )
926             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
927             if total >= amount + fee: break
928         else:
929             inputs = []
930
931         return inputs, total, fee
932
933
934     def set_fee(self, fee):
935         if self.fee != fee:
936             self.fee = fee
937             self.storage.put('fee_per_kb', self.fee, True)
938         
939     def estimated_fee(self, inputs):
940         estimated_size =  len(inputs) * 180 + 80     # this assumes non-compressed keys
941         fee = self.fee * int(round(estimated_size/1024.))
942         if fee == 0: fee = self.fee
943         return fee
944
945
946     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None, account=0 ):
947         "add change to a transaction"
948         change_amount = total - ( amount + fee )
949         if change_amount != 0:
950             if not change_addr:
951                 if account is None: 
952                     # send change to one of the accounts involved in the tx
953                     address = inputs[0].get('address')
954                     account, _ = self.get_address_index(address)
955
956                 if not self.use_change or account == -1:
957                     change_addr = inputs[-1]['address']
958                 else:
959                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
960
961             # Insert the change output at a random position in the outputs
962             posn = random.randint(0, len(outputs))
963             outputs[posn:posn] = [( change_addr,  change_amount)]
964         return outputs
965
966
967     def get_history(self, address):
968         with self.lock:
969             return self.history.get(address)
970
971
972     def get_status(self, h):
973         if not h: return None
974         if h == ['*']: return '*'
975         status = ''
976         for tx_hash, height in h:
977             status += tx_hash + ':%d:' % height
978         return hashlib.sha256( status ).digest().encode('hex')
979
980
981     def receive_tx_callback(self, tx_hash, tx, tx_height):
982         if not self.check_new_tx(tx_hash, tx):
983             # may happen due to pruning
984             print_error("received transaction that is no longer referenced in history", tx_hash)
985             return
986
987         with self.transaction_lock:
988             self.transactions[tx_hash] = tx
989
990             self.interface.pending_transactions_for_notifications.append(tx)
991
992             self.save_transactions()
993             if self.verifier and tx_height>0: 
994                 self.verifier.add(tx_hash, tx_height)
995             self.update_tx_outputs(tx_hash)
996
997
998     def save_transactions(self):
999         tx = {}
1000         for k,v in self.transactions.items():
1001             tx[k] = str(v)
1002         self.storage.put('transactions', tx, True)
1003
1004     def receive_history_callback(self, addr, hist):
1005
1006         if not self.check_new_history(addr, hist):
1007             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
1008             
1009         with self.lock:
1010             self.history[addr] = hist
1011             self.storage.put('addr_history', self.history, True)
1012
1013         if hist != ['*']:
1014             for tx_hash, tx_height in hist:
1015                 if tx_height>0:
1016                     # add it in case it was previously unconfirmed
1017                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1018
1019
1020     def get_tx_history(self, account=None):
1021         with self.transaction_lock:
1022             history = self.transactions.items()
1023             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1024             result = []
1025     
1026             balance = 0
1027             for tx_hash, tx in history:
1028                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1029                 if v is not None: balance += v
1030
1031             c, u = self.get_account_balance(account)
1032
1033             if balance != c+u:
1034                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1035
1036             balance = c + u - balance
1037             for tx_hash, tx in history:
1038                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1039                 if not is_relevant:
1040                     continue
1041                 if value is not None:
1042                     balance += value
1043
1044                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1045                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1046
1047         return result
1048
1049
1050     def get_label(self, tx_hash):
1051         label = self.labels.get(tx_hash)
1052         is_default = (label == '') or (label is None)
1053         if is_default: label = self.get_default_label(tx_hash)
1054         return label, is_default
1055
1056
1057     def get_default_label(self, tx_hash):
1058         tx = self.transactions.get(tx_hash)
1059         default_label = ''
1060         if tx:
1061             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1062             if is_mine:
1063                 for o in tx.outputs:
1064                     o_addr, _ = o
1065                     if not self.is_mine(o_addr):
1066                         try:
1067                             default_label = self.labels[o_addr]
1068                         except KeyError:
1069                             default_label = o_addr
1070                         break
1071                 else:
1072                     default_label = '(internal)'
1073             else:
1074                 for o in tx.outputs:
1075                     o_addr, _ = o
1076                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1077                         break
1078                 else:
1079                     for o in tx.outputs:
1080                         o_addr, _ = o
1081                         if self.is_mine(o_addr):
1082                             break
1083                     else:
1084                         o_addr = None
1085
1086                 if o_addr:
1087                     dest_label = self.labels.get(o_addr)
1088                     try:
1089                         default_label = self.labels[o_addr]
1090                     except KeyError:
1091                         default_label = o_addr
1092
1093         return default_label
1094
1095
1096     def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
1097         
1098         for address, x in outputs:
1099             assert is_valid(address)
1100
1101         amount = sum( map(lambda x:x[1], outputs) )
1102
1103         inputs, total, fee = self.choose_tx_inputs( amount, fee, account )
1104         if not inputs:
1105             raise ValueError("Not enough funds")
1106
1107         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
1108         tx = Transaction.from_io(inputs, outputs)
1109
1110         keypairs = {}
1111         for i, txin in enumerate(tx.inputs):
1112             address = txin['address']
1113
1114             account, sequence = self.get_address_index(address)
1115             txin['KeyID'] = self.get_keyID(account, sequence)
1116
1117             redeemScript = self.accounts[account].redeem_script(sequence)
1118             if redeemScript: 
1119                 txin['redeemScript'] = redeemScript
1120             else:
1121                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1122
1123             private_keys = self.get_private_key(address, password)
1124
1125             for sec in private_keys:
1126                 pubkey = public_key_from_private_key(sec)
1127                 keypairs[ pubkey ] = sec
1128
1129         tx.sign(keypairs)
1130         for address, x in outputs:
1131             if address not in self.addressbook and not self.is_mine(address):
1132                 self.addressbook.append(address)
1133
1134         return tx
1135
1136
1137
1138     def sendtx(self, tx):
1139         # synchronous
1140         h = self.send_tx(tx)
1141         self.tx_event.wait()
1142         return self.receive_tx(h)
1143
1144     def send_tx(self, tx):
1145         # asynchronous
1146         self.tx_event.clear()
1147         self.interface.send([('blockchain.transaction.broadcast', [str(tx)])], 'synchronizer')
1148         return tx.hash()
1149
1150     def receive_tx(self,tx_hash):
1151         out = self.tx_result 
1152         if out != tx_hash:
1153             return False, "error: " + out
1154         return True, out
1155
1156
1157
1158     def update_password(self, seed, old_password, new_password):
1159         if new_password == '': new_password = None
1160         # this will throw an exception if unicode cannot be converted
1161         self.seed = pw_encode( seed, new_password)
1162         self.storage.put('seed', self.seed, True)
1163         self.use_encryption = (new_password != None)
1164         self.storage.put('use_encryption', self.use_encryption,True)
1165         for k in self.imported_keys.keys():
1166             a = self.imported_keys[k]
1167             b = pw_decode(a, old_password)
1168             c = pw_encode(b, new_password)
1169             self.imported_keys[k] = c
1170         self.storage.put('imported_keys', self.imported_keys, True)
1171
1172         for k, v in self.master_private_keys.items():
1173             b = pw_decode(v, old_password)
1174             c = pw_encode(b, new_password)
1175             self.master_private_keys[k] = c
1176         self.storage.put('master_private_keys', self.master_private_keys, True)
1177
1178
1179     def freeze(self,addr):
1180         if self.is_mine(addr) and addr not in self.frozen_addresses:
1181             self.unprioritize(addr)
1182             self.frozen_addresses.append(addr)
1183             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1184             return True
1185         else:
1186             return False
1187
1188     def unfreeze(self,addr):
1189         if self.is_mine(addr) and addr in self.frozen_addresses:
1190             self.frozen_addresses.remove(addr)
1191             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1192             return True
1193         else:
1194             return False
1195
1196     def prioritize(self,addr):
1197         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1198             self.unfreeze(addr)
1199             self.prioritized_addresses.append(addr)
1200             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1201             return True
1202         else:
1203             return False
1204
1205     def unprioritize(self,addr):
1206         if self.is_mine(addr) and addr in self.prioritized_addresses:
1207             self.prioritized_addresses.remove(addr)
1208             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1209             return True
1210         else:
1211             return False
1212
1213
1214     def set_verifier(self, verifier):
1215         self.verifier = verifier
1216
1217         # review transactions that are in the history
1218         for addr, hist in self.history.items():
1219             if hist == ['*']: continue
1220             for tx_hash, tx_height in hist:
1221                 if tx_height>0:
1222                     # add it in case it was previously unconfirmed
1223                     self.verifier.add(tx_hash, tx_height)
1224
1225
1226         # if we are on a pruning server, remove unverified transactions
1227         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1228         for tx_hash in self.transactions.keys():
1229             if tx_hash not in vr:
1230                 self.transactions.pop(tx_hash)
1231
1232
1233
1234     def check_new_history(self, addr, hist):
1235         
1236         # check that all tx in hist are relevant
1237         if hist != ['*']:
1238             for tx_hash, height in hist:
1239                 tx = self.transactions.get(tx_hash)
1240                 if not tx: continue
1241                 if not tx.has_address(addr):
1242                     return False
1243
1244         # check that we are not "orphaning" a transaction
1245         old_hist = self.history.get(addr,[])
1246         if old_hist == ['*']: return True
1247
1248         for tx_hash, height in old_hist:
1249             if tx_hash in map(lambda x:x[0], hist): continue
1250             found = False
1251             for _addr, _hist in self.history.items():
1252                 if _addr == addr: continue
1253                 if _hist == ['*']: continue
1254                 _tx_hist = map(lambda x:x[0], _hist)
1255                 if tx_hash in _tx_hist:
1256                     found = True
1257                     break
1258
1259             if not found:
1260                 tx = self.transactions.get(tx_hash)
1261                 # tx might not be there
1262                 if not tx: continue
1263                 
1264                 # already verified?
1265                 if self.verifier.get_height(tx_hash):
1266                     continue
1267                 # unconfirmed tx
1268                 print_error("new history is orphaning transaction:", tx_hash)
1269                 # check that all outputs are not mine, request histories
1270                 ext_requests = []
1271                 for _addr, _v in tx.outputs:
1272                     # assert not self.is_mine(_addr)
1273                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1274
1275                 ext_h = self.interface.synchronous_get(ext_requests)
1276                 print_error("sync:", ext_requests, ext_h)
1277                 height = None
1278                 for h in ext_h:
1279                     if h == ['*']: continue
1280                     for item in h:
1281                         if item.get('tx_hash') == tx_hash:
1282                             height = item.get('height')
1283                 if height:
1284                     print_error("found height for", tx_hash, height)
1285                     self.verifier.add(tx_hash, height)
1286                 else:
1287                     print_error("removing orphaned tx from history", tx_hash)
1288                     self.transactions.pop(tx_hash)
1289
1290         return True
1291
1292
1293
1294     def check_new_tx(self, tx_hash, tx):
1295         # 1 check that tx is referenced in addr_history. 
1296         addresses = []
1297         for addr, hist in self.history.items():
1298             if hist == ['*']:continue
1299             for txh, height in hist:
1300                 if txh == tx_hash: 
1301                     addresses.append(addr)
1302
1303         if not addresses:
1304             return False
1305
1306         # 2 check that referencing addresses are in the tx
1307         for addr in addresses:
1308             if not tx.has_address(addr):
1309                 return False
1310
1311         return True
1312
1313
1314     def start_threads(self, interface, blockchain):
1315         from verifier import TxVerifier
1316         self.interface = interface
1317         self.verifier = TxVerifier(interface, blockchain, self.storage)
1318         self.verifier.start()
1319         self.set_verifier(self.verifier)
1320         self.synchronizer = WalletSynchronizer(self)
1321         self.synchronizer.start()
1322
1323     def stop_threads(self):
1324         self.verifier.stop()
1325         self.synchronizer.stop()
1326
1327
1328
1329
1330 class WalletSynchronizer(threading.Thread):
1331
1332
1333     def __init__(self, wallet):
1334         threading.Thread.__init__(self)
1335         self.daemon = True
1336         self.wallet = wallet
1337         wallet.synchronizer = self
1338         self.interface = self.wallet.interface
1339         self.interface.register_channel('synchronizer')
1340         self.wallet.interface.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1341         self.was_updated = True
1342         self.running = False
1343         self.lock = threading.Lock()
1344
1345     def stop(self):
1346         with self.lock: self.running = False
1347         self.interface.poke('synchronizer')
1348
1349     def is_running(self):
1350         with self.lock: return self.running
1351
1352     
1353     def subscribe_to_addresses(self, addresses):
1354         messages = []
1355         for addr in addresses:
1356             messages.append(('blockchain.address.subscribe', [addr]))
1357         self.interface.send( messages, 'synchronizer')
1358
1359
1360     def run(self):
1361         with self.lock: self.running = True
1362
1363         requested_tx = []
1364         missing_tx = []
1365         requested_histories = {}
1366
1367         # request any missing transactions
1368         for history in self.wallet.history.values():
1369             if history == ['*']: continue
1370             for tx_hash, tx_height in history:
1371                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1372                     missing_tx.append( (tx_hash, tx_height) )
1373         print_error("missing tx", missing_tx)
1374
1375         # wait until we are connected, in case the user is not connected
1376         while not self.interface.is_connected:
1377             time.sleep(1)
1378         
1379         # subscriptions
1380         self.subscribe_to_addresses(self.wallet.addresses(True))
1381
1382         while self.is_running():
1383             # 1. create new addresses
1384             new_addresses = self.wallet.synchronize()
1385
1386             # request missing addresses
1387             if new_addresses:
1388                 self.subscribe_to_addresses(new_addresses)
1389
1390             # request missing transactions
1391             for tx_hash, tx_height in missing_tx:
1392                 if (tx_hash, tx_height) not in requested_tx:
1393                     self.interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], 'synchronizer')
1394                     requested_tx.append( (tx_hash, tx_height) )
1395             missing_tx = []
1396
1397             # detect if situation has changed
1398             if not self.interface.is_up_to_date('synchronizer'):
1399                 if self.wallet.is_up_to_date():
1400                     self.wallet.set_up_to_date(False)
1401                     self.was_updated = True
1402             else:
1403                 if not self.wallet.is_up_to_date():
1404                     self.wallet.set_up_to_date(True)
1405                     self.was_updated = True
1406
1407             if self.was_updated:
1408                 self.interface.trigger_callback('updated')
1409                 self.was_updated = False
1410
1411             # 2. get a response
1412             r = self.interface.get_response('synchronizer')
1413
1414             # poke sends None. (needed during stop)
1415             if not r: continue
1416
1417             # 3. handle response
1418             method = r['method']
1419             params = r['params']
1420             result = r.get('result')
1421             error = r.get('error')
1422             if error:
1423                 print "error", r
1424                 continue
1425
1426             if method == 'blockchain.address.subscribe':
1427                 addr = params[0]
1428                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1429                     if requested_histories.get(addr) is None:
1430                         self.interface.send([('blockchain.address.get_history', [addr])], 'synchronizer')
1431                         requested_histories[addr] = result
1432
1433             elif method == 'blockchain.address.get_history':
1434                 addr = params[0]
1435                 print_error("receiving history", addr, result)
1436                 if result == ['*']:
1437                     assert requested_histories.pop(addr) == '*'
1438                     self.wallet.receive_history_callback(addr, result)
1439                 else:
1440                     hist = []
1441                     # check that txids are unique
1442                     txids = []
1443                     for item in result:
1444                         tx_hash = item['tx_hash']
1445                         if tx_hash not in txids:
1446                             txids.append(tx_hash)
1447                             hist.append( (tx_hash, item['height']) )
1448
1449                     if len(hist) != len(result):
1450                         raise BaseException("error: server sent history with non-unique txid", result)
1451
1452                     # check that the status corresponds to what was announced
1453                     rs = requested_histories.pop(addr)
1454                     if self.wallet.get_status(hist) != rs:
1455                         raise BaseException("error: status mismatch: %s"%addr)
1456                 
1457                     # store received history
1458                     self.wallet.receive_history_callback(addr, hist)
1459
1460                     # request transactions that we don't have 
1461                     for tx_hash, tx_height in hist:
1462                         if self.wallet.transactions.get(tx_hash) is None:
1463                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1464                                 missing_tx.append( (tx_hash, tx_height) )
1465
1466             elif method == 'blockchain.transaction.get':
1467                 tx_hash = params[0]
1468                 tx_height = params[1]
1469                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1470                 tx = Transaction(result)
1471                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1472                 self.was_updated = True
1473                 requested_tx.remove( (tx_hash, tx_height) )
1474                 print_error("received tx:", tx_hash, len(tx.raw))
1475
1476             elif method == 'blockchain.transaction.broadcast':
1477                 self.wallet.tx_result = result
1478                 self.wallet.tx_event.set()
1479
1480             else:
1481                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1482
1483             if self.was_updated and not requested_tx:
1484                 self.interface.trigger_callback('updated')
1485                 self.interface.trigger_callback("new_transaction") # Updated gets called too many times from other places as well; if we use that signal we get the notification three times
1486                 
1487
1488                 self.was_updated = False