|
|
@ -1054,53 +1054,44 @@ class Channel(AbstractChannel): |
|
|
|
assert type(subject) is HTLCOwner |
|
|
|
sender = subject |
|
|
|
receiver = subject.inverted() |
|
|
|
ctx_owner = receiver |
|
|
|
# TODO but what about the other ctx? BOLT-02 only talks about checking the receiver's ctx, |
|
|
|
# however the channel reserve is only meaningful if we also check the sender's ctx! |
|
|
|
# in particular, note that dust limits can be different between the parties! |
|
|
|
# but due to the racy nature of this, we cannot be sure exactly what the sender's |
|
|
|
# next ctx will look like (e.g. what feerate it will use). hmmm :/ |
|
|
|
ctn = self.get_next_ctn(ctx_owner) |
|
|
|
sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn) |
|
|
|
receiver_balance_msat = self.balance_minus_outgoing_htlcs(whose=receiver, ctx_owner=ctx_owner, ctn=ctn) |
|
|
|
sender_reserve_msat = self.config[receiver].reserve_sat * 1000 |
|
|
|
receiver_reserve_msat = self.config[sender].reserve_sat * 1000 |
|
|
|
initiator = LOCAL if self.constraints.is_initiator else REMOTE |
|
|
|
# the initiator/funder pays on-chain fees |
|
|
|
num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn) + self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn)) |
|
|
|
feerate = self.get_feerate(ctx_owner, ctn=ctn) |
|
|
|
ctx_fees_msat = calc_fees_for_commitment_tx( |
|
|
|
num_htlcs=num_htlcs_in_ctx, |
|
|
|
feerate=feerate, |
|
|
|
is_local_initiator=self.constraints.is_initiator, |
|
|
|
round_to_sat=False, |
|
|
|
) |
|
|
|
# note: if this supposed new HTLC is large enough to create an output, the initiator needs to pay for that too |
|
|
|
# note: if sender != initiator, both the sender and the receiver need to "afford" the payment |
|
|
|
htlc_fee_msat = fee_for_htlc_output(feerate=feerate) |
|
|
|
# TODO stuck channels. extra funder reserve? "fee spike buffer" (maybe only if "strict") |
|
|
|
# see https://github.com/lightningnetwork/lightning-rfc/issues/728 |
|
|
|
# note: in terms of on-chain outputs, as we are considering the htlc_receiver's ctx, this is a "received" HTLC |
|
|
|
htlc_trim_threshold_msat = received_htlc_trim_threshold_sat(dust_limit_sat=self.config[receiver].dust_limit_sat, feerate=feerate) * 1000 |
|
|
|
if strict: |
|
|
|
# also consider the other ctx, where the trim threshold is different |
|
|
|
# note: the 'feerate' we use is not technically correct but we have no way |
|
|
|
# of knowing the actual future feerate ahead of time (this is a protocol bug) |
|
|
|
htlc_trim_threshold_msat = min(htlc_trim_threshold_msat, |
|
|
|
offered_htlc_trim_threshold_sat(dust_limit_sat=self.config[sender].dust_limit_sat, feerate=feerate) * 1000) |
|
|
|
max_send_msat = sender_balance_msat - sender_reserve_msat - ctx_fees_msat[sender] |
|
|
|
if max_send_msat < htlc_trim_threshold_msat: |
|
|
|
# there will be no corresponding HTLC output |
|
|
|
return max_send_msat |
|
|
|
if sender == initiator: |
|
|
|
max_send_after_htlc_fee_msat = max_send_msat - htlc_fee_msat |
|
|
|
max_send_msat = max(htlc_trim_threshold_msat - 1, max_send_after_htlc_fee_msat) |
|
|
|
return max_send_msat |
|
|
|
else: |
|
|
|
# the receiver is the initiator, so they need to be able to pay tx fees |
|
|
|
if receiver_balance_msat - receiver_reserve_msat - ctx_fees_msat[receiver] - htlc_fee_msat < 0: |
|
|
|
max_send_msat = htlc_trim_threshold_msat - 1 |
|
|
|
return max_send_msat |
|
|
|
initiator = LOCAL if self.constraints.is_initiator else REMOTE # the initiator/funder pays on-chain fees |
|
|
|
|
|
|
|
def consider_ctx(*, ctx_owner: HTLCOwner) -> int: |
|
|
|
ctn = self.get_next_ctn(ctx_owner) |
|
|
|
sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn) |
|
|
|
receiver_balance_msat = self.balance_minus_outgoing_htlcs(whose=receiver, ctx_owner=ctx_owner, ctn=ctn) |
|
|
|
sender_reserve_msat = self.config[receiver].reserve_sat * 1000 |
|
|
|
receiver_reserve_msat = self.config[sender].reserve_sat * 1000 |
|
|
|
num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn) + self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn)) |
|
|
|
feerate = self.get_feerate(ctx_owner, ctn=ctn) |
|
|
|
ctx_fees_msat = calc_fees_for_commitment_tx( |
|
|
|
num_htlcs=num_htlcs_in_ctx, |
|
|
|
feerate=feerate, |
|
|
|
is_local_initiator=self.constraints.is_initiator, |
|
|
|
round_to_sat=False, |
|
|
|
) |
|
|
|
htlc_fee_msat = fee_for_htlc_output(feerate=feerate) |
|
|
|
htlc_trim_func = received_htlc_trim_threshold_sat if ctx_owner == receiver else offered_htlc_trim_threshold_sat |
|
|
|
htlc_trim_threshold_msat = htlc_trim_func(dust_limit_sat=self.config[ctx_owner].dust_limit_sat, feerate=feerate) * 1000 |
|
|
|
max_send_msat = sender_balance_msat - sender_reserve_msat - ctx_fees_msat[sender] |
|
|
|
if max_send_msat < htlc_trim_threshold_msat: |
|
|
|
# there will be no corresponding HTLC output |
|
|
|
return max_send_msat |
|
|
|
if sender == initiator: |
|
|
|
max_send_after_htlc_fee_msat = max_send_msat - htlc_fee_msat |
|
|
|
max_send_msat = max(htlc_trim_threshold_msat - 1, max_send_after_htlc_fee_msat) |
|
|
|
return max_send_msat |
|
|
|
else: |
|
|
|
# the receiver is the initiator, so they need to be able to pay tx fees |
|
|
|
if receiver_balance_msat - receiver_reserve_msat - ctx_fees_msat[receiver] - htlc_fee_msat < 0: |
|
|
|
max_send_msat = htlc_trim_threshold_msat - 1 |
|
|
|
return max_send_msat |
|
|
|
|
|
|
|
max_send_msat = min(consider_ctx(ctx_owner=receiver), |
|
|
|
consider_ctx(ctx_owner=sender)) |
|
|
|
max_send_msat = max(max_send_msat, 0) |
|
|
|
return max_send_msat |
|
|
|
|
|
|
|
|
|
|
|
def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None) -> Sequence[UpdateAddHtlc]: |
|
|
|
"""Returns list of non-dust HTLCs for subject's commitment tx at ctn, |
|
|
|