Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
gruve-p committed Oct 20, 2024
2 parents c94d372 + 3a2dfb3 commit cde5865
Show file tree
Hide file tree
Showing 42 changed files with 251 additions and 157 deletions.
4 changes: 2 additions & 2 deletions contrib/android/p4a_recipes/libsecp256k1/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@


class LibSecp256k1RecipePinned(LibSecp256k1Recipe):
version = "e3a885d42a7800c1ccebad94ad1e2b82c4df5c65"
version = "642c885b6102725e25623738529895a95addc4f4"
url = "https://github.com/bitcoin-core/secp256k1/archive/{version}.zip"
sha512sum = "fef2671fcdcf9a1127597b2db0109279c38053b990bd3b979f863fdb3c92e691df9e2d3bdd22e1bb59100b3d27f27b07df1f0e314f2535148bd250665c8f1370"
sha512sum = "81c0048630e4b2ab24a71fc2156ff9f15bc6d379106cbe4724acd18a48269d07df51660662bcea4df167578a43837a8bc27af380f3a37b4c69e30cdd72f2b3fb"


recipe = LibSecp256k1RecipePinned()
5 changes: 3 additions & 2 deletions contrib/make_libsecp256k1.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@
# sudo apt-get install gcc-multilib g++-multilib
# $ AUTOCONF_FLAGS="--host=i686-linux-gnu CFLAGS=-m32 CXXFLAGS=-m32 LDFLAGS=-m32" ./contrib/make_libsecp256k1.sh

LIBSECP_VERSION="e3a885d42a7800c1ccebad94ad1e2b82c4df5c65"
# ^ tag "v0.5.0"
LIBSECP_VERSION="642c885b6102725e25623738529895a95addc4f4"
# ^ tag "v0.5.1"
# note: this version is duplicated in contrib/android/p4a_recipes/libsecp256k1/__init__.py
# (and also in electrum-ecc, for the "secp256k1" git submodule)

set -e

Expand Down
6 changes: 5 additions & 1 deletion electrum_grs/exchange_rate.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ def history_ccys(self):
return CURRENCIES[self.name()]

async def request_history(self, ccy):
# ref https://docs.coingecko.com/v3.0.1/reference/coins-id-market-chart
num_days = 365
# Setting `num_days = "max"` started erroring (around 2024-04) with:
# > Your request exceeds the allowed time range. Public API users are limited to querying
Expand Down Expand Up @@ -332,11 +333,14 @@ async def query_all_exchanges_for_their_ccys_over_network():
for name, klass in exchanges.items():
exchange = klass(None, None)
await group.spawn(get_currencies_safe(name, exchange))
loop = util.get_asyncio_loop()

loop = asyncio.new_event_loop()
try:
loop.run_until_complete(query_all_exchanges_for_their_ccys_over_network())
except Exception as e:
pass
finally:
loop.close()
with open(path, 'w', encoding='utf-8') as f:
f.write(json.dumps(d, indent=4, sort_keys=True))
return d
Expand Down
6 changes: 3 additions & 3 deletions electrum_grs/gui/qt/confirm_tx_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
from electrum_grs.bitcoin import DummyAddress

from .util import (WindowModalDialog, ColorScheme, HelpLabel, Buttons, CancelButton,
BlockingWaitingDialog, PasswordLineEdit, WWLabel, read_QIcon)
PasswordLineEdit, WWLabel, read_QIcon)

from .fee_slider import FeeSlider, FeeComboBox

Expand Down Expand Up @@ -532,7 +532,7 @@ def _update_widgets(self):
if self.not_enough_funds:
self.io_widget.update(None)
self.set_feerounding_visibility(False)
self.messages = []
self.messages = [_('Preparing transaction...')]
else:
self.messages = self.get_messages()
self.update_fee_fields()
Expand Down Expand Up @@ -619,7 +619,7 @@ def __init__(self, *, window: 'ElectrumWindow', make_tx, output_value: Union[int
title=_("New Transaction"), # todo: adapt title for channel funding tx, swaps
allow_preview=allow_preview)

BlockingWaitingDialog(window, _("Preparing transaction..."), self.update)
self.trigger_update()

def _update_amount_label(self):
tx = self.tx
Expand Down
32 changes: 8 additions & 24 deletions electrum_grs/gui/qt/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
import_meta_gui, export_meta_gui,
filename_field, address_field, char_width_in_lineedit, webopen,
TRANSACTION_FILE_EXTENSION_FILTER_ANY, MONOSPACE_FONT,
getOpenFileName, getSaveFileName, BlockingWaitingDialog, font_height)
getOpenFileName, getSaveFileName, font_height)
from .util import ButtonsLineEdit, ShowQRLineEdit
from .util import QtEventListener, qt_event_listener, event_listener
from .wizard.wallet import WIF_HELP_TEXT
Expand Down Expand Up @@ -301,26 +301,11 @@ def on_version_received(v):
self._update_check_thread.checked.connect(on_version_received)
self._update_check_thread.start()

def run_coroutine_dialog(self, coro, text, on_result, on_cancelled):
""" run coroutine in a waiting dialog, with a Cancel button that cancels the coroutine """
from electrum import util
loop = util.get_asyncio_loop()
assert util.get_running_loop() != loop, 'must not be called from asyncio thread'
future = asyncio.run_coroutine_threadsafe(coro, loop)
def task():
try:
return future.result()
except concurrent.futures.CancelledError:
on_cancelled()
try:
WaitingDialog(
self, text, task,
on_success=on_result,
on_error=self.on_error,
on_cancel=future.cancel)
except Exception as e:
self.show_error(str(e))
raise
def run_coroutine_dialog(self, coro, text):
""" run coroutine in a waiting dialog, with a Cancel button that cancels the coroutine"""
from .util import RunCoroutineDialog
d = RunCoroutineDialog(self, text, coro)
return d.run()

def run_coroutine_from_thread(self, coro, name, on_result=None):
if self._cleaned_up:
Expand Down Expand Up @@ -1180,10 +1165,9 @@ def run_swap_dialog(self, is_reverse=None, recv_amount_sat=None, channels=None):
if not self.wallet.lnworker.num_sats_can_send() and not self.wallet.lnworker.num_sats_can_receive():
self.show_error(_("You do not have liquidity in your active channels."))
return
def get_pairs_thread():
self.network.run_from_another_thread(self.wallet.lnworker.swap_manager.get_pairs())
try:
BlockingWaitingDialog(self, _('Please wait...'), get_pairs_thread)
self.run_coroutine_dialog(
self.wallet.lnworker.swap_manager.get_pairs(), _('Please wait...'))
except SwapServerError as e:
self.show_error(str(e))
return
Expand Down
40 changes: 24 additions & 16 deletions electrum_grs/gui/qt/swap_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from PyQt6.QtWidgets import QLabel, QVBoxLayout, QGridLayout, QPushButton

from electrum_grs.i18n import _
from electrum_grs.util import NotEnoughFunds, NoDynamicFeeEstimates
from electrum_grs.util import NotEnoughFunds, NoDynamicFeeEstimates, UserCancelled
from electrum_grs.bitcoin import DummyAddress
from electrum_grs.transaction import PartialTxOutput, PartialTransaction

Expand Down Expand Up @@ -319,25 +319,33 @@ def update_ok_button(self):
recv_amount = self.recv_amount_e.get_amount()
self.ok_button.setEnabled(bool(send_amount) and bool(recv_amount))

def do_normal_swap(self, lightning_amount, onchain_amount, password):
async def _do_normal_swap(self, lightning_amount, onchain_amount, password):
dummy_tx = self._create_tx(onchain_amount)
assert dummy_tx
sm = self.swap_manager
swap, invoice = await sm.request_normal_swap(
lightning_amount_sat=lightning_amount,
expected_onchain_amount_sat=onchain_amount,
channels=self.channels,
)
self._current_swap = swap
tx = sm.create_funding_tx(swap, dummy_tx, password=password)
txid = await sm.wait_for_htlcs_and_broadcast(swap=swap, invoice=invoice, tx=tx)
return txid

def do_normal_swap(self, lightning_amount, onchain_amount, password):
self._current_swap = None
async def coro():
swap, invoice = await sm.request_normal_swap(
lightning_amount_sat=lightning_amount,
expected_onchain_amount_sat=onchain_amount,
channels=self.channels,
)
self._current_swap = swap
tx = sm.create_funding_tx(swap, dummy_tx, password=password)
txid = await sm.wait_for_htlcs_and_broadcast(swap=swap, invoice=invoice, tx=tx)
return txid
self.window.run_coroutine_dialog(
coro(), _('Awaiting swap payment...'),
on_result=lambda funding_txid: self.window.on_swap_result(funding_txid, is_reverse=False),
on_cancelled=lambda: sm.cancel_normal_swap(self._current_swap))
coro = self._do_normal_swap(lightning_amount, onchain_amount, password)
try:
funding_txid = self.window.run_coroutine_dialog(coro, _('Awaiting swap payment...'))
except UserCancelled:
self.swap_manager.cancel_normal_swap(self._current_swap)
self.window.show_message(_('Swap cancelled'))
return
except Exception as e:
self.window.show_error(str(e))
return
self.window.on_swap_result(funding_txid, is_reverse=False)

def get_description(self):
onchain_funds = "onchain funds"
Expand Down
8 changes: 3 additions & 5 deletions electrum_grs/gui/qt/transaction_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
char_width_in_lineedit, TRANSACTION_FILE_EXTENSION_FILTER_SEPARATE,
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_COMPLETE_TX,
TRANSACTION_FILE_EXTENSION_FILTER_ONLY_PARTIAL_TX,
BlockingWaitingDialog, getSaveFileName, ColorSchemeItem,
getSaveFileName, ColorSchemeItem,
get_iconname_qrcode, VLine, WaitingDialog)
from .rate_limiter import rate_limited
from .my_treeview import create_toolbar_with_menu, QMenuWithConfig
Expand Down Expand Up @@ -582,11 +582,9 @@ def set_tx(self, tx: 'Transaction'):
# FIXME for PSBTs, we do a blocking fetch, as the missing data might be needed for e.g. signing
# - otherwise, the missing data is for display-completeness only, e.g. fee, input addresses (we do it async)
if not tx.is_complete() and tx.is_missing_info_from_network():
BlockingWaitingDialog(
self,
self.main_window.run_coroutine_dialog(
tx.add_info_from_network(self.wallet.network, timeout=10),
_("Adding info to tx, from network..."),
lambda: Network.run_from_another_thread(
tx.add_info_from_network(self.wallet.network, timeout=10)),
)
else:
self.maybe_fetch_txin_data()
Expand Down
63 changes: 35 additions & 28 deletions electrum_grs/gui/qt/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

from electrum_grs.i18n import _
from electrum_grs.util import FileImportFailed, FileExportFailed, resource_path
from electrum_grs.util import EventListener, event_listener, get_logger
from electrum_grs.util import EventListener, event_listener, get_logger, UserCancelled
from electrum_grs.invoices import PR_UNPAID, PR_PAID, PR_EXPIRED, PR_INFLIGHT, PR_UNKNOWN, PR_FAILED, PR_ROUTING, PR_UNCONFIRMED, PR_BROADCASTING, PR_BROADCAST
from electrum_grs.logging import Logger
from electrum_grs.qrreader import MissingQrDetectionLib
Expand Down Expand Up @@ -371,32 +371,32 @@ def update(self, msg):
self.message_label.setText(msg)


class BlockingWaitingDialog(WindowModalDialog):
"""Shows a waiting dialog whilst running a task.
Should be called from the GUI thread. The GUI thread will be blocked while
the task is running; the point of the dialog is to provide feedback
to the user regarding what is going on.
"""
def __init__(self, parent: QWidget, message: str, task: Callable[[], Any]):
assert parent
if isinstance(parent, MessageBoxMixin):
parent = parent.top_level_window()
WindowModalDialog.__init__(self, parent, _("Please wait"))
self.message_label = QLabel(message)
vbox = QVBoxLayout(self)
vbox.addWidget(self.message_label)
self.finished.connect(self.deleteLater) # see #3956
# show popup
self.show()
# refresh GUI; needed for popup to appear and for message_label to get drawn
QCoreApplication.processEvents()
QCoreApplication.processEvents()
try:
# block and run given task
task()
finally:
# close popup
self.accept()
class RunCoroutineDialog(WaitingDialog):

def __init__(self, parent: QWidget, message: str, coroutine):
from electrum import util
import asyncio
import concurrent.futures
loop = util.get_asyncio_loop()
assert util.get_running_loop() != loop, 'must not be called from asyncio thread'
self._exception = None
self._result = None
self._future = asyncio.run_coroutine_threadsafe(coroutine, loop)
def task():
try:
self._result = self._future.result()
except concurrent.futures.CancelledError:
self._exception = UserCancelled
except Exception as e:
self._exception = e
WaitingDialog.__init__(self, parent, message, task, on_cancel=self._future.cancel)

def run(self):
self.exec()
if self._exception:
raise self._exception
else:
return self._result


def line_dialog(parent, title, label, ok_label, default=None):
Expand Down Expand Up @@ -1242,14 +1242,21 @@ def getSaveFileName(
def icon_path(icon_basename: str):
return resource_path('gui', 'icons', icon_basename)

def internal_plugin_icon_path(plugin_name, icon_basename: str):
return resource_path('plugins', plugin_name, icon_basename)


@lru_cache(maxsize=1000)
def read_QIcon(icon_basename: str) -> QIcon:
return QIcon(icon_path(icon_basename))

def read_QIcon_from_bytes(b: bytes) -> QIcon:
def read_QPixmap_from_bytes(b: bytes) -> QPixmap:
qp = QPixmap()
qp.loadFromData(b)
return qp

def read_QIcon_from_bytes(b: bytes) -> QIcon:
qp = read_QPixmap_from_bytes(b)
return QIcon(qp)

class IconLabel(QWidget):
Expand Down
3 changes: 3 additions & 0 deletions electrum_grs/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,9 @@ async def _get_ssl_context(self):
else:
# pinned self-signed cert
sslc = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=self.cert_path)
# note: Flag "ssl.VERIFY_X509_STRICT" is enabled by default in python 3.13+ (disabled in older versions).
# We explicitly disable it as it breaks lots of servers.
sslc.verify_flags &= ~ssl.VERIFY_X509_STRICT
sslc.check_hostname = False
return sslc

Expand Down
Loading

0 comments on commit cde5865

Please sign in to comment.