check seed in gui. fixes #622
[electrum-nvc.git] / gui / qt / installwizard.py
1 from PyQt4.QtGui import *
2 from PyQt4.QtCore import *
3 import PyQt4.QtCore as QtCore
4
5 from electrum.i18n import _
6 from electrum import Wallet
7
8 from seed_dialog import SeedDialog
9 from network_dialog import NetworkDialog
10 from util import *
11 from amountedit import AmountEdit
12
13 import sys
14 import threading
15
16 class InstallWizard(QDialog):
17
18     def __init__(self, config, network, storage):
19         QDialog.__init__(self)
20         self.config = config
21         self.network = network
22         self.storage = storage
23         self.setMinimumSize(575, 400)
24         self.setWindowTitle('Electrum')
25         self.connect(self, QtCore.SIGNAL('accept'), self.accept)
26
27         self.stack = QStackedLayout()
28         self.setLayout(self.stack)
29
30
31     def set_layout(self, layout):
32         w = QWidget()
33         w.setLayout(layout)
34         self.stack.setCurrentIndex(self.stack.addWidget(w))
35
36
37     def restore_or_create(self):
38
39         grid = QGridLayout()
40         grid.setSpacing(5)
41
42         msg = _("Electrum could not find an existing wallet.") + "\n\n" \
43             + _("What do you want to do?") + "\n"
44         label = QLabel(msg)
45         label.setWordWrap(True)
46         grid.addWidget(label, 0, 0)
47
48         gb = QGroupBox()
49
50         b1 = QRadioButton(gb)
51         b1.setText(_("Create new wallet"))
52         b1.setChecked(True)
53
54         b2 = QRadioButton(gb)
55         b2.setText(_("Restore an existing wallet from its seed"))
56
57         b3 = QRadioButton(gb)
58         b3.setText(_("Create a watching-only version of an existing wallet"))
59
60         grid.addWidget(b1,1,0)
61         grid.addWidget(b2,2,0)
62         grid.addWidget(b3,3,0)
63
64         vbox = QVBoxLayout()
65         self.set_layout(vbox)
66
67         vbox.addLayout(grid)
68         vbox.addStretch(1)
69         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
70
71         if not self.exec_():
72             return
73         
74         if b1.isChecked():
75             answer = 'create'
76         elif b2.isChecked():
77             answer = 'restore'
78         else:
79             answer = 'watching'
80
81         return answer
82
83
84     def verify_seed(self, wallet):
85         r = self.seed_dialog(False)
86         if not r:
87             return
88
89         if r != wallet.get_mnemonic(None):
90             QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
91             return False
92         else:
93             return True
94
95
96     def seed_dialog(self, is_restore=True):
97
98         vbox = QVBoxLayout()
99         if is_restore:
100             msg = _("Please enter your wallet seed.") + "\n"
101         else:
102             msg = _("Your seed is important!") \
103                 + "\n" + _("To make sure that you have properly saved your seed, please retype it here.")
104         
105         logo = QLabel()
106         logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
107         logo.setMaximumWidth(60)
108
109         label = QLabel(msg)
110         label.setWordWrap(True)
111
112         seed_e = QTextEdit()
113         seed_e.setMaximumHeight(100)
114
115         vbox.addWidget(label)
116
117         grid = QGridLayout()
118         grid.addWidget(logo, 0, 0)
119         grid.addWidget(seed_e, 0, 1)
120
121         vbox.addLayout(grid)
122
123         vbox.addStretch(1)
124         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
125
126         self.set_layout(vbox)
127         if not self.exec_():
128             return
129
130         seed = seed_e.toPlainText()
131         seed = unicode(seed.toLower())
132
133         if not seed:
134             QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
135             return
136
137         if not Wallet.is_seed(seed):
138             QMessageBox.warning(None, _('Error'), _('Invalid seed'), _('OK'))
139             return
140
141         return seed
142
143
144
145     def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
146         def target():
147             task()
148             self.emit(QtCore.SIGNAL('accept'))
149
150         vbox = QVBoxLayout()
151         self.waiting_label = QLabel(msg)
152         vbox.addWidget(self.waiting_label)
153         self.set_layout(vbox)
154         t = threading.Thread(target = target)
155         t.start()
156         self.exec_()
157
158
159
160     def mpk_dialog(self):
161
162         vbox = QVBoxLayout()
163         vbox.addWidget(QLabel(_("Please enter your master public key.")))
164
165         grid = QGridLayout()
166         grid.setSpacing(8)
167
168         label = QLabel(_("Key")) 
169         grid.addWidget(label, 0, 0)
170         mpk_e = QTextEdit()
171         mpk_e.setMaximumHeight(100)
172         grid.addWidget(mpk_e, 0, 1)
173
174         vbox.addLayout(grid)
175
176         vbox.addStretch(1)
177         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
178
179         self.set_layout(vbox)
180         if not self.exec_(): 
181             return None
182
183         mpk = str(mpk_e.toPlainText()).strip()
184         return mpk
185
186
187     def network_dialog(self):
188         
189         grid = QGridLayout()
190         grid.setSpacing(5)
191
192         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" \
193                       + _("How do you want to connect to a server:")+" ")
194         label.setWordWrap(True)
195         grid.addWidget(label, 0, 0)
196
197         gb = QGroupBox()
198
199         b1 = QRadioButton(gb)
200         b1.setText(_("Auto connect"))
201         b1.setChecked(True)
202
203         b2 = QRadioButton(gb)
204         b2.setText(_("Select server manually"))
205
206         #b3 = QRadioButton(gb)
207         #b3.setText(_("Stay offline"))
208
209         grid.addWidget(b1,1,0)
210         grid.addWidget(b2,2,0)
211         #grid.addWidget(b3,3,0)
212
213         vbox = QVBoxLayout()
214         vbox.addLayout(grid)
215
216         vbox.addStretch(1)
217         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
218
219         self.set_layout(vbox)
220         if not self.exec_():
221             return
222         
223         if b2.isChecked():
224             return NetworkDialog(self.network, self.config, None).do_exec()
225
226         elif b1.isChecked():
227             self.config.set_key('auto_cycle', True, True)
228             return
229
230         else:
231             self.config.set_key("server", None, True)
232             self.config.set_key('auto_cycle', False, True)
233             return
234         
235
236
237     def show_seed(self, wallet):
238         from seed_dialog import make_seed_dialog
239         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
240         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
241         self.set_layout(vbox)
242         return self.exec_()
243
244
245     def password_dialog(self, wallet):
246         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
247               +_("Leave these fields empty if you want to disable encryption.")
248         from password_dialog import make_password_dialog, run_password_dialog
249         self.set_layout( make_password_dialog(self, wallet, msg) )
250         return run_password_dialog(self, wallet, self)
251
252
253     def run(self):
254
255         action = self.restore_or_create()
256         if not action: 
257             return
258
259         #gap = self.config.get('gap_limit', 5)
260         #if gap != 5:
261         #    wallet.gap_limit = gap
262         #    wallet.storage.put('gap_limit', gap, True)
263
264         if action == 'create':
265             wallet = Wallet(self.storage)
266             wallet.init_seed(None)
267             if not self.show_seed(wallet):
268                 return
269             if not self.verify_seed(wallet):
270                 return
271             ok, old_password, password = self.password_dialog(wallet)
272             def create():
273                 wallet.save_seed(password)
274                 wallet.synchronize()  # generate first addresses offline
275             self.waiting_dialog(create)
276
277         elif action == 'restore':
278             seed = self.seed_dialog()
279             if not Wallet.is_seed(seed):
280                 return
281             wallet = Wallet.from_seed(seed, self.storage)
282             ok, old_password, password = self.password_dialog(wallet)
283             wallet.save_seed(password)
284
285         elif action == 'watching':
286             mpk = self.mpk_dialog()
287             if not mpk:
288                 return
289             wallet = Wallet.from_mpk(mpk, self.storage)
290
291         else: raise
292                 
293         #if not self.config.get('server'):
294         if self.network:
295             if self.network.interfaces:
296                 self.network_dialog()
297             else:
298                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
299                 self.network.stop()
300                 self.network = None
301
302         # start wallet threads
303         wallet.start_threads(self.network)
304
305         if action == 'restore':
306
307             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
308
309             if self.network:
310                 if wallet.is_found():
311                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
312                 else:
313                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
314             else:
315                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
316
317         return wallet