Companion for polkadot#7234 (XCM: Tools for uniquely referencing messages) (#2601)

* Fixes for new API

* Formatting

* Fixes

* Fixes

* Further fixes

* XCMP dispatch events mention message ID

* XCMP event includes ID

* Add DMP message ID functionality

* Integrate into test parachains

* Remove WithUniqueTopic usage

* Use new primitive

* Formatting

* undiener

* Revert lock

* Fixes

* Fixes

* Fixes

* Fixes

* Formatting

* message_hash becomes message_id

* Rename

* Another Rename

* Fixes

* Fix

* Bump

* Fixes

* Grumble.
This commit is contained in:
Gavin Wood
2023-05-25 16:52:38 +01:00
committed by GitHub
parent febea66397
commit ec21c0a24d
23 changed files with 431 additions and 569 deletions
+81 -87
View File
@@ -75,7 +75,7 @@ pub struct PageIndexData {
/// Simple type used to identify messages for the purpose of reporting events. Secure if and only
/// if the message content is unique.
pub type MessageId = [u8; 32];
pub type MessageId = XcmHash;
/// Index used to identify overweight messages.
pub type OverweightIndex = u64;
@@ -174,23 +174,39 @@ pub mod pallet {
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// Downward message is invalid XCM.
InvalidFormat { message_id: MessageId },
InvalidFormat { message_hash: XcmHash },
/// Downward message is unsupported version of XCM.
UnsupportedVersion { message_id: MessageId },
UnsupportedVersion { message_hash: XcmHash },
/// Downward message executed with the given outcome.
ExecutedDownward { message_id: MessageId, outcome: Outcome },
ExecutedDownward { message_hash: XcmHash, message_id: XcmHash, outcome: Outcome },
/// The weight limit for handling downward messages was reached.
WeightExhausted { message_id: MessageId, remaining_weight: Weight, required_weight: Weight },
WeightExhausted {
message_hash: XcmHash,
message_id: XcmHash,
remaining_weight: Weight,
required_weight: Weight,
},
/// Downward message is overweight and was placed in the overweight queue.
OverweightEnqueued {
message_id: MessageId,
message_hash: XcmHash,
message_id: XcmHash,
overweight_index: OverweightIndex,
required_weight: Weight,
},
/// Downward message from the overweight queue was executed.
OverweightServiced { overweight_index: OverweightIndex, weight_used: Weight },
/// The maximum number of downward messages was.
MaxMessagesExhausted { message_id: MessageId },
/// The maximum number of downward messages was reached.
MaxMessagesExhausted { message_hash: XcmHash },
}
/// Error type when a message was failed to be serviced.
pub(crate) struct ServiceMessageError {
/// The message's hash.
message_hash: XcmHash,
/// The message's ID (which could also be its hash if nothing overrides it).
message_id: XcmHash,
/// Weight required for the message to be executed.
required_weight: Weight,
}
impl<T: Config> Pallet<T> {
@@ -250,8 +266,9 @@ pub mod pallet {
limit: Weight,
_sent_at: RelayBlockNumber,
mut data: &[u8],
) -> Result<Weight, (MessageId, Weight)> {
let message_id = sp_io::hashing::blake2_256(data);
) -> Result<Weight, ServiceMessageError> {
let message_hash = sp_io::hashing::blake2_256(data);
let mut message_id = message_hash;
let maybe_msg = VersionedXcm::<T::RuntimeCall>::decode_all_with_depth_limit(
MAX_XCM_DECODE_DEPTH,
&mut data,
@@ -259,21 +276,31 @@ pub mod pallet {
.map(Xcm::<T::RuntimeCall>::try_from);
match maybe_msg {
Err(_) => {
Self::deposit_event(Event::InvalidFormat { message_id });
Self::deposit_event(Event::InvalidFormat { message_hash });
Ok(Weight::zero())
},
Ok(Err(())) => {
Self::deposit_event(Event::UnsupportedVersion { message_id });
Self::deposit_event(Event::UnsupportedVersion { message_hash });
Ok(Weight::zero())
},
Ok(Ok(x)) => {
let outcome = T::XcmExecutor::execute_xcm(Parent, x, message_id, limit);
let outcome = T::XcmExecutor::prepare_and_execute(
Parent,
x,
&mut message_id,
limit,
Weight::zero(),
);
match outcome {
Outcome::Error(XcmError::WeightLimitReached(required)) =>
Err((message_id, required)),
Outcome::Error(XcmError::WeightLimitReached(required_weight)) =>
Err(ServiceMessageError { message_hash, message_id, required_weight }),
outcome => {
let weight_used = outcome.weight_used();
Self::deposit_event(Event::ExecutedDownward { message_id, outcome });
Self::deposit_event(Event::ExecutedDownward {
message_hash,
message_id,
outcome,
});
Ok(weight_used)
},
}
@@ -314,7 +341,7 @@ pub mod pallet {
maybe_enqueue_page = Some(Vec::with_capacity(item_count_left));
Self::deposit_event(Event::MaxMessagesExhausted {
message_id: sp_io::hashing::blake2_256(&data),
message_hash: sp_io::hashing::blake2_256(&data),
});
} else {
// We're not currently enqueuing - try to execute inline.
@@ -322,7 +349,11 @@ pub mod pallet {
messages_processed += 1;
match Self::try_service_message(remaining_weight, sent_at, &data[..]) {
Ok(consumed) => used += consumed,
Err((message_id, required_weight)) =>
Err(ServiceMessageError {
message_hash,
message_id,
required_weight,
}) =>
// Too much weight required right now.
{
let is_under_limit =
@@ -334,6 +365,7 @@ pub mod pallet {
let overweight_index = page_index.overweight_count;
Overweight::<T>::insert(overweight_index, (sent_at, data));
Self::deposit_event(Event::OverweightEnqueued {
message_hash,
message_id,
overweight_index,
required_weight,
@@ -348,6 +380,7 @@ pub mod pallet {
let item_count_left = item_count.saturating_sub(i);
maybe_enqueue_page = Some(Vec::with_capacity(item_count_left));
Self::deposit_event(Event::WeightExhausted {
message_hash,
message_id,
remaining_weight,
required_weight,
@@ -466,50 +499,36 @@ mod tests {
})
}
pub enum Weightless {}
impl PreparedMessage for Weightless {
pub struct MockPrepared(Xcm);
impl PreparedMessage for MockPrepared {
fn weight_of(&self) -> Weight {
unreachable!()
match ((self.0).0.len(), &(self.0).0.first()) {
(1, Some(Transact { require_weight_at_most, .. })) => *require_weight_at_most,
_ => Weight::from_parts(1, 1),
}
}
}
pub struct MockExec;
impl ExecuteXcm<RuntimeCall> for MockExec {
type Prepared = Weightless;
type Prepared = MockPrepared;
fn prepare(_message: Xcm) -> Result<Self::Prepared, Xcm> {
unreachable!()
fn prepare(message: Xcm) -> Result<Self::Prepared, Xcm> {
Ok(MockPrepared(message))
}
fn execute(
_origin: impl Into<MultiLocation>,
_pre: Weightless,
_hash: XcmHash,
_weight_credit: Weight,
) -> Outcome {
unreachable!()
}
fn execute_xcm_in_credit(
_origin: impl Into<MultiLocation>,
message: Xcm,
_hash: XcmHash,
weight_limit: Weight,
prepared: MockPrepared,
_id: &mut XcmHash,
_weight_credit: Weight,
) -> Outcome {
let message = prepared.0;
let o = match (message.0.len(), &message.0.first()) {
(1, Some(Transact { require_weight_at_most, .. })) => {
if require_weight_at_most.all_lte(weight_limit) {
Outcome::Complete(*require_weight_at_most)
} else {
Outcome::Error(XcmError::WeightLimitReached(*require_weight_at_most))
}
},
(1, Some(Transact { require_weight_at_most, .. })) =>
Outcome::Complete(*require_weight_at_most),
// use 1000 to decide that it's not supported.
_ => Outcome::Incomplete(
Weight::from_parts(1000, 1000).min(weight_limit),
XcmError::Unimplemented,
),
_ => Outcome::Incomplete(Weight::from_parts(1, 1), XcmError::Unimplemented),
};
TRACE.with(|q| q.borrow_mut().push((message, o.clone())));
o
@@ -564,13 +583,6 @@ mod tests {
(msg(weight), Outcome::Complete(Weight::from_parts(weight, weight)))
}
fn msg_limit_reached(weight: u64) -> (Xcm, Outcome) {
(
msg(weight),
Outcome::Error(XcmError::WeightLimitReached(Weight::from_parts(weight, weight))),
)
}
fn pages_queued() -> PageCounter {
PageIndex::<Test>::get().end_used - PageIndex::<Test>::get().begin_used
}
@@ -613,10 +625,7 @@ mod tests {
enqueue(&enqueued);
let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::from_parts(2001, 2001));
assert_eq!(
take_trace(),
vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(1002),]
);
assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1001),]);
});
}
@@ -631,18 +640,15 @@ mod tests {
PageIndexData { begin_used: 0, end_used: 1, overweight_count: 0 }
);
assert_eq!(Pages::<Test>::get(0).len(), 3);
assert_eq!(take_trace(), vec![msg_limit_reached(1000)]);
assert_eq!(take_trace(), vec![]);
let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::from_parts(2001, 2001));
assert_eq!(
take_trace(),
vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(1002),]
);
assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1001)]);
let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::from_parts(1002, 1002));
assert_eq!(take_trace(), vec![msg_complete(1002),]);
assert_eq!(take_trace(), vec![msg_complete(1002)]);
assert!(queue_is_empty());
});
}
@@ -655,7 +661,7 @@ mod tests {
assert_eq!(weight_used, Weight::from_parts(1000, 1000));
assert_eq!(pages_queued(), 1);
assert_eq!(Pages::<Test>::get(0).len(), 2);
assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]);
assert_eq!(take_trace(), vec![msg_complete(1000)]);
let weight_used = handle_messages(&[], Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::from_parts(2003, 2003));
@@ -693,13 +699,13 @@ mod tests {
enqueue(&enqueued);
let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000));
assert_eq!(weight_used, Weight::from_parts(1000, 1000));
assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(10001),]);
assert_eq!(take_trace(), vec![msg_complete(1000)]);
assert_eq!(pages_queued(), 2);
// 5000 is not enough to process the 10001 blocker, so nothing happens.
let weight_used = handle_messages(&[], Weight::from_parts(5000, 5000));
assert_eq!(weight_used, Weight::zero());
assert_eq!(take_trace(), vec![msg_limit_reached(10001),]);
assert_eq!(take_trace(), vec![]);
// 20000 is now enough to process everything.
let weight_used = handle_messages(&[], Weight::from_parts(20000, 20000));
@@ -720,10 +726,7 @@ mod tests {
enqueue(&enqueued);
let weight_used = handle_messages(&incoming, Weight::from_parts(5000, 5000));
assert_eq!(weight_used, Weight::from_parts(2001, 2001));
assert_eq!(
take_trace(),
vec![msg_complete(1000), msg_complete(1001), msg_limit_reached(10002),]
);
assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1001)]);
assert_eq!(pages_queued(), 1);
// 20000 is now enough to process everything.
@@ -744,12 +747,7 @@ mod tests {
assert_eq!(weight_used, Weight::from_parts(3003, 3003));
assert_eq!(
take_trace(),
vec![
msg_complete(1000),
msg_complete(1001),
msg_complete(1002),
msg_limit_reached(10003),
]
vec![msg_complete(1000), msg_complete(1001), msg_complete(1002),]
);
assert_eq!(pages_queued(), 1);
@@ -768,19 +766,19 @@ mod tests {
enqueue(&enqueued);
let weight_used = handle_messages(&[msg(1002)], Weight::from_parts(1500, 1500));
assert_eq!(weight_used, Weight::from_parts(1000, 1000));
assert_eq!(take_trace(), vec![msg_complete(1000), msg_limit_reached(1001),]);
assert_eq!(take_trace(), vec![msg_complete(1000)]);
assert_eq!(pages_queued(), 2);
assert_eq!(PageIndex::<Test>::get().begin_used, 0);
let weight_used = handle_messages(&[msg(1003)], Weight::from_parts(1500, 1500));
assert_eq!(weight_used, Weight::from_parts(1001, 1001));
assert_eq!(take_trace(), vec![msg_complete(1001), msg_limit_reached(1002),]);
assert_eq!(take_trace(), vec![msg_complete(1001)]);
assert_eq!(pages_queued(), 2);
assert_eq!(PageIndex::<Test>::get().begin_used, 1);
let weight_used = handle_messages(&[msg(1004)], Weight::from_parts(1500, 1500));
assert_eq!(weight_used, Weight::from_parts(1002, 1002));
assert_eq!(take_trace(), vec![msg_complete(1002), msg_limit_reached(1003),]);
assert_eq!(take_trace(), vec![msg_complete(1002)]);
assert_eq!(pages_queued(), 2);
assert_eq!(PageIndex::<Test>::get().begin_used, 2);
});
@@ -798,10 +796,7 @@ mod tests {
let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::from_parts(2002, 2002));
assert!(queue_is_empty());
assert_eq!(
take_trace(),
vec![msg_complete(1000), msg_limit_reached(10001), msg_complete(1002),]
);
assert_eq!(take_trace(), vec![msg_complete(1000), msg_complete(1002),]);
assert_eq!(overweights(), vec![0]);
});
@@ -818,7 +813,7 @@ mod tests {
let incoming = vec![msg(10000)];
let weight_used = handle_messages(&incoming, Weight::from_parts(2500, 2500));
assert_eq!(weight_used, Weight::zero());
assert_eq!(take_trace(), vec![msg_limit_reached(10000)]);
assert_eq!(take_trace(), vec![]);
assert_eq!(overweights(), vec![0]);
assert_noop!(
@@ -845,7 +840,7 @@ mod tests {
),
Error::<Test>::OverLimit
);
assert_eq!(take_trace(), vec![msg_limit_reached(10000)]);
assert_eq!(take_trace(), vec![]);
let base_weight =
super::Call::<Test>::service_overweight { index: 0, weight_limit: Weight::zero() }
@@ -891,7 +886,6 @@ mod tests {
msg_complete(1002),
msg_complete(1003),
msg_complete(1004),
msg_limit_reached(1005),
]
);
assert_eq!(pages_queued(), 1);