big refactoring: command line options and electrum.conf options override settings...
[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 getpass
30 import aes
31 import ecdsa
32
33 from ecdsa.util import string_to_number, number_to_string
34 from util import print_error
35 from util import user_dir
36
37 ############ functions from pywallet ##################### 
38
39 addrtype = 0
40
41 def hash_160(public_key):
42     try:
43         md = hashlib.new('ripemd160')
44         md.update(hashlib.sha256(public_key).digest())
45         return md.digest()
46     except:
47         import ripemd
48         md = ripemd.new(hashlib.sha256(public_key).digest())
49         return md.digest()
50
51
52 def public_key_to_bc_address(public_key):
53     h160 = hash_160(public_key)
54     return hash_160_to_bc_address(h160)
55
56 def hash_160_to_bc_address(h160):
57     vh160 = chr(addrtype) + h160
58     h = Hash(vh160)
59     addr = vh160 + h[0:4]
60     return b58encode(addr)
61
62 def bc_address_to_hash_160(addr):
63     bytes = b58decode(addr, 25)
64     return bytes[1:21]
65
66 def encode_point(pubkey, compressed=False):
67     order = generator_secp256k1.order()
68     p = pubkey.pubkey.point
69     x_str = ecdsa.util.number_to_string(p.x(), order)
70     y_str = ecdsa.util.number_to_string(p.y(), order)
71     if compressed:
72         return chr(2 + (p.y() & 1)) + x_str
73     else:
74         return chr(4) + pubkey.to_string() #x_str + y_str
75
76 __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
77 __b58base = len(__b58chars)
78
79 def b58encode(v):
80     """ encode v, which is a string of bytes, to base58."""
81
82     long_value = 0L
83     for (i, c) in enumerate(v[::-1]):
84         long_value += (256**i) * ord(c)
85
86     result = ''
87     while long_value >= __b58base:
88         div, mod = divmod(long_value, __b58base)
89         result = __b58chars[mod] + result
90         long_value = div
91     result = __b58chars[long_value] + result
92
93     # Bitcoin does a little leading-zero-compression:
94     # leading 0-bytes in the input become leading-1s
95     nPad = 0
96     for c in v:
97         if c == '\0': nPad += 1
98         else: break
99
100     return (__b58chars[0]*nPad) + result
101
102 def b58decode(v, length):
103     """ decode v into a string of len bytes."""
104     long_value = 0L
105     for (i, c) in enumerate(v[::-1]):
106         long_value += __b58chars.find(c) * (__b58base**i)
107
108     result = ''
109     while long_value >= 256:
110         div, mod = divmod(long_value, 256)
111         result = chr(mod) + result
112         long_value = div
113     result = chr(long_value) + result
114
115     nPad = 0
116     for c in v:
117         if c == __b58chars[0]: nPad += 1
118         else: break
119
120     result = chr(0)*nPad + result
121     if length is not None and len(result) != length:
122         return None
123
124     return result
125
126
127 def Hash(data):
128     return hashlib.sha256(hashlib.sha256(data).digest()).digest()
129
130 def EncodeBase58Check(vchIn):
131     hash = Hash(vchIn)
132     return b58encode(vchIn + hash[0:4])
133
134 def DecodeBase58Check(psz):
135     vchRet = b58decode(psz, None)
136     key = vchRet[0:-4]
137     csum = vchRet[-4:]
138     hash = Hash(key)
139     cs32 = hash[0:4]
140     if cs32 != csum:
141         return None
142     else:
143         return key
144
145 def PrivKeyToSecret(privkey):
146     return privkey[9:9+32]
147
148 def SecretToASecret(secret):
149     vchIn = chr(addrtype+128) + secret
150     return EncodeBase58Check(vchIn)
151
152 def ASecretToSecret(key):
153     vch = DecodeBase58Check(key)
154     if vch and vch[0] == chr(addrtype+128):
155         return vch[1:]
156     else:
157         return False
158
159 ########### end pywallet functions #######################
160
161 # get password routine
162 def prompt_password(prompt, confirm=True):
163     if sys.stdin.isatty():
164         password = getpass.getpass(prompt)
165
166         if password and confirm:
167             password2 = getpass.getpass("Confirm: ")
168
169             if password != password2:
170                 sys.exit("Error: Passwords do not match.")
171
172     else:
173         password = raw_input(prompt)
174
175     if not password:
176         password = None
177
178     return password
179
180 # URL decode
181 _ud = re.compile('%([0-9a-hA-H]{2})', re.MULTILINE)
182 urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x)
183
184
185 def int_to_hex(i, length=1):
186     s = hex(i)[2:].rstrip('L')
187     s = "0"*(2*length - len(s)) + s
188     return s.decode('hex')[::-1].encode('hex')
189
190
191 # AES
192 EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s))
193 DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e))
194
195
196
197 # secp256k1, http://www.oid-info.com/get/1.3.132.0.10
198 _p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
199 _r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
200 _b = 0x0000000000000000000000000000000000000000000000000000000000000007L
201 _a = 0x0000000000000000000000000000000000000000000000000000000000000000L
202 _Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
203 _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
204 curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
205 generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
206 oid_secp256k1 = (1,3,132,0,10)
207 SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) 
208
209
210 def filter(s): 
211     out = re.sub('( [^\n]*|)\n','',s)
212     out = out.replace(' ','')
213     out = out.replace('\n','')
214     return out
215
216 def raw_tx( inputs, outputs, for_sig = None ):
217     s  = int_to_hex(1,4)                                     +   '     version\n' 
218     s += int_to_hex( len(inputs) )                           +   '     number of inputs\n'
219     for i in range(len(inputs)):
220         _, _, p_hash, p_index, p_script, pubkey, sig = inputs[i]
221         s += p_hash.decode('hex')[::-1].encode('hex')        +  '     prev hash\n'
222         s += int_to_hex(p_index,4)                           +  '     prev index\n'
223         if for_sig is None:
224             sig = sig + chr(1)                               # hashtype
225             script  = int_to_hex( len(sig))                  +  '     push %d bytes\n'%len(sig)
226             script += sig.encode('hex')                      +  '     sig\n'
227             pubkey = chr(4) + pubkey
228             script += int_to_hex( len(pubkey))               +  '     push %d bytes\n'%len(pubkey)
229             script += pubkey.encode('hex')                   +  '     pubkey\n'
230         elif for_sig==i:
231             script = p_script                                +  '     scriptsig \n'
232         else:
233             script=''
234         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
235         s += script
236         s += "ffffffff"                                      +  '     sequence\n'
237     s += int_to_hex( len(outputs) )                          +  '     number of outputs\n'
238     for output in outputs:
239         addr, amount = output
240         s += int_to_hex( amount, 8)                          +  '     amount: %d\n'%amount 
241         script = '76a9'                                      # op_dup, op_hash_160
242         script += '14'                                       # push 0x14 bytes
243         script += bc_address_to_hash_160(addr).encode('hex')
244         script += '88ac'                                     # op_equalverify, op_checksig
245         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
246         s += script                                          +  '     script \n'
247     s += int_to_hex(0,4)                                     # lock time
248     if for_sig is not None: s += int_to_hex(1, 4)            # hash type
249     return s
250
251
252
253
254 def format_satoshis(x, is_diff=False, num_zeros = 0):
255     from decimal import Decimal
256     s = Decimal(x)
257     sign, digits, exp = s.as_tuple()
258     digits = map(str, digits)
259     while len(digits) < 9:
260         digits.insert(0,'0')
261     digits.insert(-8,'.')
262     s = ''.join(digits).rstrip('0')
263     if sign: 
264         s = '-' + s
265     elif is_diff:
266         s = "+" + s
267
268     p = s.find('.')
269     s += "0"*( 1 + num_zeros - ( len(s) - p ))
270     s += " "*( 9 - ( len(s) - p ))
271     s = " "*( 5 - ( p )) + s
272     return s
273
274
275 from version import ELECTRUM_VERSION, SEED_VERSION
276
277
278
279 class Wallet:
280     def __init__(self, config={}):
281
282         self.config = config
283         self.electrum_version = ELECTRUM_VERSION
284         self.update_callbacks = []
285
286         # saved fields
287         self.seed_version          = config.get('seed_version', SEED_VERSION)
288         self.gap_limit             = config.get('gap_limit', 5)
289         self.use_change            = config.get('use_change',True)
290         self.fee                   = int(config.get('fee',100000))
291         self.num_zeros             = int(config.get('num_zeros',0))
292         self.master_public_key     = config.get('master_public_key','').decode('hex')
293         self.use_encryption        = config.get('use_encryption', False)
294         self.addresses             = config.get('addresses', [])          # receiving addresses visible for user
295         self.change_addresses      = config.get('change_addresses', [])   # addresses used as change
296         self.seed                  = config.get('seed', '')               # encrypted
297         self.history               = config.get('history',{})
298         self.labels                = config.get('labels',{})              # labels for addresses and transactions
299         self.aliases               = config.get('aliases', {})            # aliases for addresses
300         self.authorities           = config.get('authorities', {})        # trusted addresses
301         self.frozen_addresses      = config.get('frozen_addresses',[])
302         self.prioritized_addresses = config.get('prioritized_addresses',[])
303         self.receipts              = config.get('receipts',{})            # signed URIs
304         self.addressbook           = config.get('contacts', [])           # outgoing addresses, for payments
305         self.imported_keys         = config.get('imported_keys',{})
306
307         # not saved
308         self.receipt = None          # next receipt
309         self.tx_history = {}
310         self.was_updated = True
311         self.blocks = -1
312         self.banner = ''
313
314         # there is a difference between self.up_to_date and self.is_up_to_date()
315         # self.is_up_to_date() returns true when all requests have been answered and processed
316         # self.up_to_date is true when the wallet is synchronized (stronger requirement)
317         self.up_to_date_event = threading.Event()
318         self.up_to_date_event.clear()
319         self.up_to_date = False
320         self.lock = threading.Lock()
321         self.tx_event = threading.Event()
322
323         self.update_tx_history()
324         if self.seed_version != SEED_VERSION:
325             raise ValueError("This wallet seed is deprecated. Please run upgrade.py for a diagnostic.")
326
327
328     def register_callback(self, update_callback):
329         with self.lock:
330             self.update_callbacks.append(update_callback)
331
332     def trigger_callbacks(self):
333         with self.lock:
334             callbacks = self.update_callbacks[:]
335         [update() for update in callbacks]
336
337     def is_up_to_date(self):
338         return self.interface.responses.empty() and not self.interface.unanswered_requests
339
340
341     def import_key(self, keypair, password):
342         address, key = keypair.split(':')
343         if not self.is_valid(address):
344             raise BaseException('Invalid Bitcoin address')
345         if address in self.all_addresses():
346             raise BaseException('Address already in wallet')
347         b = ASecretToSecret( key )
348         if not b: 
349             raise BaseException('Unsupported key format')
350         secexp = int( b.encode('hex'), 16)
351         private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 )
352         # sanity check
353         public_key = private_key.get_verifying_key()
354         if not address == public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ):
355             raise BaseException('Address does not match private key')
356         self.imported_keys[address] = self.pw_encode( key, password )
357
358
359     def new_seed(self, password):
360         seed = "%032x"%ecdsa.util.randrange( pow(2,128) )
361         #self.init_mpk(seed)
362         # encrypt
363         self.seed = self.pw_encode( seed, password )
364
365
366     def init_mpk(self,seed):
367         # public key
368         curve = SECP256k1
369         secexp = self.stretch_key(seed)
370         master_private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
371         self.master_public_key = master_private_key.get_verifying_key().to_string()
372
373     def all_addresses(self):
374         return self.addresses + self.change_addresses + self.imported_keys.keys()
375
376     def is_mine(self, address):
377         return address in self.all_addresses()
378
379     def is_change(self, address):
380         return address in self.change_addresses
381
382     def is_valid(self,addr):
383         ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z')
384         if not ADDRESS_RE.match(addr): return False
385         try:
386             h = bc_address_to_hash_160(addr)
387         except:
388             return False
389         return addr == hash_160_to_bc_address(h)
390
391     def stretch_key(self,seed):
392         oldseed = seed
393         for i in range(100000):
394             seed = hashlib.sha256(seed + oldseed).digest()
395         return string_to_number( seed )
396
397     def get_sequence(self,n,for_change):
398         return string_to_number( Hash( "%d:%d:"%(n,for_change) + self.master_public_key ) )
399
400     def get_private_key_base58(self, address, password):
401         pk = self.get_private_key(address, password)
402         if pk is None: return None
403         return SecretToASecret( pk )
404
405     def get_private_key(self, address, password):
406         """  Privatekey(type,n) = Master_private_key + H(n|S|type)  """
407         order = generator_secp256k1.order()
408         
409         if address in self.imported_keys.keys():
410             b = self.pw_decode( self.imported_keys[address], password )
411             if not b: return None
412             b = ASecretToSecret( b )
413             secexp = int( b.encode('hex'), 16)
414         else:
415             if address in self.addresses:
416                 n = self.addresses.index(address)
417                 for_change = False
418             elif address in self.change_addresses:
419                 n = self.change_addresses.index(address)
420                 for_change = True
421             else:
422                 raise BaseException("unknown address")
423             try:
424                 seed = self.pw_decode( self.seed, password)
425             except:
426                 raise BaseException("Invalid password")
427             if not seed: return None
428             secexp = self.stretch_key(seed)
429             secexp = ( secexp + self.get_sequence(n,for_change) ) % order
430
431         pk = number_to_string(secexp,order)
432         return pk
433
434     def msg_magic(self, message):
435         return "\x18Bitcoin Signed Message:\n" + chr( len(message) ) + message
436
437     def sign_message(self, address, message, password):
438         private_key = ecdsa.SigningKey.from_string( self.get_private_key(address, password), curve = SECP256k1 )
439         public_key = private_key.get_verifying_key()
440         signature = private_key.sign_digest( Hash( self.msg_magic( message ) ), sigencode = ecdsa.util.sigencode_string )
441         assert public_key.verify_digest( signature, Hash( self.msg_magic( message ) ), sigdecode = ecdsa.util.sigdecode_string)
442         for i in range(4):
443             sig = base64.b64encode( chr(27+i) + signature )
444             try:
445                 self.verify_message( address, sig, message)
446                 return sig
447             except:
448                 continue
449         else:
450             raise BaseException("error: cannot sign message")
451
452
453     def verify_message(self, address, signature, message):
454         """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """
455         from ecdsa import numbertheory, ellipticcurve, util
456         import msqr
457         curve = curve_secp256k1
458         G = generator_secp256k1
459         order = G.order()
460         # extract r,s from signature
461         sig = base64.b64decode(signature)
462         if len(sig) != 65: raise BaseException("Wrong encoding")
463         r,s = util.sigdecode_string(sig[1:], order)
464         nV = ord(sig[0])
465         if nV < 27 or nV >= 35:
466             raise BaseException("Bad encoding")
467         if nV >= 31:
468             compressed = True
469             nV -= 4
470         else:
471             compressed = False
472
473         recid = nV - 27
474         # 1.1
475         x = r + (recid/2) * order
476         # 1.3
477         alpha = ( x * x * x  + curve.a() * x + curve.b() ) % curve.p()
478         beta = msqr.modular_sqrt(alpha, curve.p())
479         y = beta if (beta - recid) % 2 == 0 else curve.p() - beta
480         # 1.4 the constructor checks that nR is at infinity
481         R = ellipticcurve.Point(curve, x, y, order)
482         # 1.5 compute e from message:
483         h = Hash( self.msg_magic( message ) )
484         e = string_to_number(h)
485         minus_e = -e % order
486         # 1.6 compute Q = r^-1 (sR - eG)
487         inv_r = numbertheory.inverse_mod(r,order)
488         Q = inv_r * ( s * R + minus_e * G )
489         public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 )
490         # check that Q is the public key
491         public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string)
492         # check that we get the original signing address
493         addr = public_key_to_bc_address( encode_point(public_key, compressed) )
494         if address != addr:
495             raise BaseException("Bad signature")
496     
497
498     def create_new_address(self, for_change):
499         n = len(self.change_addresses) if for_change else len(self.addresses)
500         address = self.get_new_address(n, for_change)
501         if for_change:
502             self.change_addresses.append(address)
503         else:
504             self.addresses.append(address)
505         self.history[address] = []
506         return address
507         
508     def get_new_address(self, n, for_change):
509         """   Publickey(type,n) = Master_public_key + H(n|S|type)*point  """
510         curve = SECP256k1
511         z = self.get_sequence(n, for_change)
512         master_public_key = ecdsa.VerifyingKey.from_string( self.master_public_key, curve = SECP256k1 )
513         pubkey_point = master_public_key.pubkey.point + z*curve.generator
514         public_key2 = ecdsa.VerifyingKey.from_public_point( pubkey_point, curve = SECP256k1 )
515         address = public_key_to_bc_address( '04'.decode('hex') + public_key2.to_string() )
516         print address
517         return address
518                                                                       
519
520     def change_gap_limit(self, value):
521         if value >= self.gap_limit:
522             self.gap_limit = value
523             self.save()
524             self.interface.poke()
525             return True
526
527         elif value >= self.min_acceptable_gap():
528             k = self.num_unused_trailing_addresses()
529             n = len(self.addresses) - k + value
530             self.addresses = self.addresses[0:n]
531             self.gap_limit = value
532             self.save()
533             return True
534         else:
535             return False
536
537     def num_unused_trailing_addresses(self):
538         k = 0
539         for a in self.addresses[::-1]:
540             if self.history.get(a):break
541             k = k + 1
542         return k
543
544     def min_acceptable_gap(self):
545         # fixme: this assumes wallet is synchronized
546         n = 0
547         nmax = 0
548         k = self.num_unused_trailing_addresses()
549         for a in self.addresses[0:-k]:
550             if self.history.get(a):
551                 n = 0
552             else:
553                 n += 1
554                 if n > nmax: nmax = n
555         return nmax + 1
556
557
558     def synchronize(self):
559         if not self.master_public_key:
560             return []
561
562         new_addresses = []
563         while True:
564             if self.change_addresses == []:
565                 new_addresses.append( self.create_new_address(True) )
566                 continue
567             a = self.change_addresses[-1]
568             if self.history.get(a):
569                 new_addresses.append( self.create_new_address(True) )
570             else:
571                 break
572
573         n = self.gap_limit
574         while True:
575             if len(self.addresses) < n:
576                 new_addresses.append( self.create_new_address(False) )
577                 continue
578             if map( lambda a: self.history.get(a), self.addresses[-n:] ) == n*[[]]:
579                 break
580             else:
581                 new_addresses.append( self.create_new_address(False) )
582
583         return new_addresses
584
585
586     def is_found(self):
587         return (len(self.change_addresses) > 1 ) or ( len(self.addresses) > self.gap_limit )
588
589     def fill_addressbook(self):
590         for tx in self.tx_history.values():
591             if tx['value']<0:
592                 for i in tx['outputs']:
593                     if not self.is_mine(i) and i not in self.addressbook:
594                         self.addressbook.append(i)
595         # redo labels
596         self.update_tx_labels()
597
598
599     def get_address_flags(self, addr):
600         flags = "C" if self.is_change(addr) else "I" if addr in self.imported_keys.keys() else "-" 
601         flags += "F" if addr in self.frozen_addresses else "P" if addr in self.prioritized_addresses else "-"
602         return flags
603         
604
605     def get_addr_balance(self, addr):
606         assert self.is_mine(addr)
607         h = self.history.get(addr,[])
608         c = u = 0
609         for item in h:
610             v = item['value']
611             if item['height']:
612                 c += v
613             else:
614                 u += v
615         return c, u
616
617     def get_balance(self):
618         conf = unconf = 0
619         for addr in self.all_addresses(): 
620             c, u = self.get_addr_balance(addr)
621             conf += c
622             unconf += u
623         return conf, unconf
624
625
626     def choose_tx_inputs( self, amount, fixed_fee, from_addr = None ):
627         """ todo: minimize tx size """
628         total = 0
629         fee = self.fee if fixed_fee is None else fixed_fee
630
631         coins = []
632         prioritized_coins = []
633         domain = [from_addr] if from_addr else self.all_addresses()
634         for i in self.frozen_addresses:
635             if i in domain: domain.remove(i)
636
637         for i in self.prioritized_addresses:
638             if i in domain: domain.remove(i)
639
640         for addr in domain:
641             h = self.history.get(addr)
642             if h is None: continue
643             for item in h:
644                 if item.get('raw_output_script'):
645                     coins.append( (addr,item))
646
647         coins = sorted( coins, key = lambda x: x[1]['timestamp'] )
648
649         for addr in self.prioritized_addresses:
650             h = self.history.get(addr)
651             if h is None: continue
652             for item in h:
653                 if item.get('raw_output_script'):
654                     prioritized_coins.append( (addr,item))
655
656         prioritized_coins = sorted( prioritized_coins, key = lambda x: x[1]['timestamp'] )
657
658         inputs = []
659         coins = prioritized_coins + coins
660
661         for c in coins: 
662             addr, item = c
663             v = item.get('value')
664             total += v
665             inputs.append((addr, v, item['tx_hash'], item['index'], item['raw_output_script'], None, None) )
666             fee = self.fee*len(inputs) if fixed_fee is None else fixed_fee
667             if total >= amount + fee: break
668         else:
669             #print "not enough funds: %s %s"%(format_satoshis(total), format_satoshis(fee))
670             inputs = []
671         return inputs, total, fee
672
673     def choose_tx_outputs( self, to_addr, amount, fee, total, change_addr=None ):
674         outputs = [ (to_addr, amount) ]
675         change_amount = total - ( amount + fee )
676         if change_amount != 0:
677             # normally, the update thread should ensure that the last change address is unused
678             if not change_addr:
679                 change_addr = self.change_addresses[-1]
680             outputs.append( ( change_addr,  change_amount) )
681         return outputs
682
683     def sign_inputs( self, inputs, outputs, password ):
684         s_inputs = []
685         for i in range(len(inputs)):
686             addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
687             private_key = ecdsa.SigningKey.from_string( self.get_private_key(addr, password), curve = SECP256k1 )
688             public_key = private_key.get_verifying_key()
689             pubkey = public_key.to_string()
690             tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
691             sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
692             assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
693             s_inputs.append( (addr, v, p_hash, p_pos, p_scriptPubKey, pubkey, sig) )
694         return s_inputs
695
696     def pw_encode(self, s, password):
697         if password:
698             secret = Hash(password)
699             return EncodeAES(secret, s)
700         else:
701             return s
702
703     def pw_decode(self, s, password):
704         if password is not None:
705             secret = Hash(password)
706             d = DecodeAES(secret, s)
707             if s == self.seed:
708                 try:
709                     d.decode('hex')
710                 except:
711                     raise ValueError("Invalid password")
712             return d
713         else:
714             return s
715
716     def get_status(self, address):
717         h = self.history.get(address)
718         if not h:
719             status = None
720         else:
721             lastpoint = h[-1]
722             status = lastpoint['block_hash']
723             if status == 'mempool': 
724                 status = status + ':%d'% len(h)
725         return status
726
727     def receive_status_callback(self, addr, status):
728         with self.lock:
729             if self.get_status(addr) != status:
730                 #print "updating status for", addr, status
731                 self.interface.get_history(addr)
732
733     def receive_history_callback(self, addr, data): 
734         #print "updating history for", addr
735         with self.lock:
736             self.history[addr] = data
737             self.update_tx_history()
738             self.save()
739
740     def get_tx_history(self):
741         lines = self.tx_history.values()
742         lines = sorted(lines, key=operator.itemgetter("timestamp"))
743         return lines
744
745     def update_tx_history(self):
746         self.tx_history= {}
747         for addr in self.all_addresses():
748             h = self.history.get(addr)
749             if h is None: continue
750             for tx in h:
751                 tx_hash = tx['tx_hash']
752                 line = self.tx_history.get(tx_hash)
753                 if not line:
754                     self.tx_history[tx_hash] = copy.copy(tx)
755                     line = self.tx_history.get(tx_hash)
756                 else:
757                     line['value'] += tx['value']
758                 if line['height'] == 0:
759                     line['timestamp'] = 1e12
760         self.update_tx_labels()
761
762     def update_tx_labels(self):
763         for tx in self.tx_history.values():
764             default_label = ''
765             if tx['value']<0:
766                 for o_addr in tx['outputs']:
767                     if not self.is_mine(o_addr):
768                         try:
769                             default_label = self.labels[o_addr]
770                         except KeyError:
771                             default_label = o_addr
772             else:
773                 for o_addr in tx['outputs']:
774                     if self.is_mine(o_addr) and not self.is_change(o_addr):
775                         break
776                 else:
777                     for o_addr in tx['outputs']:
778                         if self.is_mine(o_addr):
779                             break
780                     else:
781                         o_addr = None
782
783                 if o_addr:
784                     dest_label = self.labels.get(o_addr)
785                     try:
786                         default_label = self.labels[o_addr]
787                     except KeyError:
788                         default_label = o_addr
789
790             tx['default_label'] = default_label
791
792     def mktx(self, to_address, amount, label, password, fee=None, change_addr=None, from_addr= None):
793         if not self.is_valid(to_address):
794             raise ValueError("Invalid address")
795         inputs, total, fee = self.choose_tx_inputs( amount, fee, from_addr )
796         if not inputs:
797             raise ValueError("Not enough funds")
798
799         if not self.use_change and not change_addr:
800             change_addr = inputs[0][0]
801             print "Sending change to", change_addr
802
803         outputs = self.choose_tx_outputs( to_address, amount, fee, total, change_addr )
804         s_inputs = self.sign_inputs( inputs, outputs, password )
805
806         tx = filter( raw_tx( s_inputs, outputs ) )
807         if to_address not in self.addressbook:
808             self.addressbook.append(to_address)
809         if label: 
810             tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
811             self.labels[tx_hash] = label
812
813         return tx
814
815     def sendtx(self, tx):
816         tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
817         self.tx_event.clear()
818         self.interface.send([('blockchain.transaction.broadcast', [tx])])
819         self.tx_event.wait()
820         out = self.tx_result 
821         if out != tx_hash:
822             return False, "error: " + out
823         if self.receipt:
824             self.receipts[tx_hash] = self.receipt
825             self.receipt = None
826         return True, out
827
828
829     def read_alias(self, alias):
830         # this might not be the right place for this function.
831         import urllib
832
833         m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
834         m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
835         if m1:
836             url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) 
837         elif m2:
838             url = 'https://' + alias + '/bitcoin.id'
839         else:
840             return ''
841         try:
842             lines = urllib.urlopen(url).readlines()
843         except:
844             return ''
845
846         # line 0
847         line = lines[0].strip().split(':')
848         if len(line) == 1:
849             auth_name = None
850             target = signing_addr = line[0]
851         else:
852             target, auth_name, signing_addr, signature = line
853             msg = "alias:%s:%s:%s"%(alias,target,auth_name)
854             print msg, signature
855             self.verify_message(signing_addr, signature, msg)
856         
857         # other lines are signed updates
858         for line in lines[1:]:
859             line = line.strip()
860             if not line: continue
861             line = line.split(':')
862             previous = target
863             print repr(line)
864             target, signature = line
865             self.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
866
867         if not self.is_valid(target):
868             raise ValueError("Invalid bitcoin address")
869
870         return target, signing_addr, auth_name
871
872     def update_password(self, seed, old_password, new_password):
873         if new_password == '': new_password = None
874         self.use_encryption = (new_password != None)
875         self.seed = self.pw_encode( seed, new_password)
876         for k in self.imported_keys.keys():
877             a = self.imported_keys[k]
878             b = self.pw_decode(a, old_password)
879             c = self.pw_encode(b, new_password)
880             self.imported_keys[k] = c
881         self.save()
882
883     def get_alias(self, alias, interactive = False, show_message=None, question = None):
884         try:
885             target, signing_address, auth_name = self.read_alias(alias)
886         except BaseException, e:
887             # raise exception if verify fails (verify the chain)
888             if interactive:
889                 show_message("Alias error: " + str(e))
890             return
891
892         print target, signing_address, auth_name
893
894         if auth_name is None:
895             a = self.aliases.get(alias)
896             if not a:
897                 msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
898                 if interactive and question( msg ):
899                     self.aliases[alias] = (signing_address, target)
900                 else:
901                     target = None
902             else:
903                 if signing_address != a[0]:
904                     msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
905                     if interactive and question( msg ):
906                         self.aliases[alias] = (signing_address, target)
907                     else:
908                         target = None
909         else:
910             if signing_address not in self.authorities.keys():
911                 msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
912                 if interactive and question( msg ):
913                     self.authorities[signing_address] = auth_name
914                 else:
915                     target = None
916
917         if target:
918             self.aliases[alias] = (signing_address, target)
919             
920         return target
921
922
923     def parse_url(self, url, show_message, question):
924         o = url[8:].split('?')
925         address = o[0]
926         if len(o)>1:
927             params = o[1].split('&')
928         else:
929             params = []
930
931         amount = label = message = signature = identity = ''
932         for p in params:
933             k,v = p.split('=')
934             uv = urldecode(v)
935             if k == 'amount': amount = uv
936             elif k == 'message': message = uv
937             elif k == 'label': label = uv
938             elif k == 'signature':
939                 identity, signature = uv.split(':')
940                 url = url.replace('&%s=%s'%(k,v),'')
941             else: 
942                 print k,v
943
944         if label and self.labels.get(address) != label:
945             if question('Give label "%s" to address %s ?'%(label,address)):
946                 if address not in self.addressbook and address not in self.all_addresses(): 
947                     self.addressbook.append(address)
948                 self.labels[address] = label
949
950         if signature:
951             if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
952                 signing_address = self.get_alias(identity, True, show_message, question)
953             elif self.is_valid(identity):
954                 signing_address = identity
955             else:
956                 signing_address = None
957             if not signing_address:
958                 return
959             try:
960                 self.verify_message(signing_address, signature, url )
961                 self.receipt = (signing_address, signature, url)
962             except:
963                 show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
964                 address = amount = label = identity = message = ''
965
966         if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', address):
967             payto_address = self.get_alias(address, True, show_message, question)
968             if payto_address:
969                 address = address + ' <' + payto_address + '>'
970
971         return address, amount, label, message, signature, identity, url
972
973
974     def update(self):
975         self.interface.poke()
976         self.up_to_date_event.wait(10000000000)
977
978
979     def start_session(self, interface):
980         self.interface = interface
981         self.interface.send([('server.banner',[]), ('blockchain.numblocks.subscribe',[]), ('server.peers.subscribe',[])])
982         self.interface.subscribe(self.all_addresses())
983
984
985     def freeze(self,addr):
986         if addr in self.all_addresses() and addr not in self.frozen_addresses:
987             self.unprioritize(addr)
988             self.frozen_addresses.append(addr)
989             self.save()
990             return True
991         else:
992             return False
993
994     def unfreeze(self,addr):
995         if addr in self.all_addresses() and addr in self.frozen_addresses:
996             self.frozen_addresses.remove(addr)
997             self.save()
998             return True
999         else:
1000             return False
1001
1002     def prioritize(self,addr):
1003         if addr in self.all_addresses() and addr not in self.prioritized_addresses:
1004             self.unfreeze(addr)
1005             self.prioritized_addresses.append(addr)
1006             self.save()
1007             return True
1008         else:
1009             return False
1010
1011     def unprioritize(self,addr):
1012         if addr in self.all_addresses() and addr in self.prioritized_addresses:
1013             self.prioritized_addresses.remove(addr)
1014             self.save()
1015             return True
1016         else:
1017             return False
1018
1019     def save(self):
1020         s = {
1021             'seed_version': self.seed_version,
1022             'use_encryption': self.use_encryption,
1023             'use_change': self.use_change,
1024             'master_public_key': self.master_public_key.encode('hex'),
1025             'fee': self.fee,
1026             'seed': self.seed,
1027             'addresses': self.addresses,
1028             'change_addresses': self.change_addresses,
1029             'history': self.history, 
1030             'labels': self.labels,
1031             'contacts': self.addressbook,
1032             'imported_keys': self.imported_keys,
1033             'aliases': self.aliases,
1034             'authorities': self.authorities,
1035             'receipts': self.receipts,
1036             'num_zeros': self.num_zeros,
1037             'frozen_addresses': self.frozen_addresses,
1038             'prioritized_addresses': self.prioritized_addresses,
1039             'gap_limit': self.gap_limit,
1040         }
1041         for k, v in s.items():
1042             self.config.set_key(k,v)
1043         self.config.save()