class QR_Window(QWidget): def __init__(self, win): QWidget.__init__(self) self.win = win self.setWindowTitle('Electrum - ' + _('Payment Request')) self.setMinimumSize(800, 250) self.address = '' self.label = '' self.amount = 0 self.setFocusPolicy(QtCore.Qt.NoFocus) main_box = QHBoxLayout() self.qrw = QRCodeWidget() main_box.addWidget(self.qrw, 1) vbox = QVBoxLayout() main_box.addLayout(vbox) self.address_label = QLabel("") #self.address_label.setFont(QFont(MONOSPACE_FONT)) vbox.addWidget(self.address_label) self.label_label = QLabel("") vbox.addWidget(self.label_label) self.amount_label = QLabel("") vbox.addWidget(self.amount_label) vbox.addStretch(1) self.setLayout(main_box) def set_content(self, address, amount, message, url): address_text = "<span style='font-size: 18pt'>%s</span>" % address if address else "" self.address_label.setText(address_text) if amount: amount = self.win.format_amount(amount) amount_text = "<span style='font-size: 21pt'>%s</span> <span style='font-size: 16pt'>%s</span> " % ( amount, self.win.base_unit()) else: amount_text = '' self.amount_label.setText(amount_text) label_text = "<span style='font-size: 21pt'>%s</span>" % message if message else "" self.label_label.setText(label_text) self.qrw.setData(url)
class QR_Window(QWidget): def __init__(self): super().__init__( ) # Top-level window. Parent needs to hold a reference to us and clean us up appropriately. self.setWindowTitle('Electron Cash - ' + _('Payment Request')) self.setMinimumSize(800, 250) self.label = '' self.amount = 0 self.setFocusPolicy(Qt.NoFocus) main_box = QHBoxLayout() self.qrw = QRCodeWidget() main_box.addWidget(self.qrw, 1) vbox = QVBoxLayout() main_box.addLayout(vbox) main_box.addStretch(1) self.address_label = WWLabel() self.address_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.address_label) self.msg_label = WWLabel() self.msg_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.msg_label) self.amount_label = WWLabel() self.amount_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.amount_label) vbox.addStretch(1) self.setLayout(main_box) def set_content(self, win, address_text, amount, message, url): self.address_label.setText(address_text) if amount: amount_text = '{} {}'.format(win.format_amount(amount), win.base_unit()) else: amount_text = '' self.amount_label.setText(amount_text) self.msg_label.setText(message) self.qrw.setData(url)
class QR_Window(QWidget, MessageBoxMixin): def __init__(self): super().__init__( ) # Top-level window. Parent needs to hold a reference to us and clean us up appropriately. self.setWindowTitle('Electron Cash - ' + _('Payment Request')) self.label = '' self.amount = 0 self.setFocusPolicy(Qt.NoFocus) self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) main_box = QHBoxLayout(self) main_box.setContentsMargins(12, 12, 12, 12) self.qrw = QRCodeWidget() self.qrw.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) main_box.addWidget(self.qrw, 2) vbox = QVBoxLayout() vbox.setContentsMargins(12, 12, 12, 12) main_box.addLayout(vbox, 2) main_box.addStretch(1) self.address_label = WWLabel() self.address_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.address_label) self.msg_label = WWLabel() self.msg_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.msg_label) self.amount_label = WWLabel() self.amount_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.amount_label) self.op_return_label = WWLabel() self.op_return_label.setTextInteractionFlags(Qt.TextSelectableByMouse) vbox.addWidget(self.op_return_label) vbox.addStretch(2) copyBut = QPushButton(_("Copy QR Image")) saveBut = QPushButton(_("Save QR Image")) vbox.addLayout(Buttons(copyBut, saveBut)) weakSelf = Weak.ref( self ) # Qt & Python GC hygeine: don't hold references to self in non-method slots as it appears Qt+Python GC don't like this too much and may leak memory in that case. weakQ = Weak.ref(self.qrw) weakBut = Weak.ref(copyBut) copyBut.clicked.connect(lambda: copy_to_clipboard(weakQ(), weakBut())) saveBut.clicked.connect(lambda: save_to_file(weakQ(), weakSelf())) def set_content(self, win, address_text, amount, message, url, *, op_return=None, op_return_raw=None): if op_return is not None and op_return_raw is not None: raise ValueError( 'Must specify exactly one of op_return or op_return_hex as kwargs to QR_Window.set_content' ) self.address_label.setText(address_text) if amount: amount_text = '{} {}'.format(win.format_amount(amount), win.base_unit()) else: amount_text = '' self.amount_label.setText(amount_text) self.msg_label.setText(message) self.qrw.setData(url) if op_return: self.op_return_label.setText(f'OP_RETURN: {str(op_return)}') elif op_return_raw: self.op_return_label.setText( f'OP_RETURN (raw): {str(op_return_raw)}') self.op_return_label.setVisible(bool(op_return or op_return_raw)) self.layout().activate() def closeEvent(self, e): # May have modal up when closed -- because wallet window may force-close # us when it is gets closed (See ElectrumWindow.clean_up in # main_window.py). # .. So kill the "QR Code Copied to clipboard" modal dialog that may # be up as it can cause a crash for this window to be closed with it # still up. for c in self.findChildren(QDialog): if c.isWindow() and c.isModal() and c.isVisible(): c.reject( ) # break out of local event loop for dialog as we are about to die and we will be invalidated. super().closeEvent(e)
class LedgerAuthDialog(QDialog): def __init__(self, handler, data): '''Ask user for 2nd factor authentication. Support text, security card and paired mobile methods. Use last method from settings, but support new pairing and downgrade. ''' QDialog.__init__(self, handler.top_level_window()) self.handler = handler self.txdata = data self.idxs = self.txdata[ 'keycardData'] if self.txdata['confirmationType'] > 1 else '' self.setMinimumWidth(600) self.setWindowTitle(_("Ledger Wallet Authentication")) self.cfg = copy.deepcopy(self.handler.win.wallet.get_keystore().cfg) self.dongle = self.handler.win.wallet.get_keystore().get_client( ).dongle self.ws = None self.pin = '' self.devmode = self.getDevice2FAMode() if self.devmode == 0x11 or self.txdata['confirmationType'] == 1: self.cfg['mode'] = 0 vbox = QVBoxLayout() self.setLayout(vbox) def on_change_mode(idx): if idx < 2 and self.ws: self.ws.stop() self.ws = None self.cfg[ 'mode'] = 0 if self.devmode == 0x11 else idx if idx > 0 else 1 if self.cfg['mode'] > 1 and self.cfg['pair'] and not self.ws: self.req_validation() if self.cfg['mode'] > 0: self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.update_dlg() def add_pairing(): self.do_pairing() def return_pin(): self.pin = self.pintxt.text( ) if self.txdata['confirmationType'] == 1 else self.cardtxt.text() if self.cfg['mode'] == 1: self.pin = ''.join(chr(int(str(i), 16)) for i in self.pin) self.accept() self.modebox = QWidget() modelayout = QHBoxLayout() self.modebox.setLayout(modelayout) modelayout.addWidget(QLabel(_("Method:"))) self.modes = QComboBox() modelayout.addWidget(self.modes, 2) self.addPair = QPushButton(_("Pair")) self.addPair.setMaximumWidth(60) modelayout.addWidget(self.addPair) modelayout.addStretch(1) self.modebox.setMaximumHeight(50) vbox.addWidget(self.modebox) self.populate_modes() self.modes.currentIndexChanged.connect(on_change_mode) self.addPair.clicked.connect(add_pairing) self.helpmsg = QTextEdit() self.helpmsg.setStyleSheet( "QTextEdit { background-color: lightgray; }") self.helpmsg.setReadOnly(True) vbox.addWidget(self.helpmsg) self.pinbox = QWidget() pinlayout = QHBoxLayout() self.pinbox.setLayout(pinlayout) self.pintxt = QLineEdit() self.pintxt.setEchoMode(2) self.pintxt.setMaxLength(4) self.pintxt.returnPressed.connect(return_pin) pinlayout.addWidget(QLabel(_("Enter PIN:"))) pinlayout.addWidget(self.pintxt) pinlayout.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) pinlayout.addStretch(1) self.pinbox.setVisible(self.cfg['mode'] == 0) vbox.addWidget(self.pinbox) self.cardbox = QWidget() card = QVBoxLayout() self.cardbox.setLayout(card) self.addrtext = QTextEdit() self.addrtext.setStyleSheet( "QTextEdit { color:blue; background-color:lightgray; padding:15px 10px; border:none; font-size:20pt; }" ) self.addrtext.setReadOnly(True) self.addrtext.setMaximumHeight(120) card.addWidget(self.addrtext) def pin_changed(s): if len(s) < len(self.idxs): i = self.idxs[len(s)] addr = self.txdata['address'] addr = addr[:i] + '<u><b>' + addr[i:i + 1] + '</u></b>' + addr[i + 1:] self.addrtext.setHtml(str(addr)) else: self.addrtext.setHtml(_("Press Enter")) pin_changed('') cardpin = QHBoxLayout() cardpin.addWidget(QLabel(_("Enter PIN:"))) self.cardtxt = QLineEdit() self.cardtxt.setEchoMode(2) self.cardtxt.setMaxLength(len(self.idxs)) self.cardtxt.textChanged.connect(pin_changed) self.cardtxt.returnPressed.connect(return_pin) cardpin.addWidget(self.cardtxt) cardpin.addWidget(QLabel(_("NOT DEVICE PIN - see above"))) cardpin.addStretch(1) card.addLayout(cardpin) self.cardbox.setVisible(self.cfg['mode'] == 1) vbox.addWidget(self.cardbox) self.pairbox = QWidget() pairlayout = QVBoxLayout() self.pairbox.setLayout(pairlayout) pairhelp = QTextEdit(helpTxt[5]) pairhelp.setStyleSheet("QTextEdit { background-color: lightgray; }") pairhelp.setReadOnly(True) pairlayout.addWidget(pairhelp, 1) self.pairqr = QRCodeWidget() pairlayout.addWidget(self.pairqr, 4) self.pairbox.setVisible(False) vbox.addWidget(self.pairbox) self.update_dlg() if self.cfg['mode'] > 1 and not self.ws: self.req_validation() def populate_modes(self): self.modes.blockSignals(True) self.modes.clear() self.modes.addItem( _("Summary Text PIN (requires dongle replugging)" ) if self.txdata['confirmationType'] == 1 else _("Summary Text PIN is Disabled")) if self.txdata['confirmationType'] > 1: self.modes.addItem(_("Security Card Challenge")) if not self.cfg['pair']: self.modes.addItem(_("Mobile - Not paired")) else: self.modes.addItem(_("Mobile - %s") % self.cfg['pair'][1]) self.modes.blockSignals(False) def update_dlg(self): self.modes.setCurrentIndex(self.cfg['mode']) self.modebox.setVisible(True) self.addPair.setText( _("Pair") if not self.cfg['pair'] else _("Re-Pair")) self.addPair.setVisible(self.txdata['confirmationType'] > 2) self.helpmsg.setText( helpTxt[self.cfg['mode'] if self.cfg['mode'] < 2 else 2 if self. cfg['pair'] else 4]) self.helpmsg.setMinimumHeight(180 if self.txdata['confirmationType'] == 1 else 100) self.pairbox.setVisible(False) self.helpmsg.setVisible(True) self.pinbox.setVisible(self.cfg['mode'] == 0) self.cardbox.setVisible(self.cfg['mode'] == 1) self.pintxt.setFocus( True) if self.cfg['mode'] == 0 else self.cardtxt.setFocus(True) self.setMaximumHeight(200) def do_pairing(self): rng = os.urandom(16) pairID = rng.encode('hex') + hashlib.sha256(rng).digest()[0].encode( 'hex') self.pairqr.setData(pairID) self.modebox.setVisible(False) self.helpmsg.setVisible(False) self.pinbox.setVisible(False) self.cardbox.setVisible(False) self.pairbox.setVisible(True) self.pairqr.setMinimumSize(300, 300) if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, pairID) self.ws.pairing_done.connect(self.pairing_done) self.ws.start() def pairing_done(self, data): if data is not None: self.cfg['pair'] = [data['pairid'], data['name'], data['platform']] self.cfg['mode'] = 2 self.handler.win.wallet.get_keystore().cfg = self.cfg self.handler.win.wallet.save_keystore() self.pin = 'paired' self.accept() def req_validation(self): if self.cfg['pair'] and 'secureScreenData' in self.txdata: if self.ws: self.ws.stop() self.ws = LedgerWebSocket(self, self.cfg['pair'][0], self.txdata) self.ws.req_updated.connect(self.req_updated) self.ws.start() def req_updated(self, pin): if pin == 'accepted': self.helpmsg.setText(helpTxt[3]) else: self.pin = str(pin) self.accept() def getDevice2FAMode(self): apdu = [0xe0, 0x24, 0x01, 0x00, 0x00, 0x01] # get 2fa mode try: mode = self.dongle.exchange(bytearray(apdu)) return mode except BTChipException, e: debug_msg('Device getMode Failed') return 0x11