fix: use single long lived transport in qeswaphelper
changes qeswaphelper to shate a single, long lived transport instance instead of opening new transports to do swaps and fetch offers. This allows to continuosly fetch offers, so events which get returned later by slow relays don't get missed and the fee values stay updated. Also fixes a race causing the list to miss some swapservers, as the current implementation fetches only until `swap_manager.is_initialized()` is set, which will get set as soon as an event of the configured swapserver is received. So if the event of the configured swapserver is received as first, all server events coming in after it would get ignored.
This commit is contained in:
@@ -51,6 +51,19 @@ ElDialog {
|
|||||||
clip: true
|
clip: true
|
||||||
model: swaphelper.availableSwapServers
|
model: swaphelper.availableSwapServers
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: swaphelper
|
||||||
|
function onOffersUpdated() {
|
||||||
|
listview.model = null
|
||||||
|
listview.model = swaphelper.availableSwapServers
|
||||||
|
listview.forceLayout()
|
||||||
|
if (dialog.selectedPubkey) {
|
||||||
|
listview.currentIndex = swaphelper.availableSwapServers.indexFor(dialog.selectedPubkey)
|
||||||
|
}
|
||||||
|
console.log("swapserver list refreshed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
delegate: ItemDelegate {
|
delegate: ItemDelegate {
|
||||||
width: ListView.view.width
|
width: ListView.view.width
|
||||||
height: itemLayout.height
|
height: itemLayout.height
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ ElDialog {
|
|||||||
dialog.accepted.connect(function() {
|
dialog.accepted.connect(function() {
|
||||||
if (Config.swapServerNPub != dialog.selectedPubkey) {
|
if (Config.swapServerNPub != dialog.selectedPubkey) {
|
||||||
Config.swapServerNPub = dialog.selectedPubkey
|
Config.swapServerNPub = dialog.selectedPubkey
|
||||||
_swaphelper.init_swap_manager()
|
_swaphelper.setReadyState()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
dialog.open()
|
dialog.open()
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ ApplicationWindow
|
|||||||
})
|
})
|
||||||
dialog.accepted.connect(function() {
|
dialog.accepted.connect(function() {
|
||||||
Config.swapServerNPub = dialog.selectedPubkey
|
Config.swapServerNPub = dialog.selectedPubkey
|
||||||
_swaphelper.init_swap_manager()
|
_swaphelper.setReadyState()
|
||||||
})
|
})
|
||||||
dialog.rejected.connect(function() {
|
dialog.rejected.connect(function() {
|
||||||
_swaphelper.npubSelectionCancelled()
|
_swaphelper.npubSelectionCancelled()
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import concurrent
|
|
||||||
import threading
|
|
||||||
from enum import IntEnum
|
from enum import IntEnum
|
||||||
from typing import Union, Optional, TYPE_CHECKING, Sequence
|
from typing import Union, Optional, TYPE_CHECKING, Sequence
|
||||||
|
|
||||||
@@ -11,7 +9,8 @@ from electrum.i18n import _
|
|||||||
from electrum.bitcoin import DummyAddress
|
from electrum.bitcoin import DummyAddress
|
||||||
from electrum.logging import get_logger
|
from electrum.logging import get_logger
|
||||||
from electrum.transaction import PartialTxOutput, PartialTransaction
|
from electrum.transaction import PartialTxOutput, PartialTransaction
|
||||||
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, profiler, get_asyncio_loop, age
|
from electrum.util import (NotEnoughFunds, NoDynamicFeeEstimates, profiler, get_asyncio_loop, age,
|
||||||
|
wait_for2)
|
||||||
from electrum.submarine_swaps import NostrTransport, SwapServerTransport
|
from electrum.submarine_swaps import NostrTransport, SwapServerTransport
|
||||||
from electrum.fee_policy import FeePolicy
|
from electrum.fee_policy import FeePolicy
|
||||||
|
|
||||||
@@ -153,8 +152,14 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
self.requestTxUpdate.connect(self.tx_update_pushback_timer)
|
self.requestTxUpdate.connect(self.tx_update_pushback_timer)
|
||||||
|
|
||||||
self.offersUpdated.connect(self.on_offers_updated)
|
self.offersUpdated.connect(self.on_offers_updated)
|
||||||
|
self.transport_task: Optional[asyncio.Task] = None
|
||||||
|
self.swap_transport: Optional[SwapServerTransport] = None
|
||||||
|
self.recent_offers = []
|
||||||
|
|
||||||
def on_destroy(self):
|
def on_destroy(self):
|
||||||
|
if self.transport_task is not None:
|
||||||
|
self.transport_task.cancel()
|
||||||
|
self.transport_task = None
|
||||||
self.unregister_callbacks()
|
self.unregister_callbacks()
|
||||||
|
|
||||||
walletChanged = pyqtSignal()
|
walletChanged = pyqtSignal()
|
||||||
@@ -166,7 +171,7 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
def wallet(self, wallet: QEWallet):
|
def wallet(self, wallet: QEWallet):
|
||||||
if self._wallet != wallet:
|
if self._wallet != wallet:
|
||||||
self._wallet = wallet
|
self._wallet = wallet
|
||||||
self.init_swap_manager()
|
self.run_swap_manager()
|
||||||
self.walletChanged.emit()
|
self.walletChanged.emit()
|
||||||
|
|
||||||
sliderPosChanged = pyqtSignal()
|
sliderPosChanged = pyqtSignal()
|
||||||
@@ -338,9 +343,8 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
def isNostr(self):
|
def isNostr(self):
|
||||||
return True # TODO
|
return True # TODO
|
||||||
|
|
||||||
@pyqtSlot()
|
def run_swap_manager(self):
|
||||||
def init_swap_manager(self):
|
self._logger.debug('run_swap_manager')
|
||||||
self._logger.debug('init_swap_manager')
|
|
||||||
if (lnworker := self._wallet.wallet.lnworker) is None:
|
if (lnworker := self._wallet.wallet.lnworker) is None:
|
||||||
return
|
return
|
||||||
swap_manager = lnworker.swap_manager
|
swap_manager = lnworker.swap_manager
|
||||||
@@ -356,73 +360,107 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
|
|
||||||
swap_transport = swap_manager.create_transport()
|
swap_transport = swap_manager.create_transport()
|
||||||
|
|
||||||
def query_task(transport: SwapServerTransport):
|
async def swap_transport_task(transport: SwapServerTransport):
|
||||||
with transport:
|
async with transport:
|
||||||
try:
|
self.swap_transport = transport
|
||||||
async def wait_initialized():
|
if not swap_manager.is_initialized.is_set():
|
||||||
try:
|
self.userinfo = _('Initializing...')
|
||||||
await asyncio.wait_for(swap_manager.is_initialized.wait(), timeout=15)
|
try:
|
||||||
self._logger.debug('swapmanager initialized')
|
# is_initialized is set if we receive the event of our configured SWAPSERVER_NPUB
|
||||||
self.state = QESwapHelper.State.Initialized
|
# This will timeout if no server is configured, or our server didn't publish recently.
|
||||||
except asyncio.TimeoutError:
|
timeout = transport.connect_timeout + 1
|
||||||
self._logger.debug('swapmanager init timeout')
|
await wait_for2(swap_manager.is_initialized.wait(), timeout=timeout)
|
||||||
self.state = QESwapHelper.State.NoService
|
self._logger.debug('swapmanager initialized')
|
||||||
return
|
self.state = QESwapHelper.State.Initialized
|
||||||
|
except asyncio.TimeoutError:
|
||||||
if not swap_manager.is_initialized.is_set():
|
# only fail if we didn't get any offers or couldn't connect at all
|
||||||
self.userinfo = _('Initializing...')
|
# otherwise the timeout just means that no offer of the selected npub has
|
||||||
fut = asyncio.run_coroutine_threadsafe(wait_initialized(), get_asyncio_loop())
|
# been found (or that there is no npub selected at all), so the prompt should open
|
||||||
fut.result()
|
if isinstance(transport, NostrTransport) and not transport.is_connected.is_set():
|
||||||
except Exception as e:
|
|
||||||
try: # swaphelper might be destroyed at this point
|
|
||||||
self.userinfo = _('Error') + ': ' + str(e)
|
|
||||||
self.state = QESwapHelper.State.NoService
|
|
||||||
self._logger.error(str(e))
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
if isinstance(transport, NostrTransport):
|
|
||||||
if not swap_manager.is_initialized.is_set():
|
|
||||||
if not transport.is_connected.is_set():
|
|
||||||
self.userinfo = _('Error') + ': ' + '\n'.join([
|
self.userinfo = _('Error') + ': ' + '\n'.join([
|
||||||
_('Could not connect to a Nostr relay.'),
|
_('Could not connect to a Nostr relay.'),
|
||||||
_('Please check your relays and network connection')
|
_('Please check your relays and network connection')
|
||||||
])
|
])
|
||||||
self.state = QESwapHelper.State.NoService
|
self.state = QESwapHelper.State.NoService
|
||||||
return
|
return
|
||||||
self.recent_offers = [x for x in transport.get_recent_offers()]
|
elif not isinstance(transport, NostrTransport) or not transport.get_recent_offers():
|
||||||
if not self.recent_offers:
|
self._logger.debug('Could not find a swap provider.')
|
||||||
self.userinfo = _('Could not find a swap provider.')
|
self.userinfo = _('Could not find a swap provider.')
|
||||||
self.state = QESwapHelper.State.NoService
|
self.state = QESwapHelper.State.NoService
|
||||||
return
|
return
|
||||||
|
except Exception as e:
|
||||||
self.offersUpdated.emit()
|
try: # swaphelper might be destroyed at this point
|
||||||
self.undefinedNPub.emit()
|
self.userinfo = _('Error') + ': ' + str(e)
|
||||||
|
self.state = QESwapHelper.State.NoService
|
||||||
|
self._logger.error(str(e))
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
self.recent_offers = [x for x in transport.get_recent_offers()]
|
|
||||||
if not self.recent_offers:
|
|
||||||
self.userinfo = _('Could not find a swap provider.')
|
|
||||||
self.state = QESwapHelper.State.NoService
|
|
||||||
return
|
|
||||||
|
|
||||||
self.offersUpdated.emit()
|
if isinstance(transport, NostrTransport) and not swap_manager.is_initialized.is_set():
|
||||||
|
# not is_initialized.is_set() = configured provider was not found (or no provider configured)
|
||||||
|
# prompt user to select a swapserver
|
||||||
|
self.recent_offers = transport.get_recent_offers()
|
||||||
|
self.offersUpdated.emit()
|
||||||
|
self.undefinedNPub.emit()
|
||||||
|
elif swap_manager.is_initialized.is_set():
|
||||||
|
self.setReadyState()
|
||||||
|
|
||||||
self.state = QESwapHelper.State.ServiceReady
|
while True:
|
||||||
self.userinfo = QESwapHelper.MESSAGE_SWAP_HOWTO
|
# keep fetching new incoming offer events
|
||||||
self.init_swap_slider_range()
|
# the slider range will not get updated continuously as it would irritate the user
|
||||||
|
if isinstance(transport, NostrTransport):
|
||||||
|
if (recent_offers := transport.get_recent_offers()) != self.recent_offers:
|
||||||
|
self._logger.debug(f"received new swap offer")
|
||||||
|
self.recent_offers = recent_offers
|
||||||
|
self.offersUpdated.emit()
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
threading.Thread(target=query_task, args=(swap_transport,), daemon=True).start()
|
async def handle_swap_transport(transport: SwapServerTransport):
|
||||||
|
# ensures that swap_transport is always set None if transport closes
|
||||||
|
try:
|
||||||
|
await swap_transport_task(transport)
|
||||||
|
finally:
|
||||||
|
self.swap_transport = None
|
||||||
|
|
||||||
|
self.transport_task = asyncio.run_coroutine_threadsafe(
|
||||||
|
handle_swap_transport(swap_transport),
|
||||||
|
get_asyncio_loop()
|
||||||
|
)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def npubSelectionCancelled(self):
|
def npubSelectionCancelled(self):
|
||||||
if not self._wallet.wallet.config.SWAPSERVER_NPUB:
|
if (self._wallet.wallet.config.SWAPSERVER_NPUB
|
||||||
|
not in [offer.server_npub for offer in self.recent_offers]):
|
||||||
self._logger.debug('nostr is preferred but swapserver npub still undefined')
|
self._logger.debug('nostr is preferred but swapserver npub still undefined')
|
||||||
self.userinfo = _('No swap provider selected.')
|
if not self._wallet.wallet.config.SWAPSERVER_NPUB:
|
||||||
|
self.userinfo = _('No swap provider selected.')
|
||||||
|
else:
|
||||||
|
self.userinfo = _('Select one of the available swap providers.')
|
||||||
self.state = QESwapHelper.State.NoService
|
self.state = QESwapHelper.State.NoService
|
||||||
|
|
||||||
def init_swap_slider_range(self):
|
@pyqtSlot()
|
||||||
|
def setReadyState(self):
|
||||||
|
if self._wallet.wallet.config.SWAPSERVER_NPUB \
|
||||||
|
or not isinstance(self.swap_transport, NostrTransport):
|
||||||
|
self.state = QESwapHelper.State.ServiceReady
|
||||||
|
self.userinfo = QESwapHelper.MESSAGE_SWAP_HOWTO
|
||||||
|
self.initSwapSliderRange()
|
||||||
|
|
||||||
|
def update_swap_manager_pair(self):
|
||||||
|
"""Updates the swap manager pairs to the recent pairs of the selected server"""
|
||||||
|
assert self.swap_transport is not None, "No swap transport"
|
||||||
|
if isinstance(self.swap_transport, NostrTransport):
|
||||||
|
swap_manager = self._wallet.wallet.lnworker.swap_manager
|
||||||
|
pair = self.swap_transport.get_offer(self._wallet.wallet.config.SWAPSERVER_NPUB)
|
||||||
|
swap_manager.update_pairs(pair.pairs)
|
||||||
|
|
||||||
|
@pyqtSlot()
|
||||||
|
def initSwapSliderRange(self):
|
||||||
lnworker = self._wallet.wallet.lnworker
|
lnworker = self._wallet.wallet.lnworker
|
||||||
swap_manager = lnworker.swap_manager
|
swap_manager = lnworker.swap_manager
|
||||||
|
# update the swap_manager pair so the newest available data is used below
|
||||||
|
self.update_swap_manager_pair()
|
||||||
|
|
||||||
"""Sets the minimal and maximal amount that can be swapped for the swap
|
"""Sets the minimal and maximal amount that can be swapped for the swap
|
||||||
slider."""
|
slider."""
|
||||||
@@ -443,7 +481,7 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
max_onchain_spend))
|
max_onchain_spend))
|
||||||
# we expect range to adjust the value of the swap slider to be in the
|
# we expect range to adjust the value of the swap slider to be in the
|
||||||
# correct range, i.e., to correct an overflow when reducing the limits
|
# correct range, i.e., to correct an overflow when reducing the limits
|
||||||
self._logger.debug(f'Slider range {-reverse} - {forward}')
|
self._logger.debug(f'Slider range {-reverse} - {forward}. Pos {self._sliderPos}')
|
||||||
self.rangeMin = -reverse
|
self.rangeMin = -reverse
|
||||||
self.rangeMax = forward
|
self.rangeMax = forward
|
||||||
# percentage of void, right or left
|
# percentage of void, right or left
|
||||||
@@ -459,6 +497,12 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
self.leftVoidChanged.emit()
|
self.leftVoidChanged.emit()
|
||||||
self.rightVoidChanged.emit()
|
self.rightVoidChanged.emit()
|
||||||
|
|
||||||
|
if not self.rangeMin <= self._sliderPos <= self.rangeMax:
|
||||||
|
# clamp the slider pos into the given limits
|
||||||
|
if abs(self._sliderPos - self.rangeMin) < abs(self._sliderPos - self.rangeMax):
|
||||||
|
self._sliderPos = self.rangeMin
|
||||||
|
else:
|
||||||
|
self._sliderPos = self.rangeMax
|
||||||
self.swap_slider_moved()
|
self.swap_slider_moved()
|
||||||
|
|
||||||
@profiler
|
@profiler
|
||||||
@@ -504,7 +548,7 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
self._receive_amount = swap_manager.get_recv_amount(send_amount=self._send_amount, is_reverse=self.isReverse)
|
self._receive_amount = swap_manager.get_recv_amount(send_amount=self._send_amount, is_reverse=self.isReverse)
|
||||||
self.toreceive = QEAmount(amount_sat=self._receive_amount)
|
self.toreceive = QEAmount(amount_sat=self._receive_amount)
|
||||||
# fee breakdown
|
# fee breakdown
|
||||||
self.serverfeeperc = f'{swap_manager.percentage:0.1f}%'
|
self.serverfeeperc = f'{swap_manager.percentage:0.2f}%'
|
||||||
server_miningfee = swap_manager.mining_fee
|
server_miningfee = swap_manager.mining_fee
|
||||||
self.serverMiningfee = QEAmount(amount_sat=server_miningfee)
|
self.serverMiningfee = QEAmount(amount_sat=server_miningfee)
|
||||||
if self.isReverse:
|
if self.isReverse:
|
||||||
@@ -540,57 +584,54 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
assert self._tx
|
assert self._tx
|
||||||
if lightning_amount is None or onchain_amount is None:
|
if lightning_amount is None or onchain_amount is None:
|
||||||
return
|
return
|
||||||
loop = get_asyncio_loop()
|
|
||||||
|
|
||||||
def swap_task():
|
async def swap_task():
|
||||||
with self._wallet.wallet.lnworker.swap_manager.create_transport() as transport:
|
assert self.swap_transport is not None, "Swap transport not available"
|
||||||
coro = self._wallet.wallet.lnworker.swap_manager.request_normal_swap(
|
try:
|
||||||
transport,
|
dummy_tx = self._create_tx(onchain_amount)
|
||||||
|
self.userinfo = _('Performing swap...')
|
||||||
|
self.state = QESwapHelper.State.Started
|
||||||
|
self._swap, invoice = await self._wallet.wallet.lnworker.swap_manager.request_normal_swap(
|
||||||
|
self.swap_transport,
|
||||||
lightning_amount_sat=lightning_amount,
|
lightning_amount_sat=lightning_amount,
|
||||||
expected_onchain_amount_sat=onchain_amount,
|
expected_onchain_amount_sat=onchain_amount,
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
dummy_tx = self._create_tx(onchain_amount)
|
|
||||||
fut = asyncio.run_coroutine_threadsafe(coro, loop)
|
|
||||||
self.userinfo = _('Performing swap...')
|
|
||||||
self.state = QESwapHelper.State.Started
|
|
||||||
self._swap, invoice = fut.result()
|
|
||||||
|
|
||||||
tx = self._wallet.wallet.lnworker.swap_manager.create_funding_tx(self._swap, dummy_tx, password=self._wallet.password)
|
tx = self._wallet.wallet.lnworker.swap_manager.create_funding_tx(self._swap, dummy_tx, password=self._wallet.password)
|
||||||
coro2 = self._wallet.wallet.lnworker.swap_manager.wait_for_htlcs_and_broadcast(transport, swap=self._swap, invoice=invoice, tx=tx)
|
coro2 = self._wallet.wallet.lnworker.swap_manager.wait_for_htlcs_and_broadcast(self.swap_transport, swap=self._swap, invoice=invoice, tx=tx)
|
||||||
self._fut_htlc_wait = fut = asyncio.run_coroutine_threadsafe(coro2, loop)
|
self._fut_htlc_wait = fut = asyncio.create_task(coro2)
|
||||||
|
|
||||||
self.canCancel = True
|
self.canCancel = True
|
||||||
txid = fut.result()
|
txid = await fut
|
||||||
try: # swaphelper might be destroyed at this point
|
try: # swaphelper might be destroyed at this point
|
||||||
if txid:
|
if txid:
|
||||||
self.userinfo = _('Success!')
|
self.userinfo = _('Success!')
|
||||||
self.state = QESwapHelper.State.Success
|
self.state = QESwapHelper.State.Success
|
||||||
else:
|
else:
|
||||||
self.userinfo = _('Swap failed!')
|
self.userinfo = _('Swap failed!')
|
||||||
self.state = QESwapHelper.State.Failed
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
except concurrent.futures.CancelledError:
|
|
||||||
self._wallet.wallet.lnworker.swap_manager.cancel_normal_swap(self._swap)
|
|
||||||
self.userinfo = _('Swap cancelled')
|
|
||||||
self.state = QESwapHelper.State.Cancelled
|
|
||||||
except Exception as e:
|
|
||||||
try: # swaphelper might be destroyed at this point
|
|
||||||
self.state = QESwapHelper.State.Failed
|
self.state = QESwapHelper.State.Failed
|
||||||
self.userinfo = _('Error') + ': ' + str(e)
|
except RuntimeError:
|
||||||
self._logger.error(str(e))
|
pass
|
||||||
except RuntimeError:
|
except asyncio.CancelledError:
|
||||||
pass
|
self._wallet.wallet.lnworker.swap_manager.cancel_normal_swap(self._swap)
|
||||||
finally:
|
self.userinfo = _('Swap cancelled')
|
||||||
try: # swaphelper might be destroyed at this point
|
self.state = QESwapHelper.State.Cancelled
|
||||||
self.canCancel = False
|
except Exception as e:
|
||||||
self._swap = None
|
try: # swaphelper might be destroyed at this point
|
||||||
self._fut_htlc_wait = None
|
self.state = QESwapHelper.State.Failed
|
||||||
except RuntimeError:
|
self.userinfo = _('Error') + ': ' + str(e)
|
||||||
pass
|
self._logger.error(str(e))
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
try: # swaphelper might be destroyed at this point
|
||||||
|
self.canCancel = False
|
||||||
|
self._swap = None
|
||||||
|
self._fut_htlc_wait = None
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
threading.Thread(target=swap_task, daemon=True).start()
|
asyncio.run_coroutine_threadsafe(swap_task(), get_asyncio_loop())
|
||||||
|
|
||||||
def _create_tx(self, onchain_amount: Union[int, str, None]) -> PartialTransaction:
|
def _create_tx(self, onchain_amount: Union[int, str, None]) -> PartialTransaction:
|
||||||
# TODO: func taken from qt GUI, this should be common code
|
# TODO: func taken from qt GUI, this should be common code
|
||||||
@@ -623,41 +664,37 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
|||||||
if lightning_amount is None or onchain_amount is None:
|
if lightning_amount is None or onchain_amount is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
def swap_task():
|
async def swap_task():
|
||||||
|
assert self.swap_transport is not None, "Swap transport not available"
|
||||||
swap_manager = self._wallet.wallet.lnworker.swap_manager
|
swap_manager = self._wallet.wallet.lnworker.swap_manager
|
||||||
loop = get_asyncio_loop()
|
try:
|
||||||
with self._wallet.wallet.lnworker.swap_manager.create_transport() as transport:
|
self.userinfo = _('Performing swap...')
|
||||||
async def coro():
|
self.state = QESwapHelper.State.Started
|
||||||
await swap_manager.is_initialized.wait()
|
await swap_manager.is_initialized.wait()
|
||||||
return await swap_manager.reverse_swap(
|
txid = await swap_manager.reverse_swap(
|
||||||
transport,
|
self.swap_transport,
|
||||||
lightning_amount_sat=lightning_amount,
|
lightning_amount_sat=lightning_amount,
|
||||||
expected_onchain_amount_sat=onchain_amount + swap_manager.get_swap_tx_fee(),
|
expected_onchain_amount_sat=onchain_amount + swap_manager.get_swap_tx_fee(),
|
||||||
)
|
)
|
||||||
try:
|
try: # swaphelper might be destroyed at this point
|
||||||
fut = asyncio.run_coroutine_threadsafe(coro(), loop)
|
if txid:
|
||||||
self.userinfo = _('Performing swap...')
|
self.userinfo = _('Success!')
|
||||||
self.state = QESwapHelper.State.Started
|
self.state = QESwapHelper.State.Success
|
||||||
txid = fut.result()
|
else:
|
||||||
try: # swaphelper might be destroyed at this point
|
self.userinfo = _('Swap failed!')
|
||||||
if txid:
|
|
||||||
self.userinfo = _('Success!')
|
|
||||||
self.state = QESwapHelper.State.Success
|
|
||||||
else:
|
|
||||||
self.userinfo = _('Swap failed!')
|
|
||||||
self.state = QESwapHelper.State.Failed
|
|
||||||
except RuntimeError:
|
|
||||||
pass
|
|
||||||
except Exception as e:
|
|
||||||
try: # swaphelper might be destroyed at this point
|
|
||||||
self.state = QESwapHelper.State.Failed
|
self.state = QESwapHelper.State.Failed
|
||||||
msg = _('Timeout') if isinstance(e, TimeoutError) else str(e)
|
except RuntimeError:
|
||||||
self.userinfo = _('Error') + ': ' + msg
|
pass
|
||||||
self._logger.error(str(e))
|
except Exception as e:
|
||||||
except RuntimeError:
|
try: # swaphelper might be destroyed at this point
|
||||||
pass
|
self.state = QESwapHelper.State.Failed
|
||||||
|
msg = _('Timeout') if isinstance(e, TimeoutError) else str(e)
|
||||||
|
self.userinfo = _('Error') + ': ' + msg
|
||||||
|
self._logger.error(str(e))
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
threading.Thread(target=swap_task, daemon=True).start()
|
asyncio.run_coroutine_threadsafe(swap_task(), get_asyncio_loop())
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def executeSwap(self):
|
def executeSwap(self):
|
||||||
|
|||||||
@@ -1333,6 +1333,12 @@ class SwapServerTransport(Logger):
|
|||||||
def __exit__(self, ex_type, ex, tb):
|
def __exit__(self, ex_type, ex, tb):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
pass
|
||||||
|
|
||||||
async def send_request_to_server(self, method: str, request_data: Optional[dict]) -> dict:
|
async def send_request_to_server(self, method: str, request_data: Optional[dict]) -> dict:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -1358,6 +1364,13 @@ class HttpTransport(SwapServerTransport):
|
|||||||
def __exit__(self, ex_type, ex, tb):
|
def __exit__(self, ex_type, ex, tb):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
asyncio.create_task(self.get_pairs())
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
pass
|
||||||
|
|
||||||
async def send_request_to_server(self, method, request_data):
|
async def send_request_to_server(self, method, request_data):
|
||||||
response = await self.network.async_send_http_on_proxy(
|
response = await self.network.async_send_http_on_proxy(
|
||||||
'post' if request_data else 'get',
|
'post' if request_data else 'get',
|
||||||
@@ -1418,6 +1431,13 @@ class NostrTransport(SwapServerTransport):
|
|||||||
fut = asyncio.run_coroutine_threadsafe(self.stop(), self.network.asyncio_loop)
|
fut = asyncio.run_coroutine_threadsafe(self.stop(), self.network.asyncio_loop)
|
||||||
fut.result(timeout=5)
|
fut.result(timeout=5)
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
asyncio.create_task(self.main_loop())
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
await wait_for2(self.stop(), timeout=5)
|
||||||
|
|
||||||
@log_exceptions
|
@log_exceptions
|
||||||
async def main_loop(self):
|
async def main_loop(self):
|
||||||
self.logger.info(f'starting nostr transport with pubkey: {self.nostr_pubkey}')
|
self.logger.info(f'starting nostr transport with pubkey: {self.nostr_pubkey}')
|
||||||
|
|||||||
Reference in New Issue
Block a user