diff --git a/Cargo.lock b/Cargo.lock index b2805ebe8e..74f10bc6b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7465,7 +7465,6 @@ dependencies = [ "serde_json", "sidechain-domain", "sidechain-mc-hash", - "sidechain-slots", "sp-api", "sp-block-builder", "sp-block-participation", @@ -7539,7 +7538,6 @@ dependencies = [ "serde", "serde_json", "sidechain-domain", - "sidechain-slots", "sp-api", "sp-block-builder", "sp-block-participation", @@ -10717,23 +10715,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "sidechain-slots" -version = "1.8.0" -dependencies = [ - "envy", - "parity-scale-codec", - "proptest", - "scale-info", - "serde", - "sidechain-domain", - "sp-api", - "sp-blockchain", - "sp-consensus-slots", - "sp-core", - "sp-runtime", -] - [[package]] name = "signal-hook" version = "0.3.18" diff --git a/Cargo.toml b/Cargo.toml index 70fd80b784..1122712f45 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,7 +31,6 @@ members = [ "toolkit/sidechain/sidechain-mc-hash", "toolkit/sidechain/pallet", "toolkit/sidechain/rpc", - "toolkit/sidechain/sidechain-slots", "toolkit/sidechain/primitives", "toolkit/committee-selection/primitives", "toolkit/committee-selection/query", @@ -286,7 +285,6 @@ pallet-address-associations = { path = "toolkit/address-associations/pallet", de # sidechain core sidechain-domain = { path = "toolkit/sidechain/domain", default-features = false } -sidechain-slots = { path = "toolkit/sidechain/sidechain-slots", default-features = false } sidechain-mc-hash = { path = "toolkit/sidechain/sidechain-mc-hash", default-features = false } sp-sidechain = { path = "toolkit/sidechain/primitives", default-features = false } pallet-sidechain = { path = "toolkit/sidechain/pallet", default-features = false } diff --git a/changelog.md b/changelog.md index ce5622ee66..633c567994 100644 --- a/changelog.md +++ b/changelog.md @@ -62,6 +62,10 @@ Existing chains must add the `LegacyToV1Migration` migration to their runtime be * `sidechain-block-search` crate has been removed. All logic used by `sp-session-validator-management-query` has been moved to that crate directly. * `ScSlotNumber` type has been removed from `sidechain_domain` crate as its been obsolete after other changes +* `sidechain-slots` crate bas been removed after all other components have been decoupled from the notion of slots. +Legacy chains whose nodes used the `SlotApi` runtime API and the `sidechain_slots::runtime_api_client::slot_config` +function to read slot duration from the runtime should instead use their respective consensus algorithm's runtime API +(eg. `AuraApi` and `BabeApi`) or local environment. ## Fixed diff --git a/demo/node/Cargo.toml b/demo/node/Cargo.toml index 377f7bea72..7732628bc6 100644 --- a/demo/node/Cargo.toml +++ b/demo/node/Cargo.toml @@ -53,7 +53,6 @@ authority-selection-inherents = { workspace = true } frame-system = { workspace = true } pallet-transaction-payment = { workspace = true } sidechain-domain = { workspace = true } -sidechain-slots = { workspace = true, features = ["std", "serde"] } sp-sidechain = { workspace = true } pallet-sidechain-rpc = { workspace = true, features = ["legacy-slotapi-compat"] } pallet-session-validator-management = { workspace = true } diff --git a/demo/node/src/chain_spec.rs b/demo/node/src/chain_spec.rs index b240029cfe..619a352f99 100644 --- a/demo/node/src/chain_spec.rs +++ b/demo/node/src/chain_spec.rs @@ -5,7 +5,6 @@ use partner_chains_demo_runtime::{ }; use sc_service::ChainType; use sidechain_domain::ScEpochDuration; -use sidechain_slots::SlotsPerEpoch; use sp_core::{Pair, Public}; use sp_runtime::traits::{IdentifyAccount, Verify}; @@ -37,6 +36,8 @@ pub fn runtime_wasm() -> &'static [u8] { WASM_BINARY.expect("Runtime wasm not available") } +pub const DEFAULT_SLOTS_PER_EPOCH: u64 = 60; + /// Creates chain-spec according to the config obtained by wizards. /// [serde_json::Value] is returned instead of [sc_service::GenericChainSpec] in order to avoid /// GPL code in the toolkit. @@ -63,7 +64,7 @@ pub fn pc_create_chain_spec(config: &CreateChainSpecConfig) -> serd non_authority_keys: vec![], }, sidechain: config.pallet_sidechain_config(ScEpochDuration::from_millis( - SLOT_DURATION * u64::from(SlotsPerEpoch::default().0), + SLOT_DURATION * DEFAULT_SLOTS_PER_EPOCH, )), session_committee_management: config.pallet_session_validator_management_config(), governed_map: config.governed_map_config(), @@ -86,3 +87,10 @@ pub fn pc_create_chain_spec(config: &CreateChainSpecConfig) -> serd let chain_spec_str = chain_spec.as_json(raw).expect("Chain spec serialization can not fail"); serde_json::from_str(&chain_spec_str).unwrap() } + +pub(crate) fn read_slots_per_epoch_from_env() -> u64 { + std::env::var("SLOTS_PER_EPOCH") + .ok() + .and_then(|s| s.parse().ok()) + .unwrap_or(DEFAULT_SLOTS_PER_EPOCH) +} diff --git a/demo/node/src/inherent_data.rs b/demo/node/src/inherent_data.rs index aee3802f37..7d354308cf 100644 --- a/demo/node/src/inherent_data.rs +++ b/demo/node/src/inherent_data.rs @@ -13,7 +13,6 @@ use sidechain_domain::{ DelegatorKey, McBlockHash, ScEpochNumber, mainchain_epoch::MainchainEpochConfig, }; use sidechain_mc_hash::{McHashDataSource, McHashInherentDataProvider as McHashIDP}; -use sidechain_slots::ScSlotConfig; use sp_api::ProvideRuntimeApi; use sp_block_participation::{ BlockParticipationApi, @@ -81,16 +80,14 @@ where governed_map_data_source, bridge_data_source, } = self; - let CreateInherentDataConfig { mc_epoch_config, sc_slot_config, time_source } = config; + let CreateInherentDataConfig { mc_epoch_config, slot_duration, time_source, .. } = config; - let (slot, timestamp) = - timestamp_and_slot_cidp(sc_slot_config.slot_duration, time_source.clone()); + let (slot, timestamp) = timestamp_and_slot_cidp(*slot_duration, time_source.clone()); let parent_header = client.expect_header(parent_hash)?; // note: We pass slot start time to `McHashIDP` instead of timestamp for backward compatibility // with the old `McHashIDP` version that was slot-based. - let slot_start_timestamp = - Timestamp::new(sc_slot_config.slot_start_time(*slot).unix_millis()); + let slot_start_timestamp = config.slot_start_time(*slot); let mc_hash = McHashIDP::new_proposal( parent_header, mc_hash_data_source.as_ref(), @@ -100,7 +97,7 @@ where let ariadne_data_provider = AriadneIDP::new( client.as_ref(), - sc_slot_config.epoch_duration().as_millis() as u64, + config.sc_epoch_duration_millis, mc_epoch_config, parent_hash, (*timestamp).as_millis(), @@ -152,7 +149,7 @@ pub struct VerifierCIDP { impl CurrentSlotProvider for VerifierCIDP { fn slot(&self) -> Slot { - *timestamp_and_slot_cidp(self.config.slot_duration(), self.config.time_source.clone()).0 + *timestamp_and_slot_cidp(self.config.slot_duration, self.config.time_source.clone()).0 } } @@ -187,19 +184,16 @@ where governed_map_data_source, bridge_data_source, } = self; - let CreateInherentDataConfig { mc_epoch_config, sc_slot_config, .. } = config; + let CreateInherentDataConfig { mc_epoch_config, .. } = config; // note: Because it's not exposed during block verification, we are approximating the block // timestamp by the starting timestamp of the slots. This is also needed for backward compatibility // of [McHashIDP] for chains that used the old slot-based version of it. - let timestamp = TimestampIDP::new(Timestamp::new( - sc_slot_config.slot_start_time(verified_block_slot).unix_millis(), - )); + let timestamp = TimestampIDP::new(config.slot_start_time(verified_block_slot)); let parent_header = client.expect_header(parent_hash)?; let parent_slot = slot_from_predigest(&parent_header)?; - let parent_slot_timestamp = parent_slot - .map(|slot| Timestamp::new(sc_slot_config.slot_start_time(slot).unix_millis())); + let parent_slot_timestamp = parent_slot.map(|slot| config.slot_start_time(slot)); let mc_state_reference = McHashIDP::new_verification( parent_header, @@ -212,7 +206,7 @@ where let ariadne_data_provider = AriadneIDP::new( client.as_ref(), - sc_slot_config.epoch_duration().as_millis() as u64, + config.sc_epoch_duration_millis, mc_epoch_config, parent_hash, (*timestamp).as_millis(), @@ -263,16 +257,16 @@ pub fn slot_from_predigest( } #[derive(new, Clone)] -pub(crate) struct CreateInherentDataConfig { +pub struct CreateInherentDataConfig { pub mc_epoch_config: MainchainEpochConfig, - // TODO ETCM-4079 make sure that this struct can be instantiated only if sidechain epoch duration is divisible by slot_duration - pub sc_slot_config: ScSlotConfig, + pub slot_duration: SlotDuration, + pub sc_epoch_duration_millis: u64, pub time_source: Arc, } impl CreateInherentDataConfig { - pub fn slot_duration(&self) -> SlotDuration { - self.sc_slot_config.slot_duration + pub fn slot_start_time(&self, slot: Slot) -> Timestamp { + Timestamp::new(self.slot_duration.as_millis() * u64::from(slot)) } } diff --git a/demo/node/src/rpc.rs b/demo/node/src/rpc.rs index c5923f1355..46f06e8f7b 100644 --- a/demo/node/src/rpc.rs +++ b/demo/node/src/rpc.rs @@ -74,7 +74,6 @@ where C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BlockBuilder, C::Api: sp_consensus_aura::AuraApi, - C::Api: sidechain_slots::SlotApi, C::Api: sp_sidechain::GetGenesisUtxo, C::Api: sp_sidechain::GetSidechainStatus, C::Api: sp_block_producer_fees::BlockProducerFeesApi, diff --git a/demo/node/src/service.rs b/demo/node/src/service.rs index a9d5795c1e..90bc4c436b 100644 --- a/demo/node/src/service.rs +++ b/demo/node/src/service.rs @@ -16,9 +16,13 @@ use sc_telemetry::{Telemetry, TelemetryWorker}; use sc_transaction_pool_api::OffchainTransactionPoolFactory; use sidechain_domain::mainchain_epoch::MainchainEpochConfig; use sidechain_mc_hash::McHashInherentDigest; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_consensus_aura::AuraApi; use sp_consensus_aura::sr25519::AuthorityPair as AuraPair; use sp_partner_chains_consensus_aura::block_proposal::PartnerChainsProposerFactory; use sp_runtime::traits::Block as BlockT; +use sp_sidechain::GetEpochDurationApi; use std::{sync::Arc, time::Duration}; use time_source::SystemTimeSource; use tokio::task; @@ -76,6 +80,7 @@ pub fn new_partial( Option, DataSources, Option, + CreateInherentDataConfig, ), >, ServiceError, @@ -134,13 +139,24 @@ pub fn new_partial( telemetry.as_ref().map(|x| x.handle()), )?; - let sc_slot_config = sidechain_slots::runtime_api_client::slot_config(&*client) - .map_err(sp_blockchain::Error::from)?; + let slot_duration = client + .runtime_api() + .slot_duration(client.info().best_hash) + .expect("Aura slot duration must be configured in the runtime"); + let sc_epoch_duration_millis = client + .runtime_api() + .get_epoch_duration_millis(client.info().best_hash) + .expect("Epoch duration must be configured in the runtime"); let time_source = Arc::new(SystemTimeSource); let epoch_config = MainchainEpochConfig::read_from_env() .map_err(|err| ServiceError::Application(err.into()))?; - let inherent_config = CreateInherentDataConfig::new(epoch_config, sc_slot_config, time_source); + let inherent_config = CreateInherentDataConfig::new( + epoch_config, + slot_duration, + sc_epoch_duration_millis, + time_source, + ); let import_queue = partner_chains_aura_import_queue::import_queue::< AuraPair, @@ -155,7 +171,7 @@ pub fn new_partial( justification_import: Some(Box::new(grandpa_block_import.clone())), client: client.clone(), create_inherent_data_providers: VerifierCIDP::new( - inherent_config, + inherent_config.clone(), client.clone(), data_sources.mc_hash.clone(), data_sources.authority_selection.clone(), @@ -178,7 +194,14 @@ pub fn new_partial( keystore_container, select_chain, transaction_pool, - other: (grandpa_block_import, grandpa_link, telemetry, data_sources, mc_follower_metrics), + other: ( + grandpa_block_import, + grandpa_link, + telemetry, + data_sources, + mc_follower_metrics, + inherent_config, + ), }) } @@ -210,7 +233,7 @@ pub async fn new_full_base = PartnerChainsProposerFactory::new(basic_authorship_proposer_factory); - let sc_slot_config = sidechain_slots::runtime_api_client::slot_config(&*client) - .map_err(sp_blockchain::Error::from)?; - let time_source = Arc::new(SystemTimeSource); - let mc_epoch_config = MainchainEpochConfig::read_from_env() - .map_err(|err| ServiceError::Application(err.into()))?; - let inherent_config = - CreateInherentDataConfig::new(mc_epoch_config, sc_slot_config.clone(), time_source); let aura = sc_partner_chains_consensus_aura::start_aura::< AuraPair, _, @@ -338,7 +354,7 @@ pub async fn new_full_base(StartAuraParams { - slot_duration: sc_slot_config.slot_duration, + slot_duration: inherent_config.slot_duration, client: client.clone(), select_chain, block_import, diff --git a/demo/node/src/staging.rs b/demo/node/src/staging.rs index 55fd092bc2..9905a0145c 100644 --- a/demo/node/src/staging.rs +++ b/demo/node/src/staging.rs @@ -153,8 +153,7 @@ pub fn staging_genesis( sidechain: SidechainConfig { genesis_utxo, epoch_duration: ScEpochDuration::from_millis( - SLOT_DURATION - * u64::from(sidechain_slots::SlotsPerEpoch::read_from_env().unwrap().0), + SLOT_DURATION * read_slots_per_epoch_from_env(), ), ..Default::default() }, diff --git a/demo/node/src/template_chain_spec.rs b/demo/node/src/template_chain_spec.rs index 0b9a113dc9..287b922734 100644 --- a/demo/node/src/template_chain_spec.rs +++ b/demo/node/src/template_chain_spec.rs @@ -35,8 +35,7 @@ pub fn chain_spec() -> Result { sidechain: SidechainConfig { genesis_utxo, epoch_duration: ScEpochDuration::from_millis( - SLOT_DURATION - * u64::from(sidechain_slots::SlotsPerEpoch::read_from_env().unwrap().0), + SLOT_DURATION * read_slots_per_epoch_from_env(), ), ..Default::default() }, diff --git a/demo/node/src/testnet.rs b/demo/node/src/testnet.rs index e1bdccdf8b..f3e3c244ed 100644 --- a/demo/node/src/testnet.rs +++ b/demo/node/src/testnet.rs @@ -210,8 +210,7 @@ pub fn testnet_genesis( sidechain: SidechainConfig { genesis_utxo, epoch_duration: ScEpochDuration::from_millis( - SLOT_DURATION - * u64::from(sidechain_slots::SlotsPerEpoch::read_from_env().unwrap().0), + SLOT_DURATION * read_slots_per_epoch_from_env(), ), ..Default::default() }, diff --git a/demo/node/src/tests/mock.rs b/demo/node/src/tests/mock.rs index 503fdae851..04d53fe332 100644 --- a/demo/node/src/tests/mock.rs +++ b/demo/node/src/tests/mock.rs @@ -6,8 +6,8 @@ use partner_chains_demo_runtime::{BlockAuthor, CrossChainPublic}; use sc_consensus_aura::SlotDuration; use sidechain_domain::mainchain_epoch::MainchainEpochConfig; use sidechain_domain::*; -use sidechain_slots::{ScSlotConfig, Slot, SlotsPerEpoch}; use sp_block_participation::{BlockProducerParticipationData, BlockProductionData}; +use sp_consensus_aura::Slot; use sp_core::{ ecdsa, offchain::{Duration, Timestamp}, @@ -21,23 +21,16 @@ pub fn mock_genesis_utxo() -> UtxoId { } } -pub fn test_slot_config() -> ScSlotConfig { - ScSlotConfig { - slots_per_epoch: SlotsPerEpoch(10), - slot_duration: SlotDuration::from_millis(1000), - } -} +const SLOT_DURATION: u64 = 1000; +const SLOTS_PER_EPOCH: u64 = 10; +const SC_EPOCH_DURATION_MILLIS: u64 = SLOT_DURATION * SLOTS_PER_EPOCH; +const MC_EPOCH_DURATION_MILLIS: u64 = SC_EPOCH_DURATION_MILLIS * 10; pub fn test_epoch_config() -> MainchainEpochConfig { - let sc_slot_config = test_slot_config(); MainchainEpochConfig { first_epoch_timestamp_millis: Timestamp::from_unix_millis(0), first_epoch_number: 0, - epoch_duration_millis: Duration::from_millis( - u64::from(sc_slot_config.slots_per_epoch.0) - * sc_slot_config.slot_duration.as_millis() - * 10, - ), + epoch_duration_millis: Duration::from_millis(MC_EPOCH_DURATION_MILLIS), first_slot_number: 0, slot_duration_millis: Duration::from_millis(1000), } @@ -62,7 +55,8 @@ pub fn block_participation_data() -> BlockProductionData CreateInherentDataConfig { CreateInherentDataConfig { mc_epoch_config: test_epoch_config(), - sc_slot_config: test_slot_config(), + slot_duration: SlotDuration::from_millis(SLOT_DURATION), + sc_epoch_duration_millis: SC_EPOCH_DURATION_MILLIS, time_source: Arc::new(time_source::MockedTimeSource { current_time_millis: 30000 }), } } diff --git a/demo/node/src/tests/runtime_api_mock.rs b/demo/node/src/tests/runtime_api_mock.rs index 6e7539b013..8d43dffe2c 100644 --- a/demo/node/src/tests/runtime_api_mock.rs +++ b/demo/node/src/tests/runtime_api_mock.rs @@ -4,9 +4,9 @@ use partner_chains_demo_runtime::opaque::SessionKeys; use partner_chains_demo_runtime::{BlockAuthor, CrossChainPublic}; use sidechain_domain::*; use sidechain_mc_hash::McHashInherentDigest; -use sidechain_slots::Slot; use sp_api::{ApiRef, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; +use sp_consensus_aura::Slot; use sp_core::ecdsa; use sp_governed_map::MainChainScriptsV1; use sp_inherents::InherentIdentifier; diff --git a/demo/runtime/Cargo.toml b/demo/runtime/Cargo.toml index b519b0fe6a..baae05af22 100644 --- a/demo/runtime/Cargo.toml +++ b/demo/runtime/Cargo.toml @@ -78,7 +78,6 @@ pallet-sidechain = { workspace = true } pallet-session-validator-management = { workspace = true } sp-session-validator-management = { workspace = true, features = ["serde"] } sidechain-domain = { workspace = true, features = ["serde", "app-crypto"] } -sidechain-slots = { workspace = true } pallet-address-associations = { workspace = true } pallet-block-producer-metadata = { workspace = true } sp-block-producer-metadata = { workspace = true } @@ -157,7 +156,6 @@ std = [ "sp-weights/std", "substrate-wasm-builder", "pallet-sidechain/std", - "sidechain-slots/std", "sidechain-domain/std", "sp-inherents/std", "pallet-block-participation/std", diff --git a/demo/runtime/src/lib.rs b/demo/runtime/src/lib.rs index a1baeb82e0..836837a25c 100644 --- a/demo/runtime/src/lib.rs +++ b/demo/runtime/src/lib.rs @@ -41,10 +41,10 @@ use sidechain_domain::{ CrossChainPublicKey, DelegatorKey, MainchainKeyHash, PermissionedCandidateData, RegistrationData, ScEpochNumber, StakeDelegation, StakePoolPublicKey, UtxoId, }; -use sidechain_slots::{Slot, SlotsPerEpoch}; use sp_api::impl_runtime_apis; use sp_block_participation::AsCardanoSPO; use sp_consensus_aura::sr25519::AuthorityId as AuraId; +use sp_consensus_slots::Slot; use sp_core::{OpaqueMetadata, crypto::KeyTypeId}; use sp_governed_map::MainChainScriptsV1; use sp_inherents::InherentIdentifier; @@ -1056,15 +1056,6 @@ impl_runtime_apis! { } } - impl sidechain_slots::SlotApi for Runtime { - fn slot_config() -> sidechain_slots::ScSlotConfig { - sidechain_slots::ScSlotConfig { - slots_per_epoch: SlotsPerEpoch((Sidechain::epoch_duration_millis() / SLOT_DURATION) as u32), - slot_duration: >::slot_duration() - } - } - } - impl sp_block_producer_metadata::BlockProducerMetadataApi for Runtime { fn get_metadata_for( diff --git a/dev/local-environment/envrc b/dev/local-environment/envrc index d45a6783f2..96b6e397bb 100644 --- a/dev/local-environment/envrc +++ b/dev/local-environment/envrc @@ -61,7 +61,7 @@ fi SPEC_FILE="$RUNTIME_VALUES/chain-spec.json" if [ -f "$SPEC_FILE" ]; then - export SLOTS_PER_EPOCH=$(jq -r ".genesis.runtimeGenesis.config.sidechain.epochDuration / ${MC__SLOT_DURATION_MILLIS:=6000}" "$SPEC_FILE") + export SLOTS_PER_EPOCH=$(jq -r ".genesis.runtimeGenesis.config.sidechain.epochDuration / 6000" "$SPEC_FILE") else echo "Partner Chain chain spec file $SPEC_FILE not present" fi diff --git a/toolkit/sidechain/sidechain-slots/Cargo.toml b/toolkit/sidechain/sidechain-slots/Cargo.toml deleted file mode 100644 index d680180475..0000000000 --- a/toolkit/sidechain/sidechain-slots/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -name = "sidechain-slots" -version.workspace = true -license = "Apache-2.0" -description = "Sidechain slots and epoch calcuations" -authors.workspace = true -edition.workspace = true -homepage.workspace = true -repository.workspace = true -readme = "README.md" - -[lints] -workspace = true - -[dependencies] -parity-scale-codec = { workspace = true } -scale-info = { workspace = true } -sidechain-domain = { workspace = true } -sp-api = { workspace = true } -sp-blockchain = { workspace = true, optional = true } -sp-runtime = { workspace = true } -sp-core = { workspace = true, features = ["serde"]} -sp-consensus-slots = { workspace = true } -serde = { workspace = true, optional = true } -envy = { workspace = true, optional = true } - -[dev-dependencies] -proptest = { workspace = true } - -[features] -default = ['std'] -std = [ - 'sidechain-domain/std', - 'scale-info/std', - 'sp-api/std', - 'sp-blockchain', - 'sp-core/std', - 'sp-consensus-slots/std', - 'envy', -] -serde = [ - "dep:serde", - "scale-info/serde", - "sp-core/serde", -] diff --git a/toolkit/sidechain/sidechain-slots/README.md b/toolkit/sidechain/sidechain-slots/README.md deleted file mode 100644 index 77f7e833e5..0000000000 --- a/toolkit/sidechain/sidechain-slots/README.md +++ /dev/null @@ -1,3 +0,0 @@ -Logic to manipulate sidechain slots and epochs - -License: Apache-2.0 diff --git a/toolkit/sidechain/sidechain-slots/src/lib.rs b/toolkit/sidechain/sidechain-slots/src/lib.rs deleted file mode 100644 index cf15f86230..0000000000 --- a/toolkit/sidechain/sidechain-slots/src/lib.rs +++ /dev/null @@ -1,171 +0,0 @@ -//! This crate provides types and logic for handling Partner Chain slots. -//! -//! Partner Chain slots are grouped into epochs of equal length. -#![cfg_attr(not(feature = "std"), no_std)] -#![deny(missing_docs)] - -#[cfg(feature = "std")] -pub mod runtime_api_client; - -use core::{ops::Rem, time::Duration}; -use parity_scale_codec::{Decode, Encode, MaxEncodedLen}; -use scale_info::TypeInfo; -#[cfg(feature = "serde")] -use serde::{Deserialize, Serialize}; -use sidechain_domain::ScEpochNumber; -pub use sp_consensus_slots::{Slot, SlotDuration}; -use sp_core::offchain::Timestamp; - -/// Number of slots in each Partner Chain epoch -/// -/// This value should satisfy the following property: -/// > `main_chain_epoch_duration` % (`slots_per_epoch` * `slot_duration`) == 0 -/// that is, Cardano main chain's epoch boundaries should always coincide with -/// a Partner Chain epoch boundary, or in other words PC epochs should perfectly -/// cover a Cardano epoch. -#[derive(Clone, Copy, Debug, Encode, Decode, TypeInfo, MaxEncodedLen)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct SlotsPerEpoch(pub u32); - -/// Default number of slots per epoch. -/// -/// This value is used by [SlotsPerEpoch::read_from_env] when no other value is set in the environment. -pub fn default_slots_per_epoch() -> u32 { - 60 -} - -impl Default for SlotsPerEpoch { - /// Set to 60 to maintain backwards compatibility with existing chains. - fn default() -> Self { - SlotsPerEpoch(60) - } -} - -impl SlotsPerEpoch { - /// Reads [SlotsPerEpoch] from the environment variable `SLOTS_PER_EPOCH` with default. - #[cfg(all(feature = "std", feature = "serde"))] - pub fn read_from_env() -> Result { - #[derive(Serialize, Deserialize)] - struct SlotsPerEpochEnvConfig { - #[serde(default = "default_slots_per_epoch")] - slots_per_epoch: u32, - } - - let raw = envy::from_env::()?; - Ok(Self(raw.slots_per_epoch)) - } - - /// Returns the epoch number of `slot` - pub fn epoch_number(&self, slot: Slot) -> ScEpochNumber { - epoch_number(slot, self.0) - } - - /// Returns the number of first slot in `epoch` - pub fn first_slot_number(&self, epoch: ScEpochNumber) -> Slot { - first_slot_number(epoch, self.0) - } - - /// Returns the number of `slot` within its epoch - pub fn slot_number_in_epoch(&self, slot: Slot) -> u32 { - slot_number_in_epoch(slot, self.0) - } -} - -/// Slot and epoch configuration for a Partner Chain -#[derive(Clone, Debug, Encode, Decode, TypeInfo)] -pub struct ScSlotConfig { - /// Number of slots per Partner Chain epoch - pub slots_per_epoch: SlotsPerEpoch, - /// Duration of a single Partner Chain slot - pub slot_duration: SlotDuration, -} - -impl ScSlotConfig { - #[cfg(feature = "std")] - /// Returns epoch duration - pub fn epoch_duration(&self) -> Duration { - self.slot_duration.as_duration() * self.slots_per_epoch.0 - } - - /// Returns the epoch number of `slot` - pub fn epoch_number(&self, slot: Slot) -> ScEpochNumber { - self.slots_per_epoch.epoch_number(slot) - } - - /// Returns the number of first slot of `epoch` - pub fn first_slot_number(&self, epoch: ScEpochNumber) -> Slot { - self.slots_per_epoch.first_slot_number(epoch) - } - - /// Returns the slot number that contains `timestamp` - pub fn slot_from_timestamp(&self, timestamp: u64) -> Slot { - Slot::from_timestamp(timestamp.into(), self.slot_duration) - } - - /// Returns the start timestamp of `slot` - pub fn slot_start_time(&self, slot: Slot) -> Timestamp { - Timestamp::from_unix_millis(self.slot_duration.as_millis() * u64::from(slot)) - } - - /// Returns the start timestamp of `epoch` - pub fn epoch_start_time(&self, epoch: ScEpochNumber) -> Option { - self.first_slot_number(epoch) - .timestamp(self.slot_duration) - .map(|s| Timestamp::from_unix_millis(s.as_millis())) - } -} - -/// Returns the epoch number for `slot` given `slots_per_epoch` -pub fn epoch_number(slot: Slot, slots_per_epoch: u32) -> ScEpochNumber { - ScEpochNumber(*slot / u64::from(slots_per_epoch)) -} - -/// Get the first slot number of the epoch `epoch` -pub fn first_slot_number(epoch: ScEpochNumber, slots_per_epoch: u32) -> Slot { - Slot::from(epoch.0 * slots_per_epoch as u64) -} - -/// Returns the number of `slot` within its epoch given `slots_per_epoch` -pub fn slot_number_in_epoch(slot: Slot, slots_per_epoch: u32) -> u32 { - u32::try_from(slot.rem(u64::from(slots_per_epoch))) - .expect("slots_per_epoch is u32, thus any modulo reminder of it is also u32") -} - -/// Checks whether `slot` is the last slot of its epoch given `slots_per_epoch` -pub fn is_last_slot_of_an_epoch(slot: Slot, slots_per_epoch: u32) -> bool { - slot_number_in_epoch(slot, slots_per_epoch) == slots_per_epoch - 1 -} - -/// Error type returnes by epoch and slot handling functions in this crate -pub enum Error { - /// Indicates that an integer overflow occured during epoch calculation - OverflowError, -} - -sp_api::decl_runtime_apis! { - /// Runtime API serving slot configuration - pub trait SlotApi { - /// Returns the current slot configuration - fn slot_config() -> ScSlotConfig; - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::prelude::*; - - prop_compose! { - fn arb_slot()(slot_number in 0..u64::MAX) -> Slot { - Slot::from(slot_number) - } - } - - proptest! { - #[test] - fn slot_number_is_slot_modulo_slots_per_epoch(slot in arb_slot(), slots_per_epoch in 1..u32::MAX) { - let expected =u32::try_from(*slot % u64::from(slots_per_epoch)).unwrap(); - assert_eq!(expected, slot_number_in_epoch(slot, slots_per_epoch)) - } - } -} diff --git a/toolkit/sidechain/sidechain-slots/src/runtime_api_client.rs b/toolkit/sidechain/sidechain-slots/src/runtime_api_client.rs deleted file mode 100644 index ea28a7bb3b..0000000000 --- a/toolkit/sidechain/sidechain-slots/src/runtime_api_client.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! Module providing helper functions for interacting with [SlotApi] - -use crate::{ScSlotConfig, SlotApi}; -use sp_api::{ApiError, ProvideRuntimeApi}; -use sp_blockchain::HeaderBackend; -use sp_runtime::traits::Block as BlockT; - -/// Retrieves the slot configuration using runtime API -pub fn slot_config(client: &C) -> Result -where - B: BlockT, - C: ProvideRuntimeApi, - C: HeaderBackend, - C::Api: SlotApi, -{ - client.runtime_api().slot_config(client.info().best_hash) -}