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::{ ...@@ -27,7 +27,7 @@ use frame_support::{
}; };
use primitives::v1::Id as ParaId; use primitives::v1::Id as ParaId;
use crate::slot_range::SlotRange; 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; use parity_scale_codec::Decode;
pub use pallet::*; pub use pallet::*;
...@@ -201,7 +201,7 @@ pub mod 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 // 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 // winner information is duplicated from the previous block in case no bids happened in the
// last block. // 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)); weight = weight.saturating_add(T::DbWeight::get().reads(1));
if !Winning::<T>::contains_key(&offset) { if !Winning::<T>::contains_key(&offset) {
weight = weight.saturating_add(T::DbWeight::get().writes(1)); weight = weight.saturating_add(T::DbWeight::get().writes(1));
...@@ -241,19 +241,7 @@ pub mod pallet { ...@@ -241,19 +241,7 @@ pub mod pallet {
#[pallet::compact] lease_period_index: LeasePeriodOf<T>, #[pallet::compact] lease_period_index: LeasePeriodOf<T>,
) -> DispatchResult { ) -> DispatchResult {
T::InitiateOrigin::ensure_origin(origin)?; T::InitiateOrigin::ensure_origin(origin)?;
Self::do_new_auction(duration, lease_period_index)
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(())
} }
/// Make a new bid from an account (including a parachain account) for deploying a new /// 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> { ...@@ -316,16 +304,28 @@ impl<T: Config> Auctioneer for Pallet<T> {
Self::do_new_auction(duration, lease_period_index) Self::do_new_auction(duration, lease_period_index)
} }
// Returns whether the auction is ending, and which sample number we are on. // Returns the status of the auction given the current block number.
fn is_ending(now: Self::BlockNumber) -> Option<Self::BlockNumber> { fn auction_status(now: Self::BlockNumber) -> AuctionStatus<Self::BlockNumber> {
if let Some((_, early_end)) = AuctionInfo::<T>::get() { let early_end = match AuctionInfo::<T>::get() {
if let Some(after_early_end) = now.checked_sub(&early_end) { Some((_, early_end)) => early_end,
if after_early_end < T::EndingPeriod::get() { None => return AuctionStatus::NotStarted,
return Some(after_early_end / T::SampleLength::get()) };
}
} 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( fn place_bid(
...@@ -355,17 +355,6 @@ impl<T: Config> Pallet<T> { ...@@ -355,17 +355,6 @@ impl<T: Config> Pallet<T> {
// A trick to allow me to initialize large arrays with nothing in them. // 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; 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. /// Create a new auction.
/// ///
/// This can only happen when there isn't already an auction in progress. Accepts the `duration` /// 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> { ...@@ -375,7 +364,8 @@ impl<T: Config> Pallet<T> {
duration: T::BlockNumber, duration: T::BlockNumber,
lease_period_index: LeasePeriodOf<T>, lease_period_index: LeasePeriodOf<T>,
) -> DispatchResult { ) -> 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); ensure!(lease_period_index >= T::Leaser::lease_period_index(), Error::<T>::LeasePeriodInPast);
// Bump the counter. // Bump the counter.
...@@ -410,21 +400,24 @@ impl<T: Config> Pallet<T> { ...@@ -410,21 +400,24 @@ impl<T: Config> Pallet<T> {
// Bidding on latest auction. // Bidding on latest auction.
ensure!(auction_index == AuctionCounter::<T>::get(), Error::<T>::NotCurrentAuction); ensure!(auction_index == AuctionCounter::<T>::get(), Error::<T>::NotCurrentAuction);
// Assume it's actually an auction (this should never fail because of above). // 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 (first_lease_period, _) = AuctionInfo::<T>::get().ok_or(Error::<T>::NotAuction)?;
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 // Get the auction status and the current sample block. For the starting period, the sample
// info around because we haven't yet decided *exactly* when in the `EndingPeriod` that it ended. // block is zero.
let now = frame_system::Pallet::<T>::block_number(); let auction_status = Self::auction_status(frame_system::Pallet::<T>::block_number());
ensure!(now < late_end, Error::<T>::AuctionEnded); // 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. // Our range.
let range = SlotRange::new_bounded(first_lease_period, first_slot, last_slot)?; let range = SlotRange::new_bounded(first_lease_period, first_slot, last_slot)?;
// Range as an array index. // Range as an array index.
let range_index = range as u8 as usize; 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. // The current winning ranges.
let mut current_winning = Winning::<T>::get(offset) let mut current_winning = Winning::<T>::get(offset)
.or_else(|| offset.checked_sub(&One::one()).and_then(Winning::<T>::get)) .or_else(|| offset.checked_sub(&One::one()).and_then(Winning::<T>::get))
...@@ -463,7 +456,7 @@ impl<T: Config> Pallet<T> { ...@@ -463,7 +456,7 @@ impl<T: Config> Pallet<T> {
let mut outgoing_winner = Some((bidder.clone(), para, amount)); let mut outgoing_winner = Some((bidder.clone(), para, amount));
swap(&mut current_winning[range_index], &mut outgoing_winner); swap(&mut current_winning[range_index], &mut outgoing_winner);
if let Some((who, para, _amount)) = 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) .filter_map(Option::as_ref)
.all(|&(ref other, other_para, _)| other != &who || other_para != para) .all(|&(ref other, other_para, _)| other != &who || other_para != para)
{ {
...@@ -504,7 +497,7 @@ impl<T: Config> Pallet<T> { ...@@ -504,7 +497,7 @@ impl<T: Config> Pallet<T> {
// Our random seed was known only after the auction ended. Good to use. // 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()) 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"); .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(); let auction_counter = AuctionCounter::<T>::get();
Self::deposit_event(Event::<T>::WinningOffset(auction_counter, offset)); Self::deposit_event(Event::<T>::WinningOffset(auction_counter, offset));
...@@ -835,15 +828,13 @@ mod tests { ...@@ -835,15 +828,13 @@ mod tests {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
assert_eq!(AuctionCounter::<Test>::get(), 0); assert_eq!(AuctionCounter::<Test>::get(), 0);
assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0); assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
assert_eq!(Auctions::is_in_progress(), false); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(10); run_to_block(10);
assert_eq!(AuctionCounter::<Test>::get(), 0); assert_eq!(AuctionCounter::<Test>::get(), 0);
assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0); assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
assert_eq!(Auctions::is_in_progress(), false); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
assert_eq!(Auctions::is_ending(System::block_number()), None);
}); });
} }
...@@ -856,8 +847,7 @@ mod tests { ...@@ -856,8 +847,7 @@ mod tests {
assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1)); assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1));
assert_eq!(AuctionCounter::<Test>::get(), 1); assert_eq!(AuctionCounter::<Test>::get(), 1);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
}); });
} }
...@@ -918,40 +908,31 @@ mod tests { ...@@ -918,40 +908,31 @@ mod tests {
assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1)); assert_ok!(Auctions::new_auction(Origin::signed(6), 5, 1));
assert_eq!(AuctionCounter::<Test>::get(), 1); assert_eq!(AuctionCounter::<Test>::get(), 1);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(2); run_to_block(2);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(3); run_to_block(3);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(4); run_to_block(4);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(5); run_to_block(5);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
assert_eq!(Auctions::is_ending(System::block_number()), None);
run_to_block(6); run_to_block(6);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(0, 0));
assert_eq!(Auctions::is_ending(System::block_number()), Some(0));
run_to_block(7); run_to_block(7);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(1, 0));
assert_eq!(Auctions::is_ending(System::block_number()), Some(1));
run_to_block(8); run_to_block(8);
assert_eq!(Auctions::is_in_progress(), true); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::EndingPeriod(2, 0));
assert_eq!(Auctions::is_ending(System::block_number()), Some(2));
run_to_block(9); run_to_block(9);
assert_eq!(Auctions::is_in_progress(), false); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::NotStarted);
assert_eq!(Auctions::is_ending(System::block_number()), None);
}); });
} }
...@@ -983,19 +964,19 @@ mod tests { ...@@ -983,19 +964,19 @@ mod tests {
assert_ok!(Auctions::bid(Origin::signed(1), 0.into(), 1, 1, 4, 1)); assert_ok!(Auctions::bid(Origin::signed(1), 0.into(), 1, 1, 4, 1));
assert_eq!(Balances::reserved_balance(1), 1); assert_eq!(Balances::reserved_balance(1), 1);
assert_eq!(Balances::free_balance(1), 9); assert_eq!(Balances::free_balance(1), 9);
assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::StartingPeriod);
run_to_block(8); run_to_block(8);
// Auction has not yet ended. // Auction has not yet ended.
assert_eq!(leases(), vec![]); 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 // 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. // seed was known before the final bids were made.
set_last_random(H256::zero(), 8); set_last_random(H256::zero(), 8);
// Auction definitely ended now, but we don't know exactly when in the last 3 blocks yet since // Auction definitely ended now, but we don't know exactly when in the last 3 blocks yet since
// no randomness available yet. // no randomness available yet.
run_to_block(9); run_to_block(9);
// Auction has now ended... // Auction has now ended... But auction winner still not yet decided, so no leases yet.
assert!(!Auctions::is_in_progress()); assert_eq!(Auctions::auction_status(System::block_number()), AuctionStatus::<u32>::VrfDelay(0));
// ...But auction winner still not yet decided, so no leases yet.
assert_eq!(leases(), vec![]); assert_eq!(leases(), vec![]);
// Random seed now updated to a value known at block 9, when the auction ended. This means // Random seed now updated to a value known at block 9, when the auction ended. This means
...@@ -1003,7 +984,7 @@ mod tests { ...@@ -1003,7 +984,7 @@ mod tests {
set_last_random(H256::zero(), 9); set_last_random(H256::zero(), 9);
run_to_block(10); run_to_block(10);
// Auction ended and winner selected // 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![ assert_eq!(leases(), vec![
((0.into(), 1), LeaseData { leaser: 1, amount: 1 }), ((0.into(), 1), LeaseData { leaser: 1, amount: 1 }),
((0.into(), 2), LeaseData { leaser: 1, amount: 1 }), ((0.into(), 2), LeaseData { leaser: 1, amount: 1 }),
...@@ -1289,26 +1270,26 @@ mod tests { ...@@ -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(1), para_1, 1, 1, 4, 10));
assert_ok!(Auctions::bid(Origin::signed(2), para_2, 1, 3, 4, 20)); 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]; let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10)); winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10));
winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20)); winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20));
assert_eq!(Auctions::winning(0), Some(winning)); assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(9); 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); 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)); assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(11); 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_eq!(Auctions::winning(1), Some(winning));
assert_ok!(Auctions::bid(Origin::signed(3), para_3, 1, 3, 4, 30)); assert_ok!(Auctions::bid(Origin::signed(3), para_3, 1, 3, 4, 30));
run_to_block(12); 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)); winning[SlotRange::TwoThree as u8 as usize] = Some((3, para_3, 30));
assert_eq!(Auctions::winning(2), Some(winning)); assert_eq!(Auctions::winning(2), Some(winning));
}); });
...@@ -1342,17 +1323,17 @@ mod tests { ...@@ -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(1), para_1, 1, 11, 14, 10));
assert_ok!(Auctions::bid(Origin::signed(2), para_2, 1, 13, 14, 20)); 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]; let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10)); winning[SlotRange::ZeroThree as u8 as usize] = Some((1, para_1, 10));
winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20)); winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 20));
assert_eq!(Auctions::winning(0), Some(winning)); assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(9); 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); 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)); assert_eq!(Auctions::winning(0), Some(winning));
// New bids update the current winning // New bids update the current winning
...@@ -1361,7 +1342,7 @@ mod tests { ...@@ -1361,7 +1342,7 @@ mod tests {
assert_eq!(Auctions::winning(0), Some(winning)); assert_eq!(Auctions::winning(0), Some(winning));
run_to_block(20); 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)); assert_eq!(Auctions::winning(1), Some(winning));
run_to_block(25); run_to_block(25);
// Overbid mid sample // Overbid mid sample
...@@ -1370,13 +1351,13 @@ mod tests { ...@@ -1370,13 +1351,13 @@ mod tests {
assert_eq!(Auctions::winning(1), Some(winning)); assert_eq!(Auctions::winning(1), Some(winning));
run_to_block(30); 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)); assert_eq!(Auctions::winning(2), Some(winning));
set_last_random(H256::from([254; 32]), 40); set_last_random(H256::from([254; 32]), 40);
run_to_block(40); run_to_block(40);
// Auction ended and winner selected // 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![ assert_eq!(leases(), vec![
((3.into(), 13), LeaseData { leaser: 3, amount: 30 }), ((3.into(), 13), LeaseData { leaser: 3, amount: 30 }),
((3.into(), 14), LeaseData { leaser: 3, amount: 30 }), ((3.into(), 14), LeaseData { leaser: 3, amount: 30 }),
...@@ -1384,6 +1365,54 @@ mod tests { ...@@ -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] #[test]
fn can_cancel_auction() { fn can_cancel_auction() {
new_test_ext().execute_with(|| { new_test_ext().execute_with(|| {
......
...@@ -289,14 +289,17 @@ pub mod pallet { ...@@ -289,14 +289,17 @@ pub mod pallet {
/// The provided memo is too large. /// The provided memo is too large.
MemoTooLarge, MemoTooLarge,
/// The fund is already in NewRaise /// The fund is already in NewRaise
AlreadyInNewRaise AlreadyInNewRaise,
/// No contributions allowed during the VRF delay
VrfDelayInProgress,
} }
#[pallet::hooks] #[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
fn on_initialize(n: T::BlockNumber) -> frame_support::weights::Weight { fn on_initialize(num: T::BlockNumber) -> frame_support::weights::Weight {
if let Some(n) = T::Auctioneer::is_ending(n) { if let Some((sample, sub_sample)) = T::Auctioneer::auction_status(num).is_ending() {
if n.is_zero() { // This is the very first block in the ending period
if sample.is_zero() && sub_sample.is_zero() {
// first block of ending period. // first block of ending period.
EndingsCount::<T>::mutate(|c| *c += 1); EndingsCount::<T>::mutate(|c| *c += 1);
} }
...@@ -413,6 +416,10 @@ pub mod pallet { ...@@ -413,6 +416,10 @@ pub mod pallet {
let fund_account = Self::fund_account_id(index); let fund_account = Self::fund_account_id(index);
ensure!(!T::Auctioneer::has_won_an_auction(index, &fund_account), Error::<T>::BidOrLeaseActive); 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); 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 {
...@@ -427,7 +434,7 @@ pub mod pallet { ...