New slots/auctions architecture (#2294)

* TODOs

* Add auctions.rs, comment on changes needed.

* Remove cruft from slots

* Remove more from auctions.rs

* More logic drafting in slots.

* More logic in slots.rs

* patch some errors

* more fixes

* last nit

* Cleanups in slots.rs

* Cleanups in slots.rs

* patches

* make build

* crowdloan to new api

* auction compile

* Use ParaId instead of FundIndex in Crowdloan (#2303)

* use paraid instead of fundindex

* Update crowdloan.rs

* check caller is manager

* Auction tests and fix build warnings.

* Configurable origin for initiating auctions

* Remove on_finalize

* #2303 (manual merge)

* Tests for Slots

* some registrar tests

* Apply suggestions from code review

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Update runtime/common/src/slots.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* Slots uses Registrar for CurrentChains

* swap works test

* on swap impl

* traitify parachain cleanup

* explicit lifecycle tracking for paras

* initial implementation of lifecycles and upgrades

* clean up a bit

* Update runtime/common/src/slots.rs

Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>

* fix doc comment

* more rigid lifecycle checks

* include paras which are transitioning, and lifecycle query

* format guide

* update api

* update guide

* explicit outgoing state, fix genesis

* handle outgoing with transitioning paras

* Revert "explicit lifecycle tracking for paras"

This reverts commit 4177af7ba473bbd9c26bccd861793f25265b6657.

* remove lifecycle tracking from registrar

* do not include transitioning paras in identifier

* Update paras_registrar.rs

* final patches to registrar

* Fix test

* use noop in test

* clean up pending swap on deregistration

* finish registrar tests

* Update roadmap/implementers-guide/src/runtime/paras.md

* Update roadmap/implementers-guide/src/runtime/paras.md

* Update roadmap/implementers-guide/src/runtime/paras.md

* Apply suggestions from code review

* Use matches macro

* Correct terms

* Apply suggestions from code review

* Remove direct need for Slots and Registrar from Crowdloan

* Rejig things slightly

* actions queue

* Revert "actions queue"

This reverts commit b2e9011ec8937d6c73e99292416c9692aeb30f73.

* Traitify Auction interface.

* Mockups and initial code for Crowdloan testing

* One test...

* collapse onboarding state

* fix some crowdloan tests

* one more

* start benchmarks for auctions

* benchmark bid

* fix more crowdloan tests

* onboard and begin retirement no longer exist

* Revert "onboard and begin retirement no longer exist"

This reverts commit 2e100fd94e3540bff5f172328b5d917896f1c6fc.

* Simplify crowdloan and make it work.

* Fixes

* fix some

* finish merge fixes

* fix refund bug in auctions

* Add traits to Registrar for tests and benchmarks

* fix more auction benchmarks

* Fix TestAuctioneer

* finish crowdloan benchmarks

* start setting up full integration tests

* expand integration tests

* finish basic integration test

* add more integration tests

* begin slots benchmarks

* start paras registrar benchmarks

* fix merge

* fix tests

* clean up paras registrar

* remove println

* remove outdated cleanup config

* update benchmarks

* Add WeightInfo

* enable runtime-benchmarks feature flag

* complete swap benchmark

* add parachains and onboarding into westend

* add benchmarks and genesis

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=auctions --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=slots --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* fix benchmark execution

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=crowdloan --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=paras_registrar --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* Use `new_raise_len` in crowdloan on_initialize

* Update paras_registrar.rs

* fix westend merge

* impl on_swap for crowdloan

* Check fund exists before create

* update for crowdloan sig

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=crowdloan --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* slots on_initialize

* use integration tests environment for benchmarks

* fix hrmp event

* auction on_initialize

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=auctions --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* fix storage name in auctions

* add auction_index to winning data

* winning data takes into account current auction index

* remove println

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=auctions --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=slots --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* Revert "add auction_index to winning data"

* PastRandomness.

* Fixes

* Use new randomness

* fix use of randomness in auctions and runtime config

* expose consts

* fix auction test

* add deposit per byte for para registration

* basic swap integration test

* make swap test more comprehensive

* Add WinningVec for easier retrieval in the front-end.

* clean up `WinningVec` at the end

* Add event for when a new best bid comes in

* Fix propagation of winners in ending period

* fix benchmarks, refund weight in dissolve

* fix unused

* remove some TODOs

* setup opaque keys for paras in westend

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=crowdloan --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* remove unused

* cargo run --release --features=runtime-benchmarks -- benchmark --chain=westend-dev --steps=50 --repeat=20 --pallet=auctions --extrinsic=* --execution=wasm --wasm-execution=compiled --heap-pages=4096 --header=./file_header.txt --output=./runtime/westend/src/weights/

* back to regular runtime config

* use saturating math where user input can be

* better first slot check

* Update runtime/common/src/claims.rs

* update westend onswap impl

Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com>
Co-authored-by: Parity Benchmarking Bot <admin@parity.io>
This commit is contained in:
Gavin Wood
2021-03-14 22:39:41 +01:00
committed by GitHub
parent bb462683b9
commit edd81bf6a5
22 changed files with 5007 additions and 2956 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,882 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Mocking utilities for testing with real pallets.
use sp_std::sync::Arc;
use sp_io::TestExternalities;
use sp_core::{H256, crypto::KeyTypeId};
use sp_runtime::{
ModuleId,
traits::{
BlakeTwo256, IdentityLookup, One,
},
};
use sp_keystore::{KeystoreExt, testing::KeyStore};
use primitives::v1::{BlockNumber, Header, Id as ParaId, ValidationCode, HeadData};
use frame_support::{
parameter_types, assert_ok, assert_noop,
storage::StorageMap,
traits::{Currency, OnInitialize, OnFinalize, KeyOwnerProofSystem},
};
use frame_system::EnsureRoot;
use runtime_parachains::{
ParaLifecycle, Origin as ParaOrigin,
paras, configuration, shared,
};
use frame_support_test::TestRandomness;
use crate::{
auctions, crowdloan, slots, paras_registrar,
traits::{
Registrar as RegistrarT, Auctioneer,
},
};
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>;
type Block = frame_system::mocking::MockBlock<Test>;
type AccountId = u32;
type Balance = u32;
type Moment = u32;
frame_support::construct_runtime!(
pub enum Test where
Block = Block,
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
// System Stuff
System: frame_system::{Module, Call, Config, Storage, Event<T>},
Balances: pallet_balances::{Module, Call, Storage, Config<T>, Event<T>},
// Parachains Runtime
Configuration: configuration::{Module, Call, Storage, Config<T>},
Paras: paras::{Module, Origin, Call, Storage, Config<T>},
// Para Onboarding Pallets
Registrar: paras_registrar::{Module, Call, Storage, Event<T>},
Auctions: auctions::{Module, Call, Storage, Event<T>},
Crowdloan: crowdloan::{Module, Call, Storage, Event<T>},
Slots: slots::{Module, Call, Storage, Event<T>},
}
);
use crate::crowdloan::Error as CrowdloanError;
parameter_types! {
pub const BlockHashCount: u32 = 250;
pub BlockWeights: frame_system::limits::BlockWeights =
frame_system::limits::BlockWeights::simple_max(4 * 1024 * 1024);
}
impl frame_system::Config for Test {
type BaseCallFilter = ();
type BlockWeights = BlockWeights;
type BlockLength = ();
type DbWeight = ();
type Origin = Origin;
type Call = Call;
type Index = u64;
type BlockNumber = BlockNumber;
type Hash = H256;
type Hashing = BlakeTwo256;
type AccountId = AccountId;
type Lookup = IdentityLookup<AccountId>;
type Header = Header;
type Event = Event;
type BlockHashCount = BlockHashCount;
type Version = ();
type PalletInfo = PalletInfo;
type AccountData = pallet_balances::AccountData<Balance>;
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
type SS58Prefix = ();
}
parameter_types! {
pub const EpochDuration: u64 = 10;
pub const ExpectedBlockTime: Moment = 6_000;
pub const ReportLongevity: u64 = 10;
}
impl pallet_babe::Config for Test {
type EpochDuration = EpochDuration;
type ExpectedBlockTime = ExpectedBlockTime;
type EpochChangeTrigger = pallet_babe::ExternalTrigger;
type KeyOwnerProofSystem = ();
type KeyOwnerProof =
<Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(KeyTypeId, pallet_babe::AuthorityId)>>::Proof;
type KeyOwnerIdentification = <Self::KeyOwnerProofSystem as KeyOwnerProofSystem<(
KeyTypeId,
pallet_babe::AuthorityId,
)>>::IdentificationTuple;
type HandleEquivocation = ();
type WeightInfo = ();
}
parameter_types! {
pub const MinimumPeriod: Moment = 6_000 / 2;
}
impl pallet_timestamp::Config for Test {
type Moment = Moment;
type OnTimestampSet = ();
type MinimumPeriod = MinimumPeriod;
type WeightInfo = ();
}
parameter_types! {
pub static ExistentialDeposit: Balance = 1;
}
impl pallet_balances::Config for Test {
type MaxLocks = ();
type Balance = Balance;
type Event = Event;
type DustRemoval = ();
type ExistentialDeposit = ExistentialDeposit;
type AccountStore = System;
type WeightInfo = ();
}
impl configuration::Config for Test { }
impl shared::Config for Test { }
impl paras::Config for Test {
type Origin = Origin;
}
parameter_types! {
pub const ParaDeposit: Balance = 500;
pub const DataDepositPerByte: Balance = 1;
pub const MaxCodeSize: u32 = 200;
pub const MaxHeadSize: u32 = 100;
}
impl paras_registrar::Config for Test {
type Event = Event;
type OnSwap = (Crowdloan, Slots);
type ParaDeposit = ParaDeposit;
type DataDepositPerByte = DataDepositPerByte;
type MaxCodeSize = MaxCodeSize;
type MaxHeadSize = MaxHeadSize;
type Currency = Balances;
type Origin = Origin;
type WeightInfo = crate::paras_registrar::TestWeightInfo;
}
parameter_types! {
pub const EndingPeriod: BlockNumber = 10;
}
impl auctions::Config for Test {
type Event = Event;
type Leaser = Slots;
type EndingPeriod = EndingPeriod;
type Randomness = TestRandomness<Self>;
type InitiateOrigin = EnsureRoot<AccountId>;
type WeightInfo = crate::auctions::TestWeightInfo;
}
parameter_types! {
pub const LeasePeriod: BlockNumber = 100;
}
impl slots::Config for Test {
type Event = Event;
type Currency = Balances;
type Registrar = Registrar;
type LeasePeriod = LeasePeriod;
type WeightInfo = crate::slots::TestWeightInfo;
}
parameter_types! {
pub const CrowdloanId: ModuleId = ModuleId(*b"py/cfund");
pub const SubmissionDeposit: Balance = 100;
pub const MinContribution: Balance = 1;
pub const RetirementPeriod: BlockNumber = 10;
pub const RemoveKeysLimit: u32 = 100;
}
impl crowdloan::Config for Test {
type Event = Event;
type ModuleId = CrowdloanId;
type SubmissionDeposit = SubmissionDeposit;
type MinContribution = MinContribution;
type RetirementPeriod = RetirementPeriod;
type OrphanedFunds = ();
type RemoveKeysLimit = RemoveKeysLimit;
type Registrar = Registrar;
type Auctioneer = Auctions;
type WeightInfo = crate::crowdloan::TestWeightInfo;
}
/// Create a new set of test externalities.
pub fn new_test_ext() -> TestExternalities {
let t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
let keystore = KeyStore::new();
let mut ext: sp_io::TestExternalities = t.into();
ext.register_extension(KeystoreExt(Arc::new(keystore)));
ext.execute_with(|| System::set_block_number(1));
ext
}
const BLOCKS_PER_SESSION: u32 = 10;
fn maybe_new_session(n: u32) {
if n % BLOCKS_PER_SESSION == 0 {
shared::Module::<Test>::set_session_index(
shared::Module::<Test>::session_index() + 1
);
Paras::test_on_new_session();
}
}
fn test_genesis_head(size: usize) -> HeadData {
HeadData(vec![0u8; size])
}
fn test_validation_code(size: usize) -> ValidationCode {
let mut validation_code = vec![0u8; size as usize];
// Replace first bytes of code with "WASM_MAGIC" to pass validation test.
let _ = validation_code.splice(
..crate::WASM_MAGIC.len(),
crate::WASM_MAGIC.iter().cloned(),
).collect::<Vec<_>>();
ValidationCode(validation_code)
}
fn para_origin(id: u32) -> ParaOrigin {
ParaOrigin::Parachain(id.into())
}
fn run_to_block(n: u32) {
assert!(System::block_number() < n);
while System::block_number() < n {
let block_number = System::block_number();
AllModules::on_finalize(block_number);
System::on_finalize(block_number);
System::set_block_number(block_number + 1);
System::on_initialize(block_number + 1);
maybe_new_session(block_number + 1);
AllModules::on_initialize(block_number + 1);
}
}
fn run_to_session(n: u32) {
let block_number = BLOCKS_PER_SESSION * n;
run_to_block(block_number);
}
fn last_event() -> Event {
System::events().pop().expect("Event expected").event
}
#[test]
fn basic_end_to_end_works() {
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one());
// User 1 and 2 will own parachains
Balances::make_free_balance_be(&1, 1_000);
Balances::make_free_balance_be(&2, 1_000);
// First register 2 parathreads
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
assert_ok!(Registrar::register(
Origin::signed(1),
ParaId::from(1),
genesis_head.clone(),
validation_code.clone(),
));
assert_ok!(Registrar::register(
Origin::signed(2),
ParaId::from(2),
genesis_head,
validation_code,
));
// Paras should be onboarding
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Onboarding));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Onboarding));
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// 2 sessions later they are parathreads
run_to_session(2);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Para 1 will bid directly for slot 1, 2
// Open a crowdloan for Para 2 for slot 3, 4
assert_ok!(Crowdloan::create(
Origin::signed(2),
ParaId::from(2),
1_000, // Cap
lease_period_index_start + 2, // First Slot
lease_period_index_start + 3, // Last Slot
200, // Block End
None,
));
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(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);
Balances::make_free_balance_be(&20, 1_000);
// User 10 will bid directly for parachain 1
assert_ok!(Auctions::bid(
Origin::signed(10),
ParaId::from(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 crowdfund for parachain 2
Balances::make_free_balance_be(&2, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(2), ParaId::from(2), 920, None));
// Auction ends at block 110
run_to_block(109);
assert_eq!(
last_event(),
crowdloan::RawEvent::HandleBidResult(ParaId::from(2), Ok(())).into(),
);
run_to_block(110);
assert_eq!(
last_event(),
auctions::RawEvent::AuctionClosed(1).into(),
);
// Paras should have won slots
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(1)),
// -- 1 --- 2 --- 3 --------- 4 ------------ 5 --------
vec![None, None, None, Some((10, 910)), Some((10, 910))],
);
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(2)),
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------------- 6 --------------------------- 7 ----------------
vec![None, None, None, None, None, Some((crowdloan_account, 920)), Some((crowdloan_account, 920))],
);
// New leases will start on block 400
let lease_start_block = 400;
run_to_block(lease_start_block);
// First slot, Para 1 should be transitioning to Parachain
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::UpgradingParathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Two sessions later, it has upgraded
run_to_block(lease_start_block + 20);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parachain));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Second slot nothing happens :)
run_to_block(lease_start_block + 100);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parachain));
assert_eq!(Paras::lifecycle(ParaId::from(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(1)), Some(ParaLifecycle::DowngradingParachain));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::UpgradingParathread));
// Two sessions later, they have transitioned
run_to_block(lease_start_block + 220);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parachain));
// Fourth slot nothing happens :)
run_to_block(lease_start_block + 300);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parachain));
// Fifth slot, Para 2 is downgrading
run_to_block(lease_start_block + 400);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::DowngradingParachain));
// Two sessions later, Para 2 is downgraded
run_to_block(lease_start_block + 420);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
});
}
#[test]
fn basic_errors_fail() {
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one());
// Can't double register
Balances::make_free_balance_be(&1, 1_000);
Balances::make_free_balance_be(&2, 1_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
assert_ok!(Registrar::register(
Origin::signed(1),
ParaId::from(1),
genesis_head.clone(),
validation_code.clone(),
));
assert_noop!(Registrar::register(
Origin::signed(2),
ParaId::from(1),
genesis_head,
validation_code,
), paras_registrar::Error::<Test>::AlreadyRegistered);
// Start an auction
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// Cannot create a crowdloan if you do not own the para
assert_noop!(Crowdloan::create(
Origin::signed(2),
ParaId::from(1),
1_000, // Cap
lease_period_index_start + 2, // First Slot
lease_period_index_start + 3, // Last Slot
200, // Block End
None,
), crowdloan::Error::<Test>::InvalidOrigin);
});
}
#[test]
fn competing_slots() {
// This test will verify that competing slots, from different sources will resolve appropriately.
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one());
let max_bids = 10u32;
// Create n paras and owners
for n in 1 ..= max_bids {
Balances::make_free_balance_be(&n, 1_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
assert_ok!(Registrar::register(
Origin::signed(n),
ParaId::from(n),
genesis_head,
validation_code,
));
}
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
for n in 1 ..= max_bids {
// Increment block number
run_to_block(n * 10);
Balances::make_free_balance_be(&(n * 10), n * 1_000);
let (start, end) = match n {
1 => (0, 0),
2 => (0, 1),
3 => (0, 2),
4 => (0, 3),
5 => (1, 1),
6 => (1, 2),
7 => (1, 3),
8 => (2, 2),
9 => (2, 3),
10 => (3, 3),
_ => panic!("test not meant for this"),
};
// User 10 will bid directly for parachain 1
assert_ok!(Auctions::bid(
Origin::signed(n * 10),
ParaId::from(n),
1, // Auction Index
lease_period_index_start + start, // First Slot
lease_period_index_start + end, // Last slot
n * 900, // Amount
));
}
// All winner slots are filled by bids
for winner in &auctions::Winning::<Test>::get(0).unwrap() {
assert!(winner.is_some());
}
// Auction should be done
run_to_block(110);
// Appropriate Paras should have won slots
// 900 + 4500 + 2x 8100 = 21,600
// 900 + 4500 + 7200 + 9000 = 21,600
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(1)),
// -- 1 --- 2 --- 3 ---------- 4 ------
vec![None, None, None, Some((10, 900))],
);
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(5)),
// -- 1 --- 2 --- 3 --- 4 ---------- 5 -------
vec![None, None, None, None, Some((50, 4500))],
);
// TODO: Is this right?
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(9)),
// -- 1 --- 2 --- 3 --- 4 --- 5 ---------- 6 --------------- 7 -------
vec![None, None, None, None, None, Some((90, 8100)), Some((90, 8100))],
);
});
}
#[test]
fn competing_bids() {
// This test will verify that competing bids, from different sources will resolve appropriately.
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one());
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// Create 3 paras and owners
for n in 1 ..= 3 {
Balances::make_free_balance_be(&n, 1_000);
let genesis_head = Registrar::worst_head_data();
let validation_code = Registrar::worst_validation_code();
assert_ok!(Registrar::register(
Origin::signed(n),
ParaId::from(n),
genesis_head,
validation_code,
));
// Create a crowdloan for each para
assert_ok!(Crowdloan::create(
Origin::signed(n),
ParaId::from(n),
100_000, // Cap
lease_period_index_start + 2, // First Slot
lease_period_index_start + 3, // Last Slot
200, // Block End,
None,
));
}
for n in 1 ..= 9 {
// Increment block number
run_to_block(n * 10);
Balances::make_free_balance_be(&(n * 10), n * 1_000);
let para = n % 3 + 1;
if n % 2 == 0 {
// User 10 will bid directly for parachain 1
assert_ok!(Auctions::bid(
Origin::signed(n * 10),
ParaId::from(para),
1, // Auction Index
lease_period_index_start + 0, // First Slot
lease_period_index_start + 1, // Last slot
n * 900, // Amount
));
} else {
// User 20 will be a contribute to crowdfund for parachain 2
assert_ok!(Crowdloan::contribute(
Origin::signed(n * 10),
ParaId::from(para),
n + 900,
None,
));
}
}
// Auction should be done
run_to_block(110);
// Appropriate Paras should have won slots
let crowdloan_2 = Crowdloan::fund_account_id(ParaId::from(2));
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(1)),
// -- 1 --- 2 --- 3 --- 4 --- 5 ------------- 6 ------------------------ 7 -------------
vec![None, None, None, None, None, Some((crowdloan_2, 1812)), Some((crowdloan_2, 1812))],
);
assert_eq!(
slots::Leases::<Test>::get(ParaId::from(3)),
// -- 1 --- 2 --- 3 ---------- 4 --------------- 5 -------
vec![None, None, None, Some((80, 7200)), Some((80, 7200))],
);
});
}
#[test]
fn basic_swap_works() {
// This test will test a swap between a parachain and parathread works successfully.
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one()); // So events are emitted
// User 1 and 2 will own paras
Balances::make_free_balance_be(&1, 1_000);
Balances::make_free_balance_be(&2, 1_000);
// First register 2 parathreads with different data
assert_ok!(Registrar::register(
Origin::signed(1),
ParaId::from(1),
test_genesis_head(10),
test_validation_code(10),
));
assert_ok!(Registrar::register(
Origin::signed(2),
ParaId::from(2),
test_genesis_head(20),
test_validation_code(20),
));
// Paras should be onboarding
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Onboarding));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Onboarding));
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// 2 sessions later they are parathreads
run_to_session(2);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Open a crowdloan for Para 1 for slots 0-3
assert_ok!(Crowdloan::create(
Origin::signed(1),
ParaId::from(1),
1_000_000, // Cap
lease_period_index_start + 0, // First Slot
lease_period_index_start + 3, // Last Slot
200, // Block End
None,
));
// TODO: Check why this is the same for all paras
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(1));
// Bunch of contributions
let mut total = 0;
for i in 10 .. 20 {
Balances::make_free_balance_be(&i, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(1), 900 - i, None));
total += 900 - i;
}
assert!(total > 0);
assert_eq!(Balances::free_balance(&crowdloan_account), total);
// Go to end of auction where everyone won their slots
run_to_block(200);
// Deposit is appropriately taken
// ----------------------------------------- para deposit --- crowdloan
assert_eq!(Balances::reserved_balance(&1), (500 + 10 * 2 * 1) + 100);
assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
assert_eq!(Balances::reserved_balance(&crowdloan_account), total);
// Crowdloan is appropriately set
assert!(Crowdloan::funds(ParaId::from(1)).is_some());
assert!(Crowdloan::funds(ParaId::from(2)).is_none());
// New leases will start on block 400
let lease_start_block = 400;
run_to_block(lease_start_block);
// Slots are won by Para 1
assert!(!Slots::lease(ParaId::from(1)).is_empty());
assert!(Slots::lease(ParaId::from(2)).is_empty());
// 2 sessions later it is a parachain
run_to_block(lease_start_block + 20);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parachain));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Initiate a swap
assert_ok!(Registrar::swap(para_origin(1).into(), ParaId::from(2)));
assert_ok!(Registrar::swap(para_origin(2).into(), ParaId::from(1)));
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::DowngradingParachain));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::UpgradingParathread));
// 2 session later they have swapped
run_to_block(lease_start_block + 40);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parachain));
// Deregister parathread
assert_ok!(Registrar::deregister(para_origin(1).into(), ParaId::from(1)));
// Correct deposit is unreserved
assert_eq!(Balances::reserved_balance(&1), 100); // crowdloan deposit left over
assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
// Crowdloan ownership is swapped
assert!(Crowdloan::funds(ParaId::from(1)).is_none());
assert!(Crowdloan::funds(ParaId::from(2)).is_some());
// Slot is swapped
assert!(Slots::lease(ParaId::from(1)).is_empty());
assert!(!Slots::lease(ParaId::from(2)).is_empty());
// Cant dissolve
assert_noop!(Crowdloan::dissolve(Origin::signed(1), ParaId::from(1)), CrowdloanError::<Test>::InvalidParaId);
assert_noop!(Crowdloan::dissolve(Origin::signed(2), ParaId::from(2)), CrowdloanError::<Test>::NotReadyToDissolve);
// Go way in the future when the para is offboarded
run_to_block(lease_start_block + 1000);
// Withdraw of contributions works
assert_eq!(Balances::free_balance(&crowdloan_account), total);
for i in 10 .. 20 {
assert_ok!(Crowdloan::withdraw(Origin::signed(i), i, ParaId::from(2)));
}
assert_eq!(Balances::free_balance(&crowdloan_account), 0);
// Dissolve returns the balance of the person who put a deposit for crowdloan
assert_ok!(Crowdloan::dissolve(Origin::signed(2), ParaId::from(2)));
assert_eq!(Balances::reserved_balance(&1), 0);
assert_eq!(Balances::reserved_balance(&2), 500 + 20 * 2 * 1);
// Final deregister sets everything back to the start
assert_ok!(Registrar::deregister(para_origin(2).into(), ParaId::from(2)));
assert_eq!(Balances::reserved_balance(&2), 0);
})
}
#[test]
fn crowdloan_ending_period_bid() {
new_test_ext().execute_with(|| {
assert!(System::block_number().is_one()); // So events are emitted
// User 1 and 2 will own paras
Balances::make_free_balance_be(&1, 1_000);
Balances::make_free_balance_be(&2, 1_000);
// First register 2 parathreads
assert_ok!(Registrar::register(
Origin::signed(1),
ParaId::from(1),
test_genesis_head(10),
test_validation_code(10),
));
assert_ok!(Registrar::register(
Origin::signed(2),
ParaId::from(2),
test_genesis_head(20),
test_validation_code(20),
));
// Paras should be onboarding
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Onboarding));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Onboarding));
// Start a new auction in the future
let duration = 99u32;
let lease_period_index_start = 4u32;
assert_ok!(Auctions::new_auction(Origin::root(), duration, lease_period_index_start));
// 2 sessions later they are parathreads
run_to_session(2);
assert_eq!(Paras::lifecycle(ParaId::from(1)), Some(ParaLifecycle::Parathread));
assert_eq!(Paras::lifecycle(ParaId::from(2)), Some(ParaLifecycle::Parathread));
// Open a crowdloan for Para 1 for slots 0-3
assert_ok!(Crowdloan::create(
Origin::signed(1),
ParaId::from(1),
1_000_000, // Cap
lease_period_index_start + 0, // First Slot
lease_period_index_start + 3, // Last Slot
200, // Block End
None,
));
// TODO: Check why this is the same for all paras
let crowdloan_account = Crowdloan::fund_account_id(ParaId::from(1));
// Bunch of contributions
let mut total = 0;
for i in 10 .. 20 {
Balances::make_free_balance_be(&i, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(i), ParaId::from(1), 900 - i, None));
total += 900 - i;
}
assert!(total > 0);
assert_eq!(Balances::free_balance(&crowdloan_account), total);
// Bid for para 2 directly
Balances::make_free_balance_be(&2, 1_000);
assert_ok!(Auctions::bid(
Origin::signed(2),
ParaId::from(2),
1, // Auction Index
lease_period_index_start + 0, // First Slot
lease_period_index_start + 1, // Last slot
900, // Amount
));
// Go to beginning of ending period
run_to_block(100);
assert_eq!(Auctions::is_ending(100), Some(0));
assert_eq!(Auctions::winning(0), Some(
[
None, // 0-0
Some((2, ParaId::from(2), 900)), // 0-1
None, // 0-2
Some((crowdloan_account, ParaId::from(1), total)), // 0-3
None, // 1-1
None, // 1-2
None, // 1-3
None, // 2-2
None, // 2-3
None, // 3-3
]
));
run_to_block(101);
Balances::make_free_balance_be(&1234, 1_000);
assert_ok!(Crowdloan::contribute(Origin::signed(1234), ParaId::from(1), 900, None));
// Data propagates correctly
run_to_block(102);
assert_eq!(Auctions::winning(2), Some(
[
None, // 0-0
Some((2, ParaId::from(2), 900)), // 0-1
None, // 0-2
Some((crowdloan_account, ParaId::from(1), total + 900)), // 0-3
None, // 1-1
None, // 1-2
None, // 1-3
None, // 2-2
None, // 2-3
None, // 3-3
]
));
})
}
+6
View File
@@ -21,11 +21,17 @@
pub mod claims;
pub mod slot_range;
pub mod slots;
pub mod auctions;
pub mod crowdloan;
pub mod purchase;
pub mod impls;
pub mod paras_sudo_wrapper;
pub mod paras_registrar;
pub mod traits;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod integration_tests;
pub mod xcm_sender;
use primitives::v1::{BlockNumber, ValidatorId, AssignmentId};
+184
View File
@@ -0,0 +1,184 @@
// Copyright 2019-2021 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Mocking utilities for testing.
use std::{cell::RefCell, collections::HashMap};
use parity_scale_codec::{Encode, Decode};
use sp_runtime::traits::SaturatedConversion;
use frame_support::dispatch::DispatchResult;
use primitives::v1::{HeadData, ValidationCode, Id as ParaId};
use crate::traits::Registrar;
thread_local! {
static OPERATIONS: RefCell<Vec<(ParaId, u32, bool)>> = RefCell::new(Vec::new());
static PARACHAINS: RefCell<Vec<ParaId>> = RefCell::new(Vec::new());
static PARATHREADS: RefCell<Vec<ParaId>> = RefCell::new(Vec::new());
static MANAGERS: RefCell<HashMap<ParaId, Vec<u8>>> = RefCell::new(HashMap::new());
}
pub struct TestRegistrar<T>(sp_std::marker::PhantomData<T>);
impl<T: frame_system::Config> Registrar for TestRegistrar<T> {
type AccountId = T::AccountId;
fn manager_of(id: ParaId) -> Option<Self::AccountId> {
MANAGERS.with(|x| x.borrow().get(&id).and_then(|v| T::AccountId::decode(&mut &v[..]).ok()))
}
fn parachains() -> Vec<ParaId> {
PARACHAINS.with(|x| x.borrow().clone())
}
fn is_parathread(id: ParaId) -> bool {
PARATHREADS.with(|x| x.borrow().binary_search(&id).is_ok())
}
fn register(
manager: Self::AccountId,
id: ParaId,
_genesis_head: HeadData,
_validation_code: ValidationCode,
) -> DispatchResult {
// Should not be parachain.
PARACHAINS.with(|x| {
let parachains = x.borrow_mut();
match parachains.binary_search(&id) {
Ok(_) => panic!("Already Parachain"),
Err(_) => {},
}
});
// Should not be parathread, then make it.
PARATHREADS.with(|x| {
let mut parathreads = x.borrow_mut();
match parathreads.binary_search(&id) {
Ok(_) => panic!("Already Parathread"),
Err(i) => parathreads.insert(i, id),
}
});
MANAGERS.with(|x| x.borrow_mut().insert(id, manager.encode()));
Ok(())
}
fn deregister(id: ParaId) -> DispatchResult {
// Should not be parachain.
PARACHAINS.with(|x| {
let mut parachains = x.borrow_mut();
match parachains.binary_search(&id) {
Ok(i) => {
parachains.remove(i);
},
Err(_) => {},
}
});
// Remove from parathread.
PARATHREADS.with(|x| {
let mut parathreads = x.borrow_mut();
match parathreads.binary_search(&id) {
Ok(i) => {
parathreads.remove(i);
},
Err(_) => {},
}
});
MANAGERS.with(|x| x.borrow_mut().remove(&id));
Ok(())
}
fn make_parachain(id: ParaId) -> DispatchResult {
OPERATIONS.with(|x| x.borrow_mut().push((id, frame_system::Module::<T>::block_number().saturated_into(), true)));
PARATHREADS.with(|x| {
let mut parathreads = x.borrow_mut();
match parathreads.binary_search(&id) {
Ok(i) => {
parathreads.remove(i);
},
Err(_) => panic!("not parathread, so cannot `make_parachain`"),
}
});
PARACHAINS.with(|x| {
let mut parachains = x.borrow_mut();
match parachains.binary_search(&id) {
Ok(_) => {},
Err(i) => parachains.insert(i, id),
}
});
Ok(())
}
fn make_parathread(id: ParaId) -> DispatchResult {
OPERATIONS.with(|x| x.borrow_mut().push((id, frame_system::Module::<T>::block_number().saturated_into(), false)));
PARACHAINS.with(|x| {
let mut parachains = x.borrow_mut();
match parachains.binary_search(&id) {
Ok(i) => {
parachains.remove(i);
},
Err(_) => panic!("not parachain, so cannot `make_parathread`"),
}
});
PARATHREADS.with(|x| {
let mut parathreads = x.borrow_mut();
match parathreads.binary_search(&id) {
Ok(_) => {},
Err(i) => parathreads.insert(i, id),
}
});
Ok(())
}
#[cfg(test)]
fn worst_head_data() -> HeadData {
vec![0u8; 1000].into()
}
#[cfg(test)]
fn worst_validation_code() -> ValidationCode {
let mut validation_code = vec![0u8; 1000];
// Replace first bytes of code with "WASM_MAGIC" to pass validation test.
let _ = validation_code.splice(
..crate::WASM_MAGIC.len(),
crate::WASM_MAGIC.iter().cloned(),
).collect::<Vec<_>>();
validation_code.into()
}
#[cfg(test)]
fn execute_pending_transitions() {}
}
impl<T: frame_system::Config> TestRegistrar<T> {
pub fn operations() -> Vec<(ParaId, T::BlockNumber, bool)> {
OPERATIONS.with(|x| x.borrow().iter().map(|(p, b, c)| (*p, (*b).into(), *c)).collect::<Vec<_>>())
}
#[allow(dead_code)]
pub fn parachains() -> Vec<ParaId> {
PARACHAINS.with(|x| x.borrow().clone())
}
#[allow(dead_code)]
pub fn parathreads() -> Vec<ParaId> {
PARATHREADS.with(|x| x.borrow().clone())
}
#[allow(dead_code)]
pub fn clear_storage() {
OPERATIONS.with(|x| x.borrow_mut().clear());
PARACHAINS.with(|x| x.borrow_mut().clear());
PARATHREADS.with(|x| x.borrow_mut().clear());
MANAGERS.with(|x| x.borrow_mut().clear());
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+182
View File
@@ -0,0 +1,182 @@
// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
//! Traits used across pallets for Polkadot.
use sp_std::vec::*;
use primitives::v1::{HeadData, ValidationCode, Id as ParaId};
use frame_support::{
dispatch::DispatchResult,
traits::{Currency, ReservableCurrency},
};
/// Parachain registration API.
pub trait Registrar {
/// The account ID type that encodes a parachain manager ID.
type AccountId;
/// Report the manager (permissioned owner) of a parachain, if there is one.
fn manager_of(id: ParaId) -> Option<Self::AccountId>;
/// All parachains. Ordered ascending by ParaId. Parathreads are not included.
fn parachains() -> Vec<ParaId>;
/// Return if a ParaId is a Parachain.
fn is_parachain(id: ParaId) -> bool {
Self::parachains().binary_search(&id).is_ok()
}
/// Return if a ParaId is a Parathread.
fn is_parathread(id: ParaId) -> bool;
/// Return if a ParaId is registered in the system.
fn is_registered(id: ParaId) -> bool {
Self::is_parathread(id) || Self::is_parachain(id)
}
// Register a Para ID under control of `who`.
fn register(
who: Self::AccountId,
id: ParaId,
genesis_head: HeadData,
validation_code: ValidationCode,
) -> DispatchResult;
// Deregister a Para ID, free any data, and return any deposits.
fn deregister(id: ParaId) -> DispatchResult;
/// Elevate a para to parachain status.
fn make_parachain(id: ParaId) -> DispatchResult;
/// Lower a para back to normal from parachain status.
fn make_parathread(id: ParaId) -> DispatchResult;
#[cfg(any(feature = "runtime-benchmarks", test))]
fn worst_head_data() -> HeadData;
#[cfg(any(feature = "runtime-benchmarks", test))]
fn worst_validation_code() -> ValidationCode;
#[cfg(any(feature = "runtime-benchmarks", test))]
fn execute_pending_transitions();
}
/// Error type for something that went wrong with leasing.
pub enum LeaseError {
/// Unable to reserve the funds in the leaser's account.
ReserveFailed,
/// There is already a lease on at least one period for the given para.
AlreadyLeased,
/// The period to be leased has already ended.
AlreadyEnded,
}
/// Lease manager. Used by the auction module to handle parachain slot leases.
pub trait Leaser {
/// An account identifier for a leaser.
type AccountId;
/// The measurement type for counting lease periods (generally just a `BlockNumber`).
type LeasePeriod;
/// The currency type in which the lease is taken.
type Currency: ReservableCurrency<Self::AccountId>;
/// Lease a new parachain slot for `para`.
///
/// `leaser` shall have a total of `amount` balance reserved by the implementor of this trait.
///
/// Note: The implementor of the trait (the leasing system) is expected to do all reserve/unreserve calls. The
/// caller of this trait *SHOULD NOT* pre-reserve the deposit (though should ensure that it is reservable).
///
/// The lease will last from `period_begin` for `period_count` lease periods. It is undefined if the `para`
/// already has a slot leased during those periods.
///
/// Returns `Err` in the case of an error, and in which case nothing is changed.
fn lease_out(
para: ParaId,
leaser: &Self::AccountId,
amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
period_begin: Self::LeasePeriod,
period_count: Self::LeasePeriod,
) -> Result<(), LeaseError>;
/// Return the amount of balance currently held in reserve on `leaser`'s account for leasing `para`. This won't
/// go down outside of a lease period.
fn deposit_held(para: ParaId, leaser: &Self::AccountId) -> <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.
fn lease_period() -> Self::LeasePeriod;
/// Returns the current lease period.
fn lease_period_index() -> Self::LeasePeriod;
}
pub trait Auctioneer {
/// An account identifier for a leaser.
type AccountId;
/// The measurement type for counting blocks.
type BlockNumber;
/// The measurement type for counting lease periods (generally the same as `BlockNumber`).
type LeasePeriod;
/// The currency type in which the lease is taken.
type Currency: ReservableCurrency<Self::AccountId>;
/// Create a new auction.
///
/// 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
/// are to be auctioned.
fn new_auction(duration: Self::BlockNumber, lease_period_index: Self::LeasePeriod) -> DispatchResult;
/// Returns `Some(n)` if the `now` block is part of the ending period of an auction, where `n`
/// represents how far into the ending period this block is. Otherwise, returns `None`.
fn is_ending(now: Self::BlockNumber) -> Option<Self::BlockNumber>;
/// Place a bid in the current auction.
///
/// - `bidder`: The account that will be funding this bid.
/// - `para`: The para to bid for.
/// - `first_slot`: The first lease period index of the range to be bid on.
/// - `last_slot`: The last lease period index of the range to be bid on (inclusive).
/// - `amount`: The total amount to be the bid for deposit over the range.
///
/// The account `Bidder` must have at least `amount` available as a free balance in `Currency`. The
/// implementation *MUST* remove or reserve `amount` funds from `bidder` and those funds should be returned
/// or freed once the bid is rejected or lease has ended.
fn place_bid(
bidder: Self::AccountId,
para: ParaId,
first_slot: Self::LeasePeriod,
last_slot: Self::LeasePeriod,
amount: <Self::Currency as Currency<Self::AccountId>>::Balance,
) -> DispatchResult;
/// Returns the current lease period.
fn lease_period_index() -> Self::LeasePeriod;
}
/// Runtime hook for when we swap a parachain and parathread.
#[impl_trait_for_tuples::impl_for_tuples(30)]
pub trait OnSwap {
/// Updates any needed state/references to enact a logical swap of two parachains. Identity,
/// code and `head_data` remain equivalent for all parachains/threads, however other properties
/// such as leases, deposits held and thread/chain nature are swapped.
fn on_swap(one: ParaId, other: ParaId);
}