URI: 
       tMerge pull request #4916 from tiagotrs/master - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit 5fd83722d8f5b3aa12c529559b628da7b684d88a
   DIR parent 0657bb1b36f894a789762904035ad58a8110b6e9
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Sat, 15 Dec 2018 01:11:14 +0100
       
       Merge pull request #4916 from tiagotrs/master
       
       revealer: new interface
       closes #4540 
       closes #4676
       Diffstat:
         M electrum/gui/qt/seed_dialog.py      |       4 ----
         M electrum/plugins/revealer/__init__… |       8 ++------
         M electrum/plugins/revealer/qt.py     |     186 +++++++++++++++++--------------
       
       3 files changed, 105 insertions(+), 93 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/seed_dialog.py b/electrum/gui/qt/seed_dialog.py
       t@@ -26,8 +26,6 @@
        from electrum.i18n import _
        from electrum.mnemonic import Mnemonic
        import electrum.old_mnemonic
       -from electrum.plugin import run_hook
       -
        
        from .util import *
        from .qrtextedit import ShowQRTextEdit, ScanQRTextEdit
       t@@ -207,6 +205,4 @@ class SeedDialog(WindowModalDialog):
                title =  _("Your wallet generation seed is:")
                slayout = SeedLayout(title=title, seed=seed, msg=True, passphrase=passphrase)
                vbox.addLayout(slayout)
       -        has_extension = True if passphrase else False
       -        run_hook('set_seed', seed, has_extension, slayout.seed_e)
                vbox.addLayout(Buttons(CloseButton(self)))
   DIR diff --git a/electrum/plugins/revealer/__init__.py b/electrum/plugins/revealer/__init__.py
       t@@ -1,13 +1,9 @@
        from electrum.i18n import _
        
       -fullname = _('Revealer')
       +fullname = _('Revealer Backup Utility')
        description = ''.join(["<br/>",
            "<b>"+_("Do you have something to hide ?")+"</b>", '<br/>', '<br/>',
       -    _("Revealer is a seed phrase back-up solution. It allows you to create a cold, analog, multi-factor backup of your wallet seeds, or of any arbitrary secret."), '<br/>', '<br/>',
       -    _("Using a Revealer is better than writing your seed phrases on paper: a revealer is invulnerable to physical access and allows creation of trustless redundancy."), '<br/>', '<br/>',
       -    _("This plug-in allows you to generate a pdf file of your secret phrase encrypted visually for your physical Revealer. You can print it trustlessly - it can only be decrypted optically with your Revealer."), '<br/>', '<br/>',
       -    _("The plug-in also allows you to generate a digital Revealer file and print it yourself on a transparent overhead foil."), '<br/>', '<br/>',
       -    _("Once activated you can access the plug-in through the icon at the seed dialog."), '<br/>', '<br/>',
       +    _("This plug-in allows you to create a visually encrypted backup of your wallet seeds, or of custom alphanumeric secrets."), '<br/>', '<br/>',
            _("For more information, visit"),
            " <a href=\"https://revealer.cc\">https://revealer.cc</a>", '<br/>', '<br/>',
        ])
   DIR diff --git a/electrum/plugins/revealer/qt.py b/electrum/plugins/revealer/qt.py
       t@@ -1,19 +1,12 @@
        '''
        
        Revealer
       -So you have something to hide?
       -
       -plug-in for the electrum wallet.
       -
       -Features:
       -    - Deep Cold multi-factor backup solution
       -    - Safety - One time pad security
       -    - Redundancy - Trustless printing & distribution
       -    - Encrypt your seedphrase or any secret you want for your revealer
       -    - Based on crypto by legendary cryptographers Naor and Shamir
       +Do you have something to hide?
       +Secret backup plug-in for the electrum wallet.
        
        Tiago Romagnani Silveira, 2017
        
       +
        '''
        
        import os
       t@@ -28,14 +21,17 @@ from PyQt5.QtPrintSupport import QPrinter
        
        from electrum.plugin import BasePlugin, hook
        from electrum.i18n import _
       -from electrum.util import to_bytes, make_dir
       +from electrum.util import to_bytes, make_dir, InvalidPassword, UserCancelled
        from electrum.gui.qt.util import *
        from electrum.gui.qt.qrtextedit import ScanQRTextEdit
       +from electrum.gui.qt.main_window import StatusBarButton
        
        from .hmac_drbg import DRBG
        
        class Plugin(BasePlugin):
        
       +    MAX_PLAINTEXT_LEN = 189  # chars
       +
            def __init__(self, parent, config, name):
                BasePlugin.__init__(self, parent, config, name)
                self.base_dir = config.electrum_path()+'/revealer/'
       t@@ -57,11 +53,13 @@ class Plugin(BasePlugin):
                self.rawnoise = False
                make_dir(self.base_dir)
        
       +        self.extension = False
       +
            @hook
       -    def set_seed(self, seed, has_extension, parent):
       -        self.cseed = seed.upper()
       -        self.has_extension = has_extension
       -        parent.addButton(':icons/revealer.png', partial(self.setup_dialog, parent), "Revealer"+_(" secret backup utility"))
       +    def create_status_bar(self, parent):
       +        b = StatusBarButton(QIcon(':icons/revealer.png'), "Revealer "+_("secret backup utility"),
       +                            partial(self.setup_dialog, parent))
       +        parent.addPermanentWidget(b)
        
            def requires_settings(self):
                return True
       t@@ -69,39 +67,67 @@ class Plugin(BasePlugin):
            def settings_widget(self, window):
                return EnterButton(_('Printer Calibration'), partial(self.calibration_dialog, window))
        
       +    def password_dialog(self, msg=None, parent=None):
       +        from electrum.gui.qt.password_dialog import PasswordDialog
       +        parent = parent or self
       +        d = PasswordDialog(parent, msg)
       +        return d.run()
       +
       +    def get_seed(self):
       +        password = None
       +        if self.wallet.has_keystore_encryption():
       +            password = self.password_dialog(parent=self.d.parent())
       +            if not password:
       +                raise UserCancelled()
       +
       +        keystore = self.wallet.get_keystore()
       +        if not keystore or not keystore.has_seed():
       +            return
       +        self.extension = bool(keystore.get_passphrase(password))
       +        return keystore.get_seed(password)
       +
            def setup_dialog(self, window):
       -        self.update_wallet_name(window.parent().parent().wallet)
       +        self.wallet = window.parent().wallet
       +        self.update_wallet_name(self.wallet)
                self.user_input = False
                self.noise_seed = False
       -        self.d = WindowModalDialog(window, "Revealer")
       -        self.d.setMinimumWidth(420)
       -        vbox = QVBoxLayout(self.d)
       -        vbox.addSpacing(21)
       +
       +        self.d = WindowModalDialog(window, "Setup Dialog")
       +        self.d.setMinimumWidth(500)
       +        self.d.setMinimumHeight(210)
       +        self.d.setMaximumHeight(320)
       +        self.d.setContentsMargins(11,11,1,1)
       +
       +        self.hbox = QHBoxLayout(self.d)
       +        vbox = QVBoxLayout()
                logo = QLabel()
       -        vbox.addWidget(logo)
       +        self.hbox.addWidget(logo)
                logo.setPixmap(QPixmap(':icons/revealer.png'))
       -        logo.setAlignment(Qt.AlignCenter)
       -        vbox.addSpacing(42)
       -
       +        logo.setAlignment(Qt.AlignLeft)
       +        self.hbox.addSpacing(16)
       +        vbox.addWidget(WWLabel("<b>"+_("Revealer Secret Backup Plugin")+"</b><br>"
       +                                    +_("To encrypt your backup, first we need to load some noise.")+"<br/>"))
       +        vbox.addSpacing(7)
       +        bcreate = QPushButton(_("Create a new Revealer"))
       +        bcreate.setMaximumWidth(181)
       +        bcreate.setDefault(True)
       +        vbox.addWidget(bcreate, Qt.AlignCenter)
                self.load_noise = ScanQRTextEdit()
                self.load_noise.setTabChangesFocus(True)
                self.load_noise.textChanged.connect(self.on_edit)
                self.load_noise.setMaximumHeight(33)
       -
       -        vbox.addWidget(WWLabel("<b>"+_("Enter your physical revealer code:")+"<b>"))
       +        self.hbox.addLayout(vbox)
       +        vbox.addWidget(WWLabel(_("or type a existing revealer code below and click 'next':")))
                vbox.addWidget(self.load_noise)
       -        vbox.addSpacing(11)
       -
       +        vbox.addSpacing(3)
                self.next_button = QPushButton(_("Next"), self.d)
       -        self.next_button.setDefault(True)
                self.next_button.setEnabled(False)
                vbox.addLayout(Buttons(self.next_button))
                self.next_button.clicked.connect(self.d.close)
                self.next_button.clicked.connect(partial(self.cypherseed_dialog, window))
       -        vbox.addSpacing(21)
       -
       -        vbox.addWidget(WWLabel(_("or, alternatively: ")))
       -        bcreate = QPushButton(_("Create a digital Revealer"))
       +        vbox.addWidget(
       +            QLabel("<b>" + _("Warning") + "</b>: " + _("Each revealer should be used only once.")
       +                   +"<br>"+_("more information at <a href=\"https://revealer.cc/faq\">https://revealer.cc/faq</a>")))
        
                def mk_digital():
                    try:
       t@@ -112,15 +138,6 @@ class Plugin(BasePlugin):
                        self.cypherseed_dialog(window)
        
                bcreate.clicked.connect(mk_digital)
       -
       -        vbox.addWidget(bcreate)
       -        vbox.addSpacing(11)
       -        vbox.addWidget(QLabel(''.join([ "<b>"+_("WARNING")+ "</b>:" + _("Printing a revealer and encrypted seed"), '<br/>',
       -                                       _("on the same printer is not trustless towards the printer."), '<br/>',
       -                                       ])))
       -        vbox.addSpacing(11)
       -        vbox.addLayout(Buttons(CloseButton(self.d)))
       -
                return bool(self.d.exec_())
        
            def get_noise(self):
       t@@ -169,12 +186,14 @@ class Plugin(BasePlugin):
        
            def bcrypt(self, dialog):
                self.rawnoise = False
       -        dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at:").format(self.was, self.version, self.code_id),
       -                                     "<br/>","<b>", self.base_dir+ self.filename+self.version+"_"+self.code_id,"</b>"]))
       +        dialog.show_message(''.join([_("{} encrypted for Revealer {}_{} saved as PNG and PDF at: ").format(self.was, self.version, self.code_id),
       +                                     "<b>", self.base_dir+ self.filename+self.version+"_"+self.code_id,"</b>", "<br/>",
       +                                     "<br/>", "<b>", _("Always check you backups.") ]))
                dialog.close()
        
            def ext_warning(self, dialog):
       -        dialog.show_message(''.join(["<b>",_("Warning: "), "</b>", _("your seed extension will not be included in the encrypted backup.")]))
       +        dialog.show_message(''.join(["<b>",_("Warning"), ": </b>",
       +                                     _("your seed extension will <b>not</b> be included in the encrypted backup.")]))
                dialog.close()
        
            def bdone(self, dialog):
       t@@ -185,11 +204,11 @@ class Plugin(BasePlugin):
            def customtxt_limits(self):
                txt = self.text.text()
                self.max_chars.setVisible(False)
       -        self.char_count.setText("("+str(len(txt))+"/216)")
       +        self.char_count.setText(f"({len(txt)}/{self.MAX_PLAINTEXT_LEN})")
                if len(txt)>0:
                    self.ctext.setEnabled(True)
       -        if len(txt) > 216:
       -            self.text.setPlainText(self.text.toPlainText()[:216])
       +        if len(txt) > self.MAX_PLAINTEXT_LEN:
       +            self.text.setPlainText(txt[:self.MAX_PLAINTEXT_LEN])
                    self.max_chars.setVisible(True)
        
            def t(self):
       t@@ -198,57 +217,51 @@ class Plugin(BasePlugin):
        
            def cypherseed_dialog(self, window):
        
       -        d = WindowModalDialog(window, "Revealer")
       -        d.setMinimumWidth(420)
       -
       +        d = WindowModalDialog(window, "Encryption Dialog")
       +        d.setMinimumWidth(500)
       +        d.setMinimumHeight(210)
       +        d.setMaximumHeight(450)
       +        d.setContentsMargins(11, 11, 1, 1)
                self.c_dialog = d
        
       -        self.vbox = QVBoxLayout(d)
       -        self.vbox.addSpacing(21)
       -
       +        hbox = QHBoxLayout(d)
       +        self.vbox = QVBoxLayout()
                logo = QLabel()
       -        self.vbox.addWidget(logo)
       +        hbox.addWidget(logo)
                logo.setPixmap(QPixmap(':icons/revealer.png'))
       -        logo.setAlignment(Qt.AlignCenter)
       -        self.vbox.addSpacing(42)
       -
       +        logo.setAlignment(Qt.AlignLeft)
       +        hbox.addSpacing(16)
       +        self.vbox.addWidget(WWLabel("<b>" + _("Revealer Secret Backup Plugin") + "</b><br>"
       +                               + _("Ready to encrypt for revealer {}").format(self.version+'_'+self.code_id )))
       +        self.vbox.addSpacing(11)
       +        hbox.addLayout(self.vbox)
                grid = QGridLayout()
                self.vbox.addLayout(grid)
        
       -        cprint = QPushButton(_("Generate encrypted seed backup"))
       +        cprint = QPushButton(_("Encrypt {}'s seed").format(self.wallet_name))
       +        cprint.setMaximumWidth(250)
                cprint.clicked.connect(partial(self.seed_img, True))
                self.vbox.addWidget(cprint)
       -        self.vbox.addSpacing(14)
       -
       -        self.vbox.addWidget(WWLabel(_("OR type any secret below:")))
       +        self.vbox.addSpacing(1)
       +        self.vbox.addWidget(WWLabel("<b>"+_("OR")+"</b> "+_("type a custom alphanumerical secret below:")))
                self.text = ScanQRTextEdit()
                self.text.setTabChangesFocus(True)
                self.text.setMaximumHeight(70)
                self.text.textChanged.connect(self.customtxt_limits)
                self.vbox.addWidget(self.text)
       -
                self.char_count = WWLabel("")
                self.char_count.setAlignment(Qt.AlignRight)
                self.vbox.addWidget(self.char_count)
       -
       -        self.max_chars = WWLabel("<font color='red'>" + _("This version supports a maximum of 216 characters.")+"</font>")
       +        self.max_chars = WWLabel("<font color='red'>"
       +                                 + _("This version supports a maximum of {} characters.").format(self.MAX_PLAINTEXT_LEN)
       +                                 +"</font>")
                self.vbox.addWidget(self.max_chars)
                self.max_chars.setVisible(False)
       -
       -        self.ctext = QPushButton(_("Generate custom secret encrypted backup"))
       +        self.ctext = QPushButton(_("Encrypt custom secret"))
                self.ctext.clicked.connect(self.t)
       -
                self.vbox.addWidget(self.ctext)
                self.ctext.setEnabled(False)
       -
                self.vbox.addSpacing(11)
       -        self.vbox.addWidget(
       -                            QLabel(''.join(["<b>" + _("WARNING") + "</b>: " + _("Revealer is a one-time-pad and should be used only once."), '<br/>',
       -                            _("Multiple secrets encrypted for the same Revealer can be attacked."), '<br/>',
       -                            ])))
       -        self.vbox.addSpacing(11)
       -
       -        self.vbox.addSpacing(21)
                self.vbox.addLayout(Buttons(CloseButton(d)))
                return bool(d.exec_())
        
       t@@ -259,11 +272,18 @@ class Plugin(BasePlugin):
        
            def seed_img(self, is_seed = True):
        
       -        if not self.cseed and self.txt == False:
       -            return
       -
                if is_seed:
       -            txt = self.cseed
       +            try:
       +                cseed = self.get_seed()
       +            except UserCancelled:
       +                return
       +            except InvalidPassword as e:
       +                self.d.show_error(str(e))
       +                return
       +            if not cseed:
       +                self.d.show_message(_("This wallet has no seed"))
       +                return
       +            txt = cseed.upper()
                else:
                    txt = self.txt.upper()
        
       t@@ -282,7 +302,7 @@ class Plugin(BasePlugin):
                else:
                    fontsize = 12
                    linespace = 10
       -            max_letters = 23
       +            max_letters = 21
                    max_lines = 9
                    max_words = int(max_letters/4)
        
       t@@ -383,9 +403,9 @@ class Plugin(BasePlugin):
                else:
                    self.filename = self.wallet_name+'_'+ _('seed')+'_'
                    self.was = self.wallet_name +' ' + _('seed')
       +            if self.extension:
       +                self.ext_warning(self.c_dialog)
        
       -        if self.has_extension:
       -            self.ext_warning(self.c_dialog)
        
                if not calibration:
                    self.toPdf(QImage(cypherseed))