fix listunspent, add method wallet.get_unspent_coins
[electrum-nvc.git] / 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 import re
20 import sys, os, time, ast
21 import optparse
22 import platform
23
24 try:
25     import ecdsa  
26 except ImportError:
27     sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
28
29 try:
30     import aes
31 except ImportError:
32     sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
33
34 try:
35     from lib import *
36 except ImportError:
37     from electrum import *
38
39 from decimal import Decimal
40
41 known_commands = {
42     'help':'Prints this help',
43     'validateaddress':'Check that the address is valid', 
44     'balance': "Display the balance of your wallet or of an address.\nSyntax: balance [<address>]", 
45     'contacts': "Show your list of contacts", 
46     'create':'Create a wallet', 
47     'restore':'Restore a wallet', 
48     'payto':"""Create and broadcast a transaction.
49 Syntax: payto <recipient> <amount> [label]
50 <recipient> can be a bitcoin address or a label
51 options:\n  --fee, -f: set transaction fee\n  --fromaddr, -s: send from address -\n  --changeaddr, -c: send change to address
52             """,
53     'sendtx':
54             'Broadcasts a transaction to the network. \nSyntax: sendtx <tx>\n<tx> must be in hexadecimal.',
55     'password': 
56             "Changes your password",
57     'addresses':  
58             """Shows your list of addresses.
59 options:
60   -a: show all addresses, including change addresses
61   -k: show private keys
62   -b: show the balance of addresses""",
63
64     'history':"Shows the transaction history",
65     'label':'Assign a label to an item\nSyntax: label <tx_hash> <label>',
66     'mktx':
67         """Create a signed transaction, password protected.
68 Syntax: mktx <recipient> <amount> [label]
69 options:\n  --fee, -f: set transaction fee\n  --fromaddr, -s: send from address -\n  --changeaddr, -c: send change to address
70         """,
71     'signtx':"Sign an unsigned transaction created by a deseeded wallet\nSyntax: signtx <filename>",
72     'seed':
73             "Print the generation seed of your wallet.",
74     'importprivkey': 
75             'Import a private key\nSyntax: importprivkey <privatekey>',
76     'signmessage':
77             'Signs a message with a key\nSyntax: signmessage <address> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello  This is a weird String "',
78     'verifymessage':
79              'Verifies a signature\nSyntax: verifymessage <address> <signature> <message>\nIf you want to lead or end a message with spaces, or want double spaces inside the message make sure you quote the string. I.e. " Hello  This is a weird String "',
80     'eval':  
81              "Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"",
82     'get': 
83              "Get config parameter.",
84     'set': 
85              "Set config parameter.",
86     'deseed':
87             "Remove seed from the wallet. The seed is stored in a file that has the name of the wallet plus '.seed'",
88     'reseed':
89             "Restore seed of the wallet. The wallet must have no seed, and the seed must match the wallet's master public key.",
90     'freeze':'',
91     'unfreeze':'',
92     'prioritize':'',
93     'unprioritize':'',
94     'dumpprivkey':'similar to bitcoind\'s command',
95     'listunspent':'similar to bitcoind\'s command',
96     'createmultisig':'similar to bitcoind\'s command',
97     'createrawtransaction':'similar to bitcoind\'s command',
98     'decoderawtransaction':'similar to bitcoind\'s command',
99     'signrawtransaction':'similar to bitcoind\'s command',
100     
101     }
102
103
104
105 offline_commands = [ 'password', 'mktx', 'signtx',
106                      'label', 'contacts',
107                      'help', 'validateaddress',
108                      'signmessage', 'verifymessage',
109                      'eval', 'set', 'get', 'create', 'addresses',
110                      'importprivkey', 'seed',
111                      'deseed','reseed',
112                      'freeze','unfreeze',
113                      'prioritize','unprioritize',
114                      'dumpprivkey','listunspent',
115                      'createmultisig', 'createrawtransaction', 'decoderawtransaction', 'signrawtransaction'
116                      ]
117
118
119 protected_commands = ['payto', 'password', 'mktx', 'signtx', 'seed', 'importprivkey','signmessage', 'signrawtransaction','dumpprivkey' ]
120
121 # get password routine
122 def prompt_password(prompt, confirm=True):
123     import getpass
124     if sys.stdin.isatty():
125         password = getpass.getpass(prompt)
126         if password and confirm:
127             password2 = getpass.getpass("Confirm: ")
128             if password != password2:
129                 sys.exit("Error: Passwords do not match.")
130     else:
131         password = raw_input(prompt)
132     if not password:
133         password = None
134     return password
135
136 def arg_parser():
137     usage = "usage: %prog [options] command\nCommands: "+ (', '.join(known_commands))
138     parser = optparse.OptionParser(prog=usage)
139     parser.add_option("-g", "--gui", dest="gui", help="User interface: qt, lite, gtk or text")
140     parser.add_option("-w", "--wallet", dest="wallet_path", help="wallet path (default: electrum.dat)")
141     parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
142     parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
143     parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance at listed addresses")
144     parser.add_option("-k", "--keys",action="store_true", dest="show_keys",default=False, help="show the private keys of listed addresses")
145     parser.add_option("-f", "--fee", dest="tx_fee", default="0.005", help="set tx fee")
146     parser.add_option("-F", "--fromaddr", dest="from_addr", default=None, help="set source address for payto/mktx. if it isn't in the wallet, it will ask for the private key unless supplied in the format public_key:private_key. It's not saved in the wallet.")
147     parser.add_option("-c", "--changeaddr", dest="change_addr", default=None, help="set the change address for payto/mktx. default is a spare address, or the source address if it's not in the wallet")
148     parser.add_option("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is t or h")
149     parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http")
150     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information")
151     parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet")
152     parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI")
153     parser.add_option("-u", "--usb", dest="bitkey", action="store_true", help="Turn on support for hardware wallets (EXPERIMENTAL)")
154     return parser
155
156
157 if __name__ == '__main__':
158
159     parser = arg_parser()
160     options, args = parser.parse_args()
161     set_verbosity(options.verbose)
162
163     # config is an object passed to the various constructors (wallet, interface, gui)
164     if 'ANDROID_DATA' in os.environ:
165         config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android'}
166     else:
167         config_options = eval(str(options))
168         for k, v in config_options.items():
169             if v is None: config_options.pop(k)
170
171     # Wallet migration on Electrum 1.7
172     # Todo: In time we could remove this again
173     if platform.system() == "Windows":
174         util.check_windows_wallet_migration()
175
176     config = SimpleConfig(config_options)
177     wallet = Wallet(config)
178
179     if len(args)==0:
180         url = None
181         cmd = 'gui'
182     elif len(args)==1 and re.match('^bitcoin:', args[0]):
183         url = args[0]
184         cmd = 'gui'
185     else:
186         cmd = args[0]
187         firstarg = args[1] if len(args) > 1 else ''
188        
189     #this entire if/else block is just concerned with importing the 
190     #right GUI toolkit based the GUI command line option given 
191     if cmd == 'gui':
192         pref_gui = config.get('gui','classic')
193
194         if pref_gui == 'gtk':
195             try:
196                 import lib.gui as gui
197             except ImportError:
198                 import electrum.gui as gui
199         elif pref_gui in ['classic', 'qt']:
200             try:
201                 import lib.gui_qt as gui
202             except ImportError:
203                 import electrum.gui_qt as gui
204         elif pref_gui == 'lite':
205               try:
206                   import lib.gui_lite as gui
207               except ImportError:
208                   import electrum.gui_lite as gui
209         elif pref_gui == 'text':
210               try:
211                   import lib.gui_text as gui
212               except ImportError:
213                   import electrum.gui_text as gui
214         elif pref_gui == 'android':
215               try:
216                   import lib.gui_android as gui
217               except ImportError:
218                   import electrum.gui_android as gui
219         else:
220             sys.exit("Error: Unknown GUI: " + pref_gui )
221
222         
223         interface = Interface(config, True)
224         wallet.interface = interface
225         interface.start()
226         if interface.is_connected:
227             interface.send([('server.peers.subscribe',[])])
228
229         set_language(config.get('language'))
230         gui = gui.ElectrumGui(wallet, config)
231
232         found = config.wallet_file_exists
233         if not found:
234             a = gui.restore_or_create()
235             if not a: exit()
236             # select a server.
237             s = gui.network_dialog()
238
239             if a =='create':
240                 wallet.init_seed(None)
241             else:
242                 # ask for seed and gap.
243                 sg = gui.seed_dialog()
244                 if not sg: exit()
245                 seed, gap = sg
246                 if not seed: exit()
247                 wallet.gap_limit = gap
248                 if len(seed) == 128:
249                     wallet.seed = ''
250                     wallet.sequence.master_public_key = seed
251                 else:
252                     wallet.init_seed(str(seed))
253             
254
255             # generate the first addresses, in case we are offline
256             if s is None or a == 'create':
257                 wallet.synchronize()
258             if a == 'create':
259                 # display seed
260                 gui.show_seed()
261
262         verifier = WalletVerifier(interface, config)
263         wallet.set_verifier(verifier)
264         synchronizer = WalletSynchronizer(wallet, config)
265         synchronizer.start()
266
267         if not found and a == 'restore' and s is not None:
268             try:
269                 keep_it = gui.restore_wallet()
270                 wallet.fill_addressbook()
271             except:
272                 import traceback
273                 traceback.print_exc(file=sys.stdout)
274                 exit()
275
276             if not keep_it: exit()
277
278         if not found:
279             gui.password_dialog()
280
281         wallet.save()
282         verifier.start()
283         gui.main(url)
284         wallet.save()
285
286         verifier.stop()
287         synchronizer.stop()
288         interface.stop()
289
290         # we use daemon threads, their termination is enforced.
291         # this sleep command gives them time to terminate cleanly. 
292         time.sleep(0.1)
293         sys.exit(0)
294
295     if cmd not in known_commands:
296         cmd = 'help'
297
298     if not config.wallet_file_exists and cmd not in ['help','create','restore']:
299         print_msg("Error: Wallet file not found.")
300         print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
301         sys.exit(0)
302     
303     if cmd in ['create', 'restore']:
304         if config.wallet_file_exists:
305             sys.exit("Error: Remove the existing wallet first!")
306         password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
307
308         server = config.get('server')
309         if not server: server = pick_random_server()
310         w_host, w_port, w_protocol = server.split(':')
311         host = raw_input("server (default:%s):"%w_host)
312         port = raw_input("port (default:%s):"%w_port)
313         protocol = raw_input("protocol [t=tcp;h=http;n=native] (default:%s):"%w_protocol)
314         fee = raw_input("fee (default:%s):"%( str(Decimal(wallet.fee)/100000000)) )
315         gap = raw_input("gap limit (default 5):")
316         if host: w_host = host
317         if port: w_port = port
318         if protocol: w_protocol = protocol
319         wallet.config.set_key('server', w_host + ':' + w_port + ':' +w_protocol)
320         if fee: wallet.fee = float(fee)
321         if gap: wallet.gap_limit = int(gap)
322
323         if cmd == 'restore':
324             seed = raw_input("seed:")
325             try:
326                 seed.decode('hex')
327             except:
328                 print_error("Warning: Not hex, trying decode.")
329                 seed = mnemonic_decode( seed.split(' ') )
330             if not seed:
331                 sys.exit("Error: No seed")
332
333             if len(seed) == 128:
334                 wallet.seed = None
335                 wallet.sequence.master_public_key = seed
336             else:
337                 wallet.seed = str(seed)
338                 wallet.init_mpk( wallet.seed )
339
340             if not options.offline:
341                 interface = Interface(config)
342                 interface.start()
343                 wallet.interface = interface
344
345                 verifier = WalletVerifier(interface, config)
346                 wallet.set_verifier(verifier)
347
348                 print_msg("Recovering wallet...")
349                 WalletSynchronizer(wallet, config).start()
350                 wallet.update()
351                 if wallet.is_found():
352                     print_msg("Recovery successful")
353                 else:
354                     print_msg("Warning: Found no history for this wallet")
355             else:
356                 wallet.synchronize()
357             wallet.fill_addressbook()
358             wallet.save()
359             print_msg("Wallet saved in '%s'"%wallet.config.path)
360         else:
361             wallet.init_seed(None)
362             wallet.synchronize() # there is no wallet thread 
363             wallet.save()
364             print_msg("Your wallet generation seed is: " + wallet.seed)
365             print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
366             print_msg("Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:")
367             print_msg("\""+' '.join(mnemonic_encode(wallet.seed))+"\"")
368             print_msg("Wallet saved in '%s'"%wallet.config.path)
369             
370         if password:
371             wallet.update_password(wallet.seed, None, password)
372
373     # check syntax
374     if cmd in ['payto', 'mktx']:
375         try:
376             to_address = args[1]
377             amount = int( 100000000 * Decimal(args[2]) )
378             change_addr = None
379             label = ' '.join(args[3:])
380             if options.tx_fee: 
381                 options.tx_fee = int( 100000000 * Decimal(options.tx_fee) )
382         except:
383             firstarg = cmd
384             cmd = 'help'
385
386     # open session
387     if cmd not in offline_commands and not options.offline:
388         interface = Interface(config)
389         interface.register_callback('connected', lambda: sys.stderr.write("Connected to " + interface.connection_msg + "\n"))
390         interface.start()
391         wallet.interface = interface
392         synchronizer = WalletSynchronizer(wallet, config)
393         synchronizer.start()
394         wallet.update()
395         wallet.save()
396
397     # check if --from_addr not in wallet (for mktx/payto)
398     is_temporary = False
399     from_addr = None
400     if options.from_addr:
401         from_addr = options.from_addr
402         if from_addr not in wallet.all_addresses():
403             is_temporary = True
404                 
405     # important warning
406     if cmd=='addresses' and options.show_keys:
407         print_msg("WARNING: ALL your private keys are secret.")
408         print_msg("Exposing a single private key can compromise your entire wallet!")
409         print_msg("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
410
411     # commands needing password
412     if cmd in protected_commands or ( cmd=='addresses' and options.show_keys):
413         if wallet.use_encryption and not is_temporary:
414             password = prompt_password('Password:', False)
415             if not password:
416                 print_msg("Error: Password required")
417                 exit(1)
418             # check password
419             try:
420                 seed = wallet.decode_seed(password)
421             except:
422                 print_msg("Error: This password does not decode this wallet.")
423                 exit(1)
424         else:
425             password = None
426             seed = wallet.seed
427
428     if cmd == 'importprivkey':
429         # See if they specificed a key on the cmd line, if not prompt
430         if len(args) > 1:
431             sec = args[1]
432         else:
433             sec = prompt_password('Enter PrivateKey (will not echo):', False)
434         try:
435             addr = wallet.import_key(sec,password)
436             wallet.save()
437             print_msg("Keypair imported: ", addr)
438         except BaseException as e:
439             print_msg("Error: Keypair import failed: " + str(e))
440
441     if cmd == 'help':
442         cmd2 = firstarg
443         if cmd2 not in known_commands:
444             parser.print_help()
445             print_msg("Type 'electrum help <command>' to see the help for a specific command")
446             print_msg("Type 'electrum --help' to see the list of options")
447             print_msg("List of commands:", ', '.join(known_commands))
448         else:
449             print_msg(known_commands[cmd2])
450
451     elif cmd == 'seed':
452         print_msg(seed + ' "' + ' '.join(mnemonic_encode(seed)) + '"')
453
454     elif cmd == 'deseed':
455         if not wallet.seed:
456             print_msg("Error: This wallet has no seed")
457         elif wallet.use_encryption:
458             print_msg("Error: This wallet is encrypted")
459         else:
460             ns = wallet.config.path + '.seed'
461             print_msg("Warning: you are going to extract the seed from '%s'\nThe seed will be saved in '%s'"%(wallet.config.path,ns))
462             if raw_input("Are you sure you want to continue? (y/n) ") in ['y','Y','yes']:
463                 f = open(ns,'w')
464                 f.write(repr({'seed':wallet.seed, 'imported_keys':wallet.imported_keys})+"\n")
465                 f.close()
466                 wallet.seed = ''
467                 for k in wallet.imported_keys.keys(): wallet.imported_keys[k] = ''
468                 wallet.save()
469                 print_msg("Done.")
470             else:
471                 print_msg("Action canceled.")
472
473     elif cmd == 'reseed':
474         if wallet.seed:
475             print_msg("Warning: This wallet already has a seed", wallet.seed)
476         else:
477             ns = wallet.config.path + '.seed'
478             try:
479                 f = open(ns,'r')
480                 data = f.read()
481                 f.close()
482             except IOError:
483                 sys.exit("Error: Seed file not found")
484             try:
485                 d = ast.literal_eval( data )
486                 seed = d['seed']
487                 imported_keys = d.get('imported_keys',{})
488             except:
489                 sys.exit("Error: Error with seed file")
490
491             mpk = wallet.get_master_public_key()
492             wallet.seed = seed
493             wallet.imported_keys = imported_keys
494             wallet.use_encryption = False
495             wallet.init_mpk(seed)
496             if mpk == wallet.get_master_public_key():
497                 wallet.save()
498                 print_msg("Done: " + wallet.config.path)
499             else:
500                 print_msg("Error: Master public key does not match")
501
502     elif cmd == 'validateaddress':
503         addr = args[1]
504         is_valid = wallet.is_valid(addr)
505         out = { 'isvalid':is_valid }
506         if is_valid:
507             is_mine = wallet.is_mine(addr)
508             out['address'] = addr
509             out['ismine'] = is_mine
510             if is_mine:
511                 out['pubkey'] = wallet.get_public_key(addr)
512             
513         print_json(out)
514
515     elif cmd == 'balance':
516         try:
517             addrs = args[1:]
518         except:
519             pass
520         if addrs == []:
521             c, u = wallet.get_balance()
522             if u:
523                 print_msg(Decimal( c ) / 100000000 , Decimal( u ) / 100000000)
524             else:
525                 print_msg(Decimal( c ) / 100000000)
526         else:
527             for addr in addrs:
528                 c, u = wallet.get_addr_balance(addr)
529                 if u:
530                     print_msg("%s %s, %s" % (addr, str(Decimal(c)/100000000), str(Decimal(u)/100000000)))
531                 else:
532                     print_msg("%s %s" % (addr, str(Decimal(c)/100000000)))
533
534     elif cmd in [ 'contacts']:
535         for addr in wallet.addressbook:
536             print_msg(addr, "   ", wallet.labels.get(addr))
537
538     elif cmd == 'eval':
539         print_msg(eval(args[1]))
540         wallet.save()
541
542     elif cmd == 'get':
543         key = args[1]
544         print_msg(wallet.config.get(key))
545
546     elif cmd == 'set':
547         key, value = args[1:3]
548         if key not in ['seed', 'seed_version', 'master_public_key', 'use_encryption']:
549             wallet.config.set_key(key, value, True)
550             print_msg(True)
551         else:
552             print_msg(False)
553
554     elif cmd in [ 'addresses']:
555         for addr in wallet.all_addresses():
556             if options.show_all or not wallet.is_change(addr):
557
558                 flags = wallet.get_address_flags(addr)
559                 label = wallet.labels.get(addr,'')
560                 
561                 if label: label = "\"%s\""%label
562
563                 if options.show_balance:
564                     h = wallet.history.get(addr,[])
565                     #ni = no = 0
566                     #for item in h:
567                     #    if item['is_input']:  ni += 1
568                     #    else:              no += 1
569                     b = format_satoshis(wallet.get_addr_balance(addr)[0])
570                 else: b=''
571                 m_addr = "%34s"%addr
572                 if options.show_keys:
573                     m_addr += ':' + str(wallet.get_private_key(addr, password))
574                 print_msg(flags, m_addr, b, label)
575
576     if cmd == 'history':
577         import datetime
578         balance = 0
579         for item in wallet.get_tx_history():
580             tx_hash, conf, is_mine, value, fee, balance, timestamp = item
581             try:
582                 time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
583             except:
584                 time_str = "----"
585
586             label, is_default_label = wallet.get_label(tx_hash)
587             if not label: label = tx_hash
588             else: label = label + ' '*(64 - len(label) )
589
590             print_msg("%17s"%time_str, "  " + label + "  " + format_satoshis(value)+ "  "+ format_satoshis(balance))
591         print_msg("# balance: ", format_satoshis(balance))
592
593     elif cmd == 'label':
594         try:
595             tx = args[1]
596             label = ' '.join(args[2:])
597         except:
598             print_msg("Error. Syntax:  label <tx_hash> <text>")
599             sys.exit(1)
600         wallet.labels[tx] = label
601         wallet.save()
602             
603     elif cmd in ['payto', 'mktx']:
604         if from_addr and is_temporary:
605             if from_addr.find(":") == -1:
606                 keypair = from_addr + ":" + prompt_password('Private key:', False)
607             else:
608                 keypair = from_addr
609                 from_addr = keypair.split(':')[0]
610             if not wallet.import_key(keypair,password):
611                 print_msg("Error: Invalid key pair")
612                 exit(1)
613             wallet.history[from_addr] = interface.retrieve_history(from_addr)
614             wallet.update_tx_history()
615             change_addr = from_addr
616
617         if options.change_addr:
618             change_addr = options.change_addr
619
620         for k, v in wallet.labels.items():
621             if v == to_address:
622                 to_address = k
623                 print_msg("alias", to_address)
624                 break
625             if change_addr and v == change_addr:
626                 change_addr = k
627         try:
628             tx = wallet.mktx( [(to_address, amount)], label, password,
629                 fee = options.tx_fee, change_addr = change_addr, from_addr = from_addr )
630         except:
631             import traceback
632             traceback.print_exc(file=sys.stdout)
633             tx = None
634
635         if tx and cmd=='payto': 
636             r, h = wallet.sendtx( tx )
637             print_msg(h)
638         else:
639             print_msg(tx)
640
641         if is_temporary:
642             wallet.imported_keys.pop(from_addr)
643             del(wallet.history[from_addr])
644         wallet.save()
645
646     elif cmd == 'signtx':
647         filename = args[1]
648         f = open(filename, 'r')
649         d = ast.literal_eval(f.read())
650         f.close()
651         inputs = d['inputs']
652         outputs = d['outputs']
653         tx = wallet.signed_tx( inputs, outputs, password )
654         print_msg(tx)
655
656     elif cmd == 'sendtx':
657         tx = args[1]
658         r, h = wallet.sendtx( tx )
659         print_msg(h)
660
661     elif cmd == 'password':
662         new_password = prompt_password('New password:')
663         wallet.update_password(seed, password, new_password)
664
665     elif cmd == 'signmessage':
666         if len(args) < 3:
667             print_msg("Error: Invalid usage of signmessage.")
668             print_msg(known_commands[cmd])
669             sys.exit(1)
670         address = args[1]
671         message = ' '.join(args[2:])
672         if len(args) > 3:
673             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
674
675         print_msg(wallet.sign_message(address, message, password))
676
677     elif cmd == 'verifymessage':
678         try:
679             address = args[1]
680             signature = args[2]
681             message = ' '.join(args[3:])
682         except:
683             print_msg("Error: Not all parameters were given, displaying help instead.")
684             print_msg(known_commands[cmd])
685             sys.exit(1)
686         if len(args) > 4:
687             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
688         EC_KEY.verify_message(address, signature, message)
689         try:
690             EC_KEY.verify_message(address, signature, message)
691             print_msg(True)
692         except BaseException as e:
693             print_error("Verification error: {0}".format(e))
694             print_msg(False)
695
696     elif cmd == 'freeze':
697         addr = args[1]
698         print_msg(wallet.freeze(addr))
699         
700     elif cmd == 'unfreeze':
701         addr = args[1]
702         print_msg(wallet.unfreeze(addr))
703
704     elif cmd == 'prioritize':
705         addr = args[1]
706         print_msg(wallet.prioritize(addr))
707
708     elif cmd == 'unprioritize':
709         addr = args[1]
710         print_msg(wallet.unprioritize(addr))
711
712
713     elif cmd == 'dumpprivkey':
714         addr = args[1]
715         sec = wallet.get_private_key(addr, password)
716         print_msg( sec )
717
718         
719     elif cmd == 'createmultisig':
720         from lib.bitcoin import *
721         num = int(args[1])
722         pubkeys = ast.literal_eval(args[2])
723         assert isinstance(pubkeys,list)
724         s = multisig_script(pubkeys, num)
725         out = { "address": hash_160_to_bc_address(hash_160(s.decode('hex')), 5), "redeemScript":s }
726         print_json(out)
727     
728
729     elif cmd == 'createrawtransaction':
730         inputs = ast.literal_eval(args[1])
731         outputs = ast.literal_eval(args[2])
732         # convert to own format
733         for i in inputs:
734             i['tx_hash'] = i['txid']
735             i['index'] = i['vout']
736         outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
737         tx = Transaction.from_io(inputs, outputs)
738         print_msg( tx )
739
740
741     elif cmd == 'decoderawtransaction':
742         tx = Transaction(args[1])
743         print_json( tx.deserialize() )
744
745
746     elif cmd == 'signrawtransaction':
747         from lib.bitcoin import *
748         tx = Transaction(args[1])
749         txouts = ast.literal_eval(args[2]) if len(args)>2 else []
750         private_keys = ast.literal_eval(args[3]) if len(args)>3 else {}
751
752         for txin in tx.inputs:
753             txid = txin["prevout_hash"]
754             index = txin["prevout_n"]
755             # convert to own format
756             txin['tx_hash'] = txin['prevout_hash']
757             txin['index'] = txin['prevout_n']
758
759             for txout in txouts:
760                 if txout.get('txid') == txid and txout.get('vout') == index:
761                     # compute addr from redeemScript
762                     addr = hash_160_to_bc_address(hash_160(txout['redeemScript'].decode('hex')),5)
763                     txin['address'] = addr
764                     txin['raw_output_script'] = txout['scriptPubKey']
765                     txin['redeemScript'] = txout['redeemScript']
766                     break
767                 
768             else:
769                 if wallet.transactions.get(txid):
770                     # lookup in my own list of transactions
771                     txout = wallet.transactions[txid]['outputs'][index]
772                     txin['address'] = txout['address']
773                     txin['raw_output_script'] = txout['raw_output_script']
774
775                 else:
776                     # if neither, we might want to get it from the server..
777                     raise
778
779         if not private_keys:
780             for txin in tx.inputs:
781                 addr = txin['address']
782                 private_keys[addr] = wallet.get_private_key(addr, password)
783         else:
784             pk = {}
785             for sec in private_keys:
786                 address = bitcoin.address_from_private_key(sec)
787                 pk[address] = sec
788             private_keys = pk
789
790         tx.sign( private_keys )
791         print_msg(tx)
792
793
794     elif cmd == 'listunspent':
795         print_json(wallet.get_unspent_coins())
796         
797
798     if cmd not in offline_commands and not options.offline:
799         synchronizer.stop()
800
801