Unverified Commit a6d646b2 authored by Shawn Tabrizi's avatar Shawn Tabrizi Committed by GitHub
Browse files

Do not allow any crowdloan contributions during the VRF period (#3346)



* patch `is_ending` logic for sampling

* Create AuctionStatus abstraction

* clean up apis

* fix docs

* Add check for contributions during vrf

* custom error for this

* fix benchmark

* opening -> starting

* Apply suggestions from code review

Co-authored-by: default avatarKian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update runtime/common/src/auctions.rs

* avoid divide by zero stuff

Co-authored-by: default avatarKian Paimani <5588131+kianenigma@users.noreply.github.com>
parent f0faf5be
Pipeline #144230 passed with stages
in 38 minutes and 44 seconds
......@@ -27,7 +27,7 @@ use frame_support::{
};
use primitives::v1::Id as ParaId;
use crate::slot_range::SlotRange;
use crate::traits::{Leaser, LeaseError, Auctioneer, Registrar};
use crate::traits::{Leaser, LeaseError, Auctioneer, Registrar, AuctionStatus};
use parity_scale_codec::Decode;
pub use pallet::*;
......@@ -201,7 +201,7 @@ pub mod pallet {
// If the current auction was in its ending period last block, then ensure that the (sub-)range
// winner information is duplicated from the previous block in case no bids happened in the
// last block.
if let Some(offset) = Self::is_ending(n) {
if let AuctionStatus::EndingPeriod(offset, _sub_sample) = Self::auction_status(n) {
weight = weight.saturating_add(T::DbWeight::get().reads(1));
if !Winning::<T>::contains_key(&offset) {
weight = weight.saturating_add(T::DbWeight::get().writes(1));
......@@ -241,19 +241,7 @@ pub mod pallet {
#[pallet::compact] lease_period_index: LeasePeriodOf<T>,
) -> DispatchResult {
T::InitiateOrigin::ensure_origin(origin)?;
ensure!(!Self::is_in_progress(), Error::<T>::AuctionInProgress);
ensure!(lease_period_index >= T::Leaser::lease_period_index(), Error::<T>::LeasePeriodInPast);
// Bump the counter.
let n = AuctionCounter::<T>::mutate(|n| { *n += 1; *n });
// Set the information.
let ending = frame_system::Pallet::<T>::block_number().saturating_add(duration);
AuctionInfo::<T>::put((lease_period_index, ending));
Self::deposit_event(Event::<T>::AuctionStarted(n, lease_period_index, ending));
Ok(())
Self::do_new_auction(duration, lease_period_index)
}
/// Make a new bid from an account (including a parachain account) for deploying a new
......@@ -316,16 +304,28 @@ impl<T: Config> Auctioneer for Pallet<T> {
Self::do_new_auction(duration, lease_period_index)
}
// Returns whether the auction is ending, and which sample number we are on.
fn is_ending(now: Self::BlockNumber) -> Option<Self::BlockNumber> {
if let Some((_, early_end)) = AuctionInfo::<T>::get() {
if let Some(after_early_end) = now.checked_sub(&early_end) {
if after_early_end < T::EndingPeriod::get() {
return Some(after_early_end / T::SampleLength::get())
}
}
// Returns the status of the auction given the current block number.
fn auction_status(now: Self::BlockNumber) -> AuctionStatus<Self::BlockNumber> {
let early_end = match AuctionInfo::<T>::get() {
Some((_, early_end)) => early_end,
None => return AuctionStatus::NotStarted,
};
let after_early_end = match now.checked_sub(&early_end) {
Some(after_early_end) => after_early_end,
None => return AuctionStatus::StartingPeriod,
};
let ending_period = T::EndingPeriod::get();
if after_early_end < ending_period {
let sample_length = T::SampleLength::get().max(One::one());
let sample = after_early_end / sample_length;
let sub_sample = after_early_end % sample_length;
return AuctionStatus::EndingPeriod(sample, sub_sample)
} else {
// This is safe because of the comparison operator above
return AuctionStatus::VrfDelay(after_early_end - ending_period)
}
None
}
fn place_bid(
......@@ -355,17 +355,6 @@ impl<T: Config> Pallet<T> {
// A trick to allow me to initialize large arrays with nothing in them.
const EMPTY: Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)> = None;
/// True if an auction is in progress.
pub fn is_in_progress() -> bool {
AuctionInfo::<T>::get().map_or(false, |(_, early_end)| {
let late_end = early_end.saturating_add(T::EndingPeriod::get());
// We need to check that the auction isn't in the period where it has definitely ended, but yeah we keep the
// info around because we haven't yet decided *exactly* when in the `EndingPeriod` that it ended.
let now = frame_system::Pallet::<T>::block_number();
now < late_end
})
}
/// Create a new auction.
///
/// This can only happen when there isn't already an auction in progress. Accepts the `duration`
......@@ -375,7 +364,8 @@ impl<T: Config> Pallet<T> {
duration: T::BlockNumber,
lease_period_index: LeasePeriodOf<T>,
) -> DispatchResult {
ensure!(!Self::is_in_progress(), Error::<T>::AuctionInProgress);
let maybe_auction = AuctionInfo::<T>::get();
ensure!(maybe_auction.is_none(), Error::<T>::AuctionInProgress);
ensure!(lease_period_index >= T::Leaser::lease_period_index(), Error::<T>::LeasePeriodInPast);
// Bump the counter.
......@@ -410,21 +400,24 @@ impl<T: Config> Pallet<T> {
// Bidding on latest auction.
ensure!(auction_index == AuctionCounter::<T>::get(), Error::<T>::NotCurrentAuction);
// Assume it's actually an auction (this should never fail because of above).
let (first_lease_period, early_end) = AuctionInfo::<T>::get().ok_or(Error::<T>::NotAuction)?;
let late_end = early_end.saturating_add(T::EndingPeriod::get());
let (first_lease_period, _) = AuctionInfo::<T>::get().ok_or(Error::<T>::NotAuction)?;
// We need to check that the auction isn't in the period where it has definitely ended, but yeah we keep the
// info around because we haven't yet decided *exactly* when in the `EndingPeriod` that it ended.
let now = frame_system::Pallet::<T>::block_number();
ensure!(now < late_end, Error::<T>::AuctionEnded);
// Get the auction status and the current sample block. For the starting period, the sample
// block is zero.
let auction_status = Self::auction_status(frame_system::Pallet::<T>::block_number());
// The offset into the ending samples of the auction.
let offset = match auction_status {
AuctionStatus::NotStarted => return Err(Error::<T>::AuctionEnded.into()),
AuctionStatus::StartingPeriod => Zero::zero(),
AuctionStatus::EndingPeriod(o, _) => o,
AuctionStatus::VrfDelay(_) => return Err(Error::<T>::AuctionEnded.into()),
};
// Our range.
let range = SlotRange::new_bounded(first_lease_period, first_slot, last_slot)?;
// Range as an array index.
let range_index = range as u8 as usize;
let is_ending = Self::is_ending(frame_system::Pallet::<T>::block_number());
// The offset into the ending samples of the auction.
let offset = is_ending.unwrap_or_default();
// The current winning ranges.
let mut current_winning = Winning::<T>::get(offset)
.or_else(|| offset.checked_sub(&One::one()).and_then(Winning::<T>::get))
......@@ -463,7 +456,7 @@ impl<T: Config> Pallet<T> {
let mut outgoing_winner = Some((bidder.clone(), para, amount));
swap(&mut current_winning[range_index], &mut outgoing_winner);
if let Some((who, para, _amount)) = outgoing_winner {
if !is_ending.is_some() && current_winning.iter()
if auction_status.is_starting() && current_winning.iter()
.filter_map(Option::as_ref)
.all(|&(ref other, other_para, _)| other != &who || other_para != para)
{
......@@ -504,7 +497,7 @@ impl<T: Config> Pallet<T> {
// Our random seed was known only after the auction ended. Good to use.
let raw_offset_block_number = <T::BlockNumber>::decode(&mut raw_offset.as_ref())
.expect("secure hashes should always be bigger than the block number; qed");
let offset = (raw_offset_block_number % ending_period) / T::SampleLength::get();
let offset = (raw_offset_block_number % ending_period) / T::SampleLength::get().max(One::one());
let auction_counter = AuctionCounter::<T>::get();
Self::deposit_event(Event::<T>::WinningOffset(auction_counter, offset));
......@@ -835,15 +828,13 @@ mod tests {
new_test_ext().execute_with(|| {
assert_eq!(AuctionCounter::<Test>::get(), 0);
assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
assert_eq!(Auctions::is_in_progress(), false);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
run_to_block(10);
assert_eq!(AuctionCounter::<Test>::get(), 0);
assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
assert_eq!(Auctions::is_in_progress(), false);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
});
}
......@@ -856,8 +847,7 @@ mod tests {
assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1));
assert_eq!(AuctionCounter::<Test>::get(), 1);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
});
}
......@@ -918,40 +908,31 @@ mod tests {
assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1));
assert_eq!(AuctionCounter::<Test>::get(), 1);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(2);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(3);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(4);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(5);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(6);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), Some(0));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 0));
run_to_block(7);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), Some(1));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 0));
run_to_block(8);
assert_eq!(Auctions::is_in_progress(), true);
assert_eq!(Auctions::is_ending(System::block_number()), Some(2));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
run_to_block(9);
assert_eq!(Auctions::is_in_progress(), false);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
});
}
......@@ -983,19 +964,19 @@ mod tests {
assert_ok!(Auctions::bid(Origin::signed(1), 0.into(), 1, 1, 4, 1));
assert_eq!(Balances::reserved_balance(1), 1);
assert_eq!(Balances::free_balance(1), 9);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(8);
// Auction has not yet ended.
assert_eq!(leases(), vec![]);
assert!(Auctions::is_in_progress());
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
// This will prevent the auction's winner from being decided in the next block, since the random
// seed was known before the final bids were made.
set_last_random(H256::zero(), 8);
// Auction definitely ended now, but we don't know exactly when in the last 3 blocks yet since
// no randomness available yet.
run_to_block(9);
// Auction has now ended...
assert!(!Auctions::is_in_progress());
// ...But auction winner still not yet decided, so no leases yet.
// Auction has now ended... But auction winner still not yet decided, so no leases yet.
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::VrfDelay(0));
assert_eq!(leases(), vec![]);
// Random seed now updated to a value known at block 9, when the auction ended. This means
......@@ -1003,7 +984,7 @@ mod tests {
set_last_random(H256::zero(), 9);
run_to_block(10);
// Auction ended and winner selected
assert!(!Auctions::is_in_progress());
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
assert_eq!(leases(), vec![
((0.into(), 1), LeaseData { leaser: 1, amount: 1 }),
((0.into(), 2), LeaseData { leaser: 1, amount: 1 }),
......@@ -1289,26 +1270,26 @@ mod tests {
assert_ok!(Auctions::bid(Origin::signed(1), para_1, 1, 1, 4, 10));
assert_ok!(Auctions::bid(Origin::signed(2), para_2, 1, 3, 4, 20));
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10));
winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20));
assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(9);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(10);
assert_eq!(Auctions::is_ending(System::block_number()), Some(0));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 0));
assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(11);
assert_eq!(Auctions::is_ending(System::block_number()), Some(1));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 0));
assert_eq!(Auctions::winning(1), Some(winning));
assert_ok!(Auctions::bid(Origin::signed(3), para_3, 1, 3, 4, 30));
run_to_block(12);
assert_eq!(Auctions::is_ending(System::block_number()), Some(2));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
winning[SlotRange::TwoThree as u8 as usize] = Some((3, para_3, 30));
assert_eq!(Auctions::winning(2), Some(winning));
});
......@@ -1342,17 +1323,17 @@ mod tests {
assert_ok!(Auctions::bid(Origin::signed(1), para_1, 1, 11, 14, 10));
assert_ok!(Auctions::bid(Origin::signed(2), para_2, 1, 13, 14, 20));
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10));
winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20));
assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(9);
assert_eq!(Auctions::is_ending(System::block_number()), None);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(10);
assert_eq!(Auctions::is_ending(System::block_number()), Some(0));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 0));
assert_eq!(Auctions::winning(0), Some(winning));
// New bids update the current winning
......@@ -1361,7 +1342,7 @@ mod tests {
assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(20);
assert_eq!(Auctions::is_ending(System::block_number()), Some(1));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 0));
assert_eq!(Auctions::winning(1), Some(winning));
run_to_block(25);
// Overbid mid sample
......@@ -1370,13 +1351,13 @@ mod tests {
assert_eq!(Auctions::winning(1), Some(winning));
run_to_block(30);
assert_eq!(Auctions::is_ending(System::block_number()), Some(2));
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
assert_eq!(Auctions::winning(2), Some(winning));
set_last_random(H256::from([254; 32]), 40);
run_to_block(40);
// Auction ended and winner selected
assert!(!Auctions::is_in_progress());
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
assert_eq!(leases(), vec![
((3.into(), 13), LeaseData { leaser: 3, amount: 30 }),
((3.into(), 14), LeaseData { leaser: 3, amount: 30 }),
......@@ -1384,6 +1365,54 @@ mod tests {
});
}
#[test]
fn auction_status_works() {
new_test_ext().execute_with(|| {
EndingPeriod::set(30);
SampleLength::set(10);
set_last_random(Default::default(), 0);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
run_to_block(1);
assert_ok!(Auctions::new_auction(Origin::signed(6), 9, 11));
run_to_block(9);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(10);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 0));
run_to_block(11);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 1));
run_to_block(19);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 9));
run_to_block(20);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 0));
run_to_block(25);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 5));
run_to_block(30);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
run_to_block(39);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 9));
run_to_block(40);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::VrfDelay(0));
run_to_block(44);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::VrfDelay(4));
set_last_random(Default::default(), 45);
run_to_block(45);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
});
}
#[test]
fn can_cancel_auction() {
new_test_ext().execute_with(|| {
......
......@@ -289,14 +289,17 @@ pub mod pallet {
/// The provided memo is too large.
MemoTooLarge,
/// The fund is already in NewRaise
AlreadyInNewRaise
AlreadyInNewRaise,
/// No contributions allowed during the VRF delay
VrfDelayInProgress,
}
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(n: T::BlockNumber) -> frame_support::weights::Weight {
if let Some(n) = T::Auctioneer::is_ending(n) {
if n.is_zero() {
fn on_initialize(num: T::BlockNumber) -> frame_support::weights::Weight {
if let Some((sample, sub_sample)) = T::Auctioneer::auction_status(num).is_ending() {
// This is the very first block in the ending period
if sample.is_zero() && sub_sample.is_zero() {
// first block of ending period.
EndingsCount::<T>::mutate(|c| *c += 1);
}
......@@ -413,6 +416,10 @@ pub mod pallet {
let fund_account = Self::fund_account_id(index);
ensure!(!T::Auctioneer::has_won_an_auction(index, &fund_account), Error::<T>::BidOrLeaseActive);
// We disallow any crowdloan contributions during the VRF Period, so that people do not sneak their
// contributions into the auction when it would not impact the outcome.
ensure!(!T::Auctioneer::auction_status(now).is_vrf(), Error::<T>::VrfDelayInProgress);
let (old_balance, memo) = Self::contribution_get(fund.trie_index, &who);
if let Some(ref verifier) = fund.verifier {
......@@ -427,7 +434,7 @@ pub mod pallet {
let balance = old_balance.saturating_add(value);
Self::contribution_put(fund.trie_index, &who, &balance, &memo);
if T::Auctioneer::is_ending(now).is_some() {
if T::Auctioneer::auction_status(now).is_ending().is_some() {
match fund.last_contribution {
// In ending period; must ensure that we are in NewRaise.
LastContribution::Ending(n) if n == now => {
......@@ -758,7 +765,7 @@ mod tests {
};
use crate::{
mock::TestRegistrar,
traits::OnSwap,
traits::{OnSwap, AuctionStatus},
crowdloan,
};
use sp_keystore::{KeystoreExt, testing::KeyStore};
......@@ -837,6 +844,7 @@ mod tests {
}
thread_local! {
static AUCTION: RefCell<Option<(u64, u64)>> = RefCell::new(None);
static VRF_DELAY: RefCell<u64> = RefCell::new(0);
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());
......@@ -855,6 +863,12 @@ mod tests {
fn bids() -> Vec<BidPlaced> {
BIDS_PLACED.with(|p| p.borrow().clone())
}
fn vrf_delay() -> u64 {
VRF_DELAY.with(|p| p.borrow().clone())
}
fn set_vrf_delay(delay: u64) {
VRF_DELAY.with(|p| *p.borrow_mut() = delay);
}
// Emulate what would happen if we won an auction:
// balance is reserved and a deposit_held is recorded
fn set_winner(para: ParaId, who: u64, winner: bool) {
......@@ -884,15 +898,29 @@ mod tests {
Ok(())
}
fn is_ending(now: u64) -> Option<u64> {
if let Some((_, early_end)) = auction() {
if let Some(after_early_end) = now.checked_sub(early_end) {
if after_early_end < ending_period() {
return Some(after_early_end)
}
fn auction_status(now: u64) -> AuctionStatus<u64> {
let early_end = match auction() {
Some((_, early_end)) => early_end,
None => return AuctionStatus::NotStarted,
};
let after_early_end = match now.checked_sub(early_end) {
Some(after_early_end) => after_early_end,
None => return AuctionStatus::StartingPeriod,
};
let ending_period = ending_period();
if after_early_end < ending_period {
return AuctionStatus::EndingPeriod(after_early_end, 0)
} else {
let after_end = after_early_end - ending_period;
// Optional VRF delay
if after_end < vrf_delay() {
return AuctionStatus::VrfDelay(after_end);
} else {
// VRF delay is done, so we just end the auction
return AuctionStatus::NotStarted;
}
}
None
}
fn place_bid(
......@@ -997,10 +1025,10 @@ mod tests {
assert_ok!(TestAuctioneer::place_bid(1, 2.into(), 0, 3, 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));
assert_eq!(TestAuctioneer::is_ending(9), Some(4));
assert_eq!(TestAuctioneer::is_ending(11), None);
assert_eq!(TestAuctioneer::auction_status(4), AuctionStatus::<u64>::StartingPeriod);
assert_eq!(TestAuctioneer::auction_status(5), AuctionStatus::<u64>::EndingPeriod(0, 0));
assert_eq!(TestAuctioneer::auction_status(9), AuctionStatus::<u64>::EndingPeriod(4, 0));
assert_eq!(TestAuctioneer::auction_status(11), AuctionStatus::<u64>::NotStarted);
});
}
......@@ -1210,10 +1238,40 @@ mod tests {
}
#[test]
fn bidding_works() {
fn cannot_contribute_during_vrf() {
new_test_ext().execute_with(|| {
set_vrf_delay(5);
let para = new_para();
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_period, last_period, 20, None));
run_to_block(8);
// Can def contribute when auction is running.
assert!(TestAuctioneer::auction_status(System::block_number()).is_ending().is_some());
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
run_to_block(10);
// Can't contribute when auction is in the VRF delay period.
assert!(TestAuctioneer::auction_status(System::block_number()).is_vrf());
assert_noop!(Crowdloan::contribute(Origin::signed(2), para, 250, None), Error::<Test>::VrfDelayInProgress);
run_to_block(15);
// Its fine to contribute when no auction is running.
assert!(!TestAuctioneer::auction_status(System::block_number()).is_in_progress());
assert_ok!(Crowdloan::contribute(Origin::signed(2), para, 250, None));
})
}
#[test]
fn bidding_works() {
new_test_ext().execute_with(|| {
let para = new_para();
let first_period = 1;
let last_period = 4;
......@@ -1811,7 +1869,7 @@ mod benchmarking {
.ok_or("duration of auction less than zero")?;
T::Auctioneer::new_auction(duration, lease_period_index)?;
assert_eq!(T::Auctioneer::is_ending(end_block), Some(0u32.into()));
assert_eq!(T::Auctioneer::auction_status(end_block).is_ending(), Some((0u32.into(), 0u32.into())));
assert_eq!(NewRaise::<T>::get().len(), n as usize);
let old_endings_count = EndingsCount::<T>::get();
}: {
......
......@@ -41,7 +41,7 @@ use crate::{
auctions, crowdloan, slots, paras_registrar,
slot_range::SlotRange,
traits::{
Registrar as RegistrarT, Auctioneer,
Registrar as RegistrarT, Auctioneer, AuctionStatus,
},
};
......@@ -868,7 +868,7 @@ fn crowdloan_ending_period_bid() {
// Go to beginning of ending period
run_to_block(100);
assert_eq!(Auctions::is_ending(100), Some(