Merge pull request #10157 from SomberNight/202508_swap_sanity_check_costs
swaps: add sanity-check for total swap costs
This commit is contained in:
@@ -13,7 +13,10 @@ from electrum.i18n import _
|
||||
from electrum.logging import Logger
|
||||
from electrum.bitcoin import DummyAddress
|
||||
from electrum.plugin import run_hook
|
||||
from electrum.util import NotEnoughFunds, NoDynamicFeeEstimates, parse_max_spend, UserCancelled, ChoiceItem
|
||||
from electrum.util import (
|
||||
NotEnoughFunds, NoDynamicFeeEstimates, parse_max_spend, UserCancelled, ChoiceItem,
|
||||
UserFacingException,
|
||||
)
|
||||
from electrum.invoices import PR_PAID, Invoice, PR_BROADCASTING, PR_BROADCAST
|
||||
from electrum.transaction import Transaction, PartialTxInput, PartialTxOutput
|
||||
from electrum.network import TxBroadcastError, BestEffortRequestFailed
|
||||
@@ -338,7 +341,7 @@ class SendTab(QWidget, MessageBoxMixin, Logger):
|
||||
coro = sm.request_swap_for_amount(transport=transport, onchain_amount=swap_dummy_output.value)
|
||||
try:
|
||||
swap, swap_invoice = self.window.run_coroutine_dialog(coro, _('Requesting swap invoice...'))
|
||||
except SwapServerError as e:
|
||||
except (SwapServerError, UserFacingException) as e:
|
||||
self.show_error(str(e))
|
||||
return
|
||||
except UserCancelled:
|
||||
|
||||
@@ -33,7 +33,7 @@ from .transaction import (
|
||||
from .util import (
|
||||
log_exceptions, ignore_exceptions, BelowDustLimit, OldTaskGroup, ca_path, gen_nostr_ann_pow,
|
||||
get_nostr_ann_pow_amount, make_aiohttp_proxy_connector, get_running_loop, get_asyncio_loop, wait_for2,
|
||||
run_sync_function_on_asyncio_thread, trigger_callback, NoDynamicFeeEstimates
|
||||
run_sync_function_on_asyncio_thread, trigger_callback, NoDynamicFeeEstimates, UserFacingException,
|
||||
)
|
||||
from . import lnutil
|
||||
from .lnutil import hex_to_bytes, REDEEM_AFTER_DOUBLE_SPENT_DELAY, Keypair
|
||||
@@ -507,6 +507,26 @@ class SwapManager(Logger):
|
||||
fee_policy = FeePolicy(policy_descriptor)
|
||||
return fee_policy.estimate_fee(SWAP_TX_SIZE, network=self.network, allow_fallback_to_static_rates=True)
|
||||
|
||||
def _sanity_check_swap_costs(
|
||||
self,
|
||||
*,
|
||||
incoming_sat: int,
|
||||
outgoing_sat: int,
|
||||
) -> None:
|
||||
"""The user should have already seen the swap amounts, and hence the cost.
|
||||
These are just some last-minute sanity checks that the cost of the swap is not insane.
|
||||
"""
|
||||
costs_abs = outgoing_sat - incoming_sat
|
||||
costs_ratio = 1 - incoming_sat / outgoing_sat
|
||||
if costs_abs < 10_000: # "small" amounts are exempt from checks
|
||||
return
|
||||
exc = UserFacingException(_("Total swap costs are insane.") + f"\n({costs_ratio=}, {costs_abs=} sat)")
|
||||
if costs_ratio > 0.25:
|
||||
raise exc
|
||||
if costs_abs > 1_000_000:
|
||||
if costs_ratio > 0.15:
|
||||
raise exc
|
||||
|
||||
def get_swap(self, payment_hash: bytes) -> Optional[SwapData]:
|
||||
# for history
|
||||
swap = self._swaps.get(payment_hash.hex())
|
||||
@@ -755,6 +775,8 @@ class SwapManager(Logger):
|
||||
expected_onchain_amount_sat: int,
|
||||
channels: Optional[Sequence['Channel']] = None,
|
||||
) -> Tuple[SwapData, str]:
|
||||
self._sanity_check_swap_costs(
|
||||
incoming_sat=lightning_amount_sat, outgoing_sat=expected_onchain_amount_sat)
|
||||
await self.is_initialized.wait() # add timeout
|
||||
refund_privkey = os.urandom(32)
|
||||
refund_pubkey = ECPrivkey(refund_privkey).get_public_key_bytes(compressed=True)
|
||||
@@ -927,6 +949,8 @@ class SwapManager(Logger):
|
||||
"""
|
||||
assert self.network
|
||||
assert self.lnwatcher
|
||||
self._sanity_check_swap_costs(
|
||||
incoming_sat=expected_onchain_amount_sat, outgoing_sat=lightning_amount_sat)
|
||||
privkey = os.urandom(32)
|
||||
our_pubkey = ECPrivkey(privkey).get_public_key_bytes(compressed=True)
|
||||
preimage = os.urandom(32)
|
||||
|
||||
Reference in New Issue
Block a user