mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 16:21:02 +00:00
Allow an Offset to Lease Periods (#3980)
* add slot offset for slots * trying things out * fix test * improve api to return the first block of a new lease period * add an integration test with offset * de-duplicate test * hide lease period_period_length from public api * fix benchmarks * Update runtime/common/src/slots.rs * support the exact same range of crowdloans * fix docs * fix docs again * introduce offset to runtimes * fix and check edge case w/ offset and lease period first block * remove newline * turn into an option * fix benchmarks Co-authored-by: Robert Habermeier <rphmeier@gmail.com>
This commit is contained in:
@@ -34,8 +34,9 @@ use primitives::v1::Id as ParaId;
|
|||||||
use sp_runtime::traits::{CheckedSub, One, Saturating, Zero};
|
use sp_runtime::traits::{CheckedSub, One, Saturating, Zero};
|
||||||
use sp_std::{mem::swap, prelude::*};
|
use sp_std::{mem::swap, prelude::*};
|
||||||
|
|
||||||
type CurrencyOf<T> = <<T as Config>::Leaser as Leaser>::Currency;
|
type CurrencyOf<T> =
|
||||||
type BalanceOf<T> = <<<T as Config>::Leaser as Leaser>::Currency as Currency<
|
<<T as Config>::Leaser as Leaser<<T as frame_system::Config>::BlockNumber>>::Currency;
|
||||||
|
type BalanceOf<T> = <<<T as Config>::Leaser as Leaser<<T as frame_system::Config>::BlockNumber>>::Currency as Currency<
|
||||||
<T as frame_system::Config>::AccountId,
|
<T as frame_system::Config>::AccountId,
|
||||||
>>::Balance;
|
>>::Balance;
|
||||||
|
|
||||||
@@ -65,7 +66,9 @@ impl WeightInfo for TestWeightInfo {
|
|||||||
/// An auction index. We count auctions in this type.
|
/// An auction index. We count auctions in this type.
|
||||||
pub type AuctionIndex = u32;
|
pub type AuctionIndex = u32;
|
||||||
|
|
||||||
type LeasePeriodOf<T> = <<T as Config>::Leaser as Leaser>::LeasePeriod;
|
type LeasePeriodOf<T> =
|
||||||
|
<<T as Config>::Leaser as Leaser<<T as frame_system::Config>::BlockNumber>>::LeasePeriod;
|
||||||
|
|
||||||
// Winning data type. This encodes the top bidders of each range together with their bid.
|
// Winning data type. This encodes the top bidders of each range together with their bid.
|
||||||
type WinningData<T> = [Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)>;
|
type WinningData<T> = [Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)>;
|
||||||
SlotRange::SLOT_RANGE_COUNT];
|
SlotRange::SLOT_RANGE_COUNT];
|
||||||
@@ -91,7 +94,11 @@ pub mod pallet {
|
|||||||
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
type Event: From<Event<Self>> + IsType<<Self as frame_system::Config>::Event>;
|
||||||
|
|
||||||
/// The type representing the leasing system.
|
/// The type representing the leasing system.
|
||||||
type Leaser: Leaser<AccountId = Self::AccountId, LeasePeriod = Self::BlockNumber>;
|
type Leaser: Leaser<
|
||||||
|
Self::BlockNumber,
|
||||||
|
AccountId = Self::AccountId,
|
||||||
|
LeasePeriod = Self::BlockNumber,
|
||||||
|
>;
|
||||||
|
|
||||||
/// The parachain registrar type.
|
/// The parachain registrar type.
|
||||||
type Registrar: Registrar<AccountId = Self::AccountId>;
|
type Registrar: Registrar<AccountId = Self::AccountId>;
|
||||||
@@ -299,9 +306,8 @@ pub mod pallet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Auctioneer for Pallet<T> {
|
impl<T: Config> Auctioneer<T::BlockNumber> for Pallet<T> {
|
||||||
type AccountId = T::AccountId;
|
type AccountId = T::AccountId;
|
||||||
type BlockNumber = T::BlockNumber;
|
|
||||||
type LeasePeriod = T::BlockNumber;
|
type LeasePeriod = T::BlockNumber;
|
||||||
type Currency = CurrencyOf<T>;
|
type Currency = CurrencyOf<T>;
|
||||||
|
|
||||||
@@ -313,7 +319,7 @@ impl<T: Config> Auctioneer for Pallet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the status of the auction given the current block number.
|
// Returns the status of the auction given the current block number.
|
||||||
fn auction_status(now: Self::BlockNumber) -> AuctionStatus<Self::BlockNumber> {
|
fn auction_status(now: T::BlockNumber) -> AuctionStatus<T::BlockNumber> {
|
||||||
let early_end = match AuctionInfo::<T>::get() {
|
let early_end = match AuctionInfo::<T>::get() {
|
||||||
Some((_, early_end)) => early_end,
|
Some((_, early_end)) => early_end,
|
||||||
None => return AuctionStatus::NotStarted,
|
None => return AuctionStatus::NotStarted,
|
||||||
@@ -346,12 +352,13 @@ impl<T: Config> Auctioneer for Pallet<T> {
|
|||||||
Self::handle_bid(bidder, para, AuctionCounter::<T>::get(), first_slot, last_slot, amount)
|
Self::handle_bid(bidder, para, AuctionCounter::<T>::get(), first_slot, last_slot, amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period_index() -> Self::LeasePeriod {
|
fn lease_period_index(b: T::BlockNumber) -> Option<(Self::LeasePeriod, bool)> {
|
||||||
T::Leaser::lease_period_index()
|
T::Leaser::lease_period_index(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period() -> Self::LeasePeriod {
|
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||||
T::Leaser::lease_period()
|
fn lease_period_length() -> (T::BlockNumber, T::BlockNumber) {
|
||||||
|
T::Leaser::lease_period_length()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_won_an_auction(para: ParaId, bidder: &T::AccountId) -> bool {
|
fn has_won_an_auction(para: ParaId, bidder: &T::AccountId) -> bool {
|
||||||
@@ -374,10 +381,11 @@ impl<T: Config> Pallet<T> {
|
|||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let maybe_auction = AuctionInfo::<T>::get();
|
let maybe_auction = AuctionInfo::<T>::get();
|
||||||
ensure!(maybe_auction.is_none(), Error::<T>::AuctionInProgress);
|
ensure!(maybe_auction.is_none(), Error::<T>::AuctionInProgress);
|
||||||
ensure!(
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
lease_period_index >= T::Leaser::lease_period_index(),
|
if let Some((current_lease_period, _)) = T::Leaser::lease_period_index(now) {
|
||||||
Error::<T>::LeasePeriodInPast
|
// If there is no active lease period, then we don't need to make this check.
|
||||||
);
|
ensure!(lease_period_index >= current_lease_period, Error::<T>::LeasePeriodInPast);
|
||||||
|
}
|
||||||
|
|
||||||
// Bump the counter.
|
// Bump the counter.
|
||||||
let n = AuctionCounter::<T>::mutate(|n| {
|
let n = AuctionCounter::<T>::mutate(|n| {
|
||||||
@@ -567,7 +575,9 @@ impl<T: Config> Pallet<T> {
|
|||||||
let period_count = LeasePeriodOf::<T>::from(range.len() as u32);
|
let period_count = LeasePeriodOf::<T>::from(range.len() as u32);
|
||||||
|
|
||||||
match T::Leaser::lease_out(para, &leaser, amount, period_begin, period_count) {
|
match T::Leaser::lease_out(para, &leaser, amount, period_begin, period_count) {
|
||||||
Err(LeaseError::ReserveFailed) | Err(LeaseError::AlreadyEnded) => {
|
Err(LeaseError::ReserveFailed) |
|
||||||
|
Err(LeaseError::AlreadyEnded) |
|
||||||
|
Err(LeaseError::NoLeasePeriod) => {
|
||||||
// Should never happen since we just unreserved this amount (and our offset is from the
|
// Should never happen since we just unreserved this amount (and our offset is from the
|
||||||
// present period). But if it does, there's not much we can do.
|
// present period). But if it does, there's not much we can do.
|
||||||
},
|
},
|
||||||
@@ -735,7 +745,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestLeaser;
|
pub struct TestLeaser;
|
||||||
impl Leaser for TestLeaser {
|
impl Leaser<BlockNumber> for TestLeaser {
|
||||||
type AccountId = u64;
|
type AccountId = u64;
|
||||||
type LeasePeriod = BlockNumber;
|
type LeasePeriod = BlockNumber;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
@@ -749,7 +759,10 @@ mod tests {
|
|||||||
) -> Result<(), LeaseError> {
|
) -> Result<(), LeaseError> {
|
||||||
LEASES.with(|l| {
|
LEASES.with(|l| {
|
||||||
let mut leases = l.borrow_mut();
|
let mut leases = l.borrow_mut();
|
||||||
if period_begin < Self::lease_period_index() {
|
let now = System::block_number();
|
||||||
|
let (current_lease_period, _) =
|
||||||
|
Self::lease_period_index(now).ok_or(LeaseError::NoLeasePeriod)?;
|
||||||
|
if period_begin < current_lease_period {
|
||||||
return Err(LeaseError::AlreadyEnded)
|
return Err(LeaseError::AlreadyEnded)
|
||||||
}
|
}
|
||||||
for period in period_begin..(period_begin + period_count) {
|
for period in period_begin..(period_begin + period_count) {
|
||||||
@@ -779,12 +792,18 @@ mod tests {
|
|||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period() -> Self::LeasePeriod {
|
fn lease_period_length() -> (BlockNumber, BlockNumber) {
|
||||||
10
|
(10, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period_index() -> Self::LeasePeriod {
|
fn lease_period_index(b: BlockNumber) -> Option<(Self::LeasePeriod, bool)> {
|
||||||
(System::block_number() / Self::lease_period()).into()
|
let (lease_period_length, offset) = Self::lease_period_length();
|
||||||
|
let b = b.checked_sub(offset)?;
|
||||||
|
|
||||||
|
let lease_period = b / lease_period_length;
|
||||||
|
let first_block = (b % lease_period_length).is_zero();
|
||||||
|
|
||||||
|
Some((lease_period, first_block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn already_leased(
|
fn already_leased(
|
||||||
|
|||||||
@@ -72,8 +72,11 @@ use sp_runtime::{
|
|||||||
};
|
};
|
||||||
use sp_std::vec::Vec;
|
use sp_std::vec::Vec;
|
||||||
|
|
||||||
type CurrencyOf<T> = <<T as Config>::Auctioneer as Auctioneer>::Currency;
|
type CurrencyOf<T> =
|
||||||
type LeasePeriodOf<T> = <<T as Config>::Auctioneer as Auctioneer>::LeasePeriod;
|
<<T as Config>::Auctioneer as Auctioneer<<T as frame_system::Config>::BlockNumber>>::Currency;
|
||||||
|
type LeasePeriodOf<T> = <<T as Config>::Auctioneer as Auctioneer<
|
||||||
|
<T as frame_system::Config>::BlockNumber,
|
||||||
|
>>::LeasePeriod;
|
||||||
type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
type BalanceOf<T> = <CurrencyOf<T> as Currency<<T as frame_system::Config>::AccountId>>::Balance;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -203,8 +206,8 @@ pub mod pallet {
|
|||||||
|
|
||||||
/// The type representing the auctioning system.
|
/// The type representing the auctioning system.
|
||||||
type Auctioneer: Auctioneer<
|
type Auctioneer: Auctioneer<
|
||||||
|
Self::BlockNumber,
|
||||||
AccountId = Self::AccountId,
|
AccountId = Self::AccountId,
|
||||||
BlockNumber = Self::BlockNumber,
|
|
||||||
LeasePeriod = Self::BlockNumber,
|
LeasePeriod = Self::BlockNumber,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
@@ -313,6 +316,8 @@ pub mod pallet {
|
|||||||
AlreadyInNewRaise,
|
AlreadyInNewRaise,
|
||||||
/// No contributions allowed during the VRF delay
|
/// No contributions allowed during the VRF delay
|
||||||
VrfDelayInProgress,
|
VrfDelayInProgress,
|
||||||
|
/// A lease period has not started yet, due to an offset in the starting block.
|
||||||
|
NoLeasePeriod,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pallet::hooks]
|
#[pallet::hooks]
|
||||||
@@ -365,20 +370,31 @@ pub mod pallet {
|
|||||||
verifier: Option<MultiSigner>,
|
verifier: Option<MultiSigner>,
|
||||||
) -> DispatchResult {
|
) -> DispatchResult {
|
||||||
let depositor = ensure_signed(origin)?;
|
let depositor = ensure_signed(origin)?;
|
||||||
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
|
||||||
ensure!(first_period <= last_period, Error::<T>::LastPeriodBeforeFirstPeriod);
|
ensure!(first_period <= last_period, Error::<T>::LastPeriodBeforeFirstPeriod);
|
||||||
let last_period_limit = first_period
|
let last_period_limit = first_period
|
||||||
.checked_add(&((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into())
|
.checked_add(&((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into())
|
||||||
.ok_or(Error::<T>::FirstPeriodTooFarInFuture)?;
|
.ok_or(Error::<T>::FirstPeriodTooFarInFuture)?;
|
||||||
ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture);
|
ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture);
|
||||||
ensure!(end > <frame_system::Pallet<T>>::block_number(), Error::<T>::CannotEndInPast);
|
ensure!(end > now, Error::<T>::CannotEndInPast);
|
||||||
let last_possible_win_date = (first_period.saturating_add(One::one()))
|
|
||||||
.saturating_mul(T::Auctioneer::lease_period());
|
// Here we check the lease period on the ending block is at most the first block of the
|
||||||
ensure!(end <= last_possible_win_date, Error::<T>::EndTooFarInFuture);
|
// period after `first_period`. If it would be larger, there is no way we could win an
|
||||||
ensure!(
|
// active auction, thus it would make no sense to have a crowdloan this long.
|
||||||
first_period >= T::Auctioneer::lease_period_index(),
|
let (lease_period_at_end, is_first_block) =
|
||||||
Error::<T>::FirstPeriodInPast
|
T::Auctioneer::lease_period_index(end).ok_or(Error::<T>::NoLeasePeriod)?;
|
||||||
);
|
let adjusted_lease_period_at_end = if is_first_block {
|
||||||
|
lease_period_at_end.saturating_sub(One::one())
|
||||||
|
} else {
|
||||||
|
lease_period_at_end
|
||||||
|
};
|
||||||
|
ensure!(adjusted_lease_period_at_end <= first_period, Error::<T>::EndTooFarInFuture);
|
||||||
|
|
||||||
|
// Can't start a crowdloan for a lease period that already passed.
|
||||||
|
if let Some((current_lease_period, _)) = T::Auctioneer::lease_period_index(now) {
|
||||||
|
ensure!(first_period >= current_lease_period, Error::<T>::FirstPeriodInPast);
|
||||||
|
}
|
||||||
|
|
||||||
// There should not be an existing fund.
|
// There should not be an existing fund.
|
||||||
ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded);
|
ensure!(!Funds::<T>::contains_key(index), Error::<T>::FundNotEnded);
|
||||||
@@ -439,7 +455,9 @@ pub mod pallet {
|
|||||||
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver);
|
ensure!(now < fund.end, Error::<T>::ContributionPeriodOver);
|
||||||
|
|
||||||
// Make sure crowdloan is in a valid lease period
|
// Make sure crowdloan is in a valid lease period
|
||||||
let current_lease_period = T::Auctioneer::lease_period_index();
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
let (current_lease_period, _) =
|
||||||
|
T::Auctioneer::lease_period_index(now).ok_or(Error::<T>::NoLeasePeriod)?;
|
||||||
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
|
ensure!(current_lease_period <= fund.first_period, Error::<T>::ContributionPeriodOver);
|
||||||
|
|
||||||
// Make sure crowdloan has not already won.
|
// Make sure crowdloan has not already won.
|
||||||
@@ -751,7 +769,8 @@ impl<T: Config> Pallet<T> {
|
|||||||
// `fund.end` can represent the end of a failed crowdloan or the beginning of retirement
|
// `fund.end` can represent the end of a failed crowdloan or the beginning of retirement
|
||||||
// If the current lease period is past the first period they are trying to bid for, then
|
// If the current lease period is past the first period they are trying to bid for, then
|
||||||
// it is already too late to win the bid.
|
// it is already too late to win the bid.
|
||||||
let current_lease_period = T::Auctioneer::lease_period_index();
|
let (current_lease_period, _) =
|
||||||
|
T::Auctioneer::lease_period_index(now).ok_or(Error::<T>::NoLeasePeriod)?;
|
||||||
ensure!(
|
ensure!(
|
||||||
now >= fund.end || current_lease_period > fund.first_period,
|
now >= fund.end || current_lease_period > fund.first_period,
|
||||||
Error::<T>::FundNotEnded
|
Error::<T>::FundNotEnded
|
||||||
@@ -931,14 +950,16 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct TestAuctioneer;
|
pub struct TestAuctioneer;
|
||||||
impl Auctioneer for TestAuctioneer {
|
impl Auctioneer<u64> for TestAuctioneer {
|
||||||
type AccountId = u64;
|
type AccountId = u64;
|
||||||
type BlockNumber = BlockNumber;
|
|
||||||
type LeasePeriod = u64;
|
type LeasePeriod = u64;
|
||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
|
|
||||||
fn new_auction(duration: u64, lease_period_index: u64) -> DispatchResult {
|
fn new_auction(duration: u64, lease_period_index: u64) -> DispatchResult {
|
||||||
assert!(lease_period_index >= Self::lease_period_index());
|
let now = System::block_number();
|
||||||
|
let (current_lease_period, _) =
|
||||||
|
Self::lease_period_index(now).ok_or("no lease period yet")?;
|
||||||
|
assert!(lease_period_index >= current_lease_period);
|
||||||
|
|
||||||
let ending = System::block_number().saturating_add(duration);
|
let ending = System::block_number().saturating_add(duration);
|
||||||
AUCTION.with(|p| *p.borrow_mut() = Some((lease_period_index, ending)));
|
AUCTION.with(|p| *p.borrow_mut() = Some((lease_period_index, ending)));
|
||||||
@@ -991,12 +1012,17 @@ mod tests {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period_index() -> u64 {
|
fn lease_period_index(b: BlockNumber) -> Option<(u64, bool)> {
|
||||||
System::block_number() / Self::lease_period()
|
let (lease_period_length, offset) = Self::lease_period_length();
|
||||||
|
let b = b.checked_sub(offset)?;
|
||||||
|
|
||||||
|
let lease_period = b / lease_period_length;
|
||||||
|
let first_block = (b % lease_period_length).is_zero();
|
||||||
|
Some((lease_period, first_block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period() -> u64 {
|
fn lease_period_length() -> (u64, u64) {
|
||||||
20
|
(20, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_won_an_auction(para: ParaId, bidder: &u64) -> bool {
|
fn has_won_an_auction(para: ParaId, bidder: &u64) -> bool {
|
||||||
@@ -1367,7 +1393,8 @@ mod tests {
|
|||||||
let para_3 = new_para();
|
let para_3 = new_para();
|
||||||
assert_ok!(Crowdloan::create(Origin::signed(1), para_3, 1000, 1, 4, 40, None));
|
assert_ok!(Crowdloan::create(Origin::signed(1), para_3, 1000, 1, 4, 40, None));
|
||||||
run_to_block(40);
|
run_to_block(40);
|
||||||
assert_eq!(TestAuctioneer::lease_period_index(), 2);
|
let now = System::block_number();
|
||||||
|
assert_eq!(TestAuctioneer::lease_period_index(now).unwrap().0, 2);
|
||||||
assert_noop!(
|
assert_noop!(
|
||||||
Crowdloan::contribute(Origin::signed(1), para_3, 49, None),
|
Crowdloan::contribute(Origin::signed(1), para_3, 49, None),
|
||||||
Error::<Test>::ContributionPeriodOver
|
Error::<Test>::ContributionPeriodOver
|
||||||
@@ -1842,7 +1869,11 @@ mod benchmarking {
|
|||||||
|
|
||||||
fn create_fund<T: Config>(id: u32, end: T::BlockNumber) -> ParaId {
|
fn create_fund<T: Config>(id: u32, end: T::BlockNumber) -> ParaId {
|
||||||
let cap = BalanceOf::<T>::max_value();
|
let cap = BalanceOf::<T>::max_value();
|
||||||
let lease_period_index = T::Auctioneer::lease_period_index();
|
let (_, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
// Set to the very beginning of lease period index 0.
|
||||||
|
frame_system::Pallet::<T>::set_block_number(offset);
|
||||||
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
let (lease_period_index, _) = T::Auctioneer::lease_period_index(now).unwrap_or_default();
|
||||||
let first_period = lease_period_index;
|
let first_period = lease_period_index;
|
||||||
let last_period =
|
let last_period =
|
||||||
lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into();
|
lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into();
|
||||||
@@ -1894,7 +1925,8 @@ mod benchmarking {
|
|||||||
let cap = BalanceOf::<T>::max_value();
|
let cap = BalanceOf::<T>::max_value();
|
||||||
let first_period = 0u32.into();
|
let first_period = 0u32.into();
|
||||||
let last_period = 3u32.into();
|
let last_period = 3u32.into();
|
||||||
let end = T::Auctioneer::lease_period();
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
let head_data = T::Registrar::worst_head_data();
|
let head_data = T::Registrar::worst_head_data();
|
||||||
@@ -1913,7 +1945,9 @@ mod benchmarking {
|
|||||||
|
|
||||||
// Contribute has two arms: PreEnding and Ending, but both are equal complexity.
|
// Contribute has two arms: PreEnding and Ending, but both are equal complexity.
|
||||||
contribute {
|
contribute {
|
||||||
let fund_index = create_fund::<T>(1, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1, end);
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
let contribution = T::MinContribution::get();
|
let contribution = T::MinContribution::get();
|
||||||
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
CurrencyOf::<T>::make_free_balance_be(&caller, BalanceOf::<T>::max_value());
|
||||||
@@ -1931,7 +1965,9 @@ mod benchmarking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
withdraw {
|
withdraw {
|
||||||
let fund_index = create_fund::<T>(1337, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1337, end);
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
let contributor = account("contributor", 0, 0);
|
let contributor = account("contributor", 0, 0);
|
||||||
contribute_fund::<T>(&contributor, fund_index);
|
contribute_fund::<T>(&contributor, fund_index);
|
||||||
@@ -1945,7 +1981,9 @@ mod benchmarking {
|
|||||||
#[skip_meta]
|
#[skip_meta]
|
||||||
refund {
|
refund {
|
||||||
let k in 0 .. T::RemoveKeysLimit::get();
|
let k in 0 .. T::RemoveKeysLimit::get();
|
||||||
let fund_index = create_fund::<T>(1337, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1337, end);
|
||||||
|
|
||||||
// Dissolve will remove at most `RemoveKeysLimit` at once.
|
// Dissolve will remove at most `RemoveKeysLimit` at once.
|
||||||
for i in 0 .. k {
|
for i in 0 .. k {
|
||||||
@@ -1960,7 +1998,9 @@ mod benchmarking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dissolve {
|
dissolve {
|
||||||
let fund_index = create_fund::<T>(1337, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1337, end);
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
frame_system::Pallet::<T>::set_block_number(T::BlockNumber::max_value());
|
frame_system::Pallet::<T>::set_block_number(T::BlockNumber::max_value());
|
||||||
}: _(RawOrigin::Signed(caller.clone()), fund_index)
|
}: _(RawOrigin::Signed(caller.clone()), fund_index)
|
||||||
@@ -1973,7 +2013,8 @@ mod benchmarking {
|
|||||||
let cap = BalanceOf::<T>::max_value();
|
let cap = BalanceOf::<T>::max_value();
|
||||||
let first_period = 0u32.into();
|
let first_period = 0u32.into();
|
||||||
let last_period = 3u32.into();
|
let last_period = 3u32.into();
|
||||||
let end = T::Auctioneer::lease_period();
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
let head_data = T::Registrar::worst_head_data();
|
let head_data = T::Registrar::worst_head_data();
|
||||||
@@ -1997,7 +2038,9 @@ mod benchmarking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add_memo {
|
add_memo {
|
||||||
let fund_index = create_fund::<T>(1, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1, end);
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
contribute_fund::<T>(&caller, fund_index);
|
contribute_fund::<T>(&caller, fund_index);
|
||||||
let worst_memo = vec![42; T::MaxMemoLength::get().into()];
|
let worst_memo = vec![42; T::MaxMemoLength::get().into()];
|
||||||
@@ -2011,7 +2054,9 @@ mod benchmarking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
poke {
|
poke {
|
||||||
let fund_index = create_fund::<T>(1, 100u32.into());
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end = lpl + offset;
|
||||||
|
let fund_index = create_fund::<T>(1, end);
|
||||||
let caller: T::AccountId = whitelisted_caller();
|
let caller: T::AccountId = whitelisted_caller();
|
||||||
contribute_fund::<T>(&caller, fund_index);
|
contribute_fund::<T>(&caller, fund_index);
|
||||||
NewRaise::<T>::kill();
|
NewRaise::<T>::kill();
|
||||||
@@ -2028,7 +2073,8 @@ mod benchmarking {
|
|||||||
on_initialize {
|
on_initialize {
|
||||||
// We test the complexity over different number of new raise
|
// We test the complexity over different number of new raise
|
||||||
let n in 2 .. 100;
|
let n in 2 .. 100;
|
||||||
let end_block: T::BlockNumber = 100u32.into();
|
let (lpl, offset) = T::Auctioneer::lease_period_length();
|
||||||
|
let end_block = lpl + offset - 1u32.into();
|
||||||
|
|
||||||
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
let pubkey = crypto::create_ed25519_pubkey(b"//verifier".to_vec());
|
||||||
|
|
||||||
@@ -2043,7 +2089,8 @@ mod benchmarking {
|
|||||||
Crowdloan::<T>::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution, Some(sig))?;
|
Crowdloan::<T>::contribute(RawOrigin::Signed(contributor).into(), fund_index, contribution, Some(sig))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let lease_period_index = T::Auctioneer::lease_period_index();
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
let (lease_period_index, _) = T::Auctioneer::lease_period_index(now).unwrap_or_default();
|
||||||
let duration = end_block
|
let duration = end_block
|
||||||
.checked_sub(&frame_system::Pallet::<T>::block_number())
|
.checked_sub(&frame_system::Pallet::<T>::block_number())
|
||||||
.ok_or("duration of auction less than zero")?;
|
.ok_or("duration of auction less than zero")?;
|
||||||
@@ -2062,7 +2109,7 @@ mod benchmarking {
|
|||||||
|
|
||||||
impl_benchmark_test_suite!(
|
impl_benchmark_test_suite!(
|
||||||
Crowdloan,
|
Crowdloan,
|
||||||
crate::integration_tests::new_test_ext(),
|
crate::integration_tests::new_test_ext_with_offset(10),
|
||||||
crate::integration_tests::Test,
|
crate::integration_tests::Test,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,6 +203,7 @@ impl auctions::Config for Test {
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const LeasePeriod: BlockNumber = 100;
|
pub const LeasePeriod: BlockNumber = 100;
|
||||||
|
pub static LeaseOffset: BlockNumber = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl slots::Config for Test {
|
impl slots::Config for Test {
|
||||||
@@ -210,6 +211,7 @@ impl slots::Config for Test {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = Registrar;
|
type Registrar = Registrar;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = LeaseOffset;
|
||||||
type WeightInfo = crate::slots::TestWeightInfo;
|
type WeightInfo = crate::slots::TestWeightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +256,11 @@ pub fn new_test_ext() -> TestExternalities {
|
|||||||
ext
|
ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_test_ext_with_offset(n: BlockNumber) -> TestExternalities {
|
||||||
|
LeaseOffset::set(n);
|
||||||
|
new_test_ext()
|
||||||
|
}
|
||||||
|
|
||||||
const BLOCKS_PER_SESSION: u32 = 10;
|
const BLOCKS_PER_SESSION: u32 = 10;
|
||||||
|
|
||||||
fn maybe_new_session(n: u32) {
|
fn maybe_new_session(n: u32) {
|
||||||
@@ -298,171 +305,176 @@ fn last_event() -> Event {
|
|||||||
System::events().pop().expect("Event expected").event
|
System::events().pop().expect("Event expected").event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Runs an end to end test of the auction, crowdloan, slots, and onboarding process over varying
|
||||||
|
// lease period offsets.
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_end_to_end_works() {
|
fn basic_end_to_end_works() {
|
||||||
new_test_ext().execute_with(|| {
|
for offset in [0u32, 50, 100, 200].iter() {
|
||||||
let para_1 = LOWEST_PUBLIC_ID;
|
LeaseOffset::set(*offset);
|
||||||
let para_2 = LOWEST_PUBLIC_ID + 1;
|
new_test_ext().execute_with(|| {
|
||||||
assert!(System::block_number().is_one());
|
let para_1 = LOWEST_PUBLIC_ID;
|
||||||
// User 1 and 2 will own parachains
|
let para_2 = LOWEST_PUBLIC_ID + 1;
|
||||||
Balances::make_free_balance_be(&1, 1_000_000_000);
|
assert!(System::block_number().is_one());
|
||||||
Balances::make_free_balance_be(&2, 1_000_000_000);
|
// User 1 and 2 will own parachains
|
||||||
// First register 2 parathreads
|
Balances::make_free_balance_be(&1, 1_000_000_000);
|
||||||
let genesis_head = Registrar::worst_head_data();
|
Balances::make_free_balance_be(&2, 1_000_000_000);
|
||||||
let validation_code = Registrar::worst_validation_code();
|
// First register 2 parathreads
|
||||||
assert_ok!(Registrar::reserve(Origin::signed(1)));
|
let genesis_head = Registrar::worst_head_data();
|
||||||
assert_ok!(Registrar::register(
|
let validation_code = Registrar::worst_validation_code();
|
||||||
Origin::signed(1),
|
assert_ok!(Registrar::reserve(Origin::signed(1)));
|
||||||
ParaId::from(para_1),
|
assert_ok!(Registrar::register(
|
||||||
genesis_head.clone(),
|
Origin::signed(1),
|
||||||
validation_code.clone(),
|
ParaId::from(para_1),
|
||||||
));
|
genesis_head.clone(),
|
||||||
assert_ok!(Registrar::reserve(Origin::signed(2)));
|
validation_code.clone(),
|
||||||
assert_ok!(Registrar::register(
|
));
|
||||||
Origin::signed(2),
|
assert_ok!(Registrar::reserve(Origin::signed(2)));
|
||||||
ParaId::from(2001),
|
assert_ok!(Registrar::register(
|
||||||
genesis_head,
|
Origin::signed(2),
|
||||||
validation_code,
|
ParaId::from(2001),
|
||||||
));
|
genesis_head,
|
||||||
|
validation_code,
|
||||||
|
));
|
||||||
|
|
||||||
// Paras should be onboarding
|
// Paras should be onboarding
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Onboarding));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Onboarding));
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Onboarding));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Onboarding));
|
||||||
|
|
||||||
// Start a new auction in the future
|
// Start a new auction in the future
|
||||||
let duration = 99u32;
|
let duration = 99u32 + offset;
|
||||||
let lease_period_index_start = 4u32;
|
let lease_period_index_start = 4u32;
|
||||||
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
|
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
|
||||||
|
|
||||||
// 2 sessions later they are parathreads
|
// 2 sessions later they are parathreads
|
||||||
run_to_session(2);
|
run_to_session(2);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||||
|
|
||||||
// Para 1 will bid directly for slot 1, 2
|
// Para 1 will bid directly for slot 1, 2
|
||||||
// Open a crowdloan for Para 2 for slot 3, 4
|
// Open a crowdloan for Para 2 for slot 3, 4
|
||||||
assert_ok!(Crowdloan::create(
|
assert_ok!(Crowdloan::create(
|
||||||
Origin::signed(2),
|
Origin::signed(2),
|
||||||
ParaId::from(para_2),
|
ParaId::from(para_2),
|
||||||
1_000, // Cap
|
1_000, // Cap
|
||||||
lease_period_index_start + 2, // First Slot
|
lease_period_index_start + 2, // First Slot
|
||||||
lease_period_index_start + 3, // Last Slot
|
lease_period_index_start + 3, // Last Slot
|
||||||
200, // Block End
|
200 + offset, // Block End
|
||||||
None,
|
|
||||||
));
|
|
||||||
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(para_2));
|
|
||||||
|
|
||||||
// Auction ending begins on block 100, so we make a bid before then.
|
|
||||||
run_to_block(90);
|
|
||||||
|
|
||||||
Balances::make_free_balance_be(&10, 1_000_000_000);
|
|
||||||
Balances::make_free_balance_be(&20, 1_000_000_000);
|
|
||||||
|
|
||||||
// User 10 will bid directly for parachain 1
|
|
||||||
assert_ok!(Auctions::bid(
|
|
||||||
Origin::signed(10),
|
|
||||||
ParaId::from(para_1),
|
|
||||||
1, // Auction Index
|
|
||||||
lease_period_index_start + 0, // First Slot
|
|
||||||
lease_period_index_start + 1, // Last slot
|
|
||||||
910, // Amount
|
|
||||||
));
|
|
||||||
|
|
||||||
// User 2 will be a contribute to crowdloan for parachain 2
|
|
||||||
Balances::make_free_balance_be(&2, 1_000_000_000);
|
|
||||||
assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(para_2), 920, None));
|
|
||||||
|
|
||||||
// Auction ends at block 110
|
|
||||||
run_to_block(109);
|
|
||||||
assert_eq!(
|
|
||||||
last_event(),
|
|
||||||
crowdloan::Event::<Test>::HandleBidResult(ParaId::from(para_2), Ok(())).into(),
|
|
||||||
);
|
|
||||||
run_to_block(110);
|
|
||||||
assert_eq!(last_event(), auctions::Event::<Test>::AuctionClosed(1).into());
|
|
||||||
|
|
||||||
// Paras should have won slots
|
|
||||||
assert_eq!(
|
|
||||||
slots::Leases::<Test>::get(ParaId::from(para_1)),
|
|
||||||
// -- 1 --- 2 --- 3 --------- 4 ------------ 5 --------
|
|
||||||
vec![None, None, None, Some((10, 910)), Some((10, 910))],
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
slots::Leases::<Test>::get(ParaId::from(para_2)),
|
|
||||||
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------------- 6 --------------------------- 7 ----------------
|
|
||||||
vec![
|
|
||||||
None,
|
None,
|
||||||
None,
|
));
|
||||||
None,
|
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(para_2));
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some((crowdloan_account, 920)),
|
|
||||||
Some((crowdloan_account, 920))
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// Should not be able to contribute to a winning crowdloan
|
// Auction ending begins on block 100 + offset, so we make a bid before then.
|
||||||
Balances::make_free_balance_be(&3, 1_000_000_000);
|
run_to_block(90 + offset);
|
||||||
assert_noop!(
|
|
||||||
Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None),
|
|
||||||
CrowdloanError::<Test>::BidOrLeaseActive
|
|
||||||
);
|
|
||||||
|
|
||||||
// New leases will start on block 400
|
Balances::make_free_balance_be(&10, 1_000_000_000);
|
||||||
let lease_start_block = 400;
|
Balances::make_free_balance_be(&20, 1_000_000_000);
|
||||||
run_to_block(lease_start_block);
|
|
||||||
|
|
||||||
// First slot, Para 1 should be transitioning to Parachain
|
// User 10 will bid directly for parachain 1
|
||||||
assert_eq!(
|
assert_ok!(Auctions::bid(
|
||||||
Paras::lifecycle(ParaId::from(para_1)),
|
Origin::signed(10),
|
||||||
Some(ParaLifecycle::UpgradingParathread)
|
ParaId::from(para_1),
|
||||||
);
|
1, // Auction Index
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
lease_period_index_start + 0, // First Slot
|
||||||
|
lease_period_index_start + 1, // Last slot
|
||||||
|
910, // Amount
|
||||||
|
));
|
||||||
|
|
||||||
// Two sessions later, it has upgraded
|
// User 2 will be a contribute to crowdloan for parachain 2
|
||||||
run_to_block(lease_start_block + 20);
|
Balances::make_free_balance_be(&2, 1_000_000_000);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parachain));
|
assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(para_2), 920, None));
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
|
||||||
|
|
||||||
// Second slot nothing happens :)
|
// Auction ends at block 110 + offset
|
||||||
run_to_block(lease_start_block + 100);
|
run_to_block(109 + offset);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parachain));
|
assert_eq!(
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
last_event(),
|
||||||
|
crowdloan::Event::<Test>::HandleBidResult(ParaId::from(para_2), Ok(())).into(),
|
||||||
|
);
|
||||||
|
run_to_block(110 + offset);
|
||||||
|
assert_eq!(last_event(), auctions::Event::<Test>::AuctionClosed(1).into());
|
||||||
|
|
||||||
// Third slot, Para 2 should be upgrading, and Para 1 is downgrading
|
// Paras should have won slots
|
||||||
run_to_block(lease_start_block + 200);
|
assert_eq!(
|
||||||
assert_eq!(
|
slots::Leases::<Test>::get(ParaId::from(para_1)),
|
||||||
Paras::lifecycle(ParaId::from(para_1)),
|
// -- 1 --- 2 --- 3 --------- 4 ------------ 5 --------
|
||||||
Some(ParaLifecycle::DowngradingParachain)
|
vec![None, None, None, Some((10, 910)), Some((10, 910))],
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Paras::lifecycle(ParaId::from(para_2)),
|
slots::Leases::<Test>::get(ParaId::from(para_2)),
|
||||||
Some(ParaLifecycle::UpgradingParathread)
|
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------------- 6 --------------------------- 7 ----------------
|
||||||
);
|
vec![
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
Some((crowdloan_account, 920)),
|
||||||
|
Some((crowdloan_account, 920))
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
// Two sessions later, they have transitioned
|
// Should not be able to contribute to a winning crowdloan
|
||||||
run_to_block(lease_start_block + 220);
|
Balances::make_free_balance_be(&3, 1_000_000_000);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
assert_noop!(
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parachain));
|
Crowdloan::contribute(Origin::signed(3), ParaId::from(2001), 10, None),
|
||||||
|
CrowdloanError::<Test>::BidOrLeaseActive
|
||||||
|
);
|
||||||
|
|
||||||
// Fourth slot nothing happens :)
|
// New leases will start on block 400
|
||||||
run_to_block(lease_start_block + 300);
|
let lease_start_block = 400 + offset;
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
run_to_block(lease_start_block);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parachain));
|
|
||||||
|
|
||||||
// Fifth slot, Para 2 is downgrading
|
// First slot, Para 1 should be transitioning to Parachain
|
||||||
run_to_block(lease_start_block + 400);
|
assert_eq!(
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
Paras::lifecycle(ParaId::from(para_1)),
|
||||||
assert_eq!(
|
Some(ParaLifecycle::UpgradingParathread)
|
||||||
Paras::lifecycle(ParaId::from(para_2)),
|
);
|
||||||
Some(ParaLifecycle::DowngradingParachain)
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||||
);
|
|
||||||
|
|
||||||
// Two sessions later, Para 2 is downgraded
|
// Two sessions later, it has upgraded
|
||||||
run_to_block(lease_start_block + 420);
|
run_to_block(lease_start_block + 20);
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parachain));
|
||||||
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||||
});
|
|
||||||
|
// Second slot nothing happens :)
|
||||||
|
run_to_block(lease_start_block + 100);
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parachain));
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||||
|
|
||||||
|
// Third slot, Para 2 should be upgrading, and Para 1 is downgrading
|
||||||
|
run_to_block(lease_start_block + 200);
|
||||||
|
assert_eq!(
|
||||||
|
Paras::lifecycle(ParaId::from(para_1)),
|
||||||
|
Some(ParaLifecycle::DowngradingParachain)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Paras::lifecycle(ParaId::from(para_2)),
|
||||||
|
Some(ParaLifecycle::UpgradingParathread)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Two sessions later, they have transitioned
|
||||||
|
run_to_block(lease_start_block + 220);
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parachain));
|
||||||
|
|
||||||
|
// Fourth slot nothing happens :)
|
||||||
|
run_to_block(lease_start_block + 300);
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parachain));
|
||||||
|
|
||||||
|
// Fifth slot, Para 2 is downgrading
|
||||||
|
run_to_block(lease_start_block + 400);
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||||
|
assert_eq!(
|
||||||
|
Paras::lifecycle(ParaId::from(para_2)),
|
||||||
|
Some(ParaLifecycle::DowngradingParachain)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Two sessions later, Para 2 is downgraded
|
||||||
|
run_to_block(lease_start_block + 420);
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_1)), Some(ParaLifecycle::Parathread));
|
||||||
|
assert_eq!(Paras::lifecycle(ParaId::from(para_2)), Some(ParaLifecycle::Parathread));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ pub mod pallet {
|
|||||||
#[pallet::constant]
|
#[pallet::constant]
|
||||||
type LeasePeriod: Get<Self::BlockNumber>;
|
type LeasePeriod: Get<Self::BlockNumber>;
|
||||||
|
|
||||||
|
/// The number of blocks to offset each lease period by.
|
||||||
|
#[pallet::constant]
|
||||||
|
type LeaseOffset: Get<Self::BlockNumber>;
|
||||||
|
|
||||||
/// Weight Information for the Extrinsics in the Pallet
|
/// Weight Information for the Extrinsics in the Pallet
|
||||||
type WeightInfo: WeightInfo;
|
type WeightInfo: WeightInfo;
|
||||||
}
|
}
|
||||||
@@ -138,14 +142,15 @@ pub mod pallet {
|
|||||||
#[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) -> Weight {
|
fn on_initialize(n: T::BlockNumber) -> Weight {
|
||||||
// If we're beginning a new lease period then handle that.
|
if let Some((lease_period, first_block)) = Self::lease_period_index(n) {
|
||||||
let lease_period = T::LeasePeriod::get();
|
// If we're beginning a new lease period then handle that.
|
||||||
if (n % lease_period).is_zero() {
|
if first_block {
|
||||||
let lease_period_index = n / lease_period;
|
return Self::manage_lease_period_start(lease_period)
|
||||||
Self::manage_lease_period_start(lease_period_index)
|
}
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We didn't return early above, so we didn't do anything.
|
||||||
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +326,7 @@ impl<T: Config> crate::traits::OnSwap for Pallet<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Config> Leaser for Pallet<T> {
|
impl<T: Config> Leaser<T::BlockNumber> for Pallet<T> {
|
||||||
type AccountId = T::AccountId;
|
type AccountId = T::AccountId;
|
||||||
type LeasePeriod = T::BlockNumber;
|
type LeasePeriod = T::BlockNumber;
|
||||||
type Currency = T::Currency;
|
type Currency = T::Currency;
|
||||||
@@ -333,7 +338,9 @@ impl<T: Config> Leaser for Pallet<T> {
|
|||||||
period_begin: Self::LeasePeriod,
|
period_begin: Self::LeasePeriod,
|
||||||
period_count: Self::LeasePeriod,
|
period_count: Self::LeasePeriod,
|
||||||
) -> Result<(), LeaseError> {
|
) -> Result<(), LeaseError> {
|
||||||
let current_lease_period = Self::lease_period_index();
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
let (current_lease_period, _) =
|
||||||
|
Self::lease_period_index(now).ok_or(LeaseError::NoLeasePeriod)?;
|
||||||
// Finally, we update the deposit held so it is `amount` for the new lease period
|
// Finally, we update the deposit held so it is `amount` for the new lease period
|
||||||
// indices that were won in the auction.
|
// indices that were won in the auction.
|
||||||
let offset = period_begin
|
let offset = period_begin
|
||||||
@@ -427,12 +434,18 @@ impl<T: Config> Leaser for Pallet<T> {
|
|||||||
.unwrap_or_else(Zero::zero)
|
.unwrap_or_else(Zero::zero)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period() -> Self::LeasePeriod {
|
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||||
T::LeasePeriod::get()
|
fn lease_period_length() -> (T::BlockNumber, T::BlockNumber) {
|
||||||
|
(T::LeasePeriod::get(), T::LeaseOffset::get())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lease_period_index() -> Self::LeasePeriod {
|
fn lease_period_index(b: T::BlockNumber) -> Option<(Self::LeasePeriod, bool)> {
|
||||||
<frame_system::Pallet<T>>::block_number() / T::LeasePeriod::get()
|
// Note that blocks before `LeaseOffset` do not count as any lease period.
|
||||||
|
let offset_block_now = b.checked_sub(&T::LeaseOffset::get())?;
|
||||||
|
let lease_period = offset_block_now / T::LeasePeriod::get();
|
||||||
|
let first_block = (offset_block_now % T::LeasePeriod::get()).is_zero();
|
||||||
|
|
||||||
|
Some((lease_period, first_block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn already_leased(
|
fn already_leased(
|
||||||
@@ -440,7 +453,11 @@ impl<T: Config> Leaser for Pallet<T> {
|
|||||||
first_period: Self::LeasePeriod,
|
first_period: Self::LeasePeriod,
|
||||||
last_period: Self::LeasePeriod,
|
last_period: Self::LeasePeriod,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let current_lease_period = Self::lease_period_index();
|
let now = frame_system::Pallet::<T>::block_number();
|
||||||
|
let (current_lease_period, _) = match Self::lease_period_index(now) {
|
||||||
|
Some(clp) => clp,
|
||||||
|
None => return true,
|
||||||
|
};
|
||||||
|
|
||||||
// Can't look in the past, so we pick whichever is the biggest.
|
// Can't look in the past, so we pick whichever is the biggest.
|
||||||
let start_period = first_period.max(current_lease_period);
|
let start_period = first_period.max(current_lease_period);
|
||||||
@@ -545,6 +562,7 @@ mod tests {
|
|||||||
|
|
||||||
parameter_types! {
|
parameter_types! {
|
||||||
pub const LeasePeriod: BlockNumber = 10;
|
pub const LeasePeriod: BlockNumber = 10;
|
||||||
|
pub static LeaseOffset: BlockNumber = 0;
|
||||||
pub const ParaDeposit: u64 = 1;
|
pub const ParaDeposit: u64 = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -553,6 +571,7 @@ mod tests {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = TestRegistrar<Test>;
|
type Registrar = TestRegistrar<Test>;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = LeaseOffset;
|
||||||
type WeightInfo = crate::slots::TestWeightInfo;
|
type WeightInfo = crate::slots::TestWeightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,12 +603,14 @@ mod tests {
|
|||||||
fn basic_setup_works() {
|
fn basic_setup_works() {
|
||||||
new_test_ext().execute_with(|| {
|
new_test_ext().execute_with(|| {
|
||||||
run_to_block(1);
|
run_to_block(1);
|
||||||
assert_eq!(Slots::lease_period(), 10);
|
assert_eq!(Slots::lease_period_length(), (10, 0));
|
||||||
assert_eq!(Slots::lease_period_index(), 0);
|
let now = System::block_number();
|
||||||
|
assert_eq!(Slots::lease_period_index(now).unwrap().0, 0);
|
||||||
assert_eq!(Slots::deposit_held(1.into(), &1), 0);
|
assert_eq!(Slots::deposit_held(1.into(), &1), 0);
|
||||||
|
|
||||||
run_to_block(10);
|
run_to_block(10);
|
||||||
assert_eq!(Slots::lease_period_index(), 1);
|
let now = System::block_number();
|
||||||
|
assert_eq!(Slots::lease_period_index(now).unwrap().0, 1);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -850,7 +871,8 @@ mod tests {
|
|||||||
));
|
));
|
||||||
|
|
||||||
run_to_block(20);
|
run_to_block(20);
|
||||||
assert_eq!(Slots::lease_period_index(), 2);
|
let now = System::block_number();
|
||||||
|
assert_eq!(Slots::lease_period_index(now).unwrap().0, 2);
|
||||||
// Can't lease from the past
|
// Can't lease from the past
|
||||||
assert!(Slots::lease_out(1.into(), &1, 1, 1, 1).is_err());
|
assert!(Slots::lease_out(1.into(), &1, 1, 1, 1).is_err());
|
||||||
// Lease in the current period triggers onboarding
|
// Lease in the current period triggers onboarding
|
||||||
@@ -913,6 +935,37 @@ mod tests {
|
|||||||
assert_eq!(TestRegistrar::<Test>::operations(), vec![(2.into(), 1, true),]);
|
assert_eq!(TestRegistrar::<Test>::operations(), vec![(2.into(), 1, true),]);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn lease_period_offset_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
let (lpl, offset) = Slots::lease_period_length();
|
||||||
|
assert_eq!(offset, 0);
|
||||||
|
assert_eq!(Slots::lease_period_index(0), Some((0, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(1), Some((0, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl - 1), Some((0, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl), Some((1, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl + 1), Some((1, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl - 1), Some((1, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl), Some((2, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl + 1), Some((2, false)));
|
||||||
|
|
||||||
|
// Lease period is 10, and we add an offset of 5.
|
||||||
|
LeaseOffset::set(5);
|
||||||
|
let (lpl, offset) = Slots::lease_period_length();
|
||||||
|
assert_eq!(offset, 5);
|
||||||
|
assert_eq!(Slots::lease_period_index(0), None);
|
||||||
|
assert_eq!(Slots::lease_period_index(1), None);
|
||||||
|
assert_eq!(Slots::lease_period_index(offset), Some((0, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl), Some((0, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl - 1 + offset), Some((0, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl + offset), Some((1, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(lpl + offset + 1), Some((1, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl - 1 + offset), Some((1, false)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl + offset), Some((2, true)));
|
||||||
|
assert_eq!(Slots::lease_period_index(2 * lpl + offset + 1), Some((2, false)));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "runtime-benchmarks")]
|
#[cfg(feature = "runtime-benchmarks")]
|
||||||
|
|||||||
@@ -94,10 +94,12 @@ pub enum LeaseError {
|
|||||||
AlreadyLeased,
|
AlreadyLeased,
|
||||||
/// The period to be leased has already ended.
|
/// The period to be leased has already ended.
|
||||||
AlreadyEnded,
|
AlreadyEnded,
|
||||||
|
/// A lease period has not started yet, due to an offset in the starting block.
|
||||||
|
NoLeasePeriod,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Lease manager. Used by the auction module to handle parachain slot leases.
|
/// Lease manager. Used by the auction module to handle parachain slot leases.
|
||||||
pub trait Leaser {
|
pub trait Leaser<BlockNumber> {
|
||||||
/// An account identifier for a leaser.
|
/// An account identifier for a leaser.
|
||||||
type AccountId;
|
type AccountId;
|
||||||
|
|
||||||
@@ -133,11 +135,16 @@ pub trait Leaser {
|
|||||||
leaser: &Self::AccountId,
|
leaser: &Self::AccountId,
|
||||||
) -> <Self::Currency as Currency<Self::AccountId>>::Balance;
|
) -> <Self::Currency as Currency<Self::AccountId>>::Balance;
|
||||||
|
|
||||||
/// The lease period. This is constant, but can't be a `const` due to it being a runtime configurable quantity.
|
/// The length of a lease period, and any offset which may be introduced.
|
||||||
fn lease_period() -> Self::LeasePeriod;
|
/// This is only used in benchmarking to automate certain calls.
|
||||||
|
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||||
|
fn lease_period_length() -> (BlockNumber, BlockNumber);
|
||||||
|
|
||||||
/// Returns the current lease period.
|
/// Returns the lease period at `block`, and if this is the first block of a new lease period.
|
||||||
fn lease_period_index() -> Self::LeasePeriod;
|
///
|
||||||
|
/// Will return `None` if the first lease period has not started yet, for example when an offset
|
||||||
|
/// is placed.
|
||||||
|
fn lease_period_index(block: BlockNumber) -> Option<(Self::LeasePeriod, bool)>;
|
||||||
|
|
||||||
/// Returns true if the parachain already has a lease in any of lease periods in the inclusive
|
/// Returns true if the parachain already has a lease in any of lease periods in the inclusive
|
||||||
/// range `[first_period, last_period]`, intersected with the unbounded range [`current_lease_period`..] .
|
/// range `[first_period, last_period]`, intersected with the unbounded range [`current_lease_period`..] .
|
||||||
@@ -189,13 +196,10 @@ impl<BlockNumber> AuctionStatus<BlockNumber> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Auctioneer {
|
pub trait Auctioneer<BlockNumber> {
|
||||||
/// An account identifier for a leaser.
|
/// An account identifier for a leaser.
|
||||||
type AccountId;
|
type AccountId;
|
||||||
|
|
||||||
/// The measurement type for counting blocks.
|
|
||||||
type BlockNumber;
|
|
||||||
|
|
||||||
/// The measurement type for counting lease periods (generally the same as `BlockNumber`).
|
/// The measurement type for counting lease periods (generally the same as `BlockNumber`).
|
||||||
type LeasePeriod;
|
type LeasePeriod;
|
||||||
|
|
||||||
@@ -207,13 +211,10 @@ pub trait Auctioneer {
|
|||||||
/// 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`
|
||||||
/// of this auction and the `lease_period_index` of the initial lease period of the four that
|
/// of this auction and the `lease_period_index` of the initial lease period of the four that
|
||||||
/// are to be auctioned.
|
/// are to be auctioned.
|
||||||
fn new_auction(
|
fn new_auction(duration: BlockNumber, lease_period_index: Self::LeasePeriod) -> DispatchResult;
|
||||||
duration: Self::BlockNumber,
|
|
||||||
lease_period_index: Self::LeasePeriod,
|
|
||||||
) -> DispatchResult;
|
|
||||||
|
|
||||||
/// Given the current block number, return the current auction status.
|
/// Given the current block number, return the current auction status.
|
||||||
fn auction_status(now: Self::BlockNumber) -> AuctionStatus<Self::BlockNumber>;
|
fn auction_status(now: BlockNumber) -> AuctionStatus<BlockNumber>;
|
||||||
|
|
||||||
/// Place a bid in the current auction.
|
/// Place a bid in the current auction.
|
||||||
///
|
///
|
||||||
@@ -234,11 +235,16 @@ pub trait Auctioneer {
|
|||||||
amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
|
amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
|
||||||
) -> DispatchResult;
|
) -> DispatchResult;
|
||||||
|
|
||||||
/// Returns the current lease period.
|
/// The length of a lease period, and any offset which may be introduced.
|
||||||
fn lease_period_index() -> Self::LeasePeriod;
|
/// This is only used in benchmarking to automate certain calls.
|
||||||
|
#[cfg(any(feature = "runtime-benchmarks", test))]
|
||||||
|
fn lease_period_length() -> (BlockNumber, BlockNumber);
|
||||||
|
|
||||||
/// Returns the length of a lease period.
|
/// Returns the lease period at `block`, and if this is the first block of a new lease period.
|
||||||
fn lease_period() -> Self::LeasePeriod;
|
///
|
||||||
|
/// Will return `None` if the first lease period has not started yet, for example when an offset
|
||||||
|
/// is placed.
|
||||||
|
fn lease_period_index(block: BlockNumber) -> Option<(Self::LeasePeriod, bool)>;
|
||||||
|
|
||||||
/// Check if the para and user combination has won an auction in the past.
|
/// Check if the para and user combination has won an auction in the past.
|
||||||
fn has_won_an_auction(para: ParaId, bidder: &Self::AccountId) -> bool;
|
fn has_won_an_auction(para: ParaId, bidder: &Self::AccountId) -> bool;
|
||||||
|
|||||||
@@ -1212,6 +1212,7 @@ impl slots::Config for Runtime {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = Registrar;
|
type Registrar = Registrar;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = ();
|
||||||
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1173,6 +1173,12 @@ impl paras_registrar::Config for Runtime {
|
|||||||
parameter_types! {
|
parameter_types! {
|
||||||
// 12 weeks = 3 months per lease period -> 8 lease periods ~ 2 years
|
// 12 weeks = 3 months per lease period -> 8 lease periods ~ 2 years
|
||||||
pub const LeasePeriod: BlockNumber = 12 * WEEKS;
|
pub const LeasePeriod: BlockNumber = 12 * WEEKS;
|
||||||
|
// Polkadot Genesis was on May 26, 2020.
|
||||||
|
// Target Parachain Onboarding Date: Dec 15, 2021.
|
||||||
|
// Difference is 568 days.
|
||||||
|
// We want a lease period to start on the target onboarding date.
|
||||||
|
// 568 % (12 * 7) = 64 day offset
|
||||||
|
pub const LeaseOffset: BlockNumber = 64 * DAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl slots::Config for Runtime {
|
impl slots::Config for Runtime {
|
||||||
@@ -1180,6 +1186,7 @@ impl slots::Config for Runtime {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = Registrar;
|
type Registrar = Registrar;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = LeaseOffset;
|
||||||
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1003,6 +1003,7 @@ impl slots::Config for Runtime {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = Registrar;
|
type Registrar = Registrar;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = ();
|
||||||
type WeightInfo = slots::TestWeightInfo;
|
type WeightInfo = slots::TestWeightInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -883,6 +883,7 @@ impl slots::Config for Runtime {
|
|||||||
type Currency = Balances;
|
type Currency = Balances;
|
||||||
type Registrar = Registrar;
|
type Registrar = Registrar;
|
||||||
type LeasePeriod = LeasePeriod;
|
type LeasePeriod = LeasePeriod;
|
||||||
|
type LeaseOffset = ();
|
||||||
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
type WeightInfo = weights::runtime_common_slots::WeightInfo<Runtime>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user