Example #1
0
 def do_freeze_unfreeze(self):
     coins = getattr(self.freeze_button, "_coins", [])
     op = getattr(self.freeze_button, "_op", None)
     if not coins or op is None:
         return
     freeze = op == self.FreezeOp.Freeze
     # Freeze / Unfreeze
     self.wallet.set_frozen_coin_state(coins, freeze)
     delattr(self.freeze_button, "_coins")
     delattr(self.freeze_button, "_op")
     self.update()
     self.main_window.update_tabs()
     if freeze:
         # Freeze op success message
         self.show_message(
             ngettext("{count} coin has been frozen.",
                      "{count} coins have been frozen.", len(coins)).format(
                          count=len(coins)) + "\n" +
             _("Check the Coins tab to unfreeze."))
     else:
         # Unfreeze op success message
         self.show_message(
             ngettext("{count} coin has been unfrozen.",
                      "{count} coins have been unfrozen.",
                      len(coins)).format(count=len(coins)))
Example #2
0
 def _make_freeze_button_text(cls,
                              op: FreezeOp = FreezeOp.Freeze,
                              num_coins: int = 0) -> str:
     if op == cls.FreezeOp.Freeze:
         return ngettext("&Freeze Coin", "&Freeze Coins", num_coins)
     elif op == cls.FreezeOp.Unfreeze:
         return ngettext("&Unfreeze Coin", "&Unfreeze Coins", num_coins)
     raise ValueError(f"Invalid op: {op!r}")
Example #3
0
    def on_search():
        ok.setDisabled(ok_disables)
        name = acct.text().strip()
        tup = wallet.cashacct.parse_string(name)
        if tup:
            ca_msg(
                _("Searching for <b>{cash_account_name}</b> please wait ...").
                format(cash_account_name=name), True)
            results = None
            exc = []
            t0 = time.time()

            def resolve_verify():
                nonlocal results
                results = wallet.cashacct.resolve_verify(name, exc=exc)

            code = VerifyingDialog(
                parent.top_level_window(),
                _("Verifying Cash Account {name} please wait ...").format(
                    name=name),
                resolve_verify,
                auto_show=False).exec_()
            if code == QDialog.Rejected:
                # user cancel -- the waiting dialog thread will continue to run in the background but that's ok.. it will be a no-op
                d.reject()
                return
            if results:
                ca.setItems(results,
                            auto_resize_parent=False,
                            title='',
                            button_type=button_type)  # suppress groupbox title
            else:
                ca_msg(
                    _("The specified Cash Account does not appear to be associated with any address"
                      ), True)
                if time.time() - t0 >= cashacct.timeout:
                    if (wallet.verifier and wallet.synchronizer
                            and  # check these are still alive: these could potentially go away from under us if wallet is stopped when we get here.
                        (not wallet.verifier.is_up_to_date()
                         or not wallet.synchronizer.is_up_to_date())):
                        parent.show_message(
                            _("No results found. However, your wallet is busy updating."
                              " This can interfere with Cash Account lookups."
                              " You may want to try again when it is done."))
                    else:
                        parent.show_message(
                            _("A network timeout occurred while looking up this Cash Account. "
                              "You may want to check that your internet connection is up and "
                              "not saturated processing other requests."))
                elif exc and isinstance(exc[-1], requests.ConnectionError):
                    parent.show_error(
                        _("A network connectivity error occured. Please check your internet connection and try again."
                          ))
            nres = len(results or [])
            title = "<b>" + name + "</b> - " + ngettext(
                "{number} Cash Account", "{number} Cash Accounts",
                nres).format(number=nres)
            tit_lbl.setText(title)
        else:
            ca_msg(_("Invalid Cash Account name, please try again"), True)
Example #4
0
 def setItems(
         self,
         items: List[
             Tuple[lns.Info,
                   str]],  # list of 1 or 2 tuple : Info[, formatted_string]
         title=None,
         auto_resize_parent=True,
         sort=True,
         button_type: ButtonType = ButtonType.Radio):
     items = items or []
     nitems = len(items)
     title = ngettext("{number} LNS Name", "{number} LNS Names",
                      nitems).format(
                          number=nitems) if title is None else title
     wallet = self.wallet
     if items and sort:
         # sort items by formatted LNS Name string, also adding the string to
         # the items tuples; tuples now are modified to 2 elements:
         # (info, formatted_ca_string)
         formatter = lambda x: (x[0], wallet.lns.fmt_info(x[0]))
         if sort:
             items = sorted((formatter(x) for x in items),
                            key=lambda tup: tup[1])
         else:
             items = [formatter(x) for x in items]
     self._items = items
     self.button_type = button_type
     self.setTitle(title)
     self.refresh()
     if auto_resize_parent and self.parent():
         weakParent = util.Weak.ref(self.parent())
         QTimer.singleShot(
             0, lambda: weakParent() and weakParent().resize(weakParent().
                                                             sizeHint()))
Example #5
0
def verify_multiple_names(names: List[str],
                          parent: MessageBoxMixin,
                          wallet: Abstract_Wallet,
                          timeout=10.0) -> int:
    """ Pass a list of names and will attempt to verify them all in 1 pass.
    This is used by the Contacts tab to verify unverified LNS Names that
    may have been imported. Returns the number of successfully verified names
    or None on user cancel. """
    if not len(names):
        return 0
    names = set(names)
    nnames = len(names)
    q = queue.Queue()

    def done_cb(thing):
        if isinstance(thing, (Exception, list)):
            q.put(thing)
        else:
            q.put(None)

    ctr = 0

    def thread_func():
        nonlocal ctr
        wallet.lns.verify_name_asynch(name=names,
                                      success_cb=done_cb,
                                      error_cb=done_cb,
                                      timeout=timeout)
        errs = 0
        while ctr + errs < nnames:
            try:
                thing = q.get(timeout=timeout)
                if thing is None:
                    errs += 1
                elif isinstance(thing, Exception):
                    raise thing
                else:
                    ctr += len(thing)
            except queue.Empty:
                return

    code = VerifyingDialog(
        parent.top_level_window(),
        ngettext("Verifying {count} name please wait ...",
                 "Verifying {count} names please wait ...",
                 nnames).format(count=nnames),
        thread_func,
        auto_show=False,
        on_error=lambda e: parent.show_error(str(e))).exec_()
    if code != QDialog.Accepted:
        return None
    return ctr
Example #6
0
def verify_multiple_blocks(blocks: List[int],
                           parent: MessageBoxMixin,
                           wallet: Abstract_Wallet,
                           timeout=10.0) -> int:
    ''' Pass a list of blocks and will attempt to verify them all in 1 pass.
    This is used by the Contacts tab to verify unverified Cash Accounts that
    may have been imported. Returns the number of successfully verified blocks
    or None on user cancel. '''
    if not len(blocks):
        return 0
    blocks = set(blocks)
    nblocks = len(blocks)
    q = queue.Queue()

    def done_cb(thing):
        if isinstance(thing, cashacct.ProcessedBlock) and thing.reg_txs:
            q.put(thing)
        else:
            q.put(None)

    ctr = 0

    def thread_func():
        nonlocal ctr
        for number in blocks:
            wallet.cashacct.verify_block_asynch(number,
                                                success_cb=done_cb,
                                                error_cb=done_cb,
                                                timeout=timeout)
        errs = 0
        while ctr + errs < nblocks:
            try:
                thing = q.get(timeout=timeout)
                if thing is None:
                    errs += 1
                else:
                    ctr += 1
            except queue.Empty:
                return

    code = VerifyingDialog(
        parent.top_level_window(),
        ngettext("Verifying {count} block please wait ...",
                 "Verifying {count} blocks please wait ...",
                 nblocks).format(count=nblocks),
        thread_func,
        auto_show=False,
        on_error=lambda e: parent.show_error(str(e))).exec_()
    if code != QDialog.Accepted:
        return None
    return ctr
Example #7
0
def validate_op_return_output_and_get_data(
    output: tuple,  # tuple(typ, 'address', amount)
    max_size: int = 220,  # in bytes
    max_pushes:
    int = 1  # number of pushes supported after the OP_RETURN, most HW wallets support only 1 push, some more than 1.  Specify None to omit the number-of-pushes check.
) -> bytes:  # will return address.script[2:] (everyting after the first OP_RETURN & PUSH bytes)
    _type, address, _amount = output

    if max_pushes is None:
        # Caller says "no limit", so just to keep the below code simple, we
        # do this and effectively sets the limit on pushes to "unlimited",
        # since there can never be more pushes than bytes in the payload!
        max_pushes = max_size

    assert max_pushes >= 1

    if _type != TYPE_SCRIPT:
        raise Exception("Unexpected output type: {}".format(_type))

    ops = Script.get_ops(address.script)

    num_pushes = len(ops) - 1

    if len(ops) < 1 or ops[0][0] != OpCodes.OP_RETURN:
        raise RuntimeError(_("Only OP_RETURN scripts are supported."))

    if num_pushes < 1 or num_pushes > max_pushes or any(
            ops[i + 1][1] is None for i in range(num_pushes)):
        raise RuntimeError(
            ngettext("OP_RETURN is limited to {max_pushes} data push.",
                     "OP_RETURN is limited to {max_pushes} data pushes.",
                     max_pushes).format(max_pushes=max_pushes))

    data = address.script[
        2:]  # caller expects everything after the OP_RETURN and PUSHDATA op

    if len(data) > max_size:
        raise RuntimeError(
            _("OP_RETURN data size exceeds the maximum of {} bytes.".format(
                max_size)))

    if _amount != 0:
        raise RuntimeError(_("Amount for OP_RETURN output must be zero."))

    return data
Example #8
0
 def setItems(
         self,
         items:
     List[Tuple[
         cashacct.Info, str,
         str]],  # list of 2 or 3 tuple : Info, minimal_chash[, formatted_string]
         title=None,
         auto_resize_parent=True,
         sort=True,
         button_type: ButtonType = ButtonType.Radio):
     items = items or []
     nitems = len(items)
     title = ngettext("{number} Cash Account", "{number} Cash Accounts",
                      nitems).format(
                          number=nitems) if title is None else title
     wallet = self.wallet
     if items and (sort or len(items[0]) != 3):
         # sort items by formatted cash account string, also adding the string to
         # the items tuples; tuples now are modified to 3 elements:
         # (info, min_chash, formatted_ca_string)
         formatter = lambda x: (x[0], x[1],
                                wallet.cashacct.fmt_info(x[0], x[1]))
         if sort:
             items = sorted((formatter(x) for x in items),
                            key=lambda tup: tup[2])
         else:
             items = [formatter(x) for x in items]
     self._items = items
     self.button_type = button_type
     self.setTitle(title)
     self.refresh()
     if auto_resize_parent and self.parent():
         weakParent = util.Weak.ref(self.parent())
         QTimer.singleShot(
             0, lambda: weakParent() and weakParent().resize(weakParent().
                                                             sizeHint()))
    def create_menu(self, position):
        menu = QMenu()
        selected = self.selectedItems()
        i2c = self._i2c
        ca_unverified = self._get_ca_unverified(include_temp=False)
        if selected:
            names = [item.text(1) for item in selected]
            keys = [i2c(item) for item in selected]
            payable_keys = [k for k in keys if k.type != 'cashacct_T']
            deletable_keys = [k for k in keys if k.type in contact_types]
            needs_verif_keys = [k for k in keys if k in ca_unverified]
            column = self.currentColumn()
            column_title = self.headerItem().text(column)
            column_data = '\n'.join([item.text(column) for item in selected])
            item = self.currentItem()
            typ = i2c(item).type if item else 'unknown'
            ca_info = None
            if item and typ in ('cashacct', 'cashacct_W'):
                ca_info = self.wallet.cashacct.get_verified(i2c(item).name)
                if column == 1 and len(selected) == 1:
                    # hack .. for Cash Accounts just say "Copy Cash Account"
                    column_title = _('Cash Account')
                    if ca_info:
                        column_data = self.wallet.cashacct.fmt_info(ca_info, emoji=True)
            menu.addAction(_("Copy {}").format(column_title), lambda: self.parent.app.clipboard().setText(column_data))
            if item and column in self.editable_columns and self.on_permit_edit(item, column):
                key = item.data(0, self.DataRoles.Contact)
                # this key & find_item business is so we don't hold a reference
                # to the ephemeral item, which may be deleted while the
                # context menu is up.  Accessing the item after on_update runs
                # means the item is deleted and you get a C++ object deleted
                # runtime error.
                menu.addAction(_("Edit {}").format(column_title), lambda: self._on_edit_item(key, column))
            a = menu.addAction(_("Pay to"), lambda: self.parent.payto_contacts(payable_keys))
            if needs_verif_keys or not payable_keys:
                a.setDisabled(True)
            a = menu.addAction(_("Delete"), lambda: self.parent.delete_contacts(deletable_keys))
            if not deletable_keys:
                a.setDisabled(True)
            # Add sign/verify and encrypt/decrypt menu - but only if just 1 thing selected
            if len(keys) == 1 and Address.is_valid(keys[0]):
                signAddr = Address.from_string(keys[0])
                a = menu.addAction(_("Sign/verify message") + "...", lambda: self.parent.sign_verify_message(signAddr))
                if signAddr.kind != Address.ADDR_P2PKH:
                    a.setDisabled(True)  # We only allow this for P2PKH since it makes no sense for P2SH (ambiguous public key)
            URLs = [web.BE_URL(self.config, 'addr', Address.from_string(key.address))
                    for key in keys if Address.is_valid(key.address)]
            a = menu.addAction(_("View on block explorer"), lambda: [URL and webopen(URL) for URL in URLs])
            if not any(URLs):
                a.setDisabled(True)
            if ca_info:
                menu.addAction(_("View registration tx..."), lambda: self.parent.do_process_from_txid(txid=ca_info.txid, tx_desc=self.wallet.get_label(ca_info.txid)))
                if typ in ('cashacct_W', 'cashacct'):
                    _contact_d = i2c(item)
                    menu.addAction(_("Details..."), lambda: cashacctqt.cash_account_detail_dialog(self.parent, _contact_d.name))
            menu.addSeparator()

        menu.addAction(self.icon_cashacct,
                       _("Add Contact") + " - " + _("Cash Account"), self.new_cash_account_contact_dialog)
        menu.addAction(self.icon_contacts, _("Add Contact") + " - " + _("Address"), self.parent.new_contact_dialog)
        menu.addSeparator()
        menu.addAction(self.icon_cashacct,
                       _("Register Cash Account..."), self.parent.register_new_cash_account)
        menu.addSeparator()
        menu.addAction(QIcon(":icons/import.svg" if not ColorScheme.dark_scheme else ":icons/import_dark_theme.svg"),
                       _("Import file"), self.import_contacts)
        if not self.parent.contacts.empty:
            menu.addAction(QIcon(":icons/save.svg" if not ColorScheme.dark_scheme else ":icons/save_dark_theme.svg"),
                           _("Export file"), self.export_contacts)

        menu.addSeparator()
        a = menu.addAction(_("Show My Cash Accounts"), self.toggle_show_my_cashaccts)
        a.setCheckable(True)
        a.setChecked(self.show_my_cashaccts)

        if ca_unverified:
            def kick_off_verify():
                bnums = set()
                for contact in ca_unverified:
                    tup = self.wallet.cashacct.parse_string(contact.name)
                    if not tup:
                        continue
                    bnums.add(tup[1])  # number
                ret = cashacctqt.verify_multiple_blocks(bnums, self.parent, self.wallet)
                if ret is None:
                    # user cancel
                    return
                verified = ca_unverified - self._get_ca_unverified()
                if not verified:
                    self.parent.show_error(_("Cash Account verification failure"))

            menu.addSeparator()
            num = len(ca_unverified)
            a = menu.addAction(self.icon_unverif,
                               ngettext("Verify {count} Cash Account",
                                        "Verify {count} Cash Accounts",
                                        num).format(count=num), kick_off_verify)
            if not self.wallet.network:
                a.setDisabled(True)

        run_hook('create_contact_menu', menu, selected)
        menu.exec_(self.viewport().mapToGlobal(position))
Example #10
0
    def add_io(self, vbox):
        if self.tx.locktime > 0:
            lbl = QLabel(
                _("LockTime: {lock_time}").format(lock_time=self.tx.locktime))
            lbl.setTextInteractionFlags(lbl.textInteractionFlags()
                                        | Qt.TextSelectableByMouse)
            vbox.addWidget(lbl)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 12, 0, 0)

        self.i_text = i_text = TextBrowserKeyboardFocusFilter()
        num_inputs = len(self.tx.inputs())
        inputs_lbl_text = ngettext("&Input", "&Inputs ({num_inputs})",
                                   num_inputs).format(num_inputs=num_inputs)
        l = QLabel(inputs_lbl_text)
        l.setBuddy(i_text)
        hbox.addWidget(l)

        hbox.addSpacerItem(QSpacerItem(20, 0))  # 20 px padding
        self.dl_input_chk = chk = QCheckBox(_("&Download input data"))
        chk.setChecked(self.is_fetch_input_data())
        chk.clicked.connect(self.set_fetch_input_data)
        hbox.addWidget(chk)
        hbox.addStretch(1)
        if not self.wallet.network:
            # it makes no sense to enable this checkbox if the network is offline
            chk.setHidden(True)

        self.schnorr_label = QLabel(
            _('{} = Schnorr signed').format(SCHNORR_SIGIL))
        self.schnorr_label.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
        f = self.schnorr_label.font()
        f.setPointSize(f.pointSize() - 1)  # make it a little smaller
        self.schnorr_label.setFont(f)
        hbox.addWidget(self.schnorr_label)
        self.schnorr_label.setHidden(True)

        vbox.addLayout(hbox)

        i_text.setOpenLinks(False)  # disable automatic link opening
        i_text.anchorClicked.connect(
            self._open_internal_link)  # send links to our handler
        self.i_text_has_selection = False

        def set_i_text_has_selection(b):
            self.i_text_has_selection = bool(b)

        i_text.copyAvailable.connect(set_i_text_has_selection)
        i_text.setContextMenuPolicy(Qt.CustomContextMenu)
        i_text.customContextMenuRequested.connect(
            self.on_context_menu_for_inputs)
        i_text.setFont(QFont(MONOSPACE_FONT))
        i_text.setReadOnly(True)
        i_text.setTextInteractionFlags(i_text.textInteractionFlags()
                                       | Qt.LinksAccessibleByMouse
                                       | Qt.LinksAccessibleByKeyboard)
        vbox.addWidget(i_text)

        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        vbox.addLayout(hbox)

        self.o_text = o_text = TextBrowserKeyboardFocusFilter()
        num_outputs = len(self.tx.outputs())
        outputs_lbl_text = ngettext(
            "&Output", "&Outputs ({num_outputs})",
            num_outputs).format(num_outputs=num_outputs)
        l = QLabel(outputs_lbl_text)
        l.setBuddy(o_text)
        hbox.addWidget(l)

        box_char = "█"
        self.recv_legend = QLabel("<font color=" + ColorScheme.GREEN.as_color(
            background=True).name() + ">" + box_char + "</font> = " +
                                  _("Receiving Address"))
        self.change_legend = QLabel("<font color=" +
                                    ColorScheme.YELLOW.as_color(
                                        background=True).name() + ">" +
                                    box_char + "</font> = " +
                                    _("Change Address"))
        f = self.recv_legend.font()
        f.setPointSize(f.pointSize() - 1)
        self.recv_legend.setFont(f)
        self.change_legend.setFont(f)
        hbox.addStretch(2)
        hbox.addWidget(self.recv_legend)
        hbox.addWidget(self.change_legend)
        self.recv_legend.setHidden(True)
        self.change_legend.setHidden(True)

        o_text.setOpenLinks(False)  # disable automatic link opening
        o_text.anchorClicked.connect(
            self._open_internal_link)  # send links to our handler
        self.o_text_has_selection = False

        def set_o_text_has_selection(b):
            self.o_text_has_selection = bool(b)

        o_text.copyAvailable.connect(set_o_text_has_selection)
        o_text.setContextMenuPolicy(Qt.CustomContextMenu)
        o_text.customContextMenuRequested.connect(
            self.on_context_menu_for_outputs)
        o_text.setFont(QFont(MONOSPACE_FONT))
        o_text.setReadOnly(True)
        o_text.setTextInteractionFlags(o_text.textInteractionFlags()
                                       | Qt.LinksAccessibleByMouse
                                       | Qt.LinksAccessibleByKeyboard)
        vbox.addWidget(o_text)
        self.cashaddr_signal_slots.append(self.update_io)
        self.main_window.gui_object.cashaddr_toggled_signal.connect(
            self.update_io)
        self.update_io()