pub use crate::runtime_api::StatementSource;
use crate::{Hash, Statement, Topic, MAX_ANY_TOPICS, MAX_TOPICS};
use sp_core::{bounded_vec::BoundedVec, Bytes, ConstU32};
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct FilterId(u64);
impl FilterId {
pub fn new(id: u64) -> Self {
FilterId(id)
}
pub fn as_u64(&self) -> u64 {
self.0
}
}
#[derive(Debug, Clone)]
pub struct LiveStatementEvent {
pub hash: Hash,
pub encoded: Vec<u8>,
pub matched_filter_ids: Vec<FilterId>,
}
#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Error {
#[error("Database error: {0:?}")]
Db(String),
#[error("Decoding error: {0:?}")]
Decode(String),
#[error("Storage error: {0:?}")]
Storage(String),
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum TopicFilter {
Any,
MatchAll(BoundedVec<Topic, ConstU32<{ MAX_TOPICS as u32 }>>),
MatchAny(BoundedVec<Topic, ConstU32<{ MAX_ANY_TOPICS as u32 }>>),
}
#[derive(Clone, Debug)]
pub enum OptimizedTopicFilter {
Any,
MatchAll(HashSet<Topic>),
MatchAny(HashSet<Topic>),
}
impl OptimizedTopicFilter {
pub fn matches(&self, statement: &Statement) -> bool {
match self {
OptimizedTopicFilter::Any => true,
OptimizedTopicFilter::MatchAll(topics) => {
topics.iter().all(|topic| statement.topics().contains(topic))
},
OptimizedTopicFilter::MatchAny(topics) => {
statement.topics().iter().any(|topic| topics.contains(topic))
},
}
}
}
impl From<TopicFilter> for OptimizedTopicFilter {
fn from(filter: TopicFilter) -> Self {
match filter {
TopicFilter::Any => OptimizedTopicFilter::Any,
TopicFilter::MatchAll(topics) => {
let mut parsed_topics = HashSet::with_capacity(topics.len());
for topic in topics {
parsed_topics.insert(topic);
}
OptimizedTopicFilter::MatchAll(parsed_topics)
},
TopicFilter::MatchAny(topics) => {
let mut parsed_topics = HashSet::with_capacity(topics.len());
for topic in topics {
parsed_topics.insert(topic);
}
OptimizedTopicFilter::MatchAny(parsed_topics)
},
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "reason", rename_all = "camelCase"))]
pub enum RejectionReason {
DataTooLarge {
submitted_size: usize,
available_size: usize,
},
ChannelPriorityTooLow {
submitted_expiry: u64,
min_expiry: u64,
},
AccountFull {
submitted_expiry: u64,
min_expiry: u64,
},
StoreFull,
NoAllowance,
}
impl RejectionReason {
pub fn label(&self) -> &'static str {
match self {
RejectionReason::DataTooLarge { .. } => "data_too_large",
RejectionReason::ChannelPriorityTooLow { .. } => "channel_priority_too_low",
RejectionReason::AccountFull { .. } => "account_full",
RejectionReason::StoreFull => "store_full",
RejectionReason::NoAllowance => "no_allowance",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "reason", rename_all = "camelCase"))]
pub enum InvalidReason {
NoProof,
BadProof,
EncodingTooLarge {
submitted_size: usize,
max_size: usize,
},
AlreadyExpired,
}
impl InvalidReason {
pub fn label(&self) -> &'static str {
match self {
InvalidReason::NoProof => "no_proof",
InvalidReason::BadProof => "bad_proof",
InvalidReason::EncodingTooLarge { .. } => "encoding_too_large",
InvalidReason::AlreadyExpired => "already_expired",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "status", rename_all = "camelCase"))]
pub enum SubmitResult {
New,
Known,
KnownExpired,
Rejected(RejectionReason),
Invalid(InvalidReason),
InternalError(Error),
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "reason", rename_all = "camelCase", rename_all_fields = "camelCase")
)]
pub enum SubmitRejectionReason {
DataTooLarge {
submitted_size: usize,
available_size: usize,
},
ChannelPriorityTooLow {
submitted_expiry: u64,
min_expiry: u64,
},
AccountFull {
submitted_expiry: u64,
min_expiry: u64,
},
StoreFull,
NoAllowance,
}
impl From<RejectionReason> for SubmitRejectionReason {
fn from(reason: RejectionReason) -> Self {
match reason {
RejectionReason::DataTooLarge { submitted_size, available_size } => {
SubmitRejectionReason::DataTooLarge { submitted_size, available_size }
},
RejectionReason::ChannelPriorityTooLow { submitted_expiry, min_expiry } => {
SubmitRejectionReason::ChannelPriorityTooLow { submitted_expiry, min_expiry }
},
RejectionReason::AccountFull { submitted_expiry, min_expiry } => {
SubmitRejectionReason::AccountFull { submitted_expiry, min_expiry }
},
RejectionReason::StoreFull => SubmitRejectionReason::StoreFull,
RejectionReason::NoAllowance => SubmitRejectionReason::NoAllowance,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(
feature = "serde",
serde(tag = "reason", rename_all = "camelCase", rename_all_fields = "camelCase")
)]
pub enum SubmitInvalidReason {
NoProof,
BadProof,
EncodingTooLarge {
submitted_size: usize,
max_size: usize,
},
AlreadyExpired,
}
impl From<InvalidReason> for SubmitInvalidReason {
fn from(reason: InvalidReason) -> Self {
match reason {
InvalidReason::NoProof => SubmitInvalidReason::NoProof,
InvalidReason::BadProof => SubmitInvalidReason::BadProof,
InvalidReason::EncodingTooLarge { submitted_size, max_size } => {
SubmitInvalidReason::EncodingTooLarge { submitted_size, max_size }
},
InvalidReason::AlreadyExpired => SubmitInvalidReason::AlreadyExpired,
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "status", rename_all = "camelCase"))]
pub enum SubmitOutcome {
New,
Known,
Rejected(SubmitRejectionReason),
Invalid(SubmitInvalidReason),
}
impl SubmitOutcome {
pub fn from_submit_result(result: SubmitResult) -> std::result::Result<Self, Error> {
match result {
SubmitResult::New => Ok(SubmitOutcome::New),
SubmitResult::Known => Ok(SubmitOutcome::Known),
SubmitResult::KnownExpired => {
Err(Error::Storage("unexpected KnownExpired on local submission".into()))
},
SubmitResult::Rejected(reason) => Ok(SubmitOutcome::Rejected(reason.into())),
SubmitResult::Invalid(reason) => Ok(SubmitOutcome::Invalid(reason.into())),
SubmitResult::InternalError(error) => Err(error),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(tag = "event", content = "data", rename_all = "camelCase"))]
pub enum StatementEvent {
NewStatements {
statements: Vec<Bytes>,
#[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Option::is_none"))]
remaining: Option<u32>,
},
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FilterDecision {
Skip,
Take,
Abort,
}
#[derive(Debug)]
pub struct AdmittedBatch {
pub statements: Vec<(Hash, Statement)>,
pub cursor: u64,
pub done: bool,
}
pub trait StatementStore: Send + Sync {
fn statements(&self) -> Result<Vec<(Hash, Statement)>>;
fn take_recent_statements(&self) -> Result<Vec<(u64, Hash, Statement)>>;
fn statement(&self, hash: &Hash) -> Result<Option<Statement>>;
fn has_statement(&self, hash: &Hash) -> bool;
fn statements_by_hashes(
&self,
hashes: &[Hash],
filter: &mut dyn FnMut(&Hash, &[u8], &Statement) -> FilterDecision,
) -> Result<(Vec<(Hash, Statement)>, usize)>;
fn admission_watermark(&self) -> Result<u64>;
fn admitted_statements(
&self,
cursor: u64,
watermark: u64,
scan_limit: usize,
filter: &mut dyn FnMut(&Hash, &[u8], &Statement) -> FilterDecision,
) -> Result<AdmittedBatch>;
fn broadcasts(&self, match_all_topics: &[Topic]) -> Result<Vec<Vec<u8>>>;
fn posted(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
fn posted_clear(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
fn broadcasts_stmt(&self, match_all_topics: &[Topic]) -> Result<Vec<Vec<u8>>>;
fn posted_stmt(&self, match_all_topics: &[Topic], dest: [u8; 32]) -> Result<Vec<Vec<u8>>>;
fn posted_clear_stmt(&self, match_all_topics: &[Topic], dest: [u8; 32])
-> Result<Vec<Vec<u8>>>;
fn submit(&self, statement: Statement, source: StatementSource) -> SubmitResult;
fn remove(&self, hash: &Hash) -> Result<()>;
fn remove_by(&self, who: [u8; 32]) -> Result<()>;
}