From 85693d6883b8366823d2217934e40d82ad872457 Mon Sep 17 00:00:00 2001 From: Shawn Tabrizi <shawntabrizi@gmail.com> Date: Sun, 4 Apr 2021 13:11:37 +0200 Subject: [PATCH] Create Macro for Implementing `SlotRange` (#2788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Macro for generating `SlotRange` * remove test code * refactor slots into crate * add no_std flag * finish macro * make compile * fix copyright date * don't assume lease periods per slot * Update runtime/common/Cargo.toml * Apply suggestions from code review Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> * fixes * Update lib.rs * tests * Move consts into struct * docs * fix compile * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Update Cargo.lock * Update runtime/common/src/slot_range.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * fixes Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> --- polkadot/Cargo.lock | 11 + polkadot/Cargo.toml | 1 + polkadot/runtime/common/Cargo.toml | 2 + .../common/slot_range_helper/Cargo.toml | 19 ++ .../common/slot_range_helper/src/lib.rs | 289 ++++++++++++++++++ polkadot/runtime/common/src/auctions.rs | 25 +- polkadot/runtime/common/src/crowdloan.rs | 7 +- polkadot/runtime/common/src/lib.rs | 2 +- polkadot/runtime/common/src/slot_range.rs | 137 +-------- 9 files changed, 343 insertions(+), 150 deletions(-) create mode 100644 polkadot/runtime/common/slot_range_helper/Cargo.toml create mode 100644 polkadot/runtime/common/slot_range_helper/src/lib.rs diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock index c71c4fa51ce..ffa8f21cec5 100644 --- a/polkadot/Cargo.lock +++ b/polkadot/Cargo.lock @@ -6261,6 +6261,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "slot-range-helper", "sp-api", "sp-application-crypto", "sp-core", @@ -8732,6 +8733,16 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +[[package]] +name = "slot-range-helper" +version = "0.8.30" +dependencies = [ + "parity-scale-codec", + "paste 1.0.4", + "sp-runtime", + "sp-std", +] + [[package]] name = "smallvec" version = "0.6.13" diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml index 7d97405d09e..0f02019ca2d 100644 --- a/polkadot/Cargo.toml +++ b/polkadot/Cargo.toml @@ -34,6 +34,7 @@ members = [ "erasure-coding", "primitives", "runtime/common", + "runtime/common/slot_range_helper", "runtime/parachains", "runtime/polkadot", "runtime/kusama", diff --git a/polkadot/runtime/common/Cargo.toml b/polkadot/runtime/common/Cargo.toml index e2e8e621f49..ec44dbc13ad 100644 --- a/polkadot/runtime/common/Cargo.toml +++ b/polkadot/runtime/common/Cargo.toml @@ -46,6 +46,7 @@ primitives = { package = "polkadot-primitives", path = "../../primitives", defau libsecp256k1 = { version = "0.3.5", default-features = false } runtime-parachains = { package = "polkadot-runtime-parachains", path = "../parachains", default-features = false } +slot-range-helper = { path = "slot_range_helper", default-features = false } xcm = { path = "../../xcm", default-features = false } [dev-dependencies] @@ -91,6 +92,7 @@ std = [ "pallet-vesting/std", "pallet-transaction-payment/std", "pallet-treasury/std", + "slot-range-helper/std", "sp-runtime/std", "sp-session/std", "sp-staking/std", diff --git a/polkadot/runtime/common/slot_range_helper/Cargo.toml b/polkadot/runtime/common/slot_range_helper/Cargo.toml new file mode 100644 index 00000000000..eff28d19c91 --- /dev/null +++ b/polkadot/runtime/common/slot_range_helper/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "slot-range-helper" +version = "0.8.30" +authors = ["Parity Technologies <admin@parity.io>"] +edition = "2018" + +[dependencies] +paste = "1.0" +parity-scale-codec = { version = "2.0.0", default-features = false, features = ["derive"] } +sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } +sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false } + +[features] +default = ["std"] +std = [ + "sp-std/std", + "parity-scale-codec/std", + "sp-runtime/std", +] diff --git a/polkadot/runtime/common/slot_range_helper/src/lib.rs b/polkadot/runtime/common/slot_range_helper/src/lib.rs new file mode 100644 index 00000000000..ffad684b33a --- /dev/null +++ b/polkadot/runtime/common/slot_range_helper/src/lib.rs @@ -0,0 +1,289 @@ +// Copyright 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/>. + +//! A helper macro for generating SlotRange enum. + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use sp_std::{result, ops::Add, convert::TryInto}; +pub use sp_runtime::traits::CheckedSub; +pub use parity_scale_codec::{Encode, Decode}; +pub use paste; + +/// This macro generates a `SlotRange` enum of arbitrary length for use in the Slot Auction +/// mechanism on Polkadot. +/// +/// Usage: +/// ``` +/// slot_range_helper::generate_slot_range!(Zero(0), One(1), Two(2), Three(3)); +/// ``` +/// +/// To extend the usage, continue to add `Identifier(value)` items to the macro. +/// +/// This will generate an enum `SlotRange` with the following properties: +/// +/// * Enum variants will range from all consecutive combinations of inputs, i.e. +/// `ZeroZero`, `ZeroOne`, `ZeroTwo`, `ZeroThree`, `OneOne`, `OneTwo`, `OneThree`... +/// * A constant `LEASE_PERIODS_PER_SLOT` will count the number of lease periods. +/// * A constant `SLOT_RANGE_COUNT` will count the total number of enum variants. +/// * A function `as_pair` will return a tuple representation of the `SlotRange`. +/// * A function `intersects` will tell you if two slot ranges intersect with one another. +/// * A function `len` will tell you the length of occupying a `SlotRange`. +/// * A function `new_bounded` will generate a `SlotRange` from an input of the current +/// lease period, the starting lease period, and the final lease period. +#[macro_export] +macro_rules! generate_slot_range{ + // Entry point + ($( $x:ident ( $e:expr ) ),*) => { + $crate::generate_lease_period_per_slot!( $( $x )* ); + $crate::generate_slot_range!(@inner + { } + $( $x ( $e ) )* + ); + }; + // Does the magic... + (@inner + { $( $parsed:ident ( $t1:expr, $t2:expr ) )* } + $current:ident ( $ce:expr ) + $( $remaining:ident ( $re:expr ) )* + ) => { + $crate::paste::paste! { + $crate::generate_slot_range!(@inner + { + $( $parsed ( $t1, $t2 ) )* + [< $current $current >] ( $ce, $ce ) + $( [< $current $remaining >] ($ce, $re) )* + } + $( $remaining ( $re ) )* + ); + } + }; + (@inner + { $( $parsed:ident ( $t1:expr, $t2:expr ) )* } + ) => { + $crate::generate_slot_range_enum!(@inner $( $parsed )* ); + + $crate::generate_slot_range_count!( $( $parsed )* ); + + #[cfg(feature = "std")] + impl std::fmt::Debug for SlotRange { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + let p = self.as_pair(); + write!(fmt, "[{}..{}]", p.0, p.1) + } + } + + impl SlotRange { + pub const LEASE_PERIODS_PER_SLOT: usize = LEASE_PERIODS_PER_SLOT; + pub const SLOT_RANGE_COUNT: usize = SLOT_RANGE_COUNT; + + $crate::generate_slot_range_as_pair!(@inner $( $parsed ( $t1, $t2 ) )* ); + + $crate::generate_slot_range_len!(@inner $( $parsed ( $t1, $t2 ) )* ); + + $crate::generate_slot_range_new_bounded!(@inner $( $parsed ( $t1, $t2 ) )* ); + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_slot_range_enum { + (@inner + $( $parsed:ident )* + ) => { + /// A compactly represented sub-range from the series. + #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, $crate::Encode, $crate::Decode)] + #[repr(u8)] + pub enum SlotRange { $( $parsed ),* } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_slot_range_as_pair { + (@inner + $( $parsed:ident ( $t1:expr, $t2:expr ) )* + ) => { + /// Return true if two `SlotRange` intersect in their lease periods. + pub fn intersects(&self, other: SlotRange) -> bool { + let a = self.as_pair(); + let b = other.as_pair(); + b.0 <= a.1 && a.0 <= b.1 + // == !(b.0 > a.1 || a.0 > b.1) + } + + /// Return a tuple representation of the `SlotRange`. + /// + /// Example:`SlotRange::OneTwo.as_pair() == (1, 2)` + pub fn as_pair(&self) -> (u8, u8) { + match self { + $( SlotRange::$parsed => { ($t1, $t2) } )* + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_slot_range_len { + // Use evaluated length in function. + (@inner + $( $parsed:ident ( $t1:expr, $t2:expr ) )* + ) => { + /// Return the length of occupying a `SlotRange`. + /// + /// Example:`SlotRange::OneTwo.len() == 2` + pub fn len(&self) -> usize { + match self { + // len (0, 2) = 2 - 0 + 1 = 3 + $( SlotRange::$parsed => { ( $t2 - $t1 + 1) } )* + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_slot_range_new_bounded { + (@inner + $( $parsed:ident ( $t1:expr, $t2:expr ) )* + ) => { + /// Construct a `SlotRange` from the current lease period, the first lease period of the range, + /// and the last lease period of the range. + /// + /// For example: `SlotRange::new_bounded(1, 2, 3) == SlotRange::OneTwo`. + pub fn new_bounded< + Index: $crate::Add<Output=Index> + $crate::CheckedSub + Copy + Ord + From<u32> + $crate::TryInto<u32> + >( + current: Index, + first: Index, + last: Index + ) -> $crate::result::Result<Self, &'static str> { + if first > last || first < current || last >= current + (LEASE_PERIODS_PER_SLOT as u32).into() { + return Err("Invalid range for this auction") + } + let count: u32 = last.checked_sub(&first) + .ok_or("range ends before it begins")? + .try_into() + .map_err(|_| "range too big")?; + let first: u32 = first.checked_sub(¤t) + .ok_or("range begins too early")? + .try_into() + .map_err(|_| "start too far")?; + match (first, first + count) { + $( ($t1, $t2) => { Ok(SlotRange::$parsed) })* + _ => Err("bad range"), + } + } + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_slot_range_count { + ( + $start:ident $( $rest:ident )* + ) => { + $crate::generate_slot_range_count!(@inner 1; $( $rest )*); + }; + (@inner + $count:expr; + $start:ident $( $rest:ident )* + ) => { + $crate::generate_slot_range_count!(@inner $count + 1; $( $rest )*); + }; + (@inner + $count:expr; + ) => { + const SLOT_RANGE_COUNT: usize = $count; + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! generate_lease_period_per_slot { + ( + $start:ident $( $rest:ident )* + ) => { + $crate::generate_lease_period_per_slot!(@inner 1; $( $rest )*); + }; + (@inner + $count:expr; + $start:ident $( $rest:ident )* + ) => { + $crate::generate_lease_period_per_slot!(@inner $count + 1; $( $rest )*); + }; + (@inner + $count:expr; + ) => { + const LEASE_PERIODS_PER_SLOT: usize = $count; + }; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn slot_range_4_works() { + generate_slot_range!(Zero(0), One(1), Two(2), Three(3)); + + assert_eq!(SlotRange::LEASE_PERIODS_PER_SLOT, 4); + // Sum over n from 0 - 4 + assert_eq!(SlotRange::SLOT_RANGE_COUNT, 10); + assert_eq!(SlotRange::new_bounded(0u32, 1u32, 2u32).unwrap(), SlotRange::OneTwo); + assert_eq!(SlotRange::new_bounded(5u32, 6u32, 7u32).unwrap(), SlotRange::OneTwo); + assert!(SlotRange::new_bounded(10u32, 6u32, 7u32).is_err()); + assert!(SlotRange::new_bounded(10u32, 16u32, 17u32).is_err()); + assert!(SlotRange::new_bounded(10u32, 11u32, 10u32).is_err()); + assert_eq!(SlotRange::TwoTwo.len(), 1); + assert_eq!(SlotRange::OneTwo.len(), 2); + assert_eq!(SlotRange::ZeroThree.len(), 4); + assert!(SlotRange::ZeroOne.intersects(SlotRange::OneThree)); + assert!(!SlotRange::ZeroOne.intersects(SlotRange::TwoThree)); + assert_eq!(SlotRange::ZeroZero.as_pair(), (0, 0)); + assert_eq!(SlotRange::OneThree.as_pair(), (1, 3)); + } + + #[test] + fn slot_range_8_works() { + generate_slot_range!(Zero(0), One(1), Two(2), Three(3), Four(4), Five(5), Six(6), Seven(7)); + + assert_eq!(SlotRange::LEASE_PERIODS_PER_SLOT, 8); + // Sum over n from 0 to 8 + assert_eq!(SlotRange::SLOT_RANGE_COUNT, 36); + assert_eq!(SlotRange::new_bounded(0u32, 1u32, 2u32).unwrap(), SlotRange::OneTwo); + assert_eq!(SlotRange::new_bounded(5u32, 6u32, 7u32).unwrap(), SlotRange::OneTwo); + assert!(SlotRange::new_bounded(10u32, 6u32, 7u32).is_err()); + // This one passes with slot range 8 + assert_eq!(SlotRange::new_bounded(10u32, 16u32, 17u32).unwrap(), SlotRange::SixSeven); + assert!(SlotRange::new_bounded(10u32, 17u32, 18u32).is_err()); + assert!(SlotRange::new_bounded(10u32, 20u32, 21u32).is_err()); + assert!(SlotRange::new_bounded(10u32, 11u32, 10u32).is_err()); + assert_eq!(SlotRange::TwoTwo.len(), 1); + assert_eq!(SlotRange::OneTwo.len(), 2); + assert_eq!(SlotRange::ZeroThree.len(), 4); + assert_eq!(SlotRange::ZeroSeven.len(), 8); + assert!(SlotRange::ZeroOne.intersects(SlotRange::OneThree)); + assert!(!SlotRange::ZeroOne.intersects(SlotRange::TwoThree)); + assert!(SlotRange::FiveSix.intersects(SlotRange::SixSeven)); + assert!(!SlotRange::ThreeFive.intersects(SlotRange::SixSeven)); + assert_eq!(SlotRange::ZeroZero.as_pair(), (0, 0)); + assert_eq!(SlotRange::OneThree.as_pair(), (1, 3)); + assert_eq!(SlotRange::SixSeven.as_pair(), (6, 7)); + } +} diff --git a/polkadot/runtime/common/src/auctions.rs b/polkadot/runtime/common/src/auctions.rs index ec88de800c1..a3abc5e11ce 100644 --- a/polkadot/runtime/common/src/auctions.rs +++ b/polkadot/runtime/common/src/auctions.rs @@ -27,12 +27,13 @@ use frame_support::{ }; use primitives::v1::Id as ParaId; use frame_system::{ensure_signed, ensure_root}; -use crate::slot_range::{SlotRange, SLOT_RANGE_COUNT}; +use crate::slot_range::SlotRange; use crate::traits::{Leaser, LeaseError, Auctioneer, Registrar}; use parity_scale_codec::Decode; type CurrencyOf<T> = <<T as Config>::Leaser as Leaser>::Currency; -type BalanceOf<T> = <<<T as Config>::Leaser as Leaser>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; +type BalanceOf<T> = + <<<T as Config>::Leaser as Leaser>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; pub trait WeightInfo { fn new_auction() -> Weight; @@ -84,7 +85,7 @@ pub type AuctionIndex = u32; type LeasePeriodOf<T> = <<T as Config>::Leaser as Leaser>::LeasePeriod; // Winning data type. This encodes the top bidders of each range together with their bid. type WinningData<T> = - [Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)>; SLOT_RANGE_COUNT]; + [Option<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>)>; SlotRange::SLOT_RANGE_COUNT]; // Winners data type. This encodes each of the final winners of a parachain auction, the parachain // index assigned to them, their winning bid and the range that they won. type WinnersData<T> = Vec<(<T as frame_system::Config>::AccountId, ParaId, BalanceOf<T>, SlotRange)>; @@ -563,19 +564,19 @@ impl<T: Config> Module<T> { ) -> WinnersData<T> { let winning_ranges = { let mut best_winners_ending_at: - [(Vec<SlotRange>, BalanceOf<T>); 4] = Default::default(); + [(Vec<SlotRange>, BalanceOf<T>); SlotRange::LEASE_PERIODS_PER_SLOT] = Default::default(); let best_bid = |range: SlotRange| { winning[range as u8 as usize].as_ref() .map(|(_, _, amount)| *amount * (range.len() as u32).into()) }; - for i in 0..4 { + for i in 0..SlotRange::LEASE_PERIODS_PER_SLOT { let r = SlotRange::new_bounded(0, 0, i as u32).expect("`i < 4`; qed"); if let Some(bid) = best_bid(r) { best_winners_ending_at[i] = (vec![r], bid); } for j in 0..i { let r = SlotRange::new_bounded(0, j as u32 + 1, i as u32) - .expect("`i < 4`; `j < i`; `j + 1 < 4`; qed"); + .expect("`i < LPPS`; `j < i`; `j + 1 < LPPS`; qed"); if let Some(mut bid) = best_bid(r) { bid += best_winners_ending_at[j].1; if bid > best_winners_ending_at[i].1 { @@ -590,8 +591,7 @@ impl<T: Config> Module<T> { } } } - let [_, _, _, (winning_ranges, _)] = best_winners_ending_at; - winning_ranges + best_winners_ending_at[SlotRange::LEASE_PERIODS_PER_SLOT - 1].0.clone() }; winning_ranges.into_iter().map(|range| { @@ -722,7 +722,10 @@ mod tests { }) } - fn deposit_held(para: ParaId, leaser: &Self::AccountId) -> <Self::Currency as Currency<Self::AccountId>>::Balance { + fn deposit_held( + para: ParaId, + leaser: &Self::AccountId + ) -> <Self::Currency as Currency<Self::AccountId>>::Balance { leases().iter() .filter_map(|((id, _period), data)| if id == ¶ && &data.leaser == leaser { Some(data.amount) } else { None } @@ -1546,7 +1549,7 @@ mod benchmarking { let auction_index = AuctionCounter::get(); let minimum_balance = CurrencyOf::<T>::minimum_balance(); - for n in 1 ..= SLOT_RANGE_COUNT as u32 { + for n in 1 ..= SlotRange::SLOT_RANGE_COUNT as u32 { let owner = account("owner", n, 0); let worst_validation_code = T::Registrar::worst_validation_code(); let worst_head_data = T::Registrar::worst_head_data(); @@ -1562,7 +1565,7 @@ mod benchmarking { T::Registrar::execute_pending_transitions(); - for n in 1 ..= SLOT_RANGE_COUNT as u32 { + for n in 1 ..= SlotRange::SLOT_RANGE_COUNT as u32 { let bidder = account("bidder", n, 0); CurrencyOf::<T>::make_free_balance_be(&bidder, BalanceOf::<T>::max_value()); diff --git a/polkadot/runtime/common/src/crowdloan.rs b/polkadot/runtime/common/src/crowdloan.rs index 3af668c43c3..adf699c9162 100644 --- a/polkadot/runtime/common/src/crowdloan.rs +++ b/polkadot/runtime/common/src/crowdloan.rs @@ -73,6 +73,7 @@ use sp_runtime::{ }, }; use crate::traits::{Registrar, Auctioneer}; +use crate::slot_range::SlotRange; use parity_scale_codec::{Encode, Decode}; use sp_std::vec::Vec; use primitives::v1::Id as ParaId; @@ -318,7 +319,9 @@ decl_module! { let depositor = ensure_signed(origin)?; ensure!(first_period <= last_period, Error::<T>::LastPeriodBeforeFirstPeriod); - let last_period_limit = first_period.checked_add(&3u32.into()).ok_or(Error::<T>::FirstPeriodTooFarInFuture)?; + let last_period_limit = first_period + .checked_add(&((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into()) + .ok_or(Error::<T>::FirstPeriodTooFarInFuture)?; ensure!(last_period <= last_period_limit, Error::<T>::LastPeriodTooFarInFuture); ensure!(end > <frame_system::Pallet<T>>::block_number(), Error::<T>::CannotEndInPast); let last_possible_win_date = (first_period.saturating_add(One::one())).saturating_mul(T::Auctioneer::lease_period()); @@ -1475,7 +1478,7 @@ mod benchmarking { let cap = BalanceOf::<T>::max_value(); let lease_period_index = T::Auctioneer::lease_period_index(); let first_period = lease_period_index; - let last_period = lease_period_index + 3u32.into(); + let last_period = lease_period_index + ((SlotRange::LEASE_PERIODS_PER_SLOT as u32) - 1).into(); let para_id = id.into(); let caller = account("fund_creator", id, 0); diff --git a/polkadot/runtime/common/src/lib.rs b/polkadot/runtime/common/src/lib.rs index 8cdab773d93..b0589ad0b72 100644 --- a/polkadot/runtime/common/src/lib.rs +++ b/polkadot/runtime/common/src/lib.rs @@ -19,7 +19,6 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod claims; -pub mod slot_range; pub mod slots; pub mod auctions; pub mod crowdloan; @@ -28,6 +27,7 @@ pub mod impls; pub mod mmr; pub mod paras_sudo_wrapper; pub mod paras_registrar; +pub mod slot_range; pub mod traits; pub mod xcm_sender; diff --git a/polkadot/runtime/common/src/slot_range.rs b/polkadot/runtime/common/src/slot_range.rs index b9751e18efb..31480a6aabe 100644 --- a/polkadot/runtime/common/src/slot_range.rs +++ b/polkadot/runtime/common/src/slot_range.rs @@ -17,139 +17,4 @@ //! The SlotRange struct which succinctly handles the ten values that //! represent all sub ranges between 0 and 3 inclusive. -use sp_std::{result, ops::Add, convert::{TryFrom, TryInto}}; -use sp_runtime::traits::CheckedSub; -use parity_scale_codec::{Encode, Decode}; - -/// Total number of possible sub ranges of slots. -pub const SLOT_RANGE_COUNT: usize = 10; - -/// A compactly represented sub-range from the series (0, 1, 2, 3). -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode)] -#[repr(u8)] -pub enum SlotRange { - /// Sub range from index 0 to index 0 inclusive. - ZeroZero = 0, - /// Sub range from index 0 to index 1 inclusive. - ZeroOne = 1, - /// Sub range from index 0 to index 2 inclusive. - ZeroTwo = 2, - /// Sub range from index 0 to index 3 inclusive. - ZeroThree = 3, - /// Sub range from index 1 to index 1 inclusive. - OneOne = 4, - /// Sub range from index 1 to index 2 inclusive. - OneTwo = 5, - /// Sub range from index 1 to index 3 inclusive. - OneThree = 6, - /// Sub range from index 2 to index 2 inclusive. - TwoTwo = 7, - /// Sub range from index 2 to index 3 inclusive. - TwoThree = 8, - /// Sub range from index 3 to index 3 inclusive. - ThreeThree = 9, // == SLOT_RANGE_COUNT - 1 -} - -#[cfg(feature = "std")] -impl std::fmt::Debug for SlotRange { - fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { - let p = self.as_pair(); - write!(fmt, "[{}..{}]", p.0, p.1) - } -} - -impl SlotRange { - pub fn new_bounded< - Index: Add<Output=Index> + CheckedSub + Copy + Ord + From<u32> + TryInto<u32> - >( - initial: Index, - first: Index, - last: Index - ) -> result::Result<Self, &'static str> { - if first > last || first < initial || last > initial + 3.into() { - return Err("Invalid range for this auction") - } - let count: u32 = last.checked_sub(&first) - .ok_or("range ends before it begins")? - .try_into() - .map_err(|_| "range too big")?; - let first: u32 = first.checked_sub(&initial) - .ok_or("range begins too early")? - .try_into() - .map_err(|_| "start too far")?; - match first { - 0 => match count { - 0 => Some(SlotRange::ZeroZero), - 1 => Some(SlotRange::ZeroOne), - 2 => Some(SlotRange::ZeroTwo), - 3 => Some(SlotRange::ZeroThree), - _ => None, - }, - 1 => match count { - 0 => Some(SlotRange::OneOne), - 1 => Some(SlotRange::OneTwo), - 2 => Some(SlotRange::OneThree), - _ => None - }, - 2 => match count { 0 => Some(SlotRange::TwoTwo), 1 => Some(SlotRange::TwoThree), _ => None }, - 3 => match count { 0 => Some(SlotRange::ThreeThree), _ => None }, - _ => return Err("range begins too late"), - }.ok_or("range ends too late") - } - - pub fn as_pair(&self) -> (u8, u8) { - match self { - SlotRange::ZeroZero => (0, 0), - SlotRange::ZeroOne => (0, 1), - SlotRange::ZeroTwo => (0, 2), - SlotRange::ZeroThree => (0, 3), - SlotRange::OneOne => (1, 1), - SlotRange::OneTwo => (1, 2), - SlotRange::OneThree => (1, 3), - SlotRange::TwoTwo => (2, 2), - SlotRange::TwoThree => (2, 3), - SlotRange::ThreeThree => (3, 3), - } - } - - pub fn intersects(&self, other: SlotRange) -> bool { - let a = self.as_pair(); - let b = other.as_pair(); - b.0 <= a.1 && a.0 <= b.1 -// == !(b.0 > a.1 || a.0 > b.1) - } - - pub fn len(&self) -> usize { - match self { - SlotRange::ZeroZero => 1, - SlotRange::ZeroOne => 2, - SlotRange::ZeroTwo => 3, - SlotRange::ZeroThree => 4, - SlotRange::OneOne => 1, - SlotRange::OneTwo => 2, - SlotRange::OneThree => 3, - SlotRange::TwoTwo => 1, - SlotRange::TwoThree => 2, - SlotRange::ThreeThree => 1, - } - } -} - -impl TryFrom<usize> for SlotRange { - type Error = (); - fn try_from(x: usize) -> Result<SlotRange, ()> { - Ok(match x { - 0 => SlotRange::ZeroZero, - 1 => SlotRange::ZeroOne, - 2 => SlotRange::ZeroTwo, - 3 => SlotRange::ZeroThree, - 4 => SlotRange::OneOne, - 5 => SlotRange::OneTwo, - 6 => SlotRange::OneThree, - 7 => SlotRange::TwoTwo, - 8 => SlotRange::TwoThree, - 9 => SlotRange::ThreeThree, - _ => return Err(()), - }) - } -} +slot_range_helper::generate_slot_range!(Zero(0), One(1), Two(2), Three(3)); -- GitLab