1#[cfg(feature = "std")]
2use crate::utils;
3use crate::{msa::MessageSourceId, node::BlockNumber};
4use parity_scale_codec::{Decode, Encode};
5use scale_info::TypeInfo;
6#[cfg(feature = "std")]
7use serde::{Deserialize, Serialize};
8use sp_runtime::traits::One;
9extern crate alloc;
10use alloc::{vec, vec::Vec};
11#[cfg(feature = "std")]
12use utils::*;
13
14#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
18#[derive(Default, Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
19pub struct MessageResponse {
20 pub provider_msa_id: MessageSourceId,
25 pub index: u16,
27 pub block_number: BlockNumber,
29 #[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none", default))]
31 pub msa_id: Option<MessageSourceId>,
32 #[cfg_attr(
34 feature = "std",
35 serde(with = "as_hex_option", skip_serializing_if = "Option::is_none", default)
36 )]
37 pub payload: Option<Vec<u8>>,
38 #[cfg_attr(
40 feature = "std",
41 serde(with = "as_string_option", skip_serializing_if = "Option::is_none", default)
42 )]
43 pub cid: Option<Vec<u8>>,
44 #[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none", default))]
46 pub payload_length: Option<u32>,
47}
48#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
50#[derive(Default, Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
51pub struct BlockPaginationRequest {
52 pub from_block: BlockNumber,
54 pub from_index: u32,
56 pub to_block: BlockNumber,
58 pub page_size: u32,
60}
61
62impl BlockPaginationRequest {
63 pub const MAX_PAGE_SIZE: u32 = 10000;
65 pub const MAX_BLOCK_RANGE: u32 = 50000; pub fn validate(&self) -> bool {
72 self.page_size > 0 &&
73 self.page_size <= Self::MAX_PAGE_SIZE &&
74 self.from_block < self.to_block &&
75 self.to_block - self.from_block <= Self::MAX_BLOCK_RANGE
76 }
77}
78
79#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
81#[derive(Default, Clone, Encode, Decode, PartialEq, Debug, TypeInfo, Eq)]
82pub struct BlockPaginationResponse<T> {
83 pub content: Vec<T>,
85 pub has_next: bool,
87 #[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none"))]
88 pub next_block: Option<BlockNumber>,
90 #[cfg_attr(feature = "std", serde(skip_serializing_if = "Option::is_none"))]
91 pub next_index: Option<u32>,
93}
94
95impl<T> BlockPaginationResponse<T> {
96 pub const fn new() -> BlockPaginationResponse<T> {
98 BlockPaginationResponse {
99 content: vec![],
100 has_next: false,
101 next_block: None,
102 next_index: None,
103 }
104 }
105
106 pub fn check_end_condition_and_set_next_pagination(
109 &mut self,
110 block_number: BlockNumber,
111 current_index: u32,
112 list_size: u32,
113 request: &BlockPaginationRequest,
114 ) -> bool {
115 if self.content.len() as u32 == request.page_size {
116 let mut next_block = block_number;
117 let mut next_index = current_index + 1;
118
119 if next_index == list_size {
121 next_block = block_number + BlockNumber::one();
122 next_index = 0;
123 }
124
125 if next_block < request.to_block {
126 self.has_next = true;
127 self.next_block = Some(next_block);
128 self.next_index = Some(next_index);
129 }
130 return true
131 }
132
133 false
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use crate::{
140 messages::{BlockPaginationRequest, BlockPaginationResponse, MessageResponse},
141 node::BlockNumber,
142 };
143
144 struct TestCase<T> {
145 input: BlockPaginationRequest,
146 expected: T,
147 message: String,
148 }
149
150 #[test]
151 fn as_hex_option_msg_ipfs_serialize_deserialize_test() {
152 let msg = MessageResponse {
154 payload: None,
155 msa_id: None,
156 provider_msa_id: 1,
157 index: 1,
158 block_number: 1,
159 cid: Some(
160 "bafkreidgvpkjawlxz6sffxzwgooowe5yt7i6wsyg236mfoks77nywkptdq"
161 .as_bytes()
162 .to_vec(),
163 ),
164 payload_length: Some(42),
165 };
166 let serialized = serde_json::to_string(&msg).unwrap();
167 assert_eq!(serialized, "{\"provider_msa_id\":1,\"index\":1,\"block_number\":1,\"cid\":\"bafkreidgvpkjawlxz6sffxzwgooowe5yt7i6wsyg236mfoks77nywkptdq\",\"payload_length\":42}");
168
169 let deserialized: MessageResponse = serde_json::from_str(&serialized).unwrap();
170 assert_eq!(deserialized, msg);
171 }
172
173 #[test]
174 fn as_hex_option_empty_payload_deserialize_as_default_value() {
175 let expected_msg = MessageResponse {
176 payload: None,
177 msa_id: Some(1),
178 provider_msa_id: 1,
179 index: 1,
180 block_number: 1,
181 cid: None,
182 payload_length: None,
183 };
184
185 let serialized_msg_without_payload =
187 "{\"provider_msa_id\":1,\"index\":1,\"block_number\":1,\"msa_id\":1}";
188
189 let deserialized_result: MessageResponse =
190 serde_json::from_str(serialized_msg_without_payload).unwrap();
191 assert_eq!(deserialized_result, expected_msg);
192 }
193
194 #[test]
195 fn block_pagination_request_validation_test() {
196 let test_cases: Vec<TestCase<bool>> = vec![
197 TestCase {
198 input: BlockPaginationRequest { from_block: 10, from_index: 0, to_block: 12, page_size: 1 },
199 expected: true,
200 message: "Should be valid".to_string(),
201 },
202 TestCase {
203 input: BlockPaginationRequest { from_block: 10, from_index: 0, to_block: 12, page_size: 0 },
204 expected: false,
205 message: "Page with size 0 is invalid".to_string(),
206 },
207 TestCase {
208 input: BlockPaginationRequest { from_block: 10, from_index: 0, to_block: 8, page_size: 1 },
209 expected: false,
210 message: "from_block should be less than to_block".to_string(),
211 },
212 TestCase {
213 input: BlockPaginationRequest { from_block: 10, from_index: 0, to_block: 8, page_size: 10000 + 1 },
214 expected: false,
215 message: "page_size should be less than MAX_PAGE_SIZE".to_string(),
216 },
217 TestCase {
218 input: BlockPaginationRequest { from_block: 1, from_index: 0, to_block: 50000 + 2, page_size: 1 },
219 expected: false,
220 message: "the difference between from_block and to_block should be less than MAX_BLOCK_RANGE".to_string(),
221 },
222 ];
223
224 for tc in test_cases {
225 assert_eq!(tc.expected, tc.input.validate(), "{}", tc.message);
226 }
227 }
228
229 #[test]
230 fn check_end_condition_does_not_mutate_when_at_the_end() {
231 let mut resp = BlockPaginationResponse::<u32> {
232 content: vec![1, 2, 3],
233 has_next: false,
234 next_block: None,
235 next_index: None,
236 };
237
238 let total_data_length: u32 = resp.content.len() as u32;
239
240 let request = BlockPaginationRequest {
241 from_block: 1 as BlockNumber,
242 from_index: 0,
243 to_block: 5,
244 page_size: total_data_length + 10,
246 };
247 let current_block = 5;
249 let current_index = total_data_length - 1;
251 let list_size = current_index;
253 let is_full = resp.check_end_condition_and_set_next_pagination(
254 current_block,
255 current_index,
256 list_size,
257 &request,
258 );
259 assert!(!is_full);
261 assert!(!resp.has_next);
263 assert_eq!(None, resp.next_block);
265 assert_eq!(None, resp.next_index);
266 }
267
268 #[test]
269 fn check_end_condition_mutates_when_more_in_list_than_page() {
270 let mut resp = BlockPaginationResponse::<u32> {
271 content: vec![1, 2, 3],
272 has_next: false,
273 next_block: None,
274 next_index: None,
275 };
276
277 let total_data_length: u32 = resp.content.len() as u32;
278
279 let request = BlockPaginationRequest {
280 from_block: 1 as BlockNumber,
281 from_index: 0,
282 to_block: 5,
283 page_size: total_data_length,
284 };
285 let current_block = 1;
287 let current_index = total_data_length - 1;
289 let list_size = total_data_length + 1;
291 let is_full = resp.check_end_condition_and_set_next_pagination(
292 current_block,
293 current_index,
294 list_size,
295 &request,
296 );
297 assert!(is_full);
298 assert!(resp.has_next);
299 assert_eq!(Some(1), resp.next_block);
301 assert_eq!(Some(current_index + 1), resp.next_index);
303 }
304
305 #[test]
306 fn check_end_condition_mutates_when_more_than_page_but_none_left_in_block() {
307 let mut resp = BlockPaginationResponse::<u32> {
308 content: vec![1, 2, 3],
309 has_next: false,
310 next_block: None,
311 next_index: None,
312 };
313
314 let total_data_length: u32 = resp.content.len() as u32;
315
316 let request = BlockPaginationRequest {
317 from_block: 1 as BlockNumber,
318 from_index: 0,
319 to_block: 5,
320 page_size: total_data_length,
321 };
322 let current_block = 1;
324 let current_index = total_data_length - 1;
326 let list_size = total_data_length;
328 let is_full = resp.check_end_condition_and_set_next_pagination(
329 current_block,
330 current_index,
331 list_size,
332 &request,
333 );
334 assert!(is_full);
335 assert!(resp.has_next);
336 assert_eq!(Some(current_block + 1), resp.next_block);
338 assert_eq!(Some(0), resp.next_index);
340 }
341}