Skip to main content

sc_statement_store/
subscription.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Subscription logic for statement store.
20//!
21//! Manages subscriptions to statement topics and notifies subscribers when new statements arrive.
22//! Uses multiple matcher tasks to handle subscriptions concurrently, each responsible for a subset
23//! of subscriptions. Each matcher task maintains its own list of subscriptions and matches incoming
24//! statements against them. When a new statement is submitted, it is sent to all matcher tasks for
25//! processing. If a statement matches a subscription's filter, it is sent to the subscriber via an
26//! async channel.
27//!
28//! This design allows for efficient handling of a large number of subscriptions and statements and
29//! can be scaled by adjusting the number of matcher tasks.
30
31// Buffer size for the matcher task channels, to backpressure the submission senders.
32// This value is generous to allow for bursts of statements without dropping any or backpressuring
33// too early.
34const MATCHERS_TASK_CHANNEL_BUFFER_SIZE: usize = 80_000;
35
36// Buffer size for individual subscriptions.
37const SUBSCRIPTION_BUFFER_SIZE: usize = 128;
38const STOP_RESERVE_CHANNEL_SLOTS: usize = 1;
39
40/// Maximum number of active filters attached to one statement subscription.
41///
42/// Keeps one subscription useful for multiplexing, while bounding internal per-event filter-id
43/// metadata to 128 `u64`s, i.e. 1 KiB before `Vec` overhead.
44pub const MAX_FILTERS_PER_SUBSCRIPTION: usize = 128;
45// Keep replay batches bounded by raw statement bytes. The JSON response is roughly twice this size
46// because statements are hex-encoded.
47pub(crate) const REPLAY_CHUNK_RAW_BYTES: usize = 4 * 1024 * 1024;
48use futures::{Stream, StreamExt};
49use itertools::Itertools;
50use parking_lot::Mutex;
51
52use crate::LOG_TARGET;
53use sc_utils::id_sequence::SeqID;
54use sp_core::{traits::SpawnNamed, Bytes, Encode};
55pub use sp_statement_store::StatementStore;
56use sp_statement_store::{
57	FilterId, LiveStatementEvent, OptimizedTopicFilter, Result, Statement, StatementEvent, Topic,
58	MAX_TOPICS,
59};
60use std::{
61	collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
62	sync::{atomic::AtomicU64, Arc},
63};
64
65/// Error returned when attaching a filter to a multi-filter subscription fails
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub enum AddFilterError {
68	/// The subscription already has the maximum number of active filters
69	LimitReached,
70	/// The filter could not be registered: the store or the matcher is gone, or the subscription
71	/// is no longer active
72	Stopped,
73}
74
75/// Trait for initiating statement store subscriptions from the RPC module.
76pub trait StatementStoreSubscriptionApi: Send + Sync {
77	/// Subscribe to statements matching the topic filter.
78	///
79	/// Returns existing matching statements, a sender channel to send matched statements and a
80	/// stream for receiving matched statements when they arrive.
81	fn subscribe_statement(
82		&self,
83		topic_filter: OptimizedTopicFilter,
84	) -> Result<(Vec<Vec<u8>>, async_channel::Sender<StatementEvent>, SubscriptionStatementsStream)>;
85}
86
87/// Creates multi-filter subscriptions for the RPC module
88pub trait MultiFilterSubscriptionApi: Send + Sync {
89	/// Creates an empty subscription that can receive filters dynamically
90	fn create_subscription(&self) -> (SubscriptionHandle, MultiFilterEventStream);
91}
92
93/// Provides cursor-based replay for dynamically attached filters.
94pub(crate) trait ReplaySnapshotProvider: Send + Sync {
95	/// Capture the committed admission watermark and enqueue filter registration atomically with
96	/// respect to submit.
97	/// Returns the registered watermark, or `None` when registration could not be enqueued.
98	///
99	/// `enqueue` is called at most once, with the submit lock held: it must not block or call
100	/// back into the store.
101	fn register_replay(&self, enqueue: &mut dyn FnMut(u64) -> bool) -> Result<Option<u64>>;
102
103	/// Read the next replay batch from the persisted admission sequence.
104	fn replay_batch(
105		&self,
106		filter: &OptimizedTopicFilter,
107		cursor: u64,
108		watermark: u64,
109	) -> Result<ReplayBatch>;
110}
111
112pub(crate) struct ReplayBatch {
113	pub(crate) statements: Vec<Vec<u8>>,
114	pub(crate) cursor: u64,
115	pub(crate) done: bool,
116}
117
118/// A handle that attaches, removes, and inspects filters for one multi-filter subscription
119#[derive(Clone)]
120pub struct SubscriptionHandle {
121	pub(crate) sub_id: SeqID,
122	pub(crate) inner: Arc<Mutex<SubscriptionHandleInner>>,
123	pub(crate) matchers: SubscriptionsMatchersHandlers,
124	pub(crate) snapshot_provider: Arc<dyn ReplaySnapshotProvider>,
125}
126
127pub(crate) struct SubscriptionHandleInner {
128	active_filter_ids: HashSet<FilterId>,
129	next_filter_id: u64,
130}
131
132impl SubscriptionHandleInner {
133	pub(crate) fn new() -> Self {
134		Self { active_filter_ids: HashSet::new(), next_filter_id: 0 }
135	}
136}
137
138impl SubscriptionHandle {
139	/// Attaches a filter and returns its id
140	pub fn add_filter(
141		&self,
142		filter: OptimizedTopicFilter,
143	) -> std::result::Result<FilterId, AddFilterError> {
144		let mut inner = self.inner.lock();
145		if inner.active_filter_ids.len() >= MAX_FILTERS_PER_SUBSCRIPTION {
146			return Err(AddFilterError::LimitReached);
147		}
148
149		let filter_id = FilterId::new(inner.next_filter_id);
150		let sub_id = self.sub_id;
151
152		let (result_tx, result_rx) = async_channel::bounded(1);
153		let mut result_tx = Some(result_tx);
154		let Some(_watermark) = self
155			.snapshot_provider
156			.register_replay(&mut |watermark| {
157				let result_tx =
158					result_tx.take().expect("register_replay invokes enqueue at most once; qed");
159				self.matchers
160					.try_send_by_seq_id(
161						sub_id,
162						MatcherMessage::RegisterFilter {
163							sub_id,
164							filter_id,
165							filter: filter.clone(),
166							watermark,
167							result_tx,
168						},
169					)
170					.is_ok()
171			})
172			.map_err(|_| AddFilterError::Stopped)?
173		else {
174			return Err(AddFilterError::Stopped);
175		};
176		match result_rx.recv_blocking() {
177			Ok(result) => result?,
178			Err(_) => return Err(AddFilterError::Stopped),
179		}
180		inner.next_filter_id = inner.next_filter_id.wrapping_add(1);
181		inner.active_filter_ids.insert(filter_id);
182		Ok(filter_id)
183	}
184
185	/// Removes a filter from this subscription
186	pub fn remove_filter(&self, filter_id: FilterId) -> bool {
187		let mut inner = self.inner.lock();
188		if !inner.active_filter_ids.remove(&filter_id) {
189			return false;
190		}
191		self.matchers.send_by_seq_id(
192			self.sub_id,
193			MatcherMessage::RemoveFilter { sub_id: self.sub_id, filter_id },
194		);
195		true
196	}
197}
198
199struct PendingReplay {
200	filter_id: FilterId,
201	filter: OptimizedTopicFilter,
202	cursor: u64,
203	watermark: u64,
204}
205
206pub(crate) struct MultiFilterSubscriptionState {
207	pending_replays: VecDeque<PendingReplay>,
208	stopped: bool,
209	stop_emitted: bool,
210}
211
212impl MultiFilterSubscriptionState {
213	pub(crate) fn new() -> Self {
214		Self { pending_replays: VecDeque::new(), stopped: false, stop_emitted: false }
215	}
216
217	fn record_filter_added(
218		&mut self,
219		filter_id: FilterId,
220		filter: OptimizedTopicFilter,
221		watermark: u64,
222	) {
223		self.pending_replays
224			.push_back(PendingReplay { filter_id, filter, cursor: 0, watermark });
225	}
226
227	fn record_filter_removed(&mut self, filter_id: FilterId) {
228		self.pending_replays.retain(|replay| replay.filter_id != filter_id);
229	}
230
231	fn next_event(
232		&mut self,
233		snapshot_provider: &dyn ReplaySnapshotProvider,
234	) -> Option<MultiFilterSubscriptionEvent> {
235		if !self.stopped {
236			if let Some(event) = self.next_replay_event(snapshot_provider) {
237				return Some(event);
238			}
239		}
240
241		if self.stopped && !self.stop_emitted {
242			self.stop_emitted = true;
243			return Some(MultiFilterSubscriptionEvent::Stop);
244		}
245		None
246	}
247
248	fn next_replay_event(
249		&mut self,
250		snapshot_provider: &dyn ReplaySnapshotProvider,
251	) -> Option<MultiFilterSubscriptionEvent> {
252		let replay = self.pending_replays.front_mut()?;
253		let filter_id = replay.filter_id;
254		if replay.cursor >= replay.watermark {
255			self.pending_replays.pop_front();
256			return Some(MultiFilterSubscriptionEvent::ReplayDone { filter_id });
257		}
258		let batch =
259			match snapshot_provider.replay_batch(&replay.filter, replay.cursor, replay.watermark) {
260				Ok(batch) => batch,
261				Err(e) => {
262					log::error!(
263						target: LOG_TARGET,
264						"Stopping subscription: replaying filter {:?} from {} failed: {:?}",
265						filter_id,
266						replay.cursor,
267						e
268					);
269					self.stopped = true;
270					return None;
271				},
272			};
273		replay.cursor = batch.cursor;
274		if batch.done && batch.statements.is_empty() {
275			self.pending_replays.pop_front();
276			return Some(MultiFilterSubscriptionEvent::ReplayDone { filter_id });
277		}
278		Some(MultiFilterSubscriptionEvent::ReplayStatements {
279			filter_id,
280			statements: batch.statements,
281		})
282	}
283
284	fn new_statement_event(
285		&mut self,
286		hash: sp_statement_store::Hash,
287		encoded: Vec<u8>,
288		filter_ids: &HashSet<FilterId>,
289	) -> Option<MultiFilterSubscriptionEvent> {
290		let matched_filter_ids: Vec<FilterId> = filter_ids.iter().copied().collect();
291
292		if matched_filter_ids.is_empty() {
293			return None;
294		}
295
296		Some(MultiFilterSubscriptionEvent::NewStatement(LiveStatementEvent {
297			hash,
298			encoded,
299			matched_filter_ids,
300		}))
301	}
302}
303
304/// Event emitted by a multi-filter subscription
305#[derive(Debug, Clone)]
306pub enum MultiFilterSubscriptionEvent {
307	/// Replay statements for a newly attached filter
308	ReplayStatements {
309		/// Filter that produced this replay batch
310		filter_id: FilterId,
311		/// SCALE-encoded statements included in this replay batch
312		statements: Vec<Vec<u8>>,
313	},
314	/// Replay completed for a newly attached filter
315	ReplayDone {
316		/// Filter whose replay completed
317		filter_id: FilterId,
318	},
319	/// Live statement event matched one or more active filters
320	NewStatement(LiveStatementEvent),
321	/// Subscription stopped: the subscriber fell behind its buffer, or replaying a filter failed
322	Stop,
323}
324
325/// Stream of multi-filter subscription events
326pub struct MultiFilterEventStream {
327	sub_id: SeqID,
328	matchers: SubscriptionsMatchersHandlers,
329	rx: async_channel::Receiver<MultiFilterSubscriptionEvent>,
330}
331
332impl Stream for MultiFilterEventStream {
333	type Item = MultiFilterSubscriptionEvent;
334
335	fn poll_next(
336		self: std::pin::Pin<&mut Self>,
337		cx: &mut std::task::Context<'_>,
338	) -> std::task::Poll<Option<Self::Item>> {
339		self.get_mut().rx.poll_next_unpin(cx)
340	}
341}
342
343impl Drop for MultiFilterEventStream {
344	fn drop(&mut self) {
345		self.matchers
346			.send_by_seq_id(self.sub_id, MatcherMessage::Unsubscribe(self.sub_id));
347	}
348}
349
350/// Messages sent to matcher tasks.
351enum MatcherMessage {
352	/// A new statement has been submitted, tagged with the store sequence number.
353	NewStatement(u64, Statement),
354	/// A new subscription has been created.
355	Subscribe { info: IndexedSubscription, tx: async_channel::Sender<StatementEvent> },
356	/// A new multi-filter subscription has been created
357	SubscribeEmpty {
358		seq_id: SeqID,
359		snapshot_provider: Arc<dyn ReplaySnapshotProvider>,
360		tx: async_channel::Sender<MultiFilterSubscriptionEvent>,
361	},
362	/// Register a filter at a store watermark. The matcher drains its cursor replay before
363	/// processing later live-statement messages from this channel.
364	RegisterFilter {
365		sub_id: SeqID,
366		filter_id: FilterId,
367		filter: OptimizedTopicFilter,
368		watermark: u64,
369		result_tx: async_channel::Sender<std::result::Result<(), AddFilterError>>,
370	},
371	/// Remove a filter from an existing multi-filter subscription
372	RemoveFilter { sub_id: SeqID, filter_id: FilterId },
373
374	/// Unsubscribe the subscription with the given ID.
375	Unsubscribe(SeqID),
376}
377
378// Handle to manage all subscriptions.
379pub struct SubscriptionsHandle {
380	// Sequence generator for subscription IDs, atomic for thread safety.
381	// Subscription creation is expensive enough that we don't worry about overflow here.
382	id_sequence: AtomicU64,
383	//  Subscriptions matchers handlers.
384	matchers: SubscriptionsMatchersHandlers,
385}
386
387impl SubscriptionsHandle {
388	/// Create a new SubscriptionsHandle with the given task spawner and number of filter workers.
389	pub(crate) fn new(
390		task_spawner: Box<dyn SpawnNamed>,
391		num_matcher_workers: usize,
392	) -> SubscriptionsHandle {
393		let mut subscriptions_matchers_senders = Vec::with_capacity(num_matcher_workers);
394
395		for task in 0..num_matcher_workers {
396			let (subscription_matcher_sender, subscription_matcher_receiver) =
397				async_channel::bounded(MATCHERS_TASK_CHANNEL_BUFFER_SIZE);
398			subscriptions_matchers_senders.push(subscription_matcher_sender);
399			task_spawner.spawn_blocking(
400				"statement-store-subscription-filters",
401				Some("statement-store"),
402				Box::pin(async move {
403					let mut subscriptions = SubscriptionsInfo::new();
404					log::debug!(
405						target: LOG_TARGET,
406						"Started statement subscription matcher task: {task}"
407					);
408					loop {
409						let res = subscription_matcher_receiver.recv().await;
410						match res {
411							Ok(MatcherMessage::NewStatement(seq, statement)) => {
412								subscriptions.notify_matching_filters(seq, &statement);
413							},
414							Ok(MatcherMessage::Subscribe { info, tx }) => {
415								subscriptions.subscribe(info, tx);
416							},
417							Ok(MatcherMessage::SubscribeEmpty {
418								seq_id,
419								snapshot_provider,
420								tx,
421							}) => {
422								subscriptions.subscribe_empty(seq_id, snapshot_provider, tx);
423							},
424							Ok(MatcherMessage::RegisterFilter {
425								sub_id,
426								filter_id,
427								filter,
428								watermark,
429								result_tx,
430							}) => {
431								let result = subscriptions
432									.register_filter(sub_id, filter_id, filter, watermark);
433								let registered = result.is_ok();
434								let _ = result_tx.try_send(result);
435								if registered {
436									subscriptions.drain_ready_events(sub_id);
437								}
438							},
439							Ok(MatcherMessage::RemoveFilter { sub_id, filter_id }) => {
440								subscriptions.remove_filter(sub_id, filter_id);
441							},
442							Ok(MatcherMessage::Unsubscribe(seq_id)) => {
443								subscriptions.unsubscribe(seq_id);
444							},
445							Err(_) => {
446								// Expected when the subscription manager is dropped at shutdown.
447								log::debug!(
448									target: LOG_TARGET,
449									"Statement subscription matcher channel closed: {task}"
450								);
451								break;
452							},
453						};
454					}
455				}),
456			);
457		}
458		SubscriptionsHandle {
459			id_sequence: AtomicU64::new(0),
460			matchers: SubscriptionsMatchersHandlers::new(subscriptions_matchers_senders),
461		}
462	}
463
464	// Generate the next unique subscription ID.
465	fn next_id(&self) -> SeqID {
466		let id = self.id_sequence.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
467		SeqID::from(id)
468	}
469
470	/// Subscribe to statements matching the topic filter.
471	pub(crate) fn subscribe(
472		&self,
473		topic_filter: OptimizedTopicFilter,
474		watermark: u64,
475	) -> (async_channel::Sender<StatementEvent>, SubscriptionStatementsStream) {
476		let next_id = self.next_id();
477		let (tx, rx) = async_channel::bounded(SUBSCRIPTION_BUFFER_SIZE);
478		let subscription_info = IndexedSubscription {
479			topic_filter: topic_filter.clone(),
480			seq_id: next_id,
481			filter_key: SubscriptionFilterKey::Fixed,
482			watermark,
483		};
484		let subscription_tx = tx.clone();
485
486		let result = (
487			tx,
488			SubscriptionStatementsStream {
489				rx,
490				sub_id: subscription_info.seq_id,
491				matchers: self.matchers.clone(),
492			},
493		);
494
495		self.matchers.send_by_seq_id(
496			subscription_info.seq_id,
497			MatcherMessage::Subscribe { info: subscription_info, tx: subscription_tx },
498		);
499		result
500	}
501
502	pub(crate) fn subscribe_empty(
503		&self,
504		snapshot_provider: Arc<dyn ReplaySnapshotProvider>,
505	) -> (SeqID, MultiFilterEventStream) {
506		let sub_id = self.next_id();
507		let (tx, rx) =
508			async_channel::bounded(SUBSCRIPTION_BUFFER_SIZE + STOP_RESERVE_CHANNEL_SLOTS);
509		self.matchers.send_by_seq_id(
510			sub_id,
511			MatcherMessage::SubscribeEmpty { seq_id: sub_id, snapshot_provider, tx },
512		);
513
514		let stream = MultiFilterEventStream { sub_id, matchers: self.matchers.clone(), rx };
515		(sub_id, stream)
516	}
517
518	pub(crate) fn notify(&self, seq: u64, statement: Statement) {
519		self.matchers.send_all(seq, statement);
520	}
521
522	pub(crate) fn matchers(&self) -> SubscriptionsMatchersHandlers {
523		self.matchers.clone()
524	}
525}
526
527#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
528enum SubscriptionFilterKey {
529	Fixed,
530	Dynamic(FilterId),
531}
532
533enum SubscriptionRecord {
534	SingleFilter {
535		tx: async_channel::Sender<StatementEvent>,
536		filter: OptimizedTopicFilter,
537	},
538	MultiFilter {
539		filters: HashMap<FilterId, OptimizedTopicFilter>,
540		state: MultiFilterSubscriptionState,
541		tx: async_channel::Sender<MultiFilterSubscriptionEvent>,
542		snapshot_provider: Arc<dyn ReplaySnapshotProvider>,
543	},
544}
545
546enum MatchedSubscription {
547	Statements,
548	Live(HashSet<FilterId>),
549}
550
551enum ReadyEventDelivery {
552	Continue,
553	Stopped,
554	Closed,
555}
556
557enum PostLiveSendAction {
558	None,
559	Unsubscribe,
560}
561
562type IndexedSubscriptionKey = (SeqID, SubscriptionFilterKey);
563
564// Information about all subscriptions.
565// Each matcher task will have its own instance of this struct.
566struct SubscriptionsInfo {
567	// Subscriptions organized by topic for MatchAll filters.
568	//
569	// Maps each topic to an array of HashMaps, where the array is indexed by
570	// `(number_of_topics_in_filter - 1)`. For example, a subscription requiring
571	// topics [A, B] (2 topics) will be stored at index 1 under both topic A and B.
572	//
573	// This structure allows efficient matching: when a statement arrives with N topics,
574	// we only need to check subscriptions that require exactly N or fewer topics.
575	subscriptions_match_all_by_topic:
576		HashMap<Topic, [HashMap<IndexedSubscriptionKey, IndexedSubscription>; MAX_TOPICS]>,
577	// Subscriptions organized by topic for MatchAny filters.
578	subscriptions_match_any_by_topic:
579		HashMap<Topic, HashMap<IndexedSubscriptionKey, IndexedSubscription>>,
580	// Subscriptions that listen with Any filter (i.e., no topic filtering).
581	subscriptions_any: HashMap<IndexedSubscriptionKey, IndexedSubscription>,
582	// Mapping from subscription ID to subscription state.
583	by_sub_id: HashMap<SeqID, SubscriptionRecord>,
584}
585
586// Information about one indexed subscription filter.
587#[derive(Clone, Debug)]
588struct IndexedSubscription {
589	// The filter used for this subscription.
590	topic_filter: OptimizedTopicFilter,
591	// The unique ID of this subscription.
592	seq_id: SeqID,
593	// The filter key within the subscription.
594	filter_key: SubscriptionFilterKey,
595	// Store sequence-number boundary captured when this entry was registered: at subscription
596	// creation for a fixed filter, at `add_filter` for a dynamic one.
597	watermark: u64,
598}
599
600impl SubscriptionsInfo {
601	fn new() -> SubscriptionsInfo {
602		SubscriptionsInfo {
603			subscriptions_match_all_by_topic: HashMap::new(),
604			subscriptions_match_any_by_topic: HashMap::new(),
605			subscriptions_any: HashMap::new(),
606			by_sub_id: HashMap::new(),
607		}
608	}
609
610	// Subscribe a new subscription.
611	fn subscribe(
612		&mut self,
613		subscription_info: IndexedSubscription,
614		tx: async_channel::Sender<StatementEvent>,
615	) {
616		self.by_sub_id.insert(
617			subscription_info.seq_id,
618			SubscriptionRecord::SingleFilter { tx, filter: subscription_info.topic_filter.clone() },
619		);
620		self.index_filter(subscription_info);
621	}
622
623	fn subscribe_empty(
624		&mut self,
625		seq_id: SeqID,
626		snapshot_provider: Arc<dyn ReplaySnapshotProvider>,
627		tx: async_channel::Sender<MultiFilterSubscriptionEvent>,
628	) {
629		self.by_sub_id.insert(
630			seq_id,
631			SubscriptionRecord::MultiFilter {
632				filters: HashMap::new(),
633				state: MultiFilterSubscriptionState::new(),
634				tx,
635				snapshot_provider,
636			},
637		);
638	}
639
640	#[cfg(test)]
641	fn add_filter(
642		&mut self,
643		sub_id: SeqID,
644		filter_id: FilterId,
645		filter: OptimizedTopicFilter,
646		snapshot_hashes: Vec<sp_statement_store::Hash>,
647	) {
648		self.add_filter_with_cursor(sub_id, filter_id, filter, snapshot_hashes.len() as u64);
649		self.drain_ready_events(sub_id);
650	}
651
652	fn register_filter(
653		&mut self,
654		sub_id: SeqID,
655		filter_id: FilterId,
656		filter: OptimizedTopicFilter,
657		watermark: u64,
658	) -> std::result::Result<(), AddFilterError> {
659		let Some(SubscriptionRecord::MultiFilter { filters, .. }) = self.by_sub_id.get(&sub_id)
660		else {
661			return Err(AddFilterError::Stopped);
662		};
663		if filters.contains_key(&filter_id) {
664			return Err(AddFilterError::Stopped);
665		}
666		let output_closed = matches!(
667			self.by_sub_id.get(&sub_id),
668			Some(SubscriptionRecord::MultiFilter { tx, .. }) if tx.is_closed()
669		);
670		if output_closed {
671			self.unsubscribe(sub_id);
672			return Err(AddFilterError::Stopped);
673		}
674		self.add_filter_with_cursor(sub_id, filter_id, filter, watermark);
675		Ok(())
676	}
677
678	fn add_filter_with_cursor(
679		&mut self,
680		sub_id: SeqID,
681		filter_id: FilterId,
682		filter: OptimizedTopicFilter,
683		watermark: u64,
684	) {
685		let filter_key = SubscriptionFilterKey::Dynamic(filter_id);
686		{
687			let Some(SubscriptionRecord::MultiFilter { filters, state, .. }) =
688				self.by_sub_id.get_mut(&sub_id)
689			else {
690				return;
691			};
692
693			let Entry::Vacant(entry) = filters.entry(filter_id) else {
694				return;
695			};
696			entry.insert(filter.clone());
697			state.record_filter_added(filter_id, filter.clone(), watermark);
698		}
699		self.index_filter(IndexedSubscription {
700			topic_filter: filter,
701			seq_id: sub_id,
702			filter_key,
703			watermark,
704		});
705	}
706
707	fn remove_filter(&mut self, sub_id: SeqID, filter_id: FilterId) {
708		let Some(record) = self.by_sub_id.get_mut(&sub_id) else {
709			return;
710		};
711		let SubscriptionRecord::MultiFilter { filters, state, .. } = record else {
712			return;
713		};
714		let Some(filter) = filters.remove(&filter_id) else {
715			return;
716		};
717		state.record_filter_removed(filter_id);
718		self.remove_indexed_filter(sub_id, SubscriptionFilterKey::Dynamic(filter_id), &filter);
719		self.drain_ready_events(sub_id);
720	}
721
722	fn send_stop(tx: &async_channel::Sender<MultiFilterSubscriptionEvent>) {
723		let _ = tx.try_send(MultiFilterSubscriptionEvent::Stop);
724		tx.close();
725	}
726
727	fn send_ready_event(
728		state: &mut MultiFilterSubscriptionState,
729		tx: &async_channel::Sender<MultiFilterSubscriptionEvent>,
730		event: MultiFilterSubscriptionEvent,
731	) -> ReadyEventDelivery {
732		if tx.is_closed() {
733			return ReadyEventDelivery::Closed;
734		}
735		if matches!(event, MultiFilterSubscriptionEvent::Stop) {
736			Self::send_stop(tx);
737			return ReadyEventDelivery::Stopped;
738		}
739		if tx.len() >= SUBSCRIPTION_BUFFER_SIZE {
740			state.stopped = true;
741			Self::send_stop(tx);
742			return ReadyEventDelivery::Stopped;
743		}
744		match tx.try_send(event) {
745			Ok(()) => ReadyEventDelivery::Continue,
746			Err(async_channel::TrySendError::Full(_)) => {
747				state.stopped = true;
748				Self::send_stop(tx);
749				ReadyEventDelivery::Stopped
750			},
751			Err(async_channel::TrySendError::Closed(_)) => ReadyEventDelivery::Closed,
752		}
753	}
754
755	fn drain_ready_events(&mut self, sub_id: SeqID) {
756		loop {
757			let result = {
758				let Some(record) = self.by_sub_id.get_mut(&sub_id) else {
759					return;
760				};
761				let SubscriptionRecord::MultiFilter { state, tx, snapshot_provider, .. } = record
762				else {
763					return;
764				};
765				let Some(event) = state.next_event(snapshot_provider.as_ref()) else {
766					return;
767				};
768				match Self::send_ready_event(state, tx, event) {
769					ReadyEventDelivery::Continue => Ok(()),
770					ReadyEventDelivery::Stopped => return,
771					ReadyEventDelivery::Closed => Err(()),
772				}
773			};
774			if result.is_err() {
775				self.unsubscribe(sub_id);
776				return;
777			}
778		}
779	}
780
781	/// Emit a live statement matched for a multi-filter subscription.
782	fn deliver_live_statement(
783		&mut self,
784		sub_id: SeqID,
785		hash: sp_statement_store::Hash,
786		encoded: Vec<u8>,
787		matched_filter_ids: HashSet<FilterId>,
788	) {
789		let action = {
790			let Some(SubscriptionRecord::MultiFilter { state, tx, .. }) =
791				self.by_sub_id.get_mut(&sub_id)
792			else {
793				return;
794			};
795
796			match state.new_statement_event(hash, encoded, &matched_filter_ids) {
797				Some(event) => match Self::send_ready_event(state, tx, event) {
798					ReadyEventDelivery::Closed => PostLiveSendAction::Unsubscribe,
799					ReadyEventDelivery::Continue | ReadyEventDelivery::Stopped => {
800						PostLiveSendAction::None
801					},
802				},
803				None => PostLiveSendAction::None,
804			}
805		};
806
807		match action {
808			PostLiveSendAction::Unsubscribe => self.unsubscribe(sub_id),
809			PostLiveSendAction::None => {},
810		}
811	}
812
813	fn index_filter(&mut self, subscription_info: IndexedSubscription) {
814		let index_key = (subscription_info.seq_id, subscription_info.filter_key);
815		match &subscription_info.topic_filter {
816			OptimizedTopicFilter::Any => {
817				self.subscriptions_any.insert(index_key, subscription_info);
818			},
819			OptimizedTopicFilter::MatchAll(topics) => {
820				for topic in topics {
821					self.subscriptions_match_all_by_topic.entry(*topic).or_default()
822						[topics.len() - 1]
823						.insert(index_key, subscription_info.clone());
824				}
825			},
826			OptimizedTopicFilter::MatchAny(topics) => {
827				for topic in topics {
828					self.subscriptions_match_any_by_topic
829						.entry(*topic)
830						.or_default()
831						.insert(index_key, subscription_info.clone());
832				}
833			},
834		};
835	}
836
837	// Deliver the statement at store sequence number `seq` to every matching subscription.
838	// Subscriptions and filters registered above `seq` are skipped: the statement predates them, so
839	// it is covered by the subscribe-time snapshot
840	// (`StatementStoreSubscriptionApi::subscribe_statement`) for a fixed filter, or by the
841	// admission-cursor replay (`ReplaySnapshotProvider::replay_batch`) for a dynamic one.
842	fn notify_matching_filters(&mut self, seq: u64, statement: &Statement) {
843		let mut matches = HashMap::new();
844		self.collect_match_all_subscribers(seq, statement, &mut matches);
845		self.collect_match_any_subscribers(seq, statement, &mut matches);
846		self.collect_any_subscribers(seq, &mut matches);
847
848		let encoded = statement.encode();
849		let bytes_to_send: Bytes = encoded.clone().into();
850		let mut needs_unsubscribing = HashSet::new();
851
852		for (sub_id, matched) in matches {
853			match matched {
854				MatchedSubscription::Statements => {
855					let Some(SubscriptionRecord::SingleFilter { tx, .. }) =
856						self.by_sub_id.get(&sub_id)
857					else {
858						continue;
859					};
860					if let Err(err) = tx.try_send(StatementEvent::NewStatements {
861						statements: vec![bytes_to_send.clone()],
862						remaining: None,
863					}) {
864						log::debug!(
865							target: LOG_TARGET,
866							"Failed to send statement to subscriber {:?}: {:?} unsubscribing it", sub_id, err
867						);
868						needs_unsubscribing.insert(sub_id);
869					}
870				},
871				MatchedSubscription::Live(filter_ids) if !filter_ids.is_empty() => {
872					self.deliver_live_statement(
873						sub_id,
874						statement.hash(),
875						encoded.clone(),
876						filter_ids,
877					);
878				},
879				_ => {},
880			}
881		}
882
883		for sub_id in needs_unsubscribing {
884			self.unsubscribe(sub_id);
885		}
886	}
887
888	// Record a subscription filter as matched, unless the statement's sequence number is below the
889	// watermark captured when it was registered (exactly-once delivery: such statements are covered
890	// by the subscribe-time snapshot or the admission-cursor replay instead).
891	fn record_match(
892		matches: &mut HashMap<SeqID, MatchedSubscription>,
893		subscription: &IndexedSubscription,
894		seq: u64,
895	) {
896		if seq < subscription.watermark {
897			return;
898		}
899		match subscription.filter_key {
900			SubscriptionFilterKey::Fixed => {
901				matches.entry(subscription.seq_id).or_insert(MatchedSubscription::Statements);
902			},
903			SubscriptionFilterKey::Dynamic(filter_id) => {
904				let entry = matches
905					.entry(subscription.seq_id)
906					.or_insert_with(|| MatchedSubscription::Live(HashSet::new()));
907				if let MatchedSubscription::Live(filter_ids) = entry {
908					filter_ids.insert(filter_id);
909				}
910			},
911		}
912	}
913
914	// Collect all subscribers with MatchAny filters that match the given statement.
915	fn collect_match_any_subscribers(
916		&self,
917		seq: u64,
918		statement: &Statement,
919		matches: &mut HashMap<SeqID, MatchedSubscription>,
920	) {
921		for statement_topic in statement.topics() {
922			if let Some(subscriptions) = self.subscriptions_match_any_by_topic.get(statement_topic)
923			{
924				for subscription in subscriptions.values() {
925					Self::record_match(matches, subscription, seq);
926				}
927			}
928		}
929	}
930
931	// Collect all subscribers with MatchAll filters that match the given statement.
932	fn collect_match_all_subscribers(
933		&self,
934		seq: u64,
935		statement: &Statement,
936		matches: &mut HashMap<SeqID, MatchedSubscription>,
937	) {
938		let num_topics = statement.topics().len();
939
940		// Check all combinations of topics in the statement to find matching subscriptions.
941		// This works well because the maximum allowed topics is small (MAX_TOPICS = 4).
942		for num_topics_to_check in 1..=num_topics {
943			for topics_combination in statement.topics().iter().combinations(num_topics_to_check) {
944				// Find the topic with the fewest subscriptions to minimize the number of checks.
945				let Some(Some(topic_with_fewest)) = topics_combination
946					.iter()
947					.map(|topic| self.subscriptions_match_all_by_topic.get(*topic))
948					.min_by_key(|subscriptions| {
949						subscriptions.map_or(0, |subscriptions_by_length| {
950							subscriptions_by_length[num_topics_to_check - 1].len()
951						})
952					})
953				else {
954					continue;
955				};
956
957				for subscription in topic_with_fewest[num_topics_to_check - 1]
958					.values()
959					.filter(|subscription| subscription.topic_filter.matches(statement))
960				{
961					Self::record_match(matches, subscription, seq);
962				}
963			}
964		}
965	}
966
967	// Collect all subscribers that don't filter by topic and want to receive all statements.
968	fn collect_any_subscribers(&self, seq: u64, matches: &mut HashMap<SeqID, MatchedSubscription>) {
969		for subscription in self.subscriptions_any.values() {
970			Self::record_match(matches, subscription, seq);
971		}
972	}
973
974	// Unsubscribe a subscription by its ID.
975	fn unsubscribe(&mut self, id: SeqID) {
976		let Some(entry) = self.by_sub_id.remove(&id) else {
977			return;
978		};
979
980		match entry {
981			SubscriptionRecord::SingleFilter { filter, .. } => {
982				self.remove_indexed_filter(id, SubscriptionFilterKey::Fixed, &filter);
983			},
984			SubscriptionRecord::MultiFilter { filters, .. } => {
985				for (filter_id, filter) in filters {
986					self.remove_indexed_filter(
987						id,
988						SubscriptionFilterKey::Dynamic(filter_id),
989						&filter,
990					);
991				}
992			},
993		}
994	}
995
996	fn remove_indexed_filter(
997		&mut self,
998		id: SeqID,
999		filter_key: SubscriptionFilterKey,
1000		filter: &OptimizedTopicFilter,
1001	) {
1002		let topics = match filter {
1003			OptimizedTopicFilter::Any => {
1004				self.subscriptions_any.remove(&(id, filter_key));
1005				return;
1006			},
1007			OptimizedTopicFilter::MatchAll(topics) => topics,
1008			OptimizedTopicFilter::MatchAny(topics) => topics,
1009		};
1010
1011		// Remove subscription from relevant maps.
1012		for topic in topics {
1013			// Check MatchAny map.
1014			if let Entry::Occupied(mut entry) = self.subscriptions_match_any_by_topic.entry(*topic)
1015			{
1016				entry.get_mut().remove(&(id, filter_key));
1017				if entry.get().is_empty() {
1018					entry.remove();
1019				}
1020			}
1021			// Check MatchAll map.
1022			if let Entry::Occupied(mut entry) = self.subscriptions_match_all_by_topic.entry(*topic)
1023			{
1024				for subscriptions in entry.get_mut().iter_mut() {
1025					if subscriptions.remove(&(id, filter_key)).is_some() {
1026						break;
1027					}
1028				}
1029				if entry.get().iter().all(|s| s.is_empty()) {
1030					entry.remove();
1031				}
1032			}
1033		}
1034	}
1035}
1036
1037// Handlers to communicate with subscription matcher tasks.
1038#[derive(Clone)]
1039pub struct SubscriptionsMatchersHandlers {
1040	// Channels to send messages to matcher tasks.
1041	matchers: Vec<async_channel::Sender<MatcherMessage>>,
1042}
1043
1044impl SubscriptionsMatchersHandlers {
1045	/// Create new SubscriptionsMatchersHandlers with the given matcher task senders.
1046	fn new(matchers: Vec<async_channel::Sender<MatcherMessage>>) -> SubscriptionsMatchersHandlers {
1047		SubscriptionsMatchersHandlers { matchers }
1048	}
1049
1050	// Send a message to the matcher task responsible for the given subscription ID.
1051	fn send_by_seq_id(&self, id: SeqID, message: MatcherMessage) {
1052		// If matchers channels are full we backpressure the sender, in this case it will be the
1053		// processing of new statements.
1054		if let Err(err) = self.try_send_by_seq_id(id, message) {
1055			log::error!(
1056				target: LOG_TARGET,
1057				"Failed to send statement to matcher task: {:?}", err
1058			);
1059		}
1060	}
1061
1062	fn try_send_by_seq_id(
1063		&self,
1064		id: SeqID,
1065		message: MatcherMessage,
1066	) -> std::result::Result<(), async_channel::TrySendError<MatcherMessage>> {
1067		self.sender_by_seq_id(id).try_send(message)
1068	}
1069
1070	fn sender_by_seq_id(&self, id: SeqID) -> async_channel::Sender<MatcherMessage> {
1071		let index: u64 = id.into();
1072		self.matchers[index as usize % self.matchers.len()].clone()
1073	}
1074
1075	// Send a new statement, tagged with its store sequence number, to all matcher tasks.
1076	fn send_all(&self, seq: u64, statement: Statement) {
1077		for sender in &self.matchers {
1078			if let Err(err) =
1079				sender.send_blocking(MatcherMessage::NewStatement(seq, statement.clone()))
1080			{
1081				log::error!(
1082					target: LOG_TARGET,
1083					"Failed to send message to matcher task: {:?}", err
1084				);
1085			}
1086		}
1087	}
1088}
1089
1090// Stream of statements for a subscription.
1091pub struct SubscriptionStatementsStream {
1092	// Channel to receive statements.
1093	pub rx: async_channel::Receiver<StatementEvent>,
1094	// Subscription ID, used for cleanup on drop.
1095	sub_id: SeqID,
1096	// Reference to the matchers for cleanup.
1097	matchers: SubscriptionsMatchersHandlers,
1098}
1099
1100// When the stream is dropped, unsubscribe from the matchers.
1101impl Drop for SubscriptionStatementsStream {
1102	fn drop(&mut self) {
1103		self.matchers
1104			.send_by_seq_id(self.sub_id, MatcherMessage::Unsubscribe(self.sub_id));
1105	}
1106}
1107
1108impl Stream for SubscriptionStatementsStream {
1109	type Item = StatementEvent;
1110
1111	fn poll_next(
1112		mut self: std::pin::Pin<&mut Self>,
1113		cx: &mut std::task::Context<'_>,
1114	) -> std::task::Poll<Option<Self::Item>> {
1115		self.rx.poll_next_unpin(cx)
1116	}
1117}
1118
1119#[cfg(test)]
1120mod tests {
1121
1122	use crate::tests::signed_statement;
1123
1124	use super::*;
1125	use sp_core::Decode;
1126	use sp_statement_store::Topic;
1127	use std::{
1128		sync::{mpsc, Mutex as StdMutex},
1129		thread,
1130		time::{Duration, Instant},
1131	};
1132
1133	fn unwrap_statement(item: StatementEvent) -> Bytes {
1134		match item {
1135			StatementEvent::NewStatements { mut statements, .. } => {
1136				assert_eq!(statements.len(), 1, "Expected exactly one statement in batch");
1137				statements.remove(0)
1138			},
1139		}
1140	}
1141
1142	fn fixed_subscription(seq_id: u64, topic_filter: OptimizedTopicFilter) -> IndexedSubscription {
1143		IndexedSubscription {
1144			topic_filter,
1145			seq_id: SeqID::from(seq_id),
1146			filter_key: SubscriptionFilterKey::Fixed,
1147			watermark: 0,
1148		}
1149	}
1150
1151	fn live_event_for(statement: &Statement, filter_ids: Vec<FilterId>) -> LiveStatementEvent {
1152		LiveStatementEvent {
1153			hash: statement.hash(),
1154			encoded: statement.encode(),
1155			matched_filter_ids: filter_ids,
1156		}
1157	}
1158
1159	struct TestReplaySnapshotProvider {
1160		statements: HashMap<sp_statement_store::Hash, Vec<u8>>,
1161		snapshot_hashes: Vec<sp_statement_store::Hash>,
1162		batch_len: u64,
1163	}
1164
1165	impl TestReplaySnapshotProvider {
1166		fn with_snapshot(snapshot: &[Statement], statements: &[Statement]) -> Self {
1167			Self {
1168				statements: statements
1169					.iter()
1170					.map(|statement| (statement.hash(), statement.encode()))
1171					.collect(),
1172				snapshot_hashes: snapshot.iter().map(Statement::hash).collect(),
1173				batch_len: snapshot.len() as u64,
1174			}
1175		}
1176	}
1177
1178	impl ReplaySnapshotProvider for TestReplaySnapshotProvider {
1179		fn register_replay(&self, enqueue: &mut dyn FnMut(u64) -> bool) -> Result<Option<u64>> {
1180			let watermark = self.snapshot_hashes.len() as u64;
1181			Ok(enqueue(watermark).then_some(watermark))
1182		}
1183
1184		fn replay_batch(
1185			&self,
1186			_filter: &OptimizedTopicFilter,
1187			cursor: u64,
1188			watermark: u64,
1189		) -> Result<ReplayBatch> {
1190			let end = watermark.min(self.snapshot_hashes.len() as u64).min(cursor + self.batch_len);
1191			let statements = self.snapshot_hashes[cursor as usize..end as usize]
1192				.iter()
1193				.filter_map(|hash| self.statements.get(hash).cloned())
1194				.collect();
1195			Ok(ReplayBatch { statements, cursor: end, done: end >= watermark })
1196		}
1197	}
1198
1199	struct BlockingReplaySnapshotProvider {
1200		statements: HashMap<sp_statement_store::Hash, Vec<u8>>,
1201		snapshot_hashes: Vec<sp_statement_store::Hash>,
1202		scan_started: mpsc::SyncSender<()>,
1203		continue_scan: StdMutex<mpsc::Receiver<()>>,
1204	}
1205
1206	impl ReplaySnapshotProvider for BlockingReplaySnapshotProvider {
1207		fn register_replay(&self, enqueue: &mut dyn FnMut(u64) -> bool) -> Result<Option<u64>> {
1208			let watermark = self.snapshot_hashes.len() as u64;
1209			Ok(enqueue(watermark).then_some(watermark))
1210		}
1211
1212		fn replay_batch(
1213			&self,
1214			_filter: &OptimizedTopicFilter,
1215			cursor: u64,
1216			watermark: u64,
1217		) -> Result<ReplayBatch> {
1218			self.scan_started.send(()).expect("test receiver remains open");
1219			self.continue_scan
1220				.lock()
1221				.expect("test mutex is not poisoned")
1222				.recv()
1223				.expect("test sender remains open");
1224			let end = watermark.min(self.snapshot_hashes.len() as u64);
1225			let statements = self.snapshot_hashes[cursor as usize..end as usize]
1226				.iter()
1227				.filter_map(|hash| self.statements.get(hash).cloned())
1228				.collect();
1229			Ok(ReplayBatch { statements, cursor: end, done: end >= watermark })
1230		}
1231	}
1232
1233	struct ReplayLoadBlockingProvider {
1234		body: Vec<u8>,
1235		load_started: mpsc::SyncSender<()>,
1236		continue_load: StdMutex<mpsc::Receiver<()>>,
1237	}
1238
1239	impl ReplaySnapshotProvider for ReplayLoadBlockingProvider {
1240		fn register_replay(&self, enqueue: &mut dyn FnMut(u64) -> bool) -> Result<Option<u64>> {
1241			Ok(enqueue(1).then_some(1))
1242		}
1243
1244		fn replay_batch(
1245			&self,
1246			_filter: &OptimizedTopicFilter,
1247			cursor: u64,
1248			watermark: u64,
1249		) -> Result<ReplayBatch> {
1250			assert_eq!(cursor, 0);
1251			assert_eq!(watermark, 1);
1252			self.load_started.send(()).expect("test receiver remains open");
1253			self.continue_load
1254				.lock()
1255				.expect("test mutex is not poisoned")
1256				.recv()
1257				.expect("test sender remains open");
1258			Ok(ReplayBatch { statements: vec![self.body.clone()], cursor: 1, done: true })
1259		}
1260	}
1261
1262	fn recv_multi_filter_event(
1263		stream: &MultiFilterEventStream,
1264		timeout: Duration,
1265	) -> Option<MultiFilterSubscriptionEvent> {
1266		let deadline = Instant::now() + timeout;
1267		loop {
1268			match stream.rx.try_recv() {
1269				Ok(event) => return Some(event),
1270				Err(async_channel::TryRecvError::Closed) => return None,
1271				Err(async_channel::TryRecvError::Empty) if Instant::now() < deadline => {
1272					thread::sleep(Duration::from_millis(1));
1273				},
1274				Err(async_channel::TryRecvError::Empty) => return None,
1275			}
1276		}
1277	}
1278
1279	#[test]
1280	fn multi_filter_replays_before_live_submissions_committed_during_snapshot() {
1281		let subscriptions =
1282			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 1);
1283		let filter_id = FilterId::new(0);
1284		let replayed = signed_statement(41);
1285		let delayed = signed_statement(40);
1286		let delayed_hash = delayed.hash();
1287		let live = signed_statement(42);
1288		let (scan_started_tx, scan_started_rx) = mpsc::sync_channel(1);
1289		let (continue_scan_tx, continue_scan_rx) = mpsc::sync_channel(1);
1290		let provider = Arc::new(BlockingReplaySnapshotProvider {
1291			statements: [(replayed.hash(), replayed.encode())].into_iter().collect(),
1292			snapshot_hashes: vec![replayed.hash()],
1293			scan_started: scan_started_tx,
1294			continue_scan: StdMutex::new(continue_scan_rx),
1295		});
1296		let (sub_id, stream) = subscriptions.subscribe_empty(provider.clone());
1297		let handle = SubscriptionHandle {
1298			sub_id,
1299			inner: Arc::new(Mutex::new(SubscriptionHandleInner::new())),
1300			matchers: subscriptions.matchers(),
1301			snapshot_provider: provider,
1302		};
1303
1304		let add_filter = thread::spawn(move || handle.add_filter(OptimizedTopicFilter::Any));
1305		scan_started_rx.recv().expect("snapshot scan starts");
1306
1307		// Both post-watermark live events wait behind replay.
1308		subscriptions.notify(0, replayed.clone());
1309		subscriptions.notify(1, delayed);
1310		subscriptions.notify(2, live.clone());
1311		continue_scan_tx.send(()).expect("snapshot scan is still waiting");
1312		assert_eq!(add_filter.join().expect("add-filter thread does not panic"), Ok(filter_id));
1313
1314		assert!(matches!(
1315			recv_multi_filter_event(&stream, Duration::from_secs(1)),
1316			Some(MultiFilterSubscriptionEvent::ReplayStatements { statements, .. })
1317				if statements == vec![replayed.encode()]
1318		));
1319		assert!(matches!(
1320			recv_multi_filter_event(&stream, Duration::from_secs(1)),
1321			Some(MultiFilterSubscriptionEvent::ReplayDone { filter_id: done }) if done == filter_id
1322		));
1323		assert!(matches!(
1324			recv_multi_filter_event(&stream, Duration::from_secs(1)),
1325			Some(MultiFilterSubscriptionEvent::NewStatement(event))
1326				if event.hash == delayed_hash && event.matched_filter_ids == vec![filter_id]
1327		));
1328		assert!(matches!(
1329			recv_multi_filter_event(&stream, Duration::from_secs(1)),
1330			Some(MultiFilterSubscriptionEvent::NewStatement(event))
1331				if event.hash == live.hash() && event.matched_filter_ids == vec![filter_id]
1332		));
1333		assert!(recv_multi_filter_event(&stream, Duration::from_millis(10)).is_none());
1334	}
1335
1336	#[test]
1337	fn add_filter_returns_before_matcher_drains_replay() {
1338		let subscriptions =
1339			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 1);
1340		let statement = signed_statement(43);
1341		let (load_started_tx, load_started_rx) = mpsc::sync_channel(1);
1342		let (continue_load_tx, continue_load_rx) = mpsc::sync_channel(1);
1343		let provider = Arc::new(ReplayLoadBlockingProvider {
1344			body: statement.encode(),
1345			load_started: load_started_tx,
1346			continue_load: StdMutex::new(continue_load_rx),
1347		});
1348		let (sub_id, _stream) = subscriptions.subscribe_empty(provider.clone());
1349		let handle = SubscriptionHandle {
1350			sub_id,
1351			inner: Arc::new(Mutex::new(SubscriptionHandleInner::new())),
1352			matchers: subscriptions.matchers(),
1353			snapshot_provider: provider,
1354		};
1355		let (result_tx, result_rx) = mpsc::sync_channel(1);
1356		let add_filter = thread::spawn(move || {
1357			result_tx
1358				.send(handle.add_filter(OptimizedTopicFilter::Any))
1359				.expect("test receiver remains open");
1360		});
1361
1362		load_started_rx.recv().expect("matcher starts loading replay");
1363		let result = result_rx.recv_timeout(Duration::from_secs(1));
1364		continue_load_tx.send(()).expect("replay load is still waiting");
1365		add_filter.join().expect("add-filter thread does not panic");
1366		assert_eq!(result, Ok(Ok(FilterId::new(0))));
1367	}
1368
1369	#[test]
1370	fn multi_filter_delivers_re_admitted_hash_skipped_during_replay() {
1371		let mut subscriptions = SubscriptionsInfo::new();
1372		let sub_id = SeqID::from(12);
1373		let filter_id = FilterId::new(1);
1374		let topic = Topic::from([9u8; 32]);
1375		let filter = OptimizedTopicFilter::MatchAny(vec![topic].into_iter().collect());
1376		let (tx, rx) = async_channel::bounded::<MultiFilterSubscriptionEvent>(10);
1377		let mut statement = signed_statement(42);
1378		statement.set_topic(0, topic);
1379		// The statement has an admission entry inside the replay range, but its body is no longer
1380		// retrievable, so the replay skips that sequence number.
1381		let provider = Arc::new(TestReplaySnapshotProvider::with_snapshot(
1382			std::slice::from_ref(&statement),
1383			&[],
1384		));
1385
1386		subscriptions.subscribe_empty(sub_id, provider, tx);
1387		subscriptions.add_filter(sub_id, filter_id, filter, vec![statement.hash()]);
1388
1389		// The body could not be loaded, so the replay finishes without emitting it.
1390		assert!(matches!(
1391			rx.try_recv(),
1392			Ok(MultiFilterSubscriptionEvent::ReplayDone { filter_id: done }) if done == filter_id
1393		));
1394
1395		// The statement is re-admitted at a new sequence. It was never delivered through replay,
1396		// so it must reach the subscriber as a live event.
1397		subscriptions.notify_matching_filters(1, &statement);
1398
1399		match rx.try_recv() {
1400			Ok(MultiFilterSubscriptionEvent::NewStatement(event)) => {
1401				assert_eq!(event.hash, statement.hash());
1402				assert_eq!(event.matched_filter_ids, vec![filter_id]);
1403			},
1404			other => {
1405				panic!("statement skipped during replay must be delivered live, got {other:?}")
1406			},
1407		}
1408
1409		subscriptions.notify_matching_filters(2, &statement);
1410		assert!(matches!(
1411			rx.try_recv(),
1412			Ok(MultiFilterSubscriptionEvent::NewStatement(event))
1413				if event.hash == statement.hash() && event.matched_filter_ids == vec![filter_id]
1414		));
1415	}
1416
1417	#[test]
1418	fn multi_filter_replay_resumes_from_the_returned_cursor() {
1419		let mut subscriptions = SubscriptionsInfo::new();
1420		let sub_id = SeqID::from(11);
1421		let filter_id = FilterId::new(1);
1422		let topic = Topic::from([9u8; 32]);
1423		let filter = OptimizedTopicFilter::MatchAny(vec![topic].into_iter().collect());
1424		let (tx, rx) = async_channel::bounded::<MultiFilterSubscriptionEvent>(10);
1425		let replayed: Vec<Statement> = (0..3u8)
1426			.map(|seed| {
1427				let mut statement = signed_statement(seed);
1428				statement.set_topic(0, topic);
1429				statement
1430			})
1431			.collect();
1432		let mut provider = TestReplaySnapshotProvider::with_snapshot(&replayed, &replayed);
1433		provider.batch_len = 1;
1434
1435		subscriptions.subscribe_empty(sub_id, Arc::new(provider), tx);
1436		subscriptions.add_filter(
1437			sub_id,
1438			filter_id,
1439			filter,
1440			replayed.iter().map(Statement::hash).collect(),
1441		);
1442
1443		let mut delivered = Vec::new();
1444		let mut replay_done = 0;
1445		while let Ok(event) = rx.try_recv() {
1446			match event {
1447				MultiFilterSubscriptionEvent::ReplayStatements { filter_id: id, statements } => {
1448					assert_eq!(id, filter_id);
1449					delivered.extend(statements);
1450				},
1451				MultiFilterSubscriptionEvent::ReplayDone { filter_id: id } => {
1452					assert_eq!(id, filter_id);
1453					replay_done += 1;
1454				},
1455				other => panic!("expected replay events, got {other:?}"),
1456			}
1457		}
1458		assert_eq!(delivered, replayed.iter().map(Statement::encode).collect::<Vec<_>>());
1459		assert_eq!(replay_done, 1);
1460	}
1461
1462	#[test]
1463	fn multi_filter_pushes_ready_live_events_without_request_next() {
1464		let mut subscriptions = SubscriptionsInfo::new();
1465		let sub_id = SeqID::from(7);
1466		let filter_id = FilterId::new(1);
1467		let topic = Topic::from([9u8; 32]);
1468		let filter = OptimizedTopicFilter::MatchAny(vec![topic].into_iter().collect());
1469		let (tx, rx) = async_channel::bounded::<MultiFilterSubscriptionEvent>(10);
1470		let mut statement = signed_statement(42);
1471		statement.set_topic(0, topic);
1472		let provider = Arc::new(TestReplaySnapshotProvider::with_snapshot(
1473			&[],
1474			std::slice::from_ref(&statement),
1475		));
1476
1477		subscriptions.subscribe_empty(sub_id, provider, tx);
1478		subscriptions.add_filter(sub_id, filter_id, filter, vec![]);
1479		assert!(matches!(
1480			rx.try_recv(),
1481			Ok(MultiFilterSubscriptionEvent::ReplayDone { filter_id: done_filter })
1482				if done_filter == filter_id
1483		));
1484
1485		subscriptions.notify_matching_filters(0, &statement);
1486
1487		match rx.try_recv() {
1488			Ok(MultiFilterSubscriptionEvent::NewStatement(event)) => {
1489				assert_eq!(event.hash, statement.hash());
1490				assert_eq!(event.matched_filter_ids, vec![filter_id]);
1491			},
1492			other => panic!("expected pushed live statement, got {other:?}"),
1493		}
1494	}
1495
1496	#[test]
1497	fn multi_filter_reserves_output_slot_for_stop() {
1498		let mut subscriptions = SubscriptionsInfo::new();
1499		let sub_id = SeqID::from(8);
1500		let filter_id = FilterId::new(1);
1501		let topic = Topic::from([9u8; 32]);
1502		let filter = OptimizedTopicFilter::MatchAny(vec![topic].into_iter().collect());
1503		let mut statements =
1504			Vec::with_capacity(SUBSCRIPTION_BUFFER_SIZE + STOP_RESERVE_CHANNEL_SLOTS);
1505		for seed in 0..=SUBSCRIPTION_BUFFER_SIZE as u64 {
1506			let mut statement = signed_statement(seed as u8);
1507			statement.set_topic(0, topic);
1508			statements.push(statement);
1509		}
1510		let provider = Arc::new(TestReplaySnapshotProvider::with_snapshot(&[], &statements));
1511		let (tx, rx) = async_channel::bounded::<MultiFilterSubscriptionEvent>(
1512			SUBSCRIPTION_BUFFER_SIZE + STOP_RESERVE_CHANNEL_SLOTS,
1513		);
1514
1515		subscriptions.subscribe_empty(sub_id, provider, tx);
1516		subscriptions.add_filter(sub_id, filter_id, filter, vec![]);
1517		assert!(matches!(
1518			rx.try_recv(),
1519			Ok(MultiFilterSubscriptionEvent::ReplayDone { filter_id: done_filter })
1520				if done_filter == filter_id
1521		));
1522
1523		for statement in statements.iter().take(SUBSCRIPTION_BUFFER_SIZE) {
1524			subscriptions.notify_matching_filters(0, statement);
1525		}
1526		assert_eq!(rx.len(), SUBSCRIPTION_BUFFER_SIZE);
1527
1528		subscriptions.notify_matching_filters(0, &statements[SUBSCRIPTION_BUFFER_SIZE]);
1529		assert_eq!(rx.len(), SUBSCRIPTION_BUFFER_SIZE + STOP_RESERVE_CHANNEL_SLOTS);
1530
1531		for _ in 0..SUBSCRIPTION_BUFFER_SIZE {
1532			assert!(matches!(rx.try_recv(), Ok(MultiFilterSubscriptionEvent::NewStatement(_))));
1533		}
1534		assert!(matches!(rx.try_recv(), Ok(MultiFilterSubscriptionEvent::Stop)));
1535		assert!(rx.is_closed());
1536	}
1537
1538	#[test]
1539	fn multi_filter_closed_channel_is_reported_as_closed_before_buffer_limit() {
1540		let mut state = MultiFilterSubscriptionState::new();
1541		let (tx, rx) = async_channel::bounded::<MultiFilterSubscriptionEvent>(
1542			SUBSCRIPTION_BUFFER_SIZE + STOP_RESERVE_CHANNEL_SLOTS,
1543		);
1544		for seed in 0..SUBSCRIPTION_BUFFER_SIZE {
1545			tx.try_send(MultiFilterSubscriptionEvent::NewStatement(live_event_for(
1546				&signed_statement(seed as u8),
1547				vec![FilterId::new(1)],
1548			)))
1549			.expect("channel capacity exceeds subscription buffer size; qed");
1550		}
1551		drop(rx);
1552
1553		assert!(matches!(
1554			SubscriptionsInfo::send_ready_event(
1555				&mut state,
1556				&tx,
1557				MultiFilterSubscriptionEvent::NewStatement(live_event_for(
1558					&signed_statement(42),
1559					vec![FilterId::new(1)],
1560				)),
1561			),
1562			ReadyEventDelivery::Closed
1563		));
1564		assert!(matches!(
1565			SubscriptionsInfo::send_ready_event(
1566				&mut state,
1567				&tx,
1568				MultiFilterSubscriptionEvent::Stop,
1569			),
1570			ReadyEventDelivery::Closed
1571		));
1572		assert!(!state.stopped);
1573	}
1574
1575	#[test]
1576	fn test_subscribe_unsubscribe() {
1577		let mut subscriptions = SubscriptionsInfo::new();
1578
1579		let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
1580		let topic1 = Topic::from([8u8; 32]);
1581		let topic2 = Topic::from([9u8; 32]);
1582		let sub_info1 = fixed_subscription(
1583			1,
1584			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1585		);
1586		subscriptions.subscribe(sub_info1.clone(), tx1);
1587		assert!(subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
1588		assert!(subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
1589		assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
1590		assert!(!subscriptions
1591			.subscriptions_any
1592			.contains_key(&(sub_info1.seq_id, sub_info1.filter_key)));
1593
1594		subscriptions.unsubscribe(sub_info1.seq_id);
1595		assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
1596		assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
1597	}
1598
1599	#[test]
1600	fn test_subscribe_any() {
1601		let mut subscriptions = SubscriptionsInfo::new();
1602		let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
1603		let sub_info1 = fixed_subscription(1, OptimizedTopicFilter::Any);
1604		subscriptions.subscribe(sub_info1.clone(), tx1);
1605		assert!(subscriptions
1606			.subscriptions_any
1607			.contains_key(&(sub_info1.seq_id, sub_info1.filter_key)));
1608		assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
1609		subscriptions.unsubscribe(sub_info1.seq_id);
1610		assert!(!subscriptions
1611			.subscriptions_any
1612			.contains_key(&(sub_info1.seq_id, sub_info1.filter_key)));
1613	}
1614
1615	#[test]
1616	fn test_subscribe_match_any() {
1617		let mut subscriptions = SubscriptionsInfo::new();
1618
1619		let (tx1, _rx1) = async_channel::bounded::<StatementEvent>(10);
1620		let topic1 = Topic::from([8u8; 32]);
1621		let topic2 = Topic::from([9u8; 32]);
1622		let sub_info1 = fixed_subscription(
1623			1,
1624			OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()),
1625		);
1626		subscriptions.subscribe(sub_info1.clone(), tx1);
1627		assert!(subscriptions.subscriptions_match_any_by_topic.contains_key(&topic1));
1628		assert!(subscriptions.subscriptions_match_any_by_topic.contains_key(&topic2));
1629		assert!(subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
1630		assert!(!subscriptions
1631			.subscriptions_any
1632			.contains_key(&(sub_info1.seq_id, sub_info1.filter_key)));
1633
1634		subscriptions.unsubscribe(sub_info1.seq_id);
1635		assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic1));
1636		assert!(!subscriptions.subscriptions_match_all_by_topic.contains_key(&topic2));
1637	}
1638
1639	#[test]
1640	fn test_notify_matching_filters_any() {
1641		let mut subscriptions = SubscriptionsInfo::new();
1642
1643		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1644		let sub_info1 = fixed_subscription(1, OptimizedTopicFilter::Any);
1645		subscriptions.subscribe(sub_info1.clone(), tx1);
1646
1647		let statement = signed_statement(1);
1648		subscriptions.notify_matching_filters(0, &statement);
1649
1650		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
1651		let decoded_statement: Statement =
1652			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1653		assert_eq!(decoded_statement, statement);
1654	}
1655
1656	#[test]
1657	fn test_notify_matching_filters_match_all() {
1658		let mut subscriptions = SubscriptionsInfo::new();
1659
1660		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1661		let topic1 = Topic::from([8u8; 32]);
1662		let topic2 = Topic::from([9u8; 32]);
1663		let sub_info1 = fixed_subscription(
1664			1,
1665			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1666		);
1667		subscriptions.subscribe(sub_info1.clone(), tx1);
1668
1669		let mut statement = signed_statement(1);
1670		statement.set_topic(0, topic2);
1671		subscriptions.notify_matching_filters(0, &statement);
1672
1673		// Should not receive yet, only one topic matched.
1674		assert!(rx1.try_recv().is_err());
1675
1676		statement.set_topic(1, topic1);
1677		subscriptions.notify_matching_filters(1, &statement);
1678
1679		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
1680		let decoded_statement: Statement =
1681			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1682		assert_eq!(decoded_statement, statement);
1683	}
1684
1685	#[test]
1686	fn test_notify_matching_filters_match_any() {
1687		let mut subscriptions = SubscriptionsInfo::new();
1688		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1689		let (tx2, rx2) = async_channel::bounded::<StatementEvent>(10);
1690
1691		let topic1 = Topic::from([8u8; 32]);
1692		let topic2 = Topic::from([9u8; 32]);
1693		let sub_info1 = fixed_subscription(
1694			1,
1695			OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()),
1696		);
1697
1698		let sub_info2 = fixed_subscription(
1699			2,
1700			OptimizedTopicFilter::MatchAny(vec![topic2].into_iter().collect()),
1701		);
1702
1703		subscriptions.subscribe(sub_info1.clone(), tx1);
1704		subscriptions.subscribe(sub_info2.clone(), tx2);
1705
1706		let mut statement = signed_statement(1);
1707		statement.set_topic(0, topic1);
1708		statement.set_topic(1, topic2);
1709		subscriptions.notify_matching_filters(0, &statement);
1710
1711		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
1712		let decoded_statement: Statement =
1713			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1714		assert_eq!(decoded_statement, statement);
1715
1716		let received = unwrap_statement(rx2.try_recv().expect("Should receive statement"));
1717		let decoded_statement: Statement =
1718			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1719		assert_eq!(decoded_statement, statement);
1720	}
1721
1722	#[tokio::test]
1723	async fn test_subscription_handle_with_different_workers_number() {
1724		for num_workers in 1..5 {
1725			let subscriptions_handle = SubscriptionsHandle::new(
1726				Box::new(sp_core::testing::TaskExecutor::new()),
1727				num_workers,
1728			);
1729
1730			let topic1 = Topic::from([8u8; 32]);
1731			let topic2 = Topic::from([9u8; 32]);
1732
1733			let streams = (0..5)
1734				.into_iter()
1735				.map(|_| {
1736					subscriptions_handle.subscribe(
1737						OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1738						0,
1739					)
1740				})
1741				.collect::<Vec<_>>();
1742
1743			let mut statement = signed_statement(1);
1744			statement.set_topic(0, topic2);
1745			subscriptions_handle.notify(0, statement.clone());
1746
1747			statement.set_topic(1, topic1);
1748			subscriptions_handle.notify(1, statement.clone());
1749
1750			for (_tx, mut stream) in streams {
1751				let received =
1752					unwrap_statement(stream.next().await.expect("Should receive statement"));
1753				let decoded_statement: Statement =
1754					Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1755				assert_eq!(decoded_statement, statement);
1756			}
1757		}
1758	}
1759
1760	#[tokio::test]
1761	async fn test_handle_unsubscribe() {
1762		let subscriptions_handle =
1763			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
1764
1765		let topic1 = Topic::from([8u8; 32]);
1766		let topic2 = Topic::from([9u8; 32]);
1767
1768		let (tx, mut stream) = subscriptions_handle.subscribe(
1769			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1770			0,
1771		);
1772
1773		let mut statement = signed_statement(1);
1774		statement.set_topic(0, topic1);
1775		statement.set_topic(1, topic2);
1776
1777		// Send a statement and verify it's received.
1778		subscriptions_handle.notify(0, statement.clone());
1779
1780		let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
1781		let decoded_statement: Statement =
1782			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1783		assert_eq!(decoded_statement, statement);
1784
1785		// Drop the stream to trigger unsubscribe.
1786		drop(stream);
1787
1788		// Give some time for the unsubscribe message to be processed.
1789		tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
1790
1791		// Send another statement after unsubscribe.
1792		let mut statement2 = signed_statement(2);
1793		statement2.set_topic(0, topic1);
1794		statement2.set_topic(1, topic2);
1795		subscriptions_handle.notify(1, statement2.clone());
1796
1797		// The tx channel should be closed/disconnected since the subscription was removed.
1798		// Give some time for the notification to potentially arrive (it shouldn't).
1799		tokio::time::sleep(std::time::Duration::from_millis(1000)).await;
1800
1801		// The sender should fail to send since the subscription is gone.
1802		// We verify by checking that the tx channel is disconnected.
1803		assert!(tx.is_closed(), "Sender should be closed after unsubscribe");
1804	}
1805
1806	#[test]
1807	fn test_unsubscribe_nonexistent() {
1808		let mut subscriptions = SubscriptionsInfo::new();
1809		// Unsubscribing a non-existent subscription should not panic.
1810		subscriptions.unsubscribe(SeqID::from(999));
1811		// Verify internal state is still valid.
1812		assert!(subscriptions.by_sub_id.is_empty());
1813		assert!(subscriptions.subscriptions_any.is_empty());
1814		assert!(subscriptions.subscriptions_match_all_by_topic.is_empty());
1815		assert!(subscriptions.subscriptions_match_any_by_topic.is_empty());
1816	}
1817
1818	#[test]
1819	fn test_multiple_subscriptions_same_topic() {
1820		let mut subscriptions = SubscriptionsInfo::new();
1821
1822		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1823		let (tx2, rx2) = async_channel::bounded::<StatementEvent>(10);
1824		let topic1 = Topic::from([8u8; 32]);
1825		let topic2 = Topic::from([9u8; 32]);
1826
1827		let sub_info1 = fixed_subscription(
1828			1,
1829			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1830		);
1831		let sub_info2 = fixed_subscription(
1832			2,
1833			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1834		);
1835
1836		subscriptions.subscribe(sub_info1.clone(), tx1);
1837		subscriptions.subscribe(sub_info2.clone(), tx2);
1838
1839		// Both subscriptions should be registered under each topic.
1840		assert_eq!(
1841			subscriptions
1842				.subscriptions_match_all_by_topic
1843				.get(&topic1)
1844				.unwrap()
1845				.iter()
1846				.map(|s| s.len())
1847				.sum::<usize>(),
1848			2
1849		);
1850		assert_eq!(
1851			subscriptions
1852				.subscriptions_match_all_by_topic
1853				.get(&topic2)
1854				.unwrap()
1855				.iter()
1856				.map(|s| s.len())
1857				.sum::<usize>(),
1858			2
1859		);
1860
1861		// Send a matching statement.
1862		let mut statement = signed_statement(1);
1863		statement.set_topic(0, topic1);
1864		statement.set_topic(1, topic2);
1865		subscriptions.notify_matching_filters(0, &statement);
1866
1867		// Both should receive.
1868		assert!(rx1.try_recv().is_ok());
1869		assert!(rx2.try_recv().is_ok());
1870
1871		// Unsubscribe one.
1872		subscriptions.unsubscribe(sub_info1.seq_id);
1873
1874		// Only one subscription should remain.
1875		assert_eq!(
1876			subscriptions
1877				.subscriptions_match_all_by_topic
1878				.get(&topic1)
1879				.unwrap()
1880				.iter()
1881				.map(|s| s.len())
1882				.sum::<usize>(),
1883			1
1884		);
1885		assert_eq!(
1886			subscriptions
1887				.subscriptions_match_all_by_topic
1888				.get(&topic2)
1889				.unwrap()
1890				.iter()
1891				.map(|s| s.len())
1892				.sum::<usize>(),
1893			1
1894		);
1895		assert!(!subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
1896		assert!(subscriptions.by_sub_id.contains_key(&sub_info2.seq_id));
1897
1898		// Send another statement.
1899		subscriptions.notify_matching_filters(1, &statement);
1900
1901		// Only sub2 should receive.
1902		assert!(rx2.try_recv().is_ok());
1903		assert!(rx1.try_recv().is_err());
1904	}
1905
1906	#[test]
1907	fn test_subscriber_auto_unsubscribe_on_channel_full() {
1908		let mut subscriptions = SubscriptionsInfo::new();
1909
1910		// Create a channel with capacity 1.
1911		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(1);
1912		let topic1 = Topic::from([8u8; 32]);
1913
1914		let sub_info1 = fixed_subscription(
1915			1,
1916			OptimizedTopicFilter::MatchAny(vec![topic1].into_iter().collect()),
1917		);
1918		subscriptions.subscribe(sub_info1.clone(), tx1);
1919
1920		let mut statement = signed_statement(1);
1921		statement.set_topic(0, topic1);
1922
1923		// First notification should succeed.
1924		subscriptions.notify_matching_filters(0, &statement);
1925		assert!(rx1.try_recv().is_ok());
1926
1927		// Fill the channel.
1928		subscriptions.notify_matching_filters(1, &statement);
1929		// Channel is now full.
1930
1931		// Next notification should trigger auto-unsubscribe.
1932		subscriptions.notify_matching_filters(2, &statement);
1933
1934		// Subscription should be removed.
1935		assert!(!subscriptions.by_sub_id.contains_key(&sub_info1.seq_id));
1936		assert!(!subscriptions.subscriptions_match_any_by_topic.contains_key(&topic1));
1937	}
1938
1939	#[test]
1940	fn test_match_any_receives_once_per_statement() {
1941		let mut subscriptions = SubscriptionsInfo::new();
1942
1943		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1944		let topic1 = Topic::from([8u8; 32]);
1945		let topic2 = Topic::from([9u8; 32]);
1946
1947		// Subscribe to MatchAny with both topics.
1948		let sub_info1 = fixed_subscription(
1949			1,
1950			OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()),
1951		);
1952		subscriptions.subscribe(sub_info1.clone(), tx1);
1953
1954		// Create a statement that matches BOTH topics.
1955		let mut statement = signed_statement(1);
1956		statement.set_topic(0, topic1);
1957		statement.set_topic(1, topic2);
1958
1959		subscriptions.notify_matching_filters(0, &statement);
1960
1961		// Should receive exactly once, not twice.
1962		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
1963		let decoded_statement: Statement =
1964			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
1965		assert_eq!(decoded_statement, statement);
1966
1967		// No more messages.
1968		assert!(rx1.try_recv().is_err());
1969	}
1970
1971	#[test]
1972	fn test_match_all_receives_once_per_statement() {
1973		// A `MatchAll` subscriber must receive each matching statement exactly once, even when it
1974		// is registered under several of the statement's topics and the matcher therefore
1975		// encounters it across multiple topic combinations.
1976		let mut subscriptions = SubscriptionsInfo::new();
1977
1978		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
1979		let (tx2, _rx2) = async_channel::bounded::<StatementEvent>(10);
1980		let (tx3, _rx3) = async_channel::bounded::<StatementEvent>(10);
1981
1982		let topic1 = Topic::from([1u8; 32]);
1983		let topic2 = Topic::from([2u8; 32]);
1984		let topic3 = Topic::from([3u8; 32]);
1985		let topic4 = Topic::from([4u8; 32]);
1986
1987		// The subscription under test: MatchAll on topic1 AND topic2 (stored under both topics).
1988		let sub_info1 = fixed_subscription(
1989			1,
1990			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
1991		);
1992		subscriptions.subscribe(sub_info1, tx1);
1993
1994		// Extra MatchAll subscriptions on topic3 so the matcher encounters sub_info1 across
1995		// several topic combinations of the statement below. They do not match the statement
1996		// themselves (topic4 is absent).
1997		let sub_info2 = fixed_subscription(
1998			2,
1999			OptimizedTopicFilter::MatchAll(vec![topic3, topic4].into_iter().collect()),
2000		);
2001		let sub_info3 = fixed_subscription(
2002			3,
2003			OptimizedTopicFilter::MatchAll(vec![topic3, topic4].into_iter().collect()),
2004		);
2005		subscriptions.subscribe(sub_info2, tx2);
2006		subscriptions.subscribe(sub_info3, tx3);
2007
2008		// Statement carrying topic1, topic2 and topic3.
2009		let mut statement = signed_statement(1);
2010		statement.set_topic(0, topic1);
2011		statement.set_topic(1, topic2);
2012		statement.set_topic(2, topic3);
2013
2014		subscriptions.notify_matching_filters(0, &statement);
2015
2016		// sub_info1 must receive the statement exactly once.
2017		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
2018		let decoded_statement: Statement =
2019			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2020		assert_eq!(decoded_statement, statement);
2021
2022		assert!(
2023			rx1.try_recv().is_err(),
2024			"MatchAll subscriber must receive each statement only once"
2025		);
2026	}
2027
2028	#[test]
2029	fn test_match_all_with_single_topic_matches_statement_with_two_topics() {
2030		let mut subscriptions = SubscriptionsInfo::new();
2031
2032		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
2033		let topic1 = Topic::from([8u8; 32]);
2034		let topic2 = Topic::from([9u8; 32]);
2035
2036		// Subscribe with MatchAll on only topic1.
2037		let sub_info1 = fixed_subscription(
2038			1,
2039			OptimizedTopicFilter::MatchAll(vec![topic1].into_iter().collect()),
2040		);
2041		subscriptions.subscribe(sub_info1.clone(), tx1);
2042
2043		// Create a statement that has BOTH topic1 and topic2.
2044		let mut statement = signed_statement(1);
2045		statement.set_topic(0, topic1);
2046		statement.set_topic(1, topic2);
2047
2048		subscriptions.notify_matching_filters(0, &statement);
2049
2050		// Should receive because the statement contains topic1 (which is the only required topic).
2051		let received = unwrap_statement(rx1.try_recv().expect("Should receive statement"));
2052		let decoded_statement: Statement =
2053			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2054		assert_eq!(decoded_statement, statement);
2055
2056		// No more messages.
2057		assert!(rx1.try_recv().is_err());
2058	}
2059
2060	#[test]
2061	fn test_match_all_no_matching_topics() {
2062		let mut subscriptions = SubscriptionsInfo::new();
2063
2064		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
2065		let topic1 = Topic::from([8u8; 32]);
2066		let topic2 = Topic::from([9u8; 32]);
2067		let topic3 = Topic::from([10u8; 32]);
2068
2069		let sub_info1 = fixed_subscription(
2070			1,
2071			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
2072		);
2073		subscriptions.subscribe(sub_info1.clone(), tx1);
2074
2075		// Statement with completely different topics.
2076		let mut statement = signed_statement(1);
2077		statement.set_topic(0, topic3);
2078
2079		subscriptions.notify_matching_filters(0, &statement);
2080
2081		// Should not receive anything.
2082		assert!(rx1.try_recv().is_err());
2083	}
2084
2085	#[test]
2086	fn test_match_all_with_unsubscribed_topic_first_in_statement() {
2087		// This test guards against returning early when one statement topic has no subscriptions.
2088		// The matcher must still check later topic combinations that can match.
2089		let mut subscriptions = SubscriptionsInfo::new();
2090
2091		let (tx1, rx1) = async_channel::bounded::<StatementEvent>(10);
2092		// topic1 will have NO subscriptions
2093		let topic1 = Topic::from([1u8; 32]);
2094		// topic2 WILL have a subscription
2095		let topic2 = Topic::from([2u8; 32]);
2096
2097		// Subscribe only to topic2 with MatchAll filter.
2098		let sub_info1 = fixed_subscription(
2099			1,
2100			OptimizedTopicFilter::MatchAll(vec![topic2].into_iter().collect()),
2101		);
2102		subscriptions.subscribe(sub_info1, tx1);
2103
2104		// Create a statement with BOTH topics. topic1 comes first (lower bytes).
2105		// When iterating combinations(1), [topic1] is checked before [topic2].
2106		// Since topic1 has no subscriptions, the buggy `return` exits early,
2107		// preventing the [topic2] combination from being checked.
2108		let mut statement = signed_statement(1);
2109		statement.set_topic(0, topic1);
2110		statement.set_topic(1, topic2);
2111
2112		subscriptions.notify_matching_filters(0, &statement);
2113
2114		// The receive succeeds only if the matcher checks the [topic2] combination.
2115		let received = unwrap_statement(
2116			rx1.try_recv()
2117				.expect("Should receive statement from a later matching topic combination"),
2118		);
2119		let decoded_statement: Statement =
2120			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2121		assert_eq!(decoded_statement, statement);
2122	}
2123
2124	#[tokio::test]
2125	async fn test_handle_with_match_any_filter() {
2126		let subscriptions_handle =
2127			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
2128
2129		let topic1 = Topic::from([8u8; 32]);
2130		let topic2 = Topic::from([9u8; 32]);
2131
2132		let (_tx, mut stream) = subscriptions_handle.subscribe(
2133			OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()),
2134			0,
2135		);
2136
2137		// Statement matching only topic1.
2138		let mut statement1 = signed_statement(1);
2139		statement1.set_topic(0, topic1);
2140		subscriptions_handle.notify(0, statement1.clone());
2141
2142		let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
2143		let decoded_statement: Statement =
2144			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2145		assert_eq!(decoded_statement, statement1);
2146
2147		// Statement matching only topic2.
2148		let mut statement2 = signed_statement(2);
2149		statement2.set_topic(0, topic2);
2150		subscriptions_handle.notify(1, statement2.clone());
2151
2152		let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
2153		let decoded_statement: Statement =
2154			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2155		assert_eq!(decoded_statement, statement2);
2156	}
2157
2158	#[tokio::test]
2159	async fn test_handle_with_any_filter() {
2160		let subscriptions_handle =
2161			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
2162
2163		let (_tx, mut stream) = subscriptions_handle.subscribe(OptimizedTopicFilter::Any, 0);
2164
2165		// Send statements with various topics.
2166		let statement1 = signed_statement(1);
2167		subscriptions_handle.notify(0, statement1.clone());
2168
2169		let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
2170		let decoded_statement: Statement =
2171			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2172		assert_eq!(decoded_statement, statement1);
2173
2174		let mut statement2 = signed_statement(2);
2175		statement2.set_topic(0, Topic::from([99u8; 32]));
2176		subscriptions_handle.notify(1, statement2.clone());
2177
2178		let received = unwrap_statement(stream.next().await.expect("Should receive statement"));
2179		let decoded_statement: Statement =
2180			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2181		assert_eq!(decoded_statement, statement2);
2182	}
2183
2184	#[tokio::test]
2185	async fn test_handle_multiple_subscribers_different_filters() {
2186		let subscriptions_handle =
2187			SubscriptionsHandle::new(Box::new(sp_core::testing::TaskExecutor::new()), 2);
2188
2189		let topic1 = Topic::from([8u8; 32]);
2190		let topic2 = Topic::from([9u8; 32]);
2191
2192		// Subscriber 1: MatchAll on topic1 and topic2.
2193		let (_tx1, mut stream1) = subscriptions_handle.subscribe(
2194			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
2195			0,
2196		);
2197
2198		// Subscriber 2: MatchAny on topic1.
2199		let (_tx2, mut stream2) = subscriptions_handle
2200			.subscribe(OptimizedTopicFilter::MatchAny(vec![topic1].into_iter().collect()), 0);
2201
2202		// Subscriber 3: Any.
2203		let (_tx3, mut stream3) = subscriptions_handle.subscribe(OptimizedTopicFilter::Any, 0);
2204
2205		// Statement matching only topic1.
2206		let mut statement1 = signed_statement(1);
2207		statement1.set_topic(0, topic1);
2208		subscriptions_handle.notify(0, statement1.clone());
2209
2210		// stream1 should NOT receive (needs both topics).
2211		// stream2 should receive (MatchAny topic1).
2212		// stream3 should receive (Any).
2213
2214		let received2 = unwrap_statement(stream2.next().await.expect("stream2 should receive"));
2215		let decoded2: Statement = Statement::decode(&mut &received2.0[..]).unwrap();
2216		assert_eq!(decoded2, statement1);
2217
2218		let received3 = unwrap_statement(stream3.next().await.expect("stream3 should receive"));
2219		let decoded3: Statement = Statement::decode(&mut &received3.0[..]).unwrap();
2220		assert_eq!(decoded3, statement1);
2221
2222		// Statement matching both topics.
2223		let mut statement2 = signed_statement(2);
2224		statement2.set_topic(0, topic1);
2225		statement2.set_topic(1, topic2);
2226		subscriptions_handle.notify(1, statement2.clone());
2227
2228		// All should receive.
2229		let received1 = unwrap_statement(stream1.next().await.expect("stream1 should receive"));
2230		let decoded1: Statement = Statement::decode(&mut &received1.0[..]).unwrap();
2231		assert_eq!(decoded1, statement2);
2232
2233		let received2 = unwrap_statement(stream2.next().await.expect("stream2 should receive"));
2234		let decoded2: Statement = Statement::decode(&mut &received2.0[..]).unwrap();
2235		assert_eq!(decoded2, statement2);
2236
2237		let received3 = unwrap_statement(stream3.next().await.expect("stream3 should receive"));
2238		let decoded3: Statement = Statement::decode(&mut &received3.0[..]).unwrap();
2239		assert_eq!(decoded3, statement2);
2240	}
2241
2242	#[test]
2243	fn test_statement_without_topics_matches_only_any_filter() {
2244		let mut subscriptions = SubscriptionsInfo::new();
2245
2246		let (tx_match_all, rx_match_all) = async_channel::bounded::<StatementEvent>(10);
2247		let (tx_match_any, rx_match_any) = async_channel::bounded::<StatementEvent>(10);
2248		let (tx_any, rx_any) = async_channel::bounded::<StatementEvent>(10);
2249
2250		let topic1 = Topic::from([8u8; 32]);
2251		let topic2 = Topic::from([9u8; 32]);
2252
2253		// Subscribe with MatchAll filter.
2254		let sub_match_all = fixed_subscription(
2255			1,
2256			OptimizedTopicFilter::MatchAll(vec![topic1, topic2].into_iter().collect()),
2257		);
2258		subscriptions.subscribe(sub_match_all, tx_match_all);
2259
2260		// Subscribe with MatchAny filter.
2261		let sub_match_any = fixed_subscription(
2262			2,
2263			OptimizedTopicFilter::MatchAny(vec![topic1, topic2].into_iter().collect()),
2264		);
2265		subscriptions.subscribe(sub_match_any, tx_match_any);
2266
2267		// Subscribe with Any filter.
2268		let sub_any = fixed_subscription(3, OptimizedTopicFilter::Any);
2269		subscriptions.subscribe(sub_any, tx_any);
2270
2271		// Create a statement without any topics set.
2272		let statement = signed_statement(1);
2273		assert!(statement.topics().is_empty(), "Statement should have no topics");
2274
2275		// Notify all matching filters.
2276		subscriptions.notify_matching_filters(0, &statement);
2277
2278		// Any should receive (matches all statements regardless of topics).
2279		let received =
2280			unwrap_statement(rx_any.try_recv().expect("Any filter should receive statement"));
2281		let decoded_statement: Statement =
2282			Statement::decode(&mut &received.0[..]).expect("Should decode statement");
2283		assert_eq!(decoded_statement, statement);
2284
2285		// MatchAll should NOT receive (statement has no topics, filter requires topic1 AND topic2).
2286		assert!(
2287			rx_match_all.try_recv().is_err(),
2288			"MatchAll should not receive statement without topics"
2289		);
2290
2291		// MatchAny should NOT receive (statement has no topics, filter requires topic1 OR topic2).
2292		assert!(
2293			rx_match_any.try_recv().is_err(),
2294			"MatchAny should not receive statement without topics"
2295		);
2296	}
2297}