1
// Copyright (C) Parity Technologies (UK) Ltd.
2
// This file is part of Polkadot.
3

            
4
// Polkadot is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8

            
9
// Polkadot is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13

            
14
// You should have received a copy of the GNU General Public License
15
// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16

            
17
//! Auctioning system to determine the set of Parachains in operation. This includes logic for the
18
//! auctioning mechanism and for reserving balance as part of the "payment". Unreserving the balance
19
//! happens elsewhere.
20

            
21
use crate::{
22
	slot_range::SlotRange,
23
	traits::{AuctionStatus, Auctioneer, LeaseError, Leaser, Registrar},
24
};
25
use alloc::{vec, vec::Vec};
26
use codec::Decode;
27
use core::mem::swap;
28
use frame_support::{
29
	dispatch::DispatchResult,
30
	ensure,
31
	traits::{Currency, Get, Randomness, ReservableCurrency},
32
	weights::Weight,
33
};
34
use frame_system::pallet_prelude::BlockNumberFor;
35
pub use pallet::*;
36
use polkadot_primitives::Id as ParaId;
37
use sp_runtime::traits::{CheckedSub, One, Saturating, Zero};
38

            
39
type CurrencyOf<T> = <<T as Config>::Leaser as Leaser<BlockNumberFor<T>>>::Currency;
40
type BalanceOf<T> = <<<T as Config>::Leaser as Leaser<BlockNumberFor<T>>>::Currency as Currency<
41
	<T as frame_system::Config>::AccountId,
42
>>::Balance;
43

            
44
pub trait WeightInfo {
45
	fn new_auction() -> Weight;
46
	fn bid() -> Weight;
47
	fn cancel_auction() -> Weight;
48
	fn on_initialize() -> Weight;
49
}
50

            
51
pub struct TestWeightInfo;
52
impl WeightInfo for TestWeightInfo {
53
	fn new_auction() -> Weight {
54
		Weight::zero()
55
	}
56
	fn bid() -> Weight {
57
		Weight::zero()
58
	}
59
	fn cancel_auction() -> Weight {
60
		Weight::zero()
61
	}
62
	fn on_initialize() -> Weight {
63
		Weight::zero()
64
	}
65
}
66

            
67
/// An auction index. We count auctions in this type.
68
pub type AuctionIndex = u32;
69

            
70
type LeasePeriodOf<T> = <<T as Config>::Leaser as Leaser<BlockNumberFor<T>>>::LeasePeriod;
71

            
72
// Winning data type. This encodes the top bidders of each range together with their bid.
73
type WinningData<T> = [Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)>;
74
	SlotRange::SLOT_RANGE_COUNT];
75
// Winners data type. This encodes each of the final winners of a parachain auction, the parachain
76
// index assigned to them, their winning bid and the range that they won.
77
type WinnersData<T> =
78
	Vec<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>, SlotRange)>;
79

            
80
#[frame_support::pallet]
81
pub mod pallet {
82
	use super::*;
83
	use frame_support::{dispatch::DispatchClass, pallet_prelude::*, traits::EnsureOrigin};
84
	use frame_system::{ensure_root, ensure_signed, pallet_prelude::*};
85

            
86
	#[pallet::pallet]
87
	pub struct Pallet<T>(_);
88

            
89
	/// The module's configuration trait.
90
	#[pallet::config]
91
	pub trait Config: frame_system::Config {
92
		/// The overarching event type.
93
		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
94

            
95
		/// The type representing the leasing system.
96
		type Leaser: Leaser<
97
			BlockNumberFor<Self>,
98
			AccountId = Self::AccountId,
99
			LeasePeriod = BlockNumberFor<Self>,
100
		>;
101

            
102
		/// The parachain registrar type.
103
		type Registrar: Registrar<AccountId = Self::AccountId>;
104

            
105
		/// The number of blocks over which an auction may be retroactively ended.
106
		#[pallet::constant]
107
		type EndingPeriod: Get<BlockNumberFor<Self>>;
108

            
109
		/// The length of each sample to take during the ending period.
110
		///
111
		/// `EndingPeriod` / `SampleLength` = Total # of Samples
112
		#[pallet::constant]
113
		type SampleLength: Get<BlockNumberFor<Self>>;
114

            
115
		/// Something that provides randomness in the runtime.
116
		type Randomness: Randomness<Self::Hash, BlockNumberFor<Self>>;
117

            
118
		/// The origin which may initiate auctions.
119
		type InitiateOrigin: EnsureOrigin<Self::RuntimeOrigin>;
120

            
121
		/// Weight Information for the Extrinsics in the Pallet
122
		type WeightInfo: WeightInfo;
123
	}
124

            
125
	#[pallet::event]
126
	#[pallet::generate_deposit(pub(super) fn deposit_event)]
127
	pub enum Event<T: Config> {
128
		/// An auction started. Provides its index and the block number where it will begin to
129
		/// close and the first lease period of the quadruplet that is auctioned.
130
		AuctionStarted {
131
			auction_index: AuctionIndex,
132
			lease_period: LeasePeriodOf<T>,
133
			ending: BlockNumberFor<T>,
134
		},
135
		/// An auction ended. All funds become unreserved.
136
		AuctionClosed { auction_index: AuctionIndex },
137
		/// Funds were reserved for a winning bid. First balance is the extra amount reserved.
138
		/// Second is the total.
139
		Reserved { bidder: T::AccountId, extra_reserved: BalanceOf<T>, total_amount: BalanceOf<T> },
140
		/// Funds were unreserved since bidder is no longer active. `[bidder, amount]`
141
		Unreserved { bidder: T::AccountId, amount: BalanceOf<T> },
142
		/// Someone attempted to lease the same slot twice for a parachain. The amount is held in
143
		/// reserve but no parachain slot has been leased.
144
		ReserveConfiscated { para_id: ParaId, leaser: T::AccountId, amount: BalanceOf<T> },
145
		/// A new bid has been accepted as the current winner.
146
		BidAccepted {
147
			bidder: T::AccountId,
148
			para_id: ParaId,
149
			amount: BalanceOf<T>,
150
			first_slot: LeasePeriodOf<T>,
151
			last_slot: LeasePeriodOf<T>,
152
		},
153
		/// The winning offset was chosen for an auction. This will map into the `Winning` storage
154
		/// map.
155
		WinningOffset { auction_index: AuctionIndex, block_number: BlockNumberFor<T> },
156
	}
157

            
158
	#[pallet::error]
159
	pub enum Error<T> {
160
		/// This auction is already in progress.
161
		AuctionInProgress,
162
		/// The lease period is in the past.
163
		LeasePeriodInPast,
164
		/// Para is not registered
165
		ParaNotRegistered,
166
		/// Not a current auction.
167
		NotCurrentAuction,
168
		/// Not an auction.
169
		NotAuction,
170
		/// Auction has already ended.
171
		AuctionEnded,
172
		/// The para is already leased out for part of this range.
173
		AlreadyLeasedOut,
174
	}
175

            
176
	/// Number of auctions started so far.
177
	#[pallet::storage]
178
	pub type AuctionCounter<T> = StorageValue<_, AuctionIndex, ValueQuery>;
179

            
180
	/// Information relating to the current auction, if there is one.
181
	///
182
	/// The first item in the tuple is the lease period index that the first of the four
183
	/// contiguous lease periods on auction is for. The second is the block number when the
184
	/// auction will "begin to end", i.e. the first block of the Ending Period of the auction.
185
	#[pallet::storage]
186
	pub type AuctionInfo<T: Config> = StorageValue<_, (LeasePeriodOf<T>, BlockNumberFor<T>)>;
187

            
188
	/// Amounts currently reserved in the accounts of the bidders currently winning
189
	/// (sub-)ranges.
190
	#[pallet::storage]
191
	pub type ReservedAmounts<T: Config> =
192
		StorageMap<_, Twox64Concat, (T::AccountId, ParaId), BalanceOf<T>>;
193

            
194
	/// The winning bids for each of the 10 ranges at each sample in the final Ending Period of
195
	/// the current auction. The map's key is the 0-based index into the Sample Size. The
196
	/// first sample of the ending period is 0; the last is `Sample Size - 1`.
197
	#[pallet::storage]
198
	pub type Winning<T: Config> = StorageMap<_, Twox64Concat, BlockNumberFor<T>, WinningData<T>>;
199

            
200
	#[pallet::extra_constants]
201
	impl<T: Config> Pallet<T> {
202
		#[pallet::constant_name(SlotRangeCount)]
203
		fn slot_range_count() -> u32 {
204
			SlotRange::SLOT_RANGE_COUNT as u32
205
		}
206

            
207
		#[pallet::constant_name(LeasePeriodsPerSlot)]
208
		fn lease_periods_per_slot() -> u32 {
209
			SlotRange::LEASE_PERIODS_PER_SLOT as u32
210
		}
211
	}
212

            
213
	#[pallet::hooks]
214
	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
215
		fn on_initialize(n: BlockNumberFor<T>) -> Weight {
216
			let mut weight = T::DbWeight::get().reads(1);
217

            
218
			// If the current auction was in its ending period last block, then ensure that the
219
			// (sub-)range winner information is duplicated from the previous block in case no bids
220
			// happened in the last block.
221
			if let AuctionStatus::EndingPeriod(offset, _sub_sample) = Self::auction_status(n) {
222
				weight = weight.saturating_add(T::DbWeight::get().reads(1));
223
				if !Winning::<T>::contains_key(&offset) {
224
					weight = weight.saturating_add(T::DbWeight::get().writes(1));
225
					let winning_data = offset
226
						.checked_sub(&One::one())
227
						.and_then(Winning::<T>::get)
228
						.unwrap_or([Self::EMPTY; SlotRange::SLOT_RANGE_COUNT]);
229
					Winning::<T>::insert(offset, winning_data);
230
				}
231
			}
232

            
233
			// Check to see if an auction just ended.
234
			if let Some((winning_ranges, auction_lease_period_index)) = Self::check_auction_end(n) {
235
				// Auction is ended now. We have the winning ranges and the lease period index which
236
				// acts as the offset. Handle it.
237
				Self::manage_auction_end(auction_lease_period_index, winning_ranges);
238
				weight = weight.saturating_add(T::WeightInfo::on_initialize());
239
			}
240

            
241
			weight
242
		}
243
	}
244

            
245
	#[pallet::call]
246
	impl<T: Config> Pallet<T> {
247
		/// Create a new auction.
248
		///
249
		/// This can only happen when there isn't already an auction in progress and may only be
250
		/// called by the root origin. Accepts the `duration` of this auction and the
251
		/// `lease_period_index` of the initial lease period of the four that are to be auctioned.
252
		#[pallet::call_index(0)]
253
		#[pallet::weight((T::WeightInfo::new_auction(), DispatchClass::Operational))]
254
		pub fn new_auction(
255
			origin: OriginFor<T>,
256
			#[pallet::compact] duration: BlockNumberFor<T>,
257
			#[pallet::compact] lease_period_index: LeasePeriodOf<T>,
258
		) -> DispatchResult {
259
			T::InitiateOrigin::ensure_origin(origin)?;
260
			Self::do_new_auction(duration, lease_period_index)
261
		}
262

            
263
		/// Make a new bid from an account (including a parachain account) for deploying a new
264
		/// parachain.
265
		///
266
		/// Multiple simultaneous bids from the same bidder are allowed only as long as all active
267
		/// bids overlap each other (i.e. are mutually exclusive). Bids cannot be redacted.
268
		///
269
		/// - `sub` is the sub-bidder ID, allowing for multiple competing bids to be made by (and
270
		/// funded by) the same account.
271
		/// - `auction_index` is the index of the auction to bid on. Should just be the present
272
		/// value of `AuctionCounter`.
273
		/// - `first_slot` is the first lease period index of the range to bid on. This is the
274
		/// absolute lease period index value, not an auction-specific offset.
275
		/// - `last_slot` is the last lease period index of the range to bid on. This is the
276
		/// absolute lease period index value, not an auction-specific offset.
277
		/// - `amount` is the amount to bid to be held as deposit for the parachain should the
278
		/// bid win. This amount is held throughout the range.
279
		#[pallet::call_index(1)]
280
		#[pallet::weight(T::WeightInfo::bid())]
281
		pub fn bid(
282
			origin: OriginFor<T>,
283
			#[pallet::compact] para: ParaId,
284
			#[pallet::compact] auction_index: AuctionIndex,
285
			#[pallet::compact] first_slot: LeasePeriodOf<T>,
286
			#[pallet::compact] last_slot: LeasePeriodOf<T>,
287
			#[pallet::compact] amount: BalanceOf<T>,
288
		) -> DispatchResult {
289
			let who = ensure_signed(origin)?;
290
			Self::handle_bid(who, para, auction_index, first_slot, last_slot, amount)?;
291
			Ok(())
292
		}
293

            
294
		/// Cancel an in-progress auction.
295
		///
296
		/// Can only be called by Root origin.
297
		#[pallet::call_index(2)]
298
		#[pallet::weight(T::WeightInfo::cancel_auction())]
299
		pub fn cancel_auction(origin: OriginFor<T>) -> DispatchResult {
300
			ensure_root(origin)?;
301
			// Unreserve all bids.
302
			for ((bidder, _), amount) in ReservedAmounts::<T>::drain() {
303
				CurrencyOf::<T>::unreserve(&bidder, amount);
304
			}
305
			#[allow(deprecated)]
306
			Winning::<T>::remove_all(None);
307
			AuctionInfo::<T>::kill();
308
			Ok(())
309
		}
310
	}
311
}
312

            
313
impl<T: Config> Auctioneer<BlockNumberFor<T>> for Pallet<T> {
314
	type AccountId = T::AccountId;
315
	type LeasePeriod = BlockNumberFor<T>;
316
	type Currency = CurrencyOf<T>;
317

            
318
	fn new_auction(
319
		duration: BlockNumberFor<T>,
320
		lease_period_index: LeasePeriodOf<T>,
321
	) -> DispatchResult {
322
		Self::do_new_auction(duration, lease_period_index)
323
	}
324

            
325
	// Returns the status of the auction given the current block number.
326
	fn auction_status(now: BlockNumberFor<T>) -> AuctionStatus<BlockNumberFor<T>> {
327
		let early_end = match AuctionInfo::<T>::get() {
328
			Some((_, early_end)) => early_end,
329
			None => return AuctionStatus::NotStarted,
330
		};
331

            
332
		let after_early_end = match now.checked_sub(&early_end) {
333
			Some(after_early_end) => after_early_end,
334
			None => return AuctionStatus::StartingPeriod,
335
		};
336

            
337
		let ending_period = T::EndingPeriod::get();
338
		if after_early_end < ending_period {
339
			let sample_length = T::SampleLength::get().max(One::one());
340
			let sample = after_early_end / sample_length;
341
			let sub_sample = after_early_end % sample_length;
342
			return AuctionStatus::EndingPeriod(sample, sub_sample)
343
		} else {
344
			// This is safe because of the comparison operator above
345
			return AuctionStatus::VrfDelay(after_early_end - ending_period)
346
		}
347
	}
348

            
349
	fn place_bid(
350
		bidder: T::AccountId,
351
		para: ParaId,
352
		first_slot: LeasePeriodOf<T>,
353
		last_slot: LeasePeriodOf<T>,
354
		amount: BalanceOf<T>,
355
	) -> DispatchResult {
356
		Self::handle_bid(bidder, para, AuctionCounter::<T>::get(), first_slot, last_slot, amount)
357
	}
358

            
359
	fn lease_period_index(b: BlockNumberFor<T>) -> Option<(Self::LeasePeriod, bool)> {
360
		T::Leaser::lease_period_index(b)
361
	}
362

            
363
	#[cfg(any(feature = "runtime-benchmarks", test))]
364
	fn lease_period_length() -> (BlockNumberFor<T>, BlockNumberFor<T>) {
365
		T::Leaser::lease_period_length()
366
	}
367

            
368
	fn has_won_an_auction(para: ParaId, bidder: &T::AccountId) -> bool {
369
		!T::Leaser::deposit_held(para, bidder).is_zero()
370
	}
371
}
372

            
373
impl<T: Config> Pallet<T> {
374
	// A trick to allow me to initialize large arrays with nothing in them.
375
	const EMPTY: Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)> = None;
376

            
377
	/// Create a new auction.
378
	///
379
	/// This can only happen when there isn't already an auction in progress. Accepts the `duration`
380
	/// of this auction and the `lease_period_index` of the initial lease period of the four that
381
	/// are to be auctioned.
382
	fn do_new_auction(
383
		duration: BlockNumberFor<T>,
384
		lease_period_index: LeasePeriodOf<T>,
385
	) -> DispatchResult {
386
		let maybe_auction = AuctionInfo::<T>::get();
387
		ensure!(maybe_auction.is_none(), Error::<T>::AuctionInProgress);
388
		let now = frame_system::Pallet::<T>::block_number();
389
		if let Some((current_lease_period, _)) = T::Leaser::lease_period_index(now) {
390
			// If there is no active lease period, then we don't need to make this check.
391
			ensure!(lease_period_index >= current_lease_period, Error::<T>::LeasePeriodInPast);
392
		}
393

            
394
		// Bump the counter.
395
		let n = AuctionCounter::<T>::mutate(|n| {
396
			*n += 1;
397
			*n
398
		});
399

            
400
		// Set the information.
401
		let ending = frame_system::Pallet::<T>::block_number().saturating_add(duration);
402
		AuctionInfo::<T>::put((lease_period_index, ending));
403

            
404
		Self::deposit_event(Event::<T>::AuctionStarted {
405
			auction_index: n,
406
			lease_period: lease_period_index,
407
			ending,
408
		});
409
		Ok(())
410
	}
411

            
412
	/// Actually place a bid in the current auction.
413
	///
414
	/// - `bidder`: The account that will be funding this bid.
415
	/// - `auction_index`: The auction index of the bid. For this to succeed, must equal
416
	/// the current value of `AuctionCounter`.
417
	/// - `first_slot`: The first lease period index of the range to be bid on.
418
	/// - `last_slot`: The last lease period index of the range to be bid on (inclusive).
419
	/// - `amount`: The total amount to be the bid for deposit over the range.
420
	pub fn handle_bid(
421
		bidder: T::AccountId,
422
		para: ParaId,
423
		auction_index: u32,
424
		first_slot: LeasePeriodOf<T>,
425
		last_slot: LeasePeriodOf<T>,
426
		amount: BalanceOf<T>,
427
	) -> DispatchResult {
428
		// Ensure para is registered before placing a bid on it.
429
		ensure!(T::Registrar::is_registered(para), Error::<T>::ParaNotRegistered);
430
		// Bidding on latest auction.
431
		ensure!(auction_index == AuctionCounter::<T>::get(), Error::<T>::NotCurrentAuction);
432
		// Assume it's actually an auction (this should never fail because of above).
433
		let (first_lease_period, _) = AuctionInfo::<T>::get().ok_or(Error::<T>::NotAuction)?;
434

            
435
		// Get the auction status and the current sample block. For the starting period, the sample
436
		// block is zero.
437
		let auction_status = Self::auction_status(frame_system::Pallet::<T>::block_number());
438
		// The offset into the ending samples of the auction.
439
		let offset = match auction_status {
440
			AuctionStatus::NotStarted => return Err(Error::<T>::AuctionEnded.into()),
441
			AuctionStatus::StartingPeriod => Zero::zero(),
442
			AuctionStatus::EndingPeriod(o, _) => o,
443
			AuctionStatus::VrfDelay(_) => return Err(Error::<T>::AuctionEnded.into()),
444
		};
445

            
446
		// We also make sure that the bid is not for any existing leases the para already has.
447
		ensure!(
448
			!T::Leaser::already_leased(para, first_slot, last_slot),
449
			Error::<T>::AlreadyLeasedOut
450
		);
451

            
452
		// Our range.
453
		let range = SlotRange::new_bounded(first_lease_period, first_slot, last_slot)?;
454
		// Range as an array index.
455
		let range_index = range as u8 as usize;
456

            
457
		// The current winning ranges.
458
		let mut current_winning = Winning::<T>::get(offset)
459
			.or_else(|| offset.checked_sub(&One::one()).and_then(Winning::<T>::get))
460
			.unwrap_or([Self::EMPTY; SlotRange::SLOT_RANGE_COUNT]);
461

            
462
		// If this bid beat the previous winner of our range.
463
		if current_winning[range_index].as_ref().map_or(true, |last| amount > last.2) {
464
			// Ok; we are the new winner of this range - reserve the additional amount and record.
465

            
466
			// Get the amount already held on deposit if this is a renewal bid (i.e. there's
467
			// an existing lease on the same para by the same leaser).
468
			let existing_lease_deposit = T::Leaser::deposit_held(para, &bidder);
469
			let reserve_required = amount.saturating_sub(existing_lease_deposit);
470

            
471
			// Get the amount already reserved in any prior and still active bids by us.
472
			let bidder_para = (bidder.clone(), para);
473
			let already_reserved = ReservedAmounts::<T>::get(&bidder_para).unwrap_or_default();
474

            
475
			// If these don't already cover the bid...
476
			if let Some(additional) = reserve_required.checked_sub(&already_reserved) {
477
				// ...then reserve some more funds from their account, failing if there's not
478
				// enough funds.
479
				CurrencyOf::<T>::reserve(&bidder, additional)?;
480
				// ...and record the amount reserved.
481
				ReservedAmounts::<T>::insert(&bidder_para, reserve_required);
482

            
483
				Self::deposit_event(Event::<T>::Reserved {
484
					bidder: bidder.clone(),
485
					extra_reserved: additional,
486
					total_amount: reserve_required,
487
				});
488
			}
489

            
490
			// Return any funds reserved for the previous winner if we are not in the ending period
491
			// and they no longer have any active bids.
492
			let mut outgoing_winner = Some((bidder.clone(), para, amount));
493
			swap(&mut current_winning[range_index], &mut outgoing_winner);
494
			if let Some((who, para, _amount)) = outgoing_winner {
495
				if auction_status.is_starting() &&
496
					current_winning
497
						.iter()
498
						.filter_map(Option::as_ref)
499
						.all(|&(ref other, other_para, _)| other != &who || other_para != para)
500
				{
501
					// Previous bidder is no longer winning any ranges: unreserve their funds.
502
					if let Some(amount) = ReservedAmounts::<T>::take(&(who.clone(), para)) {
503
						// It really should be reserved; there's not much we can do here on fail.
504
						let err_amt = CurrencyOf::<T>::unreserve(&who, amount);
505
						debug_assert!(err_amt.is_zero());
506
						Self::deposit_event(Event::<T>::Unreserved { bidder: who, amount });
507
					}
508
				}
509
			}
510

            
511
			// Update the range winner.
512
			Winning::<T>::insert(offset, &current_winning);
513
			Self::deposit_event(Event::<T>::BidAccepted {
514
				bidder,
515
				para_id: para,
516
				amount,
517
				first_slot,
518
				last_slot,
519
			});
520
		}
521
		Ok(())
522
	}
523

            
524
	/// Some when the auction's end is known (with the end block number). None if it is unknown.
525
	/// If `Some` then the block number must be at most the previous block and at least the
526
	/// previous block minus `T::EndingPeriod::get()`.
527
	///
528
	/// This mutates the state, cleaning up `AuctionInfo` and `Winning` in the case of an auction
529
	/// ending. An immediately subsequent call with the same argument will always return `None`.
530
	fn check_auction_end(now: BlockNumberFor<T>) -> Option<(WinningData<T>, LeasePeriodOf<T>)> {
531
		if let Some((lease_period_index, early_end)) = AuctionInfo::<T>::get() {
532
			let ending_period = T::EndingPeriod::get();
533
			let late_end = early_end.saturating_add(ending_period);
534
			let is_ended = now >= late_end;
535
			if is_ended {
536
				// auction definitely ended.
537
				// check to see if we can determine the actual ending point.
538
				let (raw_offset, known_since) = T::Randomness::random(&b"para_auction"[..]);
539

            
540
				if late_end <= known_since {
541
					// Our random seed was known only after the auction ended. Good to use.
542
					let raw_offset_block_number = <BlockNumberFor<T>>::decode(
543
						&mut raw_offset.as_ref(),
544
					)
545
					.expect("secure hashes should always be bigger than the block number; qed");
546
					let offset = (raw_offset_block_number % ending_period) /
547
						T::SampleLength::get().max(One::one());
548

            
549
					let auction_counter = AuctionCounter::<T>::get();
550
					Self::deposit_event(Event::<T>::WinningOffset {
551
						auction_index: auction_counter,
552
						block_number: offset,
553
					});
554
					let res = Winning::<T>::get(offset)
555
						.unwrap_or([Self::EMPTY; SlotRange::SLOT_RANGE_COUNT]);
556
					// This `remove_all` statement should remove at most `EndingPeriod` /
557
					// `SampleLength` items, which should be bounded and sensibly configured in the
558
					// runtime.
559
					#[allow(deprecated)]
560
					Winning::<T>::remove_all(None);
561
					AuctionInfo::<T>::kill();
562
					return Some((res, lease_period_index))
563
				}
564
			}
565
		}
566
		None
567
	}
568

            
569
	/// Auction just ended. We have the current lease period, the auction's lease period (which
570
	/// is guaranteed to be at least the current period) and the bidders that were winning each
571
	/// range at the time of the auction's close.
572
	fn manage_auction_end(
573
		auction_lease_period_index: LeasePeriodOf<T>,
574
		winning_ranges: WinningData<T>,
575
	) {
576
		// First, unreserve all amounts that were reserved for the bids. We will later re-reserve
577
		// the amounts from the bidders that ended up being assigned the slot so there's no need to
578
		// special-case them here.
579
		for ((bidder, _), amount) in ReservedAmounts::<T>::drain() {
580
			CurrencyOf::<T>::unreserve(&bidder, amount);
581
		}
582

            
583
		// Next, calculate the winning combination of slots and thus the final winners of the
584
		// auction.
585
		let winners = Self::calculate_winners(winning_ranges);
586

            
587
		// Go through those winners and re-reserve their bid, updating our table of deposits
588
		// accordingly.
589
		for (leaser, para, amount, range) in winners.into_iter() {
590
			let begin_offset = LeasePeriodOf::<T>::from(range.as_pair().0 as u32);
591
			let period_begin = auction_lease_period_index + begin_offset;
592
			let period_count = LeasePeriodOf::<T>::from(range.len() as u32);
593

            
594
			match T::Leaser::lease_out(para, &leaser, amount, period_begin, period_count) {
595
				Err(LeaseError::ReserveFailed) |
596
				Err(LeaseError::AlreadyEnded) |
597
				Err(LeaseError::NoLeasePeriod) => {
598
					// Should never happen since we just unreserved this amount (and our offset is
599
					// from the present period). But if it does, there's not much we can do.
600
				},
601
				Err(LeaseError::AlreadyLeased) => {
602
					// The leaser attempted to get a second lease on the same para ID, possibly
603
					// griefing us. Let's keep the amount reserved and let governance sort it out.
604
					if CurrencyOf::<T>::reserve(&leaser, amount).is_ok() {
605
						Self::deposit_event(Event::<T>::ReserveConfiscated {
606
							para_id: para,
607
							leaser,
608
							amount,
609
						});
610
					}
611
				},
612
				Ok(()) => {}, // Nothing to report.
613
			}
614
		}
615

            
616
		Self::deposit_event(Event::<T>::AuctionClosed {
617
			auction_index: AuctionCounter::<T>::get(),
618
		});
619
	}
620

            
621
	/// Calculate the final winners from the winning slots.
622
	///
623
	/// This is a simple dynamic programming algorithm designed by Al, the original code is at:
624
	/// `https://github.com/w3f/consensus/blob/master/NPoS/auctiondynamicthing.py`
625
	fn calculate_winners(mut winning: WinningData<T>) -> WinnersData<T> {
626
		let winning_ranges = {
627
			let mut best_winners_ending_at: [(Vec<SlotRange>, BalanceOf<T>);
628
				SlotRange::LEASE_PERIODS_PER_SLOT] = Default::default();
629
			let best_bid = |range: SlotRange| {
630
				winning[range as u8 as usize]
631
					.as_ref()
632
					.map(|(_, _, amount)| *amount * (range.len() as u32).into())
633
			};
634
			for i in 0..SlotRange::LEASE_PERIODS_PER_SLOT {
635
				let r = SlotRange::new_bounded(0, 0, i as u32).expect("`i < LPPS`; qed");
636
				if let Some(bid) = best_bid(r) {
637
					best_winners_ending_at[i] = (vec![r], bid);
638
				}
639
				for j in 0..i {
640
					let r = SlotRange::new_bounded(0, j as u32 + 1, i as u32)
641
						.expect("`i < LPPS`; `j < i`; `j + 1 < LPPS`; qed");
642
					if let Some(mut bid) = best_bid(r) {
643
						bid += best_winners_ending_at[j].1;
644
						if bid > best_winners_ending_at[i].1 {
645
							let mut new_winners = best_winners_ending_at[j].0.clone();
646
							new_winners.push(r);
647
							best_winners_ending_at[i] = (new_winners, bid);
648
						}
649
					} else {
650
						if best_winners_ending_at[j].1 > best_winners_ending_at[i].1 {
651
							best_winners_ending_at[i] = best_winners_ending_at[j].clone();
652
						}
653
					}
654
				}
655
			}
656
			best_winners_ending_at[SlotRange::LEASE_PERIODS_PER_SLOT - 1].0.clone()
657
		};
658

            
659
		winning_ranges
660
			.into_iter()
661
			.filter_map(|range| {
662
				winning[range as u8 as usize]
663
					.take()
664
					.map(|(bidder, para, amount)| (bidder, para, amount, range))
665
			})
666
			.collect::<Vec<_>>()
667
	}
668
}
669

            
670
/// tests for this module
671
#[cfg(test)]
672
mod tests {
673
	use super::*;
674
	use crate::{auctions, mock::TestRegistrar};
675
	use frame_support::{
676
		assert_noop, assert_ok, assert_storage_noop, derive_impl, ord_parameter_types,
677
		parameter_types,
678
		traits::{EitherOfDiverse, OnFinalize, OnInitialize},
679
	};
680
	use frame_system::{EnsureRoot, EnsureSignedBy};
681
	use pallet_balances;
682
	use polkadot_primitives::{BlockNumber, Id as ParaId};
683
	use polkadot_primitives_test_helpers::{dummy_hash, dummy_head_data, dummy_validation_code};
684
	use sp_core::H256;
685
	use sp_runtime::{
686
		traits::{BlakeTwo256, IdentityLookup},
687
		BuildStorage,
688
		DispatchError::BadOrigin,
689
	};
690
	use std::{cell::RefCell, collections::BTreeMap};
691

            
692
	type Block = frame_system::mocking::MockBlockU32<Test>;
693

            
694
	frame_support::construct_runtime!(
695
		pub enum Test
696
		{
697
			System: frame_system,
698
			Balances: pallet_balances,
699
			Auctions: auctions,
700
		}
701
	);
702

            
703
	#[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
704
	impl frame_system::Config for Test {
705
		type BaseCallFilter = frame_support::traits::Everything;
706
		type BlockWeights = ();
707
		type BlockLength = ();
708
		type DbWeight = ();
709
		type RuntimeOrigin = RuntimeOrigin;
710
		type RuntimeCall = RuntimeCall;
711
		type Nonce = u64;
712
		type Hash = H256;
713
		type Hashing = BlakeTwo256;
714
		type AccountId = u64;
715
		type Lookup = IdentityLookup<Self::AccountId>;
716
		type Block = Block;
717
		type RuntimeEvent = RuntimeEvent;
718
		type Version = ();
719
		type PalletInfo = PalletInfo;
720
		type AccountData = pallet_balances::AccountData<u64>;
721
		type OnNewAccount = ();
722
		type OnKilledAccount = ();
723
		type SystemWeightInfo = ();
724
		type SS58Prefix = ();
725
		type OnSetCode = ();
726
		type MaxConsumers = frame_support::traits::ConstU32<16>;
727
	}
728

            
729
	#[derive_impl(pallet_balances::config_preludes::TestDefaultConfig)]
730
	impl pallet_balances::Config for Test {
731
		type AccountStore = System;
732
	}
733

            
734
	#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Debug)]
735
	pub struct LeaseData {
736
		leaser: u64,
737
		amount: u64,
738
	}
739

            
740
	thread_local! {
741
		pub static LEASES:
742
			RefCell<BTreeMap<(ParaId, BlockNumber), LeaseData>> = RefCell::new(BTreeMap::new());
743
	}
744

            
745
	fn leases() -> Vec<((ParaId, BlockNumber), LeaseData)> {
746
		LEASES.with(|p| (&*p.borrow()).clone().into_iter().collect::<Vec<_>>())
747
	}
748

            
749
	pub struct TestLeaser;
750
	impl Leaser<BlockNumber> for TestLeaser {
751
		type AccountId = u64;
752
		type LeasePeriod = BlockNumber;
753
		type Currency = Balances;
754

            
755
		fn lease_out(
756
			para: ParaId,
757
			leaser: &Self::AccountId,
758
			amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
759
			period_begin: Self::LeasePeriod,
760
			period_count: Self::LeasePeriod,
761
		) -> Result<(), LeaseError> {
762
			LEASES.with(|l| {
763
				let mut leases = l.borrow_mut();
764
				let now = System::block_number();
765
				let (current_lease_period, _) =
766
					Self::lease_period_index(now).ok_or(LeaseError::NoLeasePeriod)?;
767
				if period_begin < current_lease_period {
768
					return Err(LeaseError::AlreadyEnded)
769
				}
770
				for period in period_begin..(period_begin + period_count) {
771
					if leases.contains_key(&(para, period)) {
772
						return Err(LeaseError::AlreadyLeased)
773
					}
774
					leases.insert((para, period), LeaseData { leaser: *leaser, amount });
775
				}
776
				Ok(())
777
			})
778
		}
779

            
780
		fn deposit_held(
781
			para: ParaId,
782
			leaser: &Self::AccountId,
783
		) -> <Self::Currency as Currency<Self::AccountId>>::Balance {
784
			leases()
785
				.iter()
786
				.filter_map(|((id, _period), data)| {
787
					if id == &para && &data.leaser == leaser {
788
						Some(data.amount)
789
					} else {
790
						None
791
					}
792
				})
793
				.max()
794
				.unwrap_or_default()
795
		}
796

            
797
		fn lease_period_length() -> (BlockNumber, BlockNumber) {
798
			(10, 0)
799
		}
800

            
801
		fn lease_period_index(b: BlockNumber) -> Option<(Self::LeasePeriod, bool)> {
802
			let (lease_period_length, offset) = Self::lease_period_length();
803
			let b = b.checked_sub(offset)?;
804

            
805
			let lease_period = b / lease_period_length;
806
			let first_block = (b % lease_period_length).is_zero();
807

            
808
			Some((lease_period, first_block))
809
		}
810

            
811
		fn already_leased(
812
			para_id: ParaId,
813
			first_period: Self::LeasePeriod,
814
			last_period: Self::LeasePeriod,
815
		) -> bool {
816
			leases().into_iter().any(|((para, period), _data)| {
817
				para == para_id && first_period <= period && period <= last_period
818
			})
819
		}
820
	}
821

            
822
	ord_parameter_types! {
823
		pub const Six: u64 = 6;
824
	}
825

            
826
	type RootOrSix = EitherOfDiverse<EnsureRoot<u64>, EnsureSignedBy<Six, u64>>;
827

            
828
	thread_local! {
829
		pub static LAST_RANDOM: RefCell<Option<(H256, u32)>> = RefCell::new(None);
830
	}
831
	fn set_last_random(output: H256, known_since: u32) {
832
		LAST_RANDOM.with(|p| *p.borrow_mut() = Some((output, known_since)))
833
	}
834
	pub struct TestPastRandomness;
835
	impl Randomness<H256, BlockNumber> for TestPastRandomness {
836
		fn random(_subject: &[u8]) -> (H256, u32) {
837
			LAST_RANDOM.with(|p| {
838
				if let Some((output, known_since)) = &*p.borrow() {
839
					(*output, *known_since)
840
				} else {
841
					(H256::zero(), frame_system::Pallet::<Test>::block_number())
842
				}
843
			})
844
		}
845
	}
846

            
847
	parameter_types! {
848
		pub static EndingPeriod: BlockNumber = 3;
849
		pub static SampleLength: BlockNumber = 1;
850
	}
851

            
852
	impl Config for Test {
853
		type RuntimeEvent = RuntimeEvent;
854
		type Leaser = TestLeaser;
855
		type Registrar = TestRegistrar<Self>;
856
		type EndingPeriod = EndingPeriod;
857
		type SampleLength = SampleLength;
858
		type Randomness = TestPastRandomness;
859
		type InitiateOrigin = RootOrSix;
860
		type WeightInfo = crate::auctions::TestWeightInfo;
861
	}
862

            
863
	// This function basically just builds a genesis storage key/value store according to
864
	// our desired mock up.
865
	pub fn new_test_ext() -> sp_io::TestExternalities {
866
		let mut t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
867
		pallet_balances::GenesisConfig::<Test> {
868
			balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)],
869
		}
870
		.assimilate_storage(&mut t)
871
		.unwrap();
872
		let mut ext: sp_io::TestExternalities = t.into();
873
		ext.execute_with(|| {
874
			// Register para 0, 1, 2, and 3 for tests
875
			assert_ok!(TestRegistrar::<Test>::register(
876
				1,
877
				0.into(),
878
				dummy_head_data(),
879
				dummy_validation_code()
880
			));
881
			assert_ok!(TestRegistrar::<Test>::register(
882
				1,
883
				1.into(),
884
				dummy_head_data(),
885
				dummy_validation_code()
886
			));
887
			assert_ok!(TestRegistrar::<Test>::register(
888
				1,
889
				2.into(),
890
				dummy_head_data(),
891
				dummy_validation_code()
892
			));
893
			assert_ok!(TestRegistrar::<Test>::register(
894
				1,
895
				3.into(),
896
				dummy_head_data(),
897
				dummy_validation_code()
898
			));
899
		});
900
		ext
901
	}
902

            
903
	fn run_to_block(n: BlockNumber) {
904
		while System::block_number() < n {
905
			Auctions::on_finalize(System::block_number());
906
			Balances::on_finalize(System::block_number());
907
			System::on_finalize(System::block_number());
908
			System::set_block_number(System::block_number() + 1);
909
			System::on_initialize(System::block_number());
910
			Balances::on_initialize(System::block_number());
911
			Auctions::on_initialize(System::block_number());
912
		}
913
	}
914

            
915
	#[test]
916
	fn basic_setup_works() {
917
		new_test_ext().execute_with(|| {
918
			assert_eq!(AuctionCounter::<Test>::get(), 0);
919
			assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
920
			assert_eq!(
921
				Auctions::auction_status(System::block_number()),
922
				AuctionStatus::<u32>::NotStarted
923
			);
924

            
925
			run_to_block(10);
926

            
927
			assert_eq!(AuctionCounter::<Test>::get(), 0);
928
			assert_eq!(TestLeaser::deposit_held(0u32.into(), &1), 0);
929
			assert_eq!(
930
				Auctions::auction_status(System::block_number()),
931
				AuctionStatus::<u32>::NotStarted
932
			);
933
		});
934
	}
935

            
936
	#[test]
937
	fn can_start_auction() {
938
		new_test_ext().execute_with(|| {
939
			run_to_block(1);
940

            
941
			assert_noop!(Auctions::new_auction(RuntimeOrigin::signed(1), 5, 1), BadOrigin);
942
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
943

            
944
			assert_eq!(AuctionCounter::<Test>::get(), 1);
945
			assert_eq!(
946
				Auctions::auction_status(System::block_number()),
947
				AuctionStatus::<u32>::StartingPeriod
948
			);
949
		});
950
	}
951

            
952
	#[test]
953
	fn bidding_works() {
954
		new_test_ext().execute_with(|| {
955
			run_to_block(1);
956
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
957
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
958

            
959
			assert_eq!(Balances::reserved_balance(1), 5);
960
			assert_eq!(Balances::free_balance(1), 5);
961
			assert_eq!(
962
				Winning::<Test>::get(0).unwrap()[SlotRange::ZeroThree as u8 as usize],
963
				Some((1, 0.into(), 5))
964
			);
965
		});
966
	}
967

            
968
	#[test]
969
	fn under_bidding_works() {
970
		new_test_ext().execute_with(|| {
971
			run_to_block(1);
972
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
973

            
974
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
975

            
976
			assert_storage_noop!({
977
				assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 0.into(), 1, 1, 4, 1));
978
			});
979
		});
980
	}
981

            
982
	#[test]
983
	fn over_bidding_works() {
984
		new_test_ext().execute_with(|| {
985
			run_to_block(1);
986
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
987
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 5));
988
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 0.into(), 1, 1, 4, 6));
989

            
990
			assert_eq!(Balances::reserved_balance(1), 0);
991
			assert_eq!(Balances::free_balance(1), 10);
992
			assert_eq!(Balances::reserved_balance(2), 6);
993
			assert_eq!(Balances::free_balance(2), 14);
994
			assert_eq!(
995
				Winning::<Test>::get(0).unwrap()[SlotRange::ZeroThree as u8 as usize],
996
				Some((2, 0.into(), 6))
997
			);
998
		});
999
	}
	#[test]
	fn auction_proceeds_correctly() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_eq!(AuctionCounter::<Test>::get(), 1);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::StartingPeriod
			);
			run_to_block(2);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::StartingPeriod
			);
			run_to_block(3);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::StartingPeriod
			);
			run_to_block(4);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::StartingPeriod
			);
			run_to_block(5);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::StartingPeriod
			);
			run_to_block(6);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(0, 0)
			);
			run_to_block(7);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(1, 0)
			);
			run_to_block(8);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(2, 0)
			);
			run_to_block(9);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::NotStarted
			);
		});
	}
	#[test]
	fn can_win_auction() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 1));
			assert_eq!(Balances::reserved_balance(1), 1);
			assert_eq!(Balances::free_balance(1), 9);
			run_to_block(9);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 2), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 3), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 4), LeaseData { leaser: 1, amount: 1 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 1);
		});
	}
	#[test]
	fn can_win_auction_with_late_randomness() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::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_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... 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 that the winner can now be chosen.
			set_last_random(H256::zero(), 9);
			run_to_block(10);
			// Auction ended and winner selected
			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 }),
					((0.into(), 3), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 4), LeaseData { leaser: 1, amount: 1 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 1);
		});
	}
	#[test]
	fn can_win_incomplete_auction() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 4, 4, 5));
			run_to_block(9);
			assert_eq!(leases(), vec![((0.into(), 4), LeaseData { leaser: 1, amount: 5 }),]);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
		});
	}
	#[test]
	fn should_choose_best_combination() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 0.into(), 1, 2, 3, 4));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), 0.into(), 1, 4, 4, 2));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 1.into(), 1, 1, 4, 2));
			run_to_block(9);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 2), LeaseData { leaser: 2, amount: 4 }),
					((0.into(), 3), LeaseData { leaser: 2, amount: 4 }),
					((0.into(), 4), LeaseData { leaser: 3, amount: 2 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 1);
			assert_eq!(TestLeaser::deposit_held(1.into(), &1), 0);
			assert_eq!(TestLeaser::deposit_held(0.into(), &2), 4);
			assert_eq!(TestLeaser::deposit_held(0.into(), &3), 2);
		});
	}
	#[test]
	fn gap_bid_works() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			// User 1 will make a bid for period 1 and 4 for the same Para 0
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 4, 4, 4));
			// User 2 and 3 will make a bid for para 1 on period 2 and 3 respectively
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), 1.into(), 1, 2, 2, 2));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), 1.into(), 1, 3, 3, 3));
			// Total reserved should be the max of the two
			assert_eq!(Balances::reserved_balance(1), 4);
			// Other people are reserved correctly too
			assert_eq!(Balances::reserved_balance(2), 2);
			assert_eq!(Balances::reserved_balance(3), 3);
			// End the auction.
			run_to_block(9);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 4), LeaseData { leaser: 1, amount: 4 }),
					((1.into(), 2), LeaseData { leaser: 2, amount: 2 }),
					((1.into(), 3), LeaseData { leaser: 3, amount: 3 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 4);
			assert_eq!(TestLeaser::deposit_held(1.into(), &2), 2);
			assert_eq!(TestLeaser::deposit_held(1.into(), &3), 3);
		});
	}
	#[test]
	fn deposit_credit_should_work() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 5));
			assert_eq!(Balances::reserved_balance(1), 5);
			run_to_block(10);
			assert_eq!(leases(), vec![((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),]);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 2));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 2, 2, 6));
			// Only 1 reserved since we have a deposit credit of 5.
			assert_eq!(Balances::reserved_balance(1), 1);
			run_to_block(20);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),
					((0.into(), 2), LeaseData { leaser: 1, amount: 6 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 6);
		});
	}
	#[test]
	fn deposit_credit_on_alt_para_should_not_count() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 1, 5));
			assert_eq!(Balances::reserved_balance(1), 5);
			run_to_block(10);
			assert_eq!(leases(), vec![((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),]);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 2));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 1.into(), 2, 2, 2, 6));
			// 6 reserved since we are bidding on a new para; only works because we don't
			assert_eq!(Balances::reserved_balance(1), 6);
			run_to_block(20);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 1, amount: 5 }),
					((1.into(), 2), LeaseData { leaser: 1, amount: 6 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 5);
			assert_eq!(TestLeaser::deposit_held(1.into(), &1), 6);
		});
	}
	#[test]
	fn multiple_bids_work_pre_ending() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			for i in 1..6u64 {
				run_to_block(i as _);
				assert_ok!(Auctions::bid(RuntimeOrigin::signed(i), 0.into(), 1, 1, 4, i));
				for j in 1..6 {
					assert_eq!(Balances::reserved_balance(j), if j == i { j } else { 0 });
					assert_eq!(Balances::free_balance(j), if j == i { j * 9 } else { j * 10 });
				}
			}
			run_to_block(9);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 1), LeaseData { leaser: 5, amount: 5 }),
					((0.into(), 2), LeaseData { leaser: 5, amount: 5 }),
					((0.into(), 3), LeaseData { leaser: 5, amount: 5 }),
					((0.into(), 4), LeaseData { leaser: 5, amount: 5 }),
				]
			);
		});
	}
	#[test]
	fn multiple_bids_work_post_ending() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 0, 1));
			for i in 1..6u64 {
				run_to_block(((i - 1) / 2 + 1) as _);
				assert_ok!(Auctions::bid(RuntimeOrigin::signed(i), 0.into(), 1, 1, 4, i));
				for j in 1..6 {
					assert_eq!(Balances::reserved_balance(j), if j <= i { j } else { 0 });
					assert_eq!(Balances::free_balance(j), if j <= i { j * 9 } else { j * 10 });
				}
			}
			for i in 1..6u64 {
				assert_eq!(ReservedAmounts::<Test>::get((i, ParaId::from(0))).unwrap(), i);
			}
			run_to_block(5);
			assert_eq!(
				leases(),
				(1..=4)
					.map(|i| ((0.into(), i), LeaseData { leaser: 2, amount: 2 }))
					.collect::<Vec<_>>()
			);
		});
	}
	#[test]
	fn incomplete_calculate_winners_works() {
		let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
		winning[SlotRange::ThreeThree as u8 as usize] = Some((1, 0.into(), 1));
		let winners = vec![(1, 0.into(), 1, SlotRange::ThreeThree)];
		assert_eq!(Auctions::calculate_winners(winning), winners);
	}
	#[test]
	fn first_incomplete_calculate_winners_works() {
		let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
		winning[0] = Some((1, 0.into(), 1));
		let winners = vec![(1, 0.into(), 1, SlotRange::ZeroZero)];
		assert_eq!(Auctions::calculate_winners(winning), winners);
	}
	#[test]
	fn calculate_winners_works() {
		let mut winning = [None; SlotRange::SLOT_RANGE_COUNT];
		winning[SlotRange::ZeroZero as u8 as usize] = Some((2, 0.into(), 2));
		winning[SlotRange::ZeroThree as u8 as usize] = Some((1, 100.into(), 1));
		winning[SlotRange::OneOne as u8 as usize] = Some((3, 1.into(), 1));
		winning[SlotRange::TwoTwo as u8 as usize] = Some((1, 2.into(), 53));
		winning[SlotRange::ThreeThree as u8 as usize] = Some((5, 3.into(), 1));
		let winners = vec![
			(2, 0.into(), 2, SlotRange::ZeroZero),
			(3, 1.into(), 1, SlotRange::OneOne),
			(1, 2.into(), 53, SlotRange::TwoTwo),
			(5, 3.into(), 1, SlotRange::ThreeThree),
		];
		assert_eq!(Auctions::calculate_winners(winning), winners);
		winning[SlotRange::ZeroOne as u8 as usize] = Some((4, 10.into(), 3));
		let winners = vec![
			(4, 10.into(), 3, SlotRange::ZeroOne),
			(1, 2.into(), 53, SlotRange::TwoTwo),
			(5, 3.into(), 1, SlotRange::ThreeThree),
		];
		assert_eq!(Auctions::calculate_winners(winning), winners);
		winning[SlotRange::ZeroThree as u8 as usize] = Some((1, 100.into(), 100));
		let winners = vec![(1, 100.into(), 100, SlotRange::ZeroThree)];
		assert_eq!(Auctions::calculate_winners(winning), winners);
	}
	#[test]
	fn lower_bids_are_correctly_refunded() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 1, 1));
			let para_1 = ParaId::from(1_u32);
			let para_2 = ParaId::from(2_u32);
			// Make a bid and reserve a balance
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), para_1, 1, 1, 4, 9));
			assert_eq!(Balances::reserved_balance(1), 9);
			assert_eq!(ReservedAmounts::<Test>::get((1, para_1)), Some(9));
			assert_eq!(Balances::reserved_balance(2), 0);
			assert_eq!(ReservedAmounts::<Test>::get((2, para_2)), None);
			// Bigger bid, reserves new balance and returns funds
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), para_2, 1, 1, 4, 19));
			assert_eq!(Balances::reserved_balance(1), 0);
			assert_eq!(ReservedAmounts::<Test>::get((1, para_1)), None);
			assert_eq!(Balances::reserved_balance(2), 19);
			assert_eq!(ReservedAmounts::<Test>::get((2, para_2)), Some(19));
		});
	}
	#[test]
	fn initialize_winners_in_ending_period_works() {
		new_test_ext().execute_with(|| {
			let ed: u64 = <Test as pallet_balances::Config>::ExistentialDeposit::get();
			assert_eq!(ed, 1);
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 9, 1));
			let para_1 = ParaId::from(1_u32);
			let para_2 = ParaId::from(2_u32);
			let para_3 = ParaId::from(3_u32);
			// Make bids
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), para_1, 1, 1, 4, 9));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), para_2, 1, 3, 4, 19));
			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, 9));
			winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 19));
			assert_eq!(Winning::<Test>::get(0), Some(winning));
			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)
			);
			assert_eq!(Winning::<Test>::get(0), Some(winning));
			run_to_block(11);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(1, 0)
			);
			assert_eq!(Winning::<Test>::get(1), Some(winning));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), para_3, 1, 3, 4, 29));
			run_to_block(12);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(2, 0)
			);
			winning[SlotRange::TwoThree as u8 as usize] = Some((3, para_3, 29));
			assert_eq!(Winning::<Test>::get(2), Some(winning));
		});
	}
	#[test]
	fn handle_bid_requires_registered_para() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_noop!(
				Auctions::bid(RuntimeOrigin::signed(1), 1337.into(), 1, 1, 4, 1),
				Error::<Test>::ParaNotRegistered
			);
			assert_ok!(TestRegistrar::<Test>::register(
				1,
				1337.into(),
				dummy_head_data(),
				dummy_validation_code()
			));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 1337.into(), 1, 1, 4, 1));
		});
	}
	#[test]
	fn handle_bid_checks_existing_lease_periods() {
		new_test_ext().execute_with(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 2, 3, 1));
			assert_eq!(Balances::reserved_balance(1), 1);
			assert_eq!(Balances::free_balance(1), 9);
			run_to_block(9);
			assert_eq!(
				leases(),
				vec![
					((0.into(), 2), LeaseData { leaser: 1, amount: 1 }),
					((0.into(), 3), LeaseData { leaser: 1, amount: 1 }),
				]
			);
			assert_eq!(TestLeaser::deposit_held(0.into(), &1), 1);
			// Para 1 just won an auction above and won some lease periods.
			// No bids can work which overlap these periods.
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_noop!(
				Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 1, 4, 1),
				Error::<Test>::AlreadyLeasedOut,
			);
			assert_noop!(
				Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 1, 2, 1),
				Error::<Test>::AlreadyLeasedOut,
			);
			assert_noop!(
				Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 3, 4, 1),
				Error::<Test>::AlreadyLeasedOut,
			);
			// This is okay, not an overlapping bid.
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 2, 1, 1, 1));
		});
	}
	// Here we will test that taking only 10 samples during the ending period works as expected.
	#[test]
	fn less_winning_samples_work() {
		new_test_ext().execute_with(|| {
			let ed: u64 = <Test as pallet_balances::Config>::ExistentialDeposit::get();
			assert_eq!(ed, 1);
			EndingPeriod::set(30);
			SampleLength::set(10);
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 9, 11));
			let para_1 = ParaId::from(1_u32);
			let para_2 = ParaId::from(2_u32);
			let para_3 = ParaId::from(3_u32);
			// Make bids
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), para_1, 1, 11, 14, 9));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(2), para_2, 1, 13, 14, 19));
			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, 9));
			winning[SlotRange::TwoThree as u8 as usize] = Some((2, para_2, 19));
			assert_eq!(Winning::<Test>::get(0), Some(winning));
			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)
			);
			assert_eq!(Winning::<Test>::get(0), Some(winning));
			// New bids update the current winning
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), para_3, 1, 14, 14, 29));
			winning[SlotRange::ThreeThree as u8 as usize] = Some((3, para_3, 29));
			assert_eq!(Winning::<Test>::get(0), Some(winning));
			run_to_block(20);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(1, 0)
			);
			assert_eq!(Winning::<Test>::get(1), Some(winning));
			run_to_block(25);
			// Overbid mid sample
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(3), para_3, 1, 13, 14, 29));
			winning[SlotRange::TwoThree as u8 as usize] = Some((3, para_3, 29));
			assert_eq!(Winning::<Test>::get(1), Some(winning));
			run_to_block(30);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::EndingPeriod(2, 0)
			);
			assert_eq!(Winning::<Test>::get(2), Some(winning));
			set_last_random(H256::from([254; 32]), 40);
			run_to_block(40);
			// Auction ended and winner selected
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::NotStarted
			);
			assert_eq!(
				leases(),
				vec![
					((3.into(), 13), LeaseData { leaser: 3, amount: 29 }),
					((3.into(), 14), LeaseData { leaser: 3, amount: 29 }),
				]
			);
		});
	}
	#[test]
	fn auction_status_works() {
		new_test_ext().execute_with(|| {
			EndingPeriod::set(30);
			SampleLength::set(10);
			set_last_random(dummy_hash(), 0);
			assert_eq!(
				Auctions::auction_status(System::block_number()),
				AuctionStatus::<u32>::NotStarted
			);
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::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(dummy_hash(), 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(|| {
			run_to_block(1);
			assert_ok!(Auctions::new_auction(RuntimeOrigin::signed(6), 5, 1));
			assert_ok!(Auctions::bid(RuntimeOrigin::signed(1), 0.into(), 1, 1, 4, 1));
			assert_eq!(Balances::reserved_balance(1), 1);
			assert_eq!(Balances::free_balance(1), 9);
			assert_noop!(Auctions::cancel_auction(RuntimeOrigin::signed(6)), BadOrigin);
			assert_ok!(Auctions::cancel_auction(RuntimeOrigin::root()));
			assert!(AuctionInfo::<Test>::get().is_none());
			assert_eq!(Balances::reserved_balance(1), 0);
			assert_eq!(ReservedAmounts::<Test>::iter().count(), 0);
			assert_eq!(Winning::<Test>::iter().count(), 0);
		});
	}
}
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking {
	use super::{Pallet as Auctions, *};
	use frame_support::{
		assert_ok,
		traits::{EnsureOrigin, OnInitialize},
	};
	use frame_system::RawOrigin;
	use polkadot_runtime_parachains::paras;
	use sp_runtime::{traits::Bounded, SaturatedConversion};
	use frame_benchmarking::{account, benchmarks, whitelisted_caller, BenchmarkError};
	fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) {
		let events = frame_system::Pallet::<T>::events();
		let system_event: <T as frame_system::Config>::RuntimeEvent = generic_event.into();
		// compare to the last event record
		let frame_system::EventRecord { event, .. } = &events[events.len() - 1];
		assert_eq!(event, &system_event);
	}
	fn fill_winners<T: Config + paras::Config>(lease_period_index: LeasePeriodOf<T>) {
		let auction_index = AuctionCounter::<T>::get();
		let minimum_balance = CurrencyOf::<T>::minimum_balance();
		for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 {
			let owner = account("owner", n, 0);
			let worst_validation_code = T::Registrar::worst_validation_code();
			let worst_head_data = T::Registrar::worst_head_data();
			CurrencyOf::<T>::make_free_balance_be(&owner, BalanceOf::<T>::max_value());
			assert!(T::Registrar::register(
				owner,
				ParaId::from(n),
				worst_head_data,
				worst_validation_code
			)
			.is_ok());
		}
		assert_ok!(paras::Pallet::<T>::add_trusted_validation_code(
			frame_system::Origin::<T>::Root.into(),
			T::Registrar::worst_validation_code(),
		));
		T::Registrar::execute_pending_transitions();
		for n in 1..=SlotRange::SLOT_RANGE_COUNT as u32 {
			let bidder = account("bidder", n, 0);
			CurrencyOf::<T>::make_free_balance_be(&bidder, BalanceOf::<T>::max_value());
			let slot_range = SlotRange::n((n - 1) as u8).unwrap();
			let (start, end) = slot_range.as_pair();
			assert!(Auctions::<T>::bid(
				RawOrigin::Signed(bidder).into(),
				ParaId::from(n),
				auction_index,
				lease_period_index + start.into(),        // First Slot
				lease_period_index + end.into(),          // Last slot
				minimum_balance.saturating_mul(n.into()), // Amount
			)
			.is_ok());
		}
	}
	benchmarks! {
		where_clause { where T: pallet_babe::Config + paras::Config }
		new_auction {
			let duration = BlockNumberFor::<T>::max_value();
			let lease_period_index = LeasePeriodOf::<T>::max_value();
			let origin =
				T::InitiateOrigin::try_successful_origin().map_err(|_| BenchmarkError::Weightless)?;
		}: _<T::RuntimeOrigin>(origin, duration, lease_period_index)
		verify {
			assert_last_event::<T>(Event::<T>::AuctionStarted {
				auction_index: AuctionCounter::<T>::get(),
				lease_period: LeasePeriodOf::<T>::max_value(),
				ending: BlockNumberFor::<T>::max_value(),
			}.into());
		}
		// Worst case scenario a new bid comes in which kicks out an existing bid for the same slot.
		bid {
			// If there is an offset, we need to be on that block to be able to do lease things.
			let (_, offset) = T::Leaser::lease_period_length();
			frame_system::Pallet::<T>::set_block_number(offset + One::one());
			// Create a new auction
			let duration = BlockNumberFor::<T>::max_value();
			let lease_period_index = LeasePeriodOf::<T>::zero();
			let origin = T::InitiateOrigin::try_successful_origin()
				.expect("InitiateOrigin has no successful origin required for the benchmark");
			Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
			let para = ParaId::from(0);
			let new_para = ParaId::from(1_u32);
			// Register the paras
			let owner = account("owner", 0, 0);
			CurrencyOf::<T>::make_free_balance_be(&owner, BalanceOf::<T>::max_value());
			let worst_head_data = T::Registrar::worst_head_data();
			let worst_validation_code = T::Registrar::worst_validation_code();
			T::Registrar::register(owner.clone(), para, worst_head_data.clone(), worst_validation_code.clone())?;
			T::Registrar::register(owner, new_para, worst_head_data, worst_validation_code.clone())?;
			assert_ok!(paras::Pallet::<T>::add_trusted_validation_code(
				frame_system::Origin::<T>::Root.into(),
				worst_validation_code,
			));
			T::Registrar::execute_pending_transitions();
			// Make an existing bid
			let auction_index = AuctionCounter::<T>::get();
			let first_slot = AuctionInfo::<T>::get().unwrap().0;
			let last_slot = first_slot + 3u32.into();
			let first_amount = CurrencyOf::<T>::minimum_balance();
			let first_bidder: T::AccountId = account("first_bidder", 0, 0);
			CurrencyOf::<T>::make_free_balance_be(&first_bidder, BalanceOf::<T>::max_value());
			Auctions::<T>::bid(
				RawOrigin::Signed(first_bidder.clone()).into(),
				para,
				auction_index,
				first_slot,
				last_slot,
				first_amount,
			)?;
			let caller: T::AccountId = whitelisted_caller();
			CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
			let bigger_amount = CurrencyOf::<T>::minimum_balance().saturating_mul(10u32.into());
			assert_eq!(CurrencyOf::<T>::reserved_balance(&first_bidder), first_amount);
		}: _(RawOrigin::Signed(caller.clone()), new_para, auction_index, first_slot, last_slot, bigger_amount)
		verify {
			// Confirms that we unreserved funds from a previous bidder, which is worst case scenario.
			assert_eq!(CurrencyOf::<T>::reserved_balance(&caller), bigger_amount);
		}
		// Worst case: 10 bidders taking all wining spots, and we need to calculate the winner for auction end.
		// Entire winner map should be full and removed at the end of the benchmark.
		on_initialize {
			// If there is an offset, we need to be on that block to be able to do lease things.
			let (lease_length, offset) = T::Leaser::lease_period_length();
			frame_system::Pallet::<T>::set_block_number(offset + One::one());
			// Create a new auction
			let duration: BlockNumberFor<T> = lease_length / 2u32.into();
			let lease_period_index = LeasePeriodOf::<T>::zero();
			let now = frame_system::Pallet::<T>::block_number();
			let origin = T::InitiateOrigin::try_successful_origin()
				.expect("InitiateOrigin has no successful origin required for the benchmark");
			Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
			fill_winners::<T>(lease_period_index);
			for winner in Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap().iter() {
				assert!(winner.is_some());
			}
			let winning_data = Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap();
			// Make winning map full
			for i in 0u32 .. (T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() {
				Winning::<T>::insert(BlockNumberFor::<T>::from(i), winning_data.clone());
			}
			// Move ahead to the block we want to initialize
			frame_system::Pallet::<T>::set_block_number(duration + now + T::EndingPeriod::get());
			// Trigger epoch change for new random number value:
			{
				pallet_babe::EpochStart::<T>::set((Zero::zero(), u32::MAX.into()));
				pallet_babe::Pallet::<T>::on_initialize(duration + now + T::EndingPeriod::get());
				let authorities = pallet_babe::Pallet::<T>::authorities();
				// Check for non empty authority set since it otherwise emits a No-OP warning.
				if !authorities.is_empty() {
					pallet_babe::Pallet::<T>::enact_epoch_change(authorities.clone(), authorities, None);
				}
			}
		}: {
			Auctions::<T>::on_initialize(duration + now + T::EndingPeriod::get());
		} verify {
			let auction_index = AuctionCounter::<T>::get();
			assert_last_event::<T>(Event::<T>::AuctionClosed { auction_index }.into());
			assert!(Winning::<T>::iter().count().is_zero());
		}
		// Worst case: 10 bidders taking all wining spots, and winning data is full.
		cancel_auction {
			// If there is an offset, we need to be on that block to be able to do lease things.
			let (lease_length, offset) = T::Leaser::lease_period_length();
			frame_system::Pallet::<T>::set_block_number(offset + One::one());
			// Create a new auction
			let duration: BlockNumberFor<T> = lease_length / 2u32.into();
			let lease_period_index = LeasePeriodOf::<T>::zero();
			let now = frame_system::Pallet::<T>::block_number();
			let origin = T::InitiateOrigin::try_successful_origin()
				.expect("InitiateOrigin has no successful origin required for the benchmark");
			Auctions::<T>::new_auction(origin, duration, lease_period_index)?;
			fill_winners::<T>(lease_period_index);
			let winning_data = Winning::<T>::get(BlockNumberFor::<T>::from(0u32)).unwrap();
			for winner in winning_data.iter() {
				assert!(winner.is_some());
			}
			// Make winning map full
			for i in 0u32 .. (T::EndingPeriod::get() / T::SampleLength::get()).saturated_into() {
				Winning::<T>::insert(BlockNumberFor::<T>::from(i), winning_data.clone());
			}
			assert!(AuctionInfo::<T>::get().is_some());
		}: _(RawOrigin::Root)
		verify {
			assert!(AuctionInfo::<T>::get().is_none());
		}
		impl_benchmark_test_suite!(
			Auctions,
			crate::integration_tests::new_test_ext(),
			crate::integration_tests::Test,
		);
	}
}