1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3 import PyQt4.QtCore as QtCore
5 from electrum.i18n import _
6 from electrum import Wallet, Wallet_2of2, Wallet_2of3
7 import electrum.bitcoin as bitcoin
10 from network_dialog import NetworkDialog
12 from amountedit import AmountEdit
16 from electrum.plugins import run_hook
19 MSG_ENTER_ANYTHING = _("Please enter a wallet seed, a master public key, a list of Bitcoin addresses, or a list of private keys")
20 MSG_ENTER_MPK = _("Please enter your master public key")
21 MSG_ENTER_SEED_OR_MPK = _("Please enter a wallet seed, or master public key")
22 MSG_VERIFY_SEED = _("Your seed is important!") + "\n" + _("To make sure that you have properly saved your seed, please retype it here.")
25 class InstallWizard(QDialog):
27 def __init__(self, config, network, storage):
28 QDialog.__init__(self)
30 self.network = network
31 self.storage = storage
32 self.setMinimumSize(575, 400)
33 self.setWindowTitle('Electrum')
34 self.connect(self, QtCore.SIGNAL('accept'), self.accept)
36 self.stack = QStackedLayout()
37 self.setLayout(self.stack)
40 def set_layout(self, layout):
43 self.stack.setCurrentIndex(self.stack.addWidget(w))
46 def restore_or_create(self):
51 msg = _("Electrum could not find an existing wallet.") + "\n\n" \
52 + _("What do you want to do?") + "\n"
54 label.setWordWrap(True)
55 grid.addWidget(label, 0, 0)
60 b1.setText(_("Create new wallet"))
64 b2.setText(_("Restore an existing wallet"))
66 grid.addWidget(b1,1,0)
67 grid.addWidget(b2,2,0)
74 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
79 return 'create' if b1.isChecked() else 'restore'
83 def verify_seed(self, seed, sid):
84 r = self.enter_seed_dialog(MSG_VERIFY_SEED, sid)
89 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
95 def get_seed_text(self, seed_e):
96 text = unicode(seed_e.toPlainText()).strip()
97 text = ' '.join(text.split())
101 def is_seed(self, seed_e):
102 text = self.get_seed_text(seed_e)
103 return Wallet.is_seed(text) or Wallet.is_mpk(text) or Wallet.is_address(text) or Wallet.is_private_key(text)
106 def enter_seed_dialog(self, msg, sid):
107 vbox, seed_e = seed_dialog.enter_seed_box(msg, sid)
109 hbox, button = ok_cancel_buttons2(self, _('Next'))
111 button.setEnabled(False)
112 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
113 self.set_layout(vbox)
116 return self.get_seed_text(seed_e)
119 def double_seed_dialog(self):
121 vbox1, seed_e1 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'hot')
122 vbox2, seed_e2 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'cold')
123 vbox.addLayout(vbox1)
124 vbox.addLayout(vbox2)
126 hbox, button = ok_cancel_buttons2(self, _('Next'))
128 button.setEnabled(False)
129 f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
130 seed_e1.textChanged.connect(f)
131 seed_e2.textChanged.connect(f)
132 self.set_layout(vbox)
135 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
140 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
143 self.emit(QtCore.SIGNAL('accept'))
146 self.waiting_label = QLabel(msg)
147 vbox.addWidget(self.waiting_label)
148 self.set_layout(vbox)
149 t = threading.Thread(target = target)
156 def network_dialog(self):
161 label = QLabel(_("Electrum communicates with remote servers to get information about your transactions and addresses. The servers all fulfil the same purpose only differing in hardware. In most cases you simply want to let Electrum pick one at random if you have a preference though feel free to select a server manually.") + "\n\n" \
162 + _("How do you want to connect to a server:")+" ")
163 label.setWordWrap(True)
164 grid.addWidget(label, 0, 0)
168 b1 = QRadioButton(gb)
169 b1.setText(_("Auto connect"))
172 b2 = QRadioButton(gb)
173 b2.setText(_("Select server manually"))
175 #b3 = QRadioButton(gb)
176 #b3.setText(_("Stay offline"))
178 grid.addWidget(b1,1,0)
179 grid.addWidget(b2,2,0)
180 #grid.addWidget(b3,3,0)
186 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
188 self.set_layout(vbox)
193 return NetworkDialog(self.network, self.config, None).do_exec()
196 self.config.set_key('auto_cycle', True, True)
200 self.config.set_key("server", None, True)
201 self.config.set_key('auto_cycle', False, True)
205 def show_message(self, msg, icon=None):
207 self.set_layout(vbox)
212 vbox.addWidget(QLabel(msg))
214 vbox.addLayout(close_button(self, _('Next')))
219 def question(self, msg, icon=None):
221 self.set_layout(vbox)
226 vbox.addWidget(QLabel(msg))
228 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
234 def show_seed(self, seed, sid):
235 vbox = seed_dialog.show_seed_box(seed, sid)
236 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
237 self.set_layout(vbox)
241 def password_dialog(self):
242 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
243 +_("Leave these fields empty if you want to disable encryption.")
244 from password_dialog import make_password_dialog, run_password_dialog
245 self.set_layout( make_password_dialog(self, None, msg) )
246 return run_password_dialog(self, None, self)[2]
249 def choose_wallet_type(self):
253 msg = _("Choose your wallet.")
255 label.setWordWrap(True)
256 grid.addWidget(label, 0, 0)
260 b1 = QRadioButton(gb)
261 b1.setText(_("Standard wallet"))
264 b2 = QRadioButton(gb)
265 b2.setText(_("Wallet with two-factor authentication (plugin)"))
267 b3 = QRadioButton(gb)
268 b3.setText(_("Multisig wallet (paired manually)"))
270 grid.addWidget(b1,1,0)
271 grid.addWidget(b2,2,0)
272 grid.addWidget(b3,3,0)
278 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
280 self.set_layout(vbox)
287 return 'multisig_plugin'
289 return 'multisig_manual'
292 def run(self, action):
295 action = self.restore_or_create()
300 if action == 'create':
301 t = self.choose_wallet_type()
305 if t == 'multisig_plugin':
306 action = 'create_2of3_1'
307 if t == 'multisig_manual':
308 action = 'create_2of2_1'
310 if action in ['create']:
311 wallet = Wallet(self.storage)
312 elif action in ['create_2of2_1','create_2of2_2']:
313 wallet = Wallet_2of2(self.storage)
316 if action == 'create':
317 seed = wallet.make_seed()
318 if not self.show_seed(seed, None):
320 if not self.verify_seed(seed, None):
322 password = self.password_dialog()
323 wallet.add_seed(seed, password)
324 wallet.create_accounts(password)
325 self.waiting_dialog(wallet.synchronize)
328 if action == 'create_2of3_1':
329 run_hook('create_cold_seed', self.storage, self)
333 if action in ['create_2of2_1', 'create_2of3_2']:
334 msg = _('You are about to create the hot seed of a multisig wallet')
335 if not self.question(msg):
337 seed = wallet.make_seed()
338 if not self.show_seed(seed, 'hot'):
340 if not self.verify_seed(seed, 'hot'):
342 password = self.password_dialog()
343 wallet.add_seed(seed, password)
344 if action == 'create_2of2_1':
346 action = 'create_2of2_2'
348 action = 'create_2of3_3'
350 if action == 'create_2of2_2':
351 xpub = self.enter_seed_dialog(MSG_ENTER_MPK, 'cold')
352 if not Wallet.is_mpk(xpub):
354 wallet.add_master_public_key("cold/", xpub)
355 wallet.create_account()
356 self.waiting_dialog(wallet.synchronize)
359 if action == 'create_2of3_3':
360 run_hook('create_remote_key', wallet, self)
361 if not wallet.master_public_keys.get("remote/"):
363 wallet.create_account()
364 self.waiting_dialog(wallet.synchronize)
367 if action == 'restore':
368 t = self.choose_wallet_type()
373 text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None)
376 if Wallet.is_seed(text):
377 password = self.password_dialog()
378 wallet = Wallet.from_seed(text, self.storage)
379 wallet.add_seed(text, password)
380 wallet.create_accounts(password)
381 elif Wallet.is_mpk(text):
382 wallet = Wallet.from_mpk(text, self.storage)
383 elif Wallet.is_address(text):
384 wallet = Wallet.from_address(text, self.storage)
385 elif Wallet.is_private_key(text):
386 wallet = Wallet.from_private_key(text, self.storage)
390 elif t in ['multisig_plugin', 'multisig_manual']:
391 r = self.double_seed_dialog()
395 password = self.password_dialog()
396 if t == 'multisig_manual':
397 wallet = Wallet_2of2(self.storage)
399 wallet = Wallet_2of3(self.storage)
401 if Wallet.is_seed(text1):
402 wallet.add_seed(text1, password)
403 if Wallet.is_seed(text2):
404 wallet.add_cold_seed(text2, password)
406 wallet.add_master_public_key("cold/", text2)
408 elif Wallet.is_mpk(text1):
409 if Wallet.is_seed(text2):
410 wallet.add_seed(text2, password)
411 wallet.add_master_public_key("cold/", text1)
413 wallet.add_master_public_key("m/", text1)
414 wallet.add_master_public_key("cold/", text2)
417 run_hook('restore_third_key', wallet, self)
419 wallet.create_account()
426 #if not self.config.get('server'):
428 if self.network.interfaces:
429 self.network_dialog()
431 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
435 # start wallet threads
436 wallet.start_threads(self.network)
438 if action == 'restore':
440 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
443 if wallet.is_found():
444 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
446 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
448 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))