empty seed error msg
[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 from decimal import Decimal
20 import json
21 import optparse
22 import os
23 import re
24 import ast
25 import sys
26 import time
27 import traceback
28
29 try:
30     import ecdsa  # todo: 'ecdsa' imported but unused
31 except ImportError:
32     sys.exit("Error: python-ecdsa does not seem to be installed. Try 'sudo pip install ecdsa'")
33
34 try:
35     import aes  # todo: 'aes' imported but unused
36 except ImportError:
37     sys.exit("Error: AES does not seem to be installed. Try 'sudo pip install slowaes'")
38
39
40 is_local = os.path.dirname(os.path.realpath(__file__)) == os.getcwd()
41 is_android = 'ANDROID_DATA' in os.environ
42
43 import __builtin__
44 __builtin__.use_local_modules = is_local or is_android
45
46 # load local module as electrum
47 if __builtin__.use_local_modules:
48     import imp
49     imp.load_module('electrum', *imp.find_module('lib'))
50     imp.load_module('electrum_gui', *imp.find_module('gui'))
51
52 from electrum import *  # todo: import * is generally frowned upon. should import just what is used
53 from electrum.util import print_msg, print_stderr
54
55 # get password routine
56 def prompt_password(prompt, confirm=True):
57     import getpass
58     if sys.stdin.isatty():
59         password = getpass.getpass(prompt)
60         if password and confirm:
61             password2 = getpass.getpass("Confirm: ")
62             if password != password2:
63                 sys.exit("Error: Passwords do not match.")
64     else:
65         password = raw_input(prompt)
66     if not password:
67         password = None
68     return password
69
70
71 def arg_parser():
72     usage = "%prog [options] command"
73     parser = optparse.OptionParser(prog=usage, add_help_option=False)
74     parser.add_option("-h", "--help", action="callback", callback=print_help_cb, help="show this help text")
75     parser.add_option("-g", "--gui", dest="gui", help="User interface: qt, lite, gtk, text or stdio")
76     parser.add_option("-w", "--wallet", dest="wallet_path", help="wallet path (default: electrum.dat)")
77     parser.add_option("-o", "--offline", action="store_true", dest="offline", default=False, help="remain offline")
78     parser.add_option("-C", "--concealed", action="store_true", dest="concealed", default=False, help="don't echo seed to console when restoring")
79     parser.add_option("-a", "--all", action="store_true", dest="show_all", default=False, help="show all addresses")
80     parser.add_option("-l", "--labels", action="store_true", dest="show_labels", default=False, help="show the labels of listed addresses")
81     parser.add_option("-f", "--fee", dest="tx_fee", default=None, help="set tx fee")
82     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.")
83     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")
84     parser.add_option("-s", "--server", dest="server", default=None, help="set server host:port:protocol, where protocol is either t (tcp), h (http), s (tcp+ssl), or g (https)")
85     parser.add_option("-p", "--proxy", dest="proxy", default=None, help="set proxy [type:]host[:port], where type is socks4,socks5 or http")
86     parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="show debugging information")
87     parser.add_option("-P", "--portable", action="store_true", dest="portable", default=False, help="portable wallet")
88     parser.add_option("-L", "--lang", dest="language", default=None, help="defaut language used in GUI")
89     parser.add_option("-u", "--usb", dest="bitkey", action="store_true", help="Turn on support for hardware wallets (EXPERIMENTAL)")
90     parser.add_option("-G", "--gap", dest="gap_limit", default=None, help="gap limit")
91     parser.add_option("-W", "--password", dest="password", default=None, help="set password for usage with commands (currently only implemented for create command, do not use it for longrunning gui session since the password is visible in /proc)")
92     parser.add_option("-1", "--oneserver", action="store_true", dest="oneserver", default=False, help="connect to one server only")
93     parser.add_option("--bip32", action="store_true", dest="bip32", default=False, help="bip32 (not final)")
94     parser.add_option("--2of3", action="store_true", dest="2of3", default=False, help="create 2of3 wallet")
95     parser.add_option("--mpk", dest="mpk", default=False, help="restore from master public key")
96     return parser
97
98
99 def print_help(parser):
100     parser.print_help()
101     print_msg("Type 'electrum help <command>' to see the help for a specific command")
102     print_msg("Type 'electrum --help' to see the list of options")
103     run_command(known_commands['help'])
104     sys.exit(1)
105
106
107 def print_help_cb(self, opt, value, parser):
108     print_help(parser)
109
110
111 def run_command(cmd, password=None, args=[]):
112     import socket
113     if cmd.requires_network and not options.offline:
114         network = NetworkProxy(config)
115         if not network.start(start_daemon= (True if cmd.name!='daemon' else False)):
116             print "Daemon not running"
117             sys.exit(1)
118
119
120
121         if wallet:
122             wallet.start_threads(network)
123             wallet.update()
124     else:
125         network = None
126
127     cmd_runner = Commands(wallet, network)
128     func = getattr(cmd_runner, cmd.name)
129     cmd_runner.password = password
130     try:
131         result = func(*args[1:])
132     except Exception:
133         traceback.print_exc(file=sys.stdout)
134         sys.exit(1)
135
136
137     if cmd.requires_network and not options.offline:
138         if wallet:
139             wallet.stop_threads()
140
141
142     if type(result) == str:
143         util.print_msg(result)
144     elif result is not None:
145         util.print_json(result)
146
147
148
149
150
151
152 if __name__ == '__main__':
153
154     parser = arg_parser()
155     options, args = parser.parse_args()
156     if options.portable and options.wallet_path is None:
157         options.electrum_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'electrum_data')
158
159     # config is an object passed to the various constructors (wallet, interface, gui)
160     if is_android:
161         config_options = {
162             'portable': True,
163             'verbose': True,
164             'gui': 'android',
165             'auto_cycle': True,
166         }
167     else:
168         config_options = eval(str(options))
169         for k, v in config_options.items():
170             if v is None:
171                 config_options.pop(k)
172
173     set_verbosity(config_options.get('verbose'))
174
175     config = SimpleConfig(config_options)
176
177     if len(args) == 0:
178         url = None
179         cmd = 'gui'
180     elif len(args) == 1 and re.match('^bitcoin:', args[0]):
181         url = args[0]
182         cmd = 'gui'
183     else:
184         cmd = args[0]
185
186     if cmd == 'gui':
187         gui_name = config.get('gui', 'classic')
188         if gui_name in ['lite', 'classic']:
189             gui_name = 'qt'
190         try:
191             gui = __import__('electrum_gui.' + gui_name, fromlist=['electrum_gui'])
192         except ImportError:
193             traceback.print_exc(file=sys.stdout)
194             sys.exit()
195             #sys.exit("Error: Unknown GUI: " + gui_name )
196
197         # network interface
198         if not options.offline:
199             network = Network(config)
200             network.start()
201         else:
202             network = None
203
204         gui = gui.ElectrumGui(config, network)
205         gui.main(url)
206
207         if network:
208             network.stop()
209
210         # we use daemon threads, their termination is enforced.
211         # this sleep command gives them time to terminate cleanly.
212         time.sleep(0.1)
213         sys.exit(0)
214
215     if cmd not in known_commands:
216         cmd = 'help'
217
218     cmd = known_commands[cmd]
219
220     # instanciate wallet for command-line
221     storage = WalletStorage(config)
222
223
224     if cmd.name in ['create', 'restore']:
225         if storage.file_exists:
226             sys.exit("Error: Remove the existing wallet first!")
227         if options.password is not None:
228             password = options.password
229         elif cmd.name == 'restore' and options.mpk:
230             password = None
231         else:
232             password = prompt_password("Password (hit return if you do not wish to encrypt your wallet):")
233
234         # if config.server is set, the user either passed the server on command line
235         # or chose it previously already. if he didn't pass a server on the command line,
236         # we just pick up a random one.
237         if not config.get('server'):
238             config.set_key('server', pick_random_server())
239
240         #fee = options.tx_fee if options.tx_fee else raw_input("fee (default:%s):" % (str(Decimal(wallet.fee)/100000000)))
241         #gap = options.gap_limit if options.gap_limit else raw_input("gap limit (default 5):")
242         #if fee:
243         #    wallet.set_fee(float(fee)*100000000)
244         #if gap:
245         #    wallet.change_gap_limit(int(gap))
246
247         if cmd.name == 'restore':
248             if options.mpk:
249                 wallet = Wallet.from_mpk(options.mpk, storage)
250             else:
251                 import getpass
252                 seed = getpass.getpass(prompt="seed:", stream=None) if options.concealed else raw_input("seed:")
253                 if not seed:
254                     sys.exit("Error: Seed cannot be empty")
255                 wallet = Wallet.from_seed(str(seed),storage)
256                 if not wallet:
257                     sys.exit("Error: Invalid seed")
258                 wallet.save_seed(password)
259                 wallet.create_accounts(password)
260
261             if not options.offline:
262                 network = Network(config)
263                 network.start()
264                 wallet.start_threads(network)
265                 print_msg("Recovering wallet...")
266                 wallet.restore(lambda x: x)
267                 if wallet.is_found():
268                     print_msg("Recovery successful")
269                 else:
270                     print_msg("Warning: Found no history for this wallet")
271             else:
272                 wallet.synchronize()
273                 print_msg("Warning: This wallet was restored offline. It may contain more addresses than displayed.")
274
275         else:
276             if not config.get('2of3'):
277                 wallet = Wallet(storage)
278                 seed = wallet.make_seed()
279                 wallet.add_seed(seed, password)
280                 wallet.create_accounts(password)
281                 wallet.synchronize()
282                 print_msg("Your wallet generation seed is:\n\"%s\"" % seed)
283                 print_msg("Please keep it in a safe place; if you lose it, you will not be able to restore your wallet.")
284             else:
285                 wallet = Wallet_2of3(storage)
286                 cold_seed = wallet.make_seed()
287                 #wallet.save_seed()
288                 print_msg("Your cold seed is:\n\"%s\"" % cold_seed)
289                 print_msg("Please store it on paper. ")
290                 print_msg("Open this file on your online computer to complete your wallet creation.")
291
292
293         print_msg("Wallet saved in '%s'" % wallet.storage.path)
294
295         # terminate
296         sys.exit(0)
297
298
299     if cmd.name not in ['create', 'restore'] and cmd.requires_wallet and not storage.file_exists:
300         print_msg("Error: Wallet file not found.")
301         print_msg("Type 'electrum create' to create a new wallet, or provide a path to a wallet with the -w option")
302         sys.exit(0)
303
304
305     if cmd.requires_wallet:
306         wallet = Wallet(storage)
307     else:
308         wallet = None
309
310
311     # important warning
312     if cmd.name in ['dumpprivkey', 'dumpprivkeys']:
313         print_stderr("WARNING: ALL your private keys are secret.")
314         print_stderr("Exposing a single private key can compromise your entire wallet!")
315         print_stderr("In particular, DO NOT use 'redeem private key' services proposed by third parties.")
316
317     # commands needing password
318     if cmd.requires_password:
319         if wallet.seed == '':
320             seed = ''
321             password = None
322         elif wallet.use_encryption:
323             password = prompt_password('Password:', False)
324             if not password:
325                 print_msg("Error: Password required")
326                 sys.exit(1)
327             # check password
328             try:
329                 seed = wallet.get_seed(password)
330             except Exception:
331                 print_msg("Error: This password does not decode this wallet.")
332                 sys.exit(1)
333         else:
334             password = None
335             seed = wallet.get_seed(None)
336     else:
337         password = None
338
339     # add missing arguments, do type conversions
340     if cmd.name == 'importprivkey':
341         # See if they specificed a key on the cmd line, if not prompt
342         if len(args) == 1:
343             args[1] = prompt_password('Enter PrivateKey (will not echo):', False)
344
345     elif cmd.name == 'signrawtransaction':
346         args = [cmd, args[1], json.loads(args[2]) if len(args) > 2 else [], json.loads(args[3]) if len(args) > 3 else []]
347
348     elif cmd.name == 'createmultisig':
349         args = [cmd, int(args[1]), json.loads(args[2])]
350
351     elif cmd.name == 'createrawtransaction':
352         args = [cmd, json.loads(args[1]), json.loads(args[2])]
353
354     elif cmd.name == 'listaddresses':
355         args = [cmd, options.show_all, options.show_labels]
356
357     elif cmd.name in ['payto', 'mktx']:
358         domain = [options.from_addr] if options.from_addr else None
359         args = ['mktx', args[1], Decimal(args[2]), Decimal(options.tx_fee) if options.tx_fee else None, options.change_addr, domain]
360
361     elif cmd.name in ['paytomany', 'mksendmanytx']:
362         domain = [options.from_addr] if options.from_addr else None
363         outputs = []
364         for i in range(1, len(args), 2):
365             if len(args) < i+2:
366                 print_msg("Error: Mismatched arguments.")
367                 sys.exit(1)
368             outputs.append((args[i], Decimal(args[i+1])))
369         args = ['mksendmanytx', outputs, Decimal(options.tx_fee) if options.tx_fee else None, options.change_addr, domain]
370
371     elif cmd.name == 'help':
372         if len(args) < 2:
373             print_help(parser)
374
375     # check the number of arguments
376     if len(args) - 1 < cmd.min_args:
377         print_msg("Not enough arguments")
378         print_msg("Syntax:", cmd.syntax)
379         sys.exit(1)
380
381     if cmd.max_args >= 0 and len(args) - 1 > cmd.max_args:
382         print_msg("too many arguments", args)
383         print_msg("Syntax:", cmd.syntax)
384         sys.exit(1)
385
386     if cmd.max_args < 0:
387         if len(args) > cmd.min_args + 1:
388             message = ' '.join(args[cmd.min_args:])
389             print_msg("Warning: Final argument was reconstructed from several arguments:", repr(message))
390             args = args[0:cmd.min_args] + [message]
391
392
393
394     # run the command
395     if cmd.name == 'deseed':
396         if not wallet.seed:
397             print_msg("Error: This wallet has no seed")
398         else:
399             ns = wallet.storage.path + '.seedless'
400             print_msg("Warning: you are going to create a seedless wallet'\nIt will be saved in '%s'" % ns)
401             if raw_input("Are you sure you want to continue? (y/n) ") in ['y', 'Y', 'yes']:
402                 wallet.storage.path = ns
403                 wallet.seed = ''
404                 wallet.storage.put('seed', '', True)
405                 wallet.use_encryption = False
406                 wallet.storage.put('use_encryption', wallet.use_encryption, True)
407                 for k in wallet.imported_keys.keys():
408                     wallet.imported_keys[k] = ''
409                 wallet.storage.put('imported_keys', wallet.imported_keys, True)
410                 print_msg("Done.")
411             else:
412                 print_msg("Action canceled.")
413
414     elif cmd.name == 'getconfig':
415         key = args[1]
416         out = config.get(key)
417         print_msg(out)
418
419     elif cmd.name == 'setconfig':
420         key, value = args[1:3]
421         try:
422             value = ast.literal_eval(value)
423         except:
424             pass
425         config.set_key(key, value, True)
426         print_msg(True)
427
428     elif cmd.name == 'password':
429         new_password = prompt_password('New password:')
430         wallet.update_password(password, new_password)
431
432     else:
433         run_command(cmd, password, args)
434
435
436     time.sleep(0.1)
437     sys.exit(0)