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