Merge pull request #9793 from accumulator/psbt_nostr_send_description
PSBT nostr, send invoice/tx description along with PSBT
This commit is contained in:
@@ -13,6 +13,7 @@ ElDialog {
|
||||
// if text_qr is undefined text will be used
|
||||
property string text_help
|
||||
property string text_warn
|
||||
property string tx_label
|
||||
|
||||
title: qsTr('Share Transaction')
|
||||
|
||||
|
||||
@@ -98,7 +98,8 @@ Item {
|
||||
? ''
|
||||
: [qsTr('Warning: Some data (prev txs / "full utxos") was left out of the QR code as it would not fit.'),
|
||||
qsTr('This might cause issues if signing offline.'),
|
||||
qsTr('As a workaround, copy to clipboard or use the Share option instead.')].join(' ')
|
||||
qsTr('As a workaround, copy to clipboard or use the Share option instead.')].join(' '),
|
||||
tx_label: data[3]
|
||||
})
|
||||
dialog.open()
|
||||
}
|
||||
|
||||
@@ -383,9 +383,11 @@ class QETxDetails(QObject, QtEventListener):
|
||||
|
||||
self.detailsChanged.emit()
|
||||
|
||||
if self._label != txinfo.label:
|
||||
self._label = txinfo.label
|
||||
self.labelChanged.emit()
|
||||
if self._txid:
|
||||
label = self._wallet.wallet.get_label_for_txid(self._txid)
|
||||
if self._label != label:
|
||||
self._label = label
|
||||
self.labelChanged.emit()
|
||||
|
||||
def update_mined_status(self, tx_mined_info: TxMinedInfo):
|
||||
self._mempool_depth = ''
|
||||
@@ -505,4 +507,5 @@ class QETxDetails(QObject, QtEventListener):
|
||||
@pyqtSlot(result='QVariantList')
|
||||
def getSerializedTx(self):
|
||||
txqr = self._tx.to_qr_data()
|
||||
return [str(self._tx), txqr[0], txqr[1]]
|
||||
label = self._wallet.wallet.get_label_for_txid(self._tx.txid())
|
||||
return [str(self._tx), txqr[0], txqr[1], label]
|
||||
|
||||
@@ -494,7 +494,8 @@ class QETxFinalizer(TxFeeSlider):
|
||||
@pyqtSlot(result='QVariantList')
|
||||
def getSerializedTx(self):
|
||||
txqr = self._tx.to_qr_data()
|
||||
return [str(self._tx), txqr[0], txqr[1]]
|
||||
label = self._wallet.wallet.get_label_for_txid(self._tx.txid())
|
||||
return [str(self._tx), txqr[0], txqr[1], label]
|
||||
|
||||
|
||||
class TxMonMixin(QtEventListener):
|
||||
|
||||
@@ -1169,7 +1169,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
tx: Transaction,
|
||||
*,
|
||||
external_keypairs: Mapping[bytes, bytes] = None,
|
||||
payment_identifier: PaymentIdentifier = None,
|
||||
invoice: Invoice = None,
|
||||
show_sign_button: bool = True,
|
||||
show_broadcast_button: bool = True,
|
||||
):
|
||||
@@ -1177,7 +1177,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
tx,
|
||||
parent=self,
|
||||
external_keypairs=external_keypairs,
|
||||
payment_identifier=payment_identifier,
|
||||
invoice=invoice,
|
||||
show_sign_button=show_sign_button,
|
||||
show_broadcast_button=show_broadcast_button,
|
||||
)
|
||||
@@ -1413,18 +1413,18 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
"""
|
||||
return self.utxo_list.get_spend_list()
|
||||
|
||||
def broadcast_or_show(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None):
|
||||
def broadcast_or_show(self, tx: Transaction, *, invoice: 'Invoice' = None):
|
||||
if not tx.is_complete():
|
||||
self.show_transaction(tx, payment_identifier=payment_identifier)
|
||||
self.show_transaction(tx, invoice=invoice)
|
||||
return
|
||||
if not self.network:
|
||||
self.show_error(_("You can't broadcast a transaction without a live network connection."))
|
||||
self.show_transaction(tx, payment_identifier=payment_identifier)
|
||||
self.show_transaction(tx, invoice=invoice)
|
||||
return
|
||||
self.broadcast_transaction(tx, payment_identifier=payment_identifier)
|
||||
self.broadcast_transaction(tx, invoice=invoice)
|
||||
|
||||
def broadcast_transaction(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None):
|
||||
self.send_tab.broadcast_transaction(tx, payment_identifier=payment_identifier)
|
||||
def broadcast_transaction(self, tx: Transaction, *, invoice: Invoice = None):
|
||||
self.send_tab.broadcast_transaction(tx, invoice=invoice)
|
||||
|
||||
@protected
|
||||
def sign_tx(
|
||||
|
||||
@@ -305,10 +305,6 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
if run_hook('abort_send', self):
|
||||
return
|
||||
|
||||
payment_identifier = None
|
||||
if invoice and invoice.bip70:
|
||||
payment_identifier = payment_identifier_from_invoice(self.wallet, invoice)
|
||||
|
||||
is_sweep = bool(external_keypairs)
|
||||
# we call get_coins inside make_tx, so that inputs can be changed dynamically
|
||||
if get_coins is None:
|
||||
@@ -354,12 +350,12 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
tx.swap_payment_hash = swap.payment_hash
|
||||
|
||||
if is_preview:
|
||||
self.window.show_transaction(tx, external_keypairs=external_keypairs, payment_identifier=payment_identifier)
|
||||
self.window.show_transaction(tx, external_keypairs=external_keypairs, invoice=invoice)
|
||||
return
|
||||
self.save_pending_invoice()
|
||||
def sign_done(success):
|
||||
if success:
|
||||
self.window.broadcast_or_show(tx, payment_identifier=payment_identifier)
|
||||
self.window.broadcast_or_show(tx, invoice=invoice)
|
||||
self.window.sign_tx(
|
||||
tx,
|
||||
callback=sign_done,
|
||||
@@ -712,7 +708,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
chan, swap_recv_amount_sat = can_pay_with_swap
|
||||
self.window.run_swap_dialog(is_reverse=False, recv_amount_sat=swap_recv_amount_sat, channels=[chan])
|
||||
elif r == 'onchain':
|
||||
self.pay_onchain_dialog(invoice.get_outputs(), nonlocal_only=True)
|
||||
self.pay_onchain_dialog(invoice.get_outputs(), nonlocal_only=True, invoice=invoice)
|
||||
return
|
||||
|
||||
assert lnworker is not None
|
||||
@@ -725,9 +721,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
coro = lnworker.pay_invoice(invoice, amount_msat=amount_msat)
|
||||
self.window.run_coroutine_from_thread(coro, _('Sending payment'))
|
||||
|
||||
def broadcast_transaction(self, tx: Transaction, *, payment_identifier: PaymentIdentifier = None):
|
||||
# note: payment_identifier is explicitly passed as self.payto_e.payment_identifier might
|
||||
# already be cleared or otherwise have changed.
|
||||
def broadcast_transaction(self, tx: Transaction, *, invoice: Invoice = None):
|
||||
if hasattr(tx, 'swap_payment_hash'):
|
||||
sm = self.wallet.lnworker.swap_manager
|
||||
swap = sm.get_swap(tx.swap_payment_hash)
|
||||
@@ -742,7 +736,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
|
||||
def broadcast_thread():
|
||||
# non-GUI thread
|
||||
if payment_identifier and payment_identifier.has_expired():
|
||||
if invoice and invoice.has_expired():
|
||||
return False, _("Invoice has expired")
|
||||
try:
|
||||
self.network.run_from_another_thread(self.network.broadcast_transaction(tx))
|
||||
@@ -751,19 +745,22 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
except BestEffortRequestFailed as e:
|
||||
return False, repr(e)
|
||||
# success
|
||||
txid = tx.txid()
|
||||
if payment_identifier and payment_identifier.need_merchant_notify():
|
||||
refund_address = self.wallet.get_receiving_address()
|
||||
payment_identifier.notify_merchant(
|
||||
tx=tx,
|
||||
refund_address=refund_address,
|
||||
on_finished=self.notify_merchant_done_signal.emit
|
||||
)
|
||||
return True, txid
|
||||
if invoice and invoice.bip70:
|
||||
payment_identifier = payment_identifier_from_invoice(invoice)
|
||||
# FIXME: this should move to backend
|
||||
if payment_identifier and payment_identifier.need_merchant_notify():
|
||||
refund_address = self.wallet.get_receiving_address()
|
||||
payment_identifier.notify_merchant(
|
||||
tx=tx,
|
||||
refund_address=refund_address,
|
||||
on_finished=self.notify_merchant_done_signal.emit
|
||||
)
|
||||
return True, tx.txid()
|
||||
|
||||
# Capture current TL window; override might be removed on return
|
||||
parent = self.window.top_level_window(lambda win: isinstance(win, MessageBoxMixin))
|
||||
|
||||
# FIXME: move to backend and let Abstract_Wallet set broadcasting state, not gui
|
||||
self.wallet.set_broadcasting(tx, broadcasting_status=PR_BROADCASTING)
|
||||
|
||||
def broadcast_done(result):
|
||||
|
||||
@@ -64,7 +64,7 @@ from .my_treeview import create_toolbar_with_menu, QMenuWithConfig
|
||||
if TYPE_CHECKING:
|
||||
from .main_window import ElectrumWindow
|
||||
from electrum.wallet import Abstract_Wallet
|
||||
from electrum.payment_identifier import PaymentIdentifier
|
||||
from electrum.invoices import Invoice
|
||||
|
||||
|
||||
_logger = get_logger(__name__)
|
||||
@@ -418,7 +418,7 @@ def show_transaction(
|
||||
parent: 'ElectrumWindow',
|
||||
prompt_if_unsaved: bool = False,
|
||||
external_keypairs: Mapping[bytes, bytes] = None,
|
||||
payment_identifier: 'PaymentIdentifier' = None,
|
||||
invoice: 'Invoice' = None,
|
||||
on_closed: Callable[[], None] = None,
|
||||
show_sign_button: bool = True,
|
||||
show_broadcast_button: bool = True,
|
||||
@@ -429,7 +429,7 @@ def show_transaction(
|
||||
parent=parent,
|
||||
prompt_if_unsaved=prompt_if_unsaved,
|
||||
external_keypairs=external_keypairs,
|
||||
payment_identifier=payment_identifier,
|
||||
invoice=invoice,
|
||||
on_closed=on_closed,
|
||||
)
|
||||
if not show_sign_button:
|
||||
@@ -454,7 +454,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
parent: 'ElectrumWindow',
|
||||
prompt_if_unsaved: bool,
|
||||
external_keypairs: Mapping[bytes, bytes] = None,
|
||||
payment_identifier: 'PaymentIdentifier' = None,
|
||||
invoice: 'Invoice' = None,
|
||||
on_closed: Callable[[], None] = None,
|
||||
):
|
||||
'''Transactions in the wallet will show their description.
|
||||
@@ -467,13 +467,15 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
self.main_window = parent
|
||||
self.config = parent.config
|
||||
self.wallet = parent.wallet
|
||||
self.payment_identifier = payment_identifier
|
||||
self.invoice = invoice
|
||||
self.prompt_if_unsaved = prompt_if_unsaved
|
||||
self.on_closed = on_closed
|
||||
self.saved = False
|
||||
self.desc = None
|
||||
if txid := tx.txid():
|
||||
self.desc = self.wallet.get_label_for_txid(txid) or None
|
||||
if not self.desc and self.invoice:
|
||||
self.desc = self.invoice.get_message()
|
||||
self.setMinimumWidth(640)
|
||||
|
||||
self.psbt_only_widgets = [] # type: List[Union[QWidget, QAction]]
|
||||
@@ -492,13 +494,8 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
self.tx_desc_label = QLabel(_("Description:"))
|
||||
vbox.addWidget(self.tx_desc_label)
|
||||
self.tx_desc = ButtonsLineEdit('')
|
||||
def on_edited():
|
||||
text = self.tx_desc.text()
|
||||
if self.wallet.set_label(txid, text):
|
||||
self.main_window.history_list.update()
|
||||
self.main_window.utxo_list.update()
|
||||
self.main_window.labels_changed_signal.emit()
|
||||
self.tx_desc.editingFinished.connect(on_edited)
|
||||
|
||||
self.tx_desc.editingFinished.connect(self.store_tx_label)
|
||||
self.tx_desc.addCopyButton()
|
||||
vbox.addWidget(self.tx_desc)
|
||||
|
||||
@@ -579,6 +576,13 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
self.update()
|
||||
self.set_title()
|
||||
|
||||
def store_tx_label(self):
|
||||
text = self.tx_desc.text()
|
||||
if self.wallet.set_label(self.tx.txid(), text):
|
||||
self.main_window.history_list.update()
|
||||
self.main_window.utxo_list.update()
|
||||
self.main_window.labels_changed_signal.emit()
|
||||
|
||||
def set_tx(self, tx: 'Transaction'):
|
||||
# Take a copy; it might get updated in the main window by
|
||||
# e.g. the FX plugin. If this happens during or after a long
|
||||
@@ -607,7 +611,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
self.main_window.push_top_level_window(self)
|
||||
self.main_window.send_tab.save_pending_invoice()
|
||||
try:
|
||||
self.main_window.broadcast_transaction(self.tx, payment_identifier=self.payment_identifier)
|
||||
self.main_window.broadcast_transaction(self.tx, invoice=self.invoice)
|
||||
finally:
|
||||
self.main_window.pop_top_level_window(self)
|
||||
self.saved = True
|
||||
@@ -722,6 +726,7 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
def save(self):
|
||||
self.main_window.push_top_level_window(self)
|
||||
if self.main_window.save_transaction_into_wallet(self.tx):
|
||||
self.store_tx_label()
|
||||
self.save_button.setDisabled(True)
|
||||
self.saved = True
|
||||
self.main_window.pop_top_level_window(self)
|
||||
@@ -851,7 +856,8 @@ class TxDialog(QDialog, MessageBoxMixin):
|
||||
# note: when not finalized, RBF and locktime changes do not trigger
|
||||
# a make_tx, so the txid is unreliable, hence:
|
||||
self.tx_hash_e.setText(_('Unknown'))
|
||||
if not self.wallet.adb.get_transaction(txid):
|
||||
tx_in_db = bool(self.wallet.adb.get_transaction(txid))
|
||||
if not desc and not tx_in_db:
|
||||
self.tx_desc.hide()
|
||||
self.tx_desc_label.hide()
|
||||
else:
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
import asyncio
|
||||
import json
|
||||
import ssl
|
||||
import time
|
||||
from contextlib import asynccontextmanager
|
||||
@@ -30,7 +31,7 @@ from contextlib import asynccontextmanager
|
||||
import electrum_ecc as ecc
|
||||
import electrum_aionostr as aionostr
|
||||
from electrum_aionostr.key import PrivateKey
|
||||
from typing import Dict, TYPE_CHECKING, Union, List, Tuple, Optional
|
||||
from typing import Dict, TYPE_CHECKING, Union, List, Tuple, Optional, Callable
|
||||
|
||||
from electrum import util, Transaction
|
||||
from electrum.crypto import sha256
|
||||
@@ -38,8 +39,9 @@ from electrum.i18n import _
|
||||
from electrum.logging import Logger
|
||||
from electrum.plugin import BasePlugin
|
||||
from electrum.transaction import PartialTransaction, tx_from_any
|
||||
from electrum.util import (log_exceptions, OldTaskGroup, ca_path, trigger_callback, event_listener,
|
||||
make_aiohttp_proxy_connector)
|
||||
from electrum.util import (
|
||||
log_exceptions, OldTaskGroup, ca_path, trigger_callback, event_listener, json_decode, make_aiohttp_proxy_connector
|
||||
)
|
||||
from electrum.wallet import Multisig_Wallet
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -165,11 +167,11 @@ class CosignerWallet(Logger):
|
||||
yield manager
|
||||
|
||||
@log_exceptions
|
||||
async def send_direct_messages(self, messages: List[Tuple[str, str]]):
|
||||
async def send_direct_messages(self, messages: List[Tuple[str, dict]]):
|
||||
our_private_key: PrivateKey = aionostr.key.PrivateKey(bytes.fromhex(self.nostr_privkey))
|
||||
async with self.nostr_manager() as manager:
|
||||
for pubkey, msg in messages:
|
||||
encrypted_msg: str = our_private_key.encrypt_message(msg, pubkey)
|
||||
encrypted_msg: str = our_private_key.encrypt_message(json.dumps(msg), pubkey)
|
||||
eid = await aionostr._add_event(
|
||||
manager,
|
||||
kind=NOSTR_EVENT_KIND,
|
||||
@@ -209,13 +211,16 @@ class CosignerWallet(Logger):
|
||||
self.known_events[event.id] = now()
|
||||
continue
|
||||
try:
|
||||
tx = tx_from_any(message)
|
||||
message = json_decode(message)
|
||||
tx_hex = message.get('tx')
|
||||
label = message.get('label', '')
|
||||
tx = tx_from_any(tx_hex)
|
||||
except Exception as e:
|
||||
self.logger.info(_("Unable to deserialize the transaction:") + "\n" + str(e))
|
||||
self.known_events[event.id] = now()
|
||||
continue
|
||||
self.logger.info(f"received PSBT from {event.pubkey}")
|
||||
trigger_callback('psbt_nostr_received', self.wallet, event.pubkey, event.id, tx)
|
||||
trigger_callback('psbt_nostr_received', self.wallet, event.pubkey, event.id, tx, label)
|
||||
await self.pending.wait()
|
||||
self.pending.clear()
|
||||
|
||||
@@ -245,25 +250,34 @@ class CosignerWallet(Logger):
|
||||
self.known_events[event_id] = now()
|
||||
self.pending.set()
|
||||
|
||||
def prepare_messages(self, tx: Union[Transaction, PartialTransaction]) -> List[Tuple[str, str]]:
|
||||
def prepare_messages(self, tx: Union[Transaction, PartialTransaction], label: str = None) -> List[Tuple[str, dict]]:
|
||||
messages = []
|
||||
for xpub, pubkey in self.cosigner_list:
|
||||
if not self.cosigner_can_sign(tx, xpub):
|
||||
continue
|
||||
raw_tx_bytes = tx.serialize_as_bytes()
|
||||
messages.append((pubkey, raw_tx_bytes.hex()))
|
||||
payload = {'tx': tx.serialize_as_bytes().hex()}
|
||||
if label:
|
||||
payload['label'] = label
|
||||
messages.append((pubkey, payload))
|
||||
return messages
|
||||
|
||||
def send_psbt(self, tx: Union[Transaction, PartialTransaction]):
|
||||
self.do_send(self.prepare_messages(tx), tx.txid())
|
||||
def send_psbt(self, tx: Union[Transaction, PartialTransaction], label: str):
|
||||
self.do_send(self.prepare_messages(tx, label), tx.txid())
|
||||
|
||||
def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None):
|
||||
def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def on_receive(self, pubkey, event_id, tx):
|
||||
def on_receive(self, pubkey, event_id, tx, label: str):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_transaction_to_wallet(self, tx, *, on_failure=None, on_success=None):
|
||||
def add_transaction_to_wallet(
|
||||
self,
|
||||
tx: Union['Transaction', 'PartialTransaction'],
|
||||
*,
|
||||
label: str = None,
|
||||
on_failure: Callable = None,
|
||||
on_success: Callable = None
|
||||
) -> None:
|
||||
try:
|
||||
# TODO: adding tx should be handled more gracefully here:
|
||||
# 1) don't replace tx with same tx with less signatures
|
||||
@@ -272,6 +286,8 @@ class CosignerWallet(Logger):
|
||||
if not self.wallet.adb.add_transaction(tx):
|
||||
# TODO: instead of bool return value, we could use specific fail reason exceptions here
|
||||
raise Exception('transaction was not added')
|
||||
if label:
|
||||
self.wallet.set_label(tx.txid(), label)
|
||||
except Exception as e:
|
||||
if on_failure:
|
||||
on_failure(str(e))
|
||||
|
||||
@@ -50,7 +50,7 @@ class QReceiveSignalObject(QObject):
|
||||
QObject.__init__(self)
|
||||
self._plugin = plugin
|
||||
|
||||
cosignerReceivedPsbt = pyqtSignal(str, str, str)
|
||||
cosignerReceivedPsbt = pyqtSignal(str, str, str, str)
|
||||
sendPsbtFailed = pyqtSignal(str, arguments=['reason'])
|
||||
sendPsbtSuccess = pyqtSignal()
|
||||
|
||||
@@ -66,11 +66,12 @@ class QReceiveSignalObject(QObject):
|
||||
return cosigner_wallet.can_send_psbt(tx_from_any(tx, deserialize=True))
|
||||
|
||||
@pyqtSlot(QEWallet, str)
|
||||
def sendPsbt(self, wallet: 'QEWallet', tx: str):
|
||||
@pyqtSlot(QEWallet, str, str)
|
||||
def sendPsbt(self, wallet: 'QEWallet', tx: str, label: str = None):
|
||||
cosigner_wallet = self._plugin.cosigner_wallets.get(wallet.wallet)
|
||||
if not cosigner_wallet:
|
||||
return
|
||||
cosigner_wallet.send_psbt(tx_from_any(tx, deserialize=True))
|
||||
cosigner_wallet.send_psbt(tx_from_any(tx, deserialize=True), label)
|
||||
|
||||
@pyqtSlot(QEWallet, str)
|
||||
def acceptPsbt(self, wallet: 'QEWallet', event_id: str):
|
||||
@@ -126,20 +127,20 @@ class QmlCosignerWallet(EventListener, CosignerWallet):
|
||||
self.user_prompt_cooldown = None
|
||||
|
||||
@event_listener
|
||||
def on_event_psbt_nostr_received(self, wallet, pubkey, event_id, tx: 'PartialTransaction'):
|
||||
def on_event_psbt_nostr_received(self, wallet, pubkey, event_id, tx: 'PartialTransaction', label: str):
|
||||
if self.wallet == wallet:
|
||||
self.tx = tx
|
||||
if not (self.user_prompt_cooldown and self.user_prompt_cooldown > now()):
|
||||
self.plugin.so.cosignerReceivedPsbt.emit(pubkey, event_id, tx.serialize())
|
||||
self.plugin.so.cosignerReceivedPsbt.emit(pubkey, event_id, tx.serialize(), label)
|
||||
else:
|
||||
self.mark_pending_event_rcvd(event_id)
|
||||
self.add_transaction_to_wallet(self.tx, on_failure=self.on_add_fail)
|
||||
self.add_transaction_to_wallet(self.tx, label=label, on_failure=self.on_add_fail)
|
||||
|
||||
def close(self):
|
||||
super().close()
|
||||
self.unregister_callbacks()
|
||||
|
||||
def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None):
|
||||
def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None):
|
||||
if not messages:
|
||||
return
|
||||
coro = self.send_direct_messages(messages)
|
||||
|
||||
@@ -7,13 +7,16 @@ import "../../../gui/qml/components/controls"
|
||||
Item {
|
||||
Connections {
|
||||
target: AppController ? AppController.plugin('psbt_nostr') : null
|
||||
function onCosignerReceivedPsbt(pubkey, event, tx) {
|
||||
function onCosignerReceivedPsbt(pubkey, event, tx, label) {
|
||||
var dialog = app.messageDialog.createObject(app, {
|
||||
text: [
|
||||
qsTr('A transaction was received from your cosigner.'),
|
||||
label
|
||||
? qsTr('A transaction was received from your cosigner with label: <br/><br/><b>%1</b>').arg(label)
|
||||
: qsTr('A transaction was received from your cosigner.'),
|
||||
qsTr('Do you want to open it now?')
|
||||
].join('\n'),
|
||||
yesno: true
|
||||
].join('<br/><br/>'),
|
||||
yesno: true,
|
||||
richText: true
|
||||
})
|
||||
dialog.accepted.connect(function () {
|
||||
var page = app.stack.push(Qt.resolvedUrl('../../../gui/qml/components/TxDetails.qml'), {
|
||||
@@ -40,16 +43,24 @@ Item {
|
||||
onClicked: {
|
||||
console.log('about to psbt nostr send')
|
||||
psbt_nostr_send_button.enabled = false
|
||||
AppController.plugin('psbt_nostr').sendPsbt(Daemon.currentWallet, dialog.text)
|
||||
AppController.plugin('psbt_nostr').sendPsbt(Daemon.currentWallet, dialog.text, dialog.tx_label)
|
||||
}
|
||||
Connections {
|
||||
target: AppController ? AppController.plugin('psbt_nostr') : null
|
||||
function onSendPsbtSuccess() {
|
||||
dialog.close()
|
||||
var msgdialog = app.messageDialog.createObject(app, {
|
||||
text: qsTr('PSBT sent successfully')
|
||||
})
|
||||
msgdialog.open()
|
||||
}
|
||||
function onSendPsbtFailed(message) {
|
||||
psbt_nostr_send_button.enabled = true
|
||||
var dialog = app.messageDialog.createObject(app, {
|
||||
text: qsTr('Sending PSBT to co-signer failed:\n%1').arg(message)
|
||||
var msgdialog = app.messageDialog.createObject(app, {
|
||||
text: qsTr('Sending PSBT to co-signer failed:\n%1').arg(message),
|
||||
iconSource: Qt.resolvedUrl('../../../gui/icons/warning.png')
|
||||
})
|
||||
dialog.open()
|
||||
msgdialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
# SOFTWARE.
|
||||
import asyncio
|
||||
from functools import partial
|
||||
from typing import TYPE_CHECKING, List, Tuple, Optional
|
||||
from typing import TYPE_CHECKING, List, Tuple, Optional, Union
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
from PyQt6.QtWidgets import QPushButton, QMessageBox
|
||||
@@ -39,11 +39,12 @@ from electrum.gui.qt.util import read_QIcon_from_bytes
|
||||
from .psbt_nostr import PsbtNostrPlugin, CosignerWallet
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.transaction import Transaction, PartialTransaction
|
||||
from electrum.gui.qt.main_window import ElectrumWindow
|
||||
|
||||
|
||||
class QReceiveSignalObject(QObject):
|
||||
cosignerReceivedPsbt = pyqtSignal(str, str, object)
|
||||
cosignerReceivedPsbt = pyqtSignal(str, str, object, str)
|
||||
|
||||
|
||||
class Plugin(PsbtNostrPlugin):
|
||||
@@ -71,7 +72,7 @@ class Plugin(PsbtNostrPlugin):
|
||||
d.cosigner_send_button = b = QPushButton(_("Send to cosigner"))
|
||||
icon = read_QIcon_from_bytes(self.read_file("nostr_multisig.png"))
|
||||
b.setIcon(icon)
|
||||
b.clicked.connect(lambda: cw.send_to_cosigners(d.tx))
|
||||
b.clicked.connect(lambda: cw.send_to_cosigners(d.tx, d.desc))
|
||||
d.buttons.insert(0, b)
|
||||
b.setVisible(False)
|
||||
|
||||
@@ -100,11 +101,11 @@ class QtCosignerWallet(EventListener, CosignerWallet):
|
||||
if self.wallet == wallet:
|
||||
self.obj.cosignerReceivedPsbt.emit(*args) # put on UI thread via signal
|
||||
|
||||
def send_to_cosigners(self, tx):
|
||||
self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail)
|
||||
self.send_psbt(tx)
|
||||
def send_to_cosigners(self, tx: Union['Transaction', 'PartialTransaction'], label: str):
|
||||
self.add_transaction_to_wallet(tx, label=label, on_failure=self.on_add_fail)
|
||||
self.send_psbt(tx, label)
|
||||
|
||||
def do_send(self, messages: List[Tuple[str, str]], txid: Optional[str] = None):
|
||||
def do_send(self, messages: List[Tuple[str, dict]], txid: Optional[str] = None):
|
||||
if not messages:
|
||||
return
|
||||
coro = self.send_direct_messages(messages)
|
||||
@@ -122,21 +123,26 @@ class QtCosignerWallet(EventListener, CosignerWallet):
|
||||
self.window.show_message(
|
||||
_("Your transaction was sent to your cosigners via Nostr.") + '\n\n' + txid)
|
||||
|
||||
def on_receive(self, pubkey, event_id, tx):
|
||||
msg = _("A transaction was received from your cosigner ({}).").format(str(event_id)[0:8]) + '\n' + \
|
||||
_("Do you want to open it now?")
|
||||
result = self.window.show_message(msg, icon=QMessageBox.Icon.Question, buttons=[
|
||||
def on_receive(self, pubkey, event_id, tx, label):
|
||||
msg = '<br/>'.join([
|
||||
_("A transaction was received from your cosigner.") if not label else
|
||||
_("A transaction was received from your cosigner with label: <br/><big>{}</big><br/>").format(label),
|
||||
_("Do you want to open it now?")
|
||||
])
|
||||
result = self.window.show_message(msg, rich_text=True, icon=QMessageBox.Icon.Question, buttons=[
|
||||
QMessageBox.StandardButton.Open,
|
||||
(QPushButton('Discard'), QMessageBox.ButtonRole.DestructiveRole, 100),
|
||||
(QPushButton('Save to wallet'), QMessageBox.ButtonRole.AcceptRole, 101)]
|
||||
)
|
||||
if result == QMessageBox.StandardButton.Open:
|
||||
if label:
|
||||
self.wallet.set_label(tx.txid(), label)
|
||||
show_transaction(tx, parent=self.window, prompt_if_unsaved=True, on_closed=partial(self.on_tx_dialog_closed, event_id))
|
||||
else:
|
||||
self.mark_pending_event_rcvd(event_id)
|
||||
if result == 100: # Discard
|
||||
return
|
||||
self.add_transaction_to_wallet(tx, on_failure=self.on_add_fail)
|
||||
self.add_transaction_to_wallet(tx, label=label, on_failure=self.on_add_fail)
|
||||
self.window.update_tabs()
|
||||
|
||||
def on_tx_dialog_closed(self, event_id):
|
||||
|
||||
Reference in New Issue
Block a user