initial
[electrum-nvc.git] / client / electrum
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
20 import sys, base64, os, re, hashlib, socket, getpass, copy, operator, urllib2, ast
21
22 try:
23     import ecdsa  
24 except:
25     print "python-ecdsa does not seem to be installed. Try 'sudo easy_install ecdsa'"
26     exit(1)
27
28 try:
29     import Crypto
30     has_encryption = True
31 except:
32     has_encryption = False
33
34
35 ############ functions from pywallet ##################### 
36
37 addrtype = 0
38
39 def hash_160(public_key):
40     md = hashlib.new('ripemd160')
41     md.update(hashlib.sha256(public_key).digest())
42     return md.digest()
43
44 def public_key_to_bc_address(public_key):
45     h160 = hash_160(public_key)
46     return hash_160_to_bc_address(h160)
47
48 def hash_160_to_bc_address(h160):
49     vh160 = chr(addrtype) + h160
50     h = Hash(vh160)
51     addr = vh160 + h[0:4]
52     return b58encode(addr)
53
54 def bc_address_to_hash_160(addr):
55     bytes = b58decode(addr, 25)
56     return bytes[1:21]
57
58 __b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
59 __b58base = len(__b58chars)
60
61 def b58encode(v):
62     """ encode v, which is a string of bytes, to base58.                
63     """
64
65     long_value = 0L
66     for (i, c) in enumerate(v[::-1]):
67         long_value += (256**i) * ord(c)
68
69     result = ''
70     while long_value >= __b58base:
71         div, mod = divmod(long_value, __b58base)
72         result = __b58chars[mod] + result
73         long_value = div
74     result = __b58chars[long_value] + result
75
76     # Bitcoin does a little leading-zero-compression:
77     # leading 0-bytes in the input become leading-1s
78     nPad = 0
79     for c in v:
80         if c == '\0': nPad += 1
81         else: break
82
83     return (__b58chars[0]*nPad) + result
84
85 def b58decode(v, length):
86     """ decode v into a string of len bytes
87     """
88     long_value = 0L
89     for (i, c) in enumerate(v[::-1]):
90         long_value += __b58chars.find(c) * (__b58base**i)
91
92     result = ''
93     while long_value >= 256:
94         div, mod = divmod(long_value, 256)
95         result = chr(mod) + result
96         long_value = div
97     result = chr(long_value) + result
98
99     nPad = 0
100     for c in v:
101         if c == __b58chars[0]: nPad += 1
102         else: break
103
104     result = chr(0)*nPad + result
105     if length is not None and len(result) != length:
106         return None
107
108     return result
109
110
111 def Hash(data):
112     return hashlib.sha256(hashlib.sha256(data).digest()).digest()
113
114 def EncodeBase58Check(vchIn):
115     hash = Hash(vchIn)
116     return b58encode(vchIn + hash[0:4])
117
118 def DecodeBase58Check(psz):
119     vchRet = b58decode(psz, None)
120     key = vchRet[0:-4]
121     csum = vchRet[-4:]
122     hash = Hash(key)
123     cs32 = hash[0:4]
124     if cs32 != csum:
125         return None
126     else:
127         return key
128
129 def PrivKeyToSecret(privkey):
130     return privkey[9:9+32]
131
132 def SecretToASecret(secret):
133     vchIn = chr(addrtype+128) + secret
134     return EncodeBase58Check(vchIn)
135
136 def ASecretToSecret(key):
137     vch = DecodeBase58Check(key)
138     if vch and vch[0] == chr(addrtype+128):
139         return vch[1:]
140     else:
141         return False
142
143 ########### end pywallet functions #######################
144
145
146 def int_to_hex(i, length=1):
147     s = hex(i)[2:]
148     s = "0"*(2*length - len(s)) + s
149     return s.decode('hex')[::-1].encode('hex')
150
151
152 # password encryption
153 from Crypto.Cipher import AES
154 BLOCK_SIZE = 32
155 PADDING = '{'
156 pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING
157 EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
158 DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
159
160
161 # secp256k1, http://www.oid-info.com/get/1.3.132.0.10
162 _p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2FL
163 _r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L
164 _b = 0x0000000000000000000000000000000000000000000000000000000000000007L
165 _a = 0x0000000000000000000000000000000000000000000000000000000000000000L
166 _Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L
167 _Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8L
168 curve_secp256k1 = ecdsa.ellipticcurve.CurveFp( _p, _a, _b )
169 generator_secp256k1 = ecdsa.ellipticcurve.Point( curve_secp256k1, _Gx, _Gy, _r )
170 oid_secp256k1 = (1,3,132,0,10)
171 SECP256k1 = ecdsa.curves.Curve("SECP256k1", curve_secp256k1, generator_secp256k1, oid_secp256k1 ) 
172
173
174 no_wallet_message = "Wallet file not found.\nPlease provide a passphrase and a password. The passphrase will be used as a seed to generate Bitcoin addresses. It should be long and random, and nobody should be able to guess it. Memorize it, or write it down and keep it in a vault. The password will be used to encrypt your local wallet file. You will need to enter your password everytime you use your wallet. If you lose your password, you can still recover your wallet with the passphrase."
175
176 def filter(s): 
177     out = re.sub('( [^\n]*|)\n','',s)
178     out = out.replace(' ','')
179     out = out.replace('\n','')
180     return out
181
182 def raw_tx( inputs, outputs, for_sig = None ):
183     s  = int_to_hex(1,4)                                     +   '     version\n' 
184     s += int_to_hex( len(inputs) )                           +   '     number of inputs\n'
185     for i in range(len(inputs)):
186         _, _, p_hash, p_index, p_script, pubkey, sig = inputs[i]
187         s += p_hash.decode('hex')[::-1].encode('hex')        +  '     prev hash\n'
188         s += int_to_hex(p_index,4)                           +  '     prev index\n'
189         if for_sig is None:
190             sig = sig + chr(1)                               # hashtype
191             script  = int_to_hex( len(sig))                  +  '     push %d bytes\n'%len(sig)
192             script += sig.encode('hex')                      +  '     sig\n'
193             pubkey = chr(4) + pubkey
194             script += int_to_hex( len(pubkey))               +  '     push %d bytes\n'%len(pubkey)
195             script += pubkey.encode('hex')                   +  '     pubkey\n'
196         elif for_sig==i:
197             script = p_script                                +  '     scriptsig \n'
198         else:
199             script=''
200         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
201         s += script
202         s += "ffffffff"                                      +  '     sequence\n'
203     s += int_to_hex( len(outputs) )                          +  '     number of outputs\n'
204     for output in outputs:
205         addr, amount = output
206         s += int_to_hex( amount, 8)                          +  '     amount: %d\n'%amount 
207         script = '76a9'                                      # op_dup, op_hash_160
208         script += '14'                                       # push 0x14 bytes
209         script += bc_address_to_hash_160(addr).encode('hex')
210         script += '88ac'                                     # op_equalverify, op_checksig
211         s += int_to_hex( len(filter(script))/2 )             +  '     script length \n'
212         s += script                                          +  '     script \n'
213     s += int_to_hex(0,4)                                     # lock time
214     if for_sig is not None: s += int_to_hex(1, 4)            # hash type
215     return s
216
217 class InvalidPassword(Exception):
218     pass
219
220 wallet_path = os.environ["HOME"] + '/.bitcoin/electrum.dat'
221
222 class Wallet:
223     def __init__(self):
224         self.gap_limit = 5           # configuration
225         self.host = 'ecdsa.org'
226         self.port = 50000
227         self.fee = 0.005
228
229         # saved fields
230         self.use_encryption = False
231         self.addresses = []
232         self.passphrase = ''         # encrypted
233         self.private_keys = repr([]) # encrypted
234         self.change_addresses = []   # index of addresses used as change
235         self.status = {}             # current status of addresses
236         self.history = {}
237         self.labels = {}             # labels for addresses and transactions
238         self.addressbook = []        # outgoing addresses, for payments
239         self.blocks = 0 
240
241         # not saved
242         self.message = ''
243         self.tx_history = {}
244
245     def is_mine(self, address):
246         return address in self.addresses
247
248     def is_change(self, address):
249         if not self.is_mine(address): 
250             return False
251         k = self.addresses.index(address)
252         return k in self.change_addresses
253
254     def is_valid(self,addr):
255         ADDRESS_RE = re.compile('[1-9A-HJ-NP-Za-km-z]{26,}\\Z')
256         return ADDRESS_RE.match(addr)
257
258     def create_new_address(self, for_change, password):
259         passphrase = self.pw_decode( self.passphrase, password)
260         i = len( self.addresses ) - len(self.change_addresses) if not for_change else len(self.change_addresses)
261         seed = Hash( "%d:%d:"%(i,for_change) + passphrase )
262         order = generator_secp256k1.order()
263         secexp = ecdsa.util.randrange_from_seed__trytryagain( seed, order )
264         secret = SecretToASecret( ('%064x' % secexp).decode('hex') )
265         private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve = SECP256k1 )
266         public_key = private_key.get_verifying_key()
267         address = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() )
268         try:
269             private_keys = ast.literal_eval( self.pw_decode( self.private_keys, password) )
270             private_keys.append(secret)
271         except:
272             raise InvalidPassword("")
273         self.private_keys = self.pw_encode( repr(private_keys), password)
274         self.addresses.append(address)
275         if for_change: self.change_addresses.append( i )
276         h = self.retrieve_history(address)
277         self.history[address] = h
278         self.status[address] = h[-1]['blk_hash'] if h else None
279         return address
280
281     def recover(self, password):
282         passphrase = self.pw_decode( self.passphrase, password)
283         # todo: recover receiving addresses from tx
284         num_gap = 0
285         while True:
286             addr = self.create_new_address(True, password)
287             print "recovering", addr
288             if self.status[addr] is None: break
289
290         num_gap = 0
291         while True:
292             addr = self.create_new_address(False, password)
293             print "recovering", addr
294             if self.status[addr] is None:
295                 num_gap += 1
296                 if num_gap == self.gap_limit: break
297             else:
298                 num_gap = 0
299
300         # remove limit-1 addresses. [ this is ok, because change addresses are at the beginning of the list]
301         n = self.gap_limit
302         self.addresses = self.addresses[:-n]
303         self.keys = self.private_keys[:-n]
304
305         # history and addressbook
306         self.update_tx_history()
307         for tx in self.tx_history.values():
308             if tx['value']<0:
309                 for i in tx['outputs']:
310                     if not self.is_mine(i) and i not in self.addressbook:
311                         self.addressbook.append(i)
312         # redo labels
313         self.update_tx_labels()
314
315     def save(self):
316         s = repr( (self.use_encryption, self.fee, self.host, self.blocks,
317                    self.passphrase, self.addresses, self.private_keys, 
318                    self.change_addresses, self.status, self.history, 
319                    self.labels, self.addressbook) )
320         f = open(wallet_path,"w")
321         f.write(s)
322         f.close()
323
324     def read(self):
325         try:
326             f = open(wallet_path,"r")
327             data = f.read()
328             f.close()
329         except:
330             return False
331         try:
332             (self.use_encryption, self.fee, self.host, self.blocks, 
333              self.passphrase, self.addresses, self.private_keys, 
334              self.change_addresses, self.status, self.history, 
335              self.labels, self.addressbook) = ast.literal_eval( data )
336         except:
337             return False
338         self.update_tx_history()
339         return True
340         
341     def get_new_address(self, password):
342         n = 0 
343         for addr in self.addresses[-self.gap_limit:]:
344             if self.history[addr] == []: 
345                 n = n + 1
346         if n < self.gap_limit:
347             try:
348                 new_address = self.create_new_address(False, password)
349             except InvalidPassword:
350                 return False, "wrong password"
351             self.save()
352             return True, new_address
353         else:
354             return False, "The last %d addresses in your list have never been used. You should use them first, or increase the allowed gap size in your preferences. "%self.gap_limit
355
356     def get_addr_balance(self, addr):
357         h = self.history.get(addr)
358         c = u = 0
359         for item in h:
360             v = item['value']
361             if item['height']:
362                 c += v
363             else:
364                 u += v
365         return c, u
366
367     def get_balance(self):
368         conf = unconf = 0
369         for addr in self.addresses: 
370             c, u = self.get_addr_balance(addr)
371             conf += c
372             unconf += u
373         return conf, unconf
374
375     def request(self, request ):
376
377         if self.port == 80:
378             try:
379                 out = urllib2.urlopen('http://'+self.host+'/q/tw', request, timeout=5).read()
380             except:
381                 out = ''
382         else:
383             s = socket.socket( socket.AF_INET, socket.SOCK_STREAM)
384             s.connect(( self.host, self.port))
385             s.send(request)
386             out = ''
387             while 1:
388                 msg = s.recv(1024)
389                 if msg: out += msg
390                 else: break
391             s.close()
392
393         if re.match('[^:]\s*\(', out): out = ''
394         return out
395
396     def retrieve_message(self):
397         if not self.message:
398             self.message = self.request( repr ( ('msg', '')))
399
400     def send_tx(self, data):
401         return self.request( repr ( ('tx', data )))
402
403     def retrieve_history(self, address):
404         return ast.literal_eval( self.request( repr ( ('h', address ))) )
405
406     def poll(self):
407         return ast.literal_eval( self.request( repr ( ('poll', '' ))))
408
409     def new_session(self):
410         self.message = self.request( repr ( ('watch', repr(self.addresses) )))
411         
412     def update(self):
413         blocks, changed_addresses = self.poll()
414         self.blocks = blocks
415         for addr, blk_hash in changed_addresses.items():
416             if self.status[addr] != blk_hash:
417                 print "updating history for", addr
418                 self.history[addr] = self.retrieve_history(addr)
419                 self.status[addr] = blk_hash
420         self.update_tx_history()
421         if changed_addresses:
422             return True
423         else:
424             return False
425
426     def choose_inputs_outputs( self, to_addr, amount, fee, password):
427         """ todo: minimize tx size """
428
429         amount = int( 1e8*amount )
430         fee = int( 1e8*fee )
431         total = 0 
432         inputs = []
433         for addr in self.addresses:
434             h = self.history.get(addr)
435             for item in h:
436                 if item.get('raw_scriptPubKey'):
437                     v = item.get('value')
438                     total += v
439                     inputs.append((addr, v, item['tx_hash'], item['pos'], item['raw_scriptPubKey'], None, None) )
440                     if total >= amount + fee: break
441             if total >= amount + fee: break
442         else:
443             print "not enough funds: %d %d"%(total, fee)
444             return False, "not enough funds: %d %d"%(total, fee)
445         outputs = [ (to_addr, amount) ]
446         change_amount = total - ( amount + fee )
447         if change_amount != 0:
448             # first look for unused change addresses 
449             for addr in self.addresses:
450                 i = self.addresses.index(addr)
451                 if i not in self.change_addresses: continue
452                 if self.history.get(addr): continue
453                 change_address = addr
454                 break
455             else:
456                 change_address = self.create_new_address(True, password)
457                 print "new change address", change_address
458             outputs.append( (change_address,  change_amount) )
459         return inputs, outputs
460
461     def sign_inputs( self, inputs, outputs, password ):
462         s_inputs = []
463         for i in range(len(inputs)):
464             addr, v, p_hash, p_pos, p_scriptPubKey, _, _ = inputs[i]
465             private_key = self.get_private_key(addr, password)
466             public_key = private_key.get_verifying_key()
467             pubkey = public_key.to_string()
468             tx = filter( raw_tx( inputs, outputs, for_sig = i ) )
469             sig = private_key.sign_digest( Hash( tx.decode('hex') ), sigencode = ecdsa.util.sigencode_der )
470             assert public_key.verify_digest( sig, Hash( tx.decode('hex') ), sigdecode = ecdsa.util.sigdecode_der)
471             s_inputs.append( (addr, v, p_hash, p_pos, p_scriptPubKey, pubkey, sig) )
472         return s_inputs
473
474     def pw_encode(self, s, password):
475         if password:
476             secret = Hash(password)
477             cipher = AES.new(secret)
478             return EncodeAES(cipher, s)
479         else:
480             return s
481
482     def pw_decode(self, s, password):
483         if password:
484             secret = Hash(password)
485             cipher = AES.new(secret)
486             return DecodeAES(cipher, s)
487         else:
488             return s
489
490     def get_private_key( self, addr, password ):
491         try:
492             private_keys = ast.literal_eval( self.pw_decode( self.private_keys, password ) )
493         except:
494             raise InvalidPassword("")
495         k = self.addresses.index(addr)
496         secret = private_keys[k]
497         b = ASecretToSecret(secret)
498         secexp = int( b.encode('hex'), 16)
499         private_key = ecdsa.SigningKey.from_secret_exponent( secexp, curve=SECP256k1 )
500         public_key = private_key.get_verifying_key()
501         assert addr == public_key_to_bc_address( chr(4) + public_key.to_string() )
502         return private_key
503
504     def get_tx_history(self):
505         lines = self.tx_history.values()
506         lines = sorted(lines, key=operator.itemgetter("nTime"))
507         return lines
508
509     def update_tx_history(self):
510         self.tx_history= {}
511         for addr in self.addresses:
512             for tx in self.history[addr]:
513                 tx_hash = tx['tx_hash']
514                 line = self.tx_history.get(tx_hash)
515                 if not line:
516                     self.tx_history[tx_hash] = copy.copy(tx)
517                     line = self.tx_history.get(tx_hash)
518                 else:
519                     line['value'] += tx['value']
520                 if line['blk_hash'] == 'mempool':
521                     line['nTime'] = 1e12
522         self.update_tx_labels()
523
524     def update_tx_labels(self):
525         for tx in self.tx_history.values():
526             default_label = ''
527             if tx['value']<0:
528                 for o_addr in tx['outputs']:
529                     if not self.is_change(o_addr):
530                         dest_label = self.labels.get(o_addr)
531                         if dest_label:
532                             default_label = 'to: ' + dest_label
533                         else:
534                             default_label = 'to: ' + o_addr
535             else:
536                 for o_addr in tx['outputs']:
537                     if self.is_mine(o_addr) and not self.is_change(o_addr):
538                         dest_label = self.labels.get(o_addr)
539                         if dest_label:
540                             default_label = 'at: ' + dest_label
541                         else:
542                             default_label = 'at: ' + o_addr
543             tx['default_label'] = default_label
544
545
546
547     def send(self, to_address, amount, label, password):
548         try:
549             inputs, outputs = wallet.choose_inputs_outputs( to_address, amount, self.fee, password )
550         except InvalidPassword:  return False, "Wrong password"
551         if not inputs:  return False, "Not enough funds"
552         try:
553             s_inputs = wallet.sign_inputs( inputs, outputs, password )
554         except InvalidPassword:
555             return False, "Wrong password"
556         tx = raw_tx( s_inputs, outputs )
557         tx = filter( tx )
558         tx_hash = Hash(tx.decode('hex') )[::-1].encode('hex')
559         out = self.send_tx(tx)
560         if out != tx_hash:
561             return False, "error: hash mismatch"
562         wallet.labels[tx_hash] = label
563         wallet.save()
564         return True, tx_hash
565
566
567 if __name__ == '__main__':
568     try:
569         cmd = sys.argv[1]
570     except:
571         cmd = "gui"
572
573     known_commands = ['balance', 'sendtoaddress', 'password', 'getnewaddress', 'addresses', 'history', 'label', 'gui', 'all_addresses']
574     if cmd not in known_commands:
575         print "Known commands:", ', '.join(known_commands)
576         exit(0)
577
578     wallet = Wallet()
579     if cmd=='gui':
580         import gui
581         gui.init_wallet(wallet)
582         gui = gui.BitcoinGUI(wallet)
583         gui.main()
584         exit(0)
585
586     if not wallet.read():
587         print no_wallet_message
588         passphrase = raw_input("Enter passphrase: ")
589         if len(passphrase)<20:
590             print "Passphrase too short. Please at least 20 characters"
591             exit(1)
592         if has_encryption:
593             password = getpass.getpass("Password (hit return if you do not wish to encrypt your wallet):")
594             if password:
595                 password2 = getpass.getpass("Confirm password:")
596                 if password != password2:
597                     print "error"
598                     exit(1)
599         else:
600             password = None
601             print "in order to use wallet encryption, please install pycrypto  (sudo easy_install pycrypto)"
602
603         wallet.passphrase = wallet.pw_encode( passphrase, password)
604
605         print "server name and port number (default: ecdsa.org:50000)"
606         host = raw_input("server:")
607         if not host: host = 'ecdsa.org'
608
609         port = raw_input("port:")
610         if not port: port = 50000
611         else: port = int(port)
612
613         print "default fee for transactions (default 0.005)"
614         fee = raw_input("default fee:")
615         if not fee: fee = 0.005 
616
617         wallet.fee = fee
618         wallet.gap_limit = 5
619         wallet.host = host
620         wallet.port = port
621         wallet.recover(password)
622         wallet.save()
623
624     wallet.new_session()
625     wallet.update()
626     wallet.save()
627
628     if cmd in ['sendtoaddress', 'password', 'getnewaddress']:
629         password = getpass.getpass('Password:') if wallet.use_encryption else None
630
631     if cmd == 'balance':
632         c, u = wallet.get_balance()
633         if u:
634             print c*1e-8, u*1e-8
635         else:
636             print c*1e-8
637
638     elif cmd in [ 'addresses', 'all_addresses']:
639         for addr in wallet.addresses:
640             if cmd == 'all_addresses' or not wallet.is_change(addr):
641                 label = wallet.labels.get(addr) if not wallet.is_change(addr) else "[change]"
642                 if label is None: label = ''
643                 h = wallet.history.get(addr)
644                 ni = no = 0
645                 for item in h:
646                     if item['is_in']:  ni += 1
647                     else:              no += 1
648                 print addr, no, ni, wallet.get_addr_balance(addr)[0]*1e-8, label
649
650     if cmd == 'history':
651         lines = wallet.get_tx_history()
652         b = 0 
653         for line in lines:
654             import datetime
655             v = 1.*line['value']/1e8
656             b += v
657             v_str = "%f"%v if v<0 else "+%f"%v
658             try:
659                 time_str = datetime.datetime.fromtimestamp( line['nTime']) 
660             except:
661                 print line['nTime']
662                 time_str = 'pending'
663             label = line.get('label')
664             if not label: label = line['tx_hash']
665             else: label = label + ' '*(64 - len(label) )
666
667             print time_str, " ", label, " ", v_str, " ", "%f"%b
668         print "# balance: ", b
669
670     elif cmd == 'label':
671         try:
672             tx = sys.argv[2]
673             label = ' '.join(sys.argv[3:])
674         except:
675             print "syntax:  label <tx_hash> <text>"
676             exit(1)
677         wallet.labels[tx] = label
678         wallet.save()
679             
680     elif cmd == 'sendtoaddress':
681         try:
682             to_address = sys.argv[2]
683             amount = float(sys.argv[3])
684             label = ' '.join(sys.argv[4:])
685         except:
686             print "syntax: send <recipient> <amount> [label]"
687             exit(1)
688         r, h = wallet.send( to_address, amount, label, password )
689         print h 
690
691     elif cmd == 'getnewaddress':
692         a = wallet.get_new_address()
693         if a: 
694             print a
695         else:
696             print "Maximum gap reached. Increase gap in order to create more addresses."
697
698     elif cmd == 'password':
699         try:
700             passphrase = wallet.pw_decode( wallet.passphrase, password)
701             private_keys = ast.literal_eval( wallet.pw_decode( wallet.private_keys, password) )
702         except:
703             print "sorry"
704             exit(1)
705         new_password = getpass.getpass('New password:')
706         if new_password == getpass.getpass('Confirm new password:'):
707             wallet.use_encryption = (new_password != '')
708             wallet.passphrase = wallet.pw_encode( passphrase, new_password)
709             wallet.private_keys = wallet.pw_encode( repr( private_keys ), new_password)
710             wallet.save()
711         else:
712             print "error: mismatch"
713