use serialized format in signtx
[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                 wallet.config.set_key('seed','', True)
468                 for k in wallet.imported_keys.keys(): wallet.imported_keys[k] = ''
469                 wallet.save()
470                 print_msg("Done.")
471             else:
472                 print_msg("Action canceled.")
473
474     elif cmd == 'reseed':
475         if wallet.seed:
476             print_msg("Warning: This wallet already has a seed", wallet.seed)
477         else:
478             ns = wallet.config.path + '.seed'
479             try:
480                 f = open(ns,'r')
481                 data = f.read()
482                 f.close()
483             except IOError:
484                 sys.exit("Error: Seed file not found")
485             try:
486                 d = ast.literal_eval( data )
487                 seed = d['seed']
488                 imported_keys = d.get('imported_keys',{})
489             except:
490                 sys.exit("Error: Error with seed file")
491
492             mpk = wallet.get_master_public_key()
493             wallet.seed = seed
494             wallet.imported_keys = imported_keys
495             wallet.use_encryption = False
496             wallet.init_mpk(seed)
497             if mpk == wallet.get_master_public_key():
498                 wallet.save()
499                 print_msg("Done: " + wallet.config.path)
500             else:
501                 print_msg("Error: Master public key does not match")
502
503     elif cmd == 'validateaddress':
504         addr = args[1]
505         is_valid = wallet.is_valid(addr)
506         out = { 'isvalid':is_valid }
507         if is_valid:
508             is_mine = wallet.is_mine(addr)
509             out['address'] = addr
510             out['ismine'] = is_mine
511             if is_mine:
512                 out['pubkey'] = wallet.get_public_key(addr)
513             
514         print_json(out)
515
516     elif cmd == 'balance':
517         try:
518             addrs = args[1:]
519         except:
520             pass
521         if addrs == []:
522             c, u = wallet.get_balance()
523             if u:
524                 print_msg(Decimal( c ) / 100000000 , Decimal( u ) / 100000000)
525             else:
526                 print_msg(Decimal( c ) / 100000000)
527         else:
528             for addr in addrs:
529                 c, u = wallet.get_addr_balance(addr)
530                 if u:
531                     print_msg("%s %s, %s" % (addr, str(Decimal(c)/100000000), str(Decimal(u)/100000000)))
532                 else:
533                     print_msg("%s %s" % (addr, str(Decimal(c)/100000000)))
534
535     elif cmd in [ 'contacts']:
536         for addr in wallet.addressbook:
537             print_msg(addr, "   ", wallet.labels.get(addr))
538
539     elif cmd == 'eval':
540         print_msg(eval(args[1]))
541         wallet.save()
542
543     elif cmd == 'get':
544         key = args[1]
545         print_msg(wallet.config.get(key))
546
547     elif cmd == 'set':
548         key, value = args[1:3]
549         if key not in ['seed', 'seed_version', 'master_public_key', 'use_encryption']:
550             wallet.config.set_key(key, value, True)
551             print_msg(True)
552         else:
553             print_msg(False)
554
555     elif cmd in [ 'addresses']:
556         for addr in wallet.all_addresses():
557             if options.show_all or not wallet.is_change(addr):
558
559                 flags = wallet.get_address_flags(addr)
560                 label = wallet.labels.get(addr,'')
561                 
562                 if label: label = "\"%s\""%label
563
564                 if options.show_balance:
565                     h = wallet.history.get(addr,[])
566                     #ni = no = 0
567                     #for item in h:
568                     #    if item['is_input']:  ni += 1
569                     #    else:              no += 1
570                     b = format_satoshis(wallet.get_addr_balance(addr)[0])
571                 else: b=''
572                 m_addr = "%34s"%addr
573                 if options.show_keys:
574                     m_addr += ':' + str(wallet.get_private_key(addr, password))
575                 print_msg(flags, m_addr, b, label)
576
577     if cmd == 'history':
578         import datetime
579         balance = 0
580         for item in wallet.get_tx_history():
581             tx_hash, conf, is_mine, value, fee, balance, timestamp = item
582             try:
583                 time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
584             except:
585                 time_str = "----"
586
587             label, is_default_label = wallet.get_label(tx_hash)
588             if not label: label = tx_hash
589             else: label = label + ' '*(64 - len(label) )
590
591             print_msg("%17s"%time_str, "  " + label + "  " + format_satoshis(value)+ "  "+ format_satoshis(balance))
592         print_msg("# balance: ", format_satoshis(balance))
593
594     elif cmd == 'label':
595         try:
596             tx = args[1]
597             label = ' '.join(args[2:])
598         except:
599             print_msg("Error. Syntax:  label <tx_hash> <text>")
600             sys.exit(1)
601         wallet.labels[tx] = label
602         wallet.save()
603             
604     elif cmd in ['payto', 'mktx']:
605         if from_addr and is_temporary:
606             if from_addr.find(":") == -1:
607                 keypair = from_addr + ":" + prompt_password('Private key:', False)
608             else:
609                 keypair = from_addr
610                 from_addr = keypair.split(':')[0]
611             if not wallet.import_key(keypair,password):
612                 print_msg("Error: Invalid key pair")
613                 exit(1)
614             wallet.history[from_addr] = interface.retrieve_history(from_addr)
615             wallet.update_tx_history()
616             change_addr = from_addr
617
618         if options.change_addr:
619             change_addr = options.change_addr
620
621         for k, v in wallet.labels.items():
622             if v == to_address:
623                 to_address = k
624                 print_msg("alias", to_address)
625                 break
626             if change_addr and v == change_addr:
627                 change_addr = k
628         try:
629             tx = wallet.mktx( [(to_address, amount)], label, password,
630                 fee = options.tx_fee, change_addr = change_addr, from_addr = from_addr )
631         except:
632             import traceback
633             traceback.print_exc(file=sys.stdout)
634             tx = None
635
636         if tx and cmd=='payto': 
637             r, h = wallet.sendtx( tx )
638             print_msg(h)
639         else:
640             print_json({"hex":str(tx), "complete":tx.is_complete})
641
642         if is_temporary:
643             wallet.imported_keys.pop(from_addr)
644             del(wallet.history[from_addr])
645         wallet.save()
646
647     elif cmd == 'signtx':
648         tx = Transaction(args[1])
649         tx = wallet.sign_tx( tx, password )
650         print_msg(tx)
651
652     elif cmd == 'sendtx':
653         tx = args[1]
654         r, h = wallet.sendtx( tx )
655         print_msg(h)
656
657     elif cmd == 'password':
658         new_password = prompt_password('New password:')
659         wallet.update_password(seed, password, new_password)
660
661     elif cmd == 'signmessage':
662         if len(args) < 3:
663             print_msg("Error: Invalid usage of signmessage.")
664             print_msg(known_commands[cmd])
665             sys.exit(1)
666         address = args[1]
667         message = ' '.join(args[2:])
668         if len(args) > 3:
669             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
670
671         print_msg(wallet.sign_message(address, message, password))
672
673     elif cmd == 'verifymessage':
674         try:
675             address = args[1]
676             signature = args[2]
677             message = ' '.join(args[3:])
678         except:
679             print_msg("Error: Not all parameters were given, displaying help instead.")
680             print_msg(known_commands[cmd])
681             sys.exit(1)
682         if len(args) > 4:
683             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
684         EC_KEY.verify_message(address, signature, message)
685         try:
686             EC_KEY.verify_message(address, signature, message)
687             print_msg(True)
688         except BaseException as e:
689             print_error("Verification error: {0}".format(e))
690             print_msg(False)
691
692     elif cmd == 'freeze':
693         addr = args[1]
694         print_msg(wallet.freeze(addr))
695         
696     elif cmd == 'unfreeze':
697         addr = args[1]
698         print_msg(wallet.unfreeze(addr))
699
700     elif cmd == 'prioritize':
701         addr = args[1]
702         print_msg(wallet.prioritize(addr))
703
704     elif cmd == 'unprioritize':
705         addr = args[1]
706         print_msg(wallet.unprioritize(addr))
707
708
709     elif cmd == 'dumpprivkey':
710         addr = args[1]
711         sec = wallet.get_private_key(addr, password)
712         print_msg( sec )
713
714         
715     elif cmd == 'createmultisig':
716         num = int(args[1])
717         pubkeys = ast.literal_eval(args[2])
718         assert isinstance(pubkeys,list)
719         print_json( Transaction.multisig_script(pubkeys, num) )
720     
721
722     elif cmd == 'createrawtransaction':
723         inputs = ast.literal_eval(args[1])
724         outputs = ast.literal_eval(args[2])
725         # convert to own format
726         for i in inputs:
727             i['tx_hash'] = i['txid']
728             i['index'] = i['vout']
729         outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
730         tx = Transaction.from_io(inputs, outputs)
731         print_msg( tx )
732
733
734     elif cmd == 'decoderawtransaction':
735         tx = Transaction(args[1])
736         print_json( tx.deserialize() )
737
738
739     elif cmd == 'signrawtransaction':
740         tx = Transaction(args[1])
741         txouts = ast.literal_eval(args[2]) if len(args)>2 else []
742         private_keys = ast.literal_eval(args[3]) if len(args)>3 else {}
743
744         for txin in tx.inputs:
745             txid = txin["prevout_hash"]
746             index = txin["prevout_n"]
747             # convert to own format
748             txin['tx_hash'] = txin['prevout_hash']
749             txin['index'] = txin['prevout_n']
750
751             for txout in txouts:
752                 if txout.get('txid') == txid and txout.get('vout') == index:
753                     txin['raw_output_script'] = txout['scriptPubKey']
754                     txin['redeemScript'] = txout['redeemScript']
755                     break
756                 
757             else:
758                 if wallet.transactions.get(txid):
759                     # lookup in my own list of transactions
760                     txout = wallet.transactions[txid].outputs[index]
761                     txin['address'] = txout[0]
762                     #txin['raw_output_script'] = txout['raw_output_script']
763
764                 else:
765                     # if neither, we might want to get it from the server..
766                     raise
767
768         if not private_keys:
769             for txin in tx.inputs:
770                 addr = txin['address']
771                 private_keys[addr] = wallet.get_private_key(addr, password)
772         else:
773             pk = {}
774             for sec in private_keys:
775                 address = bitcoin.address_from_private_key(sec)
776                 pk[address] = sec
777             private_keys = pk
778
779         tx.sign( private_keys )
780         print_json({ "hex":str(tx),"complete":tx.is_complete})
781
782
783     elif cmd == 'listunspent':
784         print_json(wallet.get_unspent_coins())
785         
786
787     if cmd not in offline_commands and not options.offline:
788         synchronizer.stop()
789
790