1
0

lnpeer: rate-limit ordered_message_queues

This commit is contained in:
SomberNight
2025-08-19 15:04:43 +00:00
parent b8d989e13b
commit 1380ed4ba7

View File

@@ -12,6 +12,7 @@ import time
from typing import Tuple, Dict, TYPE_CHECKING, Optional, Union, Set, Callable, Awaitable, List
from datetime import datetime
import functools
from functools import partial
import electrum_ecc as ecc
from electrum_ecc import ecdsa_sig64_from_r_and_s, ecdsa_der_sig_from_ecdsa_sig64, ECPubkey
@@ -112,7 +113,7 @@ class Peer(Logger, EventListener):
self.our_gossip_timestamp_filter = None # type: Optional[GossipTimestampFilter]
self.their_gossip_timestamp_filter = None # type: Optional[GossipTimestampFilter]
self.outgoing_gossip_reply = False # type: bool
self.ordered_message_queues = defaultdict(asyncio.Queue) # type: Dict[bytes, asyncio.Queue] # for messages that are ordered
self.ordered_message_queues = defaultdict(partial(asyncio.Queue, maxsize=10)) # type: Dict[bytes, asyncio.Queue] # for messages that are ordered
self.temp_id_to_id = {} # type: Dict[bytes, Optional[bytes]] # to forward error messages
self.funding_created_sent = set() # for channels in PREOPENING
self.funding_signed_sent = set() # for channels in PREOPENING
@@ -228,6 +229,12 @@ class Peer(Logger, EventListener):
return
if message_type in self.ORDERED_MESSAGES:
chan_id = payload.get('channel_id') or payload["temporary_channel_id"]
if (
chan_id not in self.channels
and chan_id not in self.temp_id_to_id
and chan_id not in self.temp_id_to_id.values()
):
raise Exception(f"received {message_type} for unknown {chan_id.hex()=}")
self.ordered_message_queues[chan_id].put_nowait((message_type, payload))
else:
if message_type not in ('error', 'warning') and 'channel_id' in payload:
@@ -1085,8 +1092,8 @@ class Peer(Logger, EventListener):
int.from_bytes(per_commitment_secret_first, 'big'))
# store the temp id now, so that it is recognized for e.g. 'error' messages
# TODO: this is never cleaned up; the dict grows unbounded until disconnect
self.temp_id_to_id[temp_channel_id] = None
self._cleanup_temp_channelids()
self.send_message(
"open_channel",
temporary_channel_id=temp_channel_id,
@@ -1305,8 +1312,8 @@ class Peer(Logger, EventListener):
feerate = payload['feerate_per_kw'] # note: we are not validating this
temp_chan_id = payload['temporary_channel_id']
# store the temp id now, so that it is recognized for e.g. 'error' messages
# TODO: this is never cleaned up; the dict grows unbounded until disconnect
self.temp_id_to_id[temp_chan_id] = None
self._cleanup_temp_channelids()
channel_opening_fee = open_channel_tlvs.get('channel_opening_fee') if open_channel_tlvs else None
if channel_opening_fee:
# todo check that the fee is reasonable
@@ -1448,6 +1455,15 @@ class Peer(Logger, EventListener):
self.send_channel_ready(chan)
self.lnworker.add_new_channel(chan)
def _cleanup_temp_channelids(self) -> None:
self.temp_id_to_id = {
tmp_id: chan_id for (tmp_id, chan_id) in self.temp_id_to_id.items()
if chan_id not in self.channels
}
if len(self.temp_id_to_id) > 25:
# which one of us is opening all these chans?! let's disconnect
raise Exception("temp_id_to_id is getting too large.")
async def request_force_close(self, channel_id: bytes):
"""Try to trigger the remote peer to force-close."""
await self.initialized