Skip to content

move configs to genesis #870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ members = [
"toolkit/utils/db-sync-sqlx",
"toolkit/governed-map/primitives",
"toolkit/governed-map/pallet",
"toolkit/cardano-config/pallet",
]
resolver = "2"

Expand Down Expand Up @@ -329,5 +330,8 @@ partner-chains-mock-data-sources = { path = "toolkit/data-sources/mock", default
sp-governed-map = { path = "toolkit/governed-map/primitives", default-features = false }
pallet-governed-map = { path = "toolkit/governed-map/pallet", default-features = false }

# Cardano Config
pallet-cardano-config = { path = "toolkit/cardano-config/pallet", default-features = false }

# demo node
partner-chains-demo-runtime = { path = "demo/runtime" }
52 changes: 52 additions & 0 deletions toolkit/cardano-config/pallet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
[package]
name = "pallet-cardano-config"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
repository.workspace = true
version.workspace = true
license = "Apache-2.0"
description = "Pallet for storing Cardano configuration parameters in runtime storage"

[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]

[lints]
workspace = true

[dependencies]
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
parity-scale-codec = { workspace = true }
scale-info = { workspace = true }
sidechain-domain = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }

[dev-dependencies]
pretty_assertions = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }

[features]
default = ["std"]
std = [
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
"log/std",
"parity-scale-codec/std",
"scale-info/std",
"sidechain-domain/std",
"sp-core/std",
"sp-runtime/std"
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
mock = []
51 changes: 51 additions & 0 deletions toolkit/cardano-config/pallet/src/benchmarking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//! Benchmarking setup for pallet-cardano-config

#![cfg(feature = "runtime-benchmarks")]

use super::*;
use frame_benchmarking::v2::*;
use frame_support::{assert_ok, traits::Get};
use sidechain_domain::{CardanoConfig, MainchainEpochConfig};
use sp_core::offchain::{Duration, Timestamp};

/// Helper trait for benchmarking
pub trait BenchmarkHelper<T: Config> {
/// Returns a sample CardanoConfig for benchmarking
fn cardano_config() -> CardanoConfig;
}

impl<T: Config> BenchmarkHelper<T> for () {
fn cardano_config() -> CardanoConfig {
CardanoConfig {
epoch_config: MainchainEpochConfig {
epoch_duration_millis: Duration::from_millis(432000000), // 5 days
slot_duration_millis: Duration::from_millis(1000), // 1 second
first_epoch_timestamp_millis: Timestamp::from_unix_millis(1596059091000),
first_epoch_number: 208,
first_slot_number: 4492800,
},
cardano_security_parameter: 432,
cardano_active_slots_coeff: 0.05,
}
}
}

#[benchmarks]
mod benchmarks {
use super::*;

#[benchmark]
fn set_cardano_config() {
let config = T::BenchmarkHelper::cardano_config();

#[extrinsic_call]
_(RawOrigin::Root, config.clone());

assert_eq!(CardanoConfiguration::<T>::get(), Some(config.clone()));
assert_eq!(MainchainEpochConfiguration::<T>::get(), Some(config.epoch_config));
assert_eq!(CardanoSecurityParameter::<T>::get(), Some(config.cardano_security_parameter));
assert_eq!(CardanoActiveSlotsCoeff::<T>::get(), Some(config.cardano_active_slots_coeff));
}

impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
}
212 changes: 212 additions & 0 deletions toolkit/cardano-config/pallet/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
//! Pallet for storing Cardano configuration parameters in runtime storage.
//!
//! # Purpose of this pallet
//!
//! This pallet stores Cardano configuration parameters that are essential for Partner Chain operation,
//! including mainchain epoch configuration and consensus parameters. By storing these parameters in
//! runtime storage rather than relying on environment variables, we ensure all nodes have consistent
//! configuration as part of the chain state.

#![cfg_attr(not(feature = "std"), no_std)]
#![deny(missing_docs)]

extern crate alloc;

pub use pallet::*;

use crate::weights::WeightInfo;
use core::marker::PhantomData;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
use sidechain_domain::{
cardano_config::CardanoConfig,
mainchain_epoch::MainchainEpochConfig,
};
use sp_runtime::Perbill;
use sp_core::offchain::{Duration, Timestamp};

pub mod weights;

#[cfg(test)]
mod tests;

#[cfg(test)]
mod mock;

#[cfg(feature = "runtime-benchmarks")]
pub mod benchmarking;

#[frame_support::pallet]
pub mod pallet {
use super::*;

#[pallet::pallet]
pub struct Pallet<T>(_);

/// Current pallet version
pub const PALLET_VERSION: u32 = 1;

#[pallet::config]
pub trait Config: frame_system::Config {
/// Weight functions for the pallet's extrinsics
type WeightInfo: weights::WeightInfo;

/// Helper functions required by the pallet's benchmarks to construct realistic input data.
#[cfg(feature = "runtime-benchmarks")]
type BenchmarkHelper: crate::benchmarking::BenchmarkHelper<Self>;
}

/// Error type used by this pallet's extrinsics
#[pallet::error]
pub enum Error<T> {
/// Configuration has already been set and cannot be changed
ConfigurationAlreadySet,
}

/// Stores the epoch duration in milliseconds
#[pallet::storage]
#[pallet::getter(fn epoch_duration_millis)]
pub type EpochDurationMillis<T: Config> = StorageValue<_, u64, OptionQuery>;

/// Stores the slot duration in milliseconds
#[pallet::storage]
#[pallet::getter(fn slot_duration_millis)]
pub type SlotDurationMillis<T: Config> = StorageValue<_, u64, OptionQuery>;

/// Stores the first epoch timestamp in milliseconds
#[pallet::storage]
#[pallet::getter(fn first_epoch_timestamp_millis)]
pub type FirstEpochTimestampMillis<T: Config> = StorageValue<_, u64, OptionQuery>;

/// Stores the first epoch number
#[pallet::storage]
#[pallet::getter(fn first_epoch_number)]
pub type FirstEpochNumber<T: Config> = StorageValue<_, u32, OptionQuery>;

/// Stores the first slot number
#[pallet::storage]
#[pallet::getter(fn first_slot_number)]
pub type FirstSlotNumber<T: Config> = StorageValue<_, u64, OptionQuery>;

/// Stores the Cardano security parameter (k)
#[pallet::storage]
#[pallet::getter(fn cardano_security_parameter)]
pub type CardanoSecurityParameter<T: Config> = StorageValue<_, u32, OptionQuery>;

/// Stores the Cardano active slots coefficient (f) as parts per billion
#[pallet::storage]
#[pallet::getter(fn cardano_active_slots_coeff)]
pub type CardanoActiveSlotsCoeff<T: Config> = StorageValue<_, Perbill, OptionQuery>;

#[pallet::genesis_config]
#[derive(frame_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
/// Initial Cardano configuration.
pub cardano_config: Option<CardanoConfig>,
/// Phantom data marker
pub _marker: PhantomData<T>,
}

#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
if let Some(config) = &self.cardano_config {
EpochDurationMillis::<T>::put(config.epoch_config.epoch_duration_millis.as_millis());
SlotDurationMillis::<T>::put(config.epoch_config.slot_duration_millis.as_millis());
FirstEpochTimestampMillis::<T>::put(config.epoch_config.first_epoch_timestamp_millis.as_unix_millis());
FirstEpochNumber::<T>::put(config.epoch_config.first_epoch_number);
FirstSlotNumber::<T>::put(config.epoch_config.first_slot_number);
CardanoSecurityParameter::<T>::put(config.cardano_security_parameter);
CardanoActiveSlotsCoeff::<T>::put(Perbill::from_float(config.cardano_active_slots_coeff as f64));
}
}
}

#[pallet::call]
impl<T: Config> Pallet<T> {
/// Set the Cardano configuration. This can only be called once.
#[pallet::call_index(0)]
#[pallet::weight(T::WeightInfo::set_cardano_config())]
pub fn set_cardano_config(
origin: OriginFor<T>,
config: CardanoConfig,
) -> DispatchResult {
ensure_root(origin)?;

ensure!(
!Self::is_configured(),
Error::<T>::ConfigurationAlreadySet
);

EpochDurationMillis::<T>::put(config.epoch_config.epoch_duration_millis.0);
SlotDurationMillis::<T>::put(config.epoch_config.slot_duration_millis.0);
FirstEpochTimestampMillis::<T>::put(config.epoch_config.first_epoch_timestamp_millis.0);
FirstEpochNumber::<T>::put(config.epoch_config.first_epoch_number);
FirstSlotNumber::<T>::put(config.epoch_config.first_slot_number);
CardanoSecurityParameter::<T>::put(config.cardano_security_parameter);
CardanoActiveSlotsCoeff::<T>::put(Perbill::from_float(config.cardano_active_slots_coeff));

log::info!("🔧 Cardano configuration set");
Ok(())
}
}

impl<T: Config> Pallet<T> {
/// Returns the complete Cardano configuration
pub fn get_cardano_config() -> Option<CardanoConfig> {
let epoch_config = Self::get_mainchain_epoch_config()?;
let security_parameter = CardanoSecurityParameter::<T>::get()?;
let active_slots_coeff = CardanoActiveSlotsCoeff::<T>::get()?;

Some(CardanoConfig {
epoch_config,
cardano_security_parameter: security_parameter,
cardano_active_slots_coeff: active_slots_coeff.deconstruct() as f32 / 1_000_000_000.0,
})
}

/// Returns the mainchain epoch configuration
pub fn get_mainchain_epoch_config() -> Option<MainchainEpochConfig> {
let epoch_duration_millis = EpochDurationMillis::<T>::get()?;
let slot_duration_millis = SlotDurationMillis::<T>::get()?;
let first_epoch_timestamp_millis = FirstEpochTimestampMillis::<T>::get()?;
let first_epoch_number = FirstEpochNumber::<T>::get()?;
let first_slot_number = FirstSlotNumber::<T>::get()?;

Some(MainchainEpochConfig {
epoch_duration_millis: Duration::from_millis(epoch_duration_millis),
slot_duration_millis: Duration::from_millis(slot_duration_millis),
first_epoch_timestamp_millis: Timestamp::from_unix_millis(first_epoch_timestamp_millis),
first_epoch_number,
first_slot_number,
})
}

/// Returns the Cardano security parameter (k)
pub fn get_cardano_security_parameter() -> Option<u32> {
CardanoSecurityParameter::<T>::get()
}

/// Returns the Cardano active slots coefficient (f)
pub fn get_cardano_active_slots_coeff() -> Option<f32> {
CardanoActiveSlotsCoeff::<T>::get()
.map(|perbill| perbill.deconstruct() as f32 / 1_000_000_000.0)
}

/// Returns whether the configuration has been set
pub fn is_configured() -> bool {
EpochDurationMillis::<T>::exists() &&
SlotDurationMillis::<T>::exists() &&
FirstEpochTimestampMillis::<T>::exists() &&
FirstEpochNumber::<T>::exists() &&
FirstSlotNumber::<T>::exists() &&
CardanoSecurityParameter::<T>::exists() &&
CardanoActiveSlotsCoeff::<T>::exists()
}

/// Returns current pallet version
pub fn get_version() -> u32 {
PALLET_VERSION
}
}
}
Loading