4fc1b21dde5bbee92823aed150c1957066e442ca
[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             out = {"hex":str(tx), "complete":tx.is_complete}
640             if not tx.is_complete: 
641                 import json
642                 out['input_info'] = repr(tx.inputs_info).replace(' ','')
643             print_json(out)
644
645         if is_temporary:
646             wallet.imported_keys.pop(from_addr)
647             del(wallet.history[from_addr])
648         wallet.save()
649
650     elif cmd == 'sendtx':
651         tx = args[1]
652         r, h = wallet.sendtx( tx )
653         print_msg(h)
654
655     elif cmd == 'password':
656         new_password = prompt_password('New password:')
657         wallet.update_password(seed, password, new_password)
658
659     elif cmd == 'signmessage':
660         if len(args) < 3:
661             print_msg("Error: Invalid usage of signmessage.")
662             print_msg(known_commands[cmd])
663             sys.exit(1)
664         address = args[1]
665         message = ' '.join(args[2:])
666         if len(args) > 3:
667             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
668
669         print_msg(wallet.sign_message(address, message, password))
670
671     elif cmd == 'verifymessage':
672         try:
673             address = args[1]
674             signature = args[2]
675             message = ' '.join(args[3:])
676         except:
677             print_msg("Error: Not all parameters were given, displaying help instead.")
678             print_msg(known_commands[cmd])
679             sys.exit(1)
680         if len(args) > 4:
681             print_msg("Warning: Message was reconstructed from several arguments:", repr(message))
682         EC_KEY.verify_message(address, signature, message)
683         try:
684             EC_KEY.verify_message(address, signature, message)
685             print_msg(True)
686         except BaseException as e:
687             print_error("Verification error: {0}".format(e))
688             print_msg(False)
689
690     elif cmd == 'freeze':
691         addr = args[1]
692         print_msg(wallet.freeze(addr))
693         
694     elif cmd == 'unfreeze':
695         addr = args[1]
696         print_msg(wallet.unfreeze(addr))
697
698     elif cmd == 'prioritize':
699         addr = args[1]
700         print_msg(wallet.prioritize(addr))
701
702     elif cmd == 'unprioritize':
703         addr = args[1]
704         print_msg(wallet.unprioritize(addr))
705
706
707     elif cmd == 'dumpprivkey':
708         addr = args[1]
709         sec = wallet.get_private_key(addr, password)
710         print_msg( sec )
711
712         
713     elif cmd == 'createmultisig':
714         num = int(args[1])
715         pubkeys = ast.literal_eval(args[2])
716         assert isinstance(pubkeys,list)
717         print_json( Transaction.multisig_script(pubkeys, num) )
718     
719
720     elif cmd == 'createrawtransaction':
721         inputs = ast.literal_eval(args[1])
722         outputs = ast.literal_eval(args[2])
723         # convert to own format
724         for i in inputs:
725             i['tx_hash'] = i['txid']
726             i['index'] = i['vout']
727         outputs = map(lambda x: (x[0],int(1e8*x[1])), outputs.items())
728         tx = Transaction.from_io(inputs, outputs)
729         print_msg( tx )
730
731
732     elif cmd == 'decoderawtransaction':
733         tx = Transaction(args[1])
734         print_json( tx.deserialize() )
735
736
737     elif cmd == 'signrawtransaction':
738         tx = Transaction(args[1])
739         inputs_info = ast.literal_eval(args[2]) if len(args)>2 else []
740         private_keys = ast.literal_eval(args[3]) if len(args)>3 else {}
741         unspent_coins = wallet.get_unspent_coins()
742
743         # convert private_keys to dict 
744         pk = {}
745         for sec in private_keys:
746             address = bitcoin.address_from_private_key(sec)
747             pk[address] = sec
748         private_keys = pk
749
750         for txin in tx.inputs:
751             # convert to own format
752             txin['tx_hash'] = txin['prevout_hash']
753             txin['index'] = txin['prevout_n']
754
755             for item in inputs_info:
756                 if item.get('txid') == txin['tx_hash'] and item.get('vout') == txin['index']:
757                     txin['raw_output_script'] = item['scriptPubKey']
758                     txin['redeemScript'] = item.get('redeemScript')
759                     txin['electrumKeyID'] = item.get('electrumKeyID')
760                     break
761             else:
762                 for item in unspent_coins:
763                     if txin['tx_hash'] == item['tx_hash'] and txin['index'] == item['index']:
764                         txin['raw_output_script'] = item['raw_output_script']
765                         break
766                 else:
767                     # if neither, we might want to get it from the server..
768                     raise
769
770             # find the address:
771             from lib import deserialize
772             if txin.get('electrumKeyID'):
773                 n, for_change = txin.get('electrumKeyID')
774                 sec = wallet.sequence.get_private_key(n, for_change, seed)
775                 address = bitcoin.address_from_private_key(sec)
776                 txin['address'] = address
777                 private_keys[address] = sec
778
779             elif txin.get("redeemScript"):
780                 txin['address'] = hash_160_to_bc_address(hash_160(txin.get("redeemScript").decode('hex')), 5)
781
782             elif txin.get("raw_output_script"):
783                 addr = deserialize.get_address_from_output_script(txin.get("raw_output_script").decode('hex'))
784                 sec = wallet.get_private_key(addr, password)
785                 if sec: 
786                     private_keys[addr] = sec
787                     txin['address'] = addr
788
789         tx.sign( private_keys )
790         print_json({ "hex":str(tx),"complete":tx.is_complete})
791
792
793     elif cmd == 'listunspent':
794         print_json(wallet.get_unspent_coins())
795         
796
797     if cmd not in offline_commands and not options.offline:
798         synchronizer.stop()
799
800