lnchannel: refactor can_send_ctx_updates
This adds a lot more sanity checks to lnpeer/lnchannel for chan update operations, and more logging in case they are violated.
This commit is contained in:
@@ -1057,18 +1057,21 @@ class Channel(AbstractChannel):
|
||||
def set_can_send_ctx_updates(self, b: bool) -> None:
|
||||
self._can_send_ctx_updates = b
|
||||
|
||||
def can_send_ctx_updates(self) -> bool:
|
||||
"""Whether we can send update_fee, update_*_htlc changes to the remote."""
|
||||
def can_update_ctx(self, *, proposer: HTLCOwner) -> bool:
|
||||
"""Whether proposer is allowed to send commitment_signed, revoke_and_ack,
|
||||
and update_* messages.
|
||||
"""
|
||||
if self.get_state() not in (ChannelState.OPEN, ChannelState.SHUTDOWN):
|
||||
return False
|
||||
if self.peer_state != PeerState.GOOD:
|
||||
return False
|
||||
if not self._can_send_ctx_updates:
|
||||
return False
|
||||
if proposer == LOCAL:
|
||||
if not self._can_send_ctx_updates:
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_send_update_add_htlc(self) -> bool:
|
||||
return self.can_send_ctx_updates() and self.is_open()
|
||||
return self.can_update_ctx(proposer=LOCAL) and self.is_open()
|
||||
|
||||
def is_frozen_for_sending(self) -> bool:
|
||||
if self.lnworker and self.lnworker.uses_trampoline() and not self.lnworker.is_trampoline_peer(self.node_id):
|
||||
@@ -1099,10 +1102,10 @@ class Channel(AbstractChannel):
|
||||
ctn = self.get_next_ctn(htlc_receiver)
|
||||
chan_config = self.config[htlc_receiver]
|
||||
if self.get_state() != ChannelState.OPEN:
|
||||
raise PaymentFailure('Channel not open', self.get_state())
|
||||
raise PaymentFailure(f"Channel not open. {self.get_state()!r}")
|
||||
if not self.can_update_ctx(proposer=htlc_proposer):
|
||||
raise PaymentFailure(f"cannot update channel. {self.get_state()!r} {self.peer_state!r}")
|
||||
if htlc_proposer == LOCAL:
|
||||
if not self.can_send_ctx_updates():
|
||||
raise PaymentFailure('Channel cannot send ctx updates')
|
||||
if not self.can_send_update_add_htlc():
|
||||
raise PaymentFailure('Channel cannot add htlc')
|
||||
|
||||
@@ -1237,6 +1240,7 @@ class Channel(AbstractChannel):
|
||||
# TODO: when more channel types are supported, this method should depend on channel type
|
||||
next_remote_ctn = self.get_next_ctn(REMOTE)
|
||||
self.logger.info(f"sign_next_commitment. ctn={next_remote_ctn}")
|
||||
assert not self.is_closed(), self.get_state()
|
||||
|
||||
pending_remote_commitment = self.get_next_commitment(REMOTE)
|
||||
sig_64 = sign_and_get_sig_string(pending_remote_commitment, self.config[LOCAL], self.config[REMOTE])
|
||||
@@ -1284,6 +1288,7 @@ class Channel(AbstractChannel):
|
||||
# TODO: when more channel types are supported, this method should depend on channel type
|
||||
next_local_ctn = self.get_next_ctn(LOCAL)
|
||||
self.logger.info(f"receive_new_commitment. ctn={next_local_ctn}, len(htlc_sigs)={len(htlc_sigs)}")
|
||||
assert not self.is_closed(), self.get_state()
|
||||
|
||||
assert len(htlc_sigs) == 0 or type(htlc_sigs[0]) is bytes
|
||||
|
||||
@@ -1362,6 +1367,7 @@ class Channel(AbstractChannel):
|
||||
|
||||
def revoke_current_commitment(self):
|
||||
self.logger.info("revoke_current_commitment")
|
||||
assert not self.is_closed(), self.get_state()
|
||||
new_ctn = self.get_latest_ctn(LOCAL)
|
||||
new_ctx = self.get_latest_commitment(LOCAL)
|
||||
if not self.signature_fits(new_ctx):
|
||||
@@ -1375,6 +1381,7 @@ class Channel(AbstractChannel):
|
||||
|
||||
def receive_revocation(self, revocation: RevokeAndAck):
|
||||
self.logger.info("receive_revocation")
|
||||
assert not self.is_closed(), self.get_state()
|
||||
new_ctn = self.get_latest_ctn(REMOTE)
|
||||
cur_point = self.config[REMOTE].current_per_commitment_point
|
||||
derived_point = ecc.ECPrivkey(revocation.per_commitment_secret).get_public_key_bytes(compressed=True)
|
||||
@@ -1687,7 +1694,7 @@ class Channel(AbstractChannel):
|
||||
Action must be initiated by LOCAL.
|
||||
"""
|
||||
self.logger.info("settle_htlc")
|
||||
assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
assert self.can_update_ctx(proposer=LOCAL), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
htlc = self.hm.get_htlc_by_id(REMOTE, htlc_id)
|
||||
if htlc.payment_hash != sha256(preimage):
|
||||
raise Exception("incorrect preimage for HTLC")
|
||||
@@ -1704,6 +1711,7 @@ class Channel(AbstractChannel):
|
||||
Action must be initiated by REMOTE.
|
||||
"""
|
||||
self.logger.info("receive_htlc_settle")
|
||||
assert self.can_update_ctx(proposer=REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
|
||||
if htlc.payment_hash != sha256(preimage):
|
||||
raise RemoteMisbehaving("received incorrect preimage for HTLC")
|
||||
@@ -1716,7 +1724,7 @@ class Channel(AbstractChannel):
|
||||
Action must be initiated by LOCAL.
|
||||
"""
|
||||
self.logger.info("fail_htlc")
|
||||
assert self.can_send_ctx_updates(), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
assert self.can_update_ctx(proposer=LOCAL), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
with self.db_lock:
|
||||
self.hm.send_fail(htlc_id)
|
||||
|
||||
@@ -1727,6 +1735,7 @@ class Channel(AbstractChannel):
|
||||
Action must be initiated by REMOTE.
|
||||
"""
|
||||
self.logger.info("receive_fail_htlc")
|
||||
assert self.can_update_ctx(proposer=REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}"
|
||||
with self.db_lock:
|
||||
self.hm.recv_fail(htlc_id)
|
||||
self._receive_fail_reasons[htlc_id] = (error_bytes, reason)
|
||||
@@ -1760,9 +1769,9 @@ class Channel(AbstractChannel):
|
||||
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.")
|
||||
assert self.can_update_ctx(proposer=LOCAL if from_us else REMOTE), f"cannot update channel. {self.get_state()!r} {self.peer_state!r}. {from_us=}"
|
||||
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}"
|
||||
self.hm.send_update_fee(feerate)
|
||||
else:
|
||||
self.hm.recv_update_fee(feerate)
|
||||
|
||||
Reference in New Issue
Block a user