Browse Source

fix #8683; do not force close channel if we just sent update_fulfill_htlc

master
ThomasV 2 years ago
parent
commit
98f9f295cf
  1. 16
      electrum/lnchannel.py

16
electrum/lnchannel.py

@ -168,6 +168,8 @@ class RemoteCtnTooFarInFuture(Exception): pass
def htlcsum(htlcs: Iterable[UpdateAddHtlc]): def htlcsum(htlcs: Iterable[UpdateAddHtlc]):
return sum([x.amount_msat for x in htlcs]) return sum([x.amount_msat for x in htlcs])
def now():
return int(time.time())
class HTLCWithStatus(NamedTuple): class HTLCWithStatus(NamedTuple):
channel_id: bytes channel_id: bytes
@ -350,8 +352,7 @@ class AbstractChannel(Logger, ABC):
self.set_state(ChannelState.REDEEMED) self.set_state(ChannelState.REDEEMED)
break break
else: else:
now = int(time.time()) if self.lnworker and (now() - self.storage.get('init_timestamp', 0) > CHANNEL_OPENING_TIMEOUT):
if self.lnworker and (now - self.storage.get('init_timestamp', 0) > CHANNEL_OPENING_TIMEOUT):
self.lnworker.remove_channel(self.channel_id) self.lnworker.remove_channel(self.channel_id)
def update_funded_state(self, *, funding_txid: str, funding_height: TxMinedInfo) -> None: def update_funded_state(self, *, funding_txid: str, funding_height: TxMinedInfo) -> None:
@ -668,6 +669,7 @@ class Channel(AbstractChannel):
self.unconfirmed_closing_txid = None # not a state, only for GUI self.unconfirmed_closing_txid = None # not a state, only for GUI
self.sent_channel_ready = False # no need to persist this, because channel_ready is re-sent in channel_reestablish self.sent_channel_ready = False # no need to persist this, because channel_ready is re-sent in channel_reestablish
self.sent_announcement_signatures = False self.sent_announcement_signatures = False
self.htlc_settle_time = {}
def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]: def get_local_scid_alias(self, *, create_new_if_needed: bool = False) -> Optional[bytes]:
"""Get scid_alias to be used for *outgoing* HTLCs. """Get scid_alias to be used for *outgoing* HTLCs.
@ -753,8 +755,7 @@ class Channel(AbstractChannel):
def add_or_update_peer_addr(self, peer: LNPeerAddr) -> None: def add_or_update_peer_addr(self, peer: LNPeerAddr) -> None:
if 'peer_network_addresses' not in self.storage: if 'peer_network_addresses' not in self.storage:
self.storage['peer_network_addresses'] = {} self.storage['peer_network_addresses'] = {}
now = int(time.time()) self.storage['peer_network_addresses'][peer.net_addr_str()] = now()
self.storage['peer_network_addresses'][peer.net_addr_str()] = now
def get_peer_addresses(self) -> Iterator[LNPeerAddr]: def get_peer_addresses(self) -> Iterator[LNPeerAddr]:
# sort by timestamp: most recent first # sort by timestamp: most recent first
@ -776,7 +777,6 @@ class Channel(AbstractChannel):
scid = self.short_channel_id scid = self.short_channel_id
sorted_node_ids = list(sorted([self.node_id, self.get_local_pubkey()])) sorted_node_ids = list(sorted([self.node_id, self.get_local_pubkey()]))
channel_flags = b'\x00' if sorted_node_ids[0] == self.get_local_pubkey() else b'\x01' channel_flags = b'\x00' if sorted_node_ids[0] == self.get_local_pubkey() else b'\x01'
now = int(time.time())
htlc_maximum_msat = min(self.config[REMOTE].max_htlc_value_in_flight_msat, 1000 * self.constraints.capacity) htlc_maximum_msat = min(self.config[REMOTE].max_htlc_value_in_flight_msat, 1000 * self.constraints.capacity)
chan_upd = encode_msg( chan_upd = encode_msg(
@ -790,7 +790,7 @@ class Channel(AbstractChannel):
fee_base_msat=self.forwarding_fee_base_msat, fee_base_msat=self.forwarding_fee_base_msat,
fee_proportional_millionths=self.forwarding_fee_proportional_millionths, fee_proportional_millionths=self.forwarding_fee_proportional_millionths,
chain_hash=constants.net.rev_genesis_bytes(), chain_hash=constants.net.rev_genesis_bytes(),
timestamp=now, timestamp=now(),
) )
sighash = sha256d(chan_upd[2 + 64:]) sighash = sha256d(chan_upd[2 + 64:])
sig = ecc.ECPrivkey(self.lnworker.node_keypair.privkey).sign(sighash, ecc.sig_string_from_r_and_s) sig = ecc.ECPrivkey(self.lnworker.node_keypair.privkey).sign(sighash, ecc.sig_string_from_r_and_s)
@ -1449,6 +1449,7 @@ class Channel(AbstractChannel):
raise Exception("incorrect preimage for HTLC") raise Exception("incorrect preimage for HTLC")
assert htlc_id not in self.hm.log[REMOTE]['settles'] assert htlc_id not in self.hm.log[REMOTE]['settles']
self.hm.send_settle(htlc_id) self.hm.send_settle(htlc_id)
self.htlc_settle_time[htlc_id] = now()
def get_payment_hash(self, htlc_id: int) -> bytes: def get_payment_hash(self, htlc_id: int) -> bytes:
htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id) htlc = self.hm.get_htlc_by_id(LOCAL, htlc_id)
@ -1667,6 +1668,9 @@ class Channel(AbstractChannel):
continue continue
if htlc.cltv_abs - recv_htlc_deadline_delta > local_height: if htlc.cltv_abs - recv_htlc_deadline_delta > local_height:
continue continue
# Do not force-close if we just sent fullfill_htlc and have not received revack yet
if htlc_id in self.htlc_settle_time and now() - self.htlc_settle_time[htlc_id] < 30:
continue
htlcs_we_could_reclaim[(RECEIVED, htlc_id)] = htlc htlcs_we_could_reclaim[(RECEIVED, htlc_id)] = htlc
# If there is an offered HTLC which has already expired (+ some grace period after), we # If there is an offered HTLC which has already expired (+ some grace period after), we
# will unilaterally close the channel and time out the HTLC # will unilaterally close the channel and time out the HTLC

Loading…
Cancel
Save