Browse Source

lnchannel: update_fee: improve "can afford" check

bip39-recovery
SomberNight 5 years ago
parent
commit
996799d79e
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 31
      electrum/lnchannel.py
  2. 16
      electrum/lnutil.py

31
electrum/lnchannel.py

@ -1108,7 +1108,8 @@ class Channel(AbstractChannel):
return max_send_msat
def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None) -> Sequence[UpdateAddHtlc]:
def included_htlcs(self, subject: HTLCOwner, direction: Direction, ctn: int = None, *,
feerate: int = None) -> Sequence[UpdateAddHtlc]:
"""Returns list of non-dust HTLCs for subject's commitment tx at ctn,
filtered by direction (of HTLCs).
"""
@ -1116,7 +1117,8 @@ class Channel(AbstractChannel):
assert type(direction) is Direction
if ctn is None:
ctn = self.get_oldest_unrevoked_ctn(subject)
feerate = self.get_feerate(subject, ctn=ctn)
if feerate is None:
feerate = self.get_feerate(subject, ctn=ctn)
conf = self.config[subject]
if direction == RECEIVED:
threshold_sat = received_htlc_trim_threshold_sat(dust_limit_sat=conf.dust_limit_sat, feerate=feerate)
@ -1254,8 +1256,22 @@ class Channel(AbstractChannel):
# feerate uses sat/kw
if self.constraints.is_initiator != from_us:
raise Exception(f"Cannot update_fee: wrong initiator. us: {from_us}")
# TODO check that funder can afford the new on-chain fees (+ channel reserve)
# (maybe check both ctxs, at least if from_us is True??)
sender = LOCAL if from_us else REMOTE
ctx_owner = -sender
ctn = self.get_next_ctn(ctx_owner)
sender_balance_msat = self.balance_minus_outgoing_htlcs(whose=sender, ctx_owner=ctx_owner, ctn=ctn)
sender_reserve_msat = self.config[-sender].reserve_sat * 1000
num_htlcs_in_ctx = len(self.included_htlcs(ctx_owner, SENT, ctn=ctn, feerate=feerate) +
self.included_htlcs(ctx_owner, RECEIVED, ctn=ctn, feerate=feerate))
ctx_fees_msat = calc_fees_for_commitment_tx(
num_htlcs=num_htlcs_in_ctx,
feerate=feerate,
is_local_initiator=self.constraints.is_initiator,
)
remainder = sender_balance_msat - sender_reserve_msat - ctx_fees_msat[sender]
if remainder < 0:
raise Exception(f"Cannot update_fee. {sender} tried to update fee but they cannot afford it. "
f"Their balance would go below reserve: {remainder} msat missing.")
with self.db_lock:
if from_us:
assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
@ -1302,13 +1318,6 @@ class Channel(AbstractChannel):
is_local_initiator=self.constraints.is_initiator == (subject == LOCAL),
)
# TODO: we need to also include the respective channel reserves here, but not at the
# beginning of the channel lifecycle when the reserve might not be met yet
if remote_msat - onchain_fees[REMOTE] < 0:
raise Exception(f"negative remote_msat in make_commitment: {remote_msat}")
if local_msat - onchain_fees[LOCAL] < 0:
raise Exception(f"negative local_msat in make_commitment: {local_msat}")
if self.is_static_remotekey_enabled():
payment_pubkey = other_config.payment_basepoint.pubkey
else:

16
electrum/lnutil.py

@ -639,8 +639,12 @@ class HTLCOwner(IntFlag):
LOCAL = 1
REMOTE = -LOCAL
def inverted(self):
return HTLCOwner(-self)
def inverted(self) -> 'HTLCOwner':
return -self
def __neg__(self) -> 'HTLCOwner':
return HTLCOwner(super().__neg__())
class Direction(IntFlag):
SENT = -1 # in the context of HTLCs: "offered" HTLCs
@ -654,10 +658,14 @@ REMOTE = HTLCOwner.REMOTE
def make_commitment_outputs(*, fees_per_participant: Mapping[HTLCOwner, int], local_amount_msat: int, remote_amount_msat: int,
local_script: str, remote_script: str, htlcs: List[ScriptHtlc], dust_limit_sat: int) -> Tuple[List[PartialTxOutput], List[PartialTxOutput]]:
to_local_amt = local_amount_msat - fees_per_participant[LOCAL]
# BOLT-03: "Base commitment transaction fees are extracted from the funder's amount;
# if that amount is insufficient, the entire amount of the funder's output is used."
# -> if funder cannot afford feerate, their output might go negative, so take max(0, x) here:
to_local_amt = max(0, local_amount_msat - fees_per_participant[LOCAL])
to_local = PartialTxOutput(scriptpubkey=bfh(local_script), value=to_local_amt // 1000)
to_remote_amt = remote_amount_msat - fees_per_participant[REMOTE]
to_remote_amt = max(0, remote_amount_msat - fees_per_participant[REMOTE])
to_remote = PartialTxOutput(scriptpubkey=bfh(remote_script), value=to_remote_amt // 1000)
non_htlc_outputs = [to_local, to_remote]
htlc_outputs = []
for script, htlc in htlcs:

Loading…
Cancel
Save