Newer
Older
}
(total_processed > 0, next_ready)
}
/// Service as many messages of a page as possible.
///
/// Returns how many messages were processed and the page's status.
fn service_page(
origin: &MessageOriginOf<T>,
book_state: &mut BookStateOf<T>,
weight: &mut WeightMeter,
overweight_limit: Weight,
) -> (u32, PageExecutionStatus) {
use PageExecutionStatus::*;
if weight
.try_consume(
T::WeightInfo::service_page_base_completion()
.max(T::WeightInfo::service_page_base_no_completion()),
)
.is_err()
{
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
return (0, Bailed)
}
let page_index = book_state.begin;
let mut page = match Pages::<T>::get(origin, page_index) {
Some(p) => p,
None => {
defensive!("message-queue: referenced page not found");
return (0, NoMore)
},
};
let mut total_processed = 0;
// Execute as many messages as possible.
let status = loop {
use ItemExecutionStatus::*;
match Self::service_page_item(
origin,
page_index,
book_state,
&mut page,
weight,
overweight_limit,
) {
Bailed => break PageExecutionStatus::Bailed,
NoItem => break PageExecutionStatus::NoMore,
NoProgress => break PageExecutionStatus::NoProgress,
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
// Keep going as long as we make progress...
Executed(true) => total_processed.saturating_inc(),
Executed(false) => (),
}
};
if page.is_complete() {
debug_assert!(status != Bailed, "we never bail if a page became complete");
Pages::<T>::remove(origin, page_index);
debug_assert!(book_state.count > 0, "completing a page implies there are pages");
book_state.count.saturating_dec();
} else {
Pages::<T>::insert(origin, page_index, page);
}
(total_processed, status)
}
/// Execute the next message of a page.
pub(crate) fn service_page_item(
origin: &MessageOriginOf<T>,
page_index: PageIndex,
book_state: &mut BookStateOf<T>,
page: &mut PageOf<T>,
weight: &mut WeightMeter,
overweight_limit: Weight,
) -> ItemExecutionStatus {
// This ugly pre-checking is needed for the invariant
// "we never bail if a page became complete".
if page.is_complete() {
return ItemExecutionStatus::NoItem
}
if weight.try_consume(T::WeightInfo::service_page_item()).is_err() {
return ItemExecutionStatus::Bailed
}
let payload = &match page.peek_first() {
Some(m) => m,
None => return ItemExecutionStatus::NoItem,
}[..];
use MessageExecutionStatus::*;
let is_processed = match Self::process_message_payload(
origin.clone(),
page_index,
page.first_index,
payload.deref(),
weight,
overweight_limit,
) {
InsufficientWeight => return ItemExecutionStatus::Bailed,
Unprocessable { permanent: false } => return ItemExecutionStatus::NoProgress,
Processed | Unprocessable { permanent: true } => true,
Overweight => false,
};
if is_processed {
book_state.message_count.saturating_dec();
book_state.size.saturating_reduce(payload.len() as u64);
}
page.skip_first(is_processed);
ItemExecutionStatus::Executed(is_processed)
}
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
/// Ensure the correctness of state of this pallet.
///
/// # Assumptions-
///
/// If `serviceHead` points to a ready Queue, then BookState of that Queue has:
///
/// * `message_count` > 0
/// * `size` > 0
/// * `end` > `begin`
/// * Some(ready_neighbours)
/// * If `ready_neighbours.next` == self.origin, then `ready_neighbours.prev` == self.origin
/// (only queue in ring)
///
/// For Pages(begin to end-1) in BookState:
///
/// * `remaining` > 0
/// * `remaining_size` > 0
/// * `first` <= `last`
/// * Every page can be decoded into peek_* functions
#[cfg(any(test, feature = "try-runtime"))]
pub fn do_try_state() -> Result<(), sp_runtime::TryRuntimeError> {
// Checking memory corruption for BookStateFor
ensure!(
BookStateFor::<T>::iter_keys().count() == BookStateFor::<T>::iter_values().count(),
"Memory Corruption in BookStateFor"
);
// Checking memory corruption for Pages
ensure!(
Pages::<T>::iter_keys().count() == Pages::<T>::iter_values().count(),
"Memory Corruption in Pages"
);
// No state to check
if ServiceHead::<T>::get().is_none() {
return Ok(())
}
//loop around this origin
let starting_origin = ServiceHead::<T>::get().unwrap();
while let Some(head) = Self::bump_service_head(&mut WeightMeter::max_limit()) {
ensure!(
BookStateFor::<T>::contains_key(&head),
"Service head must point to an existing book"
);
let head_book_state = BookStateFor::<T>::get(&head);
ensure!(
head_book_state.message_count > 0,
"There must be some messages if in ReadyRing"
);
ensure!(head_book_state.size > 0, "There must be some message size if in ReadyRing");
ensure!(
head_book_state.end > head_book_state.begin,
"End > Begin if unprocessed messages exists"
);
ensure!(
head_book_state.ready_neighbours.is_some(),
"There must be neighbours if in ReadyRing"
);
if head_book_state.ready_neighbours.as_ref().unwrap().next == head {
ensure!(
head_book_state.ready_neighbours.as_ref().unwrap().prev == head,
"Can only happen if only queue in ReadyRing"
);
}
for page_index in head_book_state.begin..head_book_state.end {
let page = Pages::<T>::get(&head, page_index).unwrap();
let remaining_messages = page.remaining;
let mut counted_remaining_messages = 0;
ensure!(
remaining_messages > 0.into(),
"These must be some messages that have not been processed yet!"
);
for i in 0..u32::MAX {
if let Some((_, processed, _)) = page.peek_index(i as usize) {
if !processed {
counted_remaining_messages += 1;
}
} else {
break
}
}
ensure!(
remaining_messages == counted_remaining_messages.into(),
"Memory Corruption"
);
}
if head_book_state.ready_neighbours.as_ref().unwrap().next == starting_origin {
break
}
}
Ok(())
}
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
/// Print the pages in each queue and the messages in each page.
///
/// Processed messages are prefixed with a `*` and the current `begin`ning page with a `>`.
///
/// # Example output
///
/// ```text
/// queue Here:
/// page 0: []
/// > page 1: []
/// page 2: ["\0weight=4", "\0c", ]
/// page 3: ["\0bigbig 1", ]
/// page 4: ["\0bigbig 2", ]
/// page 5: ["\0bigbig 3", ]
/// ```
#[cfg(feature = "std")]
pub fn debug_info() -> String {
let mut info = String::new();
for (origin, book_state) in BookStateFor::<T>::iter() {
let mut queue = format!("queue {:?}:\n", &origin);
let mut pages = Pages::<T>::iter_prefix(&origin).collect::<Vec<_>>();
pages.sort_by(|(a, _), (b, _)| a.cmp(b));
for (page_index, mut page) in pages.into_iter() {
let page_info = if book_state.begin == page_index { ">" } else { " " };
let mut page_info = format!(
"{} page {} ({:?} first, {:?} last, {:?} remain): [ ",
page_info, page_index, page.first, page.last, page.remaining
);
for i in 0..u32::MAX {
if let Some((_, processed, message)) =
page.peek_index(i.try_into().expect("std-only code"))
{
let msg = String::from_utf8_lossy(message.deref());
if processed {
page_info.push('*');
}
page_info.push_str(&format!("{:?}, ", msg));
page.skip_first(true);
} else {
break
}
}
page_info.push_str("]\n");
queue.push_str(&page_info);
}
info.push_str(&queue);
}
info
}
/// Process a single message.
///
/// The base weight of this function needs to be accounted for by the caller. `weight` is the
/// remaining weight to process the message. `overweight_limit` is the maximum weight that a
/// message can ever consume. Messages above this limit are marked as permanently overweight.
fn process_message_payload(
origin: MessageOriginOf<T>,
page_index: PageIndex,
message_index: T::Size,
message: &[u8],
meter: &mut WeightMeter,
overweight_limit: Weight,
) -> MessageExecutionStatus {
let hash = sp_io::hashing::blake2_256(message);
use ProcessMessageError::*;
let prev_consumed = meter.consumed();
let mut id = hash;
match T::MessageProcessor::process_message(message, origin.clone(), meter, &mut id) {
Err(Overweight(w)) if w.any_gt(overweight_limit) => {
// Permanently overweight.
Self::deposit_event(Event::<T>::OverweightEnqueued {
origin,
page_index,
message_index,
});
MessageExecutionStatus::Overweight
},
Err(Overweight(_)) => {
// Temporarily overweight - save progress and stop processing this
// queue.
MessageExecutionStatus::InsufficientWeight
},
Err(Yield) => {
// Processing should be reattempted later.
MessageExecutionStatus::Unprocessable { permanent: false }
},
Err(error @ BadFormat | error @ Corrupt | error @ Unsupported) => {
Self::deposit_event(Event::<T>::ProcessingFailed { id, origin, error });
MessageExecutionStatus::Unprocessable { permanent: true }
Ok(success) => {
let weight_used = meter.consumed().saturating_sub(prev_consumed);
Self::deposit_event(Event::<T>::Processed { id, origin, weight_used, success });
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
MessageExecutionStatus::Processed
},
}
}
}
/// Provides a [`sp_core::Get`] to access the `MEL` of a [`codec::MaxEncodedLen`] type.
pub struct MaxEncodedLenOf<T>(sp_std::marker::PhantomData<T>);
impl<T: MaxEncodedLen> Get<u32> for MaxEncodedLenOf<T> {
fn get() -> u32 {
T::max_encoded_len() as u32
}
}
/// Calculates the maximum message length and exposed it through the [`codec::MaxEncodedLen`] trait.
pub struct MaxMessageLen<Origin, Size, HeapSize>(
sp_std::marker::PhantomData<(Origin, Size, HeapSize)>,
);
impl<Origin: MaxEncodedLen, Size: MaxEncodedLen + Into<u32>, HeapSize: Get<Size>> Get<u32>
for MaxMessageLen<Origin, Size, HeapSize>
{
fn get() -> u32 {
(HeapSize::get().into()).saturating_sub(ItemHeader::<Size>::max_encoded_len() as u32)
}
}
/// The maximal message length.
pub type MaxMessageLenOf<T> =
MaxMessageLen<MessageOriginOf<T>, <T as Config>::Size, <T as Config>::HeapSize>;
/// The maximal encoded origin length.
pub type MaxOriginLenOf<T> = MaxEncodedLenOf<MessageOriginOf<T>>;
/// The `MessageOrigin` of this pallet.
pub type MessageOriginOf<T> = <<T as Config>::MessageProcessor as ProcessMessage>::Origin;
/// The maximal heap size of a page.
pub type HeapSizeU32Of<T> = IntoU32<<T as Config>::HeapSize, <T as Config>::Size>;
/// The [`Page`] of this pallet.
pub type PageOf<T> = Page<<T as Config>::Size, <T as Config>::HeapSize>;
/// The [`BookState`] of this pallet.
pub type BookStateOf<T> = BookState<MessageOriginOf<T>>;
/// Converts a [`sp_core::Get`] with returns a type that can be cast into an `u32` into a `Get`
/// which returns an `u32`.
pub struct IntoU32<T, O>(sp_std::marker::PhantomData<(T, O)>);
impl<T: Get<O>, O: Into<u32>> Get<u32> for IntoU32<T, O> {
fn get() -> u32 {
T::get().into()
}
}
impl<T: Config> ServiceQueues for Pallet<T> {
type OverweightMessageAddress = (MessageOriginOf<T>, PageIndex, T::Size);
fn service_queues(weight_limit: Weight) -> Weight {
// The maximum weight that processing a single message may take.
let overweight_limit = weight_limit;
let mut weight = WeightMeter::from_limit(weight_limit);
let mut next = match Self::bump_service_head(&mut weight) {
Some(h) => h,
None => return weight.consumed(),
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
};
// The last queue that did not make any progress.
// The loop aborts as soon as it arrives at this queue again without making any progress
// on other queues in between.
let mut last_no_progress = None;
loop {
let (progressed, n) = Self::service_queue(next.clone(), &mut weight, overweight_limit);
next = match n {
Some(n) =>
if !progressed {
if last_no_progress == Some(n.clone()) {
break
}
if last_no_progress.is_none() {
last_no_progress = Some(next.clone())
}
n
} else {
last_no_progress = None;
n
},
None => break,
}
}
}
/// Execute a single overweight message.
///
/// The weight limit must be enough for `execute_overweight` and the message execution itself.
fn execute_overweight(
weight_limit: Weight,
(message_origin, page, index): Self::OverweightMessageAddress,
) -> Result<Weight, ExecuteOverweightError> {
let mut weight = WeightMeter::from_limit(weight_limit);
if weight
.try_consume(
T::WeightInfo::execute_overweight_page_removed()
.max(T::WeightInfo::execute_overweight_page_updated()),
)
.is_err()
{
return Err(ExecuteOverweightError::InsufficientWeight)
}
Pallet::<T>::do_execute_overweight(message_origin, page, index, weight.remaining()).map_err(
|e| match e {
Error::<T>::InsufficientWeight => ExecuteOverweightError::InsufficientWeight,
Error::<T>::AlreadyProcessed => ExecuteOverweightError::AlreadyProcessed,
Error::<T>::QueuePaused => ExecuteOverweightError::QueuePaused,
Error::<T>::NoPage | Error::<T>::NoMessage | Error::<T>::Queued =>
ExecuteOverweightError::NotFound,
_ => ExecuteOverweightError::Other,
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
},
)
}
}
impl<T: Config> EnqueueMessage<MessageOriginOf<T>> for Pallet<T> {
type MaxMessageLen =
MaxMessageLen<<T::MessageProcessor as ProcessMessage>::Origin, T::Size, T::HeapSize>;
fn enqueue_message(
message: BoundedSlice<u8, Self::MaxMessageLen>,
origin: <T::MessageProcessor as ProcessMessage>::Origin,
) {
Self::do_enqueue_message(&origin, message);
let book_state = BookStateFor::<T>::get(&origin);
T::QueueChangeHandler::on_queue_changed(origin, book_state.message_count, book_state.size);
}
fn enqueue_messages<'a>(
messages: impl Iterator<Item = BoundedSlice<'a, u8, Self::MaxMessageLen>>,
origin: <T::MessageProcessor as ProcessMessage>::Origin,
) {
for message in messages {
Self::do_enqueue_message(&origin, message);
}
let book_state = BookStateFor::<T>::get(&origin);
T::QueueChangeHandler::on_queue_changed(origin, book_state.message_count, book_state.size);
}
fn sweep_queue(origin: MessageOriginOf<T>) {
if !BookStateFor::<T>::contains_key(&origin) {
return
}
let mut book_state = BookStateFor::<T>::get(&origin);
book_state.begin = book_state.end;
if let Some(neighbours) = book_state.ready_neighbours.take() {
Self::ready_ring_unknit(&origin, neighbours);
}
BookStateFor::<T>::insert(&origin, &book_state);
}
fn footprint(origin: MessageOriginOf<T>) -> Footprint {
let book_state = BookStateFor::<T>::get(&origin);
Footprint { count: book_state.message_count, size: book_state.size }
}
}