Add Many Sanity Checks to Crowdloans (#2745)

* Add many sanity checks to Crowdloans

* fix tests

* test contribution block in integration test

* fix rococo build

* remove leaser from crowdloans

* fix docs and terms

* fix compile
This commit is contained in:
Shawn Tabrizi
2021-03-29 16:52:55 +02:00
committed by GitHub
parent 7c21dbbdf4
commit 76db00f398
4 changed files with 148 additions and 90 deletions
+9 -1
View File
@@ -54,7 +54,7 @@ pub trait Config: frame_system::Config {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>;
/// The number of blocks over which a single period lasts.
/// The type representing the leasing system.
type Leaser: Leaser<AccountId=Self::AccountId, LeasePeriod=Self::BlockNumber>;
/// The parachain registrar type.
@@ -332,6 +332,14 @@ impl<T: Config> Auctioneer for Module<T> {
fn lease_period_index() -> Self::LeasePeriod {
T::Leaser::lease_period_index()
}
fn lease_period() -> Self::LeasePeriod {
T::Leaser::lease_period()
}
fn has_won_an_auction(para: ParaId, bidder: &T::AccountId) -> bool {
!T::Leaser::deposit_held(para, bidder).is_zero()
}
}
impl<T: Config> Module<T> {
+127 -87
View File
@@ -53,18 +53,9 @@
//! contains all expected functionality. However, this is not enforced and deploy data may happen
//! at any point, even after a slot has been successfully won or, indeed, never.
//!
//! Funds that are successful winners of a slot may have their slot claimed through the `onboard`
//! call. This may only be done once and must be after the deploy data has been fixed. Successful
//! funds remain tracked (in the `Funds` storage item and the associated child trie) as long as
//! the parachain remains active. Once it does not, it is up to the parachain to ensure that the
//! funds are returned to this module's fund sub-account in order that they be redistributed back to
//! contributors. *Retirement* may be initiated by any account (using the `begin_retirement` call)
//! once the parachain is removed from the its slot.
//!
//! @WARNING: For funds to be returned, it is imperative that this module's account is provided as
//! the offboarding account for the slot. In the case that a parachain supplemented these funds in
//! order to win a later auction, then it is the parachain's duty to ensure that the right amount of
//! funds ultimately end up in module's fund sub-account.
//! Successful funds remain tracked (in the `Funds` storage item and the associated child trie) as long as
//! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
//! returned to the crowdloan account.
use frame_support::{
decl_module, decl_storage, decl_event, decl_error, ensure,
@@ -78,7 +69,7 @@ use frame_system::{ensure_signed, ensure_root};
use sp_runtime::{
ModuleId, DispatchResult, RuntimeDebug, MultiSignature, MultiSigner,
traits::{
AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded, Verify, IdentifyAccount,
AccountIdConversion, Hash, Saturating, Zero, One, CheckedAdd, Bounded, Verify, IdentifyAccount,
},
};
use crate::traits::{Registrar, Auctioneer};
@@ -170,7 +161,7 @@ pub enum LastContribution<BlockNumber> {
#[codec(dumb_trait_bound)]
pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
/// True if the fund is being retired. This can only be set once and only when the current
/// lease period is greater than the `last_slot`.
/// lease period is greater than the `last_period`.
retiring: bool,
/// The owning account who placed the deposit.
depositor: AccountId,
@@ -192,12 +183,12 @@ pub struct FundInfo<AccountId, Balance, BlockNumber, LeasePeriod> {
/// 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<BlockNumber>,
/// First slot in range to bid on; it's actually a LeasePeriod, but that's the same type as
/// BlockNumber.
first_slot: LeasePeriod,
/// Last slot in range to bid on; it's actually a LeasePeriod, but that's the same type as
/// BlockNumber.
last_slot: LeasePeriod,
/// First lease period in range to bid on; it's actually a LeasePeriod, but that's the same type
/// as BlockNumber.
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,
}
@@ -254,14 +245,18 @@ decl_event! {
decl_error! {
pub enum Error for Module<T: Config> {
/// The first slot needs to at least be less than 3 `max_value`.
FirstSlotTooFarInFuture,
/// Last slot must be greater than first slot.
LastSlotBeforeFirstSlot,
/// The last slot cannot be more then 3 slots after the first slot.
LastSlotTooFarInFuture,
/// The current lease period is more than the first lease period.
FirstPeriodInPast,
/// The first lease period needs to at least be less than 3 `max_value`.
FirstPeriodTooFarInFuture,
/// Last lease period must be greater than first lease period.
LastPeriodBeforeFirstPeriod,
/// The last lease period cannot be more then 3 periods after the first period.
LastPeriodTooFarInFuture,
/// The campaign ends before the current block number. The end must be in the future.
CannotEndInPast,
/// The end date for this crowdloan is not sensible.
EndTooFarInFuture,
/// There was an overflow.
Overflow,
/// The contribution was below the minimum, `MinContribution`.
@@ -310,22 +305,25 @@ decl_module! {
fn deposit_event() = default;
/// Create a new crowdloaning campaign for a parachain slot deposit for the current auction.
/// Create a new crowdloaning campaign for a parachain slot with the given lease period range.
#[weight = T::WeightInfo::create()]
pub fn create(origin,
#[compact] index: ParaId,
#[compact] cap: BalanceOf<T>,
#[compact] first_slot: LeasePeriodOf<T>,
#[compact] last_slot: LeasePeriodOf<T>,
#[compact] first_period: LeasePeriodOf<T>,
#[compact] last_period: LeasePeriodOf<T>,
#[compact] end: T::BlockNumber,
verifier: Option<MultiSigner>,
) {
let depositor = ensure_signed(origin)?;
ensure!(first_slot <= last_slot, Error::<T>::LastSlotBeforeFirstSlot);
let last_slot_limit = first_slot.checked_add(&3u32.into()).ok_or(Error::<T>::FirstSlotTooFarInFuture)?;
ensure!(last_slot <= last_slot_limit, Error::<T>::LastSlotTooFarInFuture);
ensure!(first_period <= last_period, Error::<T>::LastPeriodBeforeFirstPeriod);
let last_period_limit = first_period.checked_add(&3u32.into()).ok_or(Error::<T>::FirstPeriodTooFarInFuture)?;
ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture);
ensure!(end > <frame_system::Pallet<T>>::block_number(), Error::<T>::CannotEndInPast);
let last_possible_win_date = (first_period.saturating_add(One::one())).saturating_mul(T::Auctioneer::lease_period());
ensure!(end <= last_possible_win_date, Error::<T>::EndTooFarInFuture);
ensure!(first_period >= T::Auctioneer::lease_period_index(), Error::<T>::FirstPeriodInPast);
// There should not be an existing fund.
ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded);
@@ -349,8 +347,8 @@ decl_module! {
end,
cap,
last_contribution: LastContribution::Never,
first_slot,
last_slot,
first_period,
last_period,
trie_index,
});
@@ -379,6 +377,14 @@ decl_module! {
let now = <frame_system::Pallet<T>>::block_number();
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver);
// Make sure crowdloan is in a valid lease period
let current_lease_period = T::Auctioneer::lease_period_index();
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
// Make sure crowdloan has not already won.
let fund_account = Self::fund_account_id(index);
ensure!(!T::Auctioneer::has_won_an_auction(index, &fund_account), Error::<T>::BidOrLeaseActive);
let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);
if let Some(ref verifier) = fund.verifier {
@@ -388,7 +394,7 @@ decl_module! {
ensure!(valid, Error::<T>::InvalidSignature);
}
CurrencyOf::<T>::transfer(&who, &Self::fund_account_id(index), value, AllowDeath)?;
CurrencyOf::<T>::transfer(&who, &fund_account, value, AllowDeath)?;
let balance = old_balance.saturating_add(value);
Self::contribution_put(fund.trie_index, &who, &balance, &memo);
@@ -436,7 +442,7 @@ decl_module! {
/// - the amount of raised funds must be bigger than the _free_ balance of the account;
/// - and either:
/// - the block number must be at least `end`; or
/// - the current lease period must be greater than the fund's `last_slot`.
/// - the current lease period must be greater than the fund's `last_period`.
///
/// In this case, the fund's retirement flag is set and its `end` is reset to the current block
/// number.
@@ -514,8 +520,8 @@ decl_module! {
pub fn edit(origin,
#[compact] index: ParaId,
#[compact] cap: BalanceOf<T>,
#[compact] first_slot: LeasePeriodOf<T>,
#[compact] last_slot: LeasePeriodOf<T>,
#[compact] first_period: LeasePeriodOf<T>,
#[compact] last_period: LeasePeriodOf<T>,
#[compact] end: T::BlockNumber,
verifier: Option<MultiSigner>,
) {
@@ -532,8 +538,8 @@ decl_module! {
end,
cap,
last_contribution: fund.last_contribution,
first_slot,
last_slot,
first_period,
last_period,
trie_index: fund.trie_index,
});
@@ -571,8 +577,8 @@ decl_module! {
let result = T::Auctioneer::place_bid(
Self::fund_account_id(para_id),
para_id,
fund.first_slot,
fund.last_slot,
fund.first_period,
fund.last_period,
fund.raised,
);
@@ -630,11 +636,11 @@ impl<T: Config> Module<T> {
fund_account: &T::AccountId,
fund: &FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>
) -> DispatchResult {
// `fund.end` can represent the end of a failed crowdsale or the beginning of retirement
// If the current lease period is past the first slot they are trying to bid for, then it is already too late
// to win the bid.
// `fund.end` can represent the end of a failed crowdloan or the beginning of retirement
// If the current lease period is past the first period they are trying to bid for, then
// it is already too late to win the bid.
let current_lease_period = T::Auctioneer::lease_period_index();
ensure!(now >= fund.end || current_lease_period > fund.first_slot, Error::<T>::FundNotEnded);
ensure!(now >= fund.end || current_lease_period > fund.first_period, Error::<T>::FundNotEnded);
// free balance must greater than or equal amount raised, otherwise funds are being used
// and a bid or lease must be active.
ensure!(CurrencyOf::<T>::free_balance(&fund_account) >= fund.raised, Error::<T>::BidOrLeaseActive);
@@ -678,7 +684,7 @@ mod crypto {
mod tests {
use super::*;
use std::{cell::RefCell, sync::Arc};
use std::{cell::RefCell, sync::Arc, collections::BTreeMap};
use frame_support::{
assert_ok, assert_noop, parameter_types,
traits::{OnInitialize, OnFinalize},
@@ -716,6 +722,8 @@ mod tests {
pub const BlockHashCount: u32 = 250;
}
type BlockNumber = u64;
impl frame_system::Config for Test {
type BaseCallFilter = ();
type BlockWeights = ();
@@ -724,7 +732,7 @@ mod tests {
type Origin = Origin;
type Call = Call;
type Index = u64;
type BlockNumber = u64;
type BlockNumber = BlockNumber;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = u64;
@@ -760,15 +768,17 @@ mod tests {
height: u64,
bidder: u64,
para: ParaId,
first_slot: u64,
last_slot: u64,
first_period: u64,
last_period: u64,
amount: u64
}
thread_local! {
static AUCTION: RefCell<Option<(u64, u64)>> = RefCell::new(None);
static ENDING_PERIOD: RefCell<u64> = RefCell::new(5);
static BIDS_PLACED: RefCell<Vec<BidPlaced>> = RefCell::new(Vec::new());
static HAS_WON: RefCell<BTreeMap<(ParaId, u64), bool>> = RefCell::new(BTreeMap::new());
}
#[allow(unused)]
fn set_ending_period(ending_period: u64) {
ENDING_PERIOD.with(|p| *p.borrow_mut() = ending_period);
@@ -782,11 +792,14 @@ mod tests {
fn bids() -> Vec<BidPlaced> {
BIDS_PLACED.with(|p| p.borrow().clone())
}
fn make_winner(para: ParaId, who: u64) {
HAS_WON.with(|p| p.borrow_mut().insert((para, who), true));
}
pub struct TestAuctioneer;
impl Auctioneer for TestAuctioneer {
type AccountId = u64;
type BlockNumber = u64;
type BlockNumber = BlockNumber;
type LeasePeriod = u64;
type Currency = Balances;
@@ -812,17 +825,25 @@ mod tests {
fn place_bid(
bidder: u64,
para: ParaId,
first_slot: u64,
last_slot: u64,
first_period: u64,
last_period: u64,
amount: u64
) -> DispatchResult {
let height = System::block_number();
BIDS_PLACED.with(|p| p.borrow_mut().push(BidPlaced { height, bidder, para, first_slot, last_slot, amount }));
BIDS_PLACED.with(|p| p.borrow_mut().push(BidPlaced { height, bidder, para, first_period, last_period, amount }));
Ok(())
}
fn lease_period_index() -> u64 {
System::block_number() / 20
System::block_number() / Self::lease_period()
}
fn lease_period() -> u64 {
20
}
fn has_won_an_auction(para: ParaId, bidder: &u64) -> bool {
HAS_WON.with(|p| *p.borrow().get(&(para, *bidder)).unwrap_or(&false))
}
}
@@ -900,7 +921,7 @@ mod tests {
assert_eq!(bids(), vec![]);
assert_ok!(TestAuctioneer::place_bid(1, 2.into(), 0, 3, 6));
let b = BidPlaced { height: 0, bidder: 1, para: 2.into(), first_slot: 0, last_slot: 3, amount: 6 };
let b = BidPlaced { height: 0, bidder: 1, para: 2.into(), first_period: 0, last_period: 3, amount: 6 };
assert_eq!(bids(), vec![b]);
assert_eq!(TestAuctioneer::is_ending(4), None);
assert_eq!(TestAuctioneer::is_ending(5), Some(0));
@@ -926,8 +947,8 @@ mod tests {
end: 9,
cap: 1000,
last_contribution: LastContribution::Never,
first_slot: 1,
last_slot: 4,
first_period: 1,
last_period: 4,
trie_index: 0,
};
assert_eq!(Crowdloan::funds(para), Some(fund_info));
@@ -959,8 +980,8 @@ mod tests {
end: 9,
cap: 1000,
last_contribution: LastContribution::Never,
first_slot: 1,
last_slot: 4,
first_period: 1,
last_period: 4,
trie_index: 0,
};
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
@@ -982,16 +1003,20 @@ mod tests {
let e = Error::<Test>::InvalidParaId;
assert_noop!(Crowdloan::create(Origin::signed(1), 1.into(), 1000, 1, 4, 9, None), e);
// Cannot create a crowdloan with bad slots
let e = Error::<Test>::LastSlotBeforeFirstSlot;
// Cannot create a crowdloan with bad lease periods
let e = Error::<Test>::LastPeriodBeforeFirstPeriod;
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 4, 1, 9, None), e);
let e = Error::<Test>::LastSlotTooFarInFuture;
let e = Error::<Test>::LastPeriodTooFarInFuture;
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 5, 9, None), e);
// Cannot create a crowdloan without some deposit funds
assert_ok!(TestRegistrar::<Test>::register(1337, ParaId::from(1234), Default::default(), Default::default()));
let e = BalancesError::<Test, _>::InsufficientBalance;
assert_noop!(Crowdloan::create(Origin::signed(1337), ParaId::from(1234), 1000, 1, 3, 9, None), e);
// Cannot create a crowdloan with nonsense end date
// This crowdloan would end in lease period 2, but is bidding for some slot that starts in lease period 1.
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 4, 41, None), Error::<Test>::EndTooFarInFuture);
});
}
@@ -1093,6 +1118,22 @@ mod tests {
// Cannot contribute to ended fund
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::ContributionPeriodOver);
// If a crowdloan has already won, it should not allow contributions.
let para_2 = new_para();
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);
make_winner(para_2, crowdloan_account);
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_2, 49, None), Error::<Test>::BidOrLeaseActive);
// Move past lease period 1, should not be allowed to have further contributions with a crowdloan
// that has starting period 1.
let para_3 = new_para();
assert_ok!(Crowdloan::create(Origin::signed(1), para_3, 1000, 1, 4, 40, None));
run_to_block(40);
assert_eq!(TestAuctioneer::lease_period_index(), 2);
assert_noop!(Crowdloan::contribute(Origin::signed(1), para_3, 49, None), Error::<Test>::ContributionPeriodOver);
});
}
@@ -1101,13 +1142,13 @@ mod tests {
new_test_ext().execute_with(|| {
let para = new_para();
let first_slot = 1;
let last_slot = 4;
let first_period = 1;
let last_period = 4;
assert_ok!(TestAuctioneer::new_auction(5, 0));
// Set up a crowdloan
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_slot, last_slot, 9, None));
assert_ok!(Crowdloan::create(Origin::signed(1), para, 1000, first_period, last_period, 9, None));
let bidder = Crowdloan::fund_account_id(para);
// Fund crowdloan
@@ -1122,9 +1163,9 @@ mod tests {
run_to_block(10);
assert_eq!(bids(), vec![
BidPlaced { height: 5, amount: 250, bidder, para, first_slot, last_slot },
BidPlaced { height: 6, amount: 450, bidder, para, first_slot, last_slot },
BidPlaced { height: 9, amount: 700, bidder, para, first_slot, last_slot },
BidPlaced { height: 5, amount: 250, bidder, para, first_period, last_period },
BidPlaced { height: 6, amount: 450, bidder, para, first_period, last_period },
BidPlaced { height: 9, amount: 700, bidder, para, first_period, last_period },
]);
// Endings count incremented
@@ -1244,8 +1285,7 @@ mod tests {
// Fund crowdloans.
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
// simulate the reserving of para's funds. this actually
// happens in the Slots pallet.
// simulate the reserving of para's funds. this actually happens in the Slots pallet.
assert_ok!(Balances::reserve(&account_id, 150));
run_to_block(19);
@@ -1375,8 +1415,8 @@ mod tests {
// Some things change
assert!(old_crowdloan.cap != new_crowdloan.cap);
assert!(old_crowdloan.first_slot != new_crowdloan.first_slot);
assert!(old_crowdloan.last_slot != new_crowdloan.last_slot);
assert!(old_crowdloan.first_period != new_crowdloan.first_period);
assert!(old_crowdloan.last_period != new_crowdloan.last_period);
});
}
@@ -1433,8 +1473,8 @@ mod benchmarking {
fn create_fund<T: Config>(id: u32, end: T::BlockNumber) -> ParaId {
let cap = BalanceOf::<T>::max_value();
let lease_period_index = T::Auctioneer::lease_period_index();
let first_slot = lease_period_index;
let last_slot = lease_period_index + 3u32.into();
let first_period = lease_period_index;
let last_period = lease_period_index + 3u32.into();
let para_id = id.into();
let caller = account("fund_creator", id, 0);
@@ -1452,8 +1492,8 @@ mod benchmarking {
RawOrigin::Signed(caller).into(),
para_id,
cap,
first_slot,
last_slot,
first_period,
last_period,
end,
Some(pubkey)
));
@@ -1476,9 +1516,9 @@ mod benchmarking {
create {
let para_id = ParaId::from(1);
let cap = BalanceOf::<T>::max_value();
let first_slot = 0u32.into();
let last_slot = 3u32.into();
let end = T::BlockNumber::max_value();
let first_period = 0u32.into();
let last_period = 3u32.into();
let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data();
@@ -1489,7 +1529,7 @@ mod benchmarking {
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Registrar::register(caller.clone(), para_id, head_data, validation_code)?;
}: _(RawOrigin::Signed(caller), para_id, cap, first_slot, last_slot, end, Some(verifier))
}: _(RawOrigin::Signed(caller), para_id, cap, first_period, last_period, end, Some(verifier))
verify {
assert_last_event::<T>(RawEvent::Created(para_id).into())
}
@@ -1552,9 +1592,9 @@ mod benchmarking {
edit {
let para_id = ParaId::from(1);
let cap = BalanceOf::<T>::max_value();
let first_slot = 0u32.into();
let last_slot = 3u32.into();
let end = T::BlockNumber::max_value();
let first_period = 0u32.into();
let last_period = 3u32.into();
let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data();
@@ -1567,11 +1607,11 @@ mod benchmarking {
Crowdloan::<T>::create(
RawOrigin::Signed(caller).into(),
para_id, cap, first_slot, last_slot, end, Some(verifier.clone()),
para_id, cap, first_period, last_period, end, Some(verifier.clone()),
)?;
// Doesn't matter what we edit to, so use the same values.
}: _(RawOrigin::Root, para_id, cap, first_slot, last_slot, end, Some(verifier))
}: _(RawOrigin::Root, para_id, cap, first_period, last_period, end, Some(verifier))
verify {
assert_last_event::<T>(RawEvent::Edited(para_id).into())
}
@@ -1592,7 +1632,7 @@ mod benchmarking {
// Worst case scenario: N funds are all in the `NewRaise` list, we are
// in the beginning of the ending period, and each fund outbids the next
// over the same slot.
// over the same periods.
on_initialize {
// We test the complexity over different number of new raise
let n in 2 .. 100;
@@ -360,7 +360,7 @@ fn basic_end_to_end_works() {
910, // Amount
));
// User 2 will be a contribute to crowdfund for parachain 2
// User 2 will be a contribute to crowdloan for parachain 2
Balances::make_free_balance_be(&2, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(2), 920, None));
@@ -388,6 +388,10 @@ fn basic_end_to_end_works() {
vec![None, None, None, None, None, Some((crowdloan_account, 920)), Some((crowdloan_account, 920))],
);
// Should not be able to contribute to a winning crowdloan
Balances::make_free_balance_be(&3, 1_000);
assert_noop!(Crowdloan::contribute(Origin::signed(3), ParaId::from(2), 10, None), CrowdloanError::<Test>::BidOrLeaseActive);
// New leases will start on block 400
let lease_start_block = 400;
run_to_block(lease_start_block);
@@ -616,7 +620,7 @@ fn competing_bids() {
n * 900, // Amount
));
} else {
// User 20 will be a contribute to crowdfund for parachain 2
// User 20 will be a contribute to crowdloan for parachain 2
assert_ok!(Crowdloan::contribute(
Origin::signed(n * 10),
ParaId::from(para),
+6
View File
@@ -174,6 +174,12 @@ pub trait Auctioneer {
/// Returns the current lease period.
fn lease_period_index() -> Self::LeasePeriod;
/// Returns the length of a lease period.
fn lease_period() -> Self::LeasePeriod;
/// Check if the para and user combination has won an auction in the past.
fn has_won_an_auction(para: ParaId, bidder: &Self::AccountId) -> bool;
}
/// Runtime hook for when we swap a parachain and parathread.