|
7 | 7 | import os
|
8 | 8 | import webbrowser
|
9 | 9 | from functools import partial, lru_cache, wraps
|
| 10 | +from asyncio import iscoroutinefunction |
10 | 11 | from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, List, Any, Sequence, Tuple, Union)
|
11 | 12 |
|
12 | 13 | from PyQt6 import QtCore
|
|
20 | 21 | QFrame, QAbstractButton)
|
21 | 22 |
|
22 | 23 | from electrum.i18n import _
|
| 24 | +from electrum.logging import Logger |
23 | 25 | from electrum.util import (FileImportFailed, FileExportFailed, resource_path, EventListener, event_listener,
|
24 | 26 | get_logger, UserCancelled, UserFacingException)
|
25 | 27 | from electrum.invoices import (PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING,
|
|
34 | 36 |
|
35 | 37 | from electrum.simple_config import SimpleConfig
|
36 | 38 | from electrum.simple_config import ConfigVarWithConfig
|
37 |
| - |
| 39 | + from electrum.wallet import Abstract_Wallet |
38 | 40 |
|
39 | 41 | if platform.system() == 'Windows':
|
40 | 42 | MONOSPACE_FONT = 'Lucida Console'
|
@@ -748,6 +750,63 @@ def scan_qr_from_screenshot() -> QrCodeResult:
|
748 | 750 | assert len(scanned_qr) == 1, f"{len(scanned_qr)=}, expected 1"
|
749 | 751 | return scanned_qr[0]
|
750 | 752 |
|
| 753 | +class QtClosingWarningHandler(Logger): |
| 754 | + """ |
| 755 | + Provides handlers to register specific types of wallet closing warning callbacks, |
| 756 | + and persists them. |
| 757 | + """ |
| 758 | + def __init__(self, wallet: 'Abstract_Wallet'): |
| 759 | + Logger.__init__(self) |
| 760 | + self.wallet = wallet |
| 761 | + persisted_callbacks = wallet.db.get_dict('wallet_closing_warning_callbacks') |
| 762 | + |
| 763 | + # unconfirmed tx warnings: txid -> warning message, minimum confirmations |
| 764 | + self.unconfirmed_tx_warnings: dict[str, Tuple[str, int]] \ |
| 765 | + = persisted_callbacks.setdefault('unconfirmed_tx_warnings', {}) |
| 766 | + |
| 767 | + self._closing_warning_callbacks = [] # type: List[Callable[[], Optional[str]]] |
| 768 | + self._load_persisted_callbacks() |
| 769 | + |
| 770 | + def _load_persisted_callbacks(self) -> None: |
| 771 | + """Re-registers the callbacks loaded from the database.""" |
| 772 | + for txid, (warning_msg, minconf) in self.unconfirmed_tx_warnings.items(): |
| 773 | + self.register_unconfirmed_tx_warning(txid, warning_msg, minconf) |
| 774 | + |
| 775 | + def _register_generic_warning_callback( |
| 776 | + self, |
| 777 | + callback: Callable[[], Optional[str]], |
| 778 | + cleanup_cb: Optional[Callable[[], None]] = None |
| 779 | + ) -> None: |
| 780 | + """Wraps the callback in try/except and stores it in the callback list.""" |
| 781 | + assert not iscoroutinefunction(callback) |
| 782 | + assert not iscoroutinefunction(cleanup_cb) if cleanup_cb else True |
| 783 | + |
| 784 | + def warning_callback() -> Optional[str]: |
| 785 | + warning: Optional[str] = None |
| 786 | + try: |
| 787 | + warning = callback() |
| 788 | + except Exception: |
| 789 | + self.logger.exception("Error in closing warning callback") |
| 790 | + if warning is None and cleanup_cb: |
| 791 | + self.logger.debug(f"removing closing warning callback {callback.__name__}") |
| 792 | + cleanup_cb() |
| 793 | + return warning |
| 794 | + |
| 795 | + self.logger.debug(f"registering wallet closing warning callback: {callback.__name__}") |
| 796 | + self._closing_warning_callbacks.append(warning_callback) |
| 797 | + |
| 798 | + def register_unconfirmed_tx_warning(self, txid: str, warning_msg: str, minconf: int = 1) -> None: |
| 799 | + """Registers a closing warning for a transaction that has to get confirmed.""" |
| 800 | + self.unconfirmed_tx_warnings[txid] = (warning_msg, minconf) |
| 801 | + def unconfirmed_tx_warning_cb() -> Optional[str]: |
| 802 | + if self.wallet.adb.get_tx_height(txid).height < minconf: |
| 803 | + return warning_msg |
| 804 | + return None |
| 805 | + cleanup_cb = lambda: self.unconfirmed_tx_warnings.pop(txid, None) |
| 806 | + self._register_generic_warning_callback(unconfirmed_tx_warning_cb, cleanup_cb) |
| 807 | + |
| 808 | + def get_closing_warning_callbacks(self): |
| 809 | + return self._closing_warning_callbacks |
751 | 810 |
|
752 | 811 | class GenericInputHandler:
|
753 | 812 | def input_qr_from_camera(
|
|
0 commit comments