restore from master public key
[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
28     def restore_or_create(self):
29
30         grid = QGridLayout()
31         grid.setSpacing(5)
32
33         msg = _("Electrum could not find an existing wallet.")+"\n\n"+_("Did you use Electrum before and want to restore a previous wallet or is this your first time and do you want to create a new wallet?"+"\n")
34         label = QLabel(msg)
35         label.setWordWrap(True)
36         grid.addWidget(label, 0, 0)
37
38         gb = QGroupBox()
39
40         b1 = QRadioButton(gb)
41         b1.setText(_("Create new wallet"))
42         b1.setChecked(True)
43
44         b2 = QRadioButton(gb)
45         b2.setText(_("Restore wallet from seed"))
46
47         b3 = QRadioButton(gb)
48         b3.setText(_("Restore wallet from master public key"))
49
50         grid.addWidget(b1,1,0)
51         grid.addWidget(b2,2,0)
52         grid.addWidget(b3,3,0)
53
54         vbox = QVBoxLayout(self)
55         vbox.addLayout(grid)
56
57         vbox.addStretch(1)
58         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
59
60         if not self.exec_():
61             return
62         
63         if b1.isChecked():
64             answer = 'create'
65         elif b2.isChecked():
66             answer = 'restore'
67         else:
68             answer = 'watching'
69
70         return answer
71
72
73     def verify_seed(self, wallet):
74         r = self.seed_dialog(False)
75         if not r:
76             return
77
78         if r != wallet.seed:
79             QMessageBox.warning(None, _('Error'), _('Incorrect seed'), _('OK'))
80             return False
81         else:
82             return True
83
84
85     def seed_dialog(self, is_restore=True):
86
87         if self.layout(): QWidget().setLayout(self.layout())
88
89         vbox = QVBoxLayout(self)
90         if is_restore:
91             msg = _("Please enter your wallet seed.") + "\n"
92             msg += _("Your seed can be entered as a sequence of words, or as a hexadecimal string."+ ' \n')
93         else:
94             msg = _("Your seed is important!") \
95                   + "\n" + _("To make sure that you have properly saved your seed, please retype it here." + ' ')
96         
97         logo = QLabel()
98         logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
99         logo.setMaximumWidth(60)
100
101         label = QLabel(msg)
102         label.setWordWrap(True)
103
104         seed_e = QTextEdit()
105         seed_e.setMaximumHeight(100)
106
107         vbox.addWidget(label)
108
109         grid = QGridLayout()
110         grid.addWidget(logo, 0, 0)
111         grid.addWidget(seed_e, 0, 1)
112
113         vbox.addLayout(grid)
114
115
116         vbox.addStretch(1)
117         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
118
119         if not self.exec_():
120             return
121
122         try:
123             seed = str(seed_e.toPlainText())
124             seed.decode('hex')
125         except:
126             try:
127                 seed = mnemonic.mn_decode( seed.split() )
128             except:
129                 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
130                 return
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         if self.layout(): QWidget().setLayout(self.layout())
146         vbox = QVBoxLayout(self)
147         self.waiting_label = QLabel(msg)
148         vbox.addWidget(self.waiting_label)
149         t = threading.Thread(target = target)
150         t.start()
151         self.exec_()
152
153
154
155     def mpk_dialog(self):
156
157         if self.layout(): QWidget().setLayout(self.layout())
158
159         vbox = QVBoxLayout(self)
160
161         vbox.addWidget(QLabel(_("Please enter your master public key.")))
162
163         grid = QGridLayout()
164         grid.setSpacing(8)
165
166         label = QLabel(_("Key")) 
167         grid.addWidget(label, 0, 0)
168         mpk_e = QTextEdit()
169         mpk_e.setMaximumHeight(100)
170         grid.addWidget(mpk_e, 0, 1)
171
172         label = QLabel(_("Chain")) 
173         grid.addWidget(label, 1, 0)
174         chain_e = QTextEdit()
175         chain_e.setMaximumHeight(100)
176         grid.addWidget(chain_e, 1, 1)
177
178         vbox.addLayout(grid)
179
180         vbox.addStretch(1)
181         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
182
183         if not self.exec_(): return None, 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         if self.layout(): QWidget().setLayout(self.layout())
193
194         grid = QGridLayout()
195         grid.setSpacing(5)
196
197         label = QLabel(_("Electrum communicates with Electrum 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 or stay offline.") + "\n\n" \
198                       + _("How do you want to connect to a server: "))
199         label.setWordWrap(True)
200         grid.addWidget(label, 0, 0)
201
202         gb = QGroupBox()
203
204         b1 = QRadioButton(gb)
205         b1.setText(_("Auto connect"))
206         b1.setChecked(True)
207
208         b2 = QRadioButton(gb)
209         b2.setText(_("Select server manually"))
210
211         b3 = QRadioButton(gb)
212         b3.setText(_("Stay offline"))
213
214         grid.addWidget(b1,1,0)
215         grid.addWidget(b2,2,0)
216         grid.addWidget(b3,3,0)
217
218         vbox = QVBoxLayout(self)
219         vbox.addLayout(grid)
220
221         vbox.addStretch(1)
222         vbox.addLayout(ok_cancel_buttons(self, _('Next')))
223
224         if not self.exec_():
225             return
226         
227         if b2.isChecked():
228             return NetworkDialog(self.network, self.config, None).do_exec()
229
230         elif b1.isChecked():
231             self.config.set_key('auto_cycle', True, True)
232             return
233
234         else:
235             self.config.set_key("server", None, True)
236             self.config.set_key('auto_cycle', False, True)
237             return
238         
239         
240
241     def show_seed(self, wallet):
242         from seed_dialog import make_seed_dialog
243         if self.layout(): QWidget().setLayout(self.layout())
244         make_seed_dialog(self, wallet.seed, wallet.imported_keys)
245         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         if self.layout(): QWidget().setLayout(self.layout())
253         make_password_dialog(self, wallet, msg)
254         run_password_dialog(self, wallet, self)
255
256
257     def run(self):
258
259         action = self.restore_or_create()
260         if not action: exit()
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             self.show_seed(wallet)
271             if self.verify_seed(wallet):
272                 def create():
273                     wallet.save_seed()
274                     wallet.create_accounts()
275                     wallet.synchronize()  # generate first addresses offline
276                 self.waiting_dialog(create)
277             else:
278                 return
279                 
280         elif action == 'restore':
281             # ask for seed and gap.
282             seed = self.seed_dialog()
283             if not seed:
284                 return
285             wallet.init_seed(str(seed))
286             wallet.save_seed()
287
288         elif action == 'watching':
289             # ask for seed and gap.
290             K, chain = self.mpk_dialog()
291             if not K:
292                 return
293             wallet.seed = ''
294             wallet.create_watching_only_wallet(chain,K)
295
296
297         else: raise
298                 
299         #if not self.config.get('server'):
300         self.network_dialog()
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 wallet.is_found():
310                 QMessageBox.information(None, _('Information'), _("Recovery successful"), _('OK'))
311             else:
312                 QMessageBox.information(None, _('Information'), _("No transactions found for this seed"), _('OK'))
313             
314             wallet.fill_addressbook()
315
316         self.password_dialog(wallet)
317
318         return wallet