~ubuntu-branches/debian/jessie/armory/jessie

« back to all changes in this revision

Viewing changes to ui/Wizards.py

  • Committer: Package Import Robot
  • Author(s): Joseph Bisch
  • Date: 2014-10-07 10:22:45 UTC
  • Revision ID: package-import@ubuntu.com-20141007102245-2s3x3rhjxg689hek
Tags: upstream-0.92.3
ImportĀ upstreamĀ versionĀ 0.92.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
################################################################################
 
2
#                                                                              #
 
3
# Copyright (C) 2011-2014, Armory Technologies, Inc.                           #
 
4
# Distributed under the GNU Affero General Public License (AGPL v3)            #
 
5
# See LICENSE or http://www.gnu.org/licenses/agpl.html                         #
 
6
#                                                                              #
 
7
################################################################################
 
8
 
 
9
from PyQt4.Qt import * #@UnusedWildImport
 
10
from PyQt4.QtGui import * #@UnusedWildImport
 
11
from armoryengine.ArmoryUtils import USE_TESTNET
 
12
from ui.WalletFrames import NewWalletFrame, SetPassphraseFrame, VerifyPassphraseFrame,\
 
13
   WalletBackupFrame, WizardCreateWatchingOnlyWalletFrame
 
14
from ui.TxFrames import SendBitcoinsFrame, SignBroadcastOfflineTxFrame,\
 
15
   ReviewOfflineTxFrame
 
16
from qtdefines import USERMODE, GETFONT, tr, AddToRunningDialogsList
 
17
from armoryengine.PyBtcWallet import PyBtcWallet
 
18
from CppBlockUtils import SecureBinaryData
 
19
from armoryengine.BDM import TheBDM
 
20
from qtdialogs import DlgProgress
 
21
 
 
22
# This class is intended to be an abstract Wizard class that
 
23
# will hold all of the functionality that is common to all 
 
24
# Wizards in Armory. 
 
25
class ArmoryWizard(QWizard):
 
26
   def __init__(self, parent, main):
 
27
      super(QWizard, self).__init__(parent)
 
28
      self.setWizardStyle(QWizard.ClassicStyle)
 
29
      self.parent = parent
 
30
      self.main   = main
 
31
      self.setFont(GETFONT('var'))
 
32
      self.setWindowFlags(Qt.Window)
 
33
      # Need to adjust the wizard frame size whenever the page changes.
 
34
      self.connect(self, SIGNAL('currentIdChanged(int)'), self.fitContents)
 
35
      if USE_TESTNET:
 
36
         self.setWindowTitle('Armory - Bitcoin Wallet Management [TESTNET]')
 
37
         self.setWindowIcon(QIcon(':/armory_icon_green_32x32.png'))
 
38
      else:
 
39
         self.setWindowTitle('Armory - Bitcoin Wallet Management')
 
40
         self.setWindowIcon(QIcon(':/armory_icon_32x32.png'))
 
41
   
 
42
   def fitContents(self):
 
43
      self.adjustSize()
 
44
   
 
45
   @AddToRunningDialogsList
 
46
   def exec_(self):
 
47
      return super(ArmoryWizard, self).exec_()
 
48
 
 
49
# This class is intended to be an abstract Wizard Page class that
 
50
# will hold all of the functionality that is common to all 
 
51
# Wizard pages in Armory. 
 
52
# The layout is QVBoxLayout and holds a single QFrame (self.pageFrame)
 
53
class ArmoryWizardPage(QWizardPage):
 
54
   def __init__(self, wizard, pageFrame):
 
55
      super(ArmoryWizardPage, self).__init__(wizard)
 
56
      self.pageFrame = pageFrame
 
57
      self.pageLayout = QVBoxLayout()
 
58
      self.pageLayout.addWidget(self.pageFrame)
 
59
      self.setLayout(self.pageLayout)
 
60
   
 
61
   # override this method to implement validators
 
62
   def validatePage(self):
 
63
      return True
 
64
 
 
65
################################ Wallet Wizard ################################
 
66
# Wallet Wizard has these pages:
 
67
#     1. Create Wallet
 
68
#     2. Set Passphrase
 
69
#     3. Verify Passphrase
 
70
#     4. Create Paper Backup
 
71
#     5. Create Watcing Only Wallet
 
72
class WalletWizard(ArmoryWizard):
 
73
   def __init__(self, parent, main):
 
74
      super(WalletWizard,self).__init__(parent, main)
 
75
      self.newWallet = None
 
76
      self.isBackupCreated = False
 
77
      self.setWindowTitle(tr("Wallet Creation Wizard"))
 
78
      self.setOption(QWizard.HaveFinishButtonOnEarlyPages, on=True)
 
79
      self.setOption(QWizard.IgnoreSubTitles, on=True)
 
80
      
 
81
      # Page 1: Create Wallet
 
82
      self.walletCreationPage = WalletCreationPage(self)
 
83
      self.addPage(self.walletCreationPage)
 
84
      
 
85
      # Page 2: Set Passphrase
 
86
      self.setPassphrasePage = SetPassphrasePage(self)
 
87
      self.addPage(self.setPassphrasePage)
 
88
      
 
89
      # Page 3: Verify Passphrase
 
90
      self.verifyPassphrasePage = VerifyPassphrasePage(self)
 
91
      self.addPage(self.verifyPassphrasePage)
 
92
      
 
93
      # Page 4: Create Paper Backup
 
94
      self.walletBackupPage = WalletBackupPage(self)
 
95
      self.addPage(self.walletBackupPage)
 
96
      
 
97
      # Page 5: Create Watching Only Wallet -- but only if expert, or offline
 
98
      self.hasCWOWPage = False
 
99
      if self.main.usermode==USERMODE.Expert or not self.main.internetAvail:
 
100
         self.hasCWOWPage = True
 
101
         self.createWOWPage = CreateWatchingOnlyWalletPage(self)
 
102
         self.addPage(self.createWOWPage)
 
103
 
 
104
      self.setButtonLayout([QWizard.BackButton,
 
105
                            QWizard.Stretch,
 
106
                            QWizard.NextButton,
 
107
                            QWizard.FinishButton])
 
108
 
 
109
   def initializePage(self, *args, **kwargs):
 
110
 
 
111
      if self.currentPage() == self.verifyPassphrasePage:
 
112
         self.verifyPassphrasePage.setPassphrase(
 
113
               self.setPassphrasePage.pageFrame.getPassphrase())
 
114
      elif self.hasCWOWPage and self.currentPage() == self.createWOWPage:
 
115
         self.createWOWPage.pageFrame.setWallet(self.newWallet)
 
116
         
 
117
      if self.currentPage() == self.walletBackupPage:
 
118
         self.createNewWalletFromWizard()
 
119
         self.walletBackupPage.pageFrame.setPassphrase(
 
120
                  self.setPassphrasePage.pageFrame.getPassphrase())         
 
121
         self.walletBackupPage.pageFrame.setWallet(self.newWallet)
 
122
         
 
123
         # Hide the back button on wallet backup page  
 
124
         self.setButtonLayout([QWizard.Stretch,
 
125
                                QWizard.NextButton,
 
126
                                QWizard.FinishButton])
 
127
      elif self.currentPage() == self.walletCreationPage:
 
128
         # Hide the back button on the first page  
 
129
         self.setButtonLayout([QWizard.Stretch,
 
130
                                QWizard.NextButton,
 
131
                                QWizard.FinishButton])
 
132
      else:
 
133
         self.setButtonLayout([QWizard.BackButton,
 
134
                                QWizard.Stretch,
 
135
                                QWizard.NextButton,
 
136
                                QWizard.FinishButton])
 
137
   def done(self, event):
 
138
      if self.newWallet and not self.walletBackupPage.pageFrame.isBackupCreated:
 
139
         reply = QMessageBox.question(self, tr('Wallet Backup Warning'), tr("""
 
140
               You have not made a backup for your new wallet.  You only have 
 
141
               to make a backup of your wallet <u>one time</u> to protect 
 
142
               all the funds held by this wallet <i>any time in the future</i>
 
143
               (it is a backup of the signing keys, not the coins themselves).
 
144
               <br><br>
 
145
               If you do not make a backup, you will <u>permanently</u> lose
 
146
               the money in this wallet if you ever forget your password, or 
 
147
               suffer from hardware failure.
 
148
               <br><br>
 
149
               Are you sure that you want to leave this wizard without backing 
 
150
               up your wallet?"""), \
 
151
               QMessageBox.Yes | QMessageBox.No)
 
152
         if reply == QMessageBox.No:
 
153
            # Stay in the wizard
 
154
            return None
 
155
      return super(WalletWizard, self).done(event)
 
156
             
 
157
   def createNewWalletFromWizard(self):
 
158
      self.newWallet = PyBtcWallet().createNewWallet( \
 
159
                     securePassphrase=self.setPassphrasePage.pageFrame.getPassphrase(), \
 
160
                     kdfTargSec=self.walletCreationPage.pageFrame.getKdfSec(), \
 
161
                     kdfMaxMem=self.walletCreationPage.pageFrame.getKdfBytes(), \
 
162
                     shortLabel=self.walletCreationPage.pageFrame.getName(), \
 
163
                     longLabel=self.walletCreationPage.pageFrame.getDescription(), \
 
164
                     doRegisterWithBDM=False, \
 
165
                     extraEntropy=self.main.getExtraEntropyForKeyGen())
 
166
 
 
167
      self.newWallet.unlock(securePassphrase=
 
168
               SecureBinaryData(self.setPassphrasePage.pageFrame.getPassphrase()))
 
169
      # We always want to fill the address pool, right away.  
 
170
      fillPoolProgress = DlgProgress(self, self.main, HBar=1, \
 
171
                                     Title="Creating Wallet") 
 
172
      fillPoolProgress.exec_(self.newWallet.fillAddressPool, doRegister=False,
 
173
                             Progress=fillPoolProgress.UpdateHBar)
 
174
 
 
175
      # Reopening from file helps make sure everything is correct -- don't
 
176
      # let the user use a wallet that triggers errors on reading it
 
177
      wltpath = self.newWallet.walletPath
 
178
      walletFromDisk = PyBtcWallet().readWalletFile(wltpath)
 
179
      self.main.addWalletToApplication(walletFromDisk, walletIsNew=True)
 
180
      if TheBDM.getBDMState() in ('Uninitialized', 'Offline'):
 
181
         TheBDM.registerWallet(walletFromDisk, isFresh=True, wait=False)
 
182
      else:
 
183
         self.main.newWalletList.append([walletFromDisk, True])
 
184
   
 
185
   def cleanupPage(self, *args, **kwargs):
 
186
      if self.hasCWOWPage and self.currentPage() == self.createWOWPage:
 
187
         self.setButtonLayout([QWizard.Stretch,
 
188
                               QWizard.NextButton,
 
189
                               QWizard.FinishButton])
 
190
      # If we are backing up from setPassphrasePage must be going
 
191
      # to the first page.
 
192
      elif self.currentPage() == self.setPassphrasePage:
 
193
         # Hide the back button on the first page
 
194
         self.setButtonLayout([QWizard.Stretch,
 
195
                                QWizard.NextButton,
 
196
                                QWizard.FinishButton])
 
197
      else:
 
198
         self.setButtonLayout([QWizard.BackButton,
 
199
                               QWizard.Stretch,
 
200
                               QWizard.NextButton,
 
201
                               QWizard.FinishButton])
 
202
          
 
203
class WalletCreationPage(ArmoryWizardPage):
 
204
   def __init__(self, wizard):
 
205
      super(WalletCreationPage, self).__init__(wizard,
 
206
            NewWalletFrame(wizard, wizard.main, "Primary Wallet"))
 
207
      self.setTitle(tr("Step 1: Create Wallet"))
 
208
      self.setSubTitle(tr("""
 
209
            Create a new wallet for managing your funds.
 
210
            The name and description can be changed at any time."""))
 
211
      
 
212
   # override this method to implement validators
 
213
   def validatePage(self):
 
214
      result = True
 
215
      if self.pageFrame.getKdfSec() == -1:
 
216
         QMessageBox.critical(self, 'Invalid Target Compute Time', \
 
217
            'You entered Target Compute Time incorrectly.\n\nEnter: <Number> (ms, s)', QMessageBox.Ok)
 
218
         result = False
 
219
      elif self.pageFrame.getKdfBytes() == -1:
 
220
         QMessageBox.critical(self, 'Invalid Max Memory Usage', \
 
221
            'You entered Max Memory Usag incorrectly.\n\nnter: <Number> (kb, mb)', QMessageBox.Ok)
 
222
         result = False
 
223
      return result
 
224
 
 
225
class SetPassphrasePage(ArmoryWizardPage):
 
226
   def __init__(self, wizard):
 
227
      super(SetPassphrasePage, self).__init__(wizard, 
 
228
               SetPassphraseFrame(wizard, wizard.main, "Set Passphrase", self.updateNextButton))
 
229
      self.setTitle(tr("Step 2: Set Passphrase"))
 
230
      self.updateNextButton()
 
231
 
 
232
   def updateNextButton(self):
 
233
      self.emit(SIGNAL("completeChanged()"))
 
234
   
 
235
   def isComplete(self):
 
236
      return self.pageFrame.checkPassphrase(False)
 
237
   
 
238
class VerifyPassphrasePage(ArmoryWizardPage):
 
239
   def __init__(self, wizard):
 
240
      super(VerifyPassphrasePage, self).__init__(wizard, 
 
241
            VerifyPassphraseFrame(wizard, wizard.main, "Verify Passphrase"))
 
242
      self.passphrase = None
 
243
      self.setTitle(tr("Step 3: Verify Passphrase"))
 
244
   
 
245
   def setPassphrase(self, passphrase):
 
246
      self.passphrase = passphrase        
 
247
   
 
248
   def validatePage(self):
 
249
      result = self.passphrase == str(self.pageFrame.edtPasswd3.text())
 
250
      if not result:
 
251
         QMessageBox.critical(self, 'Invalid Passphrase', \
 
252
            'You entered your confirmation passphrase incorrectly!', QMessageBox.Ok)
 
253
      return result
 
254
      
 
255
class WalletBackupPage(ArmoryWizardPage):
 
256
   def __init__(self, wizard):
 
257
      super(WalletBackupPage, self).__init__(wizard,
 
258
                                WalletBackupFrame(wizard, wizard.main, "Backup Wallet"))
 
259
      self.myWizard = wizard
 
260
      self.setTitle(tr("Step 4: Backup Wallet"))
 
261
      self.setFinalPage(True)
 
262
 
 
263
class CreateWatchingOnlyWalletPage(ArmoryWizardPage):
 
264
   def __init__(self, wizard):
 
265
      super(CreateWatchingOnlyWalletPage, self).__init__(wizard,
 
266
                  WizardCreateWatchingOnlyWalletFrame(wizard, wizard.main, "Create Watching Only Wallet"))
 
267
      self.setTitle(tr("Step 5: Create Watching Only Wallet"))
 
268
      
 
269
############################### Offline TX Wizard ##############################
 
270
# Offline TX Wizard has these pages:
 
271
#     1. Create Transaction
 
272
#     2. Sign Transaction on Offline Computer
 
273
#     3. Broadcast Transaction
 
274
class TxWizard(ArmoryWizard):
 
275
   def __init__(self, parent, main, wlt, prefill=None, onlyOfflineWallets=False):
 
276
      super(TxWizard,self).__init__(parent, main)
 
277
      self.setWindowTitle(tr("Offline Transaction Wizard"))
 
278
      self.setOption(QWizard.IgnoreSubTitles, on=True)
 
279
      self.setOption(QWizard.HaveCustomButton1, on=True)
 
280
      self.setOption(QWizard.HaveFinishButtonOnEarlyPages, on=True)
 
281
      
 
282
      # Page 1: Create Offline TX
 
283
      self.createTxPage = CreateTxPage(self, wlt, prefill, onlyOfflineWallets=onlyOfflineWallets)
 
284
      self.addPage(self.createTxPage)
 
285
      
 
286
      # Page 2: Sign Offline TX
 
287
      self.reviewOfflineTxPage = ReviewOfflineTxPage(self)
 
288
      self.addPage(self.reviewOfflineTxPage)
 
289
      
 
290
      # Page 3: Broadcast Offline TX
 
291
      self.signBroadcastOfflineTxPage = SignBroadcastOfflineTxPage(self)
 
292
      self.addPage(self.signBroadcastOfflineTxPage)
 
293
 
 
294
      self.setButtonText(QWizard.NextButton, tr('Create Unsigned Transaction'))
 
295
      self.setButtonText(QWizard.CustomButton1, tr('Send!'))
 
296
      self.connect(self, SIGNAL('customButtonClicked(int)'), self.sendClicked)
 
297
      self.setButtonLayout([QWizard.CancelButton,
 
298
                            QWizard.BackButton,
 
299
                            QWizard.Stretch,
 
300
                            QWizard.NextButton,
 
301
                            QWizard.CustomButton1])
 
302
 
 
303
      
 
304
 
 
305
   def initializePage(self, *args, **kwargs):
 
306
      if self.currentPage() == self.createTxPage:
 
307
         self.createTxPage.pageFrame.fireWalletChange()
 
308
      elif self.currentPage() == self.reviewOfflineTxPage:
 
309
         self.setButtonText(QWizard.NextButton, tr('Next'))
 
310
         self.setButtonLayout([QWizard.BackButton,
 
311
                            QWizard.Stretch,
 
312
                            QWizard.NextButton,
 
313
                            QWizard.FinishButton])
 
314
         self.reviewOfflineTxPage.pageFrame.setTxDp(self.createTxPage.txdp)
 
315
         self.reviewOfflineTxPage.pageFrame.setWallet(
 
316
                  self.createTxPage.pageFrame.wlt)
 
317
         
 
318
   def cleanupPage(self, *args, **kwargs):
 
319
      if self.currentPage() == self.reviewOfflineTxPage:
 
320
         self.updateOnSelectWallet(self.createTxPage.pageFrame.wlt)
 
321
         self.setButtonText(QWizard.NextButton, tr('Create Unsigned Transaction'))
 
322
 
 
323
   def sendClicked(self, customButtonIndex):
 
324
      self.createTxPage.pageFrame.createTxAndBroadcast()
 
325
      self.accept()
 
326
      
 
327
   def updateOnSelectWallet(self, wlt):
 
328
      if wlt.watchingOnly:
 
329
         self.setButtonLayout([QWizard.CancelButton,
 
330
                            QWizard.BackButton,
 
331
                            QWizard.Stretch,
 
332
                            QWizard.NextButton])
 
333
      else:
 
334
         self.setButtonLayout([QWizard.CancelButton,
 
335
                            QWizard.BackButton,
 
336
                            QWizard.Stretch,
 
337
                            QWizard.NextButton,
 
338
                            QWizard.CustomButton1])
 
339
         
 
340
class CreateTxPage(ArmoryWizardPage):
 
341
   def __init__(self, wizard, wlt, prefill=None, onlyOfflineWallets=False):
 
342
      super(CreateTxPage, self).__init__(wizard,
 
343
               SendBitcoinsFrame(wizard, wizard.main,
 
344
                                 "Create Transaction", wlt, prefill,
 
345
                                 selectWltCallback=self.updateOnSelectWallet,
 
346
                                 onlyOfflineWallets=onlyOfflineWallets))
 
347
      self.setTitle(tr("Step 1: Create Transaction"))
 
348
      self.txdp = None
 
349
      
 
350
   def validatePage(self):
 
351
      result = self.pageFrame.validateInputsGetTxDP()
 
352
      # the validator also computes the transaction and returns it or False if not valid
 
353
      if result:
 
354
         self.txdp = result
 
355
         result = True
 
356
      return result
 
357
   
 
358
   def updateOnSelectWallet(self, wlt):
 
359
      self.wizard().updateOnSelectWallet(wlt)
 
360
      
 
361
class ReviewOfflineTxPage(ArmoryWizardPage):
 
362
   def __init__(self, wizard):
 
363
      super(ReviewOfflineTxPage, self).__init__(wizard,
 
364
                  ReviewOfflineTxFrame(wizard, wizard.main, "Review Offline Transaction"))
 
365
      self.setTitle(tr("Step 2: Review Offline Transaction"))
 
366
      self.setFinalPage(True)
 
367
      
 
368
class SignBroadcastOfflineTxPage(ArmoryWizardPage):
 
369
   def __init__(self, wizard):
 
370
      super(SignBroadcastOfflineTxPage, self).__init__(wizard,
 
371
                  SignBroadcastOfflineTxFrame(wizard, wizard.main, "Sign/Broadcast Offline Transaction"))
 
372
      self.setTitle(tr("Step 3: Sign/Broadcast Offline Transaction"))