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_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 return unicode(seed_e.toPlainText())
91 def is_seed(self, seed_e):
92 text = self.get_seed_text(seed_e)
93 return Wallet.is_seed(text) or Wallet.is_mpk(text)
96 def enter_seed_dialog(self, is_restore, sid):
97 vbox, seed_e = seed_dialog.enter_seed_box(is_restore, sid)
99 hbox, button = ok_cancel_buttons2(self, _('Next'))
101 button.setEnabled(False)
102 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
103 self.set_layout(vbox)
106 return self.get_seed_text(seed_e)
109 def double_seed_dialog(self):
111 vbox1, seed_e1 = seed_dialog.enter_seed_box(True, 'hot')
112 vbox2, seed_e2 = seed_dialog.enter_seed_box(True, 'cold')
113 vbox.addLayout(vbox1)
114 vbox.addLayout(vbox2)
116 hbox, button = ok_cancel_buttons2(self, _('Next'))
118 button.setEnabled(False)
119 f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
120 seed_e1.textChanged.connect(f)
121 seed_e2.textChanged.connect(f)
122 self.set_layout(vbox)
125 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
130 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
133 self.emit(QtCore.SIGNAL('accept'))
136 self.waiting_label = QLabel(msg)
137 vbox.addWidget(self.waiting_label)
138 self.set_layout(vbox)
139 t = threading.Thread(target = target)
146 def network_dialog(self):
151 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" \
152 + _("How do you want to connect to a server:")+" ")
153 label.setWordWrap(True)
154 grid.addWidget(label, 0, 0)
158 b1 = QRadioButton(gb)
159 b1.setText(_("Auto connect"))
162 b2 = QRadioButton(gb)
163 b2.setText(_("Select server manually"))
165 #b3 = QRadioButton(gb)
166 #b3.setText(_("Stay offline"))
168 grid.addWidget(b1,1,0)
169 grid.addWidget(b2,2,0)
170 #grid.addWidget(b3,3,0)
176 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
178 self.set_layout(vbox)
183 return NetworkDialog(self.network, self.config, None).do_exec()
186 self.config.set_key('auto_cycle', True, True)
190 self.config.set_key("server", None, True)
191 self.config.set_key('auto_cycle', False, True)
195 def show_message(self, msg):
197 vbox.addWidget(QLabel(msg))
199 vbox.addLayout(close_button(self, _('Next')))
200 self.set_layout(vbox)
204 def question(self, msg):
206 vbox.addWidget(QLabel(msg))
208 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
209 self.set_layout(vbox)
215 def show_seed(self, seed, sid):
216 vbox = seed_dialog.show_seed_box(seed, sid)
217 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
218 self.set_layout(vbox)
222 def password_dialog(self, wallet):
223 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
224 +_("Leave these fields empty if you want to disable encryption.")
225 from password_dialog import make_password_dialog, run_password_dialog
226 self.set_layout( make_password_dialog(self, wallet, msg) )
227 return run_password_dialog(self, wallet, self)
230 def choose_wallet_type(self):
234 msg = _("Choose your wallet.")
236 label.setWordWrap(True)
237 grid.addWidget(label, 0, 0)
241 b1 = QRadioButton(gb)
242 b1.setText(_("Standard wallet (protected by password)"))
245 b2 = QRadioButton(gb)
246 b2.setText(_("Multi-signature wallet (two-factor authentication)"))
248 grid.addWidget(b1,1,0)
249 grid.addWidget(b2,2,0)
255 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
257 self.set_layout(vbox)
267 def run(self, action = None):
270 action = self.restore_or_create()
275 if action == 'create':
276 t = self.choose_wallet_type()
281 run_hook('create_cold_seed', self.storage, self)
285 if action in ['create', 'create2of3']:
287 wallet = Wallet(self.storage)
289 wallet.init_seed(None)
290 seed = wallet.get_mnemonic(None)
291 sid = 'hot' if action == 'create2of3' else None
292 if not self.show_seed(seed, sid):
294 if not self.verify_seed(seed, sid):
296 ok, old_password, password = self.password_dialog(wallet)
297 wallet.save_seed(password)
299 if action == 'create2of3':
300 run_hook('create_third_key', wallet, self)
301 if not wallet.master_public_keys.get("remote/"):
304 wallet.create_accounts(password)
305 # generate first addresses offline
306 self.waiting_dialog(wallet.synchronize)
308 elif action == 'restore':
309 # dialog box will accept either seed or xpub.
310 # use two boxes for 2of3
311 t = self.choose_wallet_type()
316 text = self.enter_seed_dialog(True, None)
317 if Wallet.is_seed(text):
318 wallet = Wallet.from_seed(text, self.storage)
319 ok, old_password, password = self.password_dialog(wallet)
320 wallet.save_seed(password)
321 wallet.create_accounts(password)
322 elif Wallet.is_mpk(text):
323 wallet = Wallet.from_mpk(text, self.storage)
327 elif t in ['2of2', '2of3']:
328 r = self.double_seed_dialog()
332 wallet = Wallet_2of3(self.storage)
334 if Wallet.is_seed(text1):
335 xpriv, xpub = bip32_root(text1)
336 elif Wallet.is_mpk(text1):
338 wallet.add_master_public_key("m/", xpub)
340 if Wallet.is_seed(text2):
341 xpriv2, xpub2 = bip32_root(text2)
342 elif Wallet.is_mpk(text2):
344 wallet.add_master_public_key("cold/", xpub2)
346 run_hook('restore_third_key', wallet, self)
348 wallet.create_accounts(None)
355 #if not self.config.get('server'):
357 if self.network.interfaces:
358 self.network_dialog()
360 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
364 # start wallet threads
365 wallet.start_threads(self.network)
367 if action == 'restore':
369 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
372 if wallet.is_found():
373 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
375 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
377 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))