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