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