Skip to content

Commit

Permalink
swapserver: cltv budget
Browse files Browse the repository at this point in the history
  • Loading branch information
ecdsa committed Nov 8, 2023
1 parent f708e7f commit d68036d
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 12 deletions.
4 changes: 2 additions & 2 deletions electrum/lnutil.py
Expand Up @@ -1647,9 +1647,9 @@ class PaymentFeeBudget(NamedTuple):
#num_htlc: int

@classmethod
def default(cls, *, invoice_amount_msat: int) -> 'PaymentFeeBudget':
def default(cls, *, invoice_amount_msat: int, cltv_budget:int = None) -> 'PaymentFeeBudget':
from .lnrouter import get_default_fee_budget_msat
return PaymentFeeBudget(
fee_msat=get_default_fee_budget_msat(invoice_amount_msat=invoice_amount_msat),
cltv=NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE,
cltv=cltv_budget or NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE,
)
9 changes: 8 additions & 1 deletion electrum/lnworker.py
Expand Up @@ -1395,6 +1395,7 @@ async def pay_invoice(
attempts: int = None, # used only in unit tests
full_path: LNPaymentPath = None,
channels: Optional[Sequence[Channel]] = None,
max_cltv_expiry: int = None,
) -> Tuple[bool, List[HtlcLog]]:

lnaddr = self._check_invoice(invoice, amount_msat=amount_msat)
Expand All @@ -1421,7 +1422,13 @@ async def pay_invoice(
f"using_trampoline={self.uses_trampoline()}. "
f"invoice_features={invoice_features.get_names()}")
self.set_invoice_status(key, PR_INFLIGHT)
budget = PaymentFeeBudget.default(invoice_amount_msat=amount_to_pay)
if max_cltv_expiry:
total_cltv_budget = max_cltv_expiry - self.network.get_local_height()
cltv_budget = total_cltv_budget - min_final_cltv_delta
self.logger.info(f'max_cltv_expiry {max_cltv_expiry} {total_cltv_budget} {cltv_budget}')
else:
cltv_budget = None
budget = PaymentFeeBudget.default(invoice_amount_msat=amount_to_pay, cltv_budget=cltv_budget)
success = False
try:
await self.pay_to_node(
Expand Down
23 changes: 14 additions & 9 deletions electrum/submarine_swaps.py
Expand Up @@ -19,7 +19,7 @@
from .lnutil import REDEEM_AFTER_DOUBLE_SPENT_DELAY
from .bitcoin import dust_threshold, DummyAddress
from .logging import Logger
from .lnutil import hex_to_bytes
from .lnutil import hex_to_bytes, NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE
from .lnaddr import lndecode
from .json_db import StoredObject, stored_in
from . import constants
Expand All @@ -45,7 +45,7 @@
LOCKUP_FEE_SIZE = 153 # assuming 1 output, 2 outputs

MIN_LOCKTIME_DELTA = 60
LOCKTIME_DELTA_REFUND = 70
LOCKTIME_DELTA_REFUND = 144 * 2

WITNESS_TEMPLATE_SWAP = [
opcodes.OP_HASH160,
Expand Down Expand Up @@ -233,7 +233,12 @@ async def pay_invoice(self, key):
self.invoices_to_pay[key] = 1000000000000 # lock
try:
invoice = self.wallet.get_invoice(key)
success, log = await self.lnworker.pay_invoice(invoice.lightning_invoice, attempts=10)
swap = self.swaps[key]
success, log = await self.lnworker.pay_invoice(
invoice.lightning_invoice,
attempts=10,
max_cltv_expiry=swap.locktime - 10, # the htlc must expire before a refund is possible
)
except Exception as e:
self.logger.info(f'exception paying {key}, will not retry')
self.invoices_to_pay.pop(key, None)
Expand Down Expand Up @@ -279,7 +284,7 @@ async def _claim_swap(self, swap: SwapData) -> None:
if not self.lnwatcher.adb.is_up_to_date():
return
current_height = self.network.get_local_height()
delta = current_height - swap.locktime
remaining_time = swap.locktime - current_height
txos = self.lnwatcher.adb.get_addr_outputs(swap.lockup_address)

for txin in txos.values():
Expand All @@ -292,7 +297,7 @@ async def _claim_swap(self, swap: SwapData) -> None:
txin = None
# if it is a normal swap, we might have double spent the funding tx
# in that case we need to fail the HTLCs
if delta >= 0:
if remaining_time <= 0:
self._fail_swap(swap, 'expired')

if txin:
Expand Down Expand Up @@ -341,7 +346,7 @@ async def _claim_swap(self, swap: SwapData) -> None:
if spent_height > 0:
self._fail_swap(swap, 'refund tx confirmed')
return
if delta < 0:
if remaining_time > 0:
# too early for refund
return
else:
Expand All @@ -351,10 +356,10 @@ async def _claim_swap(self, swap: SwapData) -> None:
if funding_height.conf <= 0:
return
key = swap.payment_hash.hex()
if -delta <= MIN_LOCKTIME_DELTA:
if remaining_time <= MIN_LOCKTIME_DELTA:
if key in self.invoices_to_pay:
# fixme: should consider cltv of ln payment
self.logger.info(f'locktime too close {key} {delta}')
self.logger.info(f'locktime too close {key} {remaining_time}')
self.invoices_to_pay.pop(key, None)
return
if key not in self.invoices_to_pay:
Expand Down Expand Up @@ -671,7 +676,7 @@ async def request_normal_swap(self, lightning_amount_sat, expected_onchain_amoun
raise Exception(f"fswap check failed: onchain_amount is more than what we estimated: "
f"{onchain_amount} > {expected_onchain_amount_sat}")
# verify that they are not locking up funds for more than a day
if locktime - self.network.get_local_height() >= 144:
if locktime - self.network.get_local_height() >= NBLOCK_CLTV_DELTA_TOO_FAR_INTO_FUTURE:
raise Exception("fswap check failed: locktime too far in future")

swap, invoice, _ = self.add_normal_swap(
Expand Down

0 comments on commit d68036d

Please sign in to comment.