// This file is part of Substrate. // Copyright (C) Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use super::*; use frame_support::{ pallet_prelude::{DispatchResult, *}, traits::{ fungible::Balanced, tokens::{Fortitude::Polite, Precision::Exact, Preservation::Expendable}, OnUnbalanced, }, }; use frame_system::pallet_prelude::BlockNumberFor; use sp_arithmetic::{ traits::{SaturatedConversion, Saturating}, FixedPointNumber, FixedU64, }; use sp_runtime::traits::{AccountIdConversion, BlockNumberProvider}; impl Pallet { pub fn current_timeslice() -> Timeslice { let latest = RCBlockNumberProviderOf::::current_block_number(); let timeslice_period = T::TimeslicePeriod::get(); (latest / timeslice_period).saturated_into() } pub fn latest_timeslice_ready_to_commit(config: &ConfigRecordOf) -> Timeslice { let latest = RCBlockNumberProviderOf::::current_block_number(); let advanced = latest.saturating_add(config.advance_notice); let timeslice_period = T::TimeslicePeriod::get(); (advanced / timeslice_period).saturated_into() } pub fn next_timeslice_to_commit( config: &ConfigRecordOf, status: &StatusRecord, ) -> Option { if status.last_committed_timeslice < Self::latest_timeslice_ready_to_commit(config) { Some(status.last_committed_timeslice + 1) } else { None } } pub fn account_id() -> T::AccountId { T::PalletId::get().into_account_truncating() } pub fn sale_price(sale: &SaleInfoRecordOf, now: BlockNumberFor) -> BalanceOf { let num = now.saturating_sub(sale.sale_start).min(sale.leadin_length).saturated_into(); let through = FixedU64::from_rational(num, sale.leadin_length.saturated_into()); T::PriceAdapter::leadin_factor_at(through).saturating_mul_int(sale.price) } pub(crate) fn charge(who: &T::AccountId, amount: BalanceOf) -> DispatchResult { let credit = T::Currency::withdraw(&who, amount, Exact, Expendable, Polite)?; T::OnRevenue::on_unbalanced(credit); Ok(()) } pub(crate) fn issue( core: CoreIndex, begin: Timeslice, end: Timeslice, owner: T::AccountId, paid: Option>, ) -> RegionId { let id = RegionId { begin, core, mask: CoreMask::complete() }; let record = RegionRecord { end, owner, paid }; Regions::::insert(&id, &record); id } pub(crate) fn utilize( mut region_id: RegionId, maybe_check_owner: Option, finality: Finality, ) -> Result)>, Error> { let status = Status::::get().ok_or(Error::::Uninitialized)?; let region = Regions::::get(®ion_id).ok_or(Error::::UnknownRegion)?; if let Some(check_owner) = maybe_check_owner { ensure!(check_owner == region.owner, Error::::NotOwner); } Regions::::remove(®ion_id); let last_committed_timeslice = status.last_committed_timeslice; if region_id.begin <= last_committed_timeslice { let duration = region.end.saturating_sub(region_id.begin); region_id.begin = last_committed_timeslice + 1; if region_id.begin >= region.end { Self::deposit_event(Event::RegionDropped { region_id, duration }); return Ok(None) } } else { Workplan::::mutate_extant((region_id.begin, region_id.core), |p| { p.retain(|i| (i.mask & region_id.mask).is_void()) }); } if finality == Finality::Provisional { Regions::::insert(®ion_id, ®ion); } Ok(Some((region_id, region))) } }