fee belongs in wallet storage
[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         rs = self.rebase_sequence(account, sequence)
467         dd = []
468         for root, public_sequence in rs:
469             c, K, _ = self.master_public_keys[root]
470             s = '/' + '/'.join( map(lambda x:str(x), public_sequence) )
471             dd.append( 'bip32(%s,%s,%s)'%(c,K, s) )
472         return '&'.join(dd)
473
474
475     def get_public_key(self, address):
476         account, sequence = self.get_address_index(address)
477         return self.accounts[account].get_pubkey( *sequence )
478
479
480     def decode_seed(self, password):
481         seed = pw_decode(self.seed, password)
482         #todo:  #self.sequences[0].check_seed(seed)
483         return seed
484         
485
486     def get_private_key(self, address, password):
487         out = []
488         if address in self.imported_keys.keys():
489             out.append( pw_decode( self.imported_keys[address], password ) )
490         else:
491             account, sequence = self.get_address_index(address)
492             # assert address == self.accounts[account].get_address(*sequence)
493             rs = self.rebase_sequence( account, sequence)
494             for root, public_sequence in rs:
495
496                 if root not in self.master_private_keys.keys(): continue
497                 master_k = self.get_master_private_key(root, password)
498                 master_c, _, _ = self.master_public_keys[root]
499                 pk = bip32_private_key( public_sequence, master_k.decode('hex'), master_c.decode('hex'))
500                 out.append(pk)
501                     
502         return out
503
504
505
506
507     def signrawtransaction(self, tx, input_info, private_keys, password):
508         import deserialize
509         unspent_coins = self.get_unspent_coins()
510         seed = self.decode_seed(password)
511
512         # build a list of public/private keys
513         keypairs = {}
514         for sec in private_keys:
515             pubkey = public_key_from_private_key(sec)
516             keypairs[ pubkey ] = sec
517
518
519         for txin in tx.inputs:
520             # convert to own format
521             txin['tx_hash'] = txin['prevout_hash']
522             txin['index'] = txin['prevout_n']
523
524             for item in input_info:
525                 if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']:
526                     txin['raw_output_script'] = item['scriptPubKey']
527                     txin['redeemScript'] = item.get('redeemScript')
528                     txin['KeyID'] = item.get('KeyID')
529                     break
530             else:
531                 for item in unspent_coins:
532                     if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
533                         print_error( "tx input is in unspent coins" )
534                         txin['raw_output_script'] = item['raw_output_script']
535                         account, sequence = self.get_address_index(item['address'])
536                         if account != -1:
537                             txin['redeemScript'] = self.accounts[account].redeem_script(sequence)
538                         break
539                 else:
540                     raise BaseException("Unknown transaction input. Please provide the 'input_info' parameter, or synchronize this wallet")
541
542             # if available, derive private_keys from KeyID
543             keyid = txin.get('KeyID')
544             if keyid:
545                 roots = []
546                 for s in keyid.split('&'):
547                     m = re.match("bip32\(([0-9a-f]+),([0-9a-f]+),(/\d+/\d+/\d+)", s)
548                     if not m: continue
549                     c = m.group(1)
550                     K = m.group(2)
551                     sequence = m.group(3)
552                     root = self.find_root_by_master_key(c,K)
553                     if not root: continue
554                     sequence = map(lambda x:int(x), sequence.strip('/').split('/'))
555                     root = root + '%d'%sequence[0]
556                     sequence = sequence[1:]
557                     roots.append((root,sequence)) 
558
559                 account_id = " & ".join( map(lambda x:x[0], roots) )
560                 account = self.accounts.get(account_id)
561                 if not account: continue
562                 addr = account.get_address(*sequence)
563                 txin['address'] = addr
564                 pk = self.get_private_key(addr, password)
565                 for sec in pk:
566                     pubkey = public_key_from_private_key(sec)
567                     keypairs[pubkey] = sec
568
569             redeem_script = txin.get("redeemScript")
570             print_error( "p2sh:", "yes" if redeem_script else "no")
571             if redeem_script:
572                 addr = hash_160_to_bc_address(hash_160(redeem_script.decode('hex')), 5)
573             else:
574                 addr = deserialize.get_address_from_output_script(txin["raw_output_script"].decode('hex'))
575             txin['address'] = addr
576
577             # add private keys that are in the wallet
578             pk = self.get_private_key(addr, password)
579             for sec in pk:
580                 pubkey = public_key_from_private_key(sec)
581                 keypairs[pubkey] = sec
582                 if not redeem_script:
583                     txin['redeemPubkey'] = pubkey
584
585             print txin
586
587         tx.sign( keypairs )
588
589     def sign_message(self, address, message, password):
590         sec = self.get_private_key(address, password)
591         key = regenerate_key(sec)
592         compressed = is_compressed(sec)
593         return key.sign_message(message, compressed, address)
594
595     def verify_message(self, address, signature, message):
596         try:
597             EC_KEY.verify_message(address, signature, message)
598             return True
599         except BaseException as e:
600             print_error("Verification error: {0}".format(e))
601             return False
602
603
604     def change_gap_limit(self, value):
605         if value >= self.gap_limit:
606             self.gap_limit = value
607             self.storage.put('gap_limit', self.gap_limit, True)
608             self.interface.poke('synchronizer')
609             return True
610
611         elif value >= self.min_acceptable_gap():
612             for key, account in self.accounts.items():
613                 addresses = account[0]
614                 k = self.num_unused_trailing_addresses(addresses)
615                 n = len(addresses) - k + value
616                 addresses = addresses[0:n]
617                 self.accounts[key][0] = addresses
618
619             self.gap_limit = value
620             self.storage.put('gap_limit', self.gap_limit, True)
621             self.save_accounts()
622             return True
623         else:
624             return False
625
626     def num_unused_trailing_addresses(self, addresses):
627         k = 0
628         for a in addresses[::-1]:
629             if self.history.get(a):break
630             k = k + 1
631         return k
632
633     def min_acceptable_gap(self):
634         # fixme: this assumes wallet is synchronized
635         n = 0
636         nmax = 0
637
638         for account in self.accounts.values():
639             addresses = account.get_addresses(0)
640             k = self.num_unused_trailing_addresses(addresses)
641             for a in addresses[0:-k]:
642                 if self.history.get(a):
643                     n = 0
644                 else:
645                     n += 1
646                     if n > nmax: nmax = n
647         return nmax + 1
648
649
650     def address_is_old(self, address):
651         age = -1
652         h = self.history.get(address, [])
653         if h == ['*']:
654             return True
655         for tx_hash, tx_height in h:
656             if tx_height == 0:
657                 tx_age = 0
658             else: 
659                 tx_age = self.verifier.height - tx_height + 1
660             if tx_age > age:
661                 age = tx_age
662         return age > 2
663
664
665     def synchronize_sequence(self, account, for_change):
666         limit = self.gap_limit_for_change if for_change else self.gap_limit
667         new_addresses = []
668         while True:
669             addresses = account.get_addresses(for_change)
670             if len(addresses) < limit:
671                 address = account.create_new_address(for_change)
672                 self.history[address] = []
673                 new_addresses.append( address )
674                 continue
675
676             if map( lambda a: self.address_is_old(a), addresses[-limit:] ) == limit*[False]:
677                 break
678             else:
679                 address = account.create_new_address(for_change)
680                 self.history[address] = []
681                 new_addresses.append( address )
682
683         return new_addresses
684         
685
686
687     def create_pending_accounts(self):
688         for account_type in ['1','2of2','2of3']:
689             a = self.new_account_address(account_type)
690             if self.address_is_old(a):
691                 print "creating account", a
692                 self.create_account(account_type)
693
694
695     def synchronize_account(self, account):
696         new = []
697         new += self.synchronize_sequence(account, 0)
698         new += self.synchronize_sequence(account, 1)
699         return new
700
701
702     def synchronize(self):
703         if self.master_public_keys:
704             self.create_pending_accounts()
705         new = []
706         for account in self.accounts.values():
707             new += self.synchronize_account(account)
708         if new:
709             self.save_accounts()
710             self.storage.put('addr_history', self.history, True)
711         return new
712
713
714     def is_found(self):
715         return self.history.values() != [[]] * len(self.history) 
716
717
718     def add_contact(self, address, label=None):
719         self.addressbook.append(address)
720         self.storage.put('contacts', self.addressbook, True)
721         if label:  
722             self.labels[address] = label
723             self.storage.put('labels', self.labels, True)
724
725     def delete_contact(self, addr):
726         if addr in self.addressbook:
727             self.addressbook.remove(addr)
728             self.storage.put('addressbook', self.addressbook, True)
729
730
731     def fill_addressbook(self):
732         for tx_hash, tx in self.transactions.items():
733             is_relevant, is_send, _, _ = self.get_tx_value(tx)
734             if is_send:
735                 for addr, v in tx.outputs:
736                     if not self.is_mine(addr) and addr not in self.addressbook:
737                         self.addressbook.append(addr)
738         # redo labels
739         # self.update_tx_labels()
740
741     def get_num_tx(self, address):
742         n = 0 
743         for tx in self.transactions.values():
744             if address in map(lambda x:x[0], tx.outputs): n += 1
745         return n
746
747
748     def get_address_flags(self, addr):
749         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
750         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
751         return flags
752         
753
754     def get_tx_value(self, tx, account=None):
755         domain = self.get_account_addresses(account)
756         return tx.get_value(domain, self.prevout_values)
757
758     
759     def update_tx_outputs(self, tx_hash):
760         tx = self.transactions.get(tx_hash)
761
762         for i, (addr, value) in enumerate(tx.outputs):
763             key = tx_hash+ ':%d'%i
764             self.prevout_values[key] = value
765
766         for item in tx.inputs:
767             if self.is_mine(item.get('address')):
768                 key = item['prevout_hash'] + ':%d'%item['prevout_n']
769                 self.spent_outputs.append(key)
770
771
772     def get_addr_balance(self, address):
773         assert self.is_mine(address)
774         h = self.history.get(address,[])
775         if h == ['*']: return 0,0
776         c = u = 0
777         received_coins = []   # list of coins received at address
778
779         for tx_hash, tx_height in h:
780             tx = self.transactions.get(tx_hash)
781             if not tx: continue
782
783             for i, (addr, value) in enumerate(tx.outputs):
784                 if addr == address:
785                     key = tx_hash + ':%d'%i
786                     received_coins.append(key)
787
788         for tx_hash, tx_height in h:
789             tx = self.transactions.get(tx_hash)
790             if not tx: continue
791             v = 0
792
793             for item in tx.inputs:
794                 addr = item.get('address')
795                 if addr == address:
796                     key = item['prevout_hash']  + ':%d'%item['prevout_n']
797                     value = self.prevout_values.get( key )
798                     if key in received_coins: 
799                         v -= value
800
801             for i, (addr, value) in enumerate(tx.outputs):
802                 key = tx_hash + ':%d'%i
803                 if addr == address:
804                     v += value
805
806             if tx_height:
807                 c += v
808             else:
809                 u += v
810         return c, u
811
812
813     def get_accounts(self):
814         accounts = {}
815         for k, account in self.accounts.items():
816             accounts[k] = self.labels.get(k, 'unnamed')
817         if self.imported_keys:
818             accounts[-1] = 'Imported keys'
819         return accounts
820
821     def get_account_addresses(self, a, include_change=True):
822         if a is None:
823             o = self.addresses(True)
824         elif a == -1:
825             o = self.imported_keys.keys()
826         else:
827             ac = self.accounts[a]
828             o = ac.get_addresses(0)
829             if include_change: o += ac.get_addresses(1)
830         return o
831
832     def get_imported_balance(self):
833         cc = uu = 0
834         for addr in self.imported_keys.keys():
835             c, u = self.get_addr_balance(addr)
836             cc += c
837             uu += u
838         return cc, uu
839
840     def get_account_balance(self, account):
841         if account is None:
842             return self.get_balance()
843         elif account == -1:
844             return self.get_imported_balance()
845         
846         conf = unconf = 0
847         for addr in self.get_account_addresses(account): 
848             c, u = self.get_addr_balance(addr)
849             conf += c
850             unconf += u
851         return conf, unconf
852
853     def get_frozen_balance(self):
854         conf = unconf = 0
855         for addr in self.frozen_addresses:
856             c, u = self.get_addr_balance(addr)
857             conf += c
858             unconf += u
859         return conf, unconf
860
861         
862     def get_balance(self):
863         cc = uu = 0
864         for a in self.accounts.keys():
865             c, u = self.get_account_balance(a)
866             cc += c
867             uu += u
868         c, u = self.get_imported_balance()
869         cc += c
870         uu += u
871         return cc, uu
872
873
874     def get_unspent_coins(self, domain=None):
875         coins = []
876         if domain is None: domain = self.addresses(True)
877         for addr in domain:
878             h = self.history.get(addr, [])
879             if h == ['*']: continue
880             for tx_hash, tx_height in h:
881                 tx = self.transactions.get(tx_hash)
882                 if tx is None: raise BaseException("Wallet not synchronized")
883                 for output in tx.d.get('outputs'):
884                     if output.get('address') != addr: continue
885                     key = tx_hash + ":%d" % output.get('index')
886                     if key in self.spent_outputs: continue
887                     output['tx_hash'] = tx_hash
888                     coins.append(output)
889         return coins
890
891
892
893     def choose_tx_inputs( self, amount, fixed_fee, account = None ):
894         """ todo: minimize tx size """
895         total = 0
896         fee = self.fee if fixed_fee is None else fixed_fee
897         domain = self.get_account_addresses(account)
898         coins = []
899         prioritized_coins = []
900         for i in self.frozen_addresses:
901             if i in domain: domain.remove(i)
902
903         for i in self.prioritized_addresses:
904             if i in domain: domain.remove(i)
905
906         coins = self.get_unspent_coins(domain)
907         prioritized_coins = self.get_unspent_coins(self.prioritized_addresses)
908
909         inputs = []
910         coins = prioritized_coins + coins
911
912         for item in coins: 
913             addr = item.get('address')
914             v = item.get('value')
915             total += v
916             inputs.append( item )
917             fee = self.estimated_fee(inputs) if fixed_fee is None else fixed_fee
918             if total >= amount + fee: break
919         else:
920             inputs = []
921
922         return inputs, total, fee
923
924
925     def set_fee(self, fee):
926         if self.fee != fee:
927             self.fee = fee
928             self.storage.put('fee_per_kb', self.fee, True)
929         
930     def estimated_fee(self, inputs):
931         estimated_size =  len(inputs) * 180 + 80     # this assumes non-compressed keys
932         fee = self.fee * int(round(estimated_size/1024.))
933         if fee == 0: fee = self.fee
934         return fee
935
936
937     def add_tx_change( self, inputs, outputs, amount, fee, total, change_addr=None, account=0 ):
938         "add change to a transaction"
939         change_amount = total - ( amount + fee )
940         if change_amount != 0:
941             if not change_addr:
942                 if account is None: 
943                     # send change to one of the accounts involved in the tx
944                     address = inputs[0].get('address')
945                     account, _ = self.get_address_index(address)
946
947                 if not self.use_change or account == -1:
948                     change_addr = inputs[-1]['address']
949                 else:
950                     change_addr = self.accounts[account].get_addresses(1)[-self.gap_limit_for_change]
951
952             # Insert the change output at a random position in the outputs
953             posn = random.randint(0, len(outputs))
954             outputs[posn:posn] = [( change_addr,  change_amount)]
955         return outputs
956
957
958     def get_history(self, address):
959         with self.lock:
960             return self.history.get(address)
961
962
963     def get_status(self, h):
964         if not h: return None
965         if h == ['*']: return '*'
966         status = ''
967         for tx_hash, height in h:
968             status += tx_hash + ':%d:' % height
969         return hashlib.sha256( status ).digest().encode('hex')
970
971
972     def receive_tx_callback(self, tx_hash, tx, tx_height):
973         if not self.check_new_tx(tx_hash, tx):
974             # may happen due to pruning
975             print_error("received transaction that is no longer referenced in history", tx_hash)
976             return
977
978         with self.transaction_lock:
979             self.transactions[tx_hash] = tx
980
981             self.interface.pending_transactions_for_notifications.append(tx)
982
983             self.save_transactions()
984             if self.verifier and tx_height>0: 
985                 self.verifier.add(tx_hash, tx_height)
986             self.update_tx_outputs(tx_hash)
987
988
989     def save_transactions(self):
990         tx = {}
991         for k,v in self.transactions.items():
992             tx[k] = str(v)
993         self.storage.put('transactions', tx, True)
994
995     def receive_history_callback(self, addr, hist):
996
997         if not self.check_new_history(addr, hist):
998             raise BaseException("error: received history for %s is not consistent with known transactions"%addr)
999             
1000         with self.lock:
1001             self.history[addr] = hist
1002             self.storage.put('addr_history', self.history, True)
1003
1004         if hist != ['*']:
1005             for tx_hash, tx_height in hist:
1006                 if tx_height>0:
1007                     # add it in case it was previously unconfirmed
1008                     if self.verifier: self.verifier.add(tx_hash, tx_height)
1009
1010
1011     def get_tx_history(self, account=None):
1012         with self.transaction_lock:
1013             history = self.transactions.items()
1014             history.sort(key = lambda x: self.verifier.get_txpos(x[0]))
1015             result = []
1016     
1017             balance = 0
1018             for tx_hash, tx in history:
1019                 is_relevant, is_mine, v, fee = self.get_tx_value(tx, account)
1020                 if v is not None: balance += v
1021
1022             c, u = self.get_account_balance(account)
1023
1024             if balance != c+u:
1025                 result.append( ('', 1000, 0, c+u-balance, None, c+u-balance, None ) )
1026
1027             balance = c + u - balance
1028             for tx_hash, tx in history:
1029                 is_relevant, is_mine, value, fee = self.get_tx_value(tx, account)
1030                 if not is_relevant:
1031                     continue
1032                 if value is not None:
1033                     balance += value
1034
1035                 conf, timestamp = self.verifier.get_confirmations(tx_hash) if self.verifier else (None, None)
1036                 result.append( (tx_hash, conf, is_mine, value, fee, balance, timestamp) )
1037
1038         return result
1039
1040
1041     def get_label(self, tx_hash):
1042         label = self.labels.get(tx_hash)
1043         is_default = (label == '') or (label is None)
1044         if is_default: label = self.get_default_label(tx_hash)
1045         return label, is_default
1046
1047
1048     def get_default_label(self, tx_hash):
1049         tx = self.transactions.get(tx_hash)
1050         default_label = ''
1051         if tx:
1052             is_relevant, is_mine, _, _ = self.get_tx_value(tx)
1053             if is_mine:
1054                 for o in tx.outputs:
1055                     o_addr, _ = o
1056                     if not self.is_mine(o_addr):
1057                         try:
1058                             default_label = self.labels[o_addr]
1059                         except KeyError:
1060                             default_label = o_addr
1061                         break
1062                 else:
1063                     default_label = '(internal)'
1064             else:
1065                 for o in tx.outputs:
1066                     o_addr, _ = o
1067                     if self.is_mine(o_addr) and not self.is_change(o_addr):
1068                         break
1069                 else:
1070                     for o in tx.outputs:
1071                         o_addr, _ = o
1072                         if self.is_mine(o_addr):
1073                             break
1074                     else:
1075                         o_addr = None
1076
1077                 if o_addr:
1078                     dest_label = self.labels.get(o_addr)
1079                     try:
1080                         default_label = self.labels[o_addr]
1081                     except KeyError:
1082                         default_label = o_addr
1083
1084         return default_label
1085
1086
1087     def mktx(self, outputs, password, fee=None, change_addr=None, account=None ):
1088         
1089         for address, x in outputs:
1090             assert is_valid(address)
1091
1092         amount = sum( map(lambda x:x[1], outputs) )
1093
1094         inputs, total, fee = self.choose_tx_inputs( amount, fee, account )
1095         if not inputs:
1096             raise ValueError("Not enough funds")
1097
1098         outputs = self.add_tx_change(inputs, outputs, amount, fee, total, change_addr, account)
1099         tx = Transaction.from_io(inputs, outputs)
1100
1101         keypairs = {}
1102         for i, txin in enumerate(tx.inputs):
1103             address = txin['address']
1104
1105             account, sequence = self.get_address_index(address)
1106             txin['KeyID'] = self.get_keyID(account, sequence)
1107
1108             redeemScript = self.accounts[account].redeem_script(sequence)
1109             if redeemScript: 
1110                 txin['redeemScript'] = redeemScript
1111             else:
1112                 txin['redeemPubkey'] = self.accounts[account].get_pubkey(*sequence)
1113
1114             private_keys = self.get_private_key(address, password)
1115
1116             for sec in private_keys:
1117                 pubkey = public_key_from_private_key(sec)
1118                 keypairs[ pubkey ] = sec
1119
1120         tx.sign(keypairs)
1121         for address, x in outputs:
1122             if address not in self.addressbook and not self.is_mine(address):
1123                 self.addressbook.append(address)
1124
1125         return tx
1126
1127
1128
1129     def sendtx(self, tx):
1130         # synchronous
1131         h = self.send_tx(tx)
1132         self.tx_event.wait()
1133         return self.receive_tx(h)
1134
1135     def send_tx(self, tx):
1136         # asynchronous
1137         self.tx_event.clear()
1138         self.interface.send([('blockchain.transaction.broadcast', [str(tx)])], 'synchronizer')
1139         return tx.hash()
1140
1141     def receive_tx(self,tx_hash):
1142         out = self.tx_result 
1143         if out != tx_hash:
1144             return False, "error: " + out
1145         return True, out
1146
1147
1148
1149     def update_password(self, seed, old_password, new_password):
1150         if new_password == '': new_password = None
1151         # this will throw an exception if unicode cannot be converted
1152         self.seed = pw_encode( seed, new_password)
1153         self.storage.put('seed', self.seed, True)
1154         self.use_encryption = (new_password != None)
1155         self.storage.put('use_encryption', self.use_encryption,True)
1156         for k in self.imported_keys.keys():
1157             a = self.imported_keys[k]
1158             b = pw_decode(a, old_password)
1159             c = pw_encode(b, new_password)
1160             self.imported_keys[k] = c
1161         self.storage.put('imported_keys', self.imported_keys, True)
1162
1163         for k, v in self.master_private_keys.items():
1164             b = pw_decode(v, old_password)
1165             c = pw_encode(b, new_password)
1166             self.master_private_keys[k] = c
1167         self.storage.put('master_private_keys', self.master_private_keys, True)
1168
1169
1170     def freeze(self,addr):
1171         if self.is_mine(addr) and addr not in self.frozen_addresses:
1172             self.unprioritize(addr)
1173             self.frozen_addresses.append(addr)
1174             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1175             return True
1176         else:
1177             return False
1178
1179     def unfreeze(self,addr):
1180         if self.is_mine(addr) and addr in self.frozen_addresses:
1181             self.frozen_addresses.remove(addr)
1182             self.storage.put('frozen_addresses', self.frozen_addresses, True)
1183             return True
1184         else:
1185             return False
1186
1187     def prioritize(self,addr):
1188         if self.is_mine(addr) and addr not in self.prioritized_addresses:
1189             self.unfreeze(addr)
1190             self.prioritized_addresses.append(addr)
1191             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1192             return True
1193         else:
1194             return False
1195
1196     def unprioritize(self,addr):
1197         if self.is_mine(addr) and addr in self.prioritized_addresses:
1198             self.prioritized_addresses.remove(addr)
1199             self.storage.put('prioritized_addresses', self.prioritized_addresses, True)
1200             return True
1201         else:
1202             return False
1203
1204
1205     def set_verifier(self, verifier):
1206         self.verifier = verifier
1207
1208         # review transactions that are in the history
1209         for addr, hist in self.history.items():
1210             if hist == ['*']: continue
1211             for tx_hash, tx_height in hist:
1212                 if tx_height>0:
1213                     # add it in case it was previously unconfirmed
1214                     self.verifier.add(tx_hash, tx_height)
1215
1216
1217         # if we are on a pruning server, remove unverified transactions
1218         vr = self.verifier.transactions.keys() + self.verifier.verified_tx.keys()
1219         for tx_hash in self.transactions.keys():
1220             if tx_hash not in vr:
1221                 self.transactions.pop(tx_hash)
1222
1223
1224
1225     def check_new_history(self, addr, hist):
1226         
1227         # check that all tx in hist are relevant
1228         if hist != ['*']:
1229             for tx_hash, height in hist:
1230                 tx = self.transactions.get(tx_hash)
1231                 if not tx: continue
1232                 if not tx.has_address(addr):
1233                     return False
1234
1235         # check that we are not "orphaning" a transaction
1236         old_hist = self.history.get(addr,[])
1237         if old_hist == ['*']: return True
1238
1239         for tx_hash, height in old_hist:
1240             if tx_hash in map(lambda x:x[0], hist): continue
1241             found = False
1242             for _addr, _hist in self.history.items():
1243                 if _addr == addr: continue
1244                 if _hist == ['*']: continue
1245                 _tx_hist = map(lambda x:x[0], _hist)
1246                 if tx_hash in _tx_hist:
1247                     found = True
1248                     break
1249
1250             if not found:
1251                 tx = self.transactions.get(tx_hash)
1252                 # tx might not be there
1253                 if not tx: continue
1254                 
1255                 # already verified?
1256                 if self.verifier.get_height(tx_hash):
1257                     continue
1258                 # unconfirmed tx
1259                 print_error("new history is orphaning transaction:", tx_hash)
1260                 # check that all outputs are not mine, request histories
1261                 ext_requests = []
1262                 for _addr, _v in tx.outputs:
1263                     # assert not self.is_mine(_addr)
1264                     ext_requests.append( ('blockchain.address.get_history', [_addr]) )
1265
1266                 ext_h = self.interface.synchronous_get(ext_requests)
1267                 print_error("sync:", ext_requests, ext_h)
1268                 height = None
1269                 for h in ext_h:
1270                     if h == ['*']: continue
1271                     for item in h:
1272                         if item.get('tx_hash') == tx_hash:
1273                             height = item.get('height')
1274                 if height:
1275                     print_error("found height for", tx_hash, height)
1276                     self.verifier.add(tx_hash, height)
1277                 else:
1278                     print_error("removing orphaned tx from history", tx_hash)
1279                     self.transactions.pop(tx_hash)
1280
1281         return True
1282
1283
1284
1285     def check_new_tx(self, tx_hash, tx):
1286         # 1 check that tx is referenced in addr_history. 
1287         addresses = []
1288         for addr, hist in self.history.items():
1289             if hist == ['*']:continue
1290             for txh, height in hist:
1291                 if txh == tx_hash: 
1292                     addresses.append(addr)
1293
1294         if not addresses:
1295             return False
1296
1297         # 2 check that referencing addresses are in the tx
1298         for addr in addresses:
1299             if not tx.has_address(addr):
1300                 return False
1301
1302         return True
1303
1304
1305     def start_threads(self, interface, blockchain):
1306         from verifier import TxVerifier
1307         self.interface = interface
1308         self.verifier = TxVerifier(interface, blockchain, self.storage)
1309         self.verifier.start()
1310         self.synchronizer = WalletSynchronizer(self)
1311         self.synchronizer.start()
1312
1313     def stop_threads(self):
1314         self.verifier.stop()
1315         self.synchronizer.stop()
1316
1317
1318
1319
1320 class WalletSynchronizer(threading.Thread):
1321
1322
1323     def __init__(self, wallet):
1324         threading.Thread.__init__(self)
1325         self.daemon = True
1326         self.wallet = wallet
1327         wallet.synchronizer = self
1328         self.interface = self.wallet.interface
1329         self.interface.register_channel('synchronizer')
1330         self.wallet.interface.register_callback('connected', lambda: self.wallet.set_up_to_date(False))
1331         self.was_updated = True
1332         self.running = False
1333         self.lock = threading.Lock()
1334
1335     def stop(self):
1336         with self.lock: self.running = False
1337         self.interface.poke('synchronizer')
1338
1339     def is_running(self):
1340         with self.lock: return self.running
1341
1342     
1343     def subscribe_to_addresses(self, addresses):
1344         messages = []
1345         for addr in addresses:
1346             messages.append(('blockchain.address.subscribe', [addr]))
1347         self.interface.send( messages, 'synchronizer')
1348
1349
1350     def run(self):
1351         with self.lock: self.running = True
1352
1353         requested_tx = []
1354         missing_tx = []
1355         requested_histories = {}
1356
1357         # request any missing transactions
1358         for history in self.wallet.history.values():
1359             if history == ['*']: continue
1360             for tx_hash, tx_height in history:
1361                 if self.wallet.transactions.get(tx_hash) is None and (tx_hash, tx_height) not in missing_tx:
1362                     missing_tx.append( (tx_hash, tx_height) )
1363         print_error("missing tx", missing_tx)
1364
1365         # wait until we are connected, in case the user is not connected
1366         while not self.interface.is_connected:
1367             time.sleep(1)
1368         
1369         # subscriptions
1370         self.subscribe_to_addresses(self.wallet.addresses(True))
1371
1372         while self.is_running():
1373             # 1. create new addresses
1374             new_addresses = self.wallet.synchronize()
1375
1376             # request missing addresses
1377             if new_addresses:
1378                 self.subscribe_to_addresses(new_addresses)
1379
1380             # request missing transactions
1381             for tx_hash, tx_height in missing_tx:
1382                 if (tx_hash, tx_height) not in requested_tx:
1383                     self.interface.send([ ('blockchain.transaction.get',[tx_hash, tx_height]) ], 'synchronizer')
1384                     requested_tx.append( (tx_hash, tx_height) )
1385             missing_tx = []
1386
1387             # detect if situation has changed
1388             if not self.interface.is_up_to_date('synchronizer'):
1389                 if self.wallet.is_up_to_date():
1390                     self.wallet.set_up_to_date(False)
1391                     self.was_updated = True
1392             else:
1393                 if not self.wallet.is_up_to_date():
1394                     self.wallet.set_up_to_date(True)
1395                     self.was_updated = True
1396
1397             if self.was_updated:
1398                 self.interface.trigger_callback('updated')
1399                 self.was_updated = False
1400
1401             # 2. get a response
1402             r = self.interface.get_response('synchronizer')
1403
1404             # poke sends None. (needed during stop)
1405             if not r: continue
1406
1407             # 3. handle response
1408             method = r['method']
1409             params = r['params']
1410             result = r.get('result')
1411             error = r.get('error')
1412             if error:
1413                 print "error", r
1414                 continue
1415
1416             if method == 'blockchain.address.subscribe':
1417                 addr = params[0]
1418                 if self.wallet.get_status(self.wallet.get_history(addr)) != result:
1419                     if requested_histories.get(addr) is None:
1420                         self.interface.send([('blockchain.address.get_history', [addr])], 'synchronizer')
1421                         requested_histories[addr] = result
1422
1423             elif method == 'blockchain.address.get_history':
1424                 addr = params[0]
1425                 print_error("receiving history", addr, result)
1426                 if result == ['*']:
1427                     assert requested_histories.pop(addr) == '*'
1428                     self.wallet.receive_history_callback(addr, result)
1429                 else:
1430                     hist = []
1431                     # check that txids are unique
1432                     txids = []
1433                     for item in result:
1434                         tx_hash = item['tx_hash']
1435                         if tx_hash not in txids:
1436                             txids.append(tx_hash)
1437                             hist.append( (tx_hash, item['height']) )
1438
1439                     if len(hist) != len(result):
1440                         raise BaseException("error: server sent history with non-unique txid", result)
1441
1442                     # check that the status corresponds to what was announced
1443                     rs = requested_histories.pop(addr)
1444                     if self.wallet.get_status(hist) != rs:
1445                         raise BaseException("error: status mismatch: %s"%addr)
1446                 
1447                     # store received history
1448                     self.wallet.receive_history_callback(addr, hist)
1449
1450                     # request transactions that we don't have 
1451                     for tx_hash, tx_height in hist:
1452                         if self.wallet.transactions.get(tx_hash) is None:
1453                             if (tx_hash, tx_height) not in requested_tx and (tx_hash, tx_height) not in missing_tx:
1454                                 missing_tx.append( (tx_hash, tx_height) )
1455
1456             elif method == 'blockchain.transaction.get':
1457                 tx_hash = params[0]
1458                 tx_height = params[1]
1459                 assert tx_hash == hash_encode(Hash(result.decode('hex')))
1460                 tx = Transaction(result)
1461                 self.wallet.receive_tx_callback(tx_hash, tx, tx_height)
1462                 self.was_updated = True
1463                 requested_tx.remove( (tx_hash, tx_height) )
1464                 print_error("received tx:", tx_hash, len(tx.raw))
1465
1466             elif method == 'blockchain.transaction.broadcast':
1467                 self.wallet.tx_result = result
1468                 self.wallet.tx_event.set()
1469
1470             else:
1471                 print_error("Error: Unknown message:" + method + ", " + repr(params) + ", " + repr(result) )
1472
1473             if self.was_updated and not requested_tx:
1474                 self.interface.trigger_callback('updated')
1475                 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
1476                 
1477
1478                 self.was_updated = False