referrerpolicy=no-referrer-when-downgrade

cumulus_pallet_xcmp_queue/migration/
v5.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Cumulus.
3// SPDX-License-Identifier: Apache-2.0
4
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// 	http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17//! Migrates the storage to version 5.
18
19use crate::*;
20use alloc::vec::Vec;
21use cumulus_primitives_core::ListChannelInfos;
22use frame_support::{pallet_prelude::*, traits::UncheckedOnRuntimeUpgrade};
23
24/// Configs needed to run the V5 migration.
25pub trait V5Config: Config {
26	/// List all outbound channels with their target `ParaId` and maximum message size.
27	type ChannelList: ListChannelInfos;
28}
29
30/// Ensures that the storage migrates cleanly to V5.
31///
32/// The migration itself is a no-op, but it checks that none of the `BoundedVec`s would truncate on
33/// the next decode after the upgrade was applied.
34pub type MigrateV4ToV5<T> = frame_support::migrations::VersionedMigration<
35	4,
36	5,
37	unversioned::UncheckedMigrateV4ToV5<T>,
38	Pallet<T>,
39	<T as frame_system::Config>::DbWeight,
40>;
41
42// V4 storage aliases
43mod v4 {
44	use super::*;
45
46	#[frame_support::storage_alias]
47	pub(super) type OutboundXcmpStatus<T: Config> =
48		StorageValue<Pallet<T>, Vec<OutboundChannelDetails>, ValueQuery>;
49
50	#[frame_support::storage_alias]
51	pub(super) type OutboundXcmpMessages<T: Config> = StorageDoubleMap<
52		Pallet<T>,
53		Blake2_128Concat,
54		ParaId,
55		Twox64Concat,
56		u16,
57		Vec<u8>,
58		ValueQuery,
59	>;
60
61	#[frame_support::storage_alias]
62	pub(super) type SignalMessages<T: Config> =
63		StorageMap<Pallet<T>, Blake2_128Concat, ParaId, Vec<u8>, ValueQuery>;
64}
65
66// Private module to hide the migration.
67mod unversioned {
68	/// Please use [`MigrateV4ToV5`] instead.
69	pub struct UncheckedMigrateV4ToV5<T: super::V5Config>(core::marker::PhantomData<T>);
70}
71
72impl<T: V5Config> UncheckedOnRuntimeUpgrade for unversioned::UncheckedMigrateV4ToV5<T> {
73	fn on_runtime_upgrade() -> frame_support::weights::Weight {
74		Default::default()
75	}
76
77	#[cfg(feature = "try-runtime")]
78	fn post_upgrade(_: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
79		// We dont need any front-run protection for this since channels are opened by governance.
80		ensure!(
81			v4::OutboundXcmpStatus::<T>::get().len() as u32 <= T::MaxActiveOutboundChannels::get(),
82			"Too many outbound channels. Close some channels or increase `MaxActiveOutboundChannels`."
83		);
84
85		ensure!(T::MaxPageSize::get() >= 16, "Sanity check failed: MaxPageSize too small");
86
87		// Check if any channels have a too large message max sizes.
88		let max_msg_len = T::MaxPageSize::get() - XcmpMessageFormat::max_encoded_len() as u32;
89		for channel in T::ChannelList::outgoing_channels() {
90			let info = T::ChannelInfo::get_channel_info(channel)
91				.expect("All listed channels must provide info");
92
93			if info.max_message_size > max_msg_len {
94				tracing::error!(
95					target: "runtime::xcmp-queue-migration::v5",
96					channel_max=%info.max_message_size,
97					max_page_size=%max_msg_len,
98					"Max message size for channel is too large. This means that the V5 \
99					migration can be front-run and an attacker could place a large message just right \
100					before the migration to make other messages un-decodable. Please either increase \
101					`MaxPageSize` or decrease the `max_message_size` for this channel."
102				);
103				return Err("Migration can be front-run".into());
104			}
105		}
106
107		Ok(())
108	}
109}