remove signtx command (replaced by signrawtransaction).
[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     'seed':
72             "Print the generation seed of your wallet.",
73     'importprivkey': 
74             'Import a private key\nSyntax: importprivkey <privatekey>',
75     'signmessage':
76             '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 "',
77     'verifymessage':
78              '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 "',
79     'eval':  
80              "Run python eval() on an object\nSyntax: eval <expression>\nExample: eval \"wallet.aliases\"",
81     'get': 
82              "Get config parameter.",
83     'set': 
84              "Set config parameter.",
85     'deseed':
86             "Remove seed from the wallet. The seed is stored in a file that has the name of the wallet plus '.seed'",
87     'reseed':
88             "Restore seed of the wallet. The wallet must have no seed, and the seed must match the wallet's master public key.",
89     'freeze':'',
90     'unfreeze':'',
91     'prioritize':'',
92     'unprioritize':'',
93     'dumpprivkey':'similar to bitcoind\'s command',
94     'listunspent':'similar to bitcoind\'s command',
95     'createmultisig':'similar to bitcoind\'s command',
96     'createrawtransaction':'similar to bitcoind\'s command',
97     'decoderawtransaction':'similar to bitcoind\'s command',
98     'signrawtransaction':'similar to bitcoind\'s command',
99     
100     }
101
102
103
104 offline_commands = [ 'password', 'mktx',
105                      'label', 'contacts',
106                      'help', 'validateaddress',
107                      'signmessage', 'verifymessage',
108                      'eval', 'set', 'get', 'create', 'addresses',
109                      'importprivkey', 'seed',
110                      'deseed','reseed',
111                      'freeze','unfreeze',
112                      'prioritize','unprioritize',
113                      'dumpprivkey','listunspent',
114                      'createmultisig', 'createrawtransaction', 'decoderawtransaction', 'signrawtransaction'
115                      ]
116
117
118 protected_commands = ['payto', 'password', 'mktx', 'seed', 'importprivkey','signmessage', 'signrawtransaction','dumpprivkey' ]
119
120 # get password routine
121 def prompt_password(prompt, confirm=True):
122     import getpass
123     if sys.stdin.isatty():
124         password = getpass.getpass(prompt)
125         if password and confirm:
126             password2 = getpass.getpass("Confirm: ")
127             if password != password2:
128                 sys.exit("Error: Passwords do not match.")
129     else:
130         password = raw_input(prompt)
131     if not password:
132         password = None
133     return password
134
135 def arg_parser():
136     usage = "usage: %prog [options] command\nCommands: "+ (', '.join(known_commands))
137     parser = optparse.OptionParser(prog=usage)
138     parser.add_option("-g", "--gui", dest="gui", help="User interface: qt, lite, gtk or text")
139     parser.add_option("-w", "--wallet", dest="wallet_path", help="wallet path (default: electrum.dat)")
140     parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
141     parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
142     parser.add_option("-b", "--balance", action="store_true", dest="show_balance", default=False, help="show the balance at listed addresses")
143     parser.add_option("-k", "--keys",action="store_true", dest="show_keys",default=False, help="show the private keys of listed addresses")
144     parser.add_option("-f", "--fee", dest="tx_fee", default="0.005", help="set tx fee")
145     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.")
146     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")
147     parser.add_option("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is t or h")
148     parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http")
149     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information")
150     parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet")
151     parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI")
152     parser.add_option("-u", "--usb", dest="bitkey", action="store_true", help="Turn on support for hardware wallets (EXPERIMENTAL)")
153     return parser
154
155
156 if __name__ == '__main__':
157
158     parser = arg_parser()
159     options, args = parser.parse_args()
160     set_verbosity(options.verbose)
161
162     # config is an object passed to the various constructors (wallet, interface, gui)
163     if 'ANDROID_DATA' in os.environ:
164         config_options = {'wallet_path':"/sdcard/electrum.dat", 'portable':True, 'verbose':True, 'gui':'android'}
165     else:
166         config_options = eval(str(options))
167         for k, v in config_options.items():
168             if v is None: config_options.pop(k)
169
170     # Wallet migration on Electrum 1.7
171     # Todo: In time we could remove this again
172     if platform.system() == "Windows":
173         util.check_windows_wallet_migration()
174
175     config = SimpleConfig(config_options)
176     wallet = Wallet(config)
177
178     if len(args)==0:
179         url = None
180         cmd = 'gui'
181     elif len(args)==1 and re.match('^bitcoin:', args[0]):
182         url = args[0]
183         cmd = 'gui'
184     else:
185         cmd = args[0]
186         firstarg = args[1] if len(args) > 1 else ''
187        
188     #this entire if/else block is just concerned with importing the 
189     #right GUI toolkit based the GUI command line option given 
190     if cmd == 'gui':
191         pref_gui = config.get('gui','classic')
192
193         if pref_gui == 'gtk':
194             try:
195                 import lib.gui as gui
196             except ImportError:
197                 import electrum.gui as gui
198         elif pref_gui in ['classic', 'qt']:
199             try:
200                 import lib.gui_qt as gui
201             except ImportError:
202                 import electrum.gui_qt as gui
203         elif pref_gui == 'lite':
204               try:
205                   import lib.gui_lite as gui
206               except ImportError:
207                   import electrum.gui_lite as gui
208         elif pref_gui == 'text':
209               try:
210                   import lib.gui_text as gui
211               except ImportError:
212                   import electrum.gui_text as gui
213         elif pref_gui == 'android':
214               try:
215                   import lib.gui_android as gui
216               except ImportError:
217                   import electrum.gui_android as gui
218         else:
219             sys.exit("Error: Unknown GUI: " + pref_gui )
220
221         
222         interface = Interface(config, True)
223         wallet.interface = interface
224         interface.start()
225         if interface.is_connected:
226             interface.send([('server.peers.subscribe',[])])
227
228         set_language(config.get('language'))
229         gui = gui.ElectrumGui(wallet, config)
230
231         found = config.wallet_file_exists
232         if not found:
233             a = gui.restore_or_create()
234             if not a: exit()
235             # select a server.
236             s = gui.network_dialog()
237
238             if a =='create':
239                 wallet.init_seed(None)
240             else:
241                 # ask for seed and gap.
242                 sg = gui.seed_dialog()
243                 if not sg: exit()
244                 seed, gap = sg
245                 if not seed: exit()
246                 wallet.gap_limit = gap
247                 if len(seed) == 128:
248                     wallet.seed = ''
249                     wallet.sequence.master_public_key = seed
250                 else:
251                     wallet.init_seed(str(seed))
252             
253
254             # generate the first addresses, in case we are offline
255             if s is None or a == 'create':
256                 wallet.synchronize()
257             if a == 'create':
258                 # display seed
259                 gui.show_seed()
260
261         verifier = WalletVerifier(interface, config)
262         wallet.set_verifier(verifier)
263         synchronizer = WalletSynchronizer(wallet, config)
264         synchronizer.start()
265
266         if not found and a == 'restore' and s is not None:
267             try:
268                 keep_it = gui.restore_wallet()
269                 wallet.fill_addressbook()
270             except:
271                 import traceback
272                 traceback.print_exc(file=sys.stdout)
273                 exit()
274
275             if not keep_it: exit()
276
277         if not found:
278             gui.password_dialog()
279
280         wallet.save()
281         verifier.start()
282         gui.main(url)
283         wallet.save()
284
285         verifier.stop()
286         synchronizer.stop()
287         interface.stop()
288
289         # we use daemon threads, their termination is enforced.
290         # this sleep command gives them time to terminate cleanly. 
291         time.sleep(0.1)
292         sys.exit(0)
293
294     if cmd not in known_commands:
295         cmd = 'help'
296
297     if not config.wallet_file_exists and cmd not in ['help','create','restore']:
298         print_msg("Error: Wallet file not found.")
299         print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
300         sys.exit(0)
301     
302     if cmd in ['create', 'restore']:
303         if config.wallet_file_exists:
304             sys.exit("Error: Remove the existing wallet first!")
305         password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
306
307         server = config.get('server')
308         if not server: server = pick_random_server()
309         w_host, w_port, w_protocol = server.split(':')
310         host = raw_input("server (default:%s):"%w_host)
311         port = raw_input("port (default:%s):"%w_port)
312         protocol = raw_input("protocol [t=tcp;h=http;n=native] (default:%s):"%w_protocol)
313         fee = raw_input("fee (default:%s):"%( str(Decimal(wallet.fee)/100000000)) )
314         gap = raw_input("gap limit (default 5):")
315         if host: w_host = host
316         if port: w_port = port
317         if protocol: w_protocol = protocol
318         wallet.config.set_key('server', w_host + ':' + w_port + ':' +w_protocol)
319         if fee: wallet.fee = float(fee)
320         if gap: wallet.gap_limit = int(gap)
321
322         if cmd == 'restore':
323             seed = raw_input("seed:")
324             try:
325                 seed.decode('hex')
326             except:
327                 print_error("Warning: Not hex, trying decode.")
328                 seed = mnemonic_decode( seed.split(' ') )
329             if not seed:
330                 sys.exit("Error: No seed")
331
332             if len(seed) == 128:
333                 wallet.seed = None
334                 wallet.sequence.master_public_key = seed
335             else:
336                 wallet.seed = str(seed)
337                 wallet.init_mpk( wallet.seed )
338
339             if not options.offline:
340                 interface = Interface(config)
341                 interface.start()
342                 wallet.interface = interface
343
344                 verifier = WalletVerifier(interface, config)
345                 wallet.set_verifier(verifier)
346
347                 print_msg("Recovering wallet...")
348                 WalletSynchronizer(wallet, config).start()
349                 wallet.update()
350                 if wallet.is_found():
351                     print_msg("Recovery successful")
352                 else:
353                     print_msg("Warning: Found no history for this wallet")
354             else:
355                 wallet.synchronize()
356             wallet.fill_addressbook()
357             wallet.save()
358             print_msg("Wallet saved in '%s'"%wallet.config.path)
359         else:
360             wallet.init_seed(None)
361             wallet.synchronize() # there is no wallet thread 
362             wallet.save()
363             print_msg("Your wallet generation seed is: " + wallet.seed)
364             print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
365             print_msg("Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:")
366             print_msg("\""+' '.join(mnemonic_encode(wallet.seed))+"\"")
367             print_msg("Wallet saved in '%s'"%wallet.config.path)
368             
369         if password:
370             wallet.update_password(wallet.seed, None, password)
371
372     # check syntax
373     if cmd in ['payto', 'mktx']:
374         try:
375             to_address = args[1]
376             amount = int( 100000000 * Decimal(args[2]) )
377             change_addr = None
378             label = ' '.join(args[3:])
379             if options.tx_fee: 
380                 options.tx_fee = int( 100000000 * Decimal(options.tx_fee) )
381         except:
382             firstarg = cmd
383             cmd = 'help'
384
385     # open session
386     if cmd not in offline_commands and not options.offline:
387         interface = Interface(config)
388         interface.register_callback('connected', lambda: sys.stderr.write("Connected to " + interface.connection_msg + "\n"))
389         interface.start()
390         wallet.interface = interface
391         synchronizer = WalletSynchronizer(wallet, config)
392         synchronizer.start()
393         wallet.update()
394         wallet.save()
395
396     # check if --from_addr not in wallet (for mktx/payto)
397     is_temporary = False
398     from_addr = None
399     if options.from_addr:
400         from_addr = options.from_addr
401         if from_addr not in wallet.all_addresses():
402             is_temporary = True
403                 
404     # important warning
405     if cmd=='addresses' and options.show_keys:
406         print_msg("WARNING: ALL your private keys are secret.")
407         print_msg("Exposing a single private key can compromise your entire wallet!")
408         print_msg("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
409
410     # commands needing password
411     if cmd in protected_commands or ( cmd=='addresses' and options.show_keys):
412         if wallet.use_encryption and not is_temporary:
413             password = prompt_password('Password:', False)
414             if not password:
415                 print_msg("Error: Password required")
416                 exit(1)
417             # check password
418             try:
419                 seed = wallet.decode_seed(password)
420             except:
421                 print_msg("Error: This password does not decode this wallet.")
422                 exit(1)
423         else:
424             password = None
425             seed = wallet.seed
426
427     if cmd == 'importprivkey':
428         # See if they specificed a key on the cmd line, if not prompt
429         if len(args) > 1:
430             sec = args[1]
431         else:
432             sec = prompt_password('Enter PrivateKey (will not echo):', False)
433         try:
434             addr = wallet.import_key(sec,password)
435             wallet.save()
436             print_msg("Keypair imported: ", addr)
437         except BaseException as e:
438             print_msg("Error: Keypair import failed: " + str(e))
439
440     if cmd == 'help':
441         cmd2 = firstarg
442         if cmd2 not in known_commands:
443             parser.print_help()
444             print_msg("Type 'electrum help <command>' to see the help for a specific command")
445             print_msg("Type 'electrum --help' to see the list of options")
446             print_msg("List of commands:", ', '.join(known_commands))
447         else:
448             print_msg(known_commands[cmd2])
449
450     elif cmd == 'seed':
451         print_msg(seed + ' "' + ' '.join(mnemonic_encode(seed)) + '"')
452
453     elif cmd == 'deseed':
454         if not wallet.seed:
455             print_msg("Error: This wallet has no seed")
456         elif wallet.use_encryption:
457             print_msg("Error: This wallet is encrypted")
458         else:
459             ns = wallet.config.path + '.seed'
460             print_msg("Warning: you are going to extract the seed from '%s'\nThe seed will be saved in '%s'"%(wallet.config.path,ns))
461             if raw_input("Are you sure you want to continue? (y/n) ") in ['y','Y','yes']:
462                 f = open(ns,'w')
463                 f.write(repr({'seed':wallet.seed, 'imported_keys':wallet.imported_keys})+"\n")
464                 f.close()
465                 wallet.seed = ''
466                 wallet.config.set_key('seed','', True)
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_json({"hex":str(tx), "complete":tx.is_complete})
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 == 'sendtx':
647         tx = args[1]
648         r, h = wallet.sendtx( tx )
649         print_msg(h)
650
651     elif cmd == 'password':
652         new_password = prompt_password('New password:')
653         wallet.update_password(seed, password, new_password)
654
655     elif cmd == 'signmessage':
656         if len(args) < 3:
657             print_msg("Error: Invalid usage of signmessage.")
658             print_msg(known_commands[cmd])
659             sys.exit(1)
660         address = args[1]
661         message = ' '.join(args[2:])
662         if len(args) > 3:
663             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
664
665         print_msg(wallet.sign_message(address, message, password))
666
667     elif cmd == 'verifymessage':
668         try:
669             address = args[1]
670             signature = args[2]
671             message = ' '.join(args[3:])
672         except:
673             print_msg("Error: Not all parameters were given, displaying help instead.")
674             print_msg(known_commands[cmd])
675             sys.exit(1)
676         if len(args) > 4:
677             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
678         EC_KEY.verify_message(address, signature, message)
679         try:
680             EC_KEY.verify_message(address, signature, message)
681             print_msg(True)
682         except BaseException as e:
683             print_error("Verification error: {0}".format(e))
684             print_msg(False)
685
686     elif cmd == 'freeze':
687         addr = args[1]
688         print_msg(wallet.freeze(addr))
689         
690     elif cmd == 'unfreeze':
691         addr = args[1]
692         print_msg(wallet.unfreeze(addr))
693
694     elif cmd == 'prioritize':
695         addr = args[1]
696         print_msg(wallet.prioritize(addr))
697
698     elif cmd == 'unprioritize':
699         addr = args[1]
700         print_msg(wallet.unprioritize(addr))
701
702
703     elif cmd == 'dumpprivkey':
704         addr = args[1]
705         sec = wallet.get_private_key(addr, password)
706         print_msg( sec )
707
708         
709     elif cmd == 'createmultisig':
710         num = int(args[1])
711         pubkeys = ast.literal_eval(args[2])
712         assert isinstance(pubkeys,list)
713         print_json( Transaction.multisig_script(pubkeys, num) )
714     
715
716     elif cmd == 'createrawtransaction':
717         inputs = ast.literal_eval(args[1])
718         outputs = ast.literal_eval(args[2])
719         # convert to own format
720         for i in inputs:
721             i['tx_hash'] = i['txid']
722             i['index'] = i['vout']
723         outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
724         tx = Transaction.from_io(inputs, outputs)
725         print_msg( tx )
726
727
728     elif cmd == 'decoderawtransaction':
729         tx = Transaction(args[1])
730         print_json( tx.deserialize() )
731
732
733     elif cmd == 'signrawtransaction':
734         tx = Transaction(args[1])
735         txouts = ast.literal_eval(args[2]) if len(args)>2 else []
736         private_keys = ast.literal_eval(args[3]) if len(args)>3 else {}
737         unspent_coins = wallet.get_unspent_coins()
738
739         for txin in tx.inputs:
740             # convert to own format
741             txin['tx_hash'] = txin['prevout_hash']
742             txin['index'] = txin['prevout_n']
743
744             for txout in txouts:
745                 if txout.get('txid') == txin['tx_hash'] and txout.get('vout') == txin['index']:
746                     txin['raw_output_script'] = txout['scriptPubKey']
747                     txin['redeemScript'] = txout['redeemScript']
748                     break
749                 
750             else:
751                 for item in unspent_coins:
752                     if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
753                         txin['address'] = item['address']
754                         txin['raw_output_script'] = item['raw_output_script']
755                         break
756                 else:
757                     # if neither, we might want to get it from the server..
758                     raise
759
760         if not private_keys:
761             for txin in tx.inputs:
762                 addr = txin['address']
763                 private_keys[addr] = wallet.get_private_key(addr, password)
764         else:
765             pk = {}
766             for sec in private_keys:
767                 address = bitcoin.address_from_private_key(sec)
768                 pk[address] = sec
769             private_keys = pk
770
771         tx.sign( private_keys )
772         print_json({ "hex":str(tx),"complete":tx.is_complete})
773
774
775     elif cmd == 'listunspent':
776         print_json(wallet.get_unspent_coins())
777         
778
779     if cmd not in offline_commands and not options.offline:
780         synchronizer.stop()
781
782