1use core::cmp;
19
20use super::*;
21use frame_support::{
22 pallet_prelude::*,
23 traits::{fungible::Mutate, tokens::Preservation::Expendable},
24};
25use sp_arithmetic::traits::{CheckedDiv, Saturating, Zero};
26use sp_runtime::traits::{BlockNumberProvider, Convert};
27use CompletionStatus::{Complete, Partial};
28
29impl<T: Config> Pallet<T> {
30 pub(crate) fn do_configure(config: ConfigRecordOf<T>) -> DispatchResult {
31 config.validate().map_err(|()| Error::<T>::InvalidConfig)?;
32 Configuration::<T>::put(config);
33 Ok(())
34 }
35
36 pub(crate) fn do_request_core_count(core_count: CoreIndex) -> DispatchResult {
37 T::Coretime::request_core_count(core_count);
38 Self::deposit_event(Event::<T>::CoreCountRequested { core_count });
39 Ok(())
40 }
41
42 pub(crate) fn do_notify_core_count(core_count: CoreIndex) -> DispatchResult {
43 CoreCountInbox::<T>::put(core_count);
44 Ok(())
45 }
46
47 pub(crate) fn do_reserve(workload: Schedule) -> DispatchResult {
48 let mut r = Reservations::<T>::get();
49 let index = r.len() as u32;
50 r.try_push(workload.clone()).map_err(|_| Error::<T>::TooManyReservations)?;
51 Reservations::<T>::put(r);
52 Self::deposit_event(Event::<T>::ReservationMade { index, workload });
53 Ok(())
54 }
55
56 pub(crate) fn do_unreserve(index: u32) -> DispatchResult {
57 let mut r = Reservations::<T>::get();
58 ensure!(index < r.len() as u32, Error::<T>::UnknownReservation);
59 let workload = r.remove(index as usize);
60 Reservations::<T>::put(r);
61 Self::deposit_event(Event::<T>::ReservationCancelled { index, workload });
62 Ok(())
63 }
64
65 pub(crate) fn do_force_reserve(workload: Schedule, core: CoreIndex) -> DispatchResult {
66 let sale = SaleInfo::<T>::get().ok_or(Error::<T>::NoSales)?;
68
69 Self::do_reserve(workload.clone())?;
71
72 ForceReservations::<T>::try_mutate(|r| {
74 r.try_push(workload.clone()).map_err(|_| Error::<T>::TooManyReservations)
75 })?;
76
77 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
80 let timeslice = status.last_committed_timeslice.saturating_add(1);
81 if timeslice < sale.region_begin {
82 Workplan::<T>::insert((timeslice, core), &workload);
83 }
84
85 Ok(())
86 }
87
88 pub(crate) fn do_set_lease(task: TaskId, until: Timeslice) -> DispatchResult {
89 let mut r = Leases::<T>::get();
90 ensure!(until > Self::current_timeslice(), Error::<T>::AlreadyExpired);
91 r.try_push(LeaseRecordItem { until, task })
92 .map_err(|_| Error::<T>::TooManyLeases)?;
93 Leases::<T>::put(r);
94 Self::deposit_event(Event::<T>::Leased { until, task });
95 Ok(())
96 }
97
98 pub(crate) fn do_remove_lease(task: TaskId) -> DispatchResult {
99 let mut r = Leases::<T>::get();
100 let i = r.iter().position(|lease| lease.task == task).ok_or(Error::<T>::LeaseNotFound)?;
101 r.remove(i);
102 Leases::<T>::put(r);
103 Self::deposit_event(Event::<T>::LeaseRemoved { task });
104 Ok(())
105 }
106
107 pub(crate) fn do_start_sales(
108 end_price: BalanceOf<T>,
109 extra_cores: CoreIndex,
110 ) -> DispatchResult {
111 let config = Configuration::<T>::get().ok_or(Error::<T>::Uninitialized)?;
112
113 let core_count = Leases::<T>::decode_len().unwrap_or(0) as CoreIndex +
115 Reservations::<T>::decode_len().unwrap_or(0) as CoreIndex +
116 extra_cores;
117
118 Self::do_request_core_count(core_count)?;
119
120 let commit_timeslice = Self::latest_timeslice_ready_to_commit(&config);
121 let status = StatusRecord {
122 core_count,
123 private_pool_size: 0,
124 system_pool_size: 0,
125 last_committed_timeslice: commit_timeslice.saturating_sub(1),
126 last_timeslice: Self::current_timeslice(),
127 };
128 let now = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
129 let old_sale = SaleInfoRecord {
131 sale_start: now,
132 leadin_length: Zero::zero(),
133 end_price,
134 sellout_price: None,
135 region_begin: commit_timeslice,
136 region_end: commit_timeslice.saturating_add(config.region_length),
137 first_core: 0,
138 ideal_cores_sold: 0,
139 cores_offered: 0,
140 cores_sold: 0,
141 sale_index: 0,
142 };
143 Self::deposit_event(Event::<T>::SalesStarted { price: end_price, core_count });
144 Self::rotate_sale(old_sale, &config, &status);
145 Status::<T>::put(&status);
146 Ok(())
147 }
148
149 pub(crate) fn do_purchase(
150 who: T::AccountId,
151 price_limit: BalanceOf<T>,
152 ) -> Result<RegionId, DispatchError> {
153 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
154 let mut sale = SaleInfo::<T>::get().ok_or(Error::<T>::NoSales)?;
155 Self::ensure_cores_for_sale(&status, &sale)?;
156
157 let now = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
158 ensure!(now > sale.sale_start, Error::<T>::TooEarly);
159 let price = Self::sale_price(&sale, now);
160 ensure!(price_limit >= price, Error::<T>::Overpriced);
161
162 let core = Self::purchase_core(&who, price, &mut sale)?;
163
164 SaleInfo::<T>::put(&sale);
165 let id = Self::issue(
166 core,
167 sale.region_begin,
168 CoreMask::complete(),
169 sale.region_end,
170 Some(who.clone()),
171 Some(price),
172 );
173 let duration = sale.region_end.saturating_sub(sale.region_begin);
174 Self::deposit_event(Event::Purchased { who, region_id: id, price, duration });
175 Ok(id)
176 }
177
178 pub(crate) fn do_renew(who: T::AccountId, core: CoreIndex) -> Result<CoreIndex, DispatchError> {
181 let config = Configuration::<T>::get().ok_or(Error::<T>::Uninitialized)?;
182 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
183 let mut sale = SaleInfo::<T>::get().ok_or(Error::<T>::NoSales)?;
184 Self::ensure_cores_for_sale(&status, &sale)?;
185
186 let renewal_id = PotentialRenewalId { core, when: sale.region_begin };
187 let record = PotentialRenewals::<T>::get(renewal_id).ok_or(Error::<T>::NotAllowed)?;
188 let workload =
189 record.completion.drain_complete().ok_or(Error::<T>::IncompleteAssignment)?;
190
191 let old_core = core;
192
193 let core = Self::purchase_core(&who, record.price, &mut sale)?;
194
195 Self::deposit_event(Event::Renewed {
196 who,
197 old_core,
198 core,
199 price: record.price,
200 begin: sale.region_begin,
201 duration: sale.region_end.saturating_sub(sale.region_begin),
202 workload: workload.clone(),
203 });
204
205 Workplan::<T>::insert((sale.region_begin, core), &workload);
206
207 let begin = sale.region_end;
208 let end_price = sale.end_price;
209 let price_cap = cmp::max(record.price + config.renewal_bump * record.price, end_price);
211 let now = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
212 let price = Self::sale_price(&sale, now).min(price_cap);
213 log::debug!(
214 "Renew with: sale price: {:?}, price cap: {:?}, old price: {:?}",
215 price,
216 price_cap,
217 record.price
218 );
219 let new_record = PotentialRenewalRecord { price, completion: Complete(workload) };
220 PotentialRenewals::<T>::remove(renewal_id);
221 PotentialRenewals::<T>::insert(PotentialRenewalId { core, when: begin }, &new_record);
222 SaleInfo::<T>::put(&sale);
223 if let Some(workload) = new_record.completion.drain_complete() {
224 log::debug!("Recording renewable price for next run: {:?}", price);
225 Self::deposit_event(Event::Renewable { core, price, begin, workload });
226 }
227 Ok(core)
228 }
229
230 pub(crate) fn do_transfer(
231 region_id: RegionId,
232 maybe_check_owner: Option<T::AccountId>,
233 new_owner: T::AccountId,
234 ) -> Result<(), Error<T>> {
235 let mut region = Regions::<T>::get(®ion_id).ok_or(Error::<T>::UnknownRegion)?;
236
237 if let Some(check_owner) = maybe_check_owner {
238 ensure!(Some(check_owner) == region.owner, Error::<T>::NotOwner);
239 }
240
241 let old_owner = region.owner;
242 region.owner = Some(new_owner);
243 Regions::<T>::insert(®ion_id, ®ion);
244 let duration = region.end.saturating_sub(region_id.begin);
245 Self::deposit_event(Event::Transferred {
246 region_id,
247 old_owner,
248 owner: region.owner,
249 duration,
250 });
251
252 Ok(())
253 }
254
255 pub(crate) fn do_partition(
256 region_id: RegionId,
257 maybe_check_owner: Option<T::AccountId>,
258 pivot_offset: Timeslice,
259 ) -> Result<(RegionId, RegionId), Error<T>> {
260 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
261 let mut region = Regions::<T>::get(®ion_id).ok_or(Error::<T>::UnknownRegion)?;
262
263 if let Some(check_owner) = maybe_check_owner {
264 ensure!(Some(check_owner) == region.owner, Error::<T>::NotOwner);
265 }
266 let pivot = region_id.begin.saturating_add(pivot_offset);
267 ensure!(pivot < region.end, Error::<T>::PivotTooLate);
268 ensure!(pivot > region_id.begin, Error::<T>::PivotTooEarly);
269
270 region.paid = None;
271 let new_region_ids = (region_id, RegionId { begin: pivot, ..region_id });
272
273 Self::force_unpool_region(region_id, ®ion, &status);
277
278 Regions::<T>::insert(&new_region_ids.0, &RegionRecord { end: pivot, ..region.clone() });
281 Regions::<T>::insert(&new_region_ids.1, ®ion);
282 Self::deposit_event(Event::Partitioned { old_region_id: region_id, new_region_ids });
283
284 Ok(new_region_ids)
285 }
286
287 pub(crate) fn do_interlace(
288 region_id: RegionId,
289 maybe_check_owner: Option<T::AccountId>,
290 pivot: CoreMask,
291 ) -> Result<(RegionId, RegionId), Error<T>> {
292 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
293 let region = Regions::<T>::get(®ion_id).ok_or(Error::<T>::UnknownRegion)?;
294
295 if let Some(check_owner) = maybe_check_owner {
296 ensure!(Some(check_owner) == region.owner, Error::<T>::NotOwner);
297 }
298
299 ensure!((pivot & !region_id.mask).is_void(), Error::<T>::ExteriorPivot);
300 ensure!(!pivot.is_void(), Error::<T>::VoidPivot);
301 ensure!(pivot != region_id.mask, Error::<T>::CompletePivot);
302
303 Self::force_unpool_region(region_id, ®ion, &status);
307
308 Regions::<T>::remove(®ion_id);
310
311 let one = RegionId { mask: pivot, ..region_id };
312 Regions::<T>::insert(&one, ®ion);
313 let other = RegionId { mask: region_id.mask ^ pivot, ..region_id };
314 Regions::<T>::insert(&other, ®ion);
315
316 let new_region_ids = (one, other);
317 Self::deposit_event(Event::Interlaced { old_region_id: region_id, new_region_ids });
318 Ok(new_region_ids)
319 }
320
321 pub(crate) fn do_assign(
322 region_id: RegionId,
323 maybe_check_owner: Option<T::AccountId>,
324 target: TaskId,
325 finality: Finality,
326 ) -> Result<(), Error<T>> {
327 let config = Configuration::<T>::get().ok_or(Error::<T>::Uninitialized)?;
328 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
329
330 if let Some((region_id, region)) = Self::utilize(region_id, maybe_check_owner, finality)? {
331 let workplan_key = (region_id.begin, region_id.core);
332 let mut workplan = Workplan::<T>::get(&workplan_key).unwrap_or_default();
333
334 Self::force_unpool_region(region_id, ®ion, &status);
338
339 workplan.retain(|i| (i.mask & region_id.mask).is_void());
341 if workplan
342 .try_push(ScheduleItem {
343 mask: region_id.mask,
344 assignment: CoreAssignment::Task(target),
345 })
346 .is_ok()
347 {
348 Workplan::<T>::insert(&workplan_key, &workplan);
349 }
350
351 let duration = region.end.saturating_sub(region_id.begin);
352 if duration == config.region_length && finality == Finality::Final {
353 if let Some(price) = region.paid {
354 let renewal_id = PotentialRenewalId { core: region_id.core, when: region.end };
355 let assigned = match PotentialRenewals::<T>::get(renewal_id) {
356 Some(PotentialRenewalRecord { completion: Partial(w), price: p })
357 if price == p =>
358 {
359 w
360 },
361 _ => CoreMask::void(),
362 } | region_id.mask;
363 let workload =
364 if assigned.is_complete() { Complete(workplan) } else { Partial(assigned) };
365 let record = PotentialRenewalRecord { price, completion: workload };
366 PotentialRenewals::<T>::insert(&renewal_id, &record);
369 if let Some(workload) = record.completion.drain_complete() {
370 Self::deposit_event(Event::Renewable {
371 core: region_id.core,
372 price,
373 begin: region.end,
374 workload,
375 });
376 }
377 }
378 }
379 Self::deposit_event(Event::Assigned { region_id, task: target, duration });
380 }
381 Ok(())
382 }
383
384 pub(crate) fn do_remove_assignment(region_id: RegionId) -> DispatchResult {
385 let workplan_key = (region_id.begin, region_id.core);
386 ensure!(Workplan::<T>::contains_key(&workplan_key), Error::<T>::AssignmentNotFound);
387 Workplan::<T>::remove(&workplan_key);
388 Self::deposit_event(Event::<T>::AssignmentRemoved { region_id });
389 Ok(())
390 }
391
392 pub(crate) fn do_pool(
393 region_id: RegionId,
394 maybe_check_owner: Option<T::AccountId>,
395 payee: T::AccountId,
396 finality: Finality,
397 ) -> Result<(), Error<T>> {
398 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
399
400 if let Some((region_id, region)) = Self::utilize(region_id, maybe_check_owner, finality)? {
401 let workplan_key = (region_id.begin, region_id.core);
402 let mut workplan = Workplan::<T>::get(&workplan_key).unwrap_or_default();
403
404 Self::force_unpool_region(region_id, ®ion, &status);
407
408 let duration = region.end.saturating_sub(region_id.begin);
409 if workplan
410 .try_push(ScheduleItem { mask: region_id.mask, assignment: CoreAssignment::Pool })
411 .is_ok()
412 {
413 Workplan::<T>::insert(&workplan_key, &workplan);
414 let size = region_id.mask.count_ones() as i32;
415 InstaPoolIo::<T>::mutate(region_id.begin, |a| a.private.saturating_accrue(size));
416 InstaPoolIo::<T>::mutate(region.end, |a| a.private.saturating_reduce(size));
417 let record = ContributionRecord { length: duration, payee };
418 InstaPoolContribution::<T>::insert(®ion_id, record);
419 }
420
421 Self::deposit_event(Event::Pooled { region_id, duration });
422 }
423 Ok(())
424 }
425
426 pub(crate) fn do_claim_revenue(
429 mut region: RegionId,
430 max_timeslices: Timeslice,
431 ) -> Result<Timeslice, DispatchError> {
432 ensure!(max_timeslices > 0, Error::<T>::NoClaimTimeslices);
433 let first = region.begin;
434 let mut contribution =
435 InstaPoolContribution::<T>::take(region).ok_or(Error::<T>::UnknownContribution)?;
436 let contributed_parts = region.mask.count_ones();
437
438 Self::deposit_event(Event::RevenueClaimBegun { region, max_timeslices });
439
440 let mut payout = BalanceOf::<T>::zero();
441 let last = region.begin + contribution.length.min(max_timeslices);
442 let last_committed_timeslice = Status::<T>::get()
443 .map(|status| status.last_committed_timeslice)
444 .unwrap_or_default();
445 for r in region.begin..last {
446 let Some(mut pool_record) = InstaPoolHistory::<T>::get(r) else {
447 if r > last_committed_timeslice {
451 break;
452 }
453 region.begin = r + 1;
454 contribution.length.saturating_dec();
455 continue;
456 };
457 let Some(total_payout) = pool_record.maybe_payout else { break };
458
459 region.begin = r + 1;
460 contribution.length.saturating_dec();
461
462 let p = total_payout
463 .saturating_mul(contributed_parts.into())
464 .checked_div(&pool_record.private_contributions.into())
465 .unwrap_or_default();
466
467 payout.saturating_accrue(p);
468 pool_record.private_contributions.saturating_reduce(contributed_parts);
469
470 let remaining_payout = total_payout.saturating_sub(p);
471 if !remaining_payout.is_zero() && pool_record.private_contributions > 0 {
472 pool_record.maybe_payout = Some(remaining_payout);
473 InstaPoolHistory::<T>::insert(r, &pool_record);
474 } else {
475 InstaPoolHistory::<T>::remove(r);
476 }
477 if !p.is_zero() {
478 Self::deposit_event(Event::RevenueClaimItem { when: r, amount: p });
479 }
480 }
481
482 if contribution.length > 0 {
483 InstaPoolContribution::<T>::insert(region, &contribution);
484 }
485 T::Currency::transfer(&Self::account_id(), &contribution.payee, payout, Expendable)?;
489 let next = if contribution.length > 0 { Some(region) } else { None };
490 Self::deposit_event(Event::RevenueClaimPaid {
491 who: contribution.payee,
492 amount: payout,
493 next,
494 });
495 Ok(region.begin.saturating_sub(first).saturating_add(1))
496 }
497
498 pub(crate) fn do_purchase_credit(
499 who: T::AccountId,
500 amount: BalanceOf<T>,
501 beneficiary: RelayAccountIdOf<T>,
502 ) -> DispatchResult {
503 ensure!(amount >= T::MinimumCreditPurchase::get(), Error::<T>::CreditPurchaseTooSmall);
504 T::Currency::transfer(&who, &Self::account_id(), amount, Expendable)?;
505 let rc_amount = T::ConvertBalance::convert(amount);
506 T::Coretime::credit_account(beneficiary.clone(), rc_amount);
507 Self::deposit_event(Event::<T>::CreditPurchased { who, beneficiary, amount });
508 Ok(())
509 }
510
511 pub(crate) fn do_drop_region(region_id: RegionId) -> DispatchResult {
512 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
513 let region = Regions::<T>::get(®ion_id).ok_or(Error::<T>::UnknownRegion)?;
514 ensure!(status.last_committed_timeslice >= region.end, Error::<T>::StillValid);
515
516 Regions::<T>::remove(®ion_id);
517 let duration = region.end.saturating_sub(region_id.begin);
518 Self::deposit_event(Event::RegionDropped { region_id, duration });
519 Ok(())
520 }
521
522 pub(crate) fn do_drop_contribution(region_id: RegionId) -> DispatchResult {
523 let config = Configuration::<T>::get().ok_or(Error::<T>::Uninitialized)?;
524 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
525 let contrib =
526 InstaPoolContribution::<T>::get(®ion_id).ok_or(Error::<T>::UnknownContribution)?;
527 let end = region_id.begin.saturating_add(contrib.length);
528 ensure!(
529 status.last_timeslice >= end.saturating_add(config.contribution_timeout),
530 Error::<T>::StillValid
531 );
532 InstaPoolContribution::<T>::remove(region_id);
533 Self::deposit_event(Event::ContributionDropped { region_id });
534 Ok(())
535 }
536
537 pub(crate) fn do_drop_history(when: Timeslice) -> DispatchResult {
538 let config = Configuration::<T>::get().ok_or(Error::<T>::Uninitialized)?;
539 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
540 ensure!(
541 status.last_timeslice > when.saturating_add(config.contribution_timeout),
542 Error::<T>::StillValid
543 );
544 let record = InstaPoolHistory::<T>::take(when).ok_or(Error::<T>::NoHistory)?;
545 if let Some(payout) = record.maybe_payout {
546 let _ = Self::charge(&Self::account_id(), payout);
547 }
548 let revenue = record.maybe_payout.unwrap_or_default();
549 Self::deposit_event(Event::HistoryDropped { when, revenue });
550 Ok(())
551 }
552
553 pub(crate) fn do_drop_renewal(core: CoreIndex, when: Timeslice) -> DispatchResult {
554 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
555 ensure!(status.last_committed_timeslice >= when, Error::<T>::StillValid);
556 let id = PotentialRenewalId { core, when };
557 ensure!(PotentialRenewals::<T>::contains_key(id), Error::<T>::UnknownRenewal);
558 PotentialRenewals::<T>::remove(id);
559 Self::deposit_event(Event::PotentialRenewalDropped { core, when });
560 Ok(())
561 }
562
563 pub(crate) fn do_notify_revenue(revenue: OnDemandRevenueRecordOf<T>) -> DispatchResult {
564 RevenueInbox::<T>::put(revenue);
565 Ok(())
566 }
567
568 pub(crate) fn do_swap_leases(id: TaskId, other: TaskId) -> DispatchResult {
569 let mut id_leases_count = 0;
570 let mut other_leases_count = 0;
571 Leases::<T>::mutate(|leases| {
572 leases.iter_mut().for_each(|lease| {
573 if lease.task == id {
574 lease.task = other;
575 id_leases_count += 1;
576 } else if lease.task == other {
577 lease.task = id;
578 other_leases_count += 1;
579 }
580 })
581 });
582 Ok(())
583 }
584
585 pub(crate) fn do_enable_auto_renew(
586 sovereign_account: T::AccountId,
587 core: CoreIndex,
588 task: TaskId,
589 workload_end_hint: Option<Timeslice>,
590 ) -> DispatchResult {
591 let sale = SaleInfo::<T>::get().ok_or(Error::<T>::NoSales)?;
592 let mut core = core;
593
594 let renewable_now =
601 PotentialRenewals::<T>::get(PotentialRenewalId { core, when: sale.region_begin })
602 .map_or(false, |record| record.completion.is_complete_and_contains_task(task));
603
604 let next_renewal = if renewable_now {
605 core = Self::do_renew(sovereign_account.clone(), core)?;
606 sale.region_end
608 } else if let Some(workload_end) = workload_end_hint {
609 let record =
610 PotentialRenewals::<T>::get(PotentialRenewalId { core, when: workload_end })
611 .ok_or(Error::<T>::NotAllowed)?;
612 let workload = record.completion.complete().ok_or(Error::<T>::IncompleteAssignment)?;
613 ensure!(
614 workload.iter().any(|item| item.assignment == CoreAssignment::Task(task)),
615 Error::<T>::TaskNotInWorkload
616 );
617 workload_end
618 } else {
619 return Err(Error::<T>::TaskNotInWorkload.into());
620 };
621
622 AutoRenewals::<T>::try_mutate(|renewals| -> DispatchResult {
624 let pos = match renewals.binary_search_by(|r: &AutoRenewalRecord| r.core.cmp(&core)) {
625 Ok(_) => return Err(Error::<T>::AutoRenewalAlreadyEnabled.into()),
626 Err(pos) => pos,
627 };
628 renewals
629 .try_insert(pos, AutoRenewalRecord { core, task, next_renewal })
630 .map_err(|_| Error::<T>::TooManyAutoRenewals)?;
631 Ok(())
632 })?;
633
634 Self::deposit_event(Event::AutoRenewalEnabled { core, task });
635 Ok(())
636 }
637
638 pub(crate) fn do_disable_auto_renew(core: CoreIndex, task: TaskId) -> DispatchResult {
639 AutoRenewals::<T>::try_mutate(|renewals| -> DispatchResult {
640 let pos = renewals
641 .binary_search_by(|r: &AutoRenewalRecord| r.core.cmp(&core))
642 .map_err(|_| Error::<T>::AutoRenewalNotEnabled)?;
643
644 let renewal_record = renewals.get(pos).ok_or(Error::<T>::AutoRenewalNotEnabled)?;
645
646 ensure!(
647 renewal_record.core == core && renewal_record.task == task,
648 Error::<T>::NoPermission
649 );
650 renewals.remove(pos);
651 Ok(())
652 })?;
653
654 Self::deposit_event(Event::AutoRenewalDisabled { core, task });
655 Ok(())
656 }
657
658 pub(crate) fn do_remove_potential_renewal(core: CoreIndex, when: Timeslice) -> DispatchResult {
659 let renewal_id = PotentialRenewalId { core, when };
660
661 PotentialRenewals::<T>::take(renewal_id).ok_or(Error::<T>::UnknownRenewal)?;
662
663 Self::deposit_event(Event::PotentialRenewalRemoved { core, timeslice: when });
664
665 Ok(())
666 }
667
668 pub(crate) fn ensure_cores_for_sale(
669 status: &StatusRecord,
670 sale: &SaleInfoRecordOf<T>,
671 ) -> Result<(), DispatchError> {
672 ensure!(sale.first_core < status.core_count, Error::<T>::Unavailable);
673 ensure!(sale.cores_sold < sale.cores_offered, Error::<T>::SoldOut);
674
675 Ok(())
676 }
677
678 pub fn current_price() -> Result<BalanceOf<T>, DispatchError> {
680 let status = Status::<T>::get().ok_or(Error::<T>::Uninitialized)?;
681 let sale = SaleInfo::<T>::get().ok_or(Error::<T>::NoSales)?;
682
683 Self::ensure_cores_for_sale(&status, &sale)?;
684
685 let now = RCBlockNumberProviderOf::<T::Coretime>::current_block_number();
686 Ok(Self::sale_price(&sale, now))
687 }
688}