From 15d088537e987bad9d7accc112d00a5257cf7c8c Mon Sep 17 00:00:00 2001 From: CryptoKVC Date: Fri, 9 Oct 2026 16:34:06 +0200 Subject: [PATCH] Use CollectionLength for the persisted event queue length EventQueueSerWrapper cast the queue length to u16, so a queue with 65,536 or more events was restored truncated on restart (65,536 events came back empty). Write and read the count as CollectionLength, which keeps the two-byte encoding below 65,535 and uses the 0xffff extension above it, and bound the reader's preallocation. Add round-trip tests at 65,534, 65,535, 65,536 and 65,540 events and a test that small queues keep the legacy encoding. Written with AI assistance (Claude); reviewed and tested locally. Fixes #1138. Co-authored-by: Claude --- src/event.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/src/event.rs b/src/event.rs index 730a682dd..929e7d20d 100644 --- a/src/event.rs +++ b/src/event.rs @@ -30,7 +30,7 @@ use lightning::sign::EntropySource; use lightning::util::config::{ChannelConfigOverrides, ChannelConfigUpdate}; use lightning::util::errors::APIError; use lightning::util::persist::KVStore; -use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer}; +use lightning::util::ser::{CollectionLength, Readable, ReadableArgs, Writeable, Writer}; use lightning::{impl_writeable_tlv_based, impl_writeable_tlv_based_enum}; use lightning_liquidity::lsps2::utils::compute_opening_fee; use lightning_types::payment::{PaymentHash, PaymentPreimage}; @@ -510,9 +510,10 @@ impl Readable for EventQueueDeserWrapper { fn read( reader: &mut R, ) -> Result { - let len: u16 = Readable::read(reader)?; - let mut queue = VecDeque::with_capacity(len as usize); - for _ in 0..len { + let len: CollectionLength = Readable::read(reader)?; + // Bound the preallocation, as the length is read from persisted data. + let mut queue = VecDeque::with_capacity(core::cmp::min(len.0, 1024) as usize); + for _ in 0..len.0 { queue.push_back(Readable::read(reader)?); } Ok(Self(queue)) @@ -523,7 +524,7 @@ struct EventQueueSerWrapper<'a>(&'a VecDeque); impl Writeable for EventQueueSerWrapper<'_> { fn write(&self, writer: &mut W) -> Result<(), lightning::io::Error> { - (self.0.len() as u16).write(writer)?; + CollectionLength(self.0.len() as u64).write(writer)?; for e in self.0.iter() { e.write(writer)?; } @@ -2635,4 +2636,42 @@ mod tests { } assert_eq!(event_queue.next_event(), None); } + + fn test_event_queue(len: usize) -> VecDeque { + (0..len) + .map(|i| Event::ChannelReady { + channel_id: ChannelId([23u8; 32]), + user_channel_id: UserChannelId(i as u128), + counterparty_node_id: None, + funding_txo: None, + }) + .collect() + } + + #[test] + fn event_queue_round_trips_around_u16_max() { + for len in [65_534, 65_535, 65_536, 65_540] { + let queue = test_event_queue(len); + let bytes = EventQueueSerWrapper(&queue).encode(); + let read_queue: EventQueueDeserWrapper = Readable::read(&mut &bytes[..]).unwrap(); + assert_eq!(read_queue.0.len(), len); + assert_eq!(read_queue.0, queue); + } + } + + #[test] + fn event_queue_keeps_legacy_encoding_below_u16_max() { + let queue = test_event_queue(3); + + // Before `CollectionLength`, the count was written as a plain `u16`. + let mut legacy_bytes = Vec::new(); + (queue.len() as u16).write(&mut legacy_bytes).unwrap(); + for event in queue.iter() { + event.write(&mut legacy_bytes).unwrap(); + } + + assert_eq!(EventQueueSerWrapper(&queue).encode(), legacy_bytes); + let read_queue: EventQueueDeserWrapper = Readable::read(&mut &legacy_bytes[..]).unwrap(); + assert_eq!(read_queue.0, queue); + } }