Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 34f45c6

Browse files
gitofdeepanshuggwpezjuangirini
authored
Add try_state check to Pallet MessageQueue (#13502)
* feat: Add try_state for message_queue * Update frame/message-queue/src/lib.rs change if let to while let and modify bump_service_head function Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * feat: update try_state, add checks for storage * fix: add try_state builder for remaining tests * Update frame/message-queue/src/mock.rs Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * chore: assert statement to ensure * Fix tests Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Use ensure instead of assert Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Fix function signature and feature gate Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> * Cleanup code Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> --------- Signed-off-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Oliver Tale-Yazdi <oliver.tale-yazdi@parity.io> Co-authored-by: Juan <juangirini@gmail.com>
1 parent 837e481 commit 34f45c6

File tree

5 files changed

+180
-85
lines changed

5 files changed

+180
-85
lines changed

frame/message-queue/src/integration_test.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@
2222

2323
use crate::{
2424
mock::{
25-
new_test_ext, CountingMessageProcessor, IntoWeight, MockedWeightInfo, NumMessagesProcessed,
26-
YieldingQueues,
25+
build_and_execute, CountingMessageProcessor, IntoWeight, MockedWeightInfo,
26+
NumMessagesProcessed, YieldingQueues,
2727
},
2828
mock_helpers::MessageOrigin,
2929
*,
@@ -123,7 +123,7 @@ fn stress_test_enqueue_and_service() {
123123
let max_msg_len = MaxMessageLenOf::<Test>::get();
124124
let mut rng = StdRng::seed_from_u64(42);
125125

126-
new_test_ext::<Test>().execute_with(|| {
126+
build_and_execute::<Test>(|| {
127127
let mut msgs_remaining = 0;
128128
for _ in 0..blocks {
129129
// Start by enqueuing a large number of messages.
@@ -171,7 +171,7 @@ fn stress_test_queue_suspension() {
171171
let max_msg_len = MaxMessageLenOf::<Test>::get();
172172
let mut rng = StdRng::seed_from_u64(41);
173173

174-
new_test_ext::<Test>().execute_with(|| {
174+
build_and_execute::<Test>(|| {
175175
let mut suspended = BTreeSet::<u32>::new();
176176
let mut msgs_remaining = 0;
177177

frame/message-queue/src/lib.rs

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,12 @@ pub mod pallet {
578578
}
579579
}
580580

581-
/// Check all assumptions about [`crate::Config`].
581+
#[cfg(feature = "try-runtime")]
582+
fn try_state(_: BlockNumberFor<T>) -> Result<(), sp_runtime::TryRuntimeError> {
583+
Self::do_try_state()
584+
}
585+
586+
/// Check all compile-time assumptions about [`crate::Config`].
582587
fn integrity_test() {
583588
assert!(!MaxMessageLenOf::<T>::get().is_zero(), "HeapSize too low");
584589
}
@@ -1105,6 +1110,106 @@ impl<T: Config> Pallet<T> {
11051110
ItemExecutionStatus::Executed(is_processed)
11061111
}
11071112

1113+
/// Ensure the correctness of state of this pallet.
1114+
///
1115+
/// # Assumptions-
1116+
///
1117+
/// If `serviceHead` points to a ready Queue, then BookState of that Queue has:
1118+
///
1119+
/// * `message_count` > 0
1120+
/// * `size` > 0
1121+
/// * `end` > `begin`
1122+
/// * Some(ready_neighbours)
1123+
/// * If `ready_neighbours.next` == self.origin, then `ready_neighbours.prev` == self.origin
1124+
/// (only queue in ring)
1125+
///
1126+
/// For Pages(begin to end-1) in BookState:
1127+
///
1128+
/// * `remaining` > 0
1129+
/// * `remaining_size` > 0
1130+
/// * `first` <= `last`
1131+
/// * Every page can be decoded into peek_* functions
1132+
#[cfg(any(test, feature = "try-runtime"))]
1133+
pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
1134+
// Checking memory corruption for BookStateFor
1135+
ensure!(
1136+
BookStateFor::<T>::iter_keys().count() == BookStateFor::<T>::iter_values().count(),
1137+
"Memory Corruption in BookStateFor"
1138+
);
1139+
// Checking memory corruption for Pages
1140+
ensure!(
1141+
Pages::<T>::iter_keys().count() == Pages::<T>::iter_values().count(),
1142+
"Memory Corruption in Pages"
1143+
);
1144+
1145+
// No state to check
1146+
if ServiceHead::<T>::get().is_none() {
1147+
return Ok(())
1148+
}
1149+
1150+
//loop around this origin
1151+
let starting_origin = ServiceHead::<T>::get().unwrap();
1152+
1153+
while let Some(head) = Self::bump_service_head(&mut WeightMeter::max_limit()) {
1154+
ensure!(
1155+
BookStateFor::<T>::contains_key(&head),
1156+
"Service head must point to an existing book"
1157+
);
1158+
1159+
let head_book_state = BookStateFor::<T>::get(&head);
1160+
ensure!(
1161+
head_book_state.message_count > 0,
1162+
"There must be some messages if in ReadyRing"
1163+
);
1164+
ensure!(head_book_state.size > 0, "There must be some message size if in ReadyRing");
1165+
ensure!(
1166+
head_book_state.end > head_book_state.begin,
1167+
"End > Begin if unprocessed messages exists"
1168+
);
1169+
ensure!(
1170+
head_book_state.ready_neighbours.is_some(),
1171+
"There must be neighbours if in ReadyRing"
1172+
);
1173+
1174+
if head_book_state.ready_neighbours.as_ref().unwrap().next == head {
1175+
ensure!(
1176+
head_book_state.ready_neighbours.as_ref().unwrap().prev == head,
1177+
"Can only happen if only queue in ReadyRing"
1178+
);
1179+
}
1180+
1181+
for page_index in head_book_state.begin..head_book_state.end {
1182+
let page = Pages::<T>::get(&head, page_index).unwrap();
1183+
let remaining_messages = page.remaining;
1184+
let mut counted_remaining_messages = 0;
1185+
ensure!(
1186+
remaining_messages > 0.into(),
1187+
"These must be some messages that have not been processed yet!"
1188+
);
1189+
1190+
for i in 0..u32::MAX {
1191+
if let Some((_, processed, _)) = page.peek_index(i as usize) {
1192+
if !processed {
1193+
counted_remaining_messages += 1;
1194+
}
1195+
} else {
1196+
break
1197+
}
1198+
}
1199+
1200+
ensure!(
1201+
remaining_messages == counted_remaining_messages.into(),
1202+
"Memory Corruption"
1203+
);
1204+
}
1205+
1206+
if head_book_state.ready_neighbours.as_ref().unwrap().next == starting_origin {
1207+
break
1208+
}
1209+
}
1210+
Ok(())
1211+
}
1212+
11081213
/// Print the pages in each queue and the messages in each page.
11091214
///
11101215
/// Processed messages are prefixed with a `*` and the current `begin`ning page with a `>`.

frame/message-queue/src/mock.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,15 @@ where
295295
ext
296296
}
297297

298-
/// Run this closure in test externalities.
299-
pub fn test_closure<R>(f: impl FnOnce() -> R) -> R {
300-
let mut ext = new_test_ext::<Test>();
301-
ext.execute_with(f)
298+
/// Run the function pointer inside externalities and asserts the try_state hook at the end.
299+
pub fn build_and_execute<T: Config>(test: impl FnOnce() -> ())
300+
where
301+
BlockNumberFor<T>: From<u32>,
302+
{
303+
new_test_ext::<T>().execute_with(|| {
304+
test();
305+
MessageQueue::do_try_state().expect("All invariants must hold after a test");
306+
});
302307
}
303308

304309
/// Set the weight of a specific weight function.

frame/message-queue/src/mock_helpers.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ pub fn page<T: Config>(msg: &[u8]) -> PageOf<T> {
8989
}
9090

9191
pub fn single_page_book<T: Config>() -> BookStateOf<T> {
92-
BookState { begin: 0, end: 1, count: 1, ..Default::default() }
92+
BookState { begin: 0, end: 1, count: 1, message_count: 1, size: 1, ..Default::default() }
9393
}
9494

9595
pub fn empty_book<T: Config>() -> BookStateOf<T> {
@@ -139,10 +139,8 @@ pub fn setup_bump_service_head<T: Config>(
139139
current: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
140140
next: <<T as Config>::MessageProcessor as ProcessMessage>::Origin,
141141
) {
142-
let mut book = single_page_book::<T>();
143-
book.ready_neighbours = Some(Neighbours::<MessageOriginOf<T>> { prev: next.clone(), next });
144-
ServiceHead::<T>::put(&current);
145-
BookStateFor::<T>::insert(&current, &book);
142+
crate::Pallet::<T>::enqueue_message(msg("1"), current);
143+
crate::Pallet::<T>::enqueue_message(msg("1"), next);
146144
}
147145

148146
/// Knit a queue into the ready-ring and write it back to storage.
@@ -164,11 +162,8 @@ pub fn unknit<T: Config>(o: &<<T as Config>::MessageProcessor as ProcessMessage>
164162
pub fn build_ring<T: Config>(
165163
queues: &[<<T as Config>::MessageProcessor as ProcessMessage>::Origin],
166164
) {
167-
for queue in queues {
168-
BookStateFor::<T>::insert(queue, empty_book::<T>());
169-
}
170-
for queue in queues {
171-
knit::<T>(queue);
165+
for queue in queues.iter() {
166+
crate::Pallet::<T>::enqueue_message(msg("1"), queue.clone());
172167
}
173168
assert_ring::<T>(queues);
174169
}

0 commit comments

Comments
 (0)