swaps: add sanity check to reverse swap mining fee
This commit is contained in:
@@ -1975,7 +1975,7 @@ class Commands(Logger):
|
||||
arg:decimal_or_dryrun:onchain_amount:Amount to be sent, in BTC. Set it to 'dryrun' to receive a value
|
||||
"""
|
||||
sm = wallet.lnworker.swap_manager
|
||||
with sm.create_transport() as transport:
|
||||
async with sm.create_transport() as transport:
|
||||
await sm.is_initialized.wait()
|
||||
if lightning_amount == 'dryrun':
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
@@ -2002,37 +2002,46 @@ class Commands(Logger):
|
||||
}
|
||||
|
||||
@command('wnpl')
|
||||
async def reverse_swap(self, lightning_amount, onchain_amount, password=None, wallet: Abstract_Wallet = None):
|
||||
async def reverse_swap(self, lightning_amount, onchain_amount, provider_mining_fee, password=None, wallet: Abstract_Wallet = None):
|
||||
"""
|
||||
Reverse submarine swap: send on Lightning, receive on-chain
|
||||
|
||||
arg:decimal_or_dryrun:lightning_amount:Amount to be sent, in BTC. Set it to 'dryrun' to receive a value
|
||||
arg:decimal_or_dryrun:onchain_amount:Amount to be received, in BTC. Set it to 'dryrun' to receive a value
|
||||
arg:decimal_or_dryrun:provider_mining_fee:Mining fee required by the swap provider, in BTC. Set it to 'dryrun' to receive a value
|
||||
"""
|
||||
sm = wallet.lnworker.swap_manager
|
||||
with sm.create_transport() as transport:
|
||||
async with sm.create_transport() as transport:
|
||||
await sm.is_initialized.wait()
|
||||
if onchain_amount == 'dryrun':
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
onchain_amount_sat = sm.get_recv_amount(lightning_amount_sat, is_reverse=True)
|
||||
assert provider_mining_fee == "dryrun", f"Cannot use {provider_mining_fee=} in dryrun. Set it to 'dryrun'."
|
||||
provider_mining_fee = sm.mining_fee
|
||||
funding_txid = None
|
||||
elif lightning_amount == 'dryrun':
|
||||
onchain_amount_sat = satoshis(onchain_amount)
|
||||
lightning_amount_sat = sm.get_send_amount(onchain_amount_sat, is_reverse=True)
|
||||
assert provider_mining_fee == "dryrun", f"Cannot use {provider_mining_fee=} in dryrun. Set it to 'dryrun'."
|
||||
provider_mining_fee = sm.mining_fee
|
||||
funding_txid = None
|
||||
else:
|
||||
lightning_amount_sat = satoshis(lightning_amount)
|
||||
claim_fee = sm.get_fee_for_txbatcher()
|
||||
onchain_amount_sat = satoshis(onchain_amount) + claim_fee
|
||||
assert provider_mining_fee != "dryrun", "Provide the 'provder_mining_fee' obtained from the dryrun."
|
||||
provider_mining_fee = satoshis(provider_mining_fee)
|
||||
funding_txid = await wallet.lnworker.swap_manager.reverse_swap(
|
||||
transport=transport,
|
||||
lightning_amount_sat=lightning_amount_sat,
|
||||
expected_onchain_amount_sat=onchain_amount_sat,
|
||||
server_mining_fee_sat=provider_mining_fee,
|
||||
)
|
||||
return {
|
||||
'funding_txid': funding_txid,
|
||||
'lightning_amount': format_satoshis(lightning_amount_sat),
|
||||
'onchain_amount': format_satoshis(onchain_amount_sat),
|
||||
'provider_mining_fee': format_satoshis(provider_mining_fee)
|
||||
}
|
||||
|
||||
@command('n')
|
||||
|
||||
@@ -719,6 +719,7 @@ class QESwapHelper(AuthMixin, QObject, QtEventListener):
|
||||
transport=self.swap_transport,
|
||||
lightning_amount_sat=lightning_amount,
|
||||
expected_onchain_amount_sat=onchain_amount + swap_manager.get_fee_for_txbatcher(),
|
||||
server_mining_fee_sat=self.serverMiningfee.satsInt,
|
||||
)
|
||||
try: # swaphelper might be destroyed at this point
|
||||
if txid:
|
||||
|
||||
@@ -94,6 +94,7 @@ class SwapDialog(WindowModalDialog, QtEventListener):
|
||||
self.swap_limits_label = QLabel()
|
||||
self.fee_label = QLabel()
|
||||
self.server_fee_label = QLabel()
|
||||
self.last_server_mining_fee_sat = None
|
||||
h = QGridLayout()
|
||||
h.addWidget(self.description_label, 0, 0, 1, 3)
|
||||
h.addWidget(self.toggle_button, 0, 3)
|
||||
@@ -153,7 +154,12 @@ class SwapDialog(WindowModalDialog, QtEventListener):
|
||||
@qt_event_listener
|
||||
def on_event_swap_offers_changed(self, recent_offers: Sequence['SwapOffer']):
|
||||
self.set_server_button_text(len(recent_offers))
|
||||
self.update()
|
||||
if not self.ok_button.isEnabled():
|
||||
# only update the dialog with the new offer if the user hasn't entered an amount yet.
|
||||
# if the user has already entered an amount we prefer the swap to fail due to outdated
|
||||
# fees than the possibility of a swap happening with fees the user hasn't seen
|
||||
# due to an update happening just before the user initiated the swap
|
||||
self.update()
|
||||
|
||||
def set_server_button_text(self, offer_count: int):
|
||||
button_text = f' {offer_count} ' + (_('providers') if offer_count != 1 else _('provider'))
|
||||
@@ -282,8 +288,8 @@ class SwapDialog(WindowModalDialog, QtEventListener):
|
||||
f"{self.window.format_amount(max_swap_limit)} {w_base_unit}")
|
||||
self.swap_limits_label.setText(swap_limit_str)
|
||||
self.swap_limits_label.repaint() # macOS hack for #6269
|
||||
server_mining_fee = sm.mining_fee
|
||||
server_fee_str = '%.2f'%sm.percentage + '% + ' + self.window.format_amount(server_mining_fee) + ' ' + w_base_unit
|
||||
self.last_server_mining_fee_sat = sm.mining_fee
|
||||
server_fee_str = '%.2f'%sm.percentage + '% + ' + self.window.format_amount(sm.mining_fee) + ' ' + w_base_unit
|
||||
self.server_fee_label.setText(server_fee_str)
|
||||
self.server_fee_label.repaint() # macOS hack for #6269
|
||||
self.needs_tx_update = True
|
||||
@@ -332,6 +338,7 @@ class SwapDialog(WindowModalDialog, QtEventListener):
|
||||
transport=transport,
|
||||
lightning_amount_sat=lightning_amount,
|
||||
expected_onchain_amount_sat=onchain_amount + self.swap_manager.get_fee_for_txbatcher(),
|
||||
server_mining_fee_sat=self.last_server_mining_fee_sat,
|
||||
)
|
||||
try:
|
||||
# we must not leave the context, so we use run_couroutine_dialog
|
||||
|
||||
@@ -904,6 +904,7 @@ class SwapManager(Logger):
|
||||
transport: 'SwapServerTransport',
|
||||
lightning_amount_sat: int,
|
||||
expected_onchain_amount_sat: int,
|
||||
server_mining_fee_sat: int,
|
||||
channels: Optional[Sequence['Channel']] = None,
|
||||
) -> Optional[str]:
|
||||
"""send on Lightning, receive on-chain
|
||||
@@ -920,6 +921,9 @@ class SwapManager(Logger):
|
||||
- Server fulfills HTLC using preimage.
|
||||
|
||||
Note: expected_onchain_amount_sat is BEFORE deducting the on-chain claim tx fee.
|
||||
Note: server_mining_fee_sat is passed as argument instead of accessing self.mining_fee to ensure
|
||||
the mining fees the user sees in the GUI are also the values used for the checks performed here.
|
||||
We commit to server_mining_fee_sat as it limits the max fee pre-payment amt, which the server is trusted with.
|
||||
"""
|
||||
assert self.network
|
||||
assert self.lnwatcher
|
||||
@@ -977,16 +981,16 @@ class SwapManager(Logger):
|
||||
invoice_amount = int(lnaddr.get_amount_sat())
|
||||
if lnaddr.paymenthash != payment_hash:
|
||||
raise Exception("rswap check failed: inconsistent RHASH and invoice")
|
||||
# check that the lightning amount is what we requested
|
||||
if fee_invoice:
|
||||
fee_lnaddr = self.lnworker._check_bolt11_invoice(fee_invoice)
|
||||
if fee_lnaddr.get_amount_sat() > self.mining_fee * 2:
|
||||
if fee_lnaddr.get_amount_sat() > server_mining_fee_sat * 2:
|
||||
raise SwapServerError(_("Mining fee requested by swap-server larger "
|
||||
"than what was announced in their offer."))
|
||||
invoice_amount += fee_lnaddr.get_amount_sat()
|
||||
prepay_hash = fee_lnaddr.paymenthash
|
||||
else:
|
||||
prepay_hash = None
|
||||
# check that the lightning amount is what we requested
|
||||
if int(invoice_amount) != lightning_amount_sat:
|
||||
raise Exception(f"rswap check failed: invoice_amount ({invoice_amount}) "
|
||||
f"not what we requested ({lightning_amount_sat})")
|
||||
|
||||
Reference in New Issue
Block a user