Пример #1
0
    def maybe_send_invoice_payment(self, tx: Transaction) -> bool:
        pr = self._payment_request
        if pr:
            tx_hash = tx.hash()
            invoice_id = pr.get_id()

            # TODO: Remove the dependence of broadcasting a transaction to pay an invoice on that
            # invoice being active in the send tab. Until then we assume that broadcasting a
            # transaction that is not related to the active invoice and it's repercussions, has
            # been confirmed by the appropriate calling logic. Like `confirm_broadcast_transaction`
            # in the main window logic.
            invoice_row = self._account.invoices.get_invoice_for_id(invoice_id)
            if tx_hash != invoice_row.tx_hash:
                # Calling logic should have detected this and warned/confirmed with the user.
                return True

            if pr.has_expired():
                pr.error = _("The invoice has expired")
                self.payment_request_error_signal.emit(invoice_id, tx_hash)
                return False

            if not pr.send_payment(self._account, str(tx)):
                self.payment_request_error_signal.emit(invoice_id, tx_hash)
                return False

            self._account.invoices.set_invoice_paid(invoice_id)

            self._payment_request = None
            # On success we broadcast as well, but it is assumed that the merchant also
            # broadcasts.
        return True
Пример #2
0
    def remove_signed_transaction(self, tx: Transaction, wallet: AbstractAccount):
        # must remove signed transactions after a failed broadcast attempt (to unlock utxos)
        # if it's a re-broadcast attempt (same txid) and we already have a StateDispatched or
        # StateCleared transaction then *no deletion* should occur
        tx_hash = tx.hash()
        signed_tx = wallet.get_transaction(tx_hash, flags=TxFlags.StateSigned)

        if signed_tx:
            wallet.delete_transaction(tx_hash)
Пример #3
0
    def __init__(self,
                 account: Optional[AbstractAccount],
                 tx: Transaction,
                 main_window: 'ElectrumWindow',
                 prompt_if_unsaved: bool,
                 payment_request: Optional[PaymentRequest] = None) -> None:
        '''Transactions in the wallet will show their description.
        Pass desc to give a description for txs not yet in the wallet.
        '''
        # We want to be a top-level window
        QDialog.__init__(self,
                         parent=None,
                         flags=Qt.WindowSystemMenuHint | Qt.WindowTitleHint
                         | Qt.WindowCloseButtonHint)

        self.copy_data_ready_signal.connect(self._copy_transaction_ready)
        self.save_data_ready_signal.connect(self._save_transaction_ready)

        # Take a copy; it might get updated in the main window by the FX thread.  If this
        # happens during or after a long sign operation the signatures are lost.
        self.tx = copy.deepcopy(tx)
        self.tx.context = copy.deepcopy(tx.context)
        self._tx_hash = tx.hash()

        self._main_window = main_window
        self._wallet = main_window._wallet
        self._account = account
        self._account_id = account.get_id() if account is not None else None
        self._payment_request = payment_request
        self._coin_service = CoinService(self._wallet)
        self._prompt_if_unsaved = prompt_if_unsaved
        self._saved = False

        self.setMinimumWidth(1000)
        self.setWindowTitle(_("Transaction"))
        self._monospace_font = QFont(platform.monospace_font)

        self._change_brush = QBrush(
            ColorScheme.YELLOW.as_color(background=True))
        self._receiving_brush = QBrush(
            ColorScheme.GREEN.as_color(background=True))
        self._broken_brush = QBrush(ColorScheme.RED.as_color(background=True))

        form = FormSectionWidget()

        vbox = QVBoxLayout()
        vbox.addWidget(form)
        self.setLayout(vbox)

        self.tx_hash_e = ButtonsLineEdit()
        self.tx_hash_e.addButton("qrcode.png", self._on_click_show_tx_hash_qr,
                                 _("Show as QR code"))
        self.tx_hash_e.addButton("copy.png", self._on_click_copy_tx_id,
                                 _("Copy to clipboard"))
        self.tx_hash_e.setReadOnly(True)
        form.add_row(_("Transaction ID"), self.tx_hash_e, True)

        self.tx_desc = QLabel()
        form.add_row(_("Description"), self.tx_desc)

        self.status_label = QLabel()
        form.add_row(_('Status'), self.status_label)

        self.date_label = QLabel()
        form.add_row(_("Date"), self.date_label)

        self.amount_label = QLabel()
        form.add_row(_("Amount"), self.amount_label)

        self.size_label = QLabel()
        form.add_row(_("Size"), self.size_label)

        self.fee_label = QLabel()
        form.add_row(_("Fee"), self.fee_label)

        if self.tx.locktime > 0:
            form.add_row(_("Lock time"), QLabel(str(self.tx.locktime)))

        self._add_io(vbox)

        self.sign_button = b = QPushButton(_("Sign"))
        b.clicked.connect(self.sign)

        self.broadcast_button = b = QPushButton(_("Broadcast"))
        b.clicked.connect(self.do_broadcast)

        self.cancel_button = b = QPushButton(_("Close"))
        b.clicked.connect(self.close)
        b.setDefault(True)

        self.qr_button = b = QPushButton()
        b.setIcon(read_QIcon("qrcode.png"))
        b.clicked.connect(self._show_qr)

        self._copy_menu = QMenu()
        self._copy_button = QPushButton(_("Copy"))
        self._copy_button.setMenu(self._copy_menu)

        self._save_menu = QMenu()
        self.save_button = QPushButton(_("Save"))
        self.save_button.setMenu(self._save_menu)

        self.cosigner_button = b = QPushButton(_("Send to cosigner"))
        b.clicked.connect(self.cosigner_send)

        # Action buttons
        self.buttons = [
            self.cosigner_button, self.sign_button, self.broadcast_button,
            self.cancel_button
        ]
        # Transaction sharing buttons
        self.sharing_buttons = [
            self._copy_button, self.qr_button, self.save_button
        ]

        hbox = QHBoxLayout()
        hbox.addLayout(Buttons(*self.sharing_buttons))
        hbox.addStretch(1)
        hbox.addLayout(Buttons(*self.buttons))
        vbox.addLayout(hbox)
        self.update()

        # connect slots so we update in realtime as blocks come in, etc
        main_window.history_updated_signal.connect(self.update_tx_if_in_wallet)
        main_window.network_signal.connect(self._on_transaction_verified)
        main_window.transaction_added_signal.connect(
            self._on_transaction_added)
Пример #4
0
    def get_tx_info(self, tx: Transaction) -> TxInfo:
        value_delta = 0
        can_broadcast = False
        label = ''
        fee = height = conf = timestamp = None
        tx_hash = tx.hash()
        if tx.is_complete():
            entry = self._wallet._transaction_cache.get_cached_entry(tx_hash)
            metadata = entry.metadata
            fee = metadata.fee
            label = self._account.get_transaction_label(tx_hash)
            value_delta = self._account.get_transaction_delta(tx_hash)
            if value_delta is None:
                # When the transaction is fully signed and updated before the delta changes
                # are committed to the database (pending write).
                value_delta = 0
            if self._account.has_received_transaction(tx_hash):
                if (entry.flags & TxFlags.StateSettled
                        or entry.flags & TxFlags.StateCleared
                        and metadata.height > 0):
                    chain = app_state.headers.longest_chain()
                    try:
                        header = app_state.headers.header_at_height(
                            chain, metadata.height)
                        timestamp = header.timestamp
                    except MissingHeader:
                        pass

                    if entry.flags & TxFlags.StateSettled:
                        height = metadata.height
                        conf = max(
                            self._wallet.get_local_height() - height + 1, 0)
                        status = _(
                            "{:,d} confirmations (in block {:,d})").format(
                                conf, height)
                    else:
                        status = _('Not verified')
                else:
                    status = _('Unconfirmed')
            else:
                status = _("Signed")
                can_broadcast = self._wallet._network is not None
        else:
            for input in tx.inputs:
                value_delta -= input.value
            for output in tx.outputs:
                # If we know what type of script it is, we sign it's spend (or co-sign it).
                if output.script_type != ScriptType.NONE:
                    value_delta += output.value

            s, r = tx.signature_count()
            status = _("Unsigned") if s == 0 else _(
                'Partially signed') + ' (%d/%d)' % (s, r)

        if value_delta < 0:
            if fee is not None:
                amount = value_delta + fee
            else:
                amount = value_delta
        elif value_delta > 0:
            amount = value_delta
        else:
            amount = None

        return TxInfo(tx_hash, status, label, can_broadcast, amount, fee,
                      height, conf, timestamp)