1
// Copyright 2019-2025 PureStake Inc.
2
// This file is part of Moonbeam.
3

            
4
// Moonbeam is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Moonbeam is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Moonbeam.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
extern crate alloc;
18

            
19
use alloc::vec::Vec;
20

            
21
use frame_support::{traits::OnRuntimeUpgrade, weights::Weight};
22

            
23
use crate::*;
24

            
25
#[derive(
26
	Clone,
27
	PartialEq,
28
	Eq,
29
	parity_scale_codec::Decode,
30
	parity_scale_codec::Encode,
31
	sp_runtime::RuntimeDebug,
32
)]
33
/// Reserve information { account, percent_of_inflation }
34
pub struct OldParachainBondConfig<AccountId> {
35
	/// Account which receives funds intended for parachain bond
36
	pub account: AccountId,
37
	/// Percent of inflation set aside for parachain bond account
38
	pub percent: sp_runtime::Percent,
39
}
40

            
41
pub struct MigrateParachainBondConfig<T>(sp_std::marker::PhantomData<T>);
42
impl<T: Config> OnRuntimeUpgrade for MigrateParachainBondConfig<T> {
43
	fn on_runtime_upgrade() -> Weight {
44
		let (account, percent) = if let Some(config) =
45
			frame_support::storage::migration::get_storage_value::<
46
				OldParachainBondConfig<T::AccountId>,
47
			>(b"ParachainStaking", b"ParachainBondInfo", &[])
48
		{
49
			(config.account, config.percent)
50
		} else {
51
			return Weight::default();
52
		};
53

            
54
		let pbr = InflationDistributionAccount { account, percent };
55
		let treasury = InflationDistributionAccount::<T::AccountId>::default();
56
		let configs: InflationDistributionConfig<T::AccountId> = [pbr, treasury].into();
57

            
58
		//***** Start mutate storage *****//
59

            
60
		InflationDistributionInfo::<T>::put(configs);
61

            
62
		// Remove storage value ParachainStaking::ParachainBondInfo
63
		frame_support::storage::unhashed::kill(&frame_support::storage::storage_prefix(
64
			b"ParachainStaking",
65
			b"ParachainBondInfo",
66
		));
67

            
68
		Weight::default()
69
	}
70

            
71
	#[cfg(feature = "try-runtime")]
72
	fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::DispatchError> {
73
		use frame_support::ensure;
74
		use parity_scale_codec::Encode;
75

            
76
		let state = frame_support::storage::migration::get_storage_value::<
77
			OldParachainBondConfig<T::AccountId>,
78
		>(b"ParachainStaking", b"ParachainBondInfo", &[]);
79

            
80
		ensure!(state.is_some(), "State not found");
81

            
82
		Ok(state
83
			.expect("should be Some(_) due to former call to ensure!")
84
			.encode())
85
	}
86

            
87
	#[cfg(feature = "try-runtime")]
88
	fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::DispatchError> {
89
		use frame_support::ensure;
90

            
91
		let old_state: OldParachainBondConfig<T::AccountId> =
92
			parity_scale_codec::Decode::decode(&mut &state[..])
93
				.map_err(|_| sp_runtime::DispatchError::Other("Failed to decode old state"))?;
94

            
95
		let new_state = InflationDistributionInfo::<T>::get();
96

            
97
		let pbr = InflationDistributionAccount {
98
			account: old_state.account,
99
			percent: old_state.percent,
100
		};
101
		let treasury = InflationDistributionAccount::<T::AccountId>::default();
102
		let expected_new_state: InflationDistributionConfig<T::AccountId> = [pbr, treasury].into();
103

            
104
		ensure!(new_state == expected_new_state, "State migration failed");
105

            
106
		Ok(())
107
	}
108
}
109

            
110
/// Migration to move `DelegationScheduledRequests` from a single `StorageMap` keyed by collator
111
/// into a `StorageDoubleMap` keyed by (collator, delegator) and to initialize the per-collator
112
/// counter `DelegationScheduledRequestsPerCollator`.
113
///
114
/// This assumes the on-chain data was written with the old layout where:
115
/// - Storage key: ParachainStaking::DelegationScheduledRequests
116
/// - Value type: BoundedVec<ScheduledRequest<..>, AddGet<MaxTop, MaxBottom>>
117
pub struct MigrateDelegationScheduledRequestsToDoubleMap<T>(sp_std::marker::PhantomData<T>);
118

            
119
impl<T: Config> OnRuntimeUpgrade for MigrateDelegationScheduledRequestsToDoubleMap<T> {
120
	fn on_runtime_upgrade() -> Weight {
121
		use frame_support::storage::migration::storage_key_iter;
122

            
123
		type OldScheduledRequests<T> = frame_support::BoundedVec<
124
			ScheduledRequest<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
125
			AddGet<
126
				<T as pallet::Config>::MaxTopDelegationsPerCandidate,
127
				<T as pallet::Config>::MaxBottomDelegationsPerCandidate,
128
			>,
129
		>;
130

            
131
		// Collect all existing entries under the old layout.
132
		let mut entries: Vec<(
133
			<T as frame_system::Config>::AccountId,
134
			OldScheduledRequests<T>,
135
		)> = Vec::new();
136

            
137
		for (collator, requests) in storage_key_iter::<
138
			<T as frame_system::Config>::AccountId,
139
			OldScheduledRequests<T>,
140
			frame_support::Blake2_128Concat,
141
		>(b"ParachainStaking", b"DelegationScheduledRequests")
142
		{
143
			entries.push((collator, requests));
144
		}
145

            
146
		// Clear all existing keys for DelegationScheduledRequests to avoid mixing
147
		// old layout entries with the new double-map layout.
148
		//
149
		// We use the low-level `sp_io::storage::clear_prefix` on the full storage prefix so
150
		// that *all* existing keys under `ParachainStaking::DelegationScheduledRequests`
151
		// (from the legacy single-map layout) are removed before we insert the new keys for
152
		// the double-map layout.
153
		let prefix = frame_support::storage::storage_prefix(
154
			b"ParachainStaking",
155
			b"DelegationScheduledRequests",
156
		);
157
		let _ = frame_support::storage::unhashed::clear_prefix(&prefix, None, None);
158

            
159
		// Rebuild storage using the new layout and initialize the per-collator counters.
160
		for (collator, old_requests) in entries.into_iter() {
161
			let mut per_collator_count: u32 = 0;
162

            
163
			for request in old_requests.into_iter() {
164
				let delegator = request.delegator.clone();
165
				let mut new_vec: frame_support::BoundedVec<
166
					ScheduledRequest<<T as frame_system::Config>::AccountId, BalanceOf<T>>,
167
					<T as crate::pallet::Config>::MaxScheduledRequestsPerDelegator,
168
				> = Default::default();
169

            
170
				if new_vec.try_push(request).is_err() {
171
					// This should not happen since we only ever push a single element.
172
					continue;
173
				}
174

            
175
				DelegationScheduledRequests::<T>::insert(&collator, &delegator, new_vec);
176
				per_collator_count = per_collator_count.saturating_add(1);
177
			}
178

            
179
			if per_collator_count > 0 {
180
				DelegationScheduledRequestsPerCollator::<T>::insert(&collator, per_collator_count);
181
			}
182
		}
183

            
184
		Weight::zero()
185
	}
186
}