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. /// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as frame_system::Config>::Event>; 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>; type Leaser: Leaser<AccountId=Self::AccountId, LeasePeriod=Self::BlockNumber>;
/// The parachain registrar type. /// The parachain registrar type.
@@ -332,6 +332,14 @@ impl<T: Config> Auctioneer for Module<T> {
fn lease_period_index() -> Self::LeasePeriod { fn lease_period_index() -> Self::LeasePeriod {
T::Leaser::lease_period_index() 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> { 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 //! 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. //! 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` //! Successful funds remain tracked (in the `Funds` storage item and the associated child trie) as long as
//! call. This may only be done once and must be after the deploy data has been fixed. Successful //! the parachain remains active. Users can withdraw their funds once the slot is completed and funds are
//! funds remain tracked (in the `Funds` storage item and the associated child trie) as long as //! returned to the crowdloan account.
//! 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.
use frame_support::{ use frame_support::{
decl_module, decl_storage, decl_event, decl_error, ensure, decl_module, decl_storage, decl_event, decl_error, ensure,
@@ -78,7 +69,7 @@ use frame_system::{ensure_signed, ensure_root};
use sp_runtime::{ use sp_runtime::{
ModuleId, DispatchResult, RuntimeDebug, MultiSignature, MultiSigner, ModuleId, DispatchResult, RuntimeDebug, MultiSignature, MultiSigner,
traits::{ traits::{
AccountIdConversion, Hash, Saturating, Zero, CheckedAdd, Bounded, Verify, IdentifyAccount, AccountIdConversion, Hash, Saturating, Zero, One, CheckedAdd, Bounded, Verify, IdentifyAccount,
}, },
}; };
use crate::traits::{Registrar, Auctioneer}; use crate::traits::{Registrar, Auctioneer};
@@ -170,7 +161,7 @@ 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> {
/// True if the fund is being retired. This can only be set once and only when the current /// 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, retiring: bool,
/// The owning account who placed the deposit. /// The owning account who placed the deposit.
depositor: AccountId, 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, /// 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>, last_contribution: LastContribution<BlockNumber>,
/// First slot in range to bid on; it's actually a LeasePeriod, but that's the same type as /// First lease period in range to bid on; it's actually a LeasePeriod, but that's the same type
/// BlockNumber. /// as BlockNumber.
first_slot: LeasePeriod, first_period: LeasePeriod,
/// Last slot in range to bid on; it's actually a LeasePeriod, but that's the same type as /// Last lease period in range to bid on; it's actually a LeasePeriod, but that's the same type
/// BlockNumber. /// as BlockNumber.
last_slot: LeasePeriod, last_period: LeasePeriod,
/// Index used for the child trie of this fund /// Index used for the child trie of this fund
trie_index: TrieIndex, trie_index: TrieIndex,
} }
@@ -254,14 +245,18 @@ decl_event! {
decl_error! { decl_error! {
pub enum Error for Module<T: Config> { pub enum Error for Module<T: Config> {
/// The first slot needs to at least be less than 3 `max_value`. /// The current lease period is more than the first lease period.
FirstSlotTooFarInFuture, FirstPeriodInPast,
/// Last slot must be greater than first slot. /// The first lease period needs to at least be less than 3 `max_value`.
LastSlotBeforeFirstSlot, FirstPeriodTooFarInFuture,
/// The last slot cannot be more then 3 slots after the first slot. /// Last lease period must be greater than first lease period.
LastSlotTooFarInFuture, 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. /// The campaign ends before the current block number. The end must be in the future.
CannotEndInPast, CannotEndInPast,
/// The end date for this crowdloan is not sensible.
EndTooFarInFuture,
/// There was an overflow. /// There was an overflow.
Overflow, Overflow,
/// The contribution was below the minimum, `MinContribution`. /// The contribution was below the minimum, `MinContribution`.
@@ -310,22 +305,25 @@ decl_module! {
fn deposit_event() = default; 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()] #[weight = T::WeightInfo::create()]
pub fn create(origin, pub fn create(origin,
#[compact] index: ParaId, #[compact] index: ParaId,
#[compact] cap: BalanceOf<T>, #[compact] cap: BalanceOf<T>,
#[compact] first_slot: LeasePeriodOf<T>, #[compact] first_period: LeasePeriodOf<T>,
#[compact] last_slot: LeasePeriodOf<T>, #[compact] last_period: LeasePeriodOf<T>,
#[compact] end: T::BlockNumber, #[compact] end: T::BlockNumber,
verifier: Option<MultiSigner>, verifier: Option<MultiSigner>,
) { ) {
let depositor = ensure_signed(origin)?; let depositor = ensure_signed(origin)?;
ensure!(first_slot <= last_slot, Error::<T>::LastSlotBeforeFirstSlot); ensure!(first_period <= last_period, Error::<T>::LastPeriodBeforeFirstPeriod);
let last_slot_limit = first_slot.checked_add(&3u32.into()).ok_or(Error::<T>::FirstSlotTooFarInFuture)?; let last_period_limit = first_period.checked_add(&3u32.into()).ok_or(Error::<T>::FirstPeriodTooFarInFuture)?;
ensure!(last_slot <= last_slot_limit, Error::<T>::LastSlotTooFarInFuture); ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture);
ensure!(end > <frame_system::Pallet<T>>::block_number(), Error::<T>::CannotEndInPast); 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. // There should not be an existing fund.
ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded); ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded);
@@ -349,8 +347,8 @@ decl_module! {
end, end,
cap, cap,
last_contribution: LastContribution::Never, last_contribution: LastContribution::Never,
first_slot, first_period,
last_slot, last_period,
trie_index, trie_index,
}); });
@@ -379,6 +377,14 @@ decl_module! {
let now = <frame_system::Pallet<T>>::block_number(); let now = <frame_system::Pallet<T>>::block_number();
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver); 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); let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);
if let Some(ref verifier) = fund.verifier { if let Some(ref verifier) = fund.verifier {
@@ -388,7 +394,7 @@ decl_module! {
ensure!(valid, Error::<T>::InvalidSignature); 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); let balance = old_balance.saturating_add(value);
Self::contribution_put(fund.trie_index, &who, &balance, &memo); 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; /// - the amount of raised funds must be bigger than the _free_ balance of the account;
/// - and either: /// - and either:
/// - the block number must be at least `end`; or /// - 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 /// In this case, the fund's retirement flag is set and its `end` is reset to the current block
/// number. /// number.
@@ -514,8 +520,8 @@ decl_module! {
pub fn edit(origin, pub fn edit(origin,
#[compact] index: ParaId, #[compact] index: ParaId,
#[compact] cap: BalanceOf<T>, #[compact] cap: BalanceOf<T>,
#[compact] first_slot: LeasePeriodOf<T>, #[compact] first_period: LeasePeriodOf<T>,
#[compact] last_slot: LeasePeriodOf<T>, #[compact] last_period: LeasePeriodOf<T>,
#[compact] end: T::BlockNumber, #[compact] end: T::BlockNumber,
verifier: Option<MultiSigner>, verifier: Option<MultiSigner>,
) { ) {
@@ -532,8 +538,8 @@ decl_module! {
end, end,
cap, cap,
last_contribution: fund.last_contribution, last_contribution: fund.last_contribution,
first_slot, first_period,
last_slot, last_period,
trie_index: fund.trie_index, trie_index: fund.trie_index,
}); });
@@ -571,8 +577,8 @@ decl_module! {
let result = T::Auctioneer::place_bid( let result = T::Auctioneer::place_bid(
Self::fund_account_id(para_id), Self::fund_account_id(para_id),
para_id, para_id,
fund.first_slot, fund.first_period,
fund.last_slot, fund.last_period,
fund.raised, fund.raised,
); );
@@ -630,11 +636,11 @@ impl<T: Config> Module<T> {
fund_account: &T::AccountId, fund_account: &T::AccountId,
fund: &FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>> fund: &FundInfo<T::AccountId, BalanceOf<T>, T::BlockNumber, LeasePeriodOf<T>>
) -> DispatchResult { ) -> DispatchResult {
// `fund.end` can represent the end of a failed crowdsale or the beginning of retirement // `fund.end` can represent the end of a failed crowdloan 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 // If the current lease period is past the first period they are trying to bid for, then
// to win the bid. // it is already too late to win the bid.
let current_lease_period = T::Auctioneer::lease_period_index(); 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 // free balance must greater than or equal amount raised, otherwise funds are being used
// and a bid or lease must be active. // and a bid or lease must be active.
ensure!(CurrencyOf::<T>::free_balance(&fund_account) >= fund.raised, Error::<T>::BidOrLeaseActive); ensure!(CurrencyOf::<T>::free_balance(&fund_account) >= fund.raised, Error::<T>::BidOrLeaseActive);
@@ -678,7 +684,7 @@ mod crypto {
mod tests { mod tests {
use super::*; use super::*;
use std::{cell::RefCell, sync::Arc}; use std::{cell::RefCell, sync::Arc, collections::BTreeMap};
use frame_support::{ use frame_support::{
assert_ok, assert_noop, parameter_types, assert_ok, assert_noop, parameter_types,
traits::{OnInitialize, OnFinalize}, traits::{OnInitialize, OnFinalize},
@@ -716,6 +722,8 @@ mod tests {
pub const BlockHashCount: u32 = 250; pub const BlockHashCount: u32 = 250;
} }
type BlockNumber = u64;
impl frame_system::Config for Test { impl frame_system::Config for Test {
type BaseCallFilter = (); type BaseCallFilter = ();
type BlockWeights = (); type BlockWeights = ();
@@ -724,7 +732,7 @@ mod tests {
type Origin = Origin; type Origin = Origin;
type Call = Call; type Call = Call;
type Index = u64; type Index = u64;
type BlockNumber = u64; type BlockNumber = BlockNumber;
type Hash = H256; type Hash = H256;
type Hashing = BlakeTwo256; type Hashing = BlakeTwo256;
type AccountId = u64; type AccountId = u64;
@@ -760,15 +768,17 @@ mod tests {
height: u64, height: u64,
bidder: u64, bidder: u64,
para: ParaId, para: ParaId,
first_slot: u64, first_period: u64,
last_slot: u64, last_period: u64,
amount: u64 amount: u64
} }
thread_local! { thread_local! {
static AUCTION: RefCell<Option<(u64, u64)>> = RefCell::new(None); static AUCTION: RefCell<Option<(u64, u64)>> = RefCell::new(None);
static ENDING_PERIOD: RefCell<u64> = RefCell::new(5); static ENDING_PERIOD: RefCell<u64> = RefCell::new(5);
static BIDS_PLACED: RefCell<Vec<BidPlaced>> = RefCell::new(Vec::new()); static BIDS_PLACED: RefCell<Vec<BidPlaced>> = RefCell::new(Vec::new());
static HAS_WON: RefCell<BTreeMap<(ParaId, u64), bool>> = RefCell::new(BTreeMap::new());
} }
#[allow(unused)] #[allow(unused)]
fn set_ending_period(ending_period: u64) { fn set_ending_period(ending_period: u64) {
ENDING_PERIOD.with(|p| *p.borrow_mut() = ending_period); ENDING_PERIOD.with(|p| *p.borrow_mut() = ending_period);
@@ -782,11 +792,14 @@ mod tests {
fn bids() -> Vec<BidPlaced> { fn bids() -> Vec<BidPlaced> {
BIDS_PLACED.with(|p| p.borrow().clone()) 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; pub struct TestAuctioneer;
impl Auctioneer for TestAuctioneer { impl Auctioneer for TestAuctioneer {
type AccountId = u64; type AccountId = u64;
type BlockNumber = u64; type BlockNumber = BlockNumber;
type LeasePeriod = u64; type LeasePeriod = u64;
type Currency = Balances; type Currency = Balances;
@@ -812,17 +825,25 @@ mod tests {
fn place_bid( fn place_bid(
bidder: u64, bidder: u64,
para: ParaId, para: ParaId,
first_slot: u64, first_period: u64,
last_slot: u64, last_period: u64,
amount: u64 amount: u64
) -> DispatchResult { ) -> DispatchResult {
let height = System::block_number(); 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(()) Ok(())
} }
fn lease_period_index() -> u64 { 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_eq!(bids(), vec![]);
assert_ok!(TestAuctioneer::place_bid(1, 2.into(), 0, 3, 6)); 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!(bids(), vec![b]);
assert_eq!(TestAuctioneer::is_ending(4), None); assert_eq!(TestAuctioneer::is_ending(4), None);
assert_eq!(TestAuctioneer::is_ending(5), Some(0)); assert_eq!(TestAuctioneer::is_ending(5), Some(0));
@@ -926,8 +947,8 @@ mod tests {
end: 9, end: 9,
cap: 1000, cap: 1000,
last_contribution: LastContribution::Never, last_contribution: LastContribution::Never,
first_slot: 1, first_period: 1,
last_slot: 4, last_period: 4,
trie_index: 0, trie_index: 0,
}; };
assert_eq!(Crowdloan::funds(para), Some(fund_info)); assert_eq!(Crowdloan::funds(para), Some(fund_info));
@@ -959,8 +980,8 @@ mod tests {
end: 9, end: 9,
cap: 1000, cap: 1000,
last_contribution: LastContribution::Never, last_contribution: LastContribution::Never,
first_slot: 1, first_period: 1,
last_slot: 4, last_period: 4,
trie_index: 0, trie_index: 0,
}; };
assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info)); assert_eq!(Crowdloan::funds(ParaId::from(0)), Some(fund_info));
@@ -982,16 +1003,20 @@ mod tests {
let e = Error::<Test>::InvalidParaId; let e = Error::<Test>::InvalidParaId;
assert_noop!(Crowdloan::create(Origin::signed(1), 1.into(), 1000, 1, 4, 9, None), e); assert_noop!(Crowdloan::create(Origin::signed(1), 1.into(), 1000, 1, 4, 9, None), e);
// Cannot create a crowdloan with bad slots // Cannot create a crowdloan with bad lease periods
let e = Error::<Test>::LastSlotBeforeFirstSlot; let e = Error::<Test>::LastPeriodBeforeFirstPeriod;
assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 4, 1, 9, None), e); 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); assert_noop!(Crowdloan::create(Origin::signed(1), para, 1000, 1, 5, 9, None), e);
// Cannot create a crowdloan without some deposit funds // Cannot create a crowdloan without some deposit funds
assert_ok!(TestRegistrar::<Test>::register(1337, ParaId::from(1234), Default::default(), Default::default())); assert_ok!(TestRegistrar::<Test>::register(1337, ParaId::from(1234), Default::default(), Default::default()));
let e = BalancesError::<Test, _>::InsufficientBalance; let e = BalancesError::<Test, _>::InsufficientBalance;
assert_noop!(Crowdloan::create(Origin::signed(1337), ParaId::from(1234), 1000, 1, 3, 9, None), e); 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 // Cannot contribute to ended fund
assert_noop!(Crowdloan::contribute(Origin::signed(1), para, 49, None), Error::<Test>::ContributionPeriodOver); 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(|| { new_test_ext().execute_with(|| {
let para = new_para(); let para = new_para();
let first_slot = 1; let first_period = 1;
let last_slot = 4; let last_period = 4;
assert_ok!(TestAuctioneer::new_auction(5, 0)); assert_ok!(TestAuctioneer::new_auction(5, 0));
// Set up a crowdloan // 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); let bidder = Crowdloan::fund_account_id(para);
// Fund crowdloan // Fund crowdloan
@@ -1122,9 +1163,9 @@ mod tests {
run_to_block(10); run_to_block(10);
assert_eq!(bids(), vec![ assert_eq!(bids(), vec![
BidPlaced { height: 5, amount: 250, 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_slot, last_slot }, BidPlaced { height: 6, amount: 450, bidder, para, first_period, last_period },
BidPlaced { height: 9, amount: 700, bidder, para, first_slot, last_slot }, BidPlaced { height: 9, amount: 700, bidder, para, first_period, last_period },
]); ]);
// Endings count incremented // Endings count incremented
@@ -1244,8 +1285,7 @@ mod tests {
// Fund crowdloans. // Fund crowdloans.
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None)); assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 100, None));
assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None)); assert_ok!(Crowdloan::contribute(Origin::signed(3), para, 50, None));
// simulate the reserving of para's funds. this actually // simulate the reserving of para's funds. this actually happens in the Slots pallet.
// happens in the Slots pallet.
assert_ok!(Balances::reserve(&account_id, 150)); assert_ok!(Balances::reserve(&account_id, 150));
run_to_block(19); run_to_block(19);
@@ -1375,8 +1415,8 @@ mod tests {
// Some things change // Some things change
assert!(old_crowdloan.cap != new_crowdloan.cap); assert!(old_crowdloan.cap != new_crowdloan.cap);
assert!(old_crowdloan.first_slot != new_crowdloan.first_slot); assert!(old_crowdloan.first_period != new_crowdloan.first_period);
assert!(old_crowdloan.last_slot != new_crowdloan.last_slot); 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 { fn create_fund<T: Config>(id: u32, end: T::BlockNumber) -> ParaId {
let cap = BalanceOf::<T>::max_value(); let cap = BalanceOf::<T>::max_value();
let lease_period_index = T::Auctioneer::lease_period_index(); let lease_period_index = T::Auctioneer::lease_period_index();
let first_slot = lease_period_index; let first_period = lease_period_index;
let last_slot = lease_period_index + 3u32.into(); let last_period = lease_period_index + 3u32.into();
let para_id = id.into(); let para_id = id.into();
let caller = account("fund_creator", id, 0); let caller = account("fund_creator", id, 0);
@@ -1452,8 +1492,8 @@ mod benchmarking {
RawOrigin::Signed(caller).into(), RawOrigin::Signed(caller).into(),
para_id, para_id,
cap, cap,
first_slot, first_period,
last_slot, last_period,
end, end,
Some(pubkey) Some(pubkey)
)); ));
@@ -1476,9 +1516,9 @@ mod benchmarking {
create { create {
let para_id = ParaId::from(1); let para_id = ParaId::from(1);
let cap = BalanceOf::<T>::max_value(); let cap = BalanceOf::<T>::max_value();
let first_slot = 0u32.into(); let first_period = 0u32.into();
let last_slot = 3u32.into(); let last_period = 3u32.into();
let end = T::BlockNumber::max_value(); let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller(); let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data(); 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()); CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
T::Registrar::register(caller.clone(), para_id, head_data, validation_code)?; 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 { verify {
assert_last_event::<T>(RawEvent::Created(para_id).into()) assert_last_event::<T>(RawEvent::Created(para_id).into())
} }
@@ -1552,9 +1592,9 @@ mod benchmarking {
edit { edit {
let para_id = ParaId::from(1); let para_id = ParaId::from(1);
let cap = BalanceOf::<T>::max_value(); let cap = BalanceOf::<T>::max_value();
let first_slot = 0u32.into(); let first_period = 0u32.into();
let last_slot = 3u32.into(); let last_period = 3u32.into();
let end = T::BlockNumber::max_value(); let end = T::Auctioneer::lease_period();
let caller: T::AccountId = whitelisted_caller(); let caller: T::AccountId = whitelisted_caller();
let head_data = T::Registrar::worst_head_data(); let head_data = T::Registrar::worst_head_data();
@@ -1567,11 +1607,11 @@ mod benchmarking {
Crowdloan::<T>::create( Crowdloan::<T>::create(
RawOrigin::Signed(caller).into(), 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. // 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 { verify {
assert_last_event::<T>(RawEvent::Edited(para_id).into()) 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 // 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 // in the beginning of the ending period, and each fund outbids the next
// over the same slot. // over the same periods.
on_initialize { on_initialize {
// We test the complexity over different number of new raise // We test the complexity over different number of new raise
let n in 2 .. 100; let n in 2 .. 100;
@@ -360,7 +360,7 @@ fn basic_end_to_end_works() {
910, // Amount 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); Balances::make_free_balance_be(&2, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(2), 920, None)); 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))], 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 // New leases will start on block 400
let lease_start_block = 400; let lease_start_block = 400;
run_to_block(lease_start_block); run_to_block(lease_start_block);
@@ -616,7 +620,7 @@ fn competing_bids() {
n * 900, // Amount n * 900, // Amount
)); ));
} else { } 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( assert_ok!(Crowdloan::contribute(
Origin::signed(n * 10), Origin::signed(n * 10),
ParaId::from(para), ParaId::from(para),
+6
View File
@@ -174,6 +174,12 @@ pub trait Auctioneer {
/// Returns the current lease period. /// Returns the current lease period.
fn lease_period_index() -> Self::LeasePeriod; 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. /// Runtime hook for when we swap a parachain and parathread.