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_SHOW_MPK = _("This is your master public key")
21 MSG_ENTER_MPK = _("Please enter your master public key")
22 MSG_ENTER_COLD_MPK = _("Please enter the master public key of your cosigning wallet")
23 MSG_ENTER_SEED_OR_MPK = _("Please enter a wallet seed, or master public key")
24 MSG_VERIFY_SEED = _("Your seed is important!") + "\n" + _("To make sure that you have properly saved your seed, please retype it here.")
27 class InstallWizard(QDialog):
29 def __init__(self, config, network, storage):
30 QDialog.__init__(self)
32 self.network = network
33 self.storage = storage
34 self.setMinimumSize(575, 400)
35 self.setWindowTitle('Electrum')
36 self.connect(self, QtCore.SIGNAL('accept'), self.accept)
38 self.stack = QStackedLayout()
39 self.setLayout(self.stack)
42 def set_layout(self, layout):
45 self.stack.setCurrentIndex(self.stack.addWidget(w))
48 def restore_or_create(self):
52 main_label = QLabel(_("Electrum could not find an existing wallet."))
53 vbox.addWidget(main_label)
58 label = QLabel(_("What do you want to do?"))
59 label.setWordWrap(True)
60 grid.addWidget(label, 0, 0)
63 grid.addWidget(gb1, 0, 0)
65 group1 = QButtonGroup()
67 b1 = QRadioButton(gb1)
68 b1.setText(_("Create new wallet"))
71 b2 = QRadioButton(gb1)
72 b2.setText(_("Restore an existing wallet"))
77 grid.addWidget(b1, 1, 0)
78 grid.addWidget(b2, 2, 0)
84 label2 = QLabel(_("Wallet type:"))
85 grid2.addWidget(label2, 3, 0)
88 grid.addWidget(gb2, 3, 0)
90 group2 = QButtonGroup()
92 bb1 = QRadioButton(gb2)
93 bb1.setText(_("Standard wallet"))
96 bb2 = QRadioButton(gb2)
97 bb2.setText(_("Wallet with two-factor authentication (plugin)"))
99 bb3 = QRadioButton(gb2)
100 bb3.setText(_("Multisig wallet (paired manually)"))
102 grid2.addWidget(bb1, 4, 0)
103 grid2.addWidget(bb2, 5, 0)
104 grid2.addWidget(bb3, 6, 0)
106 group2.addButton(bb1)
107 group2.addButton(bb2)
108 group2.addButton(bb3)
110 vbox.addLayout(grid2)
112 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
114 self.set_layout(vbox)
118 action = 'create' if b1.isChecked() else 'restore'
122 elif bb2.isChecked():
123 t = 'multisig_plugin'
124 elif bb3.isChecked():
125 t = 'multisig_manual'
130 def verify_seed(self, seed, sid):
131 r = self.enter_seed_dialog(MSG_VERIFY_SEED, sid)
136 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
142 def get_seed_text(self, seed_e):
143 text = unicode(seed_e.toPlainText()).strip()
144 text = ' '.join(text.split())
148 def is_any(self, seed_e):
149 text = self.get_seed_text(seed_e)
150 return Wallet.is_seed(text) or Wallet.is_mpk(text) or Wallet.is_address(text) or Wallet.is_private_key(text)
152 def is_mpk(self, seed_e):
153 text = self.get_seed_text(seed_e)
154 return Wallet.is_mpk(text)
157 def enter_seed_dialog(self, msg, sid):
158 vbox, seed_e = seed_dialog.enter_seed_box(msg, sid)
160 hbox, button = ok_cancel_buttons2(self, _('Next'))
162 button.setEnabled(False)
163 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_any(seed_e)))
164 self.set_layout(vbox)
167 return self.get_seed_text(seed_e)
170 def cold_mpk_dialog(self, xpub_hot):
172 vbox1, seed_e1 = seed_dialog.enter_seed_box(MSG_SHOW_MPK, 'hot')
173 seed_e1.setText(xpub_hot)
174 seed_e1.setReadOnly(True)
175 vbox2, seed_e2 = seed_dialog.enter_seed_box(MSG_ENTER_COLD_MPK, 'cold')
176 vbox.addLayout(vbox1)
177 vbox.addLayout(vbox2)
179 hbox, button = ok_cancel_buttons2(self, _('Next'))
181 button.setEnabled(False)
182 f = lambda: button.setEnabled(self.is_mpk(seed_e2))
183 seed_e2.textChanged.connect(f)
184 self.set_layout(vbox)
187 return self.get_seed_text(seed_e2)
190 def double_seed_dialog(self):
192 vbox1, seed_e1 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'hot')
193 vbox2, seed_e2 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'cold')
194 vbox.addLayout(vbox1)
195 vbox.addLayout(vbox2)
197 hbox, button = ok_cancel_buttons2(self, _('Next'))
199 button.setEnabled(False)
200 f = lambda: button.setEnabled(self.is_any(seed_e1) and self.is_any(seed_e2))
201 seed_e1.textChanged.connect(f)
202 seed_e2.textChanged.connect(f)
203 self.set_layout(vbox)
206 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
211 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
214 self.emit(QtCore.SIGNAL('accept'))
217 self.waiting_label = QLabel(msg)
218 vbox.addWidget(self.waiting_label)
219 self.set_layout(vbox)
220 t = threading.Thread(target = target)
227 def network_dialog(self):
232 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" \
233 + _("How do you want to connect to a server:")+" ")
234 label.setWordWrap(True)
235 grid.addWidget(label, 0, 0)
239 b1 = QRadioButton(gb)
240 b1.setText(_("Auto connect"))
243 b2 = QRadioButton(gb)
244 b2.setText(_("Select server manually"))
246 #b3 = QRadioButton(gb)
247 #b3.setText(_("Stay offline"))
249 grid.addWidget(b1,1,0)
250 grid.addWidget(b2,2,0)
251 #grid.addWidget(b3,3,0)
257 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
259 self.set_layout(vbox)
264 return NetworkDialog(self.network, self.config, None).do_exec()
267 self.config.set_key('auto_cycle', True, True)
271 self.config.set_key("server", None, True)
272 self.config.set_key('auto_cycle', False, True)
276 def show_message(self, msg, icon=None):
278 self.set_layout(vbox)
283 vbox.addWidget(QLabel(msg))
285 vbox.addLayout(close_button(self, _('Next')))
290 def question(self, msg, icon=None):
292 self.set_layout(vbox)
297 vbox.addWidget(QLabel(msg))
299 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
305 def show_seed(self, seed, sid):
306 vbox = seed_dialog.show_seed_box(seed, sid)
307 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
308 self.set_layout(vbox)
312 def password_dialog(self):
313 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
314 +_("Leave these fields empty if you want to disable encryption.")
315 from password_dialog import make_password_dialog, run_password_dialog
316 self.set_layout( make_password_dialog(self, None, msg) )
317 return run_password_dialog(self, None, self)[2]
322 def run(self, action):
325 action, t = self.restore_or_create()
330 if action == 'create':
331 if t == 'multisig_plugin':
332 action = 'create_2of3_1'
333 if t == 'multisig_manual':
334 action = 'create_2of2_1'
336 if action in ['create']:
337 wallet = Wallet(self.storage)
338 elif action in ['create_2of2_1','create_2of2_2']:
339 wallet = Wallet_2of2(self.storage)
342 if action == 'create':
343 seed = wallet.make_seed()
344 if not self.show_seed(seed, None):
346 if not self.verify_seed(seed, None):
348 password = self.password_dialog()
349 wallet.add_seed(seed, password)
350 wallet.create_accounts(password)
351 self.waiting_dialog(wallet.synchronize)
354 if action == 'create_2of3_1':
355 run_hook('create_cold_seed', self.storage, self)
359 if action in ['create_2of2_1', 'create_2of3_2']:
360 seed = wallet.make_seed()
361 if not self.show_seed(seed, 'hot'):
363 if not self.verify_seed(seed, 'hot'):
365 password = self.password_dialog()
366 wallet.add_seed(seed, password)
367 if action == 'create_2of2_1':
369 action = 'create_2of2_2'
371 action = 'create_2of3_3'
373 if action == 'create_2of2_2':
374 xpub_hot = wallet.master_public_keys.get("m/")
375 xpub = self.cold_mpk_dialog(xpub_hot)
376 if not Wallet.is_mpk(xpub):
378 wallet.add_master_public_key("cold/", xpub)
379 wallet.create_account()
380 self.waiting_dialog(wallet.synchronize)
383 if action == 'create_2of3_3':
384 run_hook('create_remote_key', wallet, self)
385 if not wallet.master_public_keys.get("remote/"):
387 wallet.create_account()
388 self.waiting_dialog(wallet.synchronize)
391 if action == 'restore':
394 text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None)
397 if Wallet.is_seed(text):
398 password = self.password_dialog()
399 wallet = Wallet.from_seed(text, self.storage)
400 wallet.add_seed(text, password)
401 wallet.create_accounts(password)
402 elif Wallet.is_mpk(text):
403 wallet = Wallet.from_mpk(text, self.storage)
404 elif Wallet.is_address(text):
405 wallet = Wallet.from_address(text, self.storage)
406 elif Wallet.is_private_key(text):
407 wallet = Wallet.from_private_key(text, self.storage)
411 elif t in ['multisig_plugin', 'multisig_manual']:
412 r = self.double_seed_dialog()
416 password = self.password_dialog()
417 if t == 'multisig_manual':
418 wallet = Wallet_2of2(self.storage)
420 wallet = Wallet_2of3(self.storage)
422 if Wallet.is_seed(text1):
423 wallet.add_seed(text1, password)
424 if Wallet.is_seed(text2):
425 wallet.add_cold_seed(text2, password)
427 wallet.add_master_public_key("cold/", text2)
429 elif Wallet.is_mpk(text1):
430 if Wallet.is_seed(text2):
431 wallet.add_seed(text2, password)
432 wallet.add_master_public_key("cold/", text1)
434 wallet.add_master_public_key("m/", text1)
435 wallet.add_master_public_key("cold/", text2)
438 run_hook('restore_third_key', wallet, self)
440 wallet.create_account()
447 #if not self.config.get('server'):
449 if self.network.interfaces:
450 self.network_dialog()
452 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
456 # start wallet threads
457 wallet.start_threads(self.network)
459 if action == 'restore':
461 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
464 if wallet.is_found():
465 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
467 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
469 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))