1pub use crate::runtime_api::StatementSource;
19use crate::{Hash, Statement, Topic, MAX_ANY_TOPICS, MAX_TOPICS};
20use sp_core::{bounded_vec::BoundedVec, Bytes, ConstU32};
21use std::collections::HashSet;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
25pub struct FilterId(u64);
26
27impl FilterId {
28 pub fn new(id: u64) -> Self {
30 FilterId(id)
31 }
32
33 pub fn as_u64(&self) -> u64 {
35 self.0
36 }
37}
38
39#[derive(Debug, Clone)]
41pub struct LiveStatementEvent {
42 pub hash: Hash,
44 pub encoded: Vec<u8>,
46 pub matched_filter_ids: Vec<FilterId>,
48}
49
50#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
53pub enum Error {
54 #[error("Database error: {0:?}")]
56 Db(String),
57 #[error("Decoding error: {0:?}")]
59 Decode(String),
60 #[error("Storage error: {0:?}")]
62 Storage(String),
63 #[error("Invalid configuration: {0}")]
65 InvalidConfig(String),
66}
67
68#[derive(Debug, Clone)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
72pub enum TopicFilter {
73 Any,
75 MatchAll(BoundedVec<Topic, ConstU32<{ MAX_TOPICS as u32 }>>),
78 MatchAny(BoundedVec<Topic, ConstU32<{ MAX_ANY_TOPICS as u32 }>>),
81}
82
83#[derive(Clone, Debug)]
85pub enum OptimizedTopicFilter {
86 Any,
88 MatchAll(HashSet<Topic>),
91 MatchAny(HashSet<Topic>),
94}
95
96impl OptimizedTopicFilter {
97 pub fn matches(&self, statement: &Statement) -> bool {
99 match self {
100 OptimizedTopicFilter::Any => true,
101 OptimizedTopicFilter::MatchAll(topics) => {
102 topics.iter().all(|topic| statement.topics().contains(topic))
103 },
104 OptimizedTopicFilter::MatchAny(topics) => {
105 statement.topics().iter().any(|topic| topics.contains(topic))
106 },
107 }
108 }
109}
110
111impl From<TopicFilter> for OptimizedTopicFilter {
113 fn from(filter: TopicFilter) -> Self {
114 match filter {
115 TopicFilter::Any => OptimizedTopicFilter::Any,
116 TopicFilter::MatchAll(topics) => {
117 let mut parsed_topics = HashSet::with_capacity(topics.len());
118 for topic in topics {
119 parsed_topics.insert(topic);
120 }
121 OptimizedTopicFilter::MatchAll(parsed_topics)
122 },
123 TopicFilter::MatchAny(topics) => {
124 let mut parsed_topics = HashSet::with_capacity(topics.len());
125 for topic in topics {
126 parsed_topics.insert(topic);
127 }
128 OptimizedTopicFilter::MatchAny(parsed_topics)
129 },
130 }
131 }
132}
133
134#[derive(Debug, Clone, Eq, PartialEq)]
136#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
137#[cfg_attr(feature = "serde", serde(tag = "reason", rename_all = "camelCase"))]
138pub enum RejectionReason {
139 DataTooLarge {
141 submitted_size: usize,
143 available_size: usize,
145 },
146 ChannelPriorityTooLow {
148 submitted_expiry: u64,
150 min_expiry: u64,
152 },
153 AccountFull {
155 submitted_expiry: u64,
157 min_expiry: u64,
159 },
160 StoreFull,
162 NoAllowance,
164}
165
166impl RejectionReason {
167 pub fn label(&self) -> &'static str {
169 match self {
170 RejectionReason::DataTooLarge { .. } => "data_too_large",
171 RejectionReason::ChannelPriorityTooLow { .. } => "channel_priority_too_low",
172 RejectionReason::AccountFull { .. } => "account_full",
173 RejectionReason::StoreFull => "store_full",
174 RejectionReason::NoAllowance => "no_allowance",
175 }
176 }
177}
178
179#[derive(Debug, Clone, Eq, PartialEq)]
181#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
182#[cfg_attr(feature = "serde", serde(tag = "reason", rename_all = "camelCase"))]
183pub enum InvalidReason {
184 NoProof,
186 BadProof,
188 EncodingTooLarge {
190 submitted_size: usize,
192 max_size: usize,
194 },
195 AlreadyExpired,
197}
198
199impl InvalidReason {
200 pub fn label(&self) -> &'static str {
202 match self {
203 InvalidReason::NoProof => "no_proof",
204 InvalidReason::BadProof => "bad_proof",
205 InvalidReason::EncodingTooLarge { .. } => "encoding_too_large",
206 InvalidReason::AlreadyExpired => "already_expired",
207 }
208 }
209}
210
211#[derive(Debug, Clone, Eq, PartialEq)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
214#[cfg_attr(feature = "serde", serde(tag = "status", rename_all = "camelCase"))]
215pub enum SubmitResult {
216 New,
218 Known,
220 KnownExpired,
222 Rejected(RejectionReason),
224 Invalid(InvalidReason),
226 InternalError(Error),
228}
229
230#[derive(Debug, Clone, Eq, PartialEq)]
232#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
233#[cfg_attr(
234 feature = "serde",
235 serde(tag = "reason", rename_all = "camelCase", rename_all_fields = "camelCase")
236)]
237pub enum SubmitRejectionReason {
238 DataTooLarge {
240 submitted_size: usize,
242 available_size: usize,
244 },
245 ChannelPriorityTooLow {
247 submitted_expiry: u64,
249 min_expiry: u64,
251 },
252 AccountFull {
254 submitted_expiry: u64,
256 min_expiry: u64,
258 },
259 StoreFull,
261 NoAllowance,
263}
264
265impl From<RejectionReason> for SubmitRejectionReason {
266 fn from(reason: RejectionReason) -> Self {
267 match reason {
268 RejectionReason::DataTooLarge { submitted_size, available_size } => {
269 SubmitRejectionReason::DataTooLarge { submitted_size, available_size }
270 },
271 RejectionReason::ChannelPriorityTooLow { submitted_expiry, min_expiry } => {
272 SubmitRejectionReason::ChannelPriorityTooLow { submitted_expiry, min_expiry }
273 },
274 RejectionReason::AccountFull { submitted_expiry, min_expiry } => {
275 SubmitRejectionReason::AccountFull { submitted_expiry, min_expiry }
276 },
277 RejectionReason::StoreFull => SubmitRejectionReason::StoreFull,
278 RejectionReason::NoAllowance => SubmitRejectionReason::NoAllowance,
279 }
280 }
281}
282
283#[derive(Debug, Clone, Eq, PartialEq)]
285#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
286#[cfg_attr(
287 feature = "serde",
288 serde(tag = "reason", rename_all = "camelCase", rename_all_fields = "camelCase")
289)]
290pub enum SubmitInvalidReason {
291 NoProof,
293 BadProof,
295 EncodingTooLarge {
297 submitted_size: usize,
299 max_size: usize,
301 },
302 AlreadyExpired,
304}
305
306impl From<InvalidReason> for SubmitInvalidReason {
307 fn from(reason: InvalidReason) -> Self {
308 match reason {
309 InvalidReason::NoProof => SubmitInvalidReason::NoProof,
310 InvalidReason::BadProof => SubmitInvalidReason::BadProof,
311 InvalidReason::EncodingTooLarge { submitted_size, max_size } => {
312 SubmitInvalidReason::EncodingTooLarge { submitted_size, max_size }
313 },
314 InvalidReason::AlreadyExpired => SubmitInvalidReason::AlreadyExpired,
315 }
316 }
317}
318
319#[derive(Debug, Clone, Eq, PartialEq)]
321#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
322#[cfg_attr(feature = "serde", serde(tag = "status", rename_all = "camelCase"))]
323pub enum SubmitOutcome {
324 New,
326 Known,
328 Rejected(SubmitRejectionReason),
330 Invalid(SubmitInvalidReason),
332}
333
334impl SubmitOutcome {
335 pub fn from_submit_result(result: SubmitResult) -> std::result::Result<Self, Error> {
337 match result {
338 SubmitResult::New => Ok(SubmitOutcome::New),
339 SubmitResult::Known => Ok(SubmitOutcome::Known),
340 SubmitResult::KnownExpired => {
342 Err(Error::Storage("unexpected KnownExpired on local submission".into()))
343 },
344 SubmitResult::Rejected(reason) => Ok(SubmitOutcome::Rejected(reason.into())),
345 SubmitResult::Invalid(reason) => Ok(SubmitOutcome::Invalid(reason.into())),
346 SubmitResult::InternalError(error) => Err(error),
347 }
348 }
349}
350
351#[derive(Debug, Clone, Eq, PartialEq)]
353#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
354#[cfg_attr(feature = "serde", serde(tag = "event", content = "data", rename_all = "camelCase"))]
355pub enum StatementEvent {
356 NewStatements {
358 statements: Vec<Bytes>,
361 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
366 remaining: Option<u32>,
367 },
368}
369
370pub type Result<T> = std::result::Result<T, Error>;
372
373#[derive(Debug, Clone, Copy, PartialEq, Eq)]
375pub enum FilterDecision {
376 Skip,
378 Take,
380 Abort,
382}
383
384pub trait StatementStore: Send + Sync {
386 fn statements(&self) -> Result<Vec<(Hash, Statement)>>;
388
389 fn take_recent_statements(&self) -> Result<Vec<(Hash, Statement)>>;
394
395 fn statement(&self, hash: &Hash) -> Result<Option<Statement>>;
397
398 fn has_statement(&self, hash: &Hash) -> bool;
402
403 fn statement_hashes(&self) -> Vec<Hash>;
405
406 fn statements_by_hashes(
415 &self,
416 hashes: &[Hash],
417 filter: &mut dyn FnMut(&Hash, &[u8], &Statement) -> FilterDecision,
418 ) -> Result<(Vec<(Hash, Statement)>, usize)>;
419
420 fn broadcasts(&self, match_all_topics: &[Topic]) -> Result<Vec<Vec<u8>>>;
423
424 fn posted(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
428
429 fn posted_clear(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
432
433 fn broadcasts_stmt(&self, match_all_topics: &[Topic]) -> Result<Vec<Vec<u8>>>;
436
437 fn posted_stmt(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
441
442 fn posted_clear_stmt(&self, match_all_topics: &[Topic], dest: [u8; 32])
448 -> Result<Vec<Vec<u8>>>;
449
450 fn submit(&self, statement: Statement, source: StatementSource) -> SubmitResult;
452
453 fn remove(&self, hash: &Hash) -> Result<()>;
455
456 fn remove_by(&self, who: [u8; 32]) -> Result<()>;
458}