コード例 #1
0
    def update_io(self, i_text, o_text):
        ext = QTextCharFormat()
        rec = QTextCharFormat()
        rec.setBackground(QBrush(ColorScheme.GREEN.as_color(background=True)))
        rec.setToolTip(_("Wallet receive address"))
        chg = QTextCharFormat()
        chg.setBackground(QBrush(QColor("yellow")))
        chg.setToolTip(_("Wallet change address"))

        def text_format(addr):
            if isinstance(addr, Address) and self.wallet.is_mine(addr):
                return chg if self.wallet.is_change(addr) else rec
            return ext

        def format_amount(amt):
            return self.main_window.format_amount(amt, whitespaces=True)

        i_text.clear()
        cursor = i_text.textCursor()
        for txin in self.tx.inputs:
            if txin.is_coinbase():
                cursor.insertText('coinbase')
            else:
                prev_hash = hash_to_hex_str(txin.prev_hash)
                prev_idx = txin.prev_idx
                cursor.insertText(f'{prev_hash}:{prev_idx:<6d}', ext)
                addr = txin.address
                if isinstance(addr, PublicKey):
                    addr = addr.toAddress()
                if addr is None:
                    addr_text = _('unknown')
                else:
                    addr_text = addr.to_string()
                cursor.insertText(addr_text, text_format(addr))
                if txin.value is not None:
                    cursor.insertText(format_amount(txin.value), ext)
            cursor.insertBlock()

        o_text.clear()
        cursor = o_text.textCursor()
        for tx_output in self.tx.outputs:
            text, kind = tx_output_to_display_text(tx_output)
            cursor.insertText(text, text_format(kind))

            if len(text) > 42:  # for long outputs, make a linebreak.
                cursor.insertBlock()
                text = '\u21b3'
                cursor.insertText(text, ext)
            # insert enough spaces until column 43, to line up amounts
            cursor.insertText(' ' * (43 - len(text)), ext)
            cursor.insertText(format_amount(tx_output.value), ext)
            cursor.insertBlock()
コード例 #2
0
    def update_io(self, i_text: QTextEdit, o_text: QTextEdit):
        ext = QTextCharFormat()
        rec = QTextCharFormat()
        rec.setBackground(QBrush(ColorScheme.GREEN.as_color(background=True)))
        rec.setToolTip(_("Wallet receive key"))

        # chg = QTextCharFormat()
        # chg.setBackground(QBrush(QColor("yellow")))
        # chg.setToolTip(_("Wallet change key"))

        def verify_own_output(output: XTxOutput) -> bool:
            if not output.x_pubkeys:
                return False
            for x_pubkey in output.x_pubkeys:
                result = self._main_window._wallet.resolve_xpubkey(x_pubkey)
                if result is not None:
                    account, keyinstance_id = result
                    return account.get_script_for_id(
                        keyinstance_id) == output.script_pubkey
            return False

        known_txos = set(self._account._utxos) | set(self._account._stxos)

        def text_format(utxo_key: Tuple[bytes, int]) -> QTextCharFormat:
            nonlocal known_txos
            return rec if utxo_key in known_txos else ext

        def format_amount(amt: int) -> str:
            return self._main_window.format_amount(amt, whitespaces=True)

        i_text.clear()
        cursor = i_text.textCursor()
        for txin in self.tx.inputs:
            if txin.is_coinbase():
                cursor.insertText('coinbase')
            else:
                prev_hash_hex = hash_to_hex_str(txin.prev_hash)
                cursor.insertText(f'{prev_hash_hex}:{txin.prev_idx:<6d}', ext)
                txo_key = (txin.prev_hash, txin.prev_idx)
                if txo_key in known_txos:
                    txo_text = _("Mine")
                else:
                    txo_text = _("Unknown")
                cursor.insertText(txo_text, text_format(txo_key))
                if txin.value is not None:
                    cursor.insertText(format_amount(txin.value), ext)
            cursor.insertBlock()

        o_text.clear()
        cursor = o_text.textCursor()
        tx_hash: bytes = self.tx.hash()
        for tx_index, tx_output in enumerate(self.tx.outputs):
            text, kind = tx_output_to_display_text(tx_output)

            out_format = ext
            if verify_own_output(tx_output):
                out_format = rec
            elif (self._tx_hash, tx_index) in known_txos:
                out_format = rec
            cursor.insertText(text, out_format)

            if len(text) > 42:  # for long outputs, make a linebreak.
                cursor.insertBlock()
                text = '\u21b3'
                cursor.insertText(text, ext)
            # insert enough spaces until column 43, to line up amounts
            cursor.insertText(' ' * (43 - len(text)), ext)
            cursor.insertText(format_amount(tx_output.value), ext)
            cursor.insertBlock()
コード例 #3
0
    def _update_io(self, i_table: MyTreeWidget, o_table: MyTreeWidget) -> None:
        def get_xtxoutput_account(
                output: XTxOutput) -> Tuple[Optional[AbstractAccount], int]:
            if output.x_pubkeys:
                for x_pubkey in output.x_pubkeys:
                    result = self._main_window._wallet.resolve_xpubkey(
                        x_pubkey)
                    if result is not None:
                        account, keyinstance_id = result
                        if account.get_script_for_id(
                                keyinstance_id) == output.script_pubkey:
                            return account, keyinstance_id
                        # TODO: Document when this happens
                        break
            return None, -1

        def get_keyinstance_id(account: AbstractAccount,
                               txo_key: TxoKeyType) -> Optional[int]:
            utxo = account._utxos.get(txo_key)
            if utxo is not None:
                return utxo.keyinstance_id
            stxo_keyinstance_id = account._stxos.get(txo_key)
            if stxo_keyinstance_id is not None:
                return stxo_keyinstance_id
            return None

        def compare_key_path(account: AbstractAccount, keyinstance_id: int,
                             leading_path: Sequence[int]) -> bool:
            key_path = account.get_derivation_path(keyinstance_id)
            return key_path is not None and key_path[:len(leading_path
                                                          )] == leading_path

        def name_for_account(account: AbstractAccount) -> str:
            name = account.display_name()
            return f"{account.get_id()}: {name}"

        is_tx_complete = self.tx.is_complete()
        is_tx_known = self._account and self._account.have_transaction_data(
            self._tx_hash)

        prev_txos = self._coin_service.get_outputs([
            TxoKeyType(txin.prev_hash, txin.prev_idx)
            for txin in self.tx.inputs
        ])
        prev_txo_dict = {
            TxoKeyType(r.tx_hash, r.tx_index): r
            for r in prev_txos
        }
        self._spent_value_label.setText(
            _("Spent input value") + ": " +
            app_state.format_amount(sum(r.value for r in prev_txos)))

        for tx_index, txin in enumerate(self.tx.inputs):
            account_name = ""
            source_text = ""
            amount_text = ""
            is_receiving = is_change = is_broken = False
            txo_key = TxoKeyType(txin.prev_hash, txin.prev_idx)

            if txin.is_coinbase():
                source_text = "<coinbase>"
            else:
                prev_hash_hex = hash_to_hex_str(txin.prev_hash)
                source_text = f"{prev_hash_hex}:{txin.prev_idx}"
                # There are only certain kinds of transactions that have values on the inputs,
                # likely deserialised incomplete transactions from cosigners. Others?
                value = txin.value
                if self._account is not None:
                    keyinstance_id = get_keyinstance_id(self._account, txo_key)
                    is_receiving = compare_key_path(self._account,
                                                    keyinstance_id,
                                                    RECEIVING_SUBPATH)
                    is_change = compare_key_path(self._account, keyinstance_id,
                                                 CHANGE_SUBPATH)
                    account_name = name_for_account(self._account)
                    prev_txo = prev_txo_dict.get(txo_key, None)
                    if prev_txo is not None and is_tx_complete:
                        value = prev_txo.value
                        if is_tx_known:
                            # The transaction has been added to the account.
                            is_broken = (prev_txo.flags
                                         & TransactionOutputFlag.IS_SPENT) == 0
                        else:
                            # The transaction was most likely loaded from external source and is
                            # being viewed but has not been added to the account.
                            is_broken = (prev_txo.flags
                                         & TransactionOutputFlag.IS_SPENT) != 0
                amount_text = app_state.format_amount(value, whitespaces=True)

            item = QTreeWidgetItem(
                [str(tx_index), account_name, source_text, amount_text])
            item.setData(InputColumns.INDEX, Roles.TX_HASH, txin.prev_hash)
            item.setData(InputColumns.INDEX, Roles.IS_MINE, is_change
                         or is_receiving)
            if is_receiving:
                item.setBackground(InputColumns.SOURCE, self._receiving_brush)
            if is_change:
                item.setBackground(InputColumns.SOURCE, self._change_brush)
            if is_broken:
                item.setBackground(InputColumns.SOURCE, self._broken_brush)
            item.setTextAlignment(InputColumns.AMOUNT,
                                  Qt.AlignRight | Qt.AlignVCenter)
            item.setFont(InputColumns.AMOUNT, self._monospace_font)
            i_table.addTopLevelItem(item)

        # TODO: Rewrite this to be lot simpler when we have better TXO management. At this time
        # we do not track UTXOs except when a transaction is known to the network as we rely on
        # the server state to map key usage to transactions or something. We need to completely
        # rewrite that and then rewrite this. Anyway, that is why signed tx outputs do not get
        # identified and colourised.

        received_value = 0
        for tx_index, tx_output in enumerate(self.tx.outputs):
            text, _kind = tx_output_to_display_text(tx_output)
            if isinstance(_kind, Unknown_Output):
                text = script_bytes_to_asm(tx_output.script_pubkey)

            # In the longer run we will have some form of abstraction for incomplete transactions
            # that maps where the keys come from, but for now we manually map them to the limited
            # key hierarchy that currently exists.
            xtxo_account, xtxo_keyinstance_id = get_xtxoutput_account(
                tx_output)
            accounts: List[AbstractAccount] = []
            if xtxo_account is not None:
                accounts.append(xtxo_account)
            if self._account is not None and xtxo_account is not self._account:
                accounts.append(self._account)

            account_id: Optional[int] = None
            account_name = ""
            keyinstance_id: Optional[int] = None
            is_receiving = is_change = False
            txo_key = TxoKeyType(self._tx_hash, tx_index)
            for account in accounts:
                if is_tx_complete:
                    keyinstance_id = get_keyinstance_id(account, txo_key)
                elif account is xtxo_account and xtxo_keyinstance_id != -1:
                    keyinstance_id = xtxo_keyinstance_id

                if keyinstance_id is not None:
                    account_id = account.get_id()
                    is_receiving = compare_key_path(account, keyinstance_id,
                                                    RECEIVING_SUBPATH)
                    is_change = compare_key_path(account, keyinstance_id,
                                                 CHANGE_SUBPATH)
                    account_name = name_for_account(account)
                    received_value += tx_output.value
                    break

            amount_text = app_state.format_amount(tx_output.value,
                                                  whitespaces=True)

            item = QTreeWidgetItem(
                [str(tx_index), account_name, text, amount_text])
            item.setData(OutputColumns.INDEX, Roles.IS_MINE, is_change
                         or is_receiving)
            item.setData(OutputColumns.INDEX, Roles.ACCOUNT_ID, account_id)
            item.setData(OutputColumns.INDEX, Roles.KEY_ID, keyinstance_id)
            if is_receiving:
                item.setBackground(OutputColumns.DESTINATION,
                                   self._receiving_brush)
            if is_change:
                item.setBackground(OutputColumns.DESTINATION,
                                   self._change_brush)
            item.setTextAlignment(OutputColumns.AMOUNT,
                                  Qt.AlignRight | Qt.AlignVCenter)
            item.setFont(OutputColumns.AMOUNT, self._monospace_font)
            o_table.addTopLevelItem(item)

        self._received_value_label.setText(
            _("Received output value") + ": " +
            app_state.format_amount(received_value))
コード例 #4
0
    def _update_io(self, i_table: MyTreeWidget, o_table: MyTreeWidget) -> None:
        def get_xtxoutput_account(
                output: XTxOutput) -> Optional[AbstractAccount]:
            if not output.x_pubkeys:
                return None
            for x_pubkey in output.x_pubkeys:
                result = self._main_window._wallet.resolve_xpubkey(x_pubkey)
                if result is not None:
                    account, keyinstance_id = result
                    if account.get_script_for_id(
                            keyinstance_id) == output.script_pubkey:
                        return account
                    return None
            return None

        known_txos: Set[Tuple[bytes, int]] = set()
        if self._account is not None:
            known_txos = set(self._account._utxos) | set(self._account._stxos)

        def compare_key_path(account: AbstractAccount, txo_key: TxoKeyType,
                             leading_path: Sequence[int]) -> bool:
            utxo = account._utxos.get(txo_key)
            if utxo is not None:
                key_path = account.get_derivation_path(utxo.keyinstance_id)
                if key_path is not None and key_path[:len(leading_path
                                                          )] == leading_path:
                    return True
            stxo_keyinstance_id = account._stxos.get(txo_key)
            if stxo_keyinstance_id is not None:
                key_path = account.get_derivation_path(stxo_keyinstance_id)
                if key_path is not None and key_path[:len(leading_path
                                                          )] == leading_path:
                    return True
            return False

        def name_for_account(account: AbstractAccount) -> str:
            name = account.display_name()
            return f"{account.get_id()}: {name}"

        for tx_index, txin in enumerate(self.tx.inputs):
            account_name = ""
            source_text = ""
            amount_text = ""
            is_receiving = is_change = False

            if txin.is_coinbase():
                source_text = "<coinbase>"
            else:
                prev_hash_hex = hash_to_hex_str(txin.prev_hash)
                source_text = f"{prev_hash_hex}:{txin.prev_idx}"
                if self._account is not None:
                    txo_key = TxoKeyType(txin.prev_hash, txin.prev_idx)
                    is_receiving = compare_key_path(self._account, txo_key,
                                                    RECEIVING_SUBPATH)
                    is_change = compare_key_path(self._account, txo_key,
                                                 CHANGE_SUBPATH)
                    account_name = name_for_account(self._account)
                # TODO(rt12): When does a txin have a value? Loaded incomplete transactions only?
                if txin.value is not None:
                    amount_text = app_state.format_amount(txin.value,
                                                          whitespaces=True)

            item = QTreeWidgetItem(
                [str(tx_index), account_name, source_text, amount_text])
            # item.setData(0, Qt.UserRole, row.paymentrequest_id)
            if is_receiving:
                item.setBackground(2, self._receiving_brush)
            if is_change:
                item.setBackground(2, self._change_brush)
            item.setTextAlignment(3, Qt.AlignRight | Qt.AlignVCenter)
            item.setFont(3, self._monospace_font)
            i_table.addTopLevelItem(item)

        for tx_index, tx_output in enumerate(self.tx.outputs):
            text, _kind = tx_output_to_display_text(tx_output)
            if isinstance(_kind, Unknown_Output):
                text = script_bytes_to_asm(tx_output.script_pubkey)

            account = get_xtxoutput_account(tx_output)
            accounts: List[AbstractAccount] = []
            if account is not None:
                accounts.append(account)
            if self._account is not None and account is not self._account:
                accounts.append(self._account)

            account_name = ""
            is_receiving = is_change = False
            txo_key = TxoKeyType(self._tx_hash, tx_index)
            for account in accounts:
                if txo_key in account._stxos or txo_key in account._utxos:
                    is_receiving = compare_key_path(account, txo_key,
                                                    RECEIVING_SUBPATH)
                    is_change = compare_key_path(account, txo_key,
                                                 CHANGE_SUBPATH)
                    account_name = name_for_account(account)
                    break

            amount_text = app_state.format_amount(tx_output.value,
                                                  whitespaces=True)

            item = QTreeWidgetItem(
                [str(tx_index), account_name, text, amount_text])
            # item.setData(0, Qt.UserRole, row.paymentrequest_id)
            if is_receiving:
                item.setBackground(2, self._receiving_brush)
            if is_change:
                item.setBackground(2, self._change_brush)
            item.setTextAlignment(3, Qt.AlignRight | Qt.AlignVCenter)
            item.setFont(3, self._monospace_font)
            o_table.addTopLevelItem(item)