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
9 from network_dialog import NetworkDialog
11 from amountedit import AmountEdit
15 from electrum.plugins import run_hook
17 class InstallWizard(QDialog):
19 def __init__(self, config, network, storage):
20 QDialog.__init__(self)
22 self.network = network
23 self.storage = storage
24 self.setMinimumSize(575, 400)
25 self.setWindowTitle('Electrum')
26 self.connect(self, QtCore.SIGNAL('accept'), self.accept)
28 self.stack = QStackedLayout()
29 self.setLayout(self.stack)
32 def set_layout(self, layout):
35 self.stack.setCurrentIndex(self.stack.addWidget(w))
38 def restore_or_create(self):
43 msg = _("Electrum could not find an existing wallet.") + "\n\n" \
44 + _("What do you want to do?") + "\n"
46 label.setWordWrap(True)
47 grid.addWidget(label, 0, 0)
52 b1.setText(_("Create new wallet"))
56 b2.setText(_("Restore an existing wallet"))
58 grid.addWidget(b1,1,0)
59 grid.addWidget(b2,2,0)
66 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
71 return 'create' if b1.isChecked() else 'restore'
75 def verify_seed(self, seed, sid):
76 r = self.enter_seed_dialog(False, sid)
81 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
87 def get_seed_text(self, seed_e):
88 text = unicode(seed_e.toPlainText()).strip()
89 text = ' '.join(text.split())
93 def is_seed(self, seed_e):
94 text = self.get_seed_text(seed_e)
95 return Wallet.is_seed(text) or Wallet.is_mpk(text)
98 def enter_seed_dialog(self, is_restore, sid):
99 vbox, seed_e = seed_dialog.enter_seed_box(is_restore, sid)
101 hbox, button = ok_cancel_buttons2(self, _('Next'))
103 button.setEnabled(False)
104 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
105 self.set_layout(vbox)
108 return self.get_seed_text(seed_e)
111 def double_seed_dialog(self):
113 vbox1, seed_e1 = seed_dialog.enter_seed_box(True, 'hot')
114 vbox2, seed_e2 = seed_dialog.enter_seed_box(True, 'cold')
115 vbox.addLayout(vbox1)
116 vbox.addLayout(vbox2)
118 hbox, button = ok_cancel_buttons2(self, _('Next'))
120 button.setEnabled(False)
121 f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
122 seed_e1.textChanged.connect(f)
123 seed_e2.textChanged.connect(f)
124 self.set_layout(vbox)
127 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
132 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
135 self.emit(QtCore.SIGNAL('accept'))
138 self.waiting_label = QLabel(msg)
139 vbox.addWidget(self.waiting_label)
140 self.set_layout(vbox)
141 t = threading.Thread(target = target)
148 def network_dialog(self):
153 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" \
154 + _("How do you want to connect to a server:")+" ")
155 label.setWordWrap(True)
156 grid.addWidget(label, 0, 0)
160 b1 = QRadioButton(gb)
161 b1.setText(_("Auto connect"))
164 b2 = QRadioButton(gb)
165 b2.setText(_("Select server manually"))
167 #b3 = QRadioButton(gb)
168 #b3.setText(_("Stay offline"))
170 grid.addWidget(b1,1,0)
171 grid.addWidget(b2,2,0)
172 #grid.addWidget(b3,3,0)
178 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
180 self.set_layout(vbox)
185 return NetworkDialog(self.network, self.config, None).do_exec()
188 self.config.set_key('auto_cycle', True, True)
192 self.config.set_key("server", None, True)
193 self.config.set_key('auto_cycle', False, True)
197 def show_message(self, msg, icon=None):
199 self.set_layout(vbox)
204 vbox.addWidget(QLabel(msg))
206 vbox.addLayout(close_button(self, _('Next')))
211 def question(self, msg, icon=None):
213 self.set_layout(vbox)
218 vbox.addWidget(QLabel(msg))
220 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
226 def show_seed(self, seed, sid):
227 vbox = seed_dialog.show_seed_box(seed, sid)
228 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
229 self.set_layout(vbox)
233 def password_dialog(self):
234 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
235 +_("Leave these fields empty if you want to disable encryption.")
236 from password_dialog import make_password_dialog, run_password_dialog
237 self.set_layout( make_password_dialog(self, None, msg) )
238 return run_password_dialog(self, None, self)[2]
241 def choose_wallet_type(self):
245 msg = _("Choose your wallet.")
247 label.setWordWrap(True)
248 grid.addWidget(label, 0, 0)
252 b1 = QRadioButton(gb)
253 b1.setText(_("Standard wallet"))
256 b2 = QRadioButton(gb)
257 b2.setText(_("Wallet with two-factor authentication (plugin)"))
259 b3 = QRadioButton(gb)
260 b3.setText(_("Multisig wallet (paired manually)"))
262 grid.addWidget(b1,1,0)
263 grid.addWidget(b2,2,0)
264 grid.addWidget(b3,3,0)
270 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
272 self.set_layout(vbox)
279 return 'multisig_plugin'
281 return 'multisig_manual'
284 def run(self, action):
287 action = self.restore_or_create()
292 if action == 'create':
293 t = self.choose_wallet_type()
297 if t == 'multisig_plugin':
298 action = 'create_2of3_1'
299 if t == 'multisig_manual':
300 action = 'create_2of2_1'
302 if action in ['create']:
303 wallet = Wallet(self.storage)
304 elif action in ['create_2of2_1','create_2of2_2']:
305 wallet = Wallet_2of2(self.storage)
308 if action == 'create':
309 seed = wallet.make_seed()
310 if not self.show_seed(seed, None):
312 if not self.verify_seed(seed, None):
314 password = self.password_dialog()
315 wallet.add_seed(seed, password)
316 wallet.create_accounts(password)
317 self.waiting_dialog(wallet.synchronize)
320 if action == 'create_2of3_1':
321 run_hook('create_cold_seed', self.storage, self)
325 if action in ['create_2of2_1', 'create_2of3_2']:
326 msg = _('You are about to create the hot seed of a multisig wallet')
327 if not self.question(msg):
329 seed = wallet.make_seed()
330 if not self.show_seed(seed, 'hot'):
332 if not self.verify_seed(seed, 'hot'):
334 password = self.password_dialog()
335 wallet.add_seed(seed, password)
336 if action == 'create_2of2_1':
338 action = 'create_2of2_2'
340 action = 'create_2of3_3'
342 if action == 'create_2of2_2':
343 xpub = self.enter_seed_dialog(True, 'cold')
344 if not Wallet.is_mpk(xpub):
346 wallet.add_master_public_key("cold/", xpub)
347 wallet.create_account()
348 self.waiting_dialog(wallet.synchronize)
351 if action == 'create_2of3_3':
352 run_hook('create_remote_key', wallet, self)
353 if not wallet.master_public_keys.get("remote/"):
355 wallet.create_account()
356 self.waiting_dialog(wallet.synchronize)
359 if action == 'restore':
360 t = self.choose_wallet_type()
365 text = self.enter_seed_dialog(True, None)
366 if Wallet.is_seed(text):
367 password = self.password_dialog()
368 wallet = Wallet.from_seed(text, self.storage)
369 wallet.add_seed(text, password)
370 wallet.create_accounts(password)
371 elif Wallet.is_mpk(text):
372 wallet = Wallet.from_mpk(text, self.storage)
376 elif t in ['multisig_plugin', 'multisig_manual']:
377 r = self.double_seed_dialog()
381 password = self.password_dialog()
382 if t == 'multisig_manual':
383 wallet = Wallet_2of2(self.storage)
385 wallet = Wallet_2of3(self.storage)
387 if Wallet.is_seed(text1):
388 wallet.add_seed(text1, password)
389 if Wallet.is_seed(text2):
390 wallet.add_cold_seed(text2, password)
392 wallet.add_master_public_key("cold/", text2)
394 elif Wallet.is_mpk(text1):
395 if Wallet.is_seed(text2):
396 wallet.add_seed(text2, password)
397 wallet.add_master_public_key("cold/", text1)
399 wallet.add_master_public_key("m/", text1)
400 wallet.add_master_public_key("cold/", text2)
403 run_hook('restore_third_key', wallet, self)
405 wallet.create_account()
412 #if not self.config.get('server'):
414 if self.network.interfaces:
415 self.network_dialog()
417 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
421 # start wallet threads
422 wallet.start_threads(self.network)
424 if action == 'restore':
426 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
429 if wallet.is_found():
430 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
432 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
434 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))