EventListener class to handle callbacks
and QtEventListener for Qt
This commit is contained in:
@@ -37,6 +37,7 @@ from .verifier import SPV
|
||||
from .blockchain import hash_header, Blockchain
|
||||
from .i18n import _
|
||||
from .logging import Logger
|
||||
from .util import EventListener, event_listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .network import Network
|
||||
@@ -57,7 +58,7 @@ class HistoryItem(NamedTuple):
|
||||
balance: int
|
||||
|
||||
|
||||
class AddressSynchronizer(Logger):
|
||||
class AddressSynchronizer(Logger, EventListener):
|
||||
""" address database """
|
||||
|
||||
network: Optional['Network']
|
||||
@@ -181,9 +182,10 @@ class AddressSynchronizer(Logger):
|
||||
self.synchronizer = Synchronizer(self)
|
||||
self.verifier = SPV(self.network, self)
|
||||
self.asyncio_loop = network.asyncio_loop
|
||||
util.register_callback(self.on_blockchain_updated, ['blockchain_updated'])
|
||||
self.register_callbacks()
|
||||
|
||||
def on_blockchain_updated(self, event, *args):
|
||||
@event_listener
|
||||
def on_event_blockchain_updated(self, *args):
|
||||
self._get_balance_cache = {} # invalidate cache
|
||||
|
||||
async def stop(self):
|
||||
@@ -197,7 +199,7 @@ class AddressSynchronizer(Logger):
|
||||
finally: # even if we get cancelled
|
||||
self.synchronizer = None
|
||||
self.verifier = None
|
||||
util.unregister_callback(self.on_blockchain_updated)
|
||||
self.unregister_callbacks()
|
||||
self.db.put('stored_height', self.get_local_height())
|
||||
|
||||
def add_address(self, address):
|
||||
|
||||
@@ -19,6 +19,7 @@ from electrum import util
|
||||
from electrum.util import (profiler, InvalidPassword, send_exception_to_crash_reporter,
|
||||
format_satoshis, format_satoshis_plain, format_fee_satoshis,
|
||||
maybe_extract_bolt11_invoice, parse_max_spend)
|
||||
from electrum.util import EventListener, event_listener
|
||||
from electrum.invoices import PR_PAID, PR_FAILED, Invoice
|
||||
from electrum import blockchain
|
||||
from electrum.network import Network, TxBroadcastError, BestEffortRequestFailed
|
||||
@@ -99,7 +100,7 @@ if TYPE_CHECKING:
|
||||
from electrum.paymentrequest import PaymentRequest
|
||||
|
||||
|
||||
class ElectrumWindow(App, Logger):
|
||||
class ElectrumWindow(App, Logger, EventListener):
|
||||
|
||||
electrum_config = ObjectProperty(None)
|
||||
language = StringProperty('en')
|
||||
@@ -252,21 +253,25 @@ class ElectrumWindow(App, Logger):
|
||||
if self.history_screen:
|
||||
self.history_screen.update()
|
||||
|
||||
def on_quotes(self, d):
|
||||
@event_listener
|
||||
def on_event_on_quotes(self):
|
||||
self.logger.info("on_quotes")
|
||||
self._trigger_update_status()
|
||||
self._trigger_update_history()
|
||||
|
||||
def on_history(self, d):
|
||||
@event_listener
|
||||
def on_event_on_history(self):
|
||||
self.logger.info("on_history")
|
||||
if self.wallet:
|
||||
self.wallet.clear_coin_price_cache()
|
||||
self._trigger_update_history()
|
||||
|
||||
def on_fee_histogram(self, *args):
|
||||
@event_listener
|
||||
def on_event_fee_histogram(self, *args):
|
||||
self._trigger_update_history()
|
||||
|
||||
def on_request_status(self, event, wallet, key, status):
|
||||
@event_listener
|
||||
def on_event_request_status(self, wallet, key, status):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
req = self.wallet.receive_requests.get(key)
|
||||
@@ -283,7 +288,8 @@ class ElectrumWindow(App, Logger):
|
||||
self.show_info(_('Payment Received') + '\n' + key)
|
||||
self._trigger_update_history()
|
||||
|
||||
def on_invoice_status(self, event, wallet, key):
|
||||
@event_listener
|
||||
def on_event_invoice_status(self, wallet, key):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
req = self.wallet.get_invoice(key)
|
||||
@@ -299,14 +305,16 @@ class ElectrumWindow(App, Logger):
|
||||
if self.invoice_popup and self.invoice_popup.key == key:
|
||||
self.invoice_popup.update_status()
|
||||
|
||||
def on_payment_succeeded(self, event, wallet, key):
|
||||
@event_listener
|
||||
def on_event_payment_succeeded(self, wallet, key):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
description = self.wallet.get_label(key)
|
||||
self.show_info(_('Payment succeeded') + '\n\n' + description)
|
||||
self._trigger_update_history()
|
||||
|
||||
def on_payment_failed(self, event, wallet, key, reason):
|
||||
@event_listener
|
||||
def on_event_payment_failed(self, wallet, key, reason):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
self.show_info(_('Payment failed') + '\n\n' + reason)
|
||||
@@ -647,25 +655,7 @@ class ElectrumWindow(App, Logger):
|
||||
mactivity = PythonActivity.mActivity
|
||||
self.on_new_intent(mactivity.getIntent())
|
||||
activity.bind(on_new_intent=self.on_new_intent)
|
||||
# connect callbacks
|
||||
if self.network:
|
||||
interests = ['wallet_updated', 'network_updated', 'blockchain_updated',
|
||||
'status', 'new_transaction', 'verified']
|
||||
util.register_callback(self.on_network_event, interests)
|
||||
util.register_callback(self.on_fee, ['fee'])
|
||||
util.register_callback(self.on_fee_histogram, ['fee_histogram'])
|
||||
util.register_callback(self.on_quotes, ['on_quotes'])
|
||||
util.register_callback(self.on_history, ['on_history'])
|
||||
util.register_callback(self.on_channels, ['channels_updated'])
|
||||
util.register_callback(self.on_channel, ['channel'])
|
||||
util.register_callback(self.on_invoice_status, ['invoice_status'])
|
||||
util.register_callback(self.on_request_status, ['request_status'])
|
||||
util.register_callback(self.on_payment_failed, ['payment_failed'])
|
||||
util.register_callback(self.on_payment_succeeded, ['payment_succeeded'])
|
||||
util.register_callback(self.on_channel_db, ['channel_db'])
|
||||
util.register_callback(self.set_num_peers, ['gossip_peers'])
|
||||
util.register_callback(self.set_unknown_channels, ['unknown_channels'])
|
||||
|
||||
self.register_callbacks()
|
||||
if self.network and self.electrum_config.get('auto_connect') is None:
|
||||
self.popup_dialog("first_screen")
|
||||
# load_wallet_on_start will be called later, after initial network setup is completed
|
||||
@@ -677,14 +667,17 @@ class ElectrumWindow(App, Logger):
|
||||
if uri:
|
||||
self.set_URI(uri)
|
||||
|
||||
def on_channel_db(self, event, num_nodes, num_channels, num_policies):
|
||||
@event_listener
|
||||
def on_event_channel_db(self, num_nodes, num_channels, num_policies):
|
||||
self.lightning_gossip_num_nodes = num_nodes
|
||||
self.lightning_gossip_num_channels = num_channels
|
||||
|
||||
def set_num_peers(self, event, num_peers):
|
||||
@event_listener
|
||||
def on_event_gossip_peers(self, num_peers):
|
||||
self.lightning_gossip_num_peers = num_peers
|
||||
|
||||
def set_unknown_channels(self, event, unknown):
|
||||
@event_listener
|
||||
def on_event_unknown_channels(self, unknown):
|
||||
self.lightning_gossip_num_queries = unknown
|
||||
|
||||
def get_wallet_path(self):
|
||||
@@ -818,11 +811,13 @@ class ElectrumWindow(App, Logger):
|
||||
delete_gossip)
|
||||
d.open()
|
||||
|
||||
def on_channel(self, evt, wallet, chan):
|
||||
@event_listener
|
||||
def on_event_channel(self, wallet, chan):
|
||||
if self._channels_dialog:
|
||||
Clock.schedule_once(lambda dt: self._channels_dialog.update())
|
||||
|
||||
def on_channels(self, evt, wallet):
|
||||
@event_listener
|
||||
def on_event_channels(self, wallet):
|
||||
if self._channels_dialog:
|
||||
Clock.schedule_once(lambda dt: self._channels_dialog.update())
|
||||
|
||||
@@ -904,23 +899,32 @@ class ElectrumWindow(App, Logger):
|
||||
self.proxy_config = net_params.proxy or {}
|
||||
self.update_proxy_str(self.proxy_config)
|
||||
|
||||
def on_network_event(self, event, *args):
|
||||
self.logger.info('network event: '+ event)
|
||||
if event == 'network_updated':
|
||||
self._trigger_update_interfaces()
|
||||
self._trigger_update_status()
|
||||
elif event == 'wallet_updated':
|
||||
self._trigger_update_wallet()
|
||||
self._trigger_update_status()
|
||||
elif event == 'blockchain_updated':
|
||||
# to update number of confirmations in history
|
||||
self._trigger_update_wallet()
|
||||
elif event == 'status':
|
||||
self._trigger_update_status()
|
||||
elif event == 'new_transaction':
|
||||
self._trigger_update_wallet()
|
||||
elif event == 'verified':
|
||||
self._trigger_update_wallet()
|
||||
@event_listener
|
||||
def on_event_network_updated(self):
|
||||
self._trigger_update_interfaces()
|
||||
self._trigger_update_status()
|
||||
|
||||
@event_listener
|
||||
def on_event_wallet_updated(self, *args):
|
||||
self._trigger_update_wallet()
|
||||
self._trigger_update_status()
|
||||
|
||||
@event_listener
|
||||
def on_event_blockchain_updated(self, *args):
|
||||
# to update number of confirmations in history
|
||||
self._trigger_update_wallet()
|
||||
|
||||
@event_listener
|
||||
def on_event_status(self, *args):
|
||||
self._trigger_update_status()
|
||||
|
||||
@event_listener
|
||||
def on_event_new_transaction(self, *args):
|
||||
self._trigger_update_wallet()
|
||||
|
||||
@event_listener
|
||||
def on_event_verified(self, *args):
|
||||
self._trigger_update_wallet()
|
||||
|
||||
@profiler
|
||||
def load_wallet(self, wallet: 'Abstract_Wallet'):
|
||||
@@ -1265,7 +1269,8 @@ class ElectrumWindow(App, Logger):
|
||||
target, tooltip, dyn = self.electrum_config.get_fee_target()
|
||||
self.fee_status = target
|
||||
|
||||
def on_fee(self, event, *arg):
|
||||
@event_listener
|
||||
def on_event_fee(self, *arg):
|
||||
self.set_fee_status()
|
||||
|
||||
def protected(self, msg, f, args):
|
||||
|
||||
@@ -102,9 +102,6 @@ class QElectrumApplication(QApplication):
|
||||
alias_received_signal = pyqtSignal()
|
||||
|
||||
|
||||
class QNetworkUpdatedSignalObject(QObject):
|
||||
network_updated_signal = pyqtSignal(str, object)
|
||||
|
||||
|
||||
class ElectrumGui(BaseElectrumGui, Logger):
|
||||
|
||||
@@ -142,7 +139,6 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
self.network_dialog = None
|
||||
self.lightning_dialog = None
|
||||
self.watchtower_dialog = None
|
||||
self.network_updated_signal_obj = QNetworkUpdatedSignalObject()
|
||||
self._num_wizards_in_progress = 0
|
||||
self._num_wizards_lock = threading.Lock()
|
||||
self.dark_icon = self.config.get("dark_icon", False)
|
||||
@@ -251,7 +247,6 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
self.network_dialog.close()
|
||||
self.network_dialog.clean_up()
|
||||
self.network_dialog = None
|
||||
self.network_updated_signal_obj = None
|
||||
if self.lightning_dialog:
|
||||
self.lightning_dialog.close()
|
||||
self.lightning_dialog = None
|
||||
@@ -298,14 +293,13 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
|
||||
def show_network_dialog(self):
|
||||
if self.network_dialog:
|
||||
self.network_dialog.on_update()
|
||||
self.network_dialog.on_event_network_updated()
|
||||
self.network_dialog.show()
|
||||
self.network_dialog.raise_()
|
||||
return
|
||||
self.network_dialog = NetworkDialog(
|
||||
network=self.daemon.network,
|
||||
config=self.config,
|
||||
network_updated_signal_obj=self.network_updated_signal_obj)
|
||||
config=self.config)
|
||||
self.network_dialog.show()
|
||||
|
||||
def _create_window_for_wallet(self, wallet):
|
||||
|
||||
@@ -5,7 +5,7 @@ import PyQt5.QtWidgets as QtWidgets
|
||||
import PyQt5.QtCore as QtCore
|
||||
from PyQt5.QtWidgets import QLabel, QLineEdit, QHBoxLayout
|
||||
|
||||
from electrum import util
|
||||
from electrum.util import EventListener
|
||||
from electrum.i18n import _
|
||||
from electrum.util import bh2u, format_time
|
||||
from electrum.lnutil import format_short_channel_id, LOCAL, REMOTE, UpdateAddHtlc, Direction
|
||||
@@ -15,6 +15,7 @@ from electrum.bitcoin import COIN
|
||||
from electrum.wallet import Abstract_Wallet
|
||||
|
||||
from .util import Buttons, CloseButton, ButtonsLineEdit, MessageBoxMixin, WWLabel
|
||||
from .util import QtEventListener, qt_event_listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .main_window import ElectrumWindow
|
||||
@@ -35,7 +36,8 @@ class LinkedLabel(QtWidgets.QLabel):
|
||||
self.linkActivated.connect(on_clicked)
|
||||
|
||||
|
||||
class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin):
|
||||
class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin, QtEventListener):
|
||||
|
||||
def make_htlc_item(self, i: UpdateAddHtlc, direction: Direction) -> HTLCItem:
|
||||
it = HTLCItem(_('Sent HTLC with ID {}' if Direction.SENT == direction else 'Received HTLC with ID {}').format(i.htlc_id))
|
||||
it.appendRow([HTLCItem(_('Amount')),HTLCItem(self.format_msat(i.amount_msat))])
|
||||
@@ -83,35 +85,28 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin):
|
||||
dest_mapping = self.keyname_rows[to]
|
||||
dest_mapping[payment_hash] = len(dest_mapping)
|
||||
|
||||
htlc_fulfilled = QtCore.pyqtSignal(str, bytes, Channel, int)
|
||||
htlc_failed = QtCore.pyqtSignal(str, bytes, Channel, int)
|
||||
htlc_added = QtCore.pyqtSignal(str, Channel, UpdateAddHtlc, Direction)
|
||||
state_changed = QtCore.pyqtSignal(str, Abstract_Wallet, AbstractChannel)
|
||||
|
||||
@QtCore.pyqtSlot(str, Abstract_Wallet, AbstractChannel)
|
||||
def do_state_changed(self, wallet, chan):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
@qt_event_listener
|
||||
def on_event_channel(self, wallet, chan):
|
||||
if chan == self.chan:
|
||||
self.update()
|
||||
|
||||
@QtCore.pyqtSlot(str, Channel, UpdateAddHtlc, Direction)
|
||||
def on_htlc_added(self, evtname, chan, htlc, direction):
|
||||
@qt_event_listener
|
||||
def on_event_htlc_added(self, chan, htlc, direction):
|
||||
if chan != self.chan:
|
||||
return
|
||||
mapping = self.keyname_rows['inflight']
|
||||
mapping[htlc.payment_hash] = len(mapping)
|
||||
self.folders['inflight'].appendRow(self.make_htlc_item(htlc, direction))
|
||||
|
||||
@QtCore.pyqtSlot(str, bytes, Channel, int)
|
||||
def on_htlc_fulfilled(self, evtname, payment_hash, chan, htlc_id):
|
||||
@qt_event_listener
|
||||
def on_event_htlc_fulfilled(self, payment_hash, chan, htlc_id):
|
||||
if chan.channel_id != self.chan.channel_id:
|
||||
return
|
||||
self.move('inflight', 'settled', payment_hash)
|
||||
self.update()
|
||||
|
||||
@QtCore.pyqtSlot(str, bytes, Channel, int)
|
||||
def on_htlc_failed(self, evtname, payment_hash, chan, htlc_id):
|
||||
@qt_event_listener
|
||||
def on_event_htlc_failed(self, payment_hash, chan, htlc_id):
|
||||
if chan.channel_id != self.chan.channel_id:
|
||||
return
|
||||
self.move('inflight', 'failed', payment_hash)
|
||||
@@ -143,17 +138,8 @@ class ChannelDetailsDialog(QtWidgets.QDialog, MessageBoxMixin):
|
||||
self.format_msat = lambda msat: window.format_amount_and_units(msat / 1000)
|
||||
self.format_sat = lambda sat: window.format_amount_and_units(sat)
|
||||
|
||||
# connect signals with slots
|
||||
self.htlc_fulfilled.connect(self.on_htlc_fulfilled)
|
||||
self.htlc_failed.connect(self.on_htlc_failed)
|
||||
self.state_changed.connect(self.do_state_changed)
|
||||
self.htlc_added.connect(self.on_htlc_added)
|
||||
|
||||
# register callbacks for updating
|
||||
util.register_callback(self.htlc_fulfilled.emit, ['htlc_fulfilled'])
|
||||
util.register_callback(self.htlc_failed.emit, ['htlc_failed'])
|
||||
util.register_callback(self.htlc_added.emit, ['htlc_added'])
|
||||
util.register_callback(self.state_changed.emit, ['channel'])
|
||||
self.register_callbacks()
|
||||
|
||||
# set attributes of QDialog
|
||||
self.setWindowTitle(_('Channel Details'))
|
||||
|
||||
@@ -27,16 +27,16 @@ from typing import TYPE_CHECKING
|
||||
|
||||
from PyQt5.QtWidgets import (QDialog, QLabel, QVBoxLayout, QPushButton)
|
||||
|
||||
from electrum import util
|
||||
from electrum.i18n import _
|
||||
|
||||
from .util import Buttons
|
||||
from .util import QtEventListener, qt_event_listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import ElectrumGui
|
||||
|
||||
|
||||
class LightningDialog(QDialog):
|
||||
class LightningDialog(QDialog, QtEventListener):
|
||||
|
||||
def __init__(self, gui_object: 'ElectrumGui'):
|
||||
QDialog.__init__(self)
|
||||
@@ -59,24 +59,25 @@ class LightningDialog(QDialog):
|
||||
b = QPushButton(_('Close'))
|
||||
b.clicked.connect(self.close)
|
||||
vbox.addLayout(Buttons(b))
|
||||
util.register_callback(self.on_channel_db, ['channel_db'])
|
||||
util.register_callback(self.set_num_peers, ['gossip_peers'])
|
||||
util.register_callback(self.set_unknown_channels, ['unknown_channels'])
|
||||
self.register_callbacks()
|
||||
self.network.channel_db.update_counts() # trigger callback
|
||||
if self.network.lngossip:
|
||||
self.set_num_peers('', self.network.lngossip.num_peers())
|
||||
self.set_unknown_channels('', len(self.network.lngossip.unknown_ids))
|
||||
self.on_event_gossip_peers(self.network.lngossip.num_peers())
|
||||
self.on_event_unknown_channels(len(self.network.lngossip.unknown_ids))
|
||||
else:
|
||||
self.num_peers.setText(_('Lightning gossip not active.'))
|
||||
|
||||
def on_channel_db(self, event, num_nodes, num_channels, num_policies):
|
||||
@qt_event_listener
|
||||
def on_event_channel_db(self, num_nodes, num_channels, num_policies):
|
||||
self.num_nodes.setText(_('{} nodes').format(num_nodes))
|
||||
self.num_channels.setText(_('{} channels').format(num_channels))
|
||||
|
||||
def set_num_peers(self, event, num_peers):
|
||||
@qt_event_listener
|
||||
def on_event_gossip_peers(self, num_peers):
|
||||
self.num_peers.setText(_('Connected to {} peers').format(num_peers))
|
||||
|
||||
def set_unknown_channels(self, event, unknown):
|
||||
@qt_event_listener
|
||||
def on_event_unknown_channels(self, unknown):
|
||||
self.status.setText(_('Requesting {} channels...').format(unknown) if unknown else '')
|
||||
|
||||
def is_hidden(self):
|
||||
@@ -93,5 +94,6 @@ class LightningDialog(QDialog):
|
||||
self.raise_()
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.unregister_callbacks()
|
||||
self.gui_object.lightning_dialog = None
|
||||
event.accept()
|
||||
|
||||
@@ -193,12 +193,12 @@ def protected(func):
|
||||
return func(self, *args, **kwargs)
|
||||
return request_password
|
||||
|
||||
from .util import QtEventListener, qt_event_listener, event_listener
|
||||
|
||||
class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger, QtEventListener):
|
||||
|
||||
payment_request_ok_signal = pyqtSignal()
|
||||
payment_request_error_signal = pyqtSignal()
|
||||
network_signal = pyqtSignal(str, object)
|
||||
#ln_payment_attempt_signal = pyqtSignal(str)
|
||||
computing_privkeys_signal = pyqtSignal()
|
||||
show_privkeys_signal = pyqtSignal()
|
||||
@@ -208,7 +208,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
|
||||
def __init__(self, gui_object: 'ElectrumGui', wallet: Abstract_Wallet):
|
||||
QMainWindow.__init__(self)
|
||||
|
||||
self.gui_object = gui_object
|
||||
self.config = config = gui_object.config # type: SimpleConfig
|
||||
self.gui_thread = gui_object.gui_thread
|
||||
@@ -314,21 +313,9 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
self.history_list.setFocus(True)
|
||||
|
||||
# network callbacks
|
||||
if self.network:
|
||||
self.network_signal.connect(self.on_network_qt)
|
||||
interests = ['wallet_updated', 'network_updated', 'blockchain_updated',
|
||||
'new_transaction', 'status',
|
||||
'banner', 'verified', 'fee', 'fee_histogram', 'on_quotes',
|
||||
'on_history', 'channel', 'channels_updated',
|
||||
'payment_failed', 'payment_succeeded',
|
||||
'invoice_status', 'request_status', 'ln_gossip_sync_progress',
|
||||
'cert_mismatch', 'gossip_db_loaded']
|
||||
# To avoid leaking references to "self" that prevent the
|
||||
# window from being GC-ed when closed, callbacks should be
|
||||
# methods of this class only, and specifically not be
|
||||
# partials, lambdas or methods of subobjects. Hence...
|
||||
util.register_callback(self.on_network, interests)
|
||||
# set initial message
|
||||
self.register_callbacks()
|
||||
# banner may already be there
|
||||
if self.network and self.network.banner:
|
||||
self.console.showMessage(self.network.banner)
|
||||
|
||||
# update fee slider in case we missed the callback
|
||||
@@ -463,74 +450,75 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
pass # see #4418
|
||||
self.show_error(repr(e))
|
||||
|
||||
def on_network(self, event, *args):
|
||||
# Handle in GUI thread
|
||||
self.network_signal.emit(event, args)
|
||||
@event_listener
|
||||
def on_event_wallet_updated(self, wallet):
|
||||
if wallet == self.wallet:
|
||||
self.need_update.set()
|
||||
|
||||
def on_network_qt(self, event, args=None):
|
||||
# Handle a network message in the GUI thread
|
||||
# note: all windows get events from all wallets!
|
||||
if event == 'wallet_updated':
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.need_update.set()
|
||||
elif event == 'network_updated':
|
||||
self.gui_object.network_updated_signal_obj.network_updated_signal \
|
||||
.emit(event, args)
|
||||
self.network_signal.emit('status', None)
|
||||
elif event == 'blockchain_updated':
|
||||
# to update number of confirmations in history
|
||||
self.refresh_tabs()
|
||||
elif event == 'new_transaction':
|
||||
wallet, tx = args
|
||||
if wallet == self.wallet:
|
||||
self.tx_notification_queue.put(tx)
|
||||
elif event == 'on_quotes':
|
||||
self.on_fx_quotes()
|
||||
elif event == 'on_history':
|
||||
self.on_fx_history()
|
||||
elif event == 'gossip_db_loaded':
|
||||
self.channels_list.gossip_db_loaded.emit(*args)
|
||||
elif event == 'channels_updated':
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.channels_list.update_rows.emit(*args)
|
||||
elif event == 'channel':
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.channels_list.update_single_row.emit(*args)
|
||||
self.update_status()
|
||||
elif event == 'request_status':
|
||||
self.on_request_status(*args)
|
||||
elif event == 'invoice_status':
|
||||
self.on_invoice_status(*args)
|
||||
elif event == 'payment_succeeded':
|
||||
# sent by lnworker, redundant with invoice_status
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.on_payment_succeeded(*args)
|
||||
elif event == 'payment_failed':
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.on_payment_failed(*args)
|
||||
elif event == 'status':
|
||||
@event_listener
|
||||
def on_event_new_transaction(self, wallet, tx):
|
||||
if wallet == self.wallet:
|
||||
self.tx_notification_queue.put(tx)
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_status(self):
|
||||
self.update_status()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_network_updated(self, *args):
|
||||
self.update_status()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_blockchain_updated(self, *args):
|
||||
# update the number of confirmations in history
|
||||
self.refresh_tabs()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_on_quotes(self, *args):
|
||||
self.on_fx_quotes()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_on_history(self, *args):
|
||||
self.on_fx_history()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_gossip_db_loaded(self, *args):
|
||||
self.channels_list.gossip_db_loaded.emit(*args)
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_channels_updated(self, *args):
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.channels_list.update_rows.emit(*args)
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_channel(self, *args):
|
||||
wallet = args[0]
|
||||
if wallet == self.wallet:
|
||||
self.channels_list.update_single_row.emit(*args)
|
||||
self.update_status()
|
||||
elif event == 'banner':
|
||||
self.console.showMessage(args[0])
|
||||
elif event == 'verified':
|
||||
wallet, tx_hash, tx_mined_status = args
|
||||
if wallet == self.wallet:
|
||||
self.history_model.update_tx_mined_status(tx_hash, tx_mined_status)
|
||||
elif event == 'fee':
|
||||
pass
|
||||
elif event == 'fee_histogram':
|
||||
self.history_model.on_fee_histogram()
|
||||
elif event == 'ln_gossip_sync_progress':
|
||||
self.update_lightning_icon()
|
||||
elif event == 'cert_mismatch':
|
||||
self.show_cert_mismatch_error()
|
||||
else:
|
||||
self.logger.info(f"unexpected network event: {event} {args}")
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_banner(self, *args):
|
||||
self.console.showMessage(args[0])
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_verified(self, *args):
|
||||
wallet, tx_hash, tx_mined_status = args
|
||||
if wallet == self.wallet:
|
||||
self.history_model.update_tx_mined_status(tx_hash, tx_mined_status)
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_fee_histogram(self, *args):
|
||||
self.history_model.on_fee_histogram()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_ln_gossip_sync_progress(self, *args):
|
||||
self.update_lightning_icon()
|
||||
|
||||
@qt_event_listener
|
||||
def on_event_cert_mismatch(self, *args):
|
||||
self.show_cert_mismatch_error()
|
||||
|
||||
def close_wallet(self):
|
||||
if self.wallet:
|
||||
@@ -1821,7 +1809,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
d = SwapDialog(self, is_reverse=is_reverse, recv_amount_sat=recv_amount_sat, channels=channels)
|
||||
return d.run()
|
||||
|
||||
def on_request_status(self, wallet, key, status):
|
||||
@qt_event_listener
|
||||
def on_event_request_status(self, wallet, key, status):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
req = self.wallet.receive_requests.get(key)
|
||||
@@ -1840,7 +1829,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
else:
|
||||
self.request_list.refresh_item(key)
|
||||
|
||||
def on_invoice_status(self, wallet, key):
|
||||
@qt_event_listener
|
||||
def on_event_invoice_status(self, wallet, key):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
invoice = self.wallet.get_invoice(key)
|
||||
@@ -1852,12 +1842,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
else:
|
||||
self.invoice_list.refresh_item(key)
|
||||
|
||||
def on_payment_succeeded(self, wallet, key):
|
||||
@qt_event_listener
|
||||
def on_event_payment_succeeded(self, wallet, key):
|
||||
# sent by lnworker, redundant with invoice_status
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
description = self.wallet.get_label(key)
|
||||
self.notify(_('Payment sent') + '\n\n' + description)
|
||||
self.need_update.set()
|
||||
|
||||
def on_payment_failed(self, wallet, key, reason):
|
||||
@qt_event_listener
|
||||
def on_event_payment_failed(self, wallet, key, reason):
|
||||
if wallet != self.wallet:
|
||||
return
|
||||
invoice = self.wallet.get_invoice(key)
|
||||
if invoice and invoice.is_lightning() and invoice.get_address():
|
||||
if self.question(_('Payment failed') + '\n\n' + reason + '\n\n'+ 'Fallback to onchain payment?'):
|
||||
@@ -3497,7 +3494,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger):
|
||||
self.thread = None
|
||||
for fut in self._coroutines_scheduled.keys():
|
||||
fut.cancel()
|
||||
util.unregister_callback(self.on_network)
|
||||
self.unregister_callbacks()
|
||||
self.config.set_key("is_maximized", self.isMaximized())
|
||||
if not self.isMaximized():
|
||||
g = self.geometry()
|
||||
|
||||
@@ -43,6 +43,7 @@ from electrum.logging import get_logger
|
||||
|
||||
from .util import (Buttons, CloseButton, HelpButton, read_QIcon, char_width_in_lineedit,
|
||||
PasswordLineEdit)
|
||||
from .util import QtEventListener, qt_event_listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from electrum.simple_config import SimpleConfig
|
||||
@@ -53,27 +54,20 @@ _logger = get_logger(__name__)
|
||||
protocol_names = ['TCP', 'SSL']
|
||||
protocol_letters = 'ts'
|
||||
|
||||
class NetworkDialog(QDialog):
|
||||
def __init__(self, *, network: Network, config: 'SimpleConfig', network_updated_signal_obj):
|
||||
class NetworkDialog(QDialog, QtEventListener):
|
||||
def __init__(self, *, network: Network, config: 'SimpleConfig'):
|
||||
QDialog.__init__(self)
|
||||
self.setWindowTitle(_('Network'))
|
||||
self.setMinimumSize(500, 500)
|
||||
self.nlayout = NetworkChoiceLayout(network, config)
|
||||
self.network_updated_signal_obj = network_updated_signal_obj
|
||||
vbox = QVBoxLayout(self)
|
||||
vbox.addLayout(self.nlayout.layout())
|
||||
vbox.addLayout(Buttons(CloseButton(self)))
|
||||
self.network_updated_signal_obj.network_updated_signal.connect(
|
||||
self.on_update)
|
||||
util.register_callback(self.on_network, ['network_updated'])
|
||||
self.register_callbacks()
|
||||
self._cleaned_up = False
|
||||
|
||||
def on_network(self, event, *args):
|
||||
signal_obj = self.network_updated_signal_obj
|
||||
if signal_obj:
|
||||
signal_obj.network_updated_signal.emit(event, args)
|
||||
|
||||
def on_update(self):
|
||||
@qt_event_listener
|
||||
def on_event_network_updated(self):
|
||||
self.nlayout.update()
|
||||
|
||||
def clean_up(self):
|
||||
@@ -81,8 +75,7 @@ class NetworkDialog(QDialog):
|
||||
return
|
||||
self._cleaned_up = True
|
||||
self.nlayout.clean_up()
|
||||
self.network_updated_signal_obj.network_updated_signal.disconnect()
|
||||
self.network_updated_signal_obj = None
|
||||
self.unregister_callbacks()
|
||||
|
||||
|
||||
class NodesListWidget(QTreeWidget):
|
||||
|
||||
@@ -8,7 +8,7 @@ import traceback
|
||||
import os
|
||||
import webbrowser
|
||||
from decimal import Decimal
|
||||
from functools import partial, lru_cache
|
||||
from functools import partial, lru_cache, wraps
|
||||
from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, Union, List, Dict, Any,
|
||||
Sequence, Iterable)
|
||||
|
||||
@@ -1503,6 +1503,34 @@ class VTabWidget(QtWidgets.QTabWidget):
|
||||
return super().resizeEvent(e)
|
||||
|
||||
|
||||
from electrum.util import EventListener, event_listener
|
||||
|
||||
class QtEventListener(EventListener):
|
||||
|
||||
qt_callback_signal = QtCore.pyqtSignal(tuple)
|
||||
|
||||
def register_callbacks(self):
|
||||
self.qt_callback_signal.connect(self.on_qt_callback_signal)
|
||||
EventListener.register_callbacks(self)
|
||||
|
||||
def unregister_callbacks(self):
|
||||
self.qt_callback_signal.disconnect()
|
||||
EventListener.unregister_callbacks(self)
|
||||
|
||||
def on_qt_callback_signal(self, args):
|
||||
func = args[0]
|
||||
return func(self, *args[1:])
|
||||
|
||||
# decorator for members of the QtEventListener class
|
||||
def qt_event_listener(func):
|
||||
assert func.__name__.startswith('on_event_')
|
||||
func._is_event_listener = True
|
||||
@wraps(func)
|
||||
def decorator(self, *args):
|
||||
self.qt_callback_signal.emit( (func,) + args)
|
||||
return decorator
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
t = WaitingDialog(None, 'testing ...', lambda: [time.sleep(1)], lambda x: QMessageBox.information(None, 'done', "done"))
|
||||
|
||||
@@ -12,6 +12,7 @@ import electrum
|
||||
from electrum.gui import BaseElectrumGui
|
||||
from electrum import util
|
||||
from electrum.util import format_satoshis
|
||||
from electrum.util import EventListener, event_listener
|
||||
from electrum.bitcoin import is_address, COIN
|
||||
from electrum.transaction import PartialTxOutput
|
||||
from electrum.wallet import Wallet, Abstract_Wallet
|
||||
@@ -29,7 +30,7 @@ if TYPE_CHECKING:
|
||||
_ = lambda x:x # i18n
|
||||
|
||||
|
||||
class ElectrumGui(BaseElectrumGui):
|
||||
class ElectrumGui(BaseElectrumGui, EventListener):
|
||||
|
||||
def __init__(self, *, config: 'SimpleConfig', daemon: 'Daemon', plugins: 'Plugins'):
|
||||
BaseElectrumGui.__init__(self, config=config, daemon=daemon, plugins=plugins)
|
||||
@@ -74,12 +75,20 @@ class ElectrumGui(BaseElectrumGui):
|
||||
self.history = None
|
||||
self.txid = []
|
||||
|
||||
util.register_callback(self.update, ['wallet_updated', 'network_updated'])
|
||||
self.register_callbacks()
|
||||
|
||||
self.tab_names = [_("History"), _("Send"), _("Receive"), _("Addresses"), _("Contacts"), _("Banner")]
|
||||
self.num_tabs = len(self.tab_names)
|
||||
|
||||
|
||||
@event_listener
|
||||
def on_event_wallet_updated(self, wallet):
|
||||
self.update()
|
||||
|
||||
@event_listener
|
||||
def on_event_network_updated(self):
|
||||
self.update()
|
||||
|
||||
def set_cursor(self, x):
|
||||
try:
|
||||
curses.curs_set(x)
|
||||
@@ -101,7 +110,7 @@ class ElectrumGui(BaseElectrumGui):
|
||||
self.set_cursor(0)
|
||||
return s
|
||||
|
||||
def update(self, event, *args):
|
||||
def update(self):
|
||||
self.update_history()
|
||||
if self.tab == 0:
|
||||
self.print_history()
|
||||
|
||||
@@ -135,8 +135,9 @@ class SweepStore(SqlDB):
|
||||
return [(r[0], r[1]) for r in c.fetchall()]
|
||||
|
||||
|
||||
from .util import EventListener, event_listener
|
||||
|
||||
class LNWatcher(Logger):
|
||||
class LNWatcher(Logger, EventListener):
|
||||
|
||||
LOGGING_SHORTCUT = 'W'
|
||||
|
||||
@@ -147,23 +148,12 @@ class LNWatcher(Logger):
|
||||
self.config = network.config
|
||||
self.callbacks = {} # address -> lambda: coroutine
|
||||
self.network = network
|
||||
|
||||
util.register_callback(self.on_fee, ['fee'])
|
||||
util.register_callback(self.on_blockchain_updated, ['blockchain_updated'])
|
||||
util.register_callback(self.on_network_updated, ['network_updated'])
|
||||
util.register_callback(self.on_adb_added_verified_tx, ['adb_added_verified_tx'])
|
||||
util.register_callback(self.on_adb_set_up_to_date, ['adb_set_up_to_date'])
|
||||
|
||||
self.register_callbacks()
|
||||
# status gets populated when we run
|
||||
self.channel_status = {}
|
||||
|
||||
|
||||
async def stop(self):
|
||||
util.unregister_callback(self.on_fee)
|
||||
util.unregister_callback(self.on_blockchain_updated)
|
||||
util.unregister_callback(self.on_network_updated)
|
||||
util.unregister_callback(self.on_adb_added_verified_tx)
|
||||
util.unregister_callback(self.on_adb_set_up_to_date)
|
||||
self.unregister_callbacks()
|
||||
|
||||
def get_channel_status(self, outpoint):
|
||||
return self.channel_status.get(outpoint, 'unknown')
|
||||
@@ -185,21 +175,26 @@ class LNWatcher(Logger):
|
||||
self.adb.add_address(address)
|
||||
self.callbacks[address] = callback
|
||||
|
||||
async def on_fee(self, event, *args):
|
||||
@event_listener
|
||||
async def on_event_fee(self, *args):
|
||||
await self.trigger_callbacks()
|
||||
|
||||
async def on_network_updated(self, event, *args):
|
||||
@event_listener
|
||||
async def on_event_network_updated(self, *args):
|
||||
await self.trigger_callbacks()
|
||||
|
||||
async def on_blockchain_updated(self, event, *args):
|
||||
@event_listener
|
||||
async def on_event_blockchain_updated(self, *args):
|
||||
await self.trigger_callbacks()
|
||||
|
||||
async def on_adb_added_verified_tx(self, event, adb, tx_hash):
|
||||
@event_listener
|
||||
async def on_event_adb_added_verified_tx(self, adb, tx_hash):
|
||||
if adb != self.adb:
|
||||
return
|
||||
await self.trigger_callbacks()
|
||||
|
||||
async def on_adb_set_up_to_date(self, event, adb):
|
||||
@event_listener
|
||||
async def on_event_adb_set_up_to_date(self, adb):
|
||||
if adb != self.adb:
|
||||
return
|
||||
await self.trigger_callbacks()
|
||||
|
||||
@@ -31,6 +31,7 @@ from . import keystore
|
||||
from .util import profiler, chunks, OldTaskGroup
|
||||
from .invoices import Invoice, PR_UNPAID, PR_EXPIRED, PR_PAID, PR_INFLIGHT, PR_FAILED, PR_ROUTING, LN_EXPIRY_NEVER
|
||||
from .util import NetworkRetryManager, JsonRPCClient, NotEnoughFunds
|
||||
from .util import EventListener, event_listener
|
||||
from .lnutil import LN_MAX_FUNDING_SAT
|
||||
from .keystore import BIP32_KeyStore
|
||||
from .bitcoin import COIN
|
||||
@@ -192,7 +193,7 @@ LNGOSSIP_FEATURES = BASE_FEATURES\
|
||||
| LnFeatures.GOSSIP_QUERIES_REQ\
|
||||
|
||||
|
||||
class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
class LNWorker(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
|
||||
|
||||
INITIAL_TRAMPOLINE_FEE_LEVEL = 1 # only used for trampoline payments. set to 0 in tests.
|
||||
|
||||
@@ -216,7 +217,7 @@ class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
self.config = None # type: Optional[SimpleConfig]
|
||||
self.stopping_soon = False # whether we are being shut down
|
||||
|
||||
util.register_callback(self.on_proxy_changed, ['proxy_set'])
|
||||
self.register_callbacks()
|
||||
|
||||
@property
|
||||
def channel_db(self):
|
||||
@@ -340,7 +341,7 @@ class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
async def stop(self):
|
||||
if self.listen_server:
|
||||
self.listen_server.close()
|
||||
util.unregister_callback(self.on_proxy_changed)
|
||||
self.unregister_callbacks()
|
||||
await self.taskgroup.cancel_remaining()
|
||||
|
||||
def _add_peers_from_config(self):
|
||||
@@ -477,7 +478,8 @@ class LNWorker(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
choice = random.choice(addr_list)
|
||||
return choice
|
||||
|
||||
def on_proxy_changed(self, event, *args):
|
||||
@event_listener
|
||||
def on_event_proxy_set(self, *args):
|
||||
for peer in self.peers.values():
|
||||
peer.close_and_cleanup()
|
||||
self._clear_addr_retry_times()
|
||||
|
||||
@@ -22,7 +22,7 @@ from electrum.ecc import ECPrivkey
|
||||
from electrum import simple_config, lnutil
|
||||
from electrum.lnaddr import lnencode, LnAddr, lndecode
|
||||
from electrum.bitcoin import COIN, sha256
|
||||
from electrum.util import bh2u, NetworkRetryManager, bfh, OldTaskGroup
|
||||
from electrum.util import bh2u, NetworkRetryManager, bfh, OldTaskGroup, EventListener
|
||||
from electrum.lnpeer import Peer
|
||||
from electrum.lnutil import LNPeerAddr, Keypair, privkey_to_pubkey
|
||||
from electrum.lnutil import PaymentFailure, LnFeatures, HTLCOwner
|
||||
@@ -124,7 +124,7 @@ class MockWallet:
|
||||
return True
|
||||
|
||||
|
||||
class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
class MockLNWallet(Logger, EventListener, NetworkRetryManager[LNPeerAddr]):
|
||||
MPP_EXPIRY = 2 # HTLC timestamps are cast to int, so this cannot be 1
|
||||
PAYMENT_TIMEOUT = 120
|
||||
TIMEOUT_SHUTDOWN_FAIL_PENDING_HTLCS = 0
|
||||
@@ -255,7 +255,7 @@ class MockLNWallet(Logger, NetworkRetryManager[LNPeerAddr]):
|
||||
handle_error_code_from_failed_htlc = LNWallet.handle_error_code_from_failed_htlc
|
||||
is_trampoline_peer = LNWallet.is_trampoline_peer
|
||||
wait_for_received_pending_htlcs_to_get_removed = LNWallet.wait_for_received_pending_htlcs_to_get_removed
|
||||
on_proxy_changed = LNWallet.on_proxy_changed
|
||||
#on_event_proxy_set = LNWallet.on_event_proxy_set
|
||||
_decode_channel_update_msg = LNWallet._decode_channel_update_msg
|
||||
_handle_chanupd_from_failed_htlc = LNWallet._handle_chanupd_from_failed_htlc
|
||||
_on_maybe_forwarded_htlc_resolved = LNWallet._on_maybe_forwarded_htlc_resolved
|
||||
|
||||
@@ -1594,10 +1594,10 @@ class CallbackManager:
|
||||
self.callbacks = defaultdict(list) # note: needs self.callback_lock
|
||||
self.asyncio_loop = None
|
||||
|
||||
def register_callback(self, callback, events):
|
||||
def register_callback(self, func, events):
|
||||
with self.callback_lock:
|
||||
for event in events:
|
||||
self.callbacks[event].append(callback)
|
||||
self.callbacks[event].append(func)
|
||||
|
||||
def unregister_callback(self, callback):
|
||||
with self.callback_lock:
|
||||
@@ -1618,13 +1618,13 @@ class CallbackManager:
|
||||
for callback in callbacks:
|
||||
# FIXME: if callback throws, we will lose the traceback
|
||||
if asyncio.iscoroutinefunction(callback):
|
||||
asyncio.run_coroutine_threadsafe(callback(event, *args), self.asyncio_loop)
|
||||
asyncio.run_coroutine_threadsafe(callback(*args), self.asyncio_loop)
|
||||
elif get_running_loop() == self.asyncio_loop:
|
||||
# run callback immediately, so that it is guaranteed
|
||||
# to have been executed when this method returns
|
||||
callback(event, *args)
|
||||
callback(*args)
|
||||
else:
|
||||
self.asyncio_loop.call_soon_threadsafe(callback, event, *args)
|
||||
self.asyncio_loop.call_soon_threadsafe(callback, *args)
|
||||
|
||||
|
||||
callback_mgr = CallbackManager()
|
||||
@@ -1633,6 +1633,38 @@ register_callback = callback_mgr.register_callback
|
||||
unregister_callback = callback_mgr.unregister_callback
|
||||
|
||||
|
||||
|
||||
class EventListener:
|
||||
|
||||
def _list_callbacks(self):
|
||||
for method_name in dir(self):
|
||||
# Fixme: getattr executes the code of methods decorated by @property.
|
||||
# This if why we shield with startswith
|
||||
if not method_name.startswith('on_event_'):
|
||||
continue
|
||||
method = getattr(self, method_name)
|
||||
if not getattr(method, '_is_event_listener', False):
|
||||
continue
|
||||
assert callable(method)
|
||||
yield method_name[len('on_event_'):], method
|
||||
|
||||
def register_callbacks(self):
|
||||
for name, method in self._list_callbacks():
|
||||
_logger.info(f'registering callback {method}')
|
||||
register_callback(method, [name])
|
||||
|
||||
def unregister_callbacks(self):
|
||||
for name, method in self._list_callbacks():
|
||||
_logger.info(f'unregistering callback {method}')
|
||||
unregister_callback(method)
|
||||
|
||||
|
||||
def event_listener(func):
|
||||
assert func.__name__.startswith('on_event_')
|
||||
func._is_event_listener = True
|
||||
return func
|
||||
|
||||
|
||||
_NetAddrType = TypeVar("_NetAddrType")
|
||||
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ from .logging import get_logger, Logger
|
||||
from .lnworker import LNWallet
|
||||
from .paymentrequest import PaymentRequest
|
||||
from .util import read_json_file, write_json_file, UserFacingException
|
||||
from .util import EventListener, event_listener
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .network import Network
|
||||
@@ -267,7 +268,7 @@ class TxWalletDetails(NamedTuple):
|
||||
is_lightning_funding_tx: bool
|
||||
|
||||
|
||||
class Abstract_Wallet(ABC, Logger):
|
||||
class Abstract_Wallet(ABC, Logger, EventListener):
|
||||
"""
|
||||
Wallet classes are created to handle various address generation methods.
|
||||
Completion states (watching-only, single account, no seed, etc) are handled inside classes.
|
||||
@@ -282,6 +283,7 @@ class Abstract_Wallet(ABC, Logger):
|
||||
lnworker: Optional['LNWallet']
|
||||
|
||||
def __init__(self, db: WalletDB, storage: Optional[WalletStorage], *, config: SimpleConfig):
|
||||
|
||||
if not db.is_ready_to_be_used_by_wallet():
|
||||
raise Exception("storage not ready to be used by Abstract_Wallet")
|
||||
|
||||
@@ -332,10 +334,7 @@ class Abstract_Wallet(ABC, Logger):
|
||||
self.lnworker = None
|
||||
self.load_keystore()
|
||||
self.test_addresses_sanity()
|
||||
# callbacks
|
||||
util.register_callback(self.on_adb_added_tx, ['adb_added_tx'])
|
||||
util.register_callback(self.on_adb_added_verified_tx, ['adb_added_verified_tx'])
|
||||
util.register_callback(self.on_adb_removed_verified_tx, ['adb_removed_verified_tx'])
|
||||
self.register_callbacks()
|
||||
|
||||
@ignore_exceptions # don't kill outer taskgroup
|
||||
async def main_loop(self):
|
||||
@@ -422,9 +421,7 @@ class Abstract_Wallet(ABC, Logger):
|
||||
|
||||
async def stop(self):
|
||||
"""Stop all networking and save DB to disk."""
|
||||
util.unregister_callback(self.on_adb_added_tx)
|
||||
util.unregister_callback(self.on_adb_added_verified_tx)
|
||||
util.unregister_callback(self.on_adb_removed_verified_tx)
|
||||
self.unregister_callbacks()
|
||||
try:
|
||||
async with ignore_after(5):
|
||||
if self.network:
|
||||
@@ -454,7 +451,8 @@ class Abstract_Wallet(ABC, Logger):
|
||||
if status_changed:
|
||||
self.logger.info(f'set_up_to_date: {up_to_date}')
|
||||
|
||||
def on_adb_added_tx(self, event, adb, tx_hash, notify_GUI):
|
||||
@event_listener
|
||||
def on_event_adb_added_tx(self, adb, tx_hash, notify_GUI):
|
||||
if self.adb != adb:
|
||||
return
|
||||
tx = self.db.get_transaction(tx_hash)
|
||||
@@ -471,14 +469,16 @@ class Abstract_Wallet(ABC, Logger):
|
||||
if notify_GUI:
|
||||
util.trigger_callback('new_transaction', self, tx)
|
||||
|
||||
def on_adb_added_verified_tx(self, event, adb, tx_hash):
|
||||
@event_listener
|
||||
def on_event_adb_added_verified_tx(self, adb, tx_hash):
|
||||
if adb != self.adb:
|
||||
return
|
||||
self._update_request_statuses_touched_by_tx(tx_hash)
|
||||
tx_mined_status = self.adb.get_tx_height(tx_hash)
|
||||
util.trigger_callback('verified', self, tx_hash, tx_mined_status)
|
||||
|
||||
def on_adb_removed_verified_tx(self, event, adb, tx_hash):
|
||||
@event_listener
|
||||
def on_event_adb_removed_verified_tx(self, adb, tx_hash):
|
||||
if adb != self.adb:
|
||||
return
|
||||
self._update_request_statuses_touched_by_tx(tx_hash)
|
||||
|
||||
Reference in New Issue
Block a user