Exemple #1
0
 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)
Exemple #2
0
 def callback_PassphraseRequest(self, req):
     if self.creating_wallet:
         msg = _("Enter a passphrase to generate this wallet.  Each time "
                 "you use this wallet your %s will prompt you for the "
                 "passphrase.  If you forget the passphrase you cannot "
                 "access the neblios in the wallet.") % self.device
     else:
         msg = _("Enter the passphrase to unlock this wallet:")
     passphrase = self.handler.get_passphrase(msg, self.creating_wallet)
     if passphrase is None:
         return self.proto.Cancel()
     passphrase = bip39_normalize_passphrase(passphrase)
     return self.proto.PassphraseAck(passphrase=passphrase)
Exemple #3
0
        def update(features):
            self.features = features
            set_label_enabled()
            bl_hash = features.bootloader_hash.encode('hex')
            bl_hash = "\n".join([bl_hash[:32], bl_hash[32:]])
            noyes = [_("No"), _("Yes")]
            endis = [_("Enable Passphrases"), _("Disable Passphrases")]
            disen = [_("Disabled"), _("Enabled")]
            setchange = [_("Set a PIN"), _("Change PIN")]

            version = "%d.%d.%d" % (features.major_version,
                                    features.minor_version,
                                    features.patch_version)
            coins = ", ".join(coin.coin_name for coin in features.coins)

            device_label.setText(features.label)
            pin_set_label.setText(noyes[features.pin_protection])
            passphrases_label.setText(disen[features.passphrase_protection])
            bl_hash_label.setText(bl_hash)
            label_edit.setText(features.label)
            device_id_label.setText(features.device_id)
            initialized_label.setText(noyes[features.initialized])
            version_label.setText(version)
            coins_label.setText(coins)
            clear_pin_button.setVisible(features.pin_protection)
            clear_pin_warning.setVisible(features.pin_protection)
            pin_button.setText(setchange[features.pin_protection])
            pin_msg.setVisible(not features.pin_protection)
            passphrase_button.setText(endis[features.passphrase_protection])
            language_label.setText(features.language)
 def on_update(self):
     self.wallet = self.parent.wallet
     item = self.currentItem()
     current_address = item.data(0,
                                 Qt.UserRole).toString() if item else None
     self.clear()
     receiving_addresses = self.wallet.get_receiving_addresses()
     change_addresses = self.wallet.get_change_addresses()
     if True:
         account_item = self
         sequences = [0, 1] if change_addresses else [0]
         for is_change in sequences:
             if len(sequences) > 1:
                 name = _("Receiving") if not is_change else _("Change")
                 seq_item = QTreeWidgetItem([name, '', '', '', ''])
                 account_item.addChild(seq_item)
                 if not is_change:
                     seq_item.setExpanded(True)
             else:
                 seq_item = account_item
             used_item = QTreeWidgetItem([_("Used"), '', '', '', ''])
             used_flag = False
             addr_list = change_addresses if is_change else receiving_addresses
             for address in addr_list:
                 num = len(self.wallet.history.get(address, []))
                 is_used = self.wallet.is_used(address)
                 label = self.wallet.labels.get(address, '')
                 c, u, x = self.wallet.get_addr_balance(address)
                 balance = self.parent.format_amount(c + u + x)
                 address_item = QTreeWidgetItem(
                     [address, label, balance,
                      "%d" % num])
                 address_item.setFont(0, QFont(MONOSPACE_FONT))
                 address_item.setData(0, Qt.UserRole, address)
                 address_item.setData(0, Qt.UserRole + 1,
                                      True)  # label can be edited
                 if self.wallet.is_frozen(address):
                     address_item.setBackgroundColor(0, QColor('lightblue'))
                 if self.wallet.is_beyond_limit(address, is_change):
                     address_item.setBackgroundColor(0, QColor('red'))
                 if is_used:
                     if not used_flag:
                         seq_item.insertChild(0, used_item)
                         used_flag = True
                     used_item.addChild(address_item)
                 else:
                     seq_item.addChild(address_item)
                 if address == current_address:
                     self.setCurrentItem(address_item)
 def __init__(self, parent=None, msg=None):
     msg = msg or _('Please enter your password')
     WindowModalDialog.__init__(self, parent, _("Enter Password"))
     self.pw = pw = QLineEdit()
     pw.setEchoMode(2)
     vbox = QVBoxLayout()
     vbox.addWidget(QLabel(msg))
     grid = QGridLayout()
     grid.setSpacing(8)
     grid.addWidget(QLabel(_('Password')), 1, 0)
     grid.addWidget(pw, 1, 1)
     vbox.addLayout(grid)
     vbox.addLayout(Buttons(CancelButton(self), OkButton(self)))
     self.setLayout(vbox)
     run_hook('password_dialog', pw, grid, 1)
Exemple #6
0
 def create_menu(self, position):
     item = self.currentItem()
     if not item:
         return
     is_server = not bool(item.data(0, Qt.UserRole).toInt()[0])
     menu = QMenu()
     if is_server:
         server = unicode(item.data(1, Qt.UserRole).toString())
         menu.addAction(_("Use as server"),
                        lambda: self.parent.follow_server(server))
     else:
         index = item.data(1, Qt.UserRole).toInt()[0]
         menu.addAction(_("Follow this branch"),
                        lambda: self.parent.follow_branch(index))
     menu.exec_(self.viewport().mapToGlobal(position))
 def seed_device_dialog(self):
     msg = _("Choose how to initialize your Digital Bitbox:\n")
     choices = [
         (_("Generate a new random wallet")),
         (_("Load a wallet from the micro SD card"))
     ]
     try:
         reply = self.handler.win.query_choice(msg, choices)
     except Exception:
         return # Back button pushed
     if reply == 0:
         self.dbb_generate_wallet()
     else:
         if not self.dbb_load_backup(show_msg=False):
             return
     self.isInitialized = True
Exemple #8
0
 def create_menu(self, position):
     menu = QMenu()
     item = self.itemAt(position)
     key = str(item.data(0, 32).toString())
     column = self.currentColumn()
     column_title = self.headerItem().text(column)
     column_data = item.text(column)
     pr = self.parent.invoices.get(key)
     status = self.parent.invoices.get_status(key)
     if column_data:
         menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
     menu.addAction(_("Details"), lambda: self.parent.show_invoice(key))
     if status == PR_UNPAID:
         menu.addAction(_("Pay Now"), lambda: self.parent.do_pay_invoice(key))
     menu.addAction(_("Delete"), lambda: self.parent.delete_invoice(key))
     menu.exec_(self.viewport().mapToGlobal(position))
Exemple #9
0
 def callback_WordRequest(self, msg):
     self.step += 1
     msg = _("Step %d/24.  Enter seed word as explained on "
             "your %s:") % (self.step, self.device)
     word = self.handler.get_word(msg)
     # Unfortunately the device can't handle self.proto.Cancel()
     return self.proto.WordAck(word=word)
Exemple #10
0
 def create_menu(self, position):
     item = self.itemAt(position)
     if not item:
         return
     addr = str(item.text(1))
     req = self.wallet.receive_requests[addr]
     column = self.currentColumn()
     column_title = self.headerItem().text(column)
     column_data = item.text(column)
     menu = QMenu(self)
     menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data))
     menu.addAction(_("Copy URI"), lambda: self.parent.view_and_paste('URI', '', self.parent.get_request_URI(addr)))
     menu.addAction(_("Save as BIP70 file"), lambda: self.parent.export_payment_request(addr))
     menu.addAction(_("Delete"), lambda: self.parent.delete_payment_request(addr))
     run_hook('receive_list_menu', menu, addr)
     menu.exec_(self.viewport().mapToGlobal(position))
Exemple #11
0
 def password_dialog(self, pw, grid, pos):
     vkb_button = QPushButton(_("+"))
     vkb_button.setFixedWidth(20)
     vkb_button.clicked.connect(lambda: self.toggle_vkb(grid, pw))
     grid.addWidget(vkb_button, pos, 2)
     self.kb_pos = 2
     self.vkb = None
Exemple #12
0
    def create_client(self, device, handler):
        # disable bridge because it seems to never returns if keepkey is plugged
        #transport = self._try_bridge(device) or self._try_hid(device)
        transport = self._try_hid(device)
        if not transport:
            self.print_error("cannot connect to device")
            return

        self.print_error("connected to device at", device.path)

        client = self.client_class(transport, handler, self)

        # Try a ping for device sanity
        try:
            client.ping('t')
        except BaseException as e:
            self.print_error("ping failed", str(e))
            return None

        if not client.atleast_version(*self.minimum_firmware):
            msg = (_('Outdated %s firmware for device labelled %s. Please '
                     'download the updated firmware from %s') %
                   (self.device, client.label(), self.firmware_URL))
            self.print_error(msg)
            handler.show_error(msg)
            return None

        return client
Exemple #13
0
    def __init__(self, win):
        QWidget.__init__(self)
        self.win = win
        self.setWindowTitle('Electrum-NEBL - ' + _('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)
Exemple #14
0
 def f(b):
     self.is_seed = (lambda x: bool(x)) if b else self.saved_is_seed
     self.on_edit()
     self.is_bip39 = b
     if b:
         msg = ' '.join([
             '<b>' + _('Warning') +
             ': BIP39 seeds can be imported by Electrum, but not exported!'
             + '</b><br/><br/>',
             _('BIP39 seeds can be imported in Electrum so that users can access funds from our Android or Orion wallets.'
               ),
             _('However, Electrum does not export BIP39 seeds or save your seed phrase for later viewing.'
               ),
         ])
     else:
         msg = ''
     self.seed_warning.setText(msg)
Exemple #15
0
 def create_menu(self, position):
     item = self.currentItem()
     if not item:
         return
     menu = QMenu()
     server = unicode(item.data(1, Qt.UserRole).toString())
     menu.addAction(_("Use as server"), lambda: self.set_server(server))
     menu.exec_(self.viewport().mapToGlobal(position))
Exemple #16
0
 def message_dialog(self, msg):
     self.clear_dialog()
     self.dialog = dialog = WindowModalDialog(self.top_level_window(),
                                              _("Ledger Status"))
     l = QLabel(msg)
     vbox = QVBoxLayout(dialog)
     vbox.addWidget(l)
     dialog.show()
Exemple #17
0
 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"))
Exemple #18
0
 def func_wrapper(*args, **kwargs):
     run_next = kwargs['run_next']
     wizard = args[0]
     wizard.back_button.setText(
         _('Back') if wizard.can_go_back() else _('Cancel'))
     try:
         out = func(*args, **kwargs)
     except GoBack:
         wizard.go_back() if wizard.can_go_back() else wizard.close()
         return
     except UserCancelled:
         return
     #if out is None:
     #    out = ()
     if type(out) is not tuple:
         out = (out, )
     run_next(*out)
Exemple #19
0
 def show_seed_dialog(self, run_next, seed_text):
     title = _("Your wallet generation seed is:")
     slayout = SeedLayout(seed=seed_text,
                          title=title,
                          msg=True,
                          options=['ext'])
     self.exec_layout(slayout)
     return slayout.is_ext
Exemple #20
0
 def __init__(self, seed=None, title=None, icon=True, msg=None, options=None, is_seed=None, passphrase=None, parent=None):
     QVBoxLayout.__init__(self)
     self.parent = parent
     self.options = options
     if title:
         self.addWidget(WWLabel(title))
     if seed:
         self.seed_e = ShowQRTextEdit()
         self.seed_e.setText(seed)
     else:
         self.seed_e = ScanQRTextEdit()
         self.seed_e.setTabChangesFocus(True)
         self.is_seed = is_seed
         self.saved_is_seed = self.is_seed
         self.seed_e.textChanged.connect(self.on_edit)
     self.seed_e.setMaximumHeight(75)
     hbox = QHBoxLayout()
     if icon:
         logo = QLabel()
         logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(64))
         logo.setMaximumWidth(60)
         hbox.addWidget(logo)
     hbox.addWidget(self.seed_e)
     self.addLayout(hbox)
     hbox = QHBoxLayout()
     hbox.addStretch(1)
     self.seed_type_label = QLabel('')
     hbox.addWidget(self.seed_type_label)
     if options:
         opt_button = EnterButton(_('Options'), self.seed_options)
         hbox.addWidget(opt_button)
         self.addLayout(hbox)
     if passphrase:
         hbox = QHBoxLayout()
         passphrase_e = QLineEdit()
         passphrase_e.setText(passphrase)
         passphrase_e.setReadOnly(True)
         hbox.addWidget(QLabel(_("Your seed extension is") + ':'))
         hbox.addWidget(passphrase_e)
         self.addLayout(hbox)
     self.addStretch(1)
     self.seed_warning = WWLabel('')
     if msg:
         self.seed_warning.setText(seed_warning_msg(seed))
     self.addWidget(self.seed_warning)
Exemple #21
0
def filename_field(parent, config, defaultname, select_msg):

    vbox = QVBoxLayout()
    vbox.addWidget(QLabel(_("Format")))
    gb = QGroupBox("format", parent)
    b1 = QRadioButton(gb)
    b1.setText(_("CSV"))
    b1.setChecked(True)
    b2 = QRadioButton(gb)
    b2.setText(_("json"))
    vbox.addWidget(b1)
    vbox.addWidget(b2)

    hbox = QHBoxLayout()

    directory = config.get('io_dir', unicode(os.path.expanduser('~')))
    path = os.path.join(directory, defaultname)
    filename_e = QLineEdit()
    filename_e.setText(path)

    def func():
        text = unicode(filename_e.text())
        _filter = "*.csv" if text.endswith(
            ".csv") else "*.json" if text.endswith(".json") else None
        p = unicode(
            QFileDialog.getSaveFileName(None, select_msg, text, _filter))
        if p:
            filename_e.setText(p)

    button = QPushButton(_('File'))
    button.clicked.connect(func)
    hbox.addWidget(button)
    hbox.addWidget(filename_e)
    vbox.addLayout(hbox)

    def set_csv(v):
        text = unicode(filename_e.text())
        text = text.replace(".json", ".csv") if v else text.replace(
            ".csv", ".json")
        filename_e.setText(text)

    b1.clicked.connect(lambda: set_csv(True))
    b2.clicked.connect(lambda: set_csv(False))

    return vbox, filename_e, b1
Exemple #22
0
    def create_menu(self, position):
        selected = [
            str(x.data(0, Qt.UserRole).toString())
            for x in self.selectedItems()
        ]
        if not selected:
            return
        menu = QMenu()
        coins = filter(lambda x: self.get_name(x) in selected, self.utxos)

        menu.addAction(_("Spend"), lambda: self.parent.spend_coins(coins))
        if len(selected) == 1:
            txid = selected[0].split(':')[0]
            tx = self.wallet.transactions.get(txid)
            menu.addAction(_("Details"),
                           lambda: self.parent.show_transaction(tx))

        menu.exec_(self.viewport().mapToGlobal(position))
Exemple #23
0
    def __init__(self, data, parent=None, title="", show_text=False):
        WindowModalDialog.__init__(self, parent, title)

        vbox = QVBoxLayout()
        qrw = QRCodeWidget(data)
        vbox.addWidget(qrw, 1)
        if show_text:
            text = QTextEdit()
            text.setText(data)
            text.setReadOnly(True)
            vbox.addWidget(text)
        hbox = QHBoxLayout()
        hbox.addStretch(1)

        config = electrum_nebl.get_config()
        if config:
            filename = os.path.join(config.path, "qrcode.png")

            def print_qr():
                p = QPixmap.grabWindow(qrw.winId())
                p.save(filename, 'png')
                self.show_message(_("QR code saved to file") + " " + filename)

            def copy_to_clipboard():
                p = QPixmap.grabWindow(qrw.winId())
                p.save(filename, 'png')
                QApplication.clipboard().setImage(QImage(filename))
                self.show_message(_("QR code copied to clipboard"))

            b = QPushButton(_("Copy"))
            hbox.addWidget(b)
            b.clicked.connect(copy_to_clipboard)

            b = QPushButton(_("Save"))
            hbox.addWidget(b)
            b.clicked.connect(print_qr)

        b = QPushButton(_("Close"))
        hbox.addWidget(b)
        b.clicked.connect(self.accept)
        b.setDefault(True)

        vbox.addLayout(hbox)
        self.setLayout(vbox)
Exemple #24
0
 def __init__(self, network, config):
     QDialog.__init__(self)
     self.setWindowTitle(_('Network'))
     self.setMinimumSize(500, 20)
     self.nlayout = NetworkChoiceLayout(network, config)
     vbox = QVBoxLayout(self)
     vbox.addLayout(self.nlayout.layout())
     vbox.addLayout(Buttons(CloseButton(self)))
     self.connect(self, QtCore.SIGNAL('updated'), self.on_update)
     network.register_callback(self.on_network, ['updated', 'interfaces'])
 def check_device_dialog(self):
     # Set password if fresh device
     if self.password is None and not self.dbb_has_password():
         if not self.setupRunning:
             return False # A fresh device cannot connect to an existing wallet
         msg = _("An uninitialized Digital Bitbox is detected. " \
                 "Enter a new password below.\r\n\r\n REMEMBER THE PASSWORD!\r\n\r\n" \
                 "You cannot access your coins or a backup without the password.\r\n" \
                 "A backup is saved automatically when generating a new wallet.")
         if self.password_dialog(msg):
             reply = self.hid_send_plain('{"password":"******"}')
         else:
             return False
     
     # Get password from user if not yet set
     msg = _("Enter your Digital Bitbox password:"******"led":"blink"}')
         if 'error' in reply:
             self.password = None
             if reply['error']['code'] == 109:
                 msg = _("Incorrect password entered.\r\n\r\n"  \
                         + reply['error']['message'] + "\r\n\r\n" \
                         "Enter your Digital Bitbox password:"******"Unexpected error occurred.\r\n\r\n"  \
                         + reply['error']['message'] + "\r\n\r\n" \
                         "Enter your Digital Bitbox password:"******"device":"info"}')
         if reply['device']['id'] <> "":
             self.recover_or_erase_dialog() # Already seeded
         else:
             self.seed_device_dialog() # Seed if not initialized
     
     return self.isInitialized
Exemple #26
0
 def show_address(self, wallet, address):
     client = self.get_client(wallet.keystore)
     if not client.atleast_version(1, 3):
         keystore.handler.show_error(_("Your device firmware is too old"))
         return
     change, index = wallet.get_address_index(address)
     derivation = wallet.keystore.derivation
     address_path = "%s/%d/%d" % (derivation, change, index)
     address_n = client.expand_path(address_path)
     client.get_address(self.get_coin_name(), address_n, True)
Exemple #27
0
 def toggle_passphrase():
     title = _("Confirm Toggle Passphrase Protection")
     currently_enabled = self.features.passphrase_protection
     if currently_enabled:
         msg = _("After disabling passphrases, you can only pair this "
                 "Electrum wallet if it had an empty passphrase.  "
                 "If its passphrase was not empty, you will need to "
                 "create a new wallet with the install wizard.  You "
                 "can use this wallet again at any time by re-enabling "
                 "passphrases and entering its passphrase.")
     else:
         msg = _("Your current Electrum wallet can only be used with "
                 "an empty passphrase.  You must create a separate "
                 "wallet with the install wizard for other passphrases "
                 "as each one generates a new set of addresses.")
     msg += "\n\n" + _("Are you sure you want to proceed?")
     if not self.question(msg, title=title):
         return
     invoke_client('toggle_passphrase', unpair_after=currently_enabled)
 def recover_or_erase_dialog(self):
     msg = _("The Digital Bitbox is already seeded. Choose an option:\n")
     choices = [
         (_("Create a wallet using the current seed")),
         (_("Load a wallet from the micro SD card (the current seed is overwritten)")),
         (_("Erase the Digital Bitbox"))
     ]
     try:
         reply = self.handler.win.query_choice(msg, choices)
     except Exception:
         return # Back button pushed
     if reply == 2:
         self.dbb_erase()
     elif reply == 1:
         if not self.dbb_load_backup():
             return
     else:
         pass # Use existing seed
     self.isInitialized = True
    def sign_message(self, sequence, message, password):
        sig = None
        try:
            inputPath = self.get_derivation() + "/%d/%d" % sequence
            inputHash = Hash(msg_magic(message)).encode('hex')
            hasharray = []
            hasharray.append({'hash': inputHash, 'keypath': inputPath})
            hasharray = json.dumps(hasharray)
            
            msg = '{"sign":{"meta":"sign message", "data":%s}}' % (hasharray)
            
            dbb_client = self.plugin.get_client(self)
            
            if not dbb_client.is_paired():
                raise Exception("Could not sign message.")
            
            reply = dbb_client.hid_send_encrypt(msg)
            self.handler.show_message(_("Signing message ...\r\n\r\n" \
                                        "To continue, touch the Digital Bitbox's blinking light for 3 seconds.\r\n\r\n" \
                                        "To cancel, briefly touch the blinking light or wait for the timeout."))
            reply = dbb_client.hid_send_encrypt(msg) # Send twice, first returns an echo for smart verification (not implemented)
            self.handler.clear_dialog()

            if 'error' in reply:
                raise Exception(reply['error']['message'])
          
            if 'sign' not in reply:
                raise Exception("Could not sign message.")

            if 'recid' in reply['sign'][0]:
                # firmware > v2.1.1
                sig = chr(27 + int(reply['sign'][0]['recid'], 16) + 4) + reply['sign'][0]['sig'].decode('hex')
                h = Hash(msg_magic(message))
                pk, compressed = pubkey_from_signature(sig, h)
                pk = point_to_ser(pk.pubkey.point, compressed)
                addr = public_key_to_p2pkh(pk)
                if verify_message(addr, sig, message) is False:
                    raise Exception("Could not sign message")
            elif 'pubkey' in reply['sign'][0]:
                # firmware <= v2.1.1
                for i in range(4):
                    sig = chr(27 + i + 4) + reply['sign'][0]['sig'].decode('hex')
                    try:
                        addr = public_key_to_p2pkh(reply['sign'][0]['pubkey'].decode('hex'))
                        if verify_message(addr, sig, message):
                            break
                    except Exception:
                        continue
                else:
                    raise Exception("Could not sign message")
            
            
        except BaseException as e:
            self.give_error(e)
        return sig
Exemple #30
0
    def multisig_dialog(self, run_next):
        cw = CosignWidget(2, 2)
        m_edit = QSlider(Qt.Horizontal, self)
        n_edit = QSlider(Qt.Horizontal, self)
        n_edit.setMinimum(2)
        n_edit.setMaximum(15)
        m_edit.setMinimum(1)
        m_edit.setMaximum(2)
        n_edit.setValue(2)
        m_edit.setValue(2)
        n_label = QLabel()
        m_label = QLabel()
        grid = QGridLayout()
        grid.addWidget(n_label, 0, 0)
        grid.addWidget(n_edit, 0, 1)
        grid.addWidget(m_label, 1, 0)
        grid.addWidget(m_edit, 1, 1)

        def on_m(m):
            m_label.setText(_('Require %d signatures') % m)
            cw.set_m(m)

        def on_n(n):
            n_label.setText(_('From %d cosigners') % n)
            cw.set_n(n)
            m_edit.setMaximum(n)

        n_edit.valueChanged.connect(on_n)
        m_edit.valueChanged.connect(on_m)
        on_n(2)
        on_m(2)
        vbox = QVBoxLayout()
        vbox.addWidget(cw)
        vbox.addWidget(
            WWLabel(
                _("Choose the number of signatures needed to unlock funds in your wallet:"
                  )))
        vbox.addLayout(grid)
        self.exec_layout(vbox, _("Multi-Signature Wallet"))
        m = int(m_edit.value())
        n = int(n_edit.value())
        return (m, n)