mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
Allow two Parachains to swap (#4772)
* add support for parachain to parachain swap * enable swaps on kusama * sanity test in paras_registrar * express more errors * finish up tests * fmt * make fields pub * refactor integration tests to use real accounts * Update Crowdloan Account to FundIndex (#4824) * update fund account to use index * fix integration tests * Update runtime/common/src/crowdloan.rs * finish parachain swap test * format * fix warning * fix spacing * fix formatting * write migrations * add migration * fixes * more fixes to migration * Update runtime/common/src/crowdloan/mod.rs Co-authored-by: Zeke Mostov <z.mostov@gmail.com> * Update runtime/common/src/paras_registrar.rs * Update migration.rs * extract swap function Co-authored-by: Zeke Mostov <z.mostov@gmail.com>
This commit is contained in:
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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<T: Config>(index: ParaId) -> T::AccountId {
|
||||||
|
T::PalletId::get().into_sub_account(index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pre_migrate<T: Config>() -> Result<(), &'static str> {
|
||||||
|
// `NextTrieIndex` should have a value.
|
||||||
|
generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
|
||||||
|
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::<T>::iter() {
|
||||||
|
let old_fund_account = old_fund_account_id::<T>(para_id);
|
||||||
|
let total_balance = CurrencyOf::<T>::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<T: Config>() -> frame_support::weights::Weight {
|
||||||
|
let mut weight = 0;
|
||||||
|
|
||||||
|
// First migrate `NextTrieIndex` counter to `NextFundIndex`.
|
||||||
|
generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
|
||||||
|
|
||||||
|
let next_index = NextTrieIndex::take().unwrap_or_default();
|
||||||
|
NextFundIndex::<T>::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::<T>::iter() {
|
||||||
|
let old_fund_account = old_fund_account_id::<T>(para_id);
|
||||||
|
let new_fund_account = Pallet::<T>::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::<T>::take(old_fund_account);
|
||||||
|
frame_system::Account::<T>::insert(new_fund_account, account_info);
|
||||||
|
|
||||||
|
weight = weight.saturating_add(T::DbWeight::get().reads_writes(1, 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
weight
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_migrate<T: Config>() -> Result<(), &'static str> {
|
||||||
|
// `NextTrieIndex` should not have a value, and `NextFundIndex` should.
|
||||||
|
generate_storage_alias!(Crowdloan, NextTrieIndex => Value<FundIndex>);
|
||||||
|
ensure!(NextTrieIndex::get().is_none(), "NextTrieIndex still has a value.");
|
||||||
|
|
||||||
|
let next_index = NextFundIndex::<T>::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::<T>::iter() {
|
||||||
|
// Old fund account is deleted.
|
||||||
|
let old_fund_account = old_fund_account_id::<T>(para_id);
|
||||||
|
ensure!(
|
||||||
|
frame_system::Account::<T>::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::<T>::fund_account_id(fund.fund_index);
|
||||||
|
let total_balance = CurrencyOf::<T>::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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
-53
@@ -49,6 +49,8 @@
|
|||||||
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
|
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
|
||||||
//! returned to the crowdloan account.
|
//! returned to the crowdloan account.
|
||||||
|
|
||||||
|
pub mod migration;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
slot_range::SlotRange,
|
slot_range::SlotRange,
|
||||||
traits::{Auctioneer, Registrar},
|
traits::{Auctioneer, Registrar},
|
||||||
@@ -87,7 +89,7 @@ type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::Acco
|
|||||||
type NegativeImbalanceOf<T> =
|
type NegativeImbalanceOf<T> =
|
||||||
<CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
<CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::NegativeImbalance;
|
||||||
|
|
||||||
type TrieIndex = u32;
|
type FundIndex = u32;
|
||||||
|
|
||||||
pub trait WeightInfo {
|
pub trait WeightInfo {
|
||||||
fn create() -> Weight;
|
fn create() -> Weight;
|
||||||
@@ -145,33 +147,33 @@ pub enum LastContribution<BlockNumber> {
|
|||||||
#[codec(dumb_trait_bound)]
|
#[codec(dumb_trait_bound)]
|
||||||
pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
|
pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
|
||||||
/// The owning account who placed the deposit.
|
/// The owning account who placed the deposit.
|
||||||
depositor: AccountId,
|
pub depositor: AccountId,
|
||||||
/// An optional verifier. If exists, contributions must be signed by verifier.
|
/// An optional verifier. If exists, contributions must be signed by verifier.
|
||||||
verifier: Option<MultiSigner>,
|
pub verifier: Option<MultiSigner>,
|
||||||
/// The amount of deposit placed.
|
/// The amount of deposit placed.
|
||||||
deposit: Balance,
|
pub deposit: Balance,
|
||||||
/// The total amount raised.
|
/// The total amount raised.
|
||||||
raised: Balance,
|
pub raised: Balance,
|
||||||
/// Block number after which the funding must have succeeded. If not successful at this number
|
/// Block number after which the funding must have succeeded. If not successful at this number
|
||||||
/// then everyone may withdraw their funds.
|
/// then everyone may withdraw their funds.
|
||||||
end: BlockNumber,
|
pub end: BlockNumber,
|
||||||
/// A hard-cap on the amount that may be contributed.
|
/// 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.
|
/// 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 `Never`, this fund has never received a contribution.
|
||||||
/// If this is `PreEnding(n)`, this fund received a contribution sometime in auction
|
/// If this is `PreEnding(n)`, this fund received a contribution sometime in auction
|
||||||
/// number `n` before the ending period.
|
/// number `n` before the ending period.
|
||||||
/// If this is `Ending(n)`, this fund received a contribution during the current 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.
|
/// where `n` is how far into the ending period the contribution was made.
|
||||||
last_contribution: LastContribution<BlockNumber>,
|
pub last_contribution: LastContribution<BlockNumber>,
|
||||||
/// First lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
|
/// First lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
|
||||||
/// as `BlockNumber`.
|
/// 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
|
/// Last lease period in range to bid on; it's actually a `LeasePeriod`, but that's the same type
|
||||||
/// as `BlockNumber`.
|
/// as `BlockNumber`.
|
||||||
last_period: LeasePeriod,
|
pub last_period: LeasePeriod,
|
||||||
/// Index used for the child trie of this fund
|
/// Unique index used to represent this fund.
|
||||||
trie_index: TrieIndex,
|
pub fund_index: FundIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[frame_support::pallet]
|
#[frame_support::pallet]
|
||||||
@@ -244,10 +246,10 @@ pub mod pallet {
|
|||||||
#[pallet::getter(fn endings_count)]
|
#[pallet::getter(fn endings_count)]
|
||||||
pub(super) type EndingsCount<T> = StorageValue<_, u32, ValueQuery>;
|
pub(super) type EndingsCount<T> = StorageValue<_, u32, ValueQuery>;
|
||||||
|
|
||||||
/// Tracker for the next available trie index
|
/// Tracker for the next available fund index
|
||||||
#[pallet::storage]
|
#[pallet::storage]
|
||||||
#[pallet::getter(fn next_trie_index)]
|
#[pallet::getter(fn next_fund_index)]
|
||||||
pub(super) type NextTrieIndex<T> = StorageValue<_, u32, ValueQuery>;
|
pub(super) type NextFundIndex<T> = StorageValue<_, u32, ValueQuery>;
|
||||||
|
|
||||||
#[pallet::event]
|
#[pallet::event]
|
||||||
#[pallet::generate_deposit(pub(super) fn deposit_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
|
// 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`.
|
// the crowdloaning configuration. We do some checks ahead of time in crowdloan `create`.
|
||||||
let result = T::Auctioneer::place_bid(
|
let result = T::Auctioneer::place_bid(
|
||||||
Self::fund_account_id(para_id),
|
Self::fund_account_id(fund.fund_index),
|
||||||
para_id,
|
para_id,
|
||||||
fund.first_period,
|
fund.first_period,
|
||||||
fund.last_period,
|
fund.last_period,
|
||||||
@@ -408,8 +410,8 @@ pub mod pallet {
|
|||||||
ensure!(depositor == manager, Error::<T>::InvalidOrigin);
|
ensure!(depositor == manager, Error::<T>::InvalidOrigin);
|
||||||
ensure!(T::Registrar::is_registered(index), Error::<T>::InvalidParaId);
|
ensure!(T::Registrar::is_registered(index), Error::<T>::InvalidParaId);
|
||||||
|
|
||||||
let trie_index = Self::next_trie_index();
|
let fund_index = Self::next_fund_index();
|
||||||
let new_trie_index = trie_index.checked_add(1).ok_or(Error::<T>::Overflow)?;
|
let new_fund_index = fund_index.checked_add(1).ok_or(Error::<T>::Overflow)?;
|
||||||
|
|
||||||
let deposit = T::SubmissionDeposit::get();
|
let deposit = T::SubmissionDeposit::get();
|
||||||
|
|
||||||
@@ -427,11 +429,11 @@ pub mod pallet {
|
|||||||
last_contribution: LastContribution::Never,
|
last_contribution: LastContribution::Never,
|
||||||
first_period,
|
first_period,
|
||||||
last_period,
|
last_period,
|
||||||
trie_index,
|
fund_index,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
NextTrieIndex::<T>::put(new_trie_index);
|
NextFundIndex::<T>::put(new_fund_index);
|
||||||
// Add a lock to the para so that the configuration cannot be changed.
|
// Add a lock to the para so that the configuration cannot be changed.
|
||||||
T::Registrar::apply_lock(index);
|
T::Registrar::apply_lock(index);
|
||||||
|
|
||||||
@@ -479,15 +481,15 @@ pub mod pallet {
|
|||||||
|
|
||||||
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
||||||
let now = frame_system::Pallet::<T>::block_number();
|
let now = frame_system::Pallet::<T>::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)?;
|
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::<T>::NoContributions);
|
ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
|
||||||
|
|
||||||
CurrencyOf::<T>::transfer(&fund_account, &who, balance, AllowDeath)?;
|
CurrencyOf::<T>::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);
|
fund.raised = fund.raised.saturating_sub(balance);
|
||||||
|
|
||||||
Funds::<T>::insert(index, &fund);
|
Funds::<T>::insert(index, &fund);
|
||||||
@@ -510,12 +512,12 @@ pub mod pallet {
|
|||||||
|
|
||||||
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
let mut fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
||||||
let now = frame_system::Pallet::<T>::block_number();
|
let now = frame_system::Pallet::<T>::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)?;
|
Self::ensure_crowdloan_ended(now, &fund_account, &fund)?;
|
||||||
|
|
||||||
let mut refund_count = 0u32;
|
let mut refund_count = 0u32;
|
||||||
// Try killing the crowdloan child trie
|
// 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.
|
// Assume everyone will be refunded.
|
||||||
let mut all_refunded = true;
|
let mut all_refunded = true;
|
||||||
for (who, (balance, _)) in contributions {
|
for (who, (balance, _)) in contributions {
|
||||||
@@ -525,7 +527,7 @@ pub mod pallet {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
CurrencyOf::<T>::transfer(&fund_account, &who, balance, AllowDeath)?;
|
CurrencyOf::<T>::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);
|
fund.raised = fund.raised.saturating_sub(balance);
|
||||||
refund_count += 1;
|
refund_count += 1;
|
||||||
}
|
}
|
||||||
@@ -561,7 +563,7 @@ pub mod pallet {
|
|||||||
// Assuming state is not corrupted, the child trie should already be cleaned up
|
// 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
|
// and all funds in the crowdloan account have been returned. If not, governance
|
||||||
// can take care of that.
|
// 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::<T>::unreserve(&fund.depositor, fund.deposit);
|
CurrencyOf::<T>::unreserve(&fund.depositor, fund.deposit);
|
||||||
Funds::<T>::remove(index);
|
Funds::<T>::remove(index);
|
||||||
@@ -598,7 +600,7 @@ pub mod pallet {
|
|||||||
last_contribution: fund.last_contribution,
|
last_contribution: fund.last_contribution,
|
||||||
first_period,
|
first_period,
|
||||||
last_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::<T>::MemoTooLarge);
|
ensure!(memo.len() <= T::MaxMemoLength::get().into(), Error::<T>::MemoTooLarge);
|
||||||
let fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
let fund = Self::funds(index).ok_or(Error::<T>::InvalidParaId)?;
|
||||||
|
|
||||||
let (balance, _) = Self::contribution_get(fund.trie_index, &who);
|
let (balance, _) = Self::contribution_get(fund.fund_index, &who);
|
||||||
ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
|
ensure!(balance > Zero::zero(), Error::<T>::NoContributions);
|
||||||
|
|
||||||
Self::contribution_put(fund.trie_index, &who, &balance, &memo);
|
Self::contribution_put(fund.fund_index, &who, &balance, &memo);
|
||||||
Self::deposit_event(Event::<T>::MemoUpdated(who, index, memo));
|
Self::deposit_event(Event::<T>::MemoUpdated(who, index, memo));
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -658,11 +660,11 @@ impl<T: Config> Pallet<T> {
|
|||||||
///
|
///
|
||||||
/// This actually does computation. If you need to keep using it, then make sure you cache the
|
/// This actually does computation. If you need to keep using it, then make sure you cache the
|
||||||
/// value and only call this once.
|
/// 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)
|
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();
|
let mut buf = Vec::new();
|
||||||
buf.extend_from_slice(b"crowdloan");
|
buf.extend_from_slice(b"crowdloan");
|
||||||
buf.extend_from_slice(&index.encode()[..]);
|
buf.extend_from_slice(&index.encode()[..]);
|
||||||
@@ -670,7 +672,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn contribution_put(
|
pub fn contribution_put(
|
||||||
index: TrieIndex,
|
index: FundIndex,
|
||||||
who: &T::AccountId,
|
who: &T::AccountId,
|
||||||
balance: &BalanceOf<T>,
|
balance: &BalanceOf<T>,
|
||||||
memo: &[u8],
|
memo: &[u8],
|
||||||
@@ -678,22 +680,22 @@ impl<T: Config> Pallet<T> {
|
|||||||
who.using_encoded(|b| child::put(&Self::id_from_index(index), b, &(balance, memo)));
|
who.using_encoded(|b| child::put(&Self::id_from_index(index), b, &(balance, memo)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contribution_get(index: TrieIndex, who: &T::AccountId) -> (BalanceOf<T>, Vec<u8>) {
|
pub fn contribution_get(index: FundIndex, who: &T::AccountId) -> (BalanceOf<T>, Vec<u8>) {
|
||||||
who.using_encoded(|b| {
|
who.using_encoded(|b| {
|
||||||
child::get_or_default::<(BalanceOf<T>, Vec<u8>)>(&Self::id_from_index(index), b)
|
child::get_or_default::<(BalanceOf<T>, Vec<u8>)>(&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));
|
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()))
|
child::kill_storage(&Self::id_from_index(index), Some(T::RemoveKeysLimit::get()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contribution_iterator(
|
pub fn contribution_iterator(
|
||||||
index: TrieIndex,
|
index: FundIndex,
|
||||||
) -> ChildTriePrefixIterator<(T::AccountId, (BalanceOf<T>, Vec<u8>))> {
|
) -> ChildTriePrefixIterator<(T::AccountId, (BalanceOf<T>, Vec<u8>))> {
|
||||||
ChildTriePrefixIterator::<_>::with_prefix_over_key::<Identity>(
|
ChildTriePrefixIterator::<_>::with_prefix_over_key::<Identity>(
|
||||||
&Self::id_from_index(index),
|
&Self::id_from_index(index),
|
||||||
@@ -752,7 +754,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
|
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
|
||||||
|
|
||||||
// Make sure crowdloan has not already won.
|
// 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!(
|
ensure!(
|
||||||
!T::Auctioneer::has_won_an_auction(index, &fund_account),
|
!T::Auctioneer::has_won_an_auction(index, &fund_account),
|
||||||
Error::<T>::BidOrLeaseActive
|
Error::<T>::BidOrLeaseActive
|
||||||
@@ -762,7 +764,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
// contributions into the auction when it would not impact the outcome.
|
// contributions into the auction when it would not impact the outcome.
|
||||||
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::VrfDelayInProgress);
|
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::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 {
|
if let Some(ref verifier) = fund.verifier {
|
||||||
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
|
let signature = signature.ok_or(Error::<T>::InvalidSignature)?;
|
||||||
@@ -776,7 +778,7 @@ impl<T: Config> Pallet<T> {
|
|||||||
CurrencyOf::<T>::transfer(&who, &fund_account, value, existence)?;
|
CurrencyOf::<T>::transfer(&who, &fund_account, value, existence)?;
|
||||||
|
|
||||||
let balance = old_balance.saturating_add(value);
|
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() {
|
if T::Auctioneer::auction_status(now).is_ending().is_some() {
|
||||||
match fund.last_contribution {
|
match fund.last_contribution {
|
||||||
@@ -966,7 +968,8 @@ mod tests {
|
|||||||
// Emulate what would happen if we won an auction:
|
// Emulate what would happen if we won an auction:
|
||||||
// balance is reserved and a deposit_held is recorded
|
// balance is reserved and a deposit_held is recorded
|
||||||
fn set_winner(para: ParaId, who: u64, winner: bool) {
|
fn set_winner(para: ParaId, who: u64, winner: bool) {
|
||||||
let account_id = Crowdloan::fund_account_id(para);
|
let fund = Funds::<Test>::get(para).unwrap();
|
||||||
|
let account_id = Crowdloan::fund_account_id(fund.fund_index);
|
||||||
if winner {
|
if winner {
|
||||||
let free_balance = Balances::free_balance(&account_id);
|
let free_balance = Balances::free_balance(&account_id);
|
||||||
Balances::reserve(&account_id, free_balance)
|
Balances::reserve(&account_id, free_balance)
|
||||||
@@ -1177,7 +1180,7 @@ mod tests {
|
|||||||
last_contribution: LastContribution::Never,
|
last_contribution: LastContribution::Never,
|
||||||
first_period: 1,
|
first_period: 1,
|
||||||
last_period: 4,
|
last_period: 4,
|
||||||
trie_index: 0,
|
fund_index: 0,
|
||||||
};
|
};
|
||||||
assert_eq!(Crowdloan::funds(para), Some(fund_info));
|
assert_eq!(Crowdloan::funds(para), Some(fund_info));
|
||||||
// User has deposit removed from their free balance
|
// User has deposit removed from their free balance
|
||||||
@@ -1217,7 +1220,7 @@ mod tests {
|
|||||||
last_contribution: LastContribution::Never,
|
last_contribution: LastContribution::Never,
|
||||||
first_period: 1,
|
first_period: 1,
|
||||||
last_period: 4,
|
last_period: 4,
|
||||||
trie_index: 0,
|
fund_index: 0,
|
||||||
};
|
};
|
||||||
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
|
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
|
||||||
// User has deposit removed from their free balance
|
// User has deposit removed from their free balance
|
||||||
@@ -1270,6 +1273,7 @@ mod tests {
|
|||||||
fn contribute_works() {
|
fn contribute_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
|
||||||
// Set up a crowdloan
|
// Set up a crowdloan
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 9, None));
|
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
|
// Contributions are stored in the trie
|
||||||
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 49);
|
assert_eq!(Crowdloan::contribution_get(u32::from(para), &1).0, 49);
|
||||||
// Contributions appear in free balance of crowdloan
|
// 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
|
// Crowdloan is added to NewRaise
|
||||||
assert_eq!(Crowdloan::new_raise(), vec![para]);
|
assert_eq!(Crowdloan::new_raise(), vec![para]);
|
||||||
|
|
||||||
@@ -1300,6 +1304,7 @@ mod tests {
|
|||||||
fn contribute_with_verifier_works() {
|
fn contribute_with_verifier_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
||||||
// Set up a crowdloan
|
// Set up a crowdloan
|
||||||
assert_ok!(Crowdloan::create(
|
assert_ok!(Crowdloan::create(
|
||||||
@@ -1364,7 +1369,7 @@ mod tests {
|
|||||||
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 10, Some(valid_signature_2)));
|
assert_ok!(Crowdloan::contribute(Origin::signed(1), para, 10, Some(valid_signature_2)));
|
||||||
|
|
||||||
// Contributions appear in free balance of crowdloan
|
// 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
|
// Contribution amount is correct
|
||||||
let fund = Crowdloan::funds(para).unwrap();
|
let fund = Crowdloan::funds(para).unwrap();
|
||||||
@@ -1409,9 +1414,10 @@ mod tests {
|
|||||||
|
|
||||||
// If a crowdloan has already won, it should not allow contributions.
|
// If a crowdloan has already won, it should not allow contributions.
|
||||||
let para_2 = new_para();
|
let para_2 = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para_2, 1000, 1, 4, 40, None));
|
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.
|
// 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);
|
set_winner(para_2, crowdloan_account, true);
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
Crowdloan::contribute(Origin::signed(1), para_2, 49, None),
|
Crowdloan::contribute(Origin::signed(1), para_2, 49, None),
|
||||||
@@ -1478,6 +1484,7 @@ mod tests {
|
|||||||
fn bidding_works() {
|
fn bidding_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
let first_period = 1;
|
let first_period = 1;
|
||||||
let last_period = 4;
|
let last_period = 4;
|
||||||
|
|
||||||
@@ -1493,7 +1500,7 @@ mod tests {
|
|||||||
9,
|
9,
|
||||||
None
|
None
|
||||||
));
|
));
|
||||||
let bidder = Crowdloan::fund_account_id(para);
|
let bidder = Crowdloan::fund_account_id(index);
|
||||||
|
|
||||||
// Fund crowdloan
|
// Fund crowdloan
|
||||||
run_to_block(1);
|
run_to_block(1);
|
||||||
@@ -1524,6 +1531,7 @@ mod tests {
|
|||||||
fn withdraw_from_failed_works() {
|
fn withdraw_from_failed_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
|
||||||
// Set up a crowdloan
|
// Set up a crowdloan
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
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));
|
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
|
||||||
|
|
||||||
run_to_block(10);
|
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.
|
// para has no reserved funds, indicating it did not win the auction.
|
||||||
assert_eq!(Balances::reserved_balance(&account_id), 0);
|
assert_eq!(Balances::reserved_balance(&account_id), 0);
|
||||||
// but there's still the funds in its balance.
|
// but there's still the funds in its balance.
|
||||||
@@ -1553,13 +1561,14 @@ mod tests {
|
|||||||
fn withdraw_cannot_be_griefed() {
|
fn withdraw_cannot_be_griefed() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
|
||||||
// Set up a crowdloan
|
// Set up a crowdloan
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
||||||
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
|
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
|
||||||
|
|
||||||
run_to_block(10);
|
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
|
// user sends the crowdloan funds trying to make an accounting error
|
||||||
assert_ok!(Balances::transfer(Origin::signed(1), account_id, 10));
|
assert_ok!(Balances::transfer(Origin::signed(1), account_id, 10));
|
||||||
@@ -1583,7 +1592,8 @@ mod tests {
|
|||||||
fn refund_works() {
|
fn refund_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
let account_id = Crowdloan::fund_account_id(para);
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
let account_id = Crowdloan::fund_account_id(index);
|
||||||
|
|
||||||
// Set up a crowdloan ending on 9
|
// Set up a crowdloan ending on 9
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
||||||
@@ -1617,7 +1627,8 @@ mod tests {
|
|||||||
fn multiple_refund_works() {
|
fn multiple_refund_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
let account_id = Crowdloan::fund_account_id(para);
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
let account_id = Crowdloan::fund_account_id(index);
|
||||||
|
|
||||||
// Set up a crowdloan ending on 9
|
// Set up a crowdloan ending on 9
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
|
assert_ok!(Crowdloan::create(Origin::signed(1), para, 100000, 1, 1, 9, None));
|
||||||
@@ -1727,7 +1738,8 @@ mod tests {
|
|||||||
fn withdraw_from_finished_works() {
|
fn withdraw_from_finished_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
let para = new_para();
|
let para = new_para();
|
||||||
let account_id = Crowdloan::fund_account_id(para);
|
let index = NextFundIndex::<Test>::get();
|
||||||
|
let account_id = Crowdloan::fund_account_id(index);
|
||||||
|
|
||||||
// Set up a crowdloan
|
// Set up a crowdloan
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 1, 9, None));
|
||||||
@@ -2079,7 +2091,7 @@ mod benchmarking {
|
|||||||
verify {
|
verify {
|
||||||
let fund = Funds::<T>::get(fund_index).expect("fund was created...");
|
let fund = Funds::<T>::get(fund_index).expect("fund was created...");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Crowdloan::<T>::contribution_get(fund.trie_index, &caller),
|
Crowdloan::<T>::contribution_get(fund.fund_index, &caller),
|
||||||
(T::MinContribution::get(), worst_memo),
|
(T::MinContribution::get(), worst_memo),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -160,6 +160,9 @@ pub mod pallet {
|
|||||||
NotReserved,
|
NotReserved,
|
||||||
/// Registering parachain with empty code is not allowed.
|
/// Registering parachain with empty code is not allowed.
|
||||||
EmptyCode,
|
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.
|
/// Pending swap operations.
|
||||||
@@ -271,31 +274,40 @@ pub mod pallet {
|
|||||||
pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
|
pub fn swap(origin: OriginFor<T>, id: ParaId, other: ParaId) -> DispatchResult {
|
||||||
Self::ensure_root_para_or_owner(origin, id)?;
|
Self::ensure_root_para_or_owner(origin, id)?;
|
||||||
|
|
||||||
if PendingSwap::<T>::get(other) == Some(id) {
|
// If `id` and `other` is the same id, we treat this as a "clear" function, and exit
|
||||||
if let Some(other_lifecycle) = paras::Pallet::<T>::lifecycle(other) {
|
// early, since swapping the same id would otherwise be a noop.
|
||||||
if let Some(id_lifecycle) = paras::Pallet::<T>::lifecycle(id) {
|
if id == other {
|
||||||
// identify which is a parachain and which is a parathread
|
PendingSwap::<T>::remove(id);
|
||||||
if id_lifecycle.is_parachain() && other_lifecycle.is_parathread() {
|
return Ok(())
|
||||||
// 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::<T>(id);
|
|
||||||
debug_assert!(res1.is_ok());
|
|
||||||
let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(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::<T>(other);
|
|
||||||
debug_assert!(res1.is_ok());
|
|
||||||
let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(id);
|
|
||||||
debug_assert!(res2.is_ok());
|
|
||||||
T::OnSwap::on_swap(id, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sanity check that `id` is even a para.
|
||||||
|
let id_lifecycle =
|
||||||
|
paras::Pallet::<T>::lifecycle(id).ok_or(Error::<T>::NotRegistered)?;
|
||||||
|
|
||||||
|
if PendingSwap::<T>::get(other) == Some(id) {
|
||||||
|
let other_lifecycle =
|
||||||
|
paras::Pallet::<T>::lifecycle(other).ok_or(Error::<T>::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::<T>::CannotSwap.into())
|
||||||
|
}
|
||||||
PendingSwap::<T>::remove(other);
|
PendingSwap::<T>::remove(other);
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
PendingSwap::<T>::insert(id, other);
|
PendingSwap::<T>::insert(id, other);
|
||||||
}
|
}
|
||||||
@@ -564,6 +576,15 @@ impl<T: Config> Pallet<T> {
|
|||||||
|
|
||||||
Ok((ParaGenesisArgs { genesis_head, validation_code, parachain }, deposit))
|
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::<T>(to_downgrade);
|
||||||
|
debug_assert!(res1.is_ok());
|
||||||
|
let res2 = runtime_parachains::schedule_parathread_upgrade::<T>(to_upgrade);
|
||||||
|
debug_assert!(res2.is_ok());
|
||||||
|
T::OnSwap::on_swap(to_upgrade, to_downgrade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -587,6 +608,7 @@ mod tests {
|
|||||||
transaction_validity::TransactionPriority,
|
transaction_validity::TransactionPriority,
|
||||||
Perbill,
|
Perbill,
|
||||||
};
|
};
|
||||||
|
use sp_std::collections::btree_map::BTreeMap;
|
||||||
|
|
||||||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
|
||||||
type Block = frame_system::mocking::MockBlock<Test>;
|
type Block = frame_system::mocking::MockBlock<Test>;
|
||||||
@@ -696,7 +718,7 @@ mod tests {
|
|||||||
type Event = Event;
|
type Event = Event;
|
||||||
type Origin = Origin;
|
type Origin = Origin;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type OnSwap = ();
|
type OnSwap = MockSwap;
|
||||||
type ParaDeposit = ParaDeposit;
|
type ParaDeposit = ParaDeposit;
|
||||||
type DataDepositPerByte = DataDepositPerByte;
|
type DataDepositPerByte = DataDepositPerByte;
|
||||||
type WeightInfo = TestWeightInfo;
|
type WeightInfo = TestWeightInfo;
|
||||||
@@ -724,6 +746,22 @@ mod tests {
|
|||||||
t.into()
|
t.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub static SwapData: BTreeMap<ParaId, u64> = 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;
|
const BLOCKS_PER_SESSION: u32 = 3;
|
||||||
|
|
||||||
fn run_to_block(n: BlockNumber) {
|
fn run_to_block(n: BlockNumber) {
|
||||||
@@ -997,9 +1035,15 @@ mod tests {
|
|||||||
));
|
));
|
||||||
run_to_session(2);
|
run_to_session(2);
|
||||||
|
|
||||||
// Upgrade 1023 into a parachain
|
// Upgrade para 1 into a parachain
|
||||||
assert_ok!(Registrar::make_parachain(para_1));
|
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);
|
run_to_session(4);
|
||||||
|
|
||||||
// Roles are as we expect
|
// Roles are as we expect
|
||||||
@@ -1014,20 +1058,15 @@ mod tests {
|
|||||||
|
|
||||||
run_to_session(6);
|
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
|
// Roles are swapped
|
||||||
assert!(!Parachains::is_parachain(para_1));
|
assert!(!Parachains::is_parachain(para_1));
|
||||||
assert!(Parachains::is_parathread(para_1));
|
assert!(Parachains::is_parathread(para_1));
|
||||||
assert!(Parachains::is_parachain(para_2));
|
assert!(Parachains::is_parachain(para_2));
|
||||||
assert!(!Parachains::is_parathread(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);
|
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::<Test>::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::<Test>::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::<Test>::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::<Test>::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::<Test>::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::<Test>::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::<Test>::CannotSwap
|
||||||
|
);
|
||||||
|
|
||||||
|
run_to_session(8);
|
||||||
|
|
||||||
|
assert!(Parachains::is_parathread(para_1));
|
||||||
|
assert!(Parachains::is_parathread(para_2));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
|||||||
@@ -138,11 +138,11 @@ pub fn native_version() -> NativeVersion {
|
|||||||
NativeVersion { runtime_version: VERSION, can_author_with: Default::default() }
|
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;
|
pub struct BaseFilter;
|
||||||
impl Contains<Call> for BaseFilter {
|
impl Contains<Call> for BaseFilter {
|
||||||
fn contains(c: &Call) -> bool {
|
fn contains(_c: &Call) -> bool {
|
||||||
!matches!(c, Call::Registrar(paras_registrar::Call::swap { .. }))
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1497,7 +1497,7 @@ pub type Executive = frame_executive::Executive<
|
|||||||
frame_system::ChainContext<Runtime>,
|
frame_system::ChainContext<Runtime>,
|
||||||
Runtime,
|
Runtime,
|
||||||
AllPalletsWithSystem,
|
AllPalletsWithSystem,
|
||||||
(SchedulerMigrationV3, RefundNickPalletDeposit),
|
(SchedulerMigrationV3, RefundNickPalletDeposit, CrowdloanIndexMigration),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in the transactions.
|
/// The payload being signed in the transactions.
|
||||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||||
@@ -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::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn pre_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
|
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
|
||||||
pub struct SessionHistoricalPalletPrefixMigration;
|
pub struct SessionHistoricalPalletPrefixMigration;
|
||||||
|
|
||||||
|
|||||||
@@ -1446,11 +1446,29 @@ pub type Executive = frame_executive::Executive<
|
|||||||
frame_system::ChainContext<Runtime>,
|
frame_system::ChainContext<Runtime>,
|
||||||
Runtime,
|
Runtime,
|
||||||
AllPalletsWithSystem,
|
AllPalletsWithSystem,
|
||||||
(SchedulerMigrationV3, FixCouncilDepositMigration),
|
(SchedulerMigrationV3, FixCouncilDepositMigration, CrowdloanIndexMigration),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in transactions.
|
/// The payload being signed in transactions.
|
||||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||||
|
|
||||||
|
// 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::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn pre_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A migration struct to fix some deposits in the council election pallet.
|
/// A migration struct to fix some deposits in the council election pallet.
|
||||||
///
|
///
|
||||||
/// See more details here: https://github.com/paritytech/polkadot/issues/4160
|
/// See more details here: https://github.com/paritytech/polkadot/issues/4160
|
||||||
|
|||||||
@@ -154,11 +154,29 @@ pub type Executive = frame_executive::Executive<
|
|||||||
frame_system::ChainContext<Runtime>,
|
frame_system::ChainContext<Runtime>,
|
||||||
Runtime,
|
Runtime,
|
||||||
AllPalletsWithSystem,
|
AllPalletsWithSystem,
|
||||||
(SessionHistoricalModulePrefixMigration,),
|
(SessionHistoricalModulePrefixMigration, CrowdloanIndexMigration),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in transactions.
|
/// The payload being signed in transactions.
|
||||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||||
|
|
||||||
|
// 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::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn pre_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
|
/// Migrate session-historical from `Session` to the new pallet prefix `Historical`
|
||||||
pub struct SessionHistoricalModulePrefixMigration;
|
pub struct SessionHistoricalModulePrefixMigration;
|
||||||
|
|
||||||
|
|||||||
@@ -1084,11 +1084,29 @@ pub type Executive = frame_executive::Executive<
|
|||||||
frame_system::ChainContext<Runtime>,
|
frame_system::ChainContext<Runtime>,
|
||||||
Runtime,
|
Runtime,
|
||||||
AllPalletsWithSystem,
|
AllPalletsWithSystem,
|
||||||
(SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3),
|
(SessionHistoricalPalletPrefixMigration, SchedulerMigrationV3, CrowdloanIndexMigration),
|
||||||
>;
|
>;
|
||||||
/// The payload being signed in transactions.
|
/// The payload being signed in transactions.
|
||||||
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
pub type SignedPayload = generic::SignedPayload<Call, SignedExtra>;
|
||||||
|
|
||||||
|
// 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::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn pre_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::pre_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "try-runtime")]
|
||||||
|
fn post_upgrade() -> Result<(), &'static str> {
|
||||||
|
crowdloan::migration::crowdloan_index_migration::post_migrate::<Runtime>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Migration for scheduler pallet to move from a plain Call to a CallOrHash.
|
// Migration for scheduler pallet to move from a plain Call to a CallOrHash.
|
||||||
pub struct SchedulerMigrationV3;
|
pub struct SchedulerMigrationV3;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user