Skip to content

Commit 59222cb

Browse files
authored
Runtime Upgrade (#383)
1 parent bd6303c commit 59222cb

File tree

7 files changed

+476
-7
lines changed

7 files changed

+476
-7
lines changed
+330-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,334 @@
11
//! A module that is responsible for migration of storage.
22
use frame_support::traits::StorageVersion;
33
/// The current storage version
4-
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
4+
pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
55
pub const LOG: &str = "runtime::funding::migration";
6+
7+
pub mod v5 {
8+
use crate::{
9+
AccountIdOf, BalanceOf, BlockNumberPair, CheckOutcome, Config, EvaluationRoundInfoOf, EvaluatorsOutcome,
10+
FixedPointNumber, FundingOutcome, HRMPChannelStatus, Pallet, PriceOf, ProjectDetailsOf, ProjectStatus,
11+
RewardInfo,
12+
};
13+
use core::marker::PhantomData;
14+
use frame_support::traits::{tokens::Balance as BalanceT, UncheckedOnRuntimeUpgrade};
15+
use frame_system::pallet_prelude::BlockNumberFor;
16+
use polimec_common::credentials::Did;
17+
use polkadot_parachain_primitives::primitives::Id as ParaId;
18+
use scale_info::TypeInfo;
19+
use serde::{Deserialize, Serialize};
20+
use sp_core::{Decode, Encode, Get, MaxEncodedLen, RuntimeDebug};
21+
use sp_runtime::traits::One;
22+
23+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
24+
pub struct OldProjectDetails<
25+
AccountId,
26+
Did,
27+
BlockNumber,
28+
Price: FixedPointNumber,
29+
Balance: BalanceT,
30+
EvaluationRoundInfo,
31+
> {
32+
pub issuer_account: AccountId,
33+
pub issuer_did: Did,
34+
/// Whether the project is frozen, so no `metadata` changes are allowed.
35+
pub is_frozen: bool,
36+
/// The price in USD per token decided after the Auction Round
37+
pub weighted_average_price: Option<Price>,
38+
/// The current status of the project
39+
pub status: OldProjectStatus,
40+
/// When the different project phases start and end
41+
pub phase_transition_points: OldPhaseTransitionPoints<BlockNumber>,
42+
/// Fundraising target amount in USD (6 decimals)
43+
pub fundraising_target_usd: Balance,
44+
/// The amount of Contribution Tokens that have not yet been sold
45+
pub remaining_contribution_tokens: Balance,
46+
/// Funding reached amount in USD (6 decimals)
47+
pub funding_amount_reached_usd: Balance,
48+
/// Information about the total amount bonded, and the outcome in regards to reward/slash/nothing
49+
pub evaluation_round_info: EvaluationRoundInfo,
50+
/// If the auction was oversubscribed, how much USD was raised across all winning bids
51+
pub usd_bid_on_oversubscription: Option<Balance>,
52+
/// When the Funding Round ends
53+
pub funding_end_block: Option<BlockNumber>,
54+
pub migration_type: Option<OldMigrationType>,
55+
}
56+
#[derive(
57+
Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, Serialize, Deserialize,
58+
)]
59+
pub enum OldProjectStatus {
60+
#[default]
61+
Application,
62+
EvaluationRound,
63+
AuctionInitializePeriod,
64+
AuctionOpening,
65+
AuctionClosing,
66+
CalculatingWAP,
67+
CommunityRound,
68+
RemainderRound,
69+
FundingFailed,
70+
AwaitingProjectDecision,
71+
FundingSuccessful,
72+
SettlementStarted(OldFundingOutcome),
73+
SettlementFinished(OldFundingOutcome),
74+
CTMigrationStarted,
75+
CTMigrationFinished,
76+
}
77+
78+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen, Serialize, Deserialize)]
79+
pub enum OldFundingOutcome {
80+
FundingSuccessful,
81+
FundingFailed,
82+
}
83+
84+
#[derive(Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
85+
pub struct OldPhaseTransitionPoints<BlockNumber> {
86+
pub application: OldBlockNumberPair<BlockNumber>,
87+
pub evaluation: OldBlockNumberPair<BlockNumber>,
88+
pub auction_initialize_period: OldBlockNumberPair<BlockNumber>,
89+
pub auction_opening: OldBlockNumberPair<BlockNumber>,
90+
pub random_closing_ending: Option<BlockNumber>,
91+
pub auction_closing: OldBlockNumberPair<BlockNumber>,
92+
pub community: OldBlockNumberPair<BlockNumber>,
93+
pub remainder: OldBlockNumberPair<BlockNumber>,
94+
}
95+
96+
#[derive(Default, Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
97+
pub struct OldBlockNumberPair<BlockNumber> {
98+
pub start: Option<BlockNumber>,
99+
pub end: Option<BlockNumber>,
100+
}
101+
102+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
103+
pub struct OldEvaluationRoundInfo<Balance> {
104+
pub total_bonded_usd: Balance,
105+
pub total_bonded_plmc: Balance,
106+
pub evaluators_outcome: OldEvaluatorsOutcome<Balance>,
107+
}
108+
109+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
110+
pub enum OldEvaluatorsOutcome<Balance> {
111+
Unchanged,
112+
Rewarded(RewardInfo<Balance>),
113+
Slashed,
114+
}
115+
116+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
117+
pub enum OldMigrationType {
118+
Offchain,
119+
Pallet(OldPalletMigrationInfo),
120+
}
121+
122+
#[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, MaxEncodedLen, TypeInfo)]
123+
pub struct OldPalletMigrationInfo {
124+
/// ParaId of project
125+
pub parachain_id: ParaId,
126+
/// HRMP Channel status
127+
pub hrmp_channel_status: HRMPChannelStatus,
128+
/// Migration readiness check
129+
pub migration_readiness_check: Option<OldPalletMigrationReadinessCheck>,
130+
}
131+
132+
#[derive(Clone, Copy, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo, MaxEncodedLen)]
133+
pub struct OldPalletMigrationReadinessCheck {
134+
pub holding_check: (xcm::v3::QueryId, CheckOutcome),
135+
pub pallet_check: (xcm::v3::QueryId, CheckOutcome),
136+
}
137+
138+
type OldProjectDetailsOf<T> = OldProjectDetails<
139+
AccountIdOf<T>,
140+
Did,
141+
BlockNumberFor<T>,
142+
PriceOf<T>,
143+
BalanceOf<T>,
144+
OldEvaluationRoundInfo<BalanceOf<T>>,
145+
>;
146+
147+
pub struct UncheckedMigrationToV5<T: Config>(PhantomData<T>);
148+
impl<T: Config> UncheckedOnRuntimeUpgrade for UncheckedMigrationToV5<T> {
149+
fn on_runtime_upgrade() -> frame_support::weights::Weight {
150+
let mut items = 0;
151+
log::info!("Starting migration to V5");
152+
let mut translate_project_details = |key, item: OldProjectDetailsOf<T>| -> Option<ProjectDetailsOf<T>> {
153+
items += 1;
154+
log::info!("project_details item {:?}", items);
155+
let round_duration: BlockNumberPair<BlockNumberFor<T>>;
156+
let new_status = match item.status {
157+
OldProjectStatus::Application => {
158+
let start =
159+
item.phase_transition_points.application.start.expect("Application start block is missing");
160+
let end =
161+
item.phase_transition_points.application.end.expect("Application end block is missing");
162+
round_duration = BlockNumberPair::new(Some(start), Some(end));
163+
ProjectStatus::Application
164+
},
165+
OldProjectStatus::EvaluationRound => {
166+
let start =
167+
item.phase_transition_points.evaluation.start.expect("Evaluation start block is missing");
168+
let end = item.phase_transition_points.evaluation.end.expect("Evaluation end block is missing");
169+
round_duration = BlockNumberPair::new(Some(start), Some(end));
170+
ProjectStatus::EvaluationRound
171+
},
172+
OldProjectStatus::AuctionInitializePeriod => {
173+
let now = frame_system::Pallet::<T>::block_number();
174+
let start = now;
175+
let end = now + <T as Config>::AuctionRoundDuration::get();
176+
round_duration = BlockNumberPair::new(Some(start), Some(end));
177+
debug_assert!(false, "AuctionInitializePeriod is not supported in V5, no project should be in this state when upgrading storage");
178+
log::error!("AuctionInitializePeriod is not supported in V5, no project should be in this state when upgrading storage");
179+
ProjectStatus::AuctionRound
180+
},
181+
OldProjectStatus::AuctionOpening => {
182+
let start = item
183+
.phase_transition_points
184+
.auction_opening
185+
.start
186+
.expect("AuctionOpening start block is missing");
187+
let end = start + <T as Config>::AuctionRoundDuration::get();
188+
round_duration = BlockNumberPair::new(Some(start), Some(end));
189+
debug_assert!(false, "AuctionOpening is not supported in V5, no project should be in this state when upgrading storage");
190+
log::error!("AuctionOpening is not supported in V5, no project should be in this state when upgrading storage");
191+
ProjectStatus::AuctionRound
192+
},
193+
OldProjectStatus::AuctionClosing => {
194+
let start = item
195+
.phase_transition_points
196+
.auction_opening
197+
.start
198+
.expect("AuctionOpening start block is missing");
199+
let end = start + <T as Config>::AuctionRoundDuration::get();
200+
round_duration = BlockNumberPair::new(Some(start), Some(end));
201+
debug_assert!(false, "AuctionClosing is not supported in V5, no project should be in this state when upgrading storage");
202+
log::error!("AuctionClosing is not supported in V5, no project should be in this state when upgrading storage");
203+
ProjectStatus::AuctionRound
204+
},
205+
OldProjectStatus::CalculatingWAP => {
206+
let start = item
207+
.phase_transition_points
208+
.auction_opening
209+
.start
210+
.expect("AuctionOpening start block is missing");
211+
let end = start + <T as Config>::AuctionRoundDuration::get();
212+
round_duration = BlockNumberPair::new(Some(start), Some(end));
213+
debug_assert!(false, "CalculatingWAP is not supported in V5, no project should be in this state when upgrading storage");
214+
log::error!("CalculatingWAP is not supported in V5, no project should be in this state when upgrading storage");
215+
ProjectStatus::AuctionRound
216+
},
217+
OldProjectStatus::CommunityRound => {
218+
let start = item
219+
.phase_transition_points
220+
.community
221+
.start
222+
.expect("CommunityRound start block is missing");
223+
let end = start +
224+
<T as Config>::CommunityRoundDuration::get() +
225+
<T as Config>::RemainderRoundDuration::get();
226+
round_duration = BlockNumberPair::new(Some(start), Some(end));
227+
debug_assert!(
228+
false,
229+
"We should not upgrade runtime while a project is still in community round"
230+
);
231+
ProjectStatus::CommunityRound(
232+
item.phase_transition_points.community.end.expect("CommunityRound end block is missing") +
233+
One::one(),
234+
)
235+
},
236+
OldProjectStatus::RemainderRound => {
237+
let start = item
238+
.phase_transition_points
239+
.community
240+
.start
241+
.expect("CommunityRound start block is missing");
242+
let end = start +
243+
<T as Config>::CommunityRoundDuration::get() +
244+
<T as Config>::RemainderRoundDuration::get();
245+
round_duration = BlockNumberPair::new(Some(start), Some(end));
246+
ProjectStatus::CommunityRound(
247+
item.phase_transition_points.remainder.start.expect("Remainder start block is missing"),
248+
)
249+
},
250+
OldProjectStatus::FundingFailed => {
251+
round_duration = BlockNumberPair::new(None, None);
252+
ProjectStatus::SettlementStarted(FundingOutcome::Failure)
253+
},
254+
OldProjectStatus::AwaitingProjectDecision => {
255+
round_duration = BlockNumberPair::new(None, None);
256+
debug_assert!(false, "AwaitingProjectDecision is not supported in V5, no project should be in this state when upgrading storage");
257+
log::error!("AwaitingProjectDecision is not supported in V5, no project should be in this state when upgrading storage");
258+
ProjectStatus::FundingSuccessful
259+
},
260+
OldProjectStatus::FundingSuccessful => {
261+
round_duration = BlockNumberPair::new(None, None);
262+
ProjectStatus::SettlementStarted(FundingOutcome::Success)
263+
},
264+
OldProjectStatus::SettlementStarted(old_outcome) => {
265+
round_duration = BlockNumberPair::new(None, None);
266+
let outcome = match old_outcome {
267+
OldFundingOutcome::FundingSuccessful => FundingOutcome::Success,
268+
OldFundingOutcome::FundingFailed => FundingOutcome::Failure,
269+
};
270+
ProjectStatus::SettlementStarted(outcome)
271+
},
272+
OldProjectStatus::SettlementFinished(old_outcome) => {
273+
round_duration = BlockNumberPair::new(None, None);
274+
let outcome = match old_outcome {
275+
OldFundingOutcome::FundingSuccessful => FundingOutcome::Success,
276+
OldFundingOutcome::FundingFailed => FundingOutcome::Failure,
277+
};
278+
ProjectStatus::SettlementFinished(outcome)
279+
},
280+
OldProjectStatus::CTMigrationStarted => {
281+
round_duration = BlockNumberPair::new(None, None);
282+
ProjectStatus::CTMigrationStarted
283+
},
284+
OldProjectStatus::CTMigrationFinished => {
285+
round_duration = BlockNumberPair::new(None, None);
286+
ProjectStatus::CTMigrationFinished
287+
},
288+
};
289+
290+
let evaluators_outcome = Some(match item.evaluation_round_info.evaluators_outcome {
291+
OldEvaluatorsOutcome::Unchanged => EvaluatorsOutcome::Rewarded(
292+
<Pallet<T>>::generate_evaluator_rewards_info(key)
293+
.expect("Evaluator rewards info should be generated"),
294+
),
295+
OldEvaluatorsOutcome::Rewarded(info) => EvaluatorsOutcome::<BalanceOf<T>>::Rewarded(info),
296+
OldEvaluatorsOutcome::Slashed => EvaluatorsOutcome::<BalanceOf<T>>::Slashed,
297+
});
298+
let evaluation_round_info = EvaluationRoundInfoOf::<T> {
299+
total_bonded_usd: item.evaluation_round_info.total_bonded_usd,
300+
total_bonded_plmc: item.evaluation_round_info.total_bonded_plmc,
301+
evaluators_outcome,
302+
};
303+
Some(ProjectDetailsOf::<T> {
304+
issuer_account: item.issuer_account,
305+
issuer_did: item.issuer_did,
306+
is_frozen: item.is_frozen,
307+
weighted_average_price: item.weighted_average_price,
308+
status: new_status,
309+
round_duration,
310+
fundraising_target_usd: item.fundraising_target_usd,
311+
remaining_contribution_tokens: item.remaining_contribution_tokens,
312+
funding_amount_reached_usd: item.funding_amount_reached_usd,
313+
evaluation_round_info,
314+
usd_bid_on_oversubscription: item.usd_bid_on_oversubscription,
315+
funding_end_block: item.funding_end_block,
316+
migration_type: None,
317+
})
318+
};
319+
crate::ProjectsDetails::<T>::translate(|key, object: OldProjectDetailsOf<T>| {
320+
translate_project_details(key, object)
321+
});
322+
323+
T::DbWeight::get().reads_writes(items, items)
324+
}
325+
}
326+
327+
pub type MigrationToV5<T> = frame_support::migrations::VersionedMigration<
328+
4,
329+
5,
330+
UncheckedMigrationToV5<T>,
331+
crate::Pallet<T>,
332+
<T as frame_system::Config>::DbWeight,
333+
>;
334+
}

pallets/funding/src/tests/4_contribution.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1758,14 +1758,17 @@ mod contribute_extrinsic {
17581758
let glutton_contribution = ContributionParams::new(BUYER_1, remaining_cts, 4u8, AcceptedFundingAsset::USDT);
17591759
let wap = project_details.weighted_average_price.unwrap();
17601760
let plmc_mint = inst.calculate_contributed_plmc_spent(vec![glutton_contribution.clone()], wap, true);
1761-
let funding_asset_mint = inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap);
1761+
let funding_asset_mint =
1762+
inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap);
17621763
inst.mint_plmc_to(plmc_mint);
17631764
inst.mint_funding_asset_to(funding_asset_mint);
17641765
inst.contribute_for_users(project_id, vec![glutton_contribution.clone()]).unwrap();
17651766

1766-
let failing_contribution = ContributionParams::<TestRuntime>::new(BUYER_2, 1000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT);
1767+
let failing_contribution =
1768+
ContributionParams::<TestRuntime>::new(BUYER_2, 1000 * CT_UNIT, 1u8, AcceptedFundingAsset::USDT);
17671769
let plmc_mint = inst.calculate_contributed_plmc_spent(vec![glutton_contribution.clone()], wap, true);
1768-
let funding_asset_mint = inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap);
1770+
let funding_asset_mint =
1771+
inst.calculate_contributed_funding_asset_spent(vec![glutton_contribution.clone()], wap);
17691772
inst.mint_plmc_to(plmc_mint);
17701773
inst.mint_funding_asset_to(funding_asset_mint);
17711774
inst.execute(|| {

0 commit comments

Comments
 (0)