diff --git a/runtime/common/src/crowdloan/migration.rs b/runtime/common/src/crowdloan/migration.rs
new file mode 100644
index 000000000000..478471454f39
--- /dev/null
+++ b/runtime/common/src/crowdloan/migration.rs
@@ -0,0 +1,130 @@
+// Copyright 2017-2020 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 .
+
+use super::*;
+use frame_support::generate_storage_alias;
+
+/// Migrations for using fund index to create fund accounts instead of para ID.
+pub mod crowdloan_index_migration {
+ use super::*;
+
+ // The old way we generated fund accounts.
+ fn old_fund_account_id(index: ParaId) -> T::AccountId {
+ T::PalletId::get().into_sub_account(index)
+ }
+
+ pub fn pre_migrate() -> Result<(), &'static str> {
+ // `NextTrieIndex` should have a value.
+ generate_storage_alias!(Crowdloan, NextTrieIndex => Value);
+ let next_index = NextTrieIndex::get().unwrap_or_default();
+ ensure!(next_index > 0, "Next index is zero, which implies no migration is needed.");
+
+ log::info!(
+ target: "runtime",
+ "next trie index: {:?}",
+ next_index,
+ );
+
+ // Each fund should have some non-zero balance.
+ for (para_id, fund) in Funds::::iter() {
+ let old_fund_account = old_fund_account_id::(para_id);
+ let total_balance = CurrencyOf::::total_balance(&old_fund_account);
+
+ log::info!(
+ target: "runtime",
+ "para_id={:?}, old_fund_account={:?}, total_balance={:?}, fund.raised={:?}",
+ para_id, old_fund_account, total_balance, fund.raised
+ );
+
+ ensure!(
+ total_balance >= fund.raised,
+ "Total balance is not equal to the funds raised."
+ );
+ ensure!(total_balance > Zero::zero(), "Total balance is equal to zero.");
+ }
+
+ Ok(())
+ }
+
+ /// This migration converts crowdloans to use a crowdloan index rather than the parachain id as a
+ /// unique identifier. This makes it easier to swap two crowdloans between parachains.
+ pub fn migrate() -> frame_support::weights::Weight {
+ let mut weight = 0;
+
+ // First migrate `NextTrieIndex` counter to `NextFundIndex`.
+ generate_storage_alias!(Crowdloan, NextTrieIndex => Value);
+
+ let next_index = NextTrieIndex::take().unwrap_or_default();
+ NextFundIndex::::set(next_index);
+
+ weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2));
+
+ // Migrate all accounts from `old_fund_account` to `fund_account` using `fund_index`.
+ for (para_id, fund) in Funds::::iter() {
+ let old_fund_account = old_fund_account_id::(para_id);
+ let new_fund_account = Pallet::::fund_account_id(fund.fund_index);
+
+ // Funds should only have a free balance and a reserve balance. Both of these are in the
+ // `Account` storage item, so we just swap them.
+ let account_info = frame_system::Account::::take(old_fund_account);
+ frame_system::Account::::insert(new_fund_account, account_info);
+
+ weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2));
+ }
+
+ weight
+ }
+
+ pub fn post_migrate() -> Result<(), &'static str> {
+ // `NextTrieIndex` should not have a value, and `NextFundIndex` should.
+ generate_storage_alias!(Crowdloan, NextTrieIndex => Value);
+ ensure!(NextTrieIndex::get().is_none(), "NextTrieIndex still has a value.");
+
+ let next_index = NextFundIndex::::get();
+ log::info!(
+ target: "runtime",
+ "next fund index: {:?}",
+ next_index,
+ );
+
+ ensure!(
+ next_index > 0,
+ "NextFundIndex was not migrated or is zero. We assume it cannot be zero else no migration is needed."
+ );
+
+ // Each fund should have balance migrated correctly.
+ for (para_id, fund) in Funds::::iter() {
+ // Old fund account is deleted.
+ let old_fund_account = old_fund_account_id::(para_id);
+ ensure!(
+ frame_system::Account::::get(&old_fund_account) == Default::default(),
+ "Old account wasn't reset to default value."
+ );
+
+ // New fund account has the correct balance.
+ let new_fund_account = Pallet::::fund_account_id(fund.fund_index);
+ let total_balance = CurrencyOf::::total_balance(&new_fund_account);
+
+ ensure!(
+ total_balance >= fund.raised,
+ "Total balance in new account is different than the funds raised."
+ );
+ ensure!(total_balance > Zero::zero(), "Total balance in the account is zero.");
+ }
+
+ Ok(())
+ }
+}
diff --git a/runtime/common/src/crowdloan.rs b/runtime/common/src/crowdloan/mod.rs
similarity index 96%
rename from runtime/common/src/crowdloan.rs
rename to runtime/common/src/crowdloan/mod.rs
index 84b1be08f7ef..44d45e6ec790 100644
--- a/runtime/common/src/crowdloan.rs
+++ b/runtime/common/src/crowdloan/mod.rs
@@ -49,6 +49,8 @@
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
//! returned to the crowdloan account.
+pub mod migration;
+
use crate::{
slot_range::SlotRange,
traits::{Auctioneer, Registrar},
@@ -87,7 +89,7 @@ type BalanceOf = as Currency<::Acco
type NegativeImbalanceOf =
as Currency<::AccountId>>::NegativeImbalance;
-type TrieIndex = u32;
+type FundIndex = u32;
pub trait WeightInfo {
fn create() -> Weight;
@@ -145,33 +147,33 @@ pub enum LastContribution {
#[codec(dumb_trait_bound)]
pub struct FundInfo {
/// The owning account who placed the deposit.
- depositor: AccountId,
+ pub depositor: AccountId,
/// An optional verifier. If exists, contributions must be signed by verifier.
- verifier: Option,
+ pub verifier: Option,
/// The amount of deposit placed.
- deposit: Balance,
+ pub deposit: Balance,
/// The total amount raised.
- raised: Balance,
+ pub raised: Balance,
/// Block number after which the funding must have succeeded. If not successful at this number
/// then everyone may withdraw their funds.
- end: BlockNumber,
+ pub end: BlockNumber,
/// A hard-cap on the amount that may be contributed.
- cap: Balance,
+ pub cap: Balance,
/// The most recent block that this had a contribution. Determines if we make a bid or not.
/// If this is `Never`, this fund has never received a contribution.
/// If this is `PreEnding(n)`, this fund received a contribution sometime in auction
/// number `n` before the ending period.
/// If this is `Ending(n)`, this fund received a contribution during the current ending period,
/// where `n` is how far into the ending period the contribution was made.
- last_contribution: LastContribution,
+ pub last_contribution: LastContribution,
/// First lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
/// as `BlockNumber`.
- first_period: LeasePeriod,
+ pub first_period: LeasePeriod,
/// Last lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
/// as `BlockNumber`.
- last_period: LeasePeriod,
- /// Index used for the child trie of this fund
- trie_index: TrieIndex,
+ pub last_period: LeasePeriod,
+ /// Unique index used to represent this fund.
+ pub fund_index: FundIndex,
}
#[frame_support::pallet]
@@ -244,10 +246,10 @@ pub mod pallet {
#[pallet::getter(fn endings_count)]
pub(super) type EndingsCount = StorageValue<_, u32, ValueQuery>;
- /// Tracker for the next available trie index
+ /// Tracker for the next available fund index
#[pallet::storage]
- #[pallet::getter(fn next_trie_index)]
- pub(super) type NextTrieIndex = StorageValue<_, u32, ValueQuery>;
+ #[pallet::getter(fn next_fund_index)]
+ pub(super) type NextFundIndex = StorageValue<_, u32, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
@@ -342,7 +344,7 @@ pub mod pallet {
// Care needs to be taken by the crowdloan creator that this function will succeed given
// the crowdloaning configuration. We do some checks ahead of time in crowdloan `create`.
let result = T::Auctioneer::place_bid(
- Self::fund_account_id(para_id),
+ Self::fund_account_id(fund.fund_index),
para_id,
fund.first_period,
fund.last_period,
@@ -408,8 +410,8 @@ pub mod pallet {
ensure!(depositor == manager, Error::::InvalidOrigin);
ensure!(T::Registrar::is_registered(index), Error::::InvalidParaId);
- let trie_index = Self::next_trie_index();
- let new_trie_index = trie_index.checked_add(1).ok_or(Error::::Overflow)?;
+ let fund_index = Self::next_fund_index();
+ let new_fund_index = fund_index.checked_add(1).ok_or(Error::::Overflow)?;
let deposit = T::SubmissionDeposit::get();
@@ -427,11 +429,11 @@ pub mod pallet {
last_contribution: LastContribution::Never,
first_period,
last_period,
- trie_index,
+ fund_index,
},
);
- NextTrieIndex::::put(new_trie_index);
+ NextFundIndex::::put(new_fund_index);
// Add a lock to the para so that the configuration cannot be changed.
T::Registrar::apply_lock(index);
@@ -479,15 +481,15 @@ pub mod pallet {
let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?;
let now = frame_system::Pallet::::block_number();
- let fund_account = Self::fund_account_id(index);
+ let fund_account = Self::fund_account_id(fund.fund_index);
Self::ensure_crowdloan_ended(now, &fund_account, &fund)?;
- let (balance, _) = Self::contribution_get(fund.trie_index, &who);
+ let (balance, _) = Self::contribution_get(fund.fund_index, &who);
ensure!(balance > Zero::zero(), Error::::NoContributions);
CurrencyOf::::transfer(&fund_account, &who, balance, AllowDeath)?;
- Self::contribution_kill(fund.trie_index, &who);
+ Self::contribution_kill(fund.fund_index, &who);
fund.raised = fund.raised.saturating_sub(balance);
Funds::::insert(index, &fund);
@@ -510,12 +512,12 @@ pub mod pallet {
let mut fund = Self::funds(index).ok_or(Error::::InvalidParaId)?;
let now = frame_system::Pallet::::block_number();
- let fund_account = Self::fund_account_id(index);
+ let fund_account = Self::fund_account_id(fund.fund_index);
Self::ensure_crowdloan_ended(now, &fund_account, &fund)?;
let mut refund_count = 0u32;
// Try killing the crowdloan child trie
- let contributions = Self::contribution_iterator(fund.trie_index);
+ let contributions = Self::contribution_iterator(fund.fund_index);
// Assume everyone will be refunded.
let mut all_refunded = true;
for (who, (balance, _)) in contributions {
@@ -525,7 +527,7 @@ pub mod pallet {
break
}
CurrencyOf::::transfer(&fund_account, &who, balance, AllowDeath)?;
- Self::contribution_kill(fund.trie_index, &who);
+ Self::contribution_kill(fund.fund_index, &who);
fund.raised = fund.raised.saturating_sub(balance);
refund_count += 1;
}
@@ -561,7 +563,7 @@ pub mod pallet {
// Assuming state is not corrupted, the child trie should already be cleaned up
// and all funds in the crowdloan account have been returned. If not, governance
// can take care of that.
- debug_assert!(Self::contribution_iterator(fund.trie_index).count().is_zero());
+ debug_assert!(Self::contribution_iterator(fund.fund_index).count().is_zero());
CurrencyOf::::unreserve(&fund.depositor, fund.deposit);
Funds::::remove(index);
@@ -598,7 +600,7 @@ pub mod pallet {
last_contribution: fund.last_contribution,
first_period,
last_period,
- trie_index: fund.trie_index,
+ fund_index: fund.fund_index,
},
);
@@ -616,10 +618,10 @@ pub mod pallet {
ensure!(memo.len() <= T::MaxMemoLength::get().into(), Error::::MemoTooLarge);
let fund = Self::funds(index).ok_or(Error::::InvalidParaId)?;
- let (balance, _) = Self::contribution_get(fund.trie_index, &who);
+ let (balance, _) = Self::contribution_get(fund.fund_index, &who);
ensure!(balance > Zero::zero(), Error::::NoContributions);
- Self::contribution_put(fund.trie_index, &who, &balance, &memo);
+ Self::contribution_put(fund.fund_index, &who, &balance, &memo);
Self::deposit_event(Event::::MemoUpdated(who, index, memo));
Ok(())
}
@@ -658,11 +660,11 @@ impl Pallet {
///
/// This actually does computation. If you need to keep using it, then make sure you cache the
/// value and only call this once.
- pub fn fund_account_id(index: ParaId) -> T::AccountId {
+ pub fn fund_account_id(index: FundIndex) -> T::AccountId {
T::PalletId::get().into_sub_account(index)
}
- pub fn id_from_index(index: TrieIndex) -> child::ChildInfo {
+ pub fn id_from_index(index: FundIndex) -> child::ChildInfo {
let mut buf = Vec::new();
buf.extend_from_slice(b"crowdloan");
buf.extend_from_slice(&index.encode()[..]);
@@ -670,7 +672,7 @@ impl Pallet {
}
pub fn contribution_put(
- index: TrieIndex,
+ index: FundIndex,
who: &T::AccountId,
balance: &BalanceOf,
memo: &[u8],
@@ -678,22 +680,22 @@ impl Pallet {
who.using_encoded(|b| child::put(&Self::id_from_index(index), b, &(balance, memo)));
}
- pub fn contribution_get(index: TrieIndex, who: &T::AccountId) -> (BalanceOf, Vec) {
+ pub fn contribution_get(index: FundIndex, who: &T::AccountId) -> (BalanceOf, Vec) {
who.using_encoded(|b| {
child::get_or_default::<(BalanceOf, Vec)>(&Self::id_from_index(index), b)
})
}
- pub fn contribution_kill(index: TrieIndex, who: &T::AccountId) {
+ pub fn contribution_kill(index: FundIndex, who: &T::AccountId) {
who.using_encoded(|b| child::kill(&Self::id_from_index(index), b));
}
- pub fn crowdloan_kill(index: TrieIndex) -> child::KillStorageResult {
+ pub fn crowdloan_kill(index: FundIndex) -> child::KillStorageResult {
child::kill_storage(&Self::id_from_index(index), Some(T::RemoveKeysLimit::get()))
}
pub fn contribution_iterator(
- index: TrieIndex,
+ index: FundIndex,
) -> ChildTriePrefixIterator<(T::AccountId, (BalanceOf, Vec))> {
ChildTriePrefixIterator::<_>::with_prefix_over_key::(
&Self::id_from_index(index),
@@ -752,7 +754,7 @@ impl Pallet {
ensure!(current_lease_period <= fund.first_period, Error::::ContributionPeriodOver);
// Make sure crowdloan has not already won.
- let fund_account = Self::fund_account_id(index);
+ let fund_account = Self::fund_account_id(fund.fund_index);
ensure!(
!T::Auctioneer::has_won_an_auction(index, &fund_account),
Error::::BidOrLeaseActive
@@ -762,7 +764,7 @@ impl Pallet {
// contributions into the auction when it would not impact the outcome.
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::::VrfDelayInProgress);
- let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);
+ let (old_balance, memo) = Self::contribution_get(fund.fund_index, &who);
if let Some(ref verifier) = fund.verifier {
let signature = signature.ok_or(Error::::InvalidSignature)?;
@@ -776,7 +778,7 @@ impl Pallet {
CurrencyOf::::transfer(&who, &fund_account, value, existence)?;
let balance = old_balance.saturating_add(value);
- Self::contribution_put(fund.trie_index, &who, &balance, &memo);
+ Self::contribution_put(fund.fund_index, &who, &balance, &memo);
if T::Auctioneer::auction_status(now).is_ending().is_some() {
match fund.last_contribution {
@@ -966,7 +968,8 @@ mod tests {
// Emulate what would happen if we won an auction:
// balance is reserved and a deposit_held is recorded
fn set_winner(para: ParaId, who: u64, winner: bool) {
- let account_id = Crowdloan::fund_account_id(para);
+ let fund = Funds::::get(para).unwrap();
+ let account_id = Crowdloan::fund_account_id(fund.fund_index);
if winner {
let free_balance = Balances::free_balance(&account_id);
Balances::reserve(&account_id, free_balance)
@@ -1177,7 +1180,7 @@ mod tests {
last_contribution: LastContribution::Never,
first_period: 1,
last_period: 4,
- trie_index: 0,
+ fund_index: 0,
};
assert_eq!(Crowdloan::funds(para), Some(fund_info));
// User has deposit removed from their free balance
@@ -1217,7 +1220,7 @@ mod tests {
last_contribution: LastContribution::Never,
first_period: 1,
last_period: 4,
- trie_index: 0,
+ fund_index: 0,
};
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
// User has deposit removed from their free balance
@@ -1270,6 +1273,7 @@ mod tests {
fn contribute_works() {
new_test_ext().execute_with(|| {
let para = new_para();
+ let index = NextFundIndex::::get();
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
@@ -1284,7 +1288,7 @@ mod tests {
// Contributions are stored in the trie
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 49);
// Contributions appear in free balance of crowdloan
- assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 49);
+ assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(index)), 49);
// Crowdloan is added to NewRaise
assert_eq!(Crowdloan::new_raise(), vec![para]);
@@ -1300,6 +1304,7 @@ mod tests {
fn contribute_with_verifier_works() {
new_test_ext().execute_with(|| {
let para = new_para();
+ let index = NextFundIndex::::get();
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
// Set up a crowdloan
assert_ok!(Crowdloan::create(
@@ -1364,7 +1369,7 @@ mod tests {
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 10, Some(valid_signature_2)));
// Contributions appear in free balance of crowdloan
- assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(para)), 59);
+ assert_eq!(Balances::free_balance(Crowdloan::fund_account_id(index)), 59);
// Contribution amount is correct
let fund = Crowdloan::funds(para).unwrap();
@@ -1409,9 +1414,10 @@ mod tests {
// If a crowdloan has already won, it should not allow contributions.
let para_2 = new_para();
+ let index = NextFundIndex::::get();
assert_ok!(Crowdloan::create(Origin::signed(1), para_2, 1000, 1, 4, 40, None));
// Emulate a win by leasing out and putting a deposit. Slots pallet would normally do this.
- let crowdloan_account = Crowdloan::fund_account_id(para_2);
+ let crowdloan_account = Crowdloan::fund_account_id(index);
set_winner(para_2, crowdloan_account, true);
assert_noop!(
Crowdloan::contribute(Origin::signed(1), para_2, 49, None),
@@ -1478,6 +1484,7 @@ mod tests {
fn bidding_works() {
new_test_ext().execute_with(|| {
let para = new_para();
+ let index = NextFundIndex::::get();
let first_period = 1;
let last_period = 4;
@@ -1493,7 +1500,7 @@ mod tests {
9,
None
));
- let bidder = Crowdloan::fund_account_id(para);
+ let bidder = Crowdloan::fund_account_id(index);
// Fund crowdloan
run_to_block(1);
@@ -1524,6 +1531,7 @@ mod tests {
fn withdraw_from_failed_works() {
new_test_ext().execute_with(|| {
let para = new_para();
+ let index = NextFundIndex::::get();
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -1531,7 +1539,7 @@ mod tests {
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
run_to_block(10);
- let account_id = Crowdloan::fund_account_id(para);
+ let account_id = Crowdloan::fund_account_id(index);
// para has no reserved funds, indicating it did not win the auction.
assert_eq!(Balances::reserved_balance(&account_id), 0);
// but there's still the funds in its balance.
@@ -1553,13 +1561,14 @@ mod tests {
fn withdraw_cannot_be_griefed() {
new_test_ext().execute_with(|| {
let para = new_para();
+ let index = NextFundIndex::::get();
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
run_to_block(10);
- let account_id = Crowdloan::fund_account_id(para);
+ let account_id = Crowdloan::fund_account_id(index);
// user sends the crowdloan funds trying to make an accounting error
assert_ok!(Balances::transfer(Origin::signed(1), account_id, 10));
@@ -1583,7 +1592,8 @@ mod tests {
fn refund_works() {
new_test_ext().execute_with(|| {
let para = new_para();
- let account_id = Crowdloan::fund_account_id(para);
+ let index = NextFundIndex::::get();
+ let account_id = Crowdloan::fund_account_id(index);
// Set up a crowdloan ending on 9
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -1617,7 +1627,8 @@ mod tests {
fn multiple_refund_works() {
new_test_ext().execute_with(|| {
let para = new_para();
- let account_id = Crowdloan::fund_account_id(para);
+ let index = NextFundIndex::::get();
+ let account_id = Crowdloan::fund_account_id(index);
// Set up a crowdloan ending on 9
assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
@@ -1727,7 +1738,8 @@ mod tests {
fn withdraw_from_finished_works() {
new_test_ext().execute_with(|| {
let para = new_para();
- let account_id = Crowdloan::fund_account_id(para);
+ let index = NextFundIndex::::get();
+ let account_id = Crowdloan::fund_account_id(index);
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
@@ -2079,7 +2091,7 @@ mod benchmarking {
verify {
let fund = Funds::::get(fund_index).expect("fund was created...");
assert_eq!(
- Crowdloan::::contribution_get(fund.trie_index, &caller),
+ Crowdloan::::contribution_get(fund.fund_index, &caller),
(T::MinContribution::get(), worst_memo),
);
}
diff --git a/runtime/common/src/integration_tests.rs b/runtime/common/src/integration_tests.rs
index 370073fa4de0..7032c6badc82 100644
--- a/runtime/common/src/integration_tests.rs
+++ b/runtime/common/src/integration_tests.rs
@@ -20,7 +20,7 @@ use crate::{
auctions, crowdloan, paras_registrar,
slot_range::SlotRange,
slots,
- traits::{AuctionStatus, Auctioneer, Registrar as RegistrarT},
+ traits::{AuctionStatus, Auctioneer, Leaser, Registrar as RegistrarT},
};
use frame_support::{
assert_noop, assert_ok, parameter_types,
@@ -29,6 +29,7 @@ use frame_support::{
};
use frame_support_test::TestRandomness;
use frame_system::EnsureRoot;
+use parity_scale_codec::Encode;
use primitives::v1::{
BlockNumber, HeadData, Header, Id as ParaId, ValidationCode, LOWEST_PUBLIC_ID,
};
@@ -41,16 +42,29 @@ use sp_keystore::{testing::KeyStore, KeystoreExt};
use sp_runtime::{
traits::{BlakeTwo256, IdentityLookup, One},
transaction_validity::TransactionPriority,
+ AccountId32,
};
-use sp_std::sync::Arc;
+use sp_std::{convert::TryInto, sync::Arc};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
type Block = frame_system::mocking::MockBlock;
-type AccountId = u32;
+type AccountId = AccountId32;
type Balance = u32;
type Moment = u32;
+fn account_id(i: u32) -> AccountId32 {
+ let b4 = i.encode();
+ let b32 = [&b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..], &b4[..]].concat();
+ let array: [u8; 32] = b32.try_into().unwrap();
+ array.into()
+}
+
+fn signed(i: u32) -> Origin {
+ let account_id = account_id(i);
+ Origin::signed(account_id)
+}
+
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
@@ -342,21 +356,21 @@ fn basic_end_to_end_works() {
let para_2 = LOWEST_PUBLIC_ID + 1;
assert!(System::block_number().is_one());
// User 1 and 2 will own parachains
- Balances::make_free_balance_be(&1, 1_000_000_000);
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
// First register 2 parathreads
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(para_1),
genesis_head.clone(),
validation_code.clone(),
));
- assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::reserve(signed(2)));
assert_ok!(Registrar::register(
- Origin::signed(2),
+ signed(2),
ParaId::from(2001),
genesis_head,
validation_code,
@@ -379,7 +393,7 @@ fn basic_end_to_end_works() {
// Para 1 will bid directly for slot 1, 2
// Open a crowdloan for Para 2 for slot 3, 4
assert_ok!(Crowdloan::create(
- Origin::signed(2),
+ signed(2),
ParaId::from(para_2),
1_000, // Cap
lease_period_index_start + 2, // First Slot
@@ -387,17 +401,18 @@ fn basic_end_to_end_works() {
200 + offset, // Block End
None,
));
- let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(para_2));
+ let fund_2 = Crowdloan::funds(ParaId::from(para_2)).unwrap();
+ let crowdloan_account = Crowdloan::fund_account_id(fund_2.fund_index);
// Auction ending begins on block 100 + offset, so we make a bid before then.
run_to_block(90 + offset);
- Balances::make_free_balance_be(&10, 1_000_000_000);
- Balances::make_free_balance_be(&20, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(10), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(20), 1_000_000_000);
// User 10 will bid directly for parachain 1
assert_ok!(Auctions::bid(
- Origin::signed(10),
+ signed(10),
ParaId::from(para_1),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -406,8 +421,8 @@ fn basic_end_to_end_works() {
));
// User 2 will be a contribute to crowdloan for parachain 2
- Balances::make_free_balance_be(&2, 1_000_000_000);
- assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(para_2), 920, None));
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(2), ParaId::from(para_2), 920, None));
// Auction ends at block 110 + offset
run_to_block(109 + offset);
@@ -421,7 +436,7 @@ fn basic_end_to_end_works() {
assert_eq!(
slots::Leases::::get(ParaId::from(para_1)),
// -- 1 --- 2 --- 3 --------- 4 ------------ 5 --------
- vec![None, None, None, Some((10, 910)), Some((10, 910))],
+ vec![None, None, None, Some((account_id(10), 910)), Some((account_id(10), 910))],
);
assert_eq!(
slots::Leases::::get(ParaId::from(para_2)),
@@ -432,15 +447,15 @@ fn basic_end_to_end_works() {
None,
None,
None,
- Some((crowdloan_account, 920)),
- Some((crowdloan_account, 920))
+ Some((crowdloan_account.clone(), 920)),
+ Some((crowdloan_account.clone(), 920))
],
);
// Should not be able to contribute to a winning crowdloan
- Balances::make_free_balance_be(&3, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(3), 1_000_000_000);
assert_noop!(
- Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None),
+ Crowdloan::contribute(signed(3), ParaId::from(2001), 10, None),
CrowdloanError::::BidOrLeaseActive
);
@@ -508,21 +523,21 @@ fn basic_errors_fail() {
assert!(System::block_number().is_one());
let para_id = LOWEST_PUBLIC_ID;
// Can't double register
- Balances::make_free_balance_be(&1, 1_000_000_000);
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
para_id,
genesis_head.clone(),
validation_code.clone(),
));
- assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::reserve(signed(2)));
assert_noop!(
- Registrar::register(Origin::signed(2), para_id, genesis_head, validation_code,),
+ Registrar::register(signed(2), para_id, genesis_head, validation_code,),
paras_registrar::Error::::NotOwner
);
@@ -534,7 +549,7 @@ fn basic_errors_fail() {
// Cannot create a crowdloan if you do not own the para
assert_noop!(
Crowdloan::create(
- Origin::signed(2),
+ signed(2),
para_id,
1_000, // Cap
lease_period_index_start + 2, // First Slot
@@ -557,12 +572,12 @@ fn competing_slots() {
// Create n paras and owners
for n in 1..=max_bids {
- Balances::make_free_balance_be(&n, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(n), 1_000_000_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
- assert_ok!(Registrar::reserve(Origin::signed(n)));
+ assert_ok!(Registrar::reserve(signed(n)));
assert_ok!(Registrar::register(
- Origin::signed(n),
+ signed(n),
para_id + n - 1,
genesis_head,
validation_code,
@@ -581,7 +596,7 @@ fn competing_slots() {
// Increment block number
run_to_block(System::block_number() + 10);
- Balances::make_free_balance_be(&(n * 10), n * 1_000);
+ Balances::make_free_balance_be(&account_id(n * 10), n * 1_000);
let (start, end) = match n {
1 => (0, 0),
@@ -599,7 +614,7 @@ fn competing_slots() {
// Users will bid directly for parachain
assert_ok!(Auctions::bid(
- Origin::signed(n * 10),
+ signed(n * 10),
para_id + n - 1,
1, // Auction Index
lease_period_index_start + start, // First Slot
@@ -617,18 +632,26 @@ fn competing_slots() {
assert_eq!(
slots::Leases::::get(para_id),
// -- 1 --- 2 --- 3 ---------- 4 ------
- vec![None, None, None, Some((10, 900))],
+ vec![None, None, None, Some((account_id(10), 900))],
);
assert_eq!(
slots::Leases::::get(para_id + 4),
// -- 1 --- 2 --- 3 --- 4 ---------- 5 -------
- vec![None, None, None, None, Some((50, 4500))],
+ vec![None, None, None, None, Some((account_id(50), 4500))],
);
// TODO: Is this right?
assert_eq!(
slots::Leases::::get(para_id + 8),
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------- 6 --------------- 7 -------
- vec![None, None, None, None, None, Some((90, 8100)), Some((90, 8100))],
+ vec![
+ None,
+ None,
+ None,
+ None,
+ None,
+ Some((account_id(90), 8100)),
+ Some((account_id(90), 8100))
+ ],
);
});
}
@@ -642,12 +665,12 @@ fn competing_bids() {
let start_para = LOWEST_PUBLIC_ID - 1;
// Create 3 paras and owners
for n in 1..=3 {
- Balances::make_free_balance_be(&n, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(n), 1_000_000_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
- assert_ok!(Registrar::reserve(Origin::signed(n)));
+ assert_ok!(Registrar::reserve(signed(n)));
assert_ok!(Registrar::register(
- Origin::signed(n),
+ signed(n),
ParaId::from(start_para + n),
genesis_head,
validation_code,
@@ -666,7 +689,7 @@ fn competing_bids() {
for n in 1..=3 {
// Create a crowdloan for each para
assert_ok!(Crowdloan::create(
- Origin::signed(n),
+ signed(n),
ParaId::from(start_para + n),
100_000, // Cap
lease_period_index_start + 2, // First Slot
@@ -680,14 +703,14 @@ fn competing_bids() {
// Increment block number
run_to_block(starting_block + n * 10);
- Balances::make_free_balance_be(&(n * 10), n * 1_000);
+ Balances::make_free_balance_be(&account_id(n * 10), n * 1_000);
let para = start_para + n % 3 + 1;
if n % 2 == 0 {
// User 10 will bid directly for parachain 1
assert_ok!(Auctions::bid(
- Origin::signed(n * 10),
+ signed(n * 10),
ParaId::from(para),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -697,7 +720,7 @@ fn competing_bids() {
} else {
// User 20 will be a contribute to crowdloan for parachain 2
assert_ok!(Crowdloan::contribute(
- Origin::signed(n * 10),
+ signed(n * 10),
ParaId::from(para),
n + 900,
None,
@@ -709,7 +732,8 @@ fn competing_bids() {
run_to_block(starting_block + 110);
// Appropriate Paras should have won slots
- let crowdloan_2 = Crowdloan::fund_account_id(ParaId::from(2001));
+ let fund_1 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ let crowdloan_1 = Crowdloan::fund_account_id(fund_1.fund_index);
assert_eq!(
slots::Leases::::get(ParaId::from(2000)),
// -- 1 --- 2 --- 3 --- 4 --- 5 ------------- 6 ------------------------ 7 -------------
@@ -719,14 +743,14 @@ fn competing_bids() {
None,
None,
None,
- Some((crowdloan_2, 1812)),
- Some((crowdloan_2, 1812))
+ Some((crowdloan_1.clone(), 1812)),
+ Some((crowdloan_1.clone(), 1812))
],
);
assert_eq!(
slots::Leases::::get(ParaId::from(2002)),
// -- 1 --- 2 --- 3 ---------- 4 --------------- 5 -------
- vec![None, None, None, Some((80, 7200)), Some((80, 7200))],
+ vec![None, None, None, Some((account_id(80), 7200)), Some((account_id(80), 7200))],
);
});
}
@@ -735,21 +759,21 @@ fn competing_bids() {
fn basic_swap_works() {
// This test will test a swap between a parachain and parathread works successfully.
new_test_ext().execute_with(|| {
- assert!(System::block_number().is_one()); // So events are emitted
- // User 1 and 2 will own paras
- Balances::make_free_balance_be(&1, 1_000_000_000);
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ assert!(System::block_number().is_one()); /* So events are emitted */
+ // User 1 and 2 will own paras
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
// First register 2 parathreads with different data
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
test_genesis_head(10),
test_validation_code(10),
));
- assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::reserve(signed(2)));
assert_ok!(Registrar::register(
- Origin::signed(2),
+ signed(2),
ParaId::from(2001),
test_genesis_head(20),
test_validation_code(20),
@@ -771,7 +795,7 @@ fn basic_swap_works() {
// Open a crowdloan for Para 1 for slots 0-3
assert_ok!(Crowdloan::create(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1_000_000, // Cap
lease_period_index_start + 0, // First Slot
@@ -779,13 +803,14 @@ fn basic_swap_works() {
200, // Block End
None,
));
- let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+ let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
// Bunch of contributions
let mut total = 0;
for i in 10..20 {
- Balances::make_free_balance_be(&i, 1_000_000_000);
- assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+ Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
total += 900 - i;
}
assert!(total > 0);
@@ -796,8 +821,8 @@ fn basic_swap_works() {
// Deposit is appropriately taken
// ----------------------------------------- para deposit --- crowdloan
- assert_eq!(Balances::reserved_balance(&1), (500 + 10 * 2 * 1) + 100);
- assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+ assert_eq!(Balances::reserved_balance(&account_id(1)), (500 + 10 * 2 * 1) + 100);
+ assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
assert_eq!(Balances::reserved_balance(&crowdloan_account), total);
// Crowdloan is appropriately set
assert!(Crowdloan::funds(ParaId::from(2000)).is_some());
@@ -839,8 +864,8 @@ fn basic_swap_works() {
// Deregister parathread
assert_ok!(Registrar::deregister(para_origin(2000).into(), ParaId::from(2000)));
// Correct deposit is unreserved
- assert_eq!(Balances::reserved_balance(&1), 100); // crowdloan deposit left over
- assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+ assert_eq!(Balances::reserved_balance(&account_id(1)), 100); // crowdloan deposit left over
+ assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
// Crowdloan ownership is swapped
assert!(Crowdloan::funds(ParaId::from(2000)).is_none());
assert!(Crowdloan::funds(ParaId::from(2001)).is_some());
@@ -850,11 +875,11 @@ fn basic_swap_works() {
// Cant dissolve
assert_noop!(
- Crowdloan::dissolve(Origin::signed(1), ParaId::from(2000)),
+ Crowdloan::dissolve(signed(1), ParaId::from(2000)),
CrowdloanError::::InvalidParaId
);
assert_noop!(
- Crowdloan::dissolve(Origin::signed(2), ParaId::from(2001)),
+ Crowdloan::dissolve(signed(2), ParaId::from(2001)),
CrowdloanError::::NotReadyToDissolve
);
@@ -864,39 +889,198 @@ fn basic_swap_works() {
// Withdraw of contributions works
assert_eq!(Balances::free_balance(&crowdloan_account), total);
for i in 10..20 {
- assert_ok!(Crowdloan::withdraw(Origin::signed(i), i, ParaId::from(2001)));
+ assert_ok!(Crowdloan::withdraw(signed(i), account_id(i), ParaId::from(2001)));
}
assert_eq!(Balances::free_balance(&crowdloan_account), 0);
// Dissolve returns the balance of the person who put a deposit for crowdloan
- assert_ok!(Crowdloan::dissolve(Origin::signed(1), ParaId::from(2001)));
- assert_eq!(Balances::reserved_balance(&1), 0);
- assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
+ assert_ok!(Crowdloan::dissolve(signed(1), ParaId::from(2001)));
+ assert_eq!(Balances::reserved_balance(&account_id(1)), 0);
+ assert_eq!(Balances::reserved_balance(&account_id(2)), 500 + 20 * 2 * 1);
// Final deregister sets everything back to the start
assert_ok!(Registrar::deregister(para_origin(2001).into(), ParaId::from(2001)));
- assert_eq!(Balances::reserved_balance(&2), 0);
+ assert_eq!(Balances::reserved_balance(&account_id(2)), 0);
+ })
+}
+
+#[test]
+fn parachain_swap_works() {
+ // This test will test a swap between two parachains works successfully.
+ new_test_ext().execute_with(|| {
+ assert!(System::block_number().is_one()); /* So events are emitted */
+ // User 1 and 2 will own paras
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
+ // First register 2 parathreads with different data
+ assert_ok!(Registrar::reserve(signed(1)));
+ assert_ok!(Registrar::register(
+ signed(1),
+ ParaId::from(2000),
+ test_genesis_head(10),
+ test_validation_code(10),
+ ));
+ assert_ok!(Registrar::reserve(signed(2)));
+ assert_ok!(Registrar::register(
+ signed(2),
+ ParaId::from(2001),
+ test_genesis_head(20),
+ test_validation_code(20),
+ ));
+
+ // Paras should be onboarding
+ assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Onboarding));
+ assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Onboarding));
+
+ assert_eq!(
+ Balances::total_balance(&Crowdloan::fund_account_id(Crowdloan::next_fund_index())),
+ 0
+ );
+
+ // Start a new auction in the future
+ let start_auction = |lease_period_index_start, winner, end| {
+ let unique_id = winner - 1999u32;
+ let starting_block = System::block_number();
+ let duration = 99u32;
+ assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
+
+ // 2 sessions later they are parathreads
+ run_to_block(starting_block + 20);
+ assert_eq!(Paras::lifecycle(ParaId::from(winner)), Some(ParaLifecycle::Parathread));
+
+ // Open a crowdloan for Para 1 for slots 0-3
+ assert_ok!(Crowdloan::create(
+ signed(unique_id),
+ ParaId::from(winner),
+ 1_000_000, // Cap
+ lease_period_index_start + 0, // First Slot
+ lease_period_index_start + 7, // Last Slot
+ end, // Block End
+ None,
+ ));
+ let winner_fund = Crowdloan::funds(ParaId::from(winner)).unwrap();
+ let crowdloan_account = Crowdloan::fund_account_id(winner_fund.fund_index);
+
+ // Bunch of contributions
+ let mut total = 0;
+ for i in (unique_id * 10)..(unique_id + 1) * 10 {
+ Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(winner), 900 - i, None));
+ total += 900 - i;
+ }
+ assert!(total > 0);
+ assert_eq!(Balances::free_balance(&crowdloan_account), total);
+
+ // Go to end of auction where everyone won their slots
+ run_to_block(end);
+
+ // Crowdloan is appropriately set
+ assert!(Crowdloan::funds(ParaId::from(winner)).is_some());
+
+ // New leases will start on block lease period index * 100
+ let lease_start_block = lease_period_index_start * 100;
+ run_to_block(lease_start_block);
+ };
+
+ start_auction(4u32, 2000, 200);
+ // Slots are won by Para 1
+ assert!(!Slots::lease(ParaId::from(2000)).is_empty());
+ assert!(Slots::lease(ParaId::from(2001)).is_empty());
+
+ // 2 sessions later it is a parachain
+ run_to_block(4 * 100 + 20);
+ assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Parachain));
+ assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Parathread));
+
+ // Let's repeat the process now for another parachain.
+ start_auction(6u32, 2001, 500);
+ // Slots are won by Para 1
+ assert!(!Slots::lease(ParaId::from(2000)).is_empty());
+ assert!(!Slots::lease(ParaId::from(2001)).is_empty());
+
+ // 2 sessions later it is a parachain
+ run_to_block(6 * 100 + 20);
+ assert_eq!(Paras::lifecycle(ParaId::from(2000)), Some(ParaLifecycle::Parachain));
+ assert_eq!(Paras::lifecycle(ParaId::from(2001)), Some(ParaLifecycle::Parachain));
+
+ // Currently we are on lease 6
+ assert_eq!(
+ >::lease_period_index(System::block_number()),
+ Some((6u32, false))
+ );
+
+ // This means that parachain 1 should only have 6 slots left, and parachain 2 has all 8.
+ assert_eq!(slots::Leases::::get(ParaId::from(2000)).len(), 6);
+ assert_eq!(slots::Leases::::get(ParaId::from(2001)).len(), 8);
+
+ let fund_2000 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ assert_eq!(fund_2000.fund_index, 0);
+ assert_eq!(
+ Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2000.fund_index)),
+ fund_2000.raised
+ );
+
+ let fund_2001 = Crowdloan::funds(ParaId::from(2001)).unwrap();
+ assert_eq!(fund_2001.fund_index, 1);
+ assert_eq!(
+ Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2001.fund_index)),
+ fund_2001.raised
+ );
+
+ assert_eq!(Slots::lease(ParaId::from(2000)).len(), 6);
+ assert_eq!(Slots::lease(ParaId::from(2001)).len(), 8);
+
+ // Now we swap them.
+ assert_ok!(Registrar::swap(
+ para_origin(2000).into(),
+ ParaId::from(2000),
+ ParaId::from(2001)
+ ));
+ assert_ok!(Registrar::swap(
+ para_origin(2001).into(),
+ ParaId::from(2001),
+ ParaId::from(2000)
+ ));
+
+ // Crowdloan Swapped
+ let fund_2000 = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ assert_eq!(fund_2000.fund_index, 1);
+ assert_eq!(
+ Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2000.fund_index)),
+ fund_2000.raised
+ );
+
+ let fund_2001 = Crowdloan::funds(ParaId::from(2001)).unwrap();
+ assert_eq!(fund_2001.fund_index, 0);
+ assert_eq!(
+ Balances::reserved_balance(&Crowdloan::fund_account_id(fund_2001.fund_index)),
+ fund_2001.raised
+ );
+
+ // Slots Swapped
+ assert_eq!(Slots::lease(ParaId::from(2000)).len(), 8);
+ assert_eq!(Slots::lease(ParaId::from(2001)).len(), 6);
})
}
#[test]
fn crowdloan_ending_period_bid() {
new_test_ext().execute_with(|| {
- assert!(System::block_number().is_one()); // So events are emitted
- // User 1 and 2 will own paras
- Balances::make_free_balance_be(&1, 1_000_000_000);
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ assert!(System::block_number().is_one()); /* So events are emitted */
+ // User 1 and 2 will own paras
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
// First register 2 parathreads
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
test_genesis_head(10),
test_validation_code(10),
));
- assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::reserve(signed(2)));
assert_ok!(Registrar::register(
- Origin::signed(2),
+ signed(2),
ParaId::from(2001),
test_genesis_head(20),
test_validation_code(20),
@@ -918,7 +1102,7 @@ fn crowdloan_ending_period_bid() {
// Open a crowdloan for Para 1 for slots 0-3
assert_ok!(Crowdloan::create(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1_000_000, // Cap
lease_period_index_start + 0, // First Slot
@@ -926,22 +1110,23 @@ fn crowdloan_ending_period_bid() {
200, // Block End
None,
));
- let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+ let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
// Bunch of contributions
let mut total = 0;
for i in 10..20 {
- Balances::make_free_balance_be(&i, 1_000_000_000);
- assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+ Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
total += 900 - i;
}
assert!(total > 0);
assert_eq!(Balances::free_balance(&crowdloan_account), total);
// Bid for para 2 directly
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
assert_ok!(Auctions::bid(
- Origin::signed(2),
+ signed(2),
ParaId::from(2001),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -953,24 +1138,25 @@ fn crowdloan_ending_period_bid() {
run_to_block(100);
assert_eq!(Auctions::auction_status(100), AuctionStatus::::EndingPeriod(0, 0));
- let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
- winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
+ let mut winning = [(); SlotRange::SLOT_RANGE_COUNT].map(|_| None);
+
+ winning[SlotRange::ZeroOne as u8 as usize] = Some((account_id(2), ParaId::from(2001), 900));
winning[SlotRange::ZeroThree as u8 as usize] =
- Some((crowdloan_account, ParaId::from(2000), total));
+ Some((crowdloan_account.clone(), ParaId::from(2000), total));
assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(101);
- Balances::make_free_balance_be(&1234, 1_000_000_000);
- assert_ok!(Crowdloan::contribute(Origin::signed(1234), ParaId::from(2000), 900, None));
+ Balances::make_free_balance_be(&account_id(1234), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(1234), ParaId::from(2000), 900, None));
// Data propagates correctly
run_to_block(102);
- let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
- winning[SlotRange::ZeroOne as u8 as usize] = Some((2, ParaId::from(2001), 900));
+ let mut winning = [(); SlotRange::SLOT_RANGE_COUNT].map(|_| None);
+ winning[SlotRange::ZeroOne as u8 as usize] = Some((account_id(2), ParaId::from(2001), 900));
winning[SlotRange::ZeroThree as u8 as usize] =
- Some((crowdloan_account, ParaId::from(2000), total + 900));
+ Some((crowdloan_account.clone(), ParaId::from(2000), total + 900));
assert_eq!(Auctions::winning(2), Some(winning));
})
}
@@ -978,7 +1164,7 @@ fn crowdloan_ending_period_bid() {
#[test]
fn auction_bid_requires_registered_para() {
new_test_ext().execute_with(|| {
- assert!(System::block_number().is_one()); // So events are emitted
+ assert!(System::block_number().is_one()); /* So events are emitted */
// Start a new auction in the future
let duration = 99u32;
@@ -986,10 +1172,10 @@ fn auction_bid_requires_registered_para() {
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// Can't bid with non-registered paras
- Balances::make_free_balance_be(&1, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
assert_noop!(
Auctions::bid(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -1000,9 +1186,9 @@ fn auction_bid_requires_registered_para() {
);
// Now we register the para
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
test_genesis_head(10),
test_validation_code(10),
@@ -1011,7 +1197,7 @@ fn auction_bid_requires_registered_para() {
// Still can't bid until it is fully onboarded
assert_noop!(
Auctions::bid(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -1025,9 +1211,9 @@ fn auction_bid_requires_registered_para() {
run_to_session(2);
// Success
- Balances::make_free_balance_be(&1, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
assert_ok!(Auctions::bid(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -1040,26 +1226,26 @@ fn auction_bid_requires_registered_para() {
#[test]
fn gap_bids_work() {
new_test_ext().execute_with(|| {
- assert!(System::block_number().is_one()); // So events are emitted
+ assert!(System::block_number().is_one()); /* So events are emitted */
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
- Balances::make_free_balance_be(&1, 1_000_000_000);
- Balances::make_free_balance_be(&2, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(2), 1_000_000_000);
// Now register 2 paras
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
test_genesis_head(10),
test_validation_code(10),
));
- assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::reserve(signed(2)));
assert_ok!(Registrar::register(
- Origin::signed(2),
+ signed(2),
ParaId::from(2001),
test_genesis_head(10),
test_validation_code(10),
@@ -1069,11 +1255,11 @@ fn gap_bids_work() {
run_to_session(2);
// Make bids
- Balances::make_free_balance_be(&10, 1_000_000_000);
- Balances::make_free_balance_be(&20, 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(10), 1_000_000_000);
+ Balances::make_free_balance_be(&account_id(20), 1_000_000_000);
// Slot 1 for 100 from 10
assert_ok!(Auctions::bid(
- Origin::signed(10),
+ signed(10),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 0, // First Slot
@@ -1082,7 +1268,7 @@ fn gap_bids_work() {
));
// Slot 4 for 400 from 10
assert_ok!(Auctions::bid(
- Origin::signed(10),
+ signed(10),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 3, // First Slot
@@ -1092,18 +1278,18 @@ fn gap_bids_work() {
// A bid for another para is counted separately.
assert_ok!(Auctions::bid(
- Origin::signed(10),
+ signed(10),
ParaId::from(2001),
1, // Auction Index
lease_period_index_start + 1, // First Slot
lease_period_index_start + 1, // Last slot
555, // Amount
));
- assert_eq!(Balances::reserved_balance(&10), 400 + 555);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400 + 555);
// Slot 2 for 800 from 20, overtaking 10's bid
assert_ok!(Auctions::bid(
- Origin::signed(20),
+ signed(20),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 1, // First Slot
@@ -1112,7 +1298,7 @@ fn gap_bids_work() {
));
// Slot 3 for 200 from 20
assert_ok!(Auctions::bid(
- Origin::signed(20),
+ signed(20),
ParaId::from(2000),
1, // Auction Index
lease_period_index_start + 2, // First Slot
@@ -1131,16 +1317,16 @@ fn gap_bids_work() {
None,
None,
None,
- Some((10, 100)),
- Some((20, 800)),
- Some((20, 200)),
- Some((10, 400))
+ Some((account_id(10), 100)),
+ Some((account_id(20), 800)),
+ Some((account_id(20), 200)),
+ Some((account_id(10), 400))
],
);
// Appropriate amount is reserved (largest of the values)
- assert_eq!(Balances::reserved_balance(&10), 400);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
// Appropriate amount is reserved (largest of the values)
- assert_eq!(Balances::reserved_balance(&20), 800);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
// Progress through the leases and note the correct amount of balance is reserved.
@@ -1148,48 +1334,57 @@ fn gap_bids_work() {
assert_eq!(
slots::Leases::::get(ParaId::from(2000)),
// --------- 4 -------------- 5 -------------- 6 -------------- 7 -------
- vec![Some((10, 100)), Some((20, 800)), Some((20, 200)), Some((10, 400))],
+ vec![
+ Some((account_id(10), 100)),
+ Some((account_id(20), 800)),
+ Some((account_id(20), 200)),
+ Some((account_id(10), 400))
+ ],
);
// Nothing changed.
- assert_eq!(Balances::reserved_balance(&10), 400);
- assert_eq!(Balances::reserved_balance(&20), 800);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
// Lease period 4 is done, but nothing is unreserved since user 1 has a debt on lease 7
run_to_block(500);
assert_eq!(
slots::Leases::::get(ParaId::from(2000)),
// --------- 5 -------------- 6 -------------- 7 -------
- vec![Some((20, 800)), Some((20, 200)), Some((10, 400))],
+ vec![
+ Some((account_id(20), 800)),
+ Some((account_id(20), 200)),
+ Some((account_id(10), 400))
+ ],
);
// Nothing changed.
- assert_eq!(Balances::reserved_balance(&10), 400);
- assert_eq!(Balances::reserved_balance(&20), 800);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 800);
// Lease period 5 is done, and 20 will unreserve down to 200.
run_to_block(600);
assert_eq!(
slots::Leases::::get(ParaId::from(2000)),
// --------- 6 -------------- 7 -------
- vec![Some((20, 200)), Some((10, 400))],
+ vec![Some((account_id(20), 200)), Some((account_id(10), 400))],
);
- assert_eq!(Balances::reserved_balance(&10), 400);
- assert_eq!(Balances::reserved_balance(&20), 200);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 200);
// Lease period 6 is done, and 20 will unreserve everything.
run_to_block(700);
assert_eq!(
slots::Leases::::get(ParaId::from(2000)),
// --------- 7 -------
- vec![Some((10, 400))],
+ vec![Some((account_id(10), 400))],
);
- assert_eq!(Balances::reserved_balance(&10), 400);
- assert_eq!(Balances::reserved_balance(&20), 0);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 400);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 0);
// All leases are done. Everything is unreserved.
run_to_block(800);
assert_eq!(slots::Leases::::get(ParaId::from(2000)), vec![]);
- assert_eq!(Balances::reserved_balance(&10), 0);
- assert_eq!(Balances::reserved_balance(&20), 0);
+ assert_eq!(Balances::reserved_balance(&account_id(10)), 0);
+ assert_eq!(Balances::reserved_balance(&account_id(20)), 0);
});
}
@@ -1198,12 +1393,12 @@ fn gap_bids_work() {
#[test]
fn cant_bid_on_existing_lease_periods() {
new_test_ext().execute_with(|| {
- assert!(System::block_number().is_one()); // So events are emitted
- Balances::make_free_balance_be(&1, 1_000_000_000);
+ assert!(System::block_number().is_one()); /* So events are emitted */
+ Balances::make_free_balance_be(&account_id(1), 1_000_000_000);
// First register a parathread
- assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(signed(1)));
assert_ok!(Registrar::register(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
test_genesis_head(10),
test_validation_code(10),
@@ -1220,7 +1415,7 @@ fn cant_bid_on_existing_lease_periods() {
// Open a crowdloan for Para 1 for slots 0-3
assert_ok!(Crowdloan::create(
- Origin::signed(1),
+ signed(1),
ParaId::from(2000),
1_000_000, // Cap
lease_period_index_start + 0, // First Slot
@@ -1228,13 +1423,14 @@ fn cant_bid_on_existing_lease_periods() {
400, // Long block end
None,
));
- let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(2000));
+ let fund = Crowdloan::funds(ParaId::from(2000)).unwrap();
+ let crowdloan_account = Crowdloan::fund_account_id(fund.fund_index);
// Bunch of contributions
let mut total = 0;
for i in 10..20 {
- Balances::make_free_balance_be(&i, 1_000_000_000);
- assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(2000), 900 - i, None));
+ Balances::make_free_balance_be(&account_id(i), 1_000_000_000);
+ assert_ok!(Crowdloan::contribute(signed(i), ParaId::from(2000), 900 - i, None));
total += 900 - i;
}
assert!(total > 0);
@@ -1251,8 +1447,8 @@ fn cant_bid_on_existing_lease_periods() {
None,
None,
None,
- Some((crowdloan_account, 8855)),
- Some((crowdloan_account, 8855))
+ Some((crowdloan_account.clone(), 8855)),
+ Some((crowdloan_account.clone(), 8855))
],
);
@@ -1263,7 +1459,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// Poke the crowdloan into `NewRaise`
- assert_ok!(Crowdloan::poke(Origin::signed(1), ParaId::from(2000)));
+ assert_ok!(Crowdloan::poke(signed(1), ParaId::from(2000)));
assert_eq!(Crowdloan::new_raise(), vec![ParaId::from(2000)]);
// Beginning of ending block.
@@ -1272,7 +1468,7 @@ fn cant_bid_on_existing_lease_periods() {
// Bids cannot be made which intersect
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start + 0,
@@ -1284,7 +1480,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start + 1,
@@ -1296,7 +1492,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start - 1,
@@ -1308,7 +1504,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start + 0,
@@ -1320,7 +1516,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start + 1,
@@ -1332,7 +1528,7 @@ fn cant_bid_on_existing_lease_periods() {
assert_noop!(
Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start - 1,
@@ -1344,7 +1540,7 @@ fn cant_bid_on_existing_lease_periods() {
// Will work when not overlapping
assert_ok!(Auctions::bid(
- Origin::signed(crowdloan_account),
+ Origin::signed(crowdloan_account.clone()),
ParaId::from(2000),
2,
lease_period_index_start + 2,
diff --git a/runtime/common/src/paras_registrar.rs b/runtime/common/src/paras_registrar.rs
index 3946e7d9aec5..88828283ceee 100644
--- a/runtime/common/src/paras_registrar.rs
+++ b/runtime/common/src/paras_registrar.rs
@@ -160,6 +160,9 @@ pub mod pallet {
NotReserved,
/// Registering parachain with empty code is not allowed.
EmptyCode,
+ /// Cannot perform a parachain slot / lifecycle swap. Check that the state of both paras are
+ /// correct for the swap to work.
+ CannotSwap,
}
/// Pending swap operations.
@@ -271,31 +274,40 @@ pub mod pallet {
pub fn swap(origin: OriginFor, id: ParaId, other: ParaId) -> DispatchResult {
Self::ensure_root_para_or_owner(origin, id)?;
+ // If `id` and `other` is the same id, we treat this as a "clear" function, and exit
+ // early, since swapping the same id would otherwise be a noop.
+ if id == other {
+ PendingSwap::::remove(id);
+ return Ok(())
+ }
+
+ // Sanity check that `id` is even a para.
+ let id_lifecycle =
+ paras::Pallet::::lifecycle(id).ok_or(Error::::NotRegistered)?;
+
if PendingSwap::::get(other) == Some(id) {
- if let Some(other_lifecycle) = paras::Pallet::::lifecycle(other) {
- if let Some(id_lifecycle) = paras::Pallet::::lifecycle(id) {
- // identify which is a parachain and which is a parathread
- if id_lifecycle.is_parachain() && other_lifecycle.is_parathread() {
- // We check that both paras are in an appropriate lifecycle for a swap,
- // so these should never fail.
- let res1 = runtime_parachains::schedule_parachain_downgrade::(id);
- debug_assert!(res1.is_ok());
- let res2 = runtime_parachains::schedule_parathread_upgrade::(other);
- debug_assert!(res2.is_ok());
- T::OnSwap::on_swap(id, other);
- } else if id_lifecycle.is_parathread() && other_lifecycle.is_parachain() {
- // We check that both paras are in an appropriate lifecycle for a swap,
- // so these should never fail.
- let res1 = runtime_parachains::schedule_parachain_downgrade::(other);
- debug_assert!(res1.is_ok());
- let res2 = runtime_parachains::schedule_parathread_upgrade::(id);
- debug_assert!(res2.is_ok());
- T::OnSwap::on_swap(id, other);
- }
-
- PendingSwap::::remove(other);
- }
+ let other_lifecycle =
+ paras::Pallet::::lifecycle(other).ok_or(Error::::NotRegistered)?;
+ // identify which is a parachain and which is a parathread
+ if id_lifecycle == ParaLifecycle::Parachain &&
+ other_lifecycle == ParaLifecycle::Parathread
+ {
+ Self::do_thread_and_chain_swap(id, other);
+ } else if id_lifecycle == ParaLifecycle::Parathread &&
+ other_lifecycle == ParaLifecycle::Parachain
+ {
+ Self::do_thread_and_chain_swap(other, id);
+ } else if id_lifecycle == ParaLifecycle::Parachain &&
+ other_lifecycle == ParaLifecycle::Parachain
+ {
+ // If both chains are currently parachains, there is nothing funny we
+ // need to do for their lifecycle management, just swap the underlying
+ // data.
+ T::OnSwap::on_swap(id, other);
+ } else {
+ return Err(Error::::CannotSwap.into())
}
+ PendingSwap::::remove(other);
} else {
PendingSwap::::insert(id, other);
}
@@ -564,6 +576,15 @@ impl Pallet {
Ok((ParaGenesisArgs { genesis_head, validation_code, parachain }, deposit))
}
+
+ /// Swap a parachain and parathread, which involves scheduling an appropriate lifecycle update.
+ fn do_thread_and_chain_swap(to_downgrade: ParaId, to_upgrade: ParaId) {
+ let res1 = runtime_parachains::schedule_parachain_downgrade::(to_downgrade);
+ debug_assert!(res1.is_ok());
+ let res2 = runtime_parachains::schedule_parathread_upgrade::(to_upgrade);
+ debug_assert!(res2.is_ok());
+ T::OnSwap::on_swap(to_upgrade, to_downgrade);
+ }
}
#[cfg(test)]
@@ -587,6 +608,7 @@ mod tests {
transaction_validity::TransactionPriority,
Perbill,
};
+ use sp_std::collections::btree_map::BTreeMap;
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic;
type Block = frame_system::mocking::MockBlock;
@@ -696,7 +718,7 @@ mod tests {
type Event = Event;
type Origin = Origin;
type Currency = Balances;
- type OnSwap = ();
+ type OnSwap = MockSwap;
type ParaDeposit = ParaDeposit;
type DataDepositPerByte = DataDepositPerByte;
type WeightInfo = TestWeightInfo;
@@ -724,6 +746,22 @@ mod tests {
t.into()
}
+ parameter_types! {
+ pub static SwapData: BTreeMap = BTreeMap::new();
+ }
+
+ pub struct MockSwap;
+ impl OnSwap for MockSwap {
+ fn on_swap(one: ParaId, other: ParaId) {
+ let mut swap_data = SwapData::get();
+ let one_data = swap_data.remove(&one).unwrap_or_default();
+ let other_data = swap_data.remove(&other).unwrap_or_default();
+ swap_data.insert(one, other_data);
+ swap_data.insert(other, one_data);
+ SwapData::set(swap_data);
+ }
+ }
+
const BLOCKS_PER_SESSION: u32 = 3;
fn run_to_block(n: BlockNumber) {
@@ -997,9 +1035,15 @@ mod tests {
));
run_to_session(2);
- // Upgrade 1023 into a parachain
+ // Upgrade para 1 into a parachain
assert_ok!(Registrar::make_parachain(para_1));
+ // Set some mock swap data.
+ let mut swap_data = SwapData::get();
+ swap_data.insert(para_1, 69);
+ swap_data.insert(para_2, 1337);
+ SwapData::set(swap_data);
+
run_to_session(4);
// Roles are as we expect
@@ -1014,20 +1058,15 @@ mod tests {
run_to_session(6);
- // Deregister a parathread that was originally a parachain
- assert_eq!(Parachains::lifecycle(para_1), Some(ParaLifecycle::Parathread));
- assert_ok!(Registrar::deregister(
- runtime_parachains::Origin::Parachain(para_1).into(),
- para_1
- ));
-
- run_to_block(21);
-
// Roles are swapped
assert!(!Parachains::is_parachain(para_1));
assert!(Parachains::is_parathread(para_1));
assert!(Parachains::is_parachain(para_2));
assert!(!Parachains::is_parathread(para_2));
+
+ // Data is swapped
+ assert_eq!(SwapData::get().get(¶_1).unwrap(), &1337);
+ assert_eq!(SwapData::get().get(¶_2).unwrap(), &69);
});
}
@@ -1059,6 +1098,121 @@ mod tests {
assert_noop!(Registrar::swap(Origin::signed(1), para_id, para_id + 2), BadOrigin);
});
}
+
+ #[test]
+ fn swap_handles_bad_states() {
+ new_test_ext().execute_with(|| {
+ let para_1 = LOWEST_PUBLIC_ID;
+ let para_2 = LOWEST_PUBLIC_ID + 1;
+ run_to_block(1);
+ // paras are not yet registered
+ assert!(!Parachains::is_parathread(para_1));
+ assert!(!Parachains::is_parathread(para_2));
+
+ // Cannot even start a swap
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_1, para_2),
+ Error::::NotRegistered
+ );
+
+ // We register Paras 1 and 2
+ assert_ok!(Registrar::reserve(Origin::signed(1)));
+ assert_ok!(Registrar::reserve(Origin::signed(2)));
+ assert_ok!(Registrar::register(
+ Origin::signed(1),
+ para_1,
+ test_genesis_head(32),
+ test_validation_code(32),
+ ));
+ assert_ok!(Registrar::register(
+ Origin::signed(2),
+ para_2,
+ test_genesis_head(32),
+ test_validation_code(32),
+ ));
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ run_to_session(2);
+
+ // They are now a parathread.
+ assert!(Parachains::is_parathread(para_1));
+ assert!(Parachains::is_parathread(para_2));
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ // Some other external process will elevate one parathread to parachain
+ assert_ok!(Registrar::make_parachain(para_1));
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ run_to_session(3);
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ run_to_session(4);
+
+ // It is now a parachain.
+ assert!(Parachains::is_parachain(para_1));
+ assert!(Parachains::is_parathread(para_2));
+
+ // Swap works here.
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_ok!(Registrar::swap(Origin::root(), para_2, para_1));
+
+ run_to_session(5);
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ run_to_session(6);
+
+ // Swap worked!
+ assert!(Parachains::is_parachain(para_2));
+ assert!(Parachains::is_parathread(para_1));
+
+ // Something starts to downgrade a para
+ assert_ok!(Registrar::make_parathread(para_2));
+
+ run_to_session(7);
+
+ // Cannot swap
+ assert_ok!(Registrar::swap(Origin::root(), para_1, para_2));
+ assert_noop!(
+ Registrar::swap(Origin::root(), para_2, para_1),
+ Error::::CannotSwap
+ );
+
+ run_to_session(8);
+
+ assert!(Parachains::is_parathread(para_1));
+ assert!(Parachains::is_parathread(para_2));
+ });
+ }
}
#[cfg(feature = "runtime-benchmarks")]
diff --git a/runtime/kusama/src/lib.rs b/runtime/kusama/src/lib.rs
index 249b43f778fb..42c8d5137585 100644
--- a/runtime/kusama/src/lib.rs
+++ b/runtime/kusama/src/lib.rs
@@ -138,11 +138,11 @@ pub fn native_version() -> NativeVersion {
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
}
-/// Don't allow swaps until parathread story is more mature.
+/// We currently allow all calls.
pub struct BaseFilter;
impl Contains for BaseFilter {
- fn contains(c: &Call) -> bool {
- !matches!(c, Call::Registrar(paras_registrar::Call::swap { .. }))
+ fn contains(_c: &Call) -> bool {
+ true
}
}
@@ -1497,7 +1497,7 @@ pub type Executive = frame_executive::Executive<
frame_system::ChainContext,
Runtime,
AllPalletsWithSystem,
- (SchedulerMigrationV3, RefundNickPalletDeposit),
+ (SchedulerMigrationV3, RefundNickPalletDeposit, CrowdloanIndexMigration),
>;
/// The payload being signed in the transactions.
pub type SignedPayload = generic::SignedPayload;
@@ -2929,6 +2929,24 @@ impl OnRuntimeUpgrade for SchedulerMigrationV3 {
}
}
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+ fn on_runtime_upgrade() -> frame_support::weights::Weight {
+ crowdloan::migration::crowdloan_index_migration::migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn pre_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::pre_migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn post_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::post_migrate::()
+ }
+}
+
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
pub struct SessionHistoricalPalletPrefixMigration;
diff --git a/runtime/polkadot/src/lib.rs b/runtime/polkadot/src/lib.rs
index 09d74520ed28..a1c5175bf300 100644
--- a/runtime/polkadot/src/lib.rs
+++ b/runtime/polkadot/src/lib.rs
@@ -1446,11 +1446,29 @@ pub type Executive = frame_executive::Executive<
frame_system::ChainContext,
Runtime,
AllPalletsWithSystem,
- (SchedulerMigrationV3, FixCouncilDepositMigration),
+ (SchedulerMigrationV3, FixCouncilDepositMigration, CrowdloanIndexMigration),
>;
/// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload;
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+ fn on_runtime_upgrade() -> frame_support::weights::Weight {
+ crowdloan::migration::crowdloan_index_migration::migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn pre_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::pre_migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn post_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::post_migrate::()
+ }
+}
+
/// A migration struct to fix some deposits in the council election pallet.
///
/// See more details here: https://github.com/paritytech/polkadot/issues/4160
diff --git a/runtime/rococo/src/lib.rs b/runtime/rococo/src/lib.rs
index fa65abcc23ce..021d66c37418 100644
--- a/runtime/rococo/src/lib.rs
+++ b/runtime/rococo/src/lib.rs
@@ -154,11 +154,29 @@ pub type Executive = frame_executive::Executive<
frame_system::ChainContext,
Runtime,
AllPalletsWithSystem,
- (SessionHistoricalModulePrefixMigration,),
+ (SessionHistoricalModulePrefixMigration, CrowdloanIndexMigration),
>;
/// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload;
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+ fn on_runtime_upgrade() -> frame_support::weights::Weight {
+ crowdloan::migration::crowdloan_index_migration::migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn pre_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::pre_migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn post_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::post_migrate::()
+ }
+}
+
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
pub struct SessionHistoricalModulePrefixMigration;
diff --git a/runtime/westend/src/lib.rs b/runtime/westend/src/lib.rs
index eb5af15c4af5..dc19bdd05bc8 100644
--- a/runtime/westend/src/lib.rs
+++ b/runtime/westend/src/lib.rs
@@ -1084,11 +1084,29 @@ pub type Executive = frame_executive::Executive<
frame_system::ChainContext,
Runtime,
AllPalletsWithSystem,
- (SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3),
+ (SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3, CrowdloanIndexMigration),
>;
/// The payload being signed in transactions.
pub type SignedPayload = generic::SignedPayload;
+// Migration for crowdloan pallet to use fund index for account generation.
+pub struct CrowdloanIndexMigration;
+impl OnRuntimeUpgrade for CrowdloanIndexMigration {
+ fn on_runtime_upgrade() -> frame_support::weights::Weight {
+ crowdloan::migration::crowdloan_index_migration::migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn pre_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::pre_migrate::()
+ }
+
+ #[cfg(feature = "try-runtime")]
+ fn post_upgrade() -> Result<(), &'static str> {
+ crowdloan::migration::crowdloan_index_migration::post_migrate::()
+ }
+}
+
// Migration for scheduler pallet to move from a plain Call to a CallOrHash.
pub struct SchedulerMigrationV3;