90d4f06168f8a7584176fee34f83a24a36ab37b5
[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, mnemonic
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         return seed
138
139
140
141     def waiting_dialog(self, task, msg= _("Electrum is generating your addresses, please wait.")):
142         def target():
143             task()
144             self.emit(QtCore.SIGNAL('accept'))
145
146         vbox = QVBoxLayout()
147         self.waiting_label = QLabel(msg)
148         vbox.addWidget(self.waiting_label)
149         self.set_layout(vbox)
150         t = threading.Thread(target = target)
151         t.start()
152         self.exec_()
153
154
155
156     def mpk_dialog(self):
157
158         vbox = QVBoxLayout()
159         vbox.addWidget(QLabel(_("Please enter your master public key.")))
160
161         grid = QGridLayout()
162         grid.setSpacing(8)
163
164         label = QLabel(_("Key")) 
165         grid.addWidget(label, 0, 0)
166         mpk_e = QTextEdit()
167         mpk_e.setMaximumHeight(100)
168         grid.addWidget(mpk_e, 0, 1)
169
170         label = QLabel(_("Chain")) 
171         #grid.addWidget(label, 1, 0)
172         chain_e = QTextEdit()
173         chain_e.setMaximumHeight(100)
174         #grid.addWidget(chain_e, 1, 1)
175
176         vbox.addLayout(grid)
177
178         vbox.addStretch(1)
179         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
180
181         self.set_layout(vbox)
182         if not self.exec_(): 
183             return None
184
185         mpk = str(mpk_e.toPlainText()).strip()
186         chain = str(chain_e.toPlainText()).strip()
187         return mpk, chain
188
189
190     def network_dialog(self):
191         
192         grid = QGridLayout()
193         grid.setSpacing(5)
194
195         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" \
196                       + _("How do you want to connect to a server:")+" ")
197         label.setWordWrap(True)
198         grid.addWidget(label, 0, 0)
199
200         gb = QGroupBox()
201
202         b1 = QRadioButton(gb)
203         b1.setText(_("Auto connect"))
204         b1.setChecked(True)
205
206         b2 = QRadioButton(gb)
207         b2.setText(_("Select server manually"))
208
209         #b3 = QRadioButton(gb)
210         #b3.setText(_("Stay offline"))
211
212         grid.addWidget(b1,1,0)
213         grid.addWidget(b2,2,0)
214         #grid.addWidget(b3,3,0)
215
216         vbox = QVBoxLayout()
217         vbox.addLayout(grid)
218
219         vbox.addStretch(1)
220         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
221
222         self.set_layout(vbox)
223         if not self.exec_():
224             return
225         
226         if b2.isChecked():
227             return NetworkDialog(self.network, self.config, None).do_exec()
228
229         elif b1.isChecked():
230             self.config.set_key('auto_cycle', True, True)
231             return
232
233         else:
234             self.config.set_key("server", None, True)
235             self.config.set_key('auto_cycle', False, True)
236             return
237         
238
239
240     def show_seed(self, wallet):
241         from seed_dialog import make_seed_dialog
242         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
243         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
244         self.set_layout(vbox)
245         return self.exec_()
246
247
248     def password_dialog(self, wallet):
249         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
250               +_("Leave these fields empty if you want to disable encryption.")
251         from password_dialog import make_password_dialog, run_password_dialog
252         self.set_layout( make_password_dialog(self, wallet, msg) )
253         return run_password_dialog(self, wallet, self)
254
255
256     def run(self):
257
258         action = self.restore_or_create()
259         if not action: 
260             return
261
262         wallet = Wallet(self.storage)
263         gap = self.config.get('gap_limit', 5)
264         if gap != 5:
265             wallet.gap_limit = gap
266             wallet.storage.put('gap_limit', gap, True)
267
268         if action == 'create':
269             wallet.init_seed(None)
270             if not self.show_seed(wallet):
271                 return
272             if not self.verify_seed(wallet):
273                 return
274             ok, old_password, password = self.password_dialog(wallet)
275             def create():
276                 wallet.save_seed(password)
277                 wallet.synchronize()  # generate first addresses offline
278             self.waiting_dialog(create)
279
280
281         elif action == 'restore':
282             seed = self.seed_dialog()
283             if not seed:
284                 return
285             try:
286                 wallet.init_seed(seed)
287             except Exception:
288                 import traceback
289                 traceback.print_exc(file=sys.stdout)
290                 QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
291                 return
292
293             ok, old_password, password = self.password_dialog(wallet)
294             wallet.save_seed(password)
295
296
297         elif action == 'watching':
298             mpk = self.mpk_dialog()
299             if not mpk:
300                 return
301             wallet.seed = ''
302             wallet.create_watching_only_wallet(mpk)
303
304         else: raise
305                 
306         #if not self.config.get('server'):
307         if self.network:
308             if self.network.interfaces:
309                 self.network_dialog()
310             else:
311                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
312                 self.network.stop()
313                 self.network = None
314
315         # start wallet threads
316         wallet.start_threads(self.network)
317
318         if action == 'restore':
319
320             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
321
322             if self.network:
323                 if wallet.is_found():
324                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
325                 else:
326                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
327             else:
328                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
329
330         return wallet