cumulus_primitives_spec_messaging/
inherent.rs1use alloc::vec::Vec;
26use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
27use polkadot_core_primitives::Hash;
28use polkadot_parachain_primitives::primitives::Id as ParaId;
29use scale_info::TypeInfo;
30use sp_core::ConstU32;
31use sp_runtime::BoundedVec;
32
33use crate::{
34 mmr::{MessagePosition, MAX_MMR_HASHES},
35 stream::StreamId,
36};
37
38pub const INHERENT_IDENTIFIER: sp_inherents::InherentIdentifier = *b"specmsg0";
40
41pub type Payload = Vec<u8>;
42
43#[derive(
47 Clone,
48 Copy,
49 Encode,
50 Decode,
51 DecodeWithMemTracking,
52 MaxEncodedLen,
53 PartialEq,
54 Eq,
55 Debug,
56 TypeInfo,
57)]
58pub enum ConsumedStream {
59 #[codec(index = 0)]
62 Channel { domain: u8, num: u16, from: MessagePosition },
63 #[codec(index = 1)]
65 Broadcast { domain: u16, subdomain: u8, num: u32, from: MessagePosition },
66}
67
68impl ConsumedStream {
69 pub fn project(stream: &StreamId, from: MessagePosition) -> Option<Self> {
72 match *stream {
73 StreamId::Channel { domain, num, .. } => Some(Self::Channel { domain, num, from }),
74 StreamId::Broadcast { domain, subdomain, num } => {
75 Some(Self::Broadcast { domain, subdomain, num, from })
76 },
77 StreamId::Ack { .. } | StreamId::Private { .. } => None,
78 }
79 }
80
81 pub fn stream_id(&self, recipient: ParaId) -> StreamId {
83 match *self {
84 Self::Channel { domain, num, .. } => StreamId::Channel { recipient, domain, num },
85 Self::Broadcast { domain, subdomain, num, .. } => {
86 StreamId::Broadcast { domain, subdomain, num }
87 },
88 }
89 }
90
91 pub fn from(&self) -> MessagePosition {
92 match *self {
93 Self::Channel { from, .. } | Self::Broadcast { from, .. } => from,
94 }
95 }
96}
97
98#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo, Default)]
101pub struct MessagingInherentData {
102 pub items: Vec<(ParaId, StreamId, ConsumeItem)>,
104}
105
106impl MessagingInherentData {
107 pub fn is_empty(&self) -> bool {
109 self.items.is_empty()
110 }
111}
112
113#[cfg(feature = "std")]
116#[async_trait::async_trait]
117impl sp_inherents::InherentDataProvider for MessagingInherentData {
118 async fn provide_inherent_data(
119 &self,
120 inherent_data: &mut sp_inherents::InherentData,
121 ) -> Result<(), sp_inherents::Error> {
122 if self.is_empty() {
123 return Ok(());
124 }
125 inherent_data.put_data(INHERENT_IDENTIFIER, self)
126 }
127
128 async fn try_handle_error(
129 &self,
130 _: &sp_inherents::InherentIdentifier,
131 _: &[u8],
132 ) -> Option<Result<(), sp_inherents::Error>> {
133 None
135 }
136}
137
138#[derive(Clone, Encode, Decode, DecodeWithMemTracking, PartialEq, Eq, Debug, TypeInfo)]
142pub enum ConsumeItem {
143 #[codec(index = 0)]
147 Channel { payloads: Vec<Payload> },
148 #[codec(index = 1)]
158 Events {
159 base: MessagePosition,
160 start_peaks: BoundedVec<Hash, ConstU32<MAX_MMR_HASHES>>,
161 payloads: Vec<Payload>,
162 },
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168
169 #[test]
171 fn variant_indices_are_frozen() {
172 let ch = ConsumedStream::Channel { domain: 0, num: 0, from: MessagePosition(0) };
173 let bc =
174 ConsumedStream::Broadcast { domain: 0, subdomain: 0, num: 0, from: MessagePosition(0) };
175 assert_eq!(ch.encode()[0], 0);
176 assert_eq!(bc.encode()[0], 1);
177
178 let item_ch = ConsumeItem::Channel { payloads: Vec::new() };
179 let item_ev = ConsumeItem::Events {
180 base: MessagePosition(0),
181 start_peaks: BoundedVec::new(),
182 payloads: Vec::new(),
183 };
184 assert_eq!(item_ch.encode()[0], 0);
185 assert_eq!(item_ev.encode()[0], 1);
186 }
187
188 #[test]
189 fn consumed_stream_projects_and_names_its_stream() {
190 let us = ParaId::from(2000);
191 let from = MessagePosition(7);
192 let channel = StreamId::Channel { recipient: us, domain: 1, num: 2 };
193 let broadcast = StreamId::Broadcast { domain: 3, subdomain: 4, num: 5 };
194 for stream in [channel, broadcast] {
195 let view = ConsumedStream::project(&stream, from).expect("fetchable kinds project");
196 assert_eq!(view.stream_id(us), stream, "round-trips through the view");
197 assert_eq!(view.from(), from);
198 }
199 let ack = StreamId::Ack { recipient: us, domain: 0, num: 0 };
201 assert_eq!(ConsumedStream::project(&ack, from), None);
202 let private =
203 StreamId::Private { kind: crate::PrivateKind::new(0x80).unwrap(), body: [0; 7] };
204 assert_eq!(ConsumedStream::project(&private, from), None);
205 }
206
207 #[test]
208 fn empty_data_places_no_inherent_and_non_empty_round_trips() {
209 use sp_inherents::InherentDataProvider;
210 let mut inherent_data = sp_inherents::InherentData::new();
211 futures::executor::block_on(
212 MessagingInherentData::default().provide_inherent_data(&mut inherent_data),
213 )
214 .expect("providing empty data succeeds");
215 assert_eq!(
216 inherent_data.get_data::<MessagingInherentData>(&INHERENT_IDENTIFIER).unwrap(),
217 None,
218 "absent = consumed nothing"
219 );
220
221 let data = MessagingInherentData {
222 items: alloc::vec![(
223 ParaId::from(1000),
224 StreamId::Channel { recipient: ParaId::from(2000), domain: 0, num: 1 },
225 ConsumeItem::Channel { payloads: alloc::vec![alloc::vec![1, 2, 3]] },
226 )],
227 };
228 let mut inherent_data = sp_inherents::InherentData::new();
229 futures::executor::block_on(data.provide_inherent_data(&mut inherent_data))
230 .expect("providing data succeeds");
231 assert_eq!(
232 inherent_data.get_data::<MessagingInherentData>(&INHERENT_IDENTIFIER).unwrap(),
233 Some(data)
234 );
235 }
236
237 #[test]
238 fn inherent_round_trips() {
239 let data = MessagingInherentData {
240 items: alloc::vec![(
241 ParaId::from(1000),
242 StreamId::Channel { recipient: ParaId::from(2000), domain: 0, num: 1 },
243 ConsumeItem::Channel { payloads: alloc::vec![alloc::vec![1, 2, 3]] },
244 )],
245 };
246 assert_eq!(MessagingInherentData::decode(&mut &data.encode()[..]).unwrap(), data);
247 }
248}