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):
50 main_label = QLabel(_("Electrum could not find an existing wallet."))
51 vbox.addWidget(main_label)
56 label = QLabel(_("What do you want to do?"))
57 label.setWordWrap(True)
58 grid.addWidget(label, 0, 0)
61 grid.addWidget(gb1, 0, 0)
63 group1 = QButtonGroup()
65 b1 = QRadioButton(gb1)
66 b1.setText(_("Create new wallet"))
69 b2 = QRadioButton(gb1)
70 b2.setText(_("Restore an existing wallet"))
75 grid.addWidget(b1, 1, 0)
76 grid.addWidget(b2, 2, 0)
82 label2 = QLabel(_("Wallet type:"))
83 grid2.addWidget(label2, 3, 0)
86 grid.addWidget(gb2, 3, 0)
88 group2 = QButtonGroup()
90 bb1 = QRadioButton(gb2)
91 bb1.setText(_("Standard wallet"))
94 bb2 = QRadioButton(gb2)
95 bb2.setText(_("Wallet with two-factor authentication (plugin)"))
97 bb3 = QRadioButton(gb2)
98 bb3.setText(_("Multisig wallet (paired manually)"))
100 grid2.addWidget(bb1, 4, 0)
101 grid2.addWidget(bb2, 5, 0)
102 grid2.addWidget(bb3, 6, 0)
104 group2.addButton(bb1)
105 group2.addButton(bb2)
106 group2.addButton(bb3)
108 vbox.addLayout(grid2)
110 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
112 self.set_layout(vbox)
116 action = 'create' if b1.isChecked() else 'restore'
120 elif bb2.isChecked():
121 t = 'multisig_plugin'
122 elif bb3.isChecked():
123 t = 'multisig_manual'
128 def verify_seed(self, seed, sid):
129 r = self.enter_seed_dialog(MSG_VERIFY_SEED, sid)
134 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
140 def get_seed_text(self, seed_e):
141 text = unicode(seed_e.toPlainText()).strip()
142 text = ' '.join(text.split())
146 def is_seed(self, seed_e):
147 text = self.get_seed_text(seed_e)
148 return Wallet.is_seed(text) or Wallet.is_mpk(text) or Wallet.is_address(text) or Wallet.is_private_key(text)
151 def enter_seed_dialog(self, msg, sid):
152 vbox, seed_e = seed_dialog.enter_seed_box(msg, sid)
154 hbox, button = ok_cancel_buttons2(self, _('Next'))
156 button.setEnabled(False)
157 seed_e.textChanged.connect(lambda: button.setEnabled(self.is_seed(seed_e)))
158 self.set_layout(vbox)
161 return self.get_seed_text(seed_e)
164 def double_seed_dialog(self):
166 vbox1, seed_e1 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'hot')
167 vbox2, seed_e2 = seed_dialog.enter_seed_box(MSG_ENTER_SEED_OR_MPK, 'cold')
168 vbox.addLayout(vbox1)
169 vbox.addLayout(vbox2)
171 hbox, button = ok_cancel_buttons2(self, _('Next'))
173 button.setEnabled(False)
174 f = lambda: button.setEnabled(self.is_seed(seed_e1) and self.is_seed(seed_e2))
175 seed_e1.textChanged.connect(f)
176 seed_e2.textChanged.connect(f)
177 self.set_layout(vbox)
180 return self.get_seed_text(seed_e1), self.get_seed_text(seed_e2)
185 def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
188 self.emit(QtCore.SIGNAL('accept'))
191 self.waiting_label = QLabel(msg)
192 vbox.addWidget(self.waiting_label)
193 self.set_layout(vbox)
194 t = threading.Thread(target = target)
201 def network_dialog(self):
206 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" \
207 + _("How do you want to connect to a server:")+" ")
208 label.setWordWrap(True)
209 grid.addWidget(label, 0, 0)
213 b1 = QRadioButton(gb)
214 b1.setText(_("Auto connect"))
217 b2 = QRadioButton(gb)
218 b2.setText(_("Select server manually"))
220 #b3 = QRadioButton(gb)
221 #b3.setText(_("Stay offline"))
223 grid.addWidget(b1,1,0)
224 grid.addWidget(b2,2,0)
225 #grid.addWidget(b3,3,0)
231 vbox.addLayout(ok_cancel_buttons(self, _('Next')))
233 self.set_layout(vbox)
238 return NetworkDialog(self.network, self.config, None).do_exec()
241 self.config.set_key('auto_cycle', True, True)
245 self.config.set_key("server", None, True)
246 self.config.set_key('auto_cycle', False, True)
250 def show_message(self, msg, icon=None):
252 self.set_layout(vbox)
257 vbox.addWidget(QLabel(msg))
259 vbox.addLayout(close_button(self, _('Next')))
264 def question(self, msg, icon=None):
266 self.set_layout(vbox)
271 vbox.addWidget(QLabel(msg))
273 vbox.addLayout(ok_cancel_buttons(self, _('OK')))
279 def show_seed(self, seed, sid):
280 vbox = seed_dialog.show_seed_box(seed, sid)
281 vbox.addLayout(ok_cancel_buttons(self, _("Next")))
282 self.set_layout(vbox)
286 def password_dialog(self):
287 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
288 +_("Leave these fields empty if you want to disable encryption.")
289 from password_dialog import make_password_dialog, run_password_dialog
290 self.set_layout( make_password_dialog(self, None, msg) )
291 return run_password_dialog(self, None, self)[2]
296 def run(self, action):
299 action, t = self.restore_or_create()
304 if action == 'create':
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':
370 text = self.enter_seed_dialog(MSG_ENTER_ANYTHING, None)
373 if Wallet.is_seed(text):
374 password = self.password_dialog()
375 wallet = Wallet.from_seed(text, self.storage)
376 wallet.add_seed(text, password)
377 wallet.create_accounts(password)
378 elif Wallet.is_mpk(text):
379 wallet = Wallet.from_mpk(text, self.storage)
380 elif Wallet.is_address(text):
381 wallet = Wallet.from_address(text, self.storage)
382 elif Wallet.is_private_key(text):
383 wallet = Wallet.from_private_key(text, self.storage)
387 elif t in ['multisig_plugin', 'multisig_manual']:
388 r = self.double_seed_dialog()
392 password = self.password_dialog()
393 if t == 'multisig_manual':
394 wallet = Wallet_2of2(self.storage)
396 wallet = Wallet_2of3(self.storage)
398 if Wallet.is_seed(text1):
399 wallet.add_seed(text1, password)
400 if Wallet.is_seed(text2):
401 wallet.add_cold_seed(text2, password)
403 wallet.add_master_public_key("cold/", text2)
405 elif Wallet.is_mpk(text1):
406 if Wallet.is_seed(text2):
407 wallet.add_seed(text2, password)
408 wallet.add_master_public_key("cold/", text1)
410 wallet.add_master_public_key("m/", text1)
411 wallet.add_master_public_key("cold/", text2)
414 run_hook('restore_third_key', wallet, self)
416 wallet.create_account()
423 #if not self.config.get('server'):
425 if self.network.interfaces:
426 self.network_dialog()
428 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
432 # start wallet threads
433 wallet.start_threads(self.network)
435 if action == 'restore':
437 self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
440 if wallet.is_found():
441 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
443 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
445 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))