restore from master public key (qt and command line)
[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         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         vbox.addLayout(grid)
171
172         vbox.addStretch(1)
173         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
174
175         self.set_layout(vbox)
176         if not self.exec_(): 
177             return None
178
179         mpk = str(mpk_e.toPlainText()).strip()
180         return mpk
181
182
183     def network_dialog(self):
184         
185         grid = QGridLayout()
186         grid.setSpacing(5)
187
188         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" \
189                       + _("How do you want to connect to a server:")+" ")
190         label.setWordWrap(True)
191         grid.addWidget(label, 0, 0)
192
193         gb = QGroupBox()
194
195         b1 = QRadioButton(gb)
196         b1.setText(_("Auto connect"))
197         b1.setChecked(True)
198
199         b2 = QRadioButton(gb)
200         b2.setText(_("Select server manually"))
201
202         #b3 = QRadioButton(gb)
203         #b3.setText(_("Stay offline"))
204
205         grid.addWidget(b1,1,0)
206         grid.addWidget(b2,2,0)
207         #grid.addWidget(b3,3,0)
208
209         vbox = QVBoxLayout()
210         vbox.addLayout(grid)
211
212         vbox.addStretch(1)
213         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
214
215         self.set_layout(vbox)
216         if not self.exec_():
217             return
218         
219         if b2.isChecked():
220             return NetworkDialog(self.network, self.config, None).do_exec()
221
222         elif b1.isChecked():
223             self.config.set_key('auto_cycle', True, True)
224             return
225
226         else:
227             self.config.set_key("server", None, True)
228             self.config.set_key('auto_cycle', False, True)
229             return
230         
231
232
233     def show_seed(self, wallet):
234         from seed_dialog import make_seed_dialog
235         vbox = make_seed_dialog(wallet.get_mnemonic(None), wallet.imported_keys)
236         vbox.addLayout(ok_cancel_buttons(self, _("Next")))
237         self.set_layout(vbox)
238         return self.exec_()
239
240
241     def password_dialog(self, wallet):
242         msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
243               +_("Leave these fields empty if you want to disable encryption.")
244         from password_dialog import make_password_dialog, run_password_dialog
245         self.set_layout( make_password_dialog(self, wallet, msg) )
246         return run_password_dialog(self, wallet, self)
247
248
249     def run(self):
250
251         action = self.restore_or_create()
252         if not action: 
253             return
254
255         #gap = self.config.get('gap_limit', 5)
256         #if gap != 5:
257         #    wallet.gap_limit = gap
258         #    wallet.storage.put('gap_limit', gap, True)
259
260         if action == 'create':
261             wallet = Wallet(self.storage)
262             wallet.init_seed(None)
263             if not self.show_seed(wallet):
264                 return
265             if not self.verify_seed(wallet):
266                 return
267             ok, old_password, password = self.password_dialog(wallet)
268             def create():
269                 wallet.save_seed(password)
270                 wallet.synchronize()  # generate first addresses offline
271             self.waiting_dialog(create)
272
273         elif action == 'restore':
274             seed = self.seed_dialog()
275             if not seed:
276                 return
277             wallet = Wallet.from_seed(seed, self.storage)
278             ok, old_password, password = self.password_dialog(wallet)
279             wallet.save_seed(password)
280
281         elif action == 'watching':
282             mpk = self.mpk_dialog()
283             if not mpk:
284                 return
285             wallet = Wallet.from_mpk(mpk, self.storage)
286
287         else: raise
288                 
289         #if not self.config.get('server'):
290         if self.network:
291             if self.network.interfaces:
292                 self.network_dialog()
293             else:
294                 QMessageBox.information(None, _('Warning'), _('You are offline'), _('OK'))
295                 self.network.stop()
296                 self.network = None
297
298         # start wallet threads
299         wallet.start_threads(self.network)
300
301         if action == 'restore':
302
303             self.waiting_dialog(lambda: wallet.restore(self.waiting_label.setText))
304
305             if self.network:
306                 if wallet.is_found():
307                     QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
308                 else:
309                     QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
310             else:
311                 QMessageBox.information(None, _('Information'), _("This wallet was restored offline. It may contain more addresses than displayed."), _('OK'))
312
313         return wallet