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
18 class InstallWizard(QDialog):
20 def __init__(self, config, network, storage):
21 QDialog.__init__(self)
23 self.network = network
24 self.storage = storage
25 self.setMinimumSize(575, 400)
26 self.setWindowTitle('Electrum')
27 self.connect(self, QtCore.SIGNAL('accept'), self.accept)
29 self.stack = QStackedLayout()
30 self.setLayout(self.stack)
33 def set_layout(self, layout):
36 self.stack.setCurrentIndex(self.stack.addWidget(w))
39 def restore_or_create(self):
44 msg = _("Electrum could not find an existing wallet.") + "\n\n" \
45 + _("What do you want to do?") + "\n"
47 label.setWordWrap(True)
48 grid.addWidget(label, 0, 0)
53 b1.setText(_("Create new wallet"))
57 b2.setText(_("Restore an existing wallet"))
59 grid.addWidget(b1,1,0)
60 grid.addWidget(b2,2,0)
67 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
72 return 'create' if b1.isChecked() else 'restore'
76 def verify_seed(self, seed, sid):
77 r = self.enter_seed_dialog(False, sid)
82 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
88 def get_seed_text(self, seed_e):
89 text = unicode(seed_e.toPlainText()).strip()
90 text = ' '.join(text.split())
94 def is_seed(self, seed_e):
95 text = self.get_seed_text(seed_e)
96 return Wallet.is_seed(text) or Wallet.is_mpk(text) or Wallet.is_address(text) or Wallet.is_private_key(text)
99 def enter_seed_dialog(self, is_restore, sid):
100 vbox, seed_e = seed_dialog.enter_seed_box(is_restore, sid)
102 hbox, button = ok_cancel_buttons2(self, _('Next'))
104 button.setEnabled(False)
105 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
106 self.set_layout(vbox)
109 return self.get_seed_text(seed_e)
112 def double_seed_dialog(self):
114 vbox1, seed_e1 = seed_dialog.enter_seed_box(True, 'hot')
115 vbox2, seed_e2 = seed_dialog.enter_seed_box(True, 'cold')
116 vbox.addLayout(vbox1)
117 vbox.addLayout(vbox2)
119 hbox, button = ok_cancel_buttons2(self, _('Next'))
121 button.setEnabled(False)
122 f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
123 seed_e1.textChanged.connect(f)
124 seed_e2.textChanged.connect(f)
125 self.set_layout(vbox)
128 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
133 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
136 self.emit(QtCore.SIGNAL('accept'))
139 self.waiting_label = QLabel(msg)
140 vbox.addWidget(self.waiting_label)
141 self.set_layout(vbox)
142 t = threading.Thread(target = target)
149 def network_dialog(self):
154 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" \
155 + _("How do you want to connect to a server:")+" ")
156 label.setWordWrap(True)
157 grid.addWidget(label, 0, 0)
161 b1 = QRadioButton(gb)
162 b1.setText(_("Auto connect"))
165 b2 = QRadioButton(gb)
166 b2.setText(_("Select server manually"))
168 #b3 = QRadioButton(gb)
169 #b3.setText(_("Stay offline"))
171 grid.addWidget(b1,1,0)
172 grid.addWidget(b2,2,0)
173 #grid.addWidget(b3,3,0)
179 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
181 self.set_layout(vbox)
186 return NetworkDialog(self.network, self.config, None).do_exec()
189 self.config.set_key('auto_cycle', True, True)
193 self.config.set_key("server", None, True)
194 self.config.set_key('auto_cycle', False, True)
198 def show_message(self, msg, icon=None):
200 self.set_layout(vbox)
205 vbox.addWidget(QLabel(msg))
207 vbox.addLayout(close_button(self, _('Next')))
212 def question(self, msg, icon=None):
214 self.set_layout(vbox)
219 vbox.addWidget(QLabel(msg))
221 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
227 def show_seed(self, seed, sid):
228 vbox = seed_dialog.show_seed_box(seed, sid)
229 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
230 self.set_layout(vbox)
234 def password_dialog(self):
235 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
236 +_("Leave these fields empty if you want to disable encryption.")
237 from password_dialog import make_password_dialog, run_password_dialog
238 self.set_layout( make_password_dialog(self, None, msg) )
239 return run_password_dialog(self, None, self)[2]
242 def choose_wallet_type(self):
246 msg = _("Choose your wallet.")
248 label.setWordWrap(True)
249 grid.addWidget(label, 0, 0)
253 b1 = QRadioButton(gb)
254 b1.setText(_("Standard wallet"))
257 b2 = QRadioButton(gb)
258 b2.setText(_("Wallet with two-factor authentication (plugin)"))
260 b3 = QRadioButton(gb)
261 b3.setText(_("Multisig wallet (paired manually)"))
263 grid.addWidget(b1,1,0)
264 grid.addWidget(b2,2,0)
265 grid.addWidget(b3,3,0)
271 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
273 self.set_layout(vbox)
280 return 'multisig_plugin'
282 return 'multisig_manual'
285 def run(self, action):
288 action = self.restore_or_create()
293 if action == 'create':
294 t = self.choose_wallet_type()
298 if t == 'multisig_plugin':
299 action = 'create_2of3_1'
300 if t == 'multisig_manual':
301 action = 'create_2of2_1'
303 if action in ['create']:
304 wallet = Wallet(self.storage)
305 elif action in ['create_2of2_1','create_2of2_2']:
306 wallet = Wallet_2of2(self.storage)
309 if action == 'create':
310 seed = wallet.make_seed()
311 if not self.show_seed(seed, None):
313 if not self.verify_seed(seed, None):
315 password = self.password_dialog()
316 wallet.add_seed(seed, password)
317 wallet.create_accounts(password)
318 self.waiting_dialog(wallet.synchronize)
321 if action == 'create_2of3_1':
322 run_hook('create_cold_seed', self.storage, self)
326 if action in ['create_2of2_1', 'create_2of3_2']:
327 msg = _('You are about to create the hot seed of a multisig wallet')
328 if not self.question(msg):
330 seed = wallet.make_seed()
331 if not self.show_seed(seed, 'hot'):
333 if not self.verify_seed(seed, 'hot'):
335 password = self.password_dialog()
336 wallet.add_seed(seed, password)
337 if action == 'create_2of2_1':
339 action = 'create_2of2_2'
341 action = 'create_2of3_3'
343 if action == 'create_2of2_2':
344 xpub = self.enter_seed_dialog(True, 'cold')
345 if not Wallet.is_mpk(xpub):
347 wallet.add_master_public_key("cold/", xpub)
348 wallet.create_account()
349 self.waiting_dialog(wallet.synchronize)
352 if action == 'create_2of3_3':
353 run_hook('create_remote_key', wallet, self)
354 if not wallet.master_public_keys.get("remote/"):
356 wallet.create_account()
357 self.waiting_dialog(wallet.synchronize)
360 if action == 'restore':
361 t = self.choose_wallet_type()
366 text = self.enter_seed_dialog(True, None)
369 if Wallet.is_seed(text):
370 password = self.password_dialog()
371 wallet = Wallet.from_seed(text, self.storage)
372 wallet.add_seed(text, password)
373 wallet.create_accounts(password)
374 elif Wallet.is_mpk(text):
375 wallet = Wallet.from_mpk(text, self.storage)
376 elif Wallet.is_address(text):
377 wallet = Wallet.from_address(text, self.storage)
378 elif Wallet.is_private_key(text):
379 wallet = Wallet.from_private_key(text, self.storage)
383 elif t in ['multisig_plugin', 'multisig_manual']:
384 r = self.double_seed_dialog()
388 password = self.password_dialog()
389 if t == 'multisig_manual':
390 wallet = Wallet_2of2(self.storage)
392 wallet = Wallet_2of3(self.storage)
394 if Wallet.is_seed(text1):
395 wallet.add_seed(text1, password)
396 if Wallet.is_seed(text2):
397 wallet.add_cold_seed(text2, password)
399 wallet.add_master_public_key("cold/", text2)
401 elif Wallet.is_mpk(text1):
402 if Wallet.is_seed(text2):
403 wallet.add_seed(text2, password)
404 wallet.add_master_public_key("cold/", text1)
406 wallet.add_master_public_key("m/", text1)
407 wallet.add_master_public_key("cold/", text2)
410 run_hook('restore_third_key', wallet, self)
412 wallet.create_account()
419 #if not self.config.get('server'):
421 if self.network.interfaces:
422 self.network_dialog()
424 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
428 # start wallet threads
429 wallet.start_threads(self.network)
431 if action == 'restore':
433 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
436 if wallet.is_found():
437 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
439 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
441 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))