// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Polkadot is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Polkadot. If not, see . //! Migrates the storage to version 5. use crate::*; use cumulus_primitives_core::ListChannelInfos; use frame_support::{pallet_prelude::*, traits::OnRuntimeUpgrade}; /// Configs needed to run the V5 migration. pub trait V5Config: Config { /// List all outbound channels with their target `ParaId` and maximum message size. type ChannelList: ListChannelInfos; } /// Ensures that the storage migrates cleanly to V5. /// /// The migration itself is a no-op, but it checks that none of the `BoundedVec`s would truncate on /// the next decode after the upgrade was applied. pub type MigrateV4ToV5 = frame_support::migrations::VersionedMigration< 4, 5, unversioned::UncheckedMigrateV4ToV5, Pallet, ::DbWeight, >; // V4 storage aliases mod v4 { use super::*; #[frame_support::storage_alias] pub(super) type OutboundXcmpStatus = StorageValue, Vec, ValueQuery>; #[frame_support::storage_alias] pub(super) type OutboundXcmpMessages = StorageDoubleMap< Pallet, Blake2_128Concat, ParaId, Twox64Concat, u16, Vec, ValueQuery, >; #[frame_support::storage_alias] pub(super) type SignalMessages = StorageMap, Blake2_128Concat, ParaId, Vec, ValueQuery>; } // Private module to hide the migration. mod unversioned { /// Please use [`MigrateV4ToV5`] instead. pub struct UncheckedMigrateV4ToV5(core::marker::PhantomData); } impl OnRuntimeUpgrade for unversioned::UncheckedMigrateV4ToV5 { fn on_runtime_upgrade() -> frame_support::weights::Weight { Default::default() } #[cfg(feature = "try-runtime")] fn post_upgrade(_: Vec) -> Result<(), sp_runtime::DispatchError> { // We dont need any front-run protection for this since channels are opened by governance. ensure!( v4::OutboundXcmpStatus::::get().len() as u32 <= T::MaxActiveOutboundChannels::get(), "Too many outbound channels. Close some channels or increase `MaxActiveOutboundChannels`." ); // Check if any channels have a too large message max sizes. let max_msg_len = T::MaxPageSize::get() - XcmpMessageFormat::max_encoded_len() as u32; for channel in T::ChannelList::outgoing_channels() { let info = T::ChannelInfo::get_channel_info(channel) .expect("All listed channels must provide info"); ensure!( info.max_message_size <= max_msg_len, "Max message size for channel is too large. This means that the V5 migration can \ be front-run and an attacker could place a large message just right before the \ migration to make other messages un-decodable. Please either increase \ `MaxPageSize` or decrease the `max_message_size` for this channel.", ); } // Now check that all pages still fit into the new `BoundedVec`s: for page in v4::OutboundXcmpMessages::::iter_values() { ensure!( page.len() < T::MaxPageSize::get() as usize, "Too long message in storage. Either manually truncate the pages or increase `MaxPageSize`." ); } for page in v4::SignalMessages::::iter_values() { ensure!( page.len() < T::MaxPageSize::get() as usize, "Too long signal in storage. Either manually truncate the pages or increase `MaxPageSize`." ); } Ok(()) } }