referrerpolicy=no-referrer-when-downgrade

pallet_broker/
lib.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18#![cfg_attr(not(feature = "std"), no_std)]
19#![doc = include_str!("../README.md")]
20
21pub use pallet::*;
22
23mod adapt_price;
24mod benchmarking;
25mod coretime_interface;
26mod dispatchable_impls;
27
28#[cfg(test)]
29mod mock;
30mod nonfungible_impl;
31#[cfg(test)]
32mod test_fungibles;
33#[cfg(test)]
34mod tests;
35mod tick_impls;
36mod types;
37mod utility_impls;
38
39pub mod migration;
40pub mod runtime_api;
41
42pub mod weights;
43pub use weights::WeightInfo;
44
45pub use adapt_price::*;
46pub use coretime_interface::*;
47pub use fp_coretime::{
48	market,
49	revenue::{OnDemandRevenue, RevenueClaim},
50	CoreIndex, CoreMask, PartsOf57600, PotentialRenewalId, RegionId, TaskId, Timeslice,
51	CORE_MASK_BITS,
52};
53pub use types::*;
54
55extern crate alloc;
56
57/// The log target for this pallet.
58const LOG_TARGET: &str = "runtime::broker";
59
60#[frame_support::pallet]
61pub mod pallet {
62	use super::*;
63	use alloc::vec::Vec;
64	use frame_support::{
65		pallet_prelude::{DispatchResult, DispatchResultWithPostInfo, *},
66		traits::{
67			fungible::{Balanced, Credit, Mutate},
68			BuildGenesisConfig, EnsureOrigin, OnUnbalanced,
69		},
70		PalletId,
71	};
72	use frame_system::pallet_prelude::*;
73	use sp_runtime::traits::{Convert, ConvertBack, MaybeConvert};
74
75	const STORAGE_VERSION: StorageVersion = StorageVersion::new(5);
76
77	#[pallet::pallet]
78	#[pallet::storage_version(STORAGE_VERSION)]
79	pub struct Pallet<T>(_);
80
81	#[pallet::config]
82	pub trait Config: frame_system::Config {
83		#[allow(deprecated)]
84		type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
85
86		/// Weight information for all calls of this pallet.
87		type WeightInfo: WeightInfo;
88
89		/// Currency used to pay for Coretime.
90		type Currency: Mutate<Self::AccountId> + Balanced<Self::AccountId>;
91
92		/// The origin test needed for administrating this pallet.
93		type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
94
95		/// What to do with any revenues collected from the sale of Coretime.
96		type OnRevenue: OnUnbalanced<Credit<Self::AccountId, Self::Currency>>;
97
98		/// Relay chain's Coretime API used to interact with and instruct the low-level scheduling
99		/// system.
100		type Coretime: CoretimeInterface;
101
102		/// The on-demand Coretime market whose revenue is distributed among the contributors to
103		/// the Instantaneous Coretime Pool. Expected to live on the same chain as this pallet, so
104		/// the revenue can be claimed directly.
105		///
106		/// Once it is enabled, the revenue of each timeslice is claimed from it once that
107		/// timeslice has ended, along with that of any earlier timeslice which could not be paid
108		/// out before, and revenue reports from the Relay chain are ignored. Until then, the
109		/// revenue is requested from the Relay chain through [`Config::Coretime`], which reports
110		/// it back with `notify_revenue`.
111		type OnDemandRevenue: OnDemandRevenue<BalanceOf<Self>, Self::AccountId>;
112
113		/// The algorithm to determine the next price on the basis of market performance.
114		type PriceAdapter: AdaptPrice<BalanceOf<Self>>;
115
116		/// Reversible conversion from local balance to Relay-chain balance. This will typically be
117		/// the `Identity`, but provided just in case the chains use different representations.
118		type ConvertBalance: Convert<BalanceOf<Self>, RelayBalanceOf<Self>>
119			+ ConvertBack<BalanceOf<Self>, RelayBalanceOf<Self>>;
120
121		/// Type used for getting the associated account of a task. This account is controlled by
122		/// the task itself.
123		type SovereignAccountOf: MaybeConvert<TaskId, Self::AccountId>;
124
125		/// Identifier from which the internal Pot is generated.
126		#[pallet::constant]
127		type PalletId: Get<PalletId>;
128
129		/// Number of Relay-chain blocks per timeslice.
130		#[pallet::constant]
131		type TimeslicePeriod: Get<RelayBlockNumberOf<Self>>;
132
133		/// Maximum number of legacy leases.
134		#[pallet::constant]
135		type MaxLeasedCores: Get<u32>;
136
137		/// Maximum number of system cores.
138		#[pallet::constant]
139		type MaxReservedCores: Get<u32>;
140
141		/// Given that we are performing all auto-renewals in a single block, it has to be limited.
142		#[pallet::constant]
143		type MaxAutoRenewals: Get<u32>;
144
145		/// The smallest amount of credits a user can purchase.
146		///
147		/// Needed to prevent spam attacks.
148		#[pallet::constant]
149		type MinimumCreditPurchase: Get<BalanceOf<Self>>;
150	}
151
152	/// The current configuration of this pallet.
153	#[pallet::storage]
154	pub type Configuration<T> = StorageValue<_, ConfigRecordOf<T>, OptionQuery>;
155
156	/// The Polkadot Core reservations (generally tasked with the maintenance of System Chains).
157	#[pallet::storage]
158	pub type Reservations<T> = StorageValue<_, ReservationsRecordOf<T>, ValueQuery>;
159
160	/// Force reservations that need to be inserted into the workplan at the next sale rotation.
161	///
162	/// They are automatically freed at the next sale rotation.
163	#[pallet::storage]
164	pub type ForceReservations<T> = StorageValue<_, ReservationsRecordOf<T>, ValueQuery>;
165
166	/// The Polkadot Core legacy leases.
167	#[pallet::storage]
168	pub type Leases<T> = StorageValue<_, LeasesRecordOf<T>, ValueQuery>;
169
170	/// The current status of miscellaneous subsystems of this pallet.
171	#[pallet::storage]
172	pub type Status<T> = StorageValue<_, StatusRecord, OptionQuery>;
173
174	/// The details of the current sale, including its properties and status.
175	#[pallet::storage]
176	pub type SaleInfo<T> = StorageValue<_, SaleInfoRecordOf<T>, OptionQuery>;
177
178	/// Records of potential renewals.
179	///
180	/// Renewals will only actually be allowed if `CompletionStatus` is actually `Complete`.
181	#[pallet::storage]
182	pub type PotentialRenewals<T> =
183		StorageMap<_, Twox64Concat, PotentialRenewalId, PotentialRenewalRecordOf<T>, OptionQuery>;
184
185	/// The current (unassigned or provisionally assigend) Regions.
186	#[pallet::storage]
187	pub type Regions<T> = StorageMap<_, Blake2_128Concat, RegionId, RegionRecordOf<T>, OptionQuery>;
188
189	/// The work we plan on having each core do at a particular time in the future.
190	#[pallet::storage]
191	pub type Workplan<T> =
192		StorageMap<_, Twox64Concat, (Timeslice, CoreIndex), Schedule, OptionQuery>;
193
194	/// The current workload of each core. This gets updated with workplan as timeslices pass.
195	#[pallet::storage]
196	pub type Workload<T> = StorageMap<_, Twox64Concat, CoreIndex, Schedule, ValueQuery>;
197
198	/// Record of a single contribution to the Instantaneous Coretime Pool.
199	#[pallet::storage]
200	pub type InstaPoolContribution<T> =
201		StorageMap<_, Blake2_128Concat, RegionId, ContributionRecordOf<T>, OptionQuery>;
202
203	/// Record of Coretime entering or leaving the Instantaneous Coretime Pool.
204	#[pallet::storage]
205	pub type InstaPoolIo<T> = StorageMap<_, Blake2_128Concat, Timeslice, PoolIoRecord, ValueQuery>;
206
207	/// Total InstaPool rewards for each Timeslice and the number of core parts which contributed.
208	#[pallet::storage]
209	pub type InstaPoolHistory<T> =
210		StorageMap<_, Blake2_128Concat, Timeslice, InstaPoolHistoryRecordOf<T>>;
211
212	/// Received core count change from the relay chain.
213	#[pallet::storage]
214	pub type CoreCountInbox<T> = StorageValue<_, CoreIndex, OptionQuery>;
215
216	/// Keeping track of cores which have auto-renewal enabled.
217	///
218	/// Sorted by `CoreIndex` to make the removal of cores from auto-renewal more efficient.
219	#[pallet::storage]
220	pub type AutoRenewals<T: Config> =
221		StorageValue<_, BoundedVec<AutoRenewalRecord, T::MaxAutoRenewals>, ValueQuery>;
222
223	/// Received revenue info from the relay chain.
224	#[pallet::storage]
225	pub type RevenueInbox<T> = StorageValue<_, OnDemandRevenueRecordOf<T>, OptionQuery>;
226
227	#[pallet::event]
228	#[pallet::generate_deposit(pub(super) fn deposit_event)]
229	pub enum Event<T: Config> {
230		/// A Region of Bulk Coretime has been purchased.
231		Purchased {
232			/// The identity of the purchaser.
233			who: T::AccountId,
234			/// The identity of the Region.
235			region_id: RegionId,
236			/// The price paid for this Region.
237			price: BalanceOf<T>,
238			/// The duration of the Region.
239			duration: Timeslice,
240		},
241		/// The workload of a core has become renewable.
242		Renewable {
243			/// The core whose workload can be renewed.
244			core: CoreIndex,
245			/// The price at which the workload can be renewed.
246			price: BalanceOf<T>,
247			/// The time at which the workload would recommence of this renewal. The call to renew
248			/// cannot happen before the beginning of the interlude prior to the sale for regions
249			/// which begin at this time.
250			begin: Timeslice,
251			/// The actual workload which can be renewed.
252			workload: Schedule,
253		},
254		/// A workload has been renewed.
255		Renewed {
256			/// The identity of the renewer.
257			who: T::AccountId,
258			/// The price paid for this renewal.
259			price: BalanceOf<T>,
260			/// The index of the core on which the `workload` was previously scheduled.
261			old_core: CoreIndex,
262			/// The index of the core on which the renewed `workload` has been scheduled.
263			core: CoreIndex,
264			/// The time at which the `workload` will begin on the `core`.
265			begin: Timeslice,
266			/// The number of timeslices for which this `workload` is newly scheduled.
267			duration: Timeslice,
268			/// The workload which was renewed.
269			workload: Schedule,
270		},
271		/// Ownership of a Region has been transferred.
272		Transferred {
273			/// The Region which has been transferred.
274			region_id: RegionId,
275			/// The duration of the Region.
276			duration: Timeslice,
277			/// The old owner of the Region.
278			old_owner: Option<T::AccountId>,
279			/// The new owner of the Region.
280			owner: Option<T::AccountId>,
281		},
282		/// A Region has been split into two non-overlapping Regions.
283		Partitioned {
284			/// The Region which was split.
285			old_region_id: RegionId,
286			/// The new Regions into which it became.
287			new_region_ids: (RegionId, RegionId),
288		},
289		/// A Region has been converted into two overlapping Regions each of lesser regularity.
290		Interlaced {
291			/// The Region which was interlaced.
292			old_region_id: RegionId,
293			/// The new Regions into which it became.
294			new_region_ids: (RegionId, RegionId),
295		},
296		/// A Region has been assigned to a particular task.
297		Assigned {
298			/// The Region which was assigned.
299			region_id: RegionId,
300			/// The duration of the assignment.
301			duration: Timeslice,
302			/// The task to which the Region was assigned.
303			task: TaskId,
304		},
305		/// An assignment has been removed from the workplan.
306		AssignmentRemoved {
307			/// The Region which was removed from the workplan.
308			region_id: RegionId,
309		},
310		/// A Region has been added to the Instantaneous Coretime Pool.
311		Pooled {
312			/// The Region which was added to the Instantaneous Coretime Pool.
313			region_id: RegionId,
314			/// The duration of the Region.
315			duration: Timeslice,
316		},
317		/// A new number of cores has been requested.
318		CoreCountRequested {
319			/// The number of cores requested.
320			core_count: CoreIndex,
321		},
322		/// The number of cores available for scheduling has changed.
323		CoreCountChanged {
324			/// The new number of cores available for scheduling.
325			core_count: CoreIndex,
326		},
327		/// There is a new reservation for a workload.
328		ReservationMade {
329			/// The index of the reservation.
330			index: u32,
331			/// The workload of the reservation.
332			workload: Schedule,
333		},
334		/// A reservation for a workload has been cancelled.
335		ReservationCancelled {
336			/// The index of the reservation which was cancelled.
337			index: u32,
338			/// The workload of the now cancelled reservation.
339			workload: Schedule,
340		},
341		/// A new sale has been initialized.
342		SaleInitialized {
343			/// The relay block number at which the sale will/did start.
344			sale_start: RelayBlockNumberOf<T>,
345			/// The length in relay chain blocks of the Leadin Period (where the price is
346			/// decreasing).
347			leadin_length: RelayBlockNumberOf<T>,
348			/// The price of Bulk Coretime at the beginning of the Leadin Period.
349			start_price: BalanceOf<T>,
350			/// The price of Bulk Coretime after the Leadin Period.
351			end_price: BalanceOf<T>,
352			/// The first timeslice of the Regions which are being sold in this sale.
353			region_begin: Timeslice,
354			/// The timeslice on which the Regions which are being sold in the sale terminate.
355			/// (i.e. One after the last timeslice which the Regions control.)
356			region_end: Timeslice,
357			/// The number of cores we want to sell, ideally.
358			ideal_cores_sold: CoreIndex,
359			/// Number of cores which are/have been offered for sale.
360			cores_offered: CoreIndex,
361			/// Sequential identifier for the current sale period.
362			sale_index: SaleIndex,
363		},
364		/// A new lease has been created.
365		Leased {
366			/// The task to which a core will be assigned.
367			task: TaskId,
368			/// The timeslice contained in the sale period after which this lease will
369			/// self-terminate (and therefore the earliest timeslice at which the lease may no
370			/// longer apply).
371			until: Timeslice,
372		},
373		/// A lease has been removed.
374		LeaseRemoved {
375			/// The task to which a core was assigned.
376			task: TaskId,
377		},
378		/// A lease is about to end.
379		LeaseEnding {
380			/// The task to which a core was assigned.
381			task: TaskId,
382			/// The timeslice at which the task will no longer be scheduled.
383			when: Timeslice,
384		},
385		/// The sale rotation has been started and a new sale is imminent.
386		SalesStarted {
387			/// The nominal price of an Region of Bulk Coretime.
388			price: BalanceOf<T>,
389			/// The maximum number of cores which this pallet will attempt to assign.
390			core_count: CoreIndex,
391		},
392		/// The act of claiming revenue has begun.
393		RevenueClaimBegun {
394			/// The region to be claimed for.
395			region: RegionId,
396			/// The maximum number of timeslices which should be searched for claimed.
397			max_timeslices: Timeslice,
398		},
399		/// A particular timeslice has a non-zero claim.
400		RevenueClaimItem {
401			/// The timeslice whose claim is being processed.
402			when: Timeslice,
403			/// The amount which was claimed at this timeslice.
404			amount: BalanceOf<T>,
405		},
406		/// A revenue claim has (possibly only in part) been paid.
407		RevenueClaimPaid {
408			/// The account to whom revenue has been paid.
409			who: T::AccountId,
410			/// The total amount of revenue claimed and paid.
411			amount: BalanceOf<T>,
412			/// The next region which should be claimed for the continuation of this contribution.
413			next: Option<RegionId>,
414		},
415		/// Some Instantaneous Coretime Pool credit has been purchased.
416		CreditPurchased {
417			/// The account which purchased the credit.
418			who: T::AccountId,
419			/// The Relay-chain account to which the credit will be made.
420			beneficiary: RelayAccountIdOf<T>,
421			/// The amount of credit purchased.
422			amount: BalanceOf<T>,
423		},
424		/// A Region has been dropped due to being out of date.
425		RegionDropped {
426			/// The Region which no longer exists.
427			region_id: RegionId,
428			/// The duration of the Region.
429			duration: Timeslice,
430		},
431		/// Some historical Instantaneous Core Pool contribution record has been dropped.
432		ContributionDropped {
433			/// The Region whose contribution is no longer exists.
434			region_id: RegionId,
435		},
436		/// A region has been force-removed from the pool. This is usually due to a provisionally
437		/// pooled region being redeployed.
438		RegionUnpooled {
439			/// The Region which has been force-removed from the pool.
440			region_id: RegionId,
441			/// The timeslice at which the region was force-removed.
442			when: Timeslice,
443		},
444		/// Some historical Instantaneous Core Pool payment record has been initialized.
445		HistoryInitialized {
446			/// The timeslice whose history has been initialized.
447			when: Timeslice,
448			/// The amount of privately contributed Coretime to the Instantaneous Coretime Pool.
449			private_pool_size: CoreMaskBitCount,
450			/// The amount of Coretime contributed to the Instantaneous Coretime Pool by the
451			/// Polkadot System.
452			system_pool_size: CoreMaskBitCount,
453		},
454		/// Some historical Instantaneous Core Pool payment record has been dropped.
455		HistoryDropped {
456			/// The timeslice whose history is no longer available.
457			when: Timeslice,
458			/// The amount of revenue the system has taken.
459			revenue: BalanceOf<T>,
460		},
461		/// Some historical Instantaneous Core Pool payment record has been ignored because the
462		/// timeslice was already known. Governance may need to intervene.
463		HistoryIgnored {
464			/// The timeslice whose history is was ignored.
465			when: Timeslice,
466			/// The amount of revenue which was ignored.
467			revenue: BalanceOf<T>,
468		},
469		/// Some historical Instantaneous Core Pool Revenue is ready for payout claims.
470		ClaimsReady {
471			/// The timeslice whose history is available.
472			when: Timeslice,
473			/// The amount of revenue the Polkadot System has already taken.
474			system_payout: BalanceOf<T>,
475			/// The total amount of revenue remaining to be claimed.
476			private_payout: BalanceOf<T>,
477		},
478		/// A Core has been assigned to one or more tasks and/or the Pool on the Relay-chain.
479		CoreAssigned {
480			/// The index of the Core which has been assigned.
481			core: CoreIndex,
482			/// The Relay-chain block at which this assignment should take effect.
483			when: RelayBlockNumberOf<T>,
484			/// The workload to be done on the Core.
485			assignment: Vec<(CoreAssignment, PartsOf57600)>,
486		},
487		/// Some historical Instantaneous Core Pool payment record has been dropped.
488		PotentialRenewalDropped {
489			/// The timeslice whose renewal is no longer available.
490			when: Timeslice,
491			/// The core whose workload is no longer available to be renewed for `when`.
492			core: CoreIndex,
493		},
494		AutoRenewalEnabled {
495			/// The core for which the renewal was enabled.
496			core: CoreIndex,
497			/// The task for which the renewal was enabled.
498			task: TaskId,
499		},
500		AutoRenewalDisabled {
501			/// The core for which the renewal was disabled.
502			core: CoreIndex,
503			/// The task for which the renewal was disabled.
504			task: TaskId,
505		},
506		/// Failed to auto-renew a core, likely due to the payer account not being sufficiently
507		/// funded or the workload on the core no longer belonging to the paying task.
508		AutoRenewalFailed {
509			/// The core for which the renewal failed.
510			core: CoreIndex,
511			/// The account which was supposed to pay for renewal.
512			///
513			/// If `None` it indicates that we failed to get the sovereign account of a task.
514			payer: Option<T::AccountId>,
515		},
516		/// The auto-renewal limit has been reached upon renewing cores.
517		///
518		/// This should never happen, given that enable_auto_renew checks for this before enabling
519		/// auto-renewal.
520		AutoRenewalLimitReached,
521		/// Failed to assign a force reservation due to no free cores available.
522		ForceReservationFailed {
523			/// The schedule that could not be assigned.
524			schedule: Schedule,
525		},
526		/// Potential renewal was forcefully removed.
527		PotentialRenewalRemoved {
528			/// The core associated with the potential renewal that was removed.
529			core: CoreIndex,
530			/// The timeslice associated with the potential renewal that was removed.
531			timeslice: Timeslice,
532		},
533	}
534
535	#[pallet::error]
536	#[derive(PartialEq)]
537	pub enum Error<T> {
538		/// The given region identity is not known.
539		UnknownRegion,
540		/// The owner of the region is not the origin.
541		NotOwner,
542		/// The pivot point of the partition at or after the end of the region.
543		PivotTooLate,
544		/// The pivot point of the partition at the beginning of the region.
545		PivotTooEarly,
546		/// The pivot mask for the interlacing is not contained within the region's interlace mask.
547		ExteriorPivot,
548		/// The pivot mask for the interlacing is void (and therefore unschedulable).
549		VoidPivot,
550		/// The pivot mask for the interlacing is complete (and therefore not a strict subset).
551		CompletePivot,
552		/// The workplan of the pallet's state is invalid. This indicates a state corruption.
553		CorruptWorkplan,
554		/// There is no sale happening currently.
555		NoSales,
556		/// The price limit is exceeded.
557		Overpriced,
558		/// There are no cores available.
559		Unavailable,
560		/// The sale limit has been reached.
561		SoldOut,
562		/// The renewal operation is not valid at the current time (it may become valid in the next
563		/// sale).
564		WrongTime,
565		/// Invalid attempt to renew.
566		NotAllowed,
567		/// This pallet has not yet been initialized.
568		Uninitialized,
569		/// The purchase cannot happen yet as the sale period is yet to begin.
570		TooEarly,
571		/// There is no work to be done.
572		NothingToDo,
573		/// The maximum amount of reservations has already been reached.
574		TooManyReservations,
575		/// The maximum amount of leases has already been reached.
576		TooManyLeases,
577		/// The lease does not exist.
578		LeaseNotFound,
579		/// The revenue for the Instantaneous Core Sales of this period is not (yet) known and thus
580		/// this operation cannot proceed.
581		UnknownRevenue,
582		/// The identified contribution to the Instantaneous Core Pool is unknown.
583		UnknownContribution,
584		/// The workload assigned for renewal is incomplete. This is unexpected and indicates a
585		/// logic error.
586		IncompleteAssignment,
587		/// An item cannot be dropped because it is still valid.
588		StillValid,
589		/// The history item does not exist.
590		NoHistory,
591		/// No reservation of the given index exists.
592		UnknownReservation,
593		/// The renewal record cannot be found.
594		UnknownRenewal,
595		/// The lease expiry time has already passed.
596		AlreadyExpired,
597		/// The configuration could not be applied because it is invalid.
598		InvalidConfig,
599		/// The revenue must be claimed for 1 or more timeslices.
600		NoClaimTimeslices,
601		/// The caller doesn't have the permission to enable or disable auto-renewal.
602		NoPermission,
603		/// We reached the limit for auto-renewals.
604		TooManyAutoRenewals,
605		/// Only cores which are assigned to a task can be auto-renewed.
606		NonTaskAutoRenewal,
607		/// Failed to get the sovereign account of a task.
608		SovereignAccountNotFound,
609		/// Attempted to disable auto-renewal for a core that didn't have it enabled.
610		AutoRenewalNotEnabled,
611		/// Attempted to force remove an assignment that doesn't exist.
612		AssignmentNotFound,
613		/// Needed to prevent spam attacks.The amount of credits the user attempted to purchase is
614		/// below `T::MinimumCreditPurchase`.
615		CreditPurchaseTooSmall,
616		/// The renewable workload of the core does not include the given task.
617		TaskNotInWorkload,
618		/// Attempted to enable auto-renewal for a core that already has it enabled.
619		AutoRenewalAlreadyEnabled,
620	}
621
622	#[derive(frame_support::DefaultNoBound)]
623	#[pallet::genesis_config]
624	pub struct GenesisConfig<T: Config> {
625		#[serde(skip)]
626		pub _config: core::marker::PhantomData<T>,
627	}
628
629	#[pallet::genesis_build]
630	impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
631		fn build(&self) {
632			frame_system::Pallet::<T>::inc_providers(&Pallet::<T>::account_id());
633		}
634	}
635
636	#[pallet::hooks]
637	impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
638		fn on_initialize(_now: BlockNumberFor<T>) -> Weight {
639			Self::do_tick()
640		}
641	}
642
643	#[pallet::call(weight(<T as Config>::WeightInfo))]
644	impl<T: Config> Pallet<T> {
645		/// Configure the pallet.
646		///
647		/// - `origin`: Must be Root or pass `AdminOrigin`.
648		/// - `config`: The configuration for this pallet.
649		#[pallet::call_index(0)]
650		pub fn configure(
651			origin: OriginFor<T>,
652			config: ConfigRecordOf<T>,
653		) -> DispatchResultWithPostInfo {
654			T::AdminOrigin::ensure_origin_or_root(origin)?;
655			Self::do_configure(config)?;
656			Ok(Pays::No.into())
657		}
658
659		/// Reserve a core for a workload.
660		///
661		/// The workload will be given a reservation, but two sale period boundaries must pass
662		/// before the core is actually assigned.
663		///
664		/// - `origin`: Must be Root or pass `AdminOrigin`.
665		/// - `workload`: The workload which should be permanently placed on a core.
666		#[pallet::call_index(1)]
667		pub fn reserve(origin: OriginFor<T>, workload: Schedule) -> DispatchResultWithPostInfo {
668			T::AdminOrigin::ensure_origin_or_root(origin)?;
669			Self::do_reserve(workload)?;
670			Ok(Pays::No.into())
671		}
672
673		/// Cancel a reservation for a workload.
674		///
675		/// - `origin`: Must be Root or pass `AdminOrigin`.
676		/// - `item_index`: The index of the reservation. Usually this will also be the index of the
677		///   core on which the reservation has been scheduled. However, it is possible that if
678		///   other cores are reserved or unreserved in the same sale rotation that they won't
679		///   correspond, so it's better to look up the core properly in the `Reservations` storage.
680		#[pallet::call_index(2)]
681		pub fn unreserve(origin: OriginFor<T>, item_index: u32) -> DispatchResultWithPostInfo {
682			T::AdminOrigin::ensure_origin_or_root(origin)?;
683			Self::do_unreserve(item_index)?;
684			Ok(Pays::No.into())
685		}
686
687		/// Reserve a core for a single task workload for a limited period.
688		///
689		/// In the interlude and sale period where Bulk Coretime is sold for the period immediately
690		/// after `until`, then the same workload may be renewed.
691		///
692		/// - `origin`: Must be Root or pass `AdminOrigin`.
693		/// - `task`: The workload which should be placed on a core.
694		/// - `until`: The timeslice now earlier than which `task` should be placed as a workload on
695		///   a core.
696		#[pallet::call_index(3)]
697		pub fn set_lease(
698			origin: OriginFor<T>,
699			task: TaskId,
700			until: Timeslice,
701		) -> DispatchResultWithPostInfo {
702			T::AdminOrigin::ensure_origin_or_root(origin)?;
703			Self::do_set_lease(task, until)?;
704			Ok(Pays::No.into())
705		}
706
707		/// Begin the Bulk Coretime sales rotation.
708		///
709		/// - `origin`: Must be Root or pass `AdminOrigin`.
710		/// - `end_price`: The price after the leadin period of Bulk Coretime in the first sale.
711		/// - `extra_cores`: Number of extra cores that should be requested on top of the cores
712		///   required for `Reservations` and `Leases`.
713		///
714		/// This will call [`Self::request_core_count`] internally to set the correct core count on
715		/// the relay chain.
716		#[pallet::call_index(4)]
717		#[pallet::weight(T::WeightInfo::start_sales(
718			T::MaxLeasedCores::get() + T::MaxReservedCores::get() + *extra_cores as u32
719		))]
720		pub fn start_sales(
721			origin: OriginFor<T>,
722			end_price: BalanceOf<T>,
723			extra_cores: CoreIndex,
724		) -> DispatchResultWithPostInfo {
725			T::AdminOrigin::ensure_origin_or_root(origin)?;
726			Self::do_start_sales(end_price, extra_cores)?;
727			Ok(Pays::No.into())
728		}
729
730		/// Purchase Bulk Coretime in the ongoing Sale.
731		///
732		/// - `origin`: Must be a Signed origin with at least enough funds to pay the current price
733		///   of Bulk Coretime.
734		/// - `price_limit`: An amount no more than which should be paid.
735		#[pallet::call_index(5)]
736		pub fn purchase(
737			origin: OriginFor<T>,
738			price_limit: BalanceOf<T>,
739		) -> DispatchResultWithPostInfo {
740			let who = ensure_signed(origin)?;
741			Self::do_purchase(who, price_limit)?;
742			Ok(Pays::No.into())
743		}
744
745		/// Renew Bulk Coretime in the ongoing Sale or its prior Interlude Period.
746		///
747		/// - `origin`: Must be a Signed origin with at least enough funds to pay the renewal price
748		///   of the core.
749		/// - `core`: The core which should be renewed.
750		#[pallet::call_index(6)]
751		pub fn renew(origin: OriginFor<T>, core: CoreIndex) -> DispatchResultWithPostInfo {
752			let who = ensure_signed(origin)?;
753			Self::do_renew(who, core)?;
754			Ok(Pays::No.into())
755		}
756
757		/// Transfer a Bulk Coretime Region to a new owner.
758		///
759		/// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`.
760		/// - `region_id`: The Region whose ownership should change.
761		/// - `new_owner`: The new owner for the Region.
762		#[pallet::call_index(7)]
763		pub fn transfer(
764			origin: OriginFor<T>,
765			region_id: RegionId,
766			new_owner: T::AccountId,
767		) -> DispatchResult {
768			let who = ensure_signed(origin)?;
769			Self::do_transfer(region_id, Some(who), new_owner)?;
770			Ok(())
771		}
772
773		/// Split a Bulk Coretime Region into two non-overlapping Regions at a particular time into
774		/// the region.
775		///
776		/// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`.
777		/// - `region_id`: The Region which should be partitioned into two non-overlapping Regions.
778		/// - `pivot`: The offset in time into the Region at which to make the split.
779		#[pallet::call_index(8)]
780		pub fn partition(
781			origin: OriginFor<T>,
782			region_id: RegionId,
783			pivot: Timeslice,
784		) -> DispatchResult {
785			let who = ensure_signed(origin)?;
786			Self::do_partition(region_id, Some(who), pivot)?;
787			Ok(())
788		}
789
790		/// Split a Bulk Coretime Region into two wholly-overlapping Regions with complementary
791		/// interlace masks which together make up the original Region's interlace mask.
792		///
793		/// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`.
794		/// - `region_id`: The Region which should become two interlaced Regions of incomplete
795		///   regularity.
796		/// - `pivot`: The interlace mask of one of the two new regions (the other is its partial
797		///   complement).
798		#[pallet::call_index(9)]
799		pub fn interlace(
800			origin: OriginFor<T>,
801			region_id: RegionId,
802			pivot: CoreMask,
803		) -> DispatchResult {
804			let who = ensure_signed(origin)?;
805			Self::do_interlace(region_id, Some(who), pivot)?;
806			Ok(())
807		}
808
809		/// Assign a Bulk Coretime Region to a task.
810		///
811		/// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`.
812		/// - `region_id`: The Region which should be assigned to the task.
813		/// - `task`: The task to assign.
814		/// - `finality`: Indication of whether this assignment is final (in which case it may be
815		///   eligible for renewal) or provisional (in which case it may be manipulated and/or
816		/// reassigned at a later stage).
817		#[pallet::call_index(10)]
818		pub fn assign(
819			origin: OriginFor<T>,
820			region_id: RegionId,
821			task: TaskId,
822			finality: Finality,
823		) -> DispatchResultWithPostInfo {
824			let who = ensure_signed(origin)?;
825			Self::do_assign(region_id, Some(who), task, finality)?;
826			Ok(if finality == Finality::Final { Pays::No } else { Pays::Yes }.into())
827		}
828
829		/// Place a Bulk Coretime Region into the Instantaneous Coretime Pool.
830		///
831		/// - `origin`: Must be a Signed origin of the account which owns the Region `region_id`.
832		/// - `region_id`: The Region which should be assigned to the Pool.
833		/// - `payee`: The account which is able to collect any revenue due for the usage of this
834		///   Coretime.
835		#[pallet::call_index(11)]
836		pub fn pool(
837			origin: OriginFor<T>,
838			region_id: RegionId,
839			payee: T::AccountId,
840			finality: Finality,
841		) -> DispatchResultWithPostInfo {
842			let who = ensure_signed(origin)?;
843			Self::do_pool(region_id, Some(who), payee, finality)?;
844			Ok(if finality == Finality::Final { Pays::No } else { Pays::Yes }.into())
845		}
846
847		/// Claim the revenue owed from inclusion in the Instantaneous Coretime Pool.
848		///
849		/// - `origin`: Must be a Signed origin.
850		/// - `region_id`: The Region which was assigned to the Pool.
851		/// - `max_timeslices`: The maximum number of timeslices which should be processed. This
852		///   must be greater than 0. This may affect the weight of the call but should be ideally
853		///   made equivalent to the length of the Region `region_id`. If less, further dispatches
854		///   will be required with the same `region_id` to claim revenue for the remainder.
855		///   Processing stops at the first timeslice whose revenue the relay chain has not yet
856		///   reported; the remainder can be claimed once it has been.
857		///
858		/// The fee is waived only if at least one timeslice was processed, and the weight of the
859		/// timeslices that were not processed is refunded.
860		#[pallet::call_index(12)]
861		#[pallet::weight(T::WeightInfo::claim_revenue(*max_timeslices))]
862		pub fn claim_revenue(
863			origin: OriginFor<T>,
864			region_id: RegionId,
865			max_timeslices: Timeslice,
866		) -> DispatchResultWithPostInfo {
867			ensure_signed(origin)?;
868			let checked = Self::do_claim_revenue(region_id, max_timeslices)?;
869			let pays = if checked > 1 { Pays::No } else { Pays::Yes };
870			Ok((Some(T::WeightInfo::claim_revenue(checked)), pays).into())
871		}
872
873		/// Purchase credit for use in the Instantaneous Coretime Pool.
874		///
875		/// - `origin`: Must be a Signed origin able to pay at least `amount`.
876		/// - `amount`: The amount of credit to purchase.
877		/// - `beneficiary`: The account on the Relay-chain which controls the credit (generally
878		///   this will be the collator's hot wallet).
879		#[pallet::call_index(13)]
880		pub fn purchase_credit(
881			origin: OriginFor<T>,
882			amount: BalanceOf<T>,
883			beneficiary: RelayAccountIdOf<T>,
884		) -> DispatchResult {
885			let who = ensure_signed(origin)?;
886			Self::do_purchase_credit(who, amount, beneficiary)?;
887			Ok(())
888		}
889
890		/// Drop an expired Region from the chain.
891		///
892		/// - `origin`: Can be any kind of origin.
893		/// - `region_id`: The Region which has expired.
894		#[pallet::call_index(14)]
895		pub fn drop_region(
896			_origin: OriginFor<T>,
897			region_id: RegionId,
898		) -> DispatchResultWithPostInfo {
899			Self::do_drop_region(region_id)?;
900			Ok(Pays::No.into())
901		}
902
903		/// Drop an expired Instantaneous Pool Contribution record from the chain.
904		///
905		/// - `origin`: Can be any kind of origin.
906		/// - `region_id`: The Region identifying the Pool Contribution which has expired.
907		#[pallet::call_index(15)]
908		pub fn drop_contribution(
909			_origin: OriginFor<T>,
910			region_id: RegionId,
911		) -> DispatchResultWithPostInfo {
912			Self::do_drop_contribution(region_id)?;
913			Ok(Pays::No.into())
914		}
915
916		/// Drop an expired Instantaneous Pool History record from the chain.
917		///
918		/// - `origin`: Can be any kind of origin.
919		/// - `region_id`: The time of the Pool History record which has expired.
920		#[pallet::call_index(16)]
921		pub fn drop_history(_origin: OriginFor<T>, when: Timeslice) -> DispatchResultWithPostInfo {
922			Self::do_drop_history(when)?;
923			Ok(Pays::No.into())
924		}
925
926		/// Drop an expired Allowed Renewal record from the chain.
927		///
928		/// - `origin`: Can be any kind of origin.
929		/// - `core`: The core to which the expired renewal refers.
930		/// - `when`: The timeslice to which the expired renewal refers. This must have passed.
931		#[pallet::call_index(17)]
932		pub fn drop_renewal(
933			_origin: OriginFor<T>,
934			core: CoreIndex,
935			when: Timeslice,
936		) -> DispatchResultWithPostInfo {
937			Self::do_drop_renewal(core, when)?;
938			Ok(Pays::No.into())
939		}
940
941		/// Request a change to the number of cores available for scheduling work.
942		///
943		/// - `origin`: Must be Root or pass `AdminOrigin`.
944		/// - `core_count`: The desired number of cores to be made available.
945		#[pallet::call_index(18)]
946		#[pallet::weight(T::WeightInfo::request_core_count((*core_count).into()))]
947		pub fn request_core_count(origin: OriginFor<T>, core_count: CoreIndex) -> DispatchResult {
948			T::AdminOrigin::ensure_origin_or_root(origin)?;
949			Self::do_request_core_count(core_count)?;
950			Ok(())
951		}
952
953		#[pallet::call_index(19)]
954		#[pallet::weight(T::WeightInfo::notify_core_count())]
955		pub fn notify_core_count(origin: OriginFor<T>, core_count: CoreIndex) -> DispatchResult {
956			T::AdminOrigin::ensure_origin_or_root(origin)?;
957			Self::do_notify_core_count(core_count)?;
958			Ok(())
959		}
960
961		#[pallet::call_index(20)]
962		#[pallet::weight(T::WeightInfo::notify_revenue())]
963		pub fn notify_revenue(
964			origin: OriginFor<T>,
965			revenue: OnDemandRevenueRecordOf<T>,
966		) -> DispatchResult {
967			T::AdminOrigin::ensure_origin_or_root(origin)?;
968			Self::do_notify_revenue(revenue)?;
969			Ok(())
970		}
971
972		/// Extrinsic for enabling auto renewal.
973		///
974		/// Callable by the sovereign account of the task on the specified core. This account
975		/// will be charged at the start of every bulk period for renewing core time.
976		///
977		/// - `origin`: Must be the sovereign account of the task
978		/// - `core`: The core to which the task to be renewed is currently assigned.
979		/// - `task`: The task for which we want to enable auto renewal.
980		/// - `workload_end_hint`: should be used when enabling auto-renewal for a core that is not
981		///   expiring in the upcoming bulk period (e.g., due to holding a lease) since it would be
982		///   inefficient to look up when the core expires to schedule the next renewal. Also used
983		///   when the core is expiring with another task's workload, in which case it must point at
984		///   the task's own renewal record.
985		#[pallet::call_index(21)]
986		#[pallet::weight(T::WeightInfo::enable_auto_renew())]
987		pub fn enable_auto_renew(
988			origin: OriginFor<T>,
989			core: CoreIndex,
990			task: TaskId,
991			workload_end_hint: Option<Timeslice>,
992		) -> DispatchResult {
993			let who = ensure_signed(origin)?;
994
995			let sovereign_account = T::SovereignAccountOf::maybe_convert(task)
996				.ok_or(Error::<T>::SovereignAccountNotFound)?;
997			// Only the sovereign account of a task can enable auto renewal for its own core.
998			ensure!(who == sovereign_account, Error::<T>::NoPermission);
999
1000			Self::do_enable_auto_renew(sovereign_account, core, task, workload_end_hint)?;
1001			Ok(())
1002		}
1003
1004		/// Extrinsic for disabling auto renewal.
1005		///
1006		/// Callable by the sovereign account of the task on the specified core.
1007		///
1008		/// - `origin`: Must be the sovereign account of the task.
1009		/// - `core`: The core for which we want to disable auto renewal.
1010		/// - `task`: The task for which we want to disable auto renewal.
1011		#[pallet::call_index(22)]
1012		#[pallet::weight(T::WeightInfo::disable_auto_renew())]
1013		pub fn disable_auto_renew(
1014			origin: OriginFor<T>,
1015			core: CoreIndex,
1016			task: TaskId,
1017		) -> DispatchResult {
1018			let who = ensure_signed(origin)?;
1019
1020			let sovereign_account = T::SovereignAccountOf::maybe_convert(task)
1021				.ok_or(Error::<T>::SovereignAccountNotFound)?;
1022			// Only the sovereign account of the task can disable auto-renewal.
1023			ensure!(who == sovereign_account, Error::<T>::NoPermission);
1024
1025			Self::do_disable_auto_renew(core, task)?;
1026
1027			Ok(())
1028		}
1029
1030		/// Reserve a core for a workload immediately.
1031		///
1032		/// - `origin`: Must be Root or pass `AdminOrigin`.
1033		/// - `workload`: The workload which should be permanently placed on a core starting
1034		///   immediately.
1035		/// - `core`: The core to which the assignment should be made until the reservation takes
1036		///   effect. It is left to the caller to either add this new core or reassign any other
1037		///   tasks to this existing core.
1038		///
1039		/// This reserves the workload and then injects the workload into the Workplan for the next
1040		/// two sale periods. This overwrites any existing assignments for this core at the start of
1041		/// the next sale period.
1042		#[pallet::call_index(23)]
1043		pub fn force_reserve(
1044			origin: OriginFor<T>,
1045			workload: Schedule,
1046			core: CoreIndex,
1047		) -> DispatchResultWithPostInfo {
1048			T::AdminOrigin::ensure_origin_or_root(origin)?;
1049			Self::do_force_reserve(workload, core)?;
1050			Ok(Pays::No.into())
1051		}
1052
1053		/// Remove a lease.
1054		///
1055		/// - `origin`: Must be Root or pass `AdminOrigin`.
1056		/// - `task`: The task id of the lease which should be removed.
1057		#[pallet::call_index(24)]
1058		pub fn remove_lease(origin: OriginFor<T>, task: TaskId) -> DispatchResult {
1059			T::AdminOrigin::ensure_origin_or_root(origin)?;
1060			Self::do_remove_lease(task)
1061		}
1062
1063		/// Remove an assignment from the Workplan.
1064		///
1065		/// - `origin`: Must be Root or pass `AdminOrigin`.
1066		/// - `region_id`: The Region to be removed from the workplan.
1067		#[pallet::call_index(26)]
1068		pub fn remove_assignment(origin: OriginFor<T>, region_id: RegionId) -> DispatchResult {
1069			T::AdminOrigin::ensure_origin_or_root(origin)?;
1070			Self::do_remove_assignment(region_id)
1071		}
1072
1073		/// Forcefully remove a potential renewal record from chain.
1074		///
1075		/// Note that only the specified potential renewal will be removed while any related auto
1076		/// renewals will stay intact and will fail.
1077		///
1078		/// - `origin`: Must be Root or pass `AdminOrigin`.
1079		/// - `core`: Core which the target potential renewal record refers to.
1080		/// - `when`: Timeslice which the target potential renewal record refers to.
1081		#[pallet::call_index(27)]
1082		pub fn remove_potential_renewal(
1083			origin: OriginFor<T>,
1084			core: CoreIndex,
1085			when: Timeslice,
1086		) -> DispatchResult {
1087			T::AdminOrigin::ensure_origin_or_root(origin)?;
1088			Self::do_remove_potential_renewal(core, when)
1089		}
1090
1091		/// Transfer a Bulk Coretime Region to a new owner, ignoring the previous owner.
1092		///
1093		/// This can also be used to recover regions that have been "burned" (e.g., from an
1094		/// XCM reserve transfer).
1095		///
1096		/// - `origin`: Must be Root or pass `AdminOrigin`.
1097		/// - `region_id`: The Region whose ownership should change.
1098		/// - `new_owner`: The new owner for the Region.
1099		#[pallet::call_index(28)]
1100		pub fn force_transfer(
1101			origin: OriginFor<T>,
1102			region_id: RegionId,
1103			new_owner: T::AccountId,
1104		) -> DispatchResult {
1105			T::AdminOrigin::ensure_origin_or_root(origin)?;
1106			Self::do_transfer(region_id, None, new_owner)?;
1107			Ok(())
1108		}
1109
1110		#[pallet::call_index(99)]
1111		#[pallet::weight(T::WeightInfo::swap_leases())]
1112		pub fn swap_leases(origin: OriginFor<T>, id: TaskId, other: TaskId) -> DispatchResult {
1113			T::AdminOrigin::ensure_origin_or_root(origin)?;
1114			Self::do_swap_leases(id, other)?;
1115			Ok(())
1116		}
1117	}
1118}