diff --git a/Cargo.lock b/Cargo.lock index f889d86b..5afa3f10 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17858,20 +17858,12 @@ version = "1.0.0" dependencies = [ "parity-scale-codec", "pezframe-benchmarking", - "pezframe-election-provider-support", "pezframe-support", "pezframe-system", "pezkuwi-primitives", - "pezpallet-bags-list", "pezpallet-balances", - "pezpallet-session", - "pezpallet-staking", - "pezpallet-timestamp", - "pezsp-core", "pezsp-io", - "pezsp-npos-elections", "pezsp-runtime", - "pezsp-staking", "pezsp-std", "scale-info", "serde", diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/Cargo.toml b/pezcumulus/teyrchains/pezpallets/staking-score/Cargo.toml index fbfb85aa..cfe5df19 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/Cargo.toml +++ b/pezcumulus/teyrchains/pezpallets/staking-score/Cargo.toml @@ -15,8 +15,6 @@ codec = { workspace = true, default-features = false, features = ["derive"] } pezframe-benchmarking = { workspace = true, optional = true } pezframe-support = { default-features = false, workspace = true } pezframe-system = { default-features = false, workspace = true } -pezpallet-balances = { workspace = true, default-features = false, optional = true } -pezpallet-staking = { workspace = true, default-features = false, optional = true } pezsp-runtime = { default-features = false, workspace = true } pezsp-std = { default-features = false, workspace = true } scale-info = { default-features = false, features = [ @@ -26,89 +24,47 @@ serde = { version = "1.0.197", default-features = false, features = [ "derive", ], optional = true } -# PezkuwiChain'in özel tiplerini ve trait'lerini içeren kütüphane +# PezkuwiChain primitives pezkuwi-primitives = { workspace = true, default-features = false } [dev-dependencies] -pezframe-election-provider-support = { workspace = true, features = ["std"] } pezframe-support = { workspace = true, features = ["std"] } pezframe-system = { workspace = true, features = ["std"] } -pezpallet-bags-list = { workspace = true, features = ["std"] } pezpallet-balances = { workspace = true, features = ["std"] } -pezpallet-session = { workspace = true, features = ["std"] } -pezpallet-staking = { workspace = true, features = [ - "runtime-benchmarks", - "std", -] } -pezpallet-timestamp = { workspace = true, features = ["std"] } -pezsp-core = { workspace = true, features = ["std"] } pezsp-io = { workspace = true, features = ["std"] } -pezsp-npos-elections = { workspace = true, features = ["std"] } pezsp-runtime = { workspace = true, features = ["std"] } -pezsp-staking = { workspace = true, features = ["std"] } [features] default = ["std"] std = [ "codec/std", "pezframe-benchmarking?/std", - "pezframe-election-provider-support/std", "pezframe-support/std", "pezframe-system/std", "pezkuwi-primitives/std", - "pezpallet-bags-list/std", - "pezpallet-balances?/std", - "pezpallet-session/std", - "pezpallet-staking?/std", - "pezpallet-timestamp/std", - "pezsp-core/std", - "pezsp-io/std", - "pezsp-npos-elections/std", "pezsp-runtime/std", - "pezsp-staking/std", "pezsp-std/std", "scale-info/std", "serde?/std", ] runtime-benchmarks = [ "pezframe-benchmarking/runtime-benchmarks", - "pezframe-election-provider-support/runtime-benchmarks", "pezframe-support/runtime-benchmarks", "pezframe-system/runtime-benchmarks", "pezkuwi-primitives/runtime-benchmarks", - "pezpallet-bags-list/runtime-benchmarks", - "pezpallet-balances/runtime-benchmarks", - "pezpallet-session/runtime-benchmarks", - "pezpallet-staking/runtime-benchmarks", - "pezpallet-timestamp/runtime-benchmarks", - "pezsp-io/runtime-benchmarks", - "pezsp-npos-elections/runtime-benchmarks", "pezsp-runtime/runtime-benchmarks", - "pezsp-staking/runtime-benchmarks", ] try-runtime = [ "pezframe-benchmarking?/try-runtime", - "pezframe-election-provider-support/try-runtime", "pezframe-support/try-runtime", "pezframe-system/try-runtime", "pezkuwi-primitives/try-runtime", - "pezpallet-bags-list/try-runtime", - "pezpallet-balances?/try-runtime", - "pezpallet-session/try-runtime", - "pezpallet-staking?/try-runtime", - "pezpallet-timestamp/try-runtime", - "pezsp-npos-elections/try-runtime", "pezsp-runtime/try-runtime", - "pezsp-staking/try-runtime", ] serde = [ "codec/serde", "dep:serde", - "pezframe-benchmarking?/serde", - "pezsp-core/serde", - "pezsp-npos-elections/serde", "pezsp-runtime/serde", - "pezsp-staking/serde", "scale-info/serde", ] experimental = [] diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/src/benchmarking.rs b/pezcumulus/teyrchains/pezpallets/staking-score/src/benchmarking.rs index d9f56ba0..de417f85 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/src/benchmarking.rs +++ b/pezcumulus/teyrchains/pezpallets/staking-score/src/benchmarking.rs @@ -1,6 +1,9 @@ //! Benchmarking setup for pezpallet-staking-score -use crate::{Call, Config, Pezpallet, StakingStartBlock}; +use crate::{ + CachedStakingDetails, Call, Config, Pezpallet, StakingDetails, StakingSource, + StakingStartBlock, UNITS, +}; use pezframe_benchmarking::v2::*; use pezframe_system::RawOrigin; @@ -12,19 +15,39 @@ mod benchmarks { fn start_score_tracking() { let caller: T::AccountId = whitelisted_caller(); - // Mock staking provider kullanıyoruz, gerçek staking setup'ı yapmıyoruz - // Runtime'da conditional olarak MockStakingInfoProvider kullanılacak + // Populate CachedStakingDetails with test data + CachedStakingDetails::::insert( + &caller, + StakingSource::RelayChain, + StakingDetails { + staked_amount: (1000u128 * UNITS).into(), + nominations_count: 5, + unlocking_chunks_count: 2, + }, + ); - // Ölçümden önce, bu kullanıcının daha önce takibi başlatmadığından emin olalım. StakingStartBlock::::remove(&caller); - // EYLEM: Bu bloğun içindeki extrinsic çağrısının ne kadar sürdüğünü ölçüyoruz. #[extrinsic_call] _(RawOrigin::Signed(caller.clone())); - // DOĞRULAMA: Mock provider kullanıldığında bu başarılı olmalı assert!(StakingStartBlock::::get(&caller).is_some()); } - // Benchmark test suite is in tests.rs with mock runtime + #[benchmark] + fn receive_staking_details() { + let target: T::AccountId = whitelisted_caller(); + + #[extrinsic_call] + _( + RawOrigin::Root, + target.clone(), + StakingSource::RelayChain, + (500u128 * UNITS).into(), + 3u32, + 0u32, + ); + + assert!(CachedStakingDetails::::get(&target, StakingSource::RelayChain).is_some()); + } } diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/src/lib.rs b/pezcumulus/teyrchains/pezpallets/staking-score/src/lib.rs index edec8006..894fb3a9 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/src/lib.rs +++ b/pezcumulus/teyrchains/pezpallets/staking-score/src/lib.rs @@ -2,119 +2,30 @@ //! # Staking Score Pezpallet //! -//! A pezpallet for calculating time-weighted staking scores based on stake amount and duration. +//! Calculates time-weighted staking scores from cached staking data received via XCM. //! //! ## Overview //! -//! The Staking Score pezpallet calculates reputation scores from staking behavior by considering: -//! - **Stake Amount**: How much a user has staked -//! - **Stake Duration**: How long tokens have been staked -//! - **Nomination Count**: Number of validators nominated -//! - **Unlocking Chunks**: Pending unstake operations +//! People Chain does not have direct access to staking data. Instead, staking details +//! are pushed from Relay Chain and Asset Hub via XCM Transact into `CachedStakingDetails`. +//! This pallet aggregates stake from all sources and calculates a score based on amount +//! and duration. //! -//! These metrics combine to produce a staking score that contributes to the composite -//! trust score in `pezpallet-trust`. +//! ## Dual-Chain Staking //! -//! ## Score Calculation -//! -//! ```text -//! staking_score = base_score + time_bonus -//! -//! where: -//! base_score = (staked_amount / UNITS) * 10 -//! time_bonus = (months_staked * staked_amount * 0.05) / UNITS -//! ``` -//! -//! ### Time-Based Rewards -//! - First month: Base score only -//! - Each additional month: +5% bonus on staked amount -//! - Maximum benefit achieved through long-term commitment -//! - Score increases linearly with time +//! Users can stake on both Relay Chain (direct staking) and Asset Hub (nomination pools). +//! `CachedStakingDetails` is a `StorageDoubleMap` keyed by `(AccountId, StakingSource)` +//! to track stake per source. Score calculation aggregates across all sources. //! //! ## Workflow //! -//! 1. User stakes tokens via main staking pezpallet -//! 2. User calls `start_score_tracking()` to begin time tracking -//! 3. Tracking start block is recorded -//! 4. `pezpallet-trust` queries staking score via `StakingScoreProvider` trait -//! 5. Score calculation uses current block number vs. start block -//! 6. Time bonus accumulates automatically each month -//! -//! ## Integration with Staking -//! -//! This pezpallet does not handle staking operations directly. It: -//! - Reads staking data from main staking pezpallet via `StakingInfoProvider` -//! - Tracks when users want to start earning time bonuses -//! - Calculates scores on-demand without modifying staking state -//! -//! ## Score Components -//! -//! ### Staked Amount -//! - Primary factor in score calculation -//! - Measured in balance units (UNITS = 10^12) -//! - Higher stake = higher base score -//! -//! ### Duration -//! - Measured in months (30 days * 24 hours * 60 min * 10 blocks/min) -//! - ~432,000 blocks per month -//! - Compounds monthly for long-term stakers -//! -//! ### Additional Metrics -//! - Nomination count (contributes to complexity score) -//! - Unlocking chunks (indicates unstaking activity) -//! -//! ## Interface -//! -//! ### Extrinsics -//! -//! - `start_score_tracking()` - Begin time-based score accumulation (user, one-time) -//! -//! ### Storage -//! -//! - `StakingStartBlock` - Block number when user started score tracking -//! -//! ### Trait Implementations -//! -//! - `StakingScoreProvider` - Query staking scores for trust calculation -//! -//! ## Dependencies -//! -//! This pezpallet requires: -//! - Main staking pezpallet implementing `StakingInfoProvider` -//! - `pezpallet-trust` as consumer of staking scores -//! -//! ## Runtime Integration Example -//! -//! ```ignore -//! impl pezpallet_staking_score::Config for Runtime { -//! type RuntimeEvent = RuntimeEvent; -//! type Balance = Balance; -//! type StakingInfo = Staking; // Main staking pezpallet -//! type WeightInfo = pezpallet_staking_score::weights::BizinikiwiWeight; -//! } -//! ``` +//! 1. Relay Chain / Asset Hub pushes staking data via XCM → `receive_staking_details()` +//! 2. User calls `start_score_tracking()` to begin time-based score accumulation +//! 3. `pezpallet-trust` queries staking score via `StakingScoreProvider` trait +//! 4. Score = base_score(amount_tier) * duration_multiplier, capped at 100 pub use pezpallet::*; -// Mock staking info provider for benchmarking - ADD THIS -#[cfg(feature = "runtime-benchmarks")] -pub struct BenchmarkStakingInfoProvider; - -#[cfg(feature = "runtime-benchmarks")] -impl StakingInfoProvider for BenchmarkStakingInfoProvider -where - Balance: From, -{ - fn get_staking_details(_who: &AccountId) -> Option> { - // Always return valid stake for benchmarking - Some(StakingDetails { - staked_amount: (1000u128 * UNITS).into(), - nominations_count: 5, - unlocking_chunks_count: 2, - }) - } -} - #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; #[cfg(test)] @@ -127,30 +38,45 @@ pub mod weights; #[pezframe_support::pezpallet] pub mod pezpallet { - use super::weights::WeightInfo; // Properly importing WeightInfo from parent module. + use super::weights::WeightInfo; use core::ops::Div; use pezframe_support::pezpallet_prelude::*; use pezframe_system::pezpallet_prelude::*; - use pezsp_runtime::{ - traits::{Saturating, Zero}, - Perbill, - }; + use pezsp_runtime::traits::{Saturating, Zero}; - // --- Sabitler --- + // --- Constants --- pub const MONTH_IN_BLOCKS: u32 = 30 * 24 * 60 * 10; pub const UNITS: u128 = 1_000_000_000_000; + /// The chain from which staking data originates. + #[derive( + Encode, + Decode, + DecodeWithMemTracking, + Clone, + Copy, + PartialEq, + Eq, + TypeInfo, + Debug, + MaxEncodedLen, + )] + pub enum StakingSource { + /// Direct staking on the Relay Chain. + RelayChain = 0, + /// Staking via nomination pools on Asset Hub. + AssetHub = 1, + } + #[pezpallet::pezpallet] pub struct Pezpallet(_); #[pezpallet::config] pub trait Config: pezframe_system::Config>> where - // Ensuring BlockNumber is convertible from u32. BlockNumberFor: From, { - /// Balance type to be used for staking. - /// Adding all required mathematical and comparison properties. + /// Balance type used for staking amounts. type Balance: Member + Parameter + MaxEncodedLen @@ -159,86 +85,94 @@ pub mod pezpallet { + PartialOrd + Saturating + Zero - + Div // Specifying that division result is also Balance. + + Div + From; - /// Interface to be used for reading staking data. - type StakingInfo: StakingInfoProvider; - /// To provide extrinsic weights. + + /// Callback when staking data changes for an account. + /// Trust pallet implements this to trigger score recalculation. + type OnStakingUpdate: OnStakingDataUpdate; + + /// Weight information for extrinsics. type WeightInfo: WeightInfo; } - // --- Depolama (Storage) --- + // --- Storage --- + #[pezpallet::storage] #[pezpallet::getter(fn staking_start_block)] pub type StakingStartBlock = StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberFor, OptionQuery>; - /// Cached staking details received from Asset Hub via XCM. - /// This allows People Chain to access staking data without direct access to staking pallet. + /// Cached staking details received via XCM from various chains. + /// Keyed by (AccountId, StakingSource) to support stake aggregation across chains. #[pezpallet::storage] #[pezpallet::getter(fn cached_staking_details)] - pub type CachedStakingDetails = - StorageMap<_, Blake2_128Concat, T::AccountId, StakingDetails, OptionQuery>; + pub type CachedStakingDetails = StorageDoubleMap< + _, + Blake2_128Concat, + T::AccountId, + Blake2_128Concat, + StakingSource, + StakingDetails, + OptionQuery, + >; #[pezpallet::event] #[pezpallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// A user started time-based scoring. ScoreTrackingStarted { who: T::AccountId, start_block: BlockNumberFor }, - /// Staking details received from Asset Hub via XCM. - StakingDetailsReceived { who: T::AccountId, staked_amount: T::Balance }, + /// Staking details received from a chain via XCM. + StakingDetailsReceived { + who: T::AccountId, + source: StakingSource, + staked_amount: T::Balance, + }, } #[pezpallet::error] pub enum Error { - /// Puan takibini başlatmak için önce stake yapmış olmalısınız. + /// User must have stake to start score tracking. NoStakeFound, - /// Puan takibi zaten daha önce başlatılmış. + /// Score tracking has already been started for this account. TrackingAlreadyStarted, - /// Origin is not authorized to send staking details (must be Asset Hub via XCM). - UnauthorizedOrigin, } #[pezpallet::call] impl Pezpallet { - /// Süreye dayalı puanlamayı manuel olarak aktive eder. - /// Bu fonksiyon, her kullanıcı tarafından sadece bir kez çağrılmalıdır. + /// Start time-based score accumulation. One-time call per user. + /// Requires the user to have cached staking data from at least one source. #[pezpallet::call_index(0)] #[pezpallet::weight(T::WeightInfo::start_score_tracking())] pub fn start_score_tracking(origin: OriginFor) -> DispatchResult { let who = ensure_signed(origin)?; - // 1. Kullanıcının puan takibini daha önce başlatıp başlatmadığını kontrol et. ensure!( StakingStartBlock::::get(&who).is_none(), Error::::TrackingAlreadyStarted ); - // 2. Kullanıcının ana staking paletinde stake'i var mı diye kontrol et. - // `get_staking_details` artık Option döndürdüğü için `ok_or` ile hata yönetimi - // yapıyoruz. - let details = - T::StakingInfo::get_staking_details(&who).ok_or(Error::::NoStakeFound)?; - ensure!(!details.staked_amount.is_zero(), Error::::NoStakeFound); + // Check if user has any stake from any source. + let total_stake = Self::total_cached_stake(&who); + ensure!(!total_stake.is_zero(), Error::::NoStakeFound); - // 3. O anki blok numarasını kaydet. let current_block = pezframe_system::Pezpallet::::block_number(); StakingStartBlock::::insert(&who, current_block); + T::OnStakingUpdate::on_staking_data_changed(&who); + Self::deposit_event(Event::ScoreTrackingStarted { who, start_block: current_block }); Ok(()) } - /// Receive staking details from Asset Hub via XCM Transact. - /// This extrinsic is called by Asset Hub's staking pallet to push staking data - /// to the People Chain so trust scores can be calculated. - /// - /// Only root origin is accepted (XCM Transact from sibling chain arrives as root). + /// Receive staking details from a chain via XCM Transact. + /// Only root origin is accepted (XCM Transact from sibling/parent arrives as root). #[pezpallet::call_index(1)] - #[pezpallet::weight(T::WeightInfo::start_score_tracking())] + #[pezpallet::weight(T::WeightInfo::receive_staking_details())] pub fn receive_staking_details( origin: OriginFor, who: T::AccountId, + source: StakingSource, staked_amount: T::Balance, nominations_count: u32, unlocking_chunks_count: u32, @@ -248,63 +182,83 @@ pub mod pezpallet { let details = StakingDetails { staked_amount, nominations_count, unlocking_chunks_count }; - CachedStakingDetails::::insert(&who, details); + CachedStakingDetails::::insert(&who, source, details); - Self::deposit_event(Event::StakingDetailsReceived { who, staked_amount }); + T::OnStakingUpdate::on_staking_data_changed(&who); + + Self::deposit_event(Event::StakingDetailsReceived { who, source, staked_amount }); Ok(()) } } - // --- Arayüz (Trait) ve Tip Tanımları --- + // --- Types --- - /// Puanlamada kullanılacak ham skor tipi. + /// Raw score type used in staking score calculations. pub type RawScore = u32; - /// Staking ile ilgili detayları bir arada tutan ve dışarıdan alınacak veri yapısı. - /// `Default` ekledik çünkü testlerde ve mock'larda işimizi kolaylaştıracak. - #[derive(Default, Encode, Decode, Clone, PartialEq, Eq, TypeInfo, Debug, MaxEncodedLen)] + /// Staking details for a single source chain. + #[derive( + Default, + Encode, + Decode, + DecodeWithMemTracking, + Clone, + PartialEq, + Eq, + TypeInfo, + Debug, + MaxEncodedLen, + )] pub struct StakingDetails { pub staked_amount: Balance, pub nominations_count: u32, pub unlocking_chunks_count: u32, } - /// Bu paletin dış dünyaya sunduğu arayüz. + // --- Traits --- + + /// Interface for querying staking scores. Used by trust pallet. pub trait StakingScoreProvider { - /// Returns the score and the duration in blocks used for calculation. + /// Returns (score, duration_in_blocks) for the given account. fn get_staking_score(who: &AccountId) -> (RawScore, BlockNumber); } - /// Bu paletin, staking verilerini almak için ihtiyaç duyduğu arayüz. - pub trait StakingInfoProvider { - /// Verilen hesap için staking detaylarını döndürür. - /// Eğer kullanıcının stake'i yoksa `None` dönmelidir. Bu daha güvenli bir yöntemdir. - fn get_staking_details(who: &AccountId) -> Option>; + /// Callback trait for when staking data changes. + /// Trust pallet implements this to recalculate scores on staking updates. + pub trait OnStakingDataUpdate { + fn on_staking_data_changed(who: &AccountId); } - // --- Trait Implementasyonu --- + impl OnStakingDataUpdate for () { + fn on_staking_data_changed(_who: &AccountId) {} + } + + // --- Helpers --- + + impl Pezpallet { + /// Calculate total cached stake across all sources for a given account. + pub fn total_cached_stake(who: &T::AccountId) -> T::Balance { + let mut total = T::Balance::zero(); + for (_, details) in CachedStakingDetails::::iter_prefix(who) { + total = total.saturating_add(details.staked_amount); + } + total + } + } + + // --- StakingScoreProvider Implementation --- impl StakingScoreProvider> for Pezpallet { fn get_staking_score(who: &T::AccountId) -> (RawScore, BlockNumberFor) { - // 1. Staking detaylarını al. Önce StakingInfo provider'ı dene, - // bulunamazsa CachedStakingDetails'e (XCM ile gelen veri) bak. - let staking_details = match T::StakingInfo::get_staking_details(who) { - Some(details) => details, - None => match CachedStakingDetails::::get(who) { - Some(cached) => cached, - None => return (0, Zero::zero()), - }, - }; + // Aggregate stake from all cached sources. + let total_staked = Self::total_cached_stake(who); + let staked_hez: T::Balance = total_staked / UNITS.into(); - // Staked miktarı ana birime (HEZ) çevir. - let staked_hez: T::Balance = staking_details.staked_amount / UNITS.into(); - - // "Sıfır stake, sıfır puan" kuralını uygula. if staked_hez.is_zero() { return (0, Zero::zero()); } - // Miktara dayalı temel puanı hesapla. + // Amount-based tier scoring. let amount_score: u32 = if staked_hez <= 100u128.into() { 20 } else if staked_hez <= 250u128.into() { @@ -315,51 +269,27 @@ pub mod pezpallet { 50 // 751+ HEZ }; - // Süreye dayalı çarpanı ve duration'ı hesapla. - let (_duration_multiplier, duration_for_return) = match StakingStartBlock::::get(who) - { - // Eğer kullanıcı `start_score_tracking` çağırdıysa... + // Duration-based multiplier. + let (final_score, duration_for_return) = match StakingStartBlock::::get(who) { Some(start_block) => { let current_block = pezframe_system::Pezpallet::::block_number(); let duration_in_blocks = current_block.saturating_sub(start_block); - let multiplier = if duration_in_blocks >= (12 * MONTH_IN_BLOCKS).into() { - Perbill::from_rational(2u32, 1u32) // x2.0 (12 ay ve üstü) + let score = if duration_in_blocks >= (12 * MONTH_IN_BLOCKS).into() { + amount_score * 2 // x2.0 (12+ months) } else if duration_in_blocks >= (6 * MONTH_IN_BLOCKS).into() { - Perbill::from_rational(17u32, 10u32) // x1.7 (6-11 ay) + amount_score * 17 / 10 // x1.7 (6-11 months) } else if duration_in_blocks >= (3 * MONTH_IN_BLOCKS).into() { - Perbill::from_rational(7u32, 5u32) // x1.4 (3-5 ay) + amount_score * 14 / 10 // x1.4 (3-5 months) } else if duration_in_blocks >= MONTH_IN_BLOCKS.into() { - Perbill::from_rational(6u32, 5u32) // x1.2 (1-2 ay) + amount_score * 12 / 10 // x1.2 (1-2 months) } else { - Perbill::from_rational(1u32, 1u32) // x1.0 (< 1 ay) + amount_score // x1.0 (< 1 month) }; - (multiplier, duration_in_blocks) + (score, duration_in_blocks) }, - // Eğer takip başlatılmadıysa, çarpan 1.0'dır. - None => (Perbill::from_rational(10u32, 10u32), Zero::zero()), - }; - - // Nihai puanı hesapla ve 100 ile sınırla. - let final_score = match StakingStartBlock::::get(who) { - Some(start_block) => { - let current_block = pezframe_system::Pezpallet::::block_number(); - let duration_in_blocks = current_block.saturating_sub(start_block); - - if duration_in_blocks >= (12 * MONTH_IN_BLOCKS).into() { - amount_score * 2 // x2.0 - } else if duration_in_blocks >= (6 * MONTH_IN_BLOCKS).into() { - amount_score * 17 / 10 // x1.7 - } else if duration_in_blocks >= (3 * MONTH_IN_BLOCKS).into() { - amount_score * 14 / 10 // x1.4 - } else if duration_in_blocks >= MONTH_IN_BLOCKS.into() { - amount_score * 12 / 10 // x1.2 - } else { - amount_score // x1.0 - } - }, - None => amount_score, // Takip başlatılmadıysa çarpan yok + None => (amount_score, Zero::zero()), }; (final_score.min(100), duration_for_return) diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/src/mock.rs b/pezcumulus/teyrchains/pezpallets/staking-score/src/mock.rs index 1361be13..44f38454 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/src/mock.rs +++ b/pezcumulus/teyrchains/pezpallets/staking-score/src/mock.rs @@ -1,58 +1,33 @@ -//! pezpallet-staking-score için mock runtime. +//! Simplified mock runtime for pezpallet-staking-score. +//! No real staking pallet - all data comes via CachedStakingDetails. use crate as pezpallet_staking_score; use pezframe_support::{ - construct_runtime, derive_impl, parameter_types, - traits::{ConstU128, ConstU32, ConstU64}, + construct_runtime, derive_impl, parameter_types, traits::ConstU32, weights::constants::RocksDbWeight, }; -use pezframe_system::EnsureRoot; use pezsp_runtime::BuildStorage; -use pezsp_staking::{StakerStatus, StakingAccount}; -// Paletimizdeki sabitleri import ediyoruz. use crate::UNITS; -// --- Tip Takma Adları --- +// --- Type Aliases --- type Block = pezframe_system::mocking::MockBlock; pub type AccountId = u64; pub type Balance = u128; pub type BlockNumber = u64; -pub type SessionIndex = u32; -pub type EraIndex = u32; -// --- Paletler için Sabitler --- -pub const MAX_NOMINATIONS_CONST: u32 = 16; +// --- Constants --- parameter_types! { pub const BlockHashCount: BlockNumber = 250; pub const ExistentialDeposit: Balance = 1; - pub static SessionsPerEra: SessionIndex = 3; - pub const BondingDuration: u32 = 3; - pub const SlashDeferDuration: EraIndex = 0; - pub static HistoryDepth: u32 = 80; - pub const MaxUnlockingChunks: u32 = 32; - pub static MaxNominations: u32 = 16; - pub const MinimumPeriod: u64 = 5000; - pub static BagThresholds: &'static [u64] = &[10, 20, 30, 40, 50, 60, 1_000, 2_000, 10_000]; - pub static MaxWinners: u32 = 100; - pub static MaxBackersPerWinner: u32 = 64; - // Yeni eklenenler: pezpallet_staking::Config için gerekli minimum bond miktarları. - pub const MinNominatorBond: Balance = UNITS; // Testler için yeterince küçük bir değer. - pub const MinValidatorBond: Balance = UNITS; // Testler için yeterince küçük bir değer. } -// --- construct_runtime! Makrosu --- +// --- Runtime --- construct_runtime!( pub enum Test { System: pezframe_system, Balances: pezpallet_balances, - Staking: pezpallet_staking, - Session: pezpallet_session, - Timestamp: pezpallet_timestamp, - Historical: pezpallet_session::historical, - BagsList: pezpallet_bags_list::, - // Kendi paletimiz: StakingScore: pezpallet_staking_score, } ); @@ -72,158 +47,18 @@ impl pezpallet_balances::Config for Test { type AccountStore = System; } -pezsp_runtime::impl_opaque_keys! { - pub struct MockSessionKeys { - pub dummy: pezsp_runtime::testing::UintAuthorityId, - } -} - -impl From for MockSessionKeys { - fn from(dummy: pezsp_runtime::testing::UintAuthorityId) -> Self { - Self { dummy } - } -} - -pub struct TestSessionHandler; -impl pezpallet_session::SessionHandler for TestSessionHandler { - const KEY_TYPE_IDS: &'static [pezsp_runtime::KeyTypeId] = &[pezsp_runtime::key_types::DUMMY]; - fn on_genesis_session(_validators: &[(AccountId, T)]) {} - fn on_new_session( - _changed: bool, - _validators: &[(AccountId, T)], - _queued_validators: &[(AccountId, T)], - ) { - } - fn on_before_session_ending() {} - fn on_disabled(_validator_index: u32) {} -} - -impl pezpallet_session::Config for Test { - type SessionManager = pezpallet_session::historical::NoteHistoricalRoot; - type Keys = MockSessionKeys; - type ShouldEndSession = pezpallet_session::PeriodicSessions>; - type SessionHandler = TestSessionHandler; - type RuntimeEvent = RuntimeEvent; - type ValidatorId = AccountId; - type ValidatorIdOf = pezsp_runtime::traits::ConvertInto; - type NextSessionRotation = pezpallet_session::PeriodicSessions>; - type DisablingStrategy = (); - type WeightInfo = (); - type Currency = Balances; - type KeyDeposit = ConstU128<0>; -} - -impl pezpallet_session::historical::Config for Test { - type RuntimeEvent = RuntimeEvent; - type FullIdentification = pezpallet_staking::Exposure; - type FullIdentificationOf = pezpallet_staking::DefaultExposureOf; -} - -impl pezpallet_timestamp::Config for Test { - type Moment = u64; - type OnTimestampSet = (); - type MinimumPeriod = ConstU64<5>; - type WeightInfo = (); -} - -type VoterBagsListInstance = pezpallet_bags_list::Instance1; -impl pezpallet_bags_list::Config for Test { - type RuntimeEvent = RuntimeEvent; - type WeightInfo = (); - type ScoreProvider = Staking; - type BagThresholds = BagThresholds; - type Score = pezsp_npos_elections::VoteWeight; - type MaxAutoRebagPerBlock = (); -} - -pub struct TestBenchmarkingConfig; -impl pezpallet_staking::BenchmarkingConfig for TestBenchmarkingConfig { - type MaxValidators = ConstU32<1000>; - type MaxNominators = ConstU32<1000>; -} - -#[derive_impl(pezpallet_staking::config_preludes::TestDefaultConfig)] -impl pezpallet_staking::Config for Test { - type Currency = Balances; - type UnixTime = Timestamp; - type SessionsPerEra = SessionsPerEra; - type BondingDuration = BondingDuration; - type SlashDeferDuration = SlashDeferDuration; - type SessionInterface = Self; - type EraPayout = (); - type NextNewSession = Session; - type MaxExposurePageSize = ConstU32<64>; - type ElectionProvider = pezframe_election_provider_support::NoElection<( - AccountId, - BlockNumber, - Staking, - MaxWinners, - MaxBackersPerWinner, - )>; - type GenesisElectionProvider = Self::ElectionProvider; - type VoterList = BagsList; - type TargetList = pezpallet_staking::UseValidatorsMap; - type MaxControllersInDeprecationBatch = ConstU32<100>; - type AdminOrigin = EnsureRoot; - type EventListeners = (); - type HistoryDepth = HistoryDepth; - type NominationsQuota = pezpallet_staking::FixedNominationsQuota; - type MaxUnlockingChunks = MaxUnlockingChunks; - type BenchmarkingConfig = TestBenchmarkingConfig; - type OldCurrency = Balances; -} - -// --- Bizim Paletimiz ve Adaptörü --- -pub struct StakingDataProvider; -impl crate::StakingInfoProvider for StakingDataProvider { - fn get_staking_details(who: &AccountId) -> Option> { - if let Ok(ledger) = Staking::ledger(StakingAccount::Stash(*who)) { - let nominations_count = Staking::nominators(who).map_or(0, |n| n.targets.len() as u32); - let unlocking_chunks_count = ledger.unlocking.len() as u32; - - Some(crate::StakingDetails { - staked_amount: ledger.total, - nominations_count, - unlocking_chunks_count, - }) - } else { - None - } - } -} - impl crate::Config for Test { type Balance = Balance; type WeightInfo = (); - type StakingInfo = StakingDataProvider; + type OnStakingUpdate = (); } -// --- ExtBuilder ve Yardımcı Fonksiyonlar --- -pub struct ExtBuilder { - stakers: Vec<(AccountId, AccountId, Balance, StakerStatus)>, -} +// --- ExtBuilder --- +pub struct ExtBuilder; impl Default for ExtBuilder { fn default() -> Self { - Self { - // Benchmarking ve testlerin düzgün çalışması için başlangıç staker'larını - // testlerde kullanılacak USER_STASH (10) hesabını içermeyecek şekilde ayarlıyoruz. - // USER_STASH testlerde manuel olarak bond edilecek. - stakers: vec![ - // Sadece benchmarking için yeterli sayıda validator ve nominator - (1, 1, 1_000 * UNITS, StakerStatus::Validator), - (2, 2, 1_000 * UNITS, StakerStatus::Validator), - (3, 3, 1_000 * UNITS, StakerStatus::Validator), - (4, 4, 1_000 * UNITS, StakerStatus::Validator), - (5, 5, 1_000 * UNITS, StakerStatus::Validator), - (6, 6, 1_000 * UNITS, StakerStatus::Validator), - (7, 7, 1_000 * UNITS, StakerStatus::Validator), - (8, 8, 1_000 * UNITS, StakerStatus::Validator), - (9, 9, 1_000 * UNITS, StakerStatus::Validator), - (11, 11, 100 * UNITS, StakerStatus::Nominator(vec![1, 2])), - (12, 12, 100 * UNITS, StakerStatus::Nominator(vec![3, 4])), - ], - } + Self } } @@ -232,65 +67,13 @@ impl ExtBuilder { let mut storage = pezframe_system::GenesisConfig::::default().build_storage().unwrap(); - let mut balances: Vec<(AccountId, Balance)> = vec![ - (1, 1_000_000 * UNITS), - (2, 1_000_000 * UNITS), - // USER_STASH (10) için de başlangıçta yeterli bakiye atıyoruz, - // çünkü testlerde bond etmesi beklenecek. - (10, 1_000_000 * UNITS), - (20, 100_000 * UNITS), - (101, 2_000 * UNITS), - ]; - // ExtBuilder'daki tüm staker'ların ve diğer test hesaplarının (eğer varsa) - // yeterli bakiyeye sahip olduğundan emin olun. - // Her staker'a veya test hesabına minimum bond miktarının çok üzerinde bakiye ekle. - for (stash, _, _, _) in &self.stakers { - if !balances.iter().any(|(acc, _)| acc == stash) { - balances.push((*stash, 1_000_000 * UNITS)); // Staker'lara bol miktarda bakiye - } - } - - pezpallet_balances::GenesisConfig:: { balances, ..Default::default() } - .assimilate_storage(&mut storage) - .unwrap(); - - pezpallet_staking::GenesisConfig:: { - stakers: self.stakers.clone(), - validator_count: self.stakers.len() as u32, // Staker sayısını dinamik yap - minimum_validator_count: 0, // En az 0 validator olmasına izin ver - invulnerables: self - .stakers - .iter() - .filter_map( - |(stash, _, _, status)| { - if let StakerStatus::Validator = status { - Some(*stash) - } else { - None - } - }, - ) - .collect(), - force_era: pezpallet_staking::Forcing::ForceNew, // Yeni era başlatmaya zorla - min_nominator_bond: MinNominatorBond::get(), // Tanımlanan minimum değerleri kullan - min_validator_bond: MinValidatorBond::get(), // Tanımlanan minimum değerleri kullan - ..Default::default() - } - .assimilate_storage(&mut storage) - .unwrap(); - - pezpallet_session::GenesisConfig:: { - keys: self - .stakers - .iter() - .filter_map(|(stash, ctrl, _, status)| { - if let StakerStatus::Validator = status { - Some((*stash, *ctrl, MockSessionKeys { dummy: (*stash).into() })) - } else { - None - } - }) - .collect(), + pezpallet_balances::GenesisConfig:: { + balances: vec![ + (1, 1_000_000 * UNITS), + (2, 1_000_000 * UNITS), + (10, 1_000_000 * UNITS), + (20, 100_000 * UNITS), + ], ..Default::default() } .assimilate_storage(&mut storage) diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/src/tests.rs b/pezcumulus/teyrchains/pezpallets/staking-score/src/tests.rs index 4f5f74b7..c4082d20 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/src/tests.rs +++ b/pezcumulus/teyrchains/pezpallets/staking-score/src/tests.rs @@ -1,17 +1,19 @@ -//! pezpallet-staking-score için testler. +//! Tests for pezpallet-staking-score. +//! All tests use receive_staking_details to populate CachedStakingDetails, +//! mirroring the real People Chain architecture. -use crate::{mock::*, Error, Event, StakingScoreProvider, MONTH_IN_BLOCKS, UNITS}; +use crate::{mock::*, Error, Event, StakingScoreProvider, StakingSource, MONTH_IN_BLOCKS, UNITS}; use pezframe_support::{assert_noop, assert_ok}; -use pezpallet_staking::RewardDestination; -// Testlerde kullanacağımız sabitler const USER_STASH: AccountId = 10; +// ============================================================================ +// Basic Score Calculation +// ============================================================================ + #[test] fn zero_stake_should_return_zero_score() { ExtBuilder::default().build_and_execute(|| { - // ExtBuilder'da 10 numaralı hesap için bir staker oluşturmadık. - // Bu nedenle, palet 0 puan vermelidir. assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 0); }); } @@ -19,14 +21,15 @@ fn zero_stake_should_return_zero_score() { #[test] fn score_is_calculated_correctly_without_time_tracking() { ExtBuilder::default().build_and_execute(|| { - // 50 HEZ stake edelim. Staking::bond çağrısı ile stake işlemini başlat. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 50 * UNITS, - RewardDestination::Staked + 0, + 0 )); - // Süre takibi yokken, puan sadece miktara göre hesaplanmalı (20 puan). assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 20); }); } @@ -34,81 +37,74 @@ fn score_is_calculated_correctly_without_time_tracking() { #[test] fn start_score_tracking_works_and_enables_duration_multiplier() { ExtBuilder::default().build_and_execute(|| { - // --- 1. Kurulum ve Başlangıç --- - let initial_block = 10; + let initial_block = 10u64; System::set_block_number(initial_block); - // 500 HEZ stake edelim. Bu, 40 temel puan demektir. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 500 * UNITS, - RewardDestination::Staked + 0, + 0 )); - // Eylem: Süre takibini başlat. Depolamaya `10` yazılacak. assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Doğrulama: Başlangıç puanı doğru mu? - assert_eq!( - StakingScore::get_staking_score(&USER_STASH).0, - 40, - "Initial score should be 40" - ); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 40); - // --- 2. Dört Ay Sonrası --- + // After 4 months: 40 * 1.4 = 56 let target_block_4m = initial_block + (4 * MONTH_IN_BLOCKS) as u64; - let expected_duration_4m = target_block_4m - initial_block; - // Eylem: Zamanı 4 ay ileri "yaşat". System::set_block_number(target_block_4m); let (score_4m, duration_4m) = StakingScore::get_staking_score(&USER_STASH); - assert_eq!(duration_4m, expected_duration_4m, "Duration after 4 months is wrong"); - assert_eq!(score_4m, 56, "Score after 4 months should be 56"); + assert_eq!(duration_4m, target_block_4m - initial_block); + assert_eq!(score_4m, 56); - // --- 3. On Üç Ay Sonrası --- + // After 13 months: 40 * 2.0 = 80 let target_block_13m = initial_block + (13 * MONTH_IN_BLOCKS) as u64; - let expected_duration_13m = target_block_13m - initial_block; - // Eylem: Zamanı başlangıçtan 13 ay sonrasına "yaşat". System::set_block_number(target_block_13m); let (score_13m, duration_13m) = StakingScore::get_staking_score(&USER_STASH); - assert_eq!(duration_13m, expected_duration_13m, "Duration after 13 months is wrong"); - assert_eq!(score_13m, 80, "Score after 13 months should be 80"); + assert_eq!(duration_13m, target_block_13m - initial_block); + assert_eq!(score_13m, 80); }); } #[test] fn get_staking_score_works_without_explicit_tracking() { ExtBuilder::default().build_and_execute(|| { - // 751 HEZ stake edelim. Bu, 50 temel puan demektir. - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 751 * UNITS, - RewardDestination::Staked + 0, + 0 )); - // Puanın 50 olmasını bekliyoruz. assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 50); - // Zamanı ne kadar ileri alırsak alalım, `start_score_tracking` çağrılmadığı - // için puan değişmemeli. + // Even after time passes, score stays the same without tracking System::set_block_number(1_000_000_000); assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 50); }); } // ============================================================================ -// Amount-Based Scoring Edge Cases (4 tests) +// Amount-Based Scoring Tiers // ============================================================================ #[test] fn amount_score_boundary_100_hez() { ExtBuilder::default().build_and_execute(|| { - // Exactly 100 HEZ should give 20 points - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 100 * UNITS, - RewardDestination::Staked + 0, + 0 )); assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 20); @@ -118,11 +114,13 @@ fn amount_score_boundary_100_hez() { #[test] fn amount_score_boundary_250_hez() { ExtBuilder::default().build_and_execute(|| { - // Exactly 250 HEZ should give 30 points - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 250 * UNITS, - RewardDestination::Staked + 0, + 0 )); assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 30); @@ -132,11 +130,13 @@ fn amount_score_boundary_250_hez() { #[test] fn amount_score_boundary_750_hez() { ExtBuilder::default().build_and_execute(|| { - // Exactly 750 HEZ should give 40 points - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 750 * UNITS, - RewardDestination::Staked + 0, + 0 )); assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 40); @@ -146,40 +146,43 @@ fn amount_score_boundary_750_hez() { #[test] fn score_capped_at_100() { ExtBuilder::default().build_and_execute(|| { - // Stake maximum amount and advance time to get maximum multiplier - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), - 1000 * UNITS, // 50 base points - RewardDestination::Staked + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 1000 * UNITS, + 0, + 0 )); assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Advance 12+ months to get 2.0x multiplier + // After 12+ months: 50 * 2.0 = 100 (capped) System::set_block_number((12 * MONTH_IN_BLOCKS + 1) as u64); - // 50 * 2.0 = 100, should be capped at 100 let (score, _) = StakingScore::get_staking_score(&USER_STASH); assert_eq!(score, 100); }); } // ============================================================================ -// Duration Multiplier Tests (3 tests) +// Duration Multiplier Tests // ============================================================================ #[test] fn duration_multiplier_1_month() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), - 500 * UNITS, // 40 base points - RewardDestination::Staked + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 500 * UNITS, + 0, + 0 )); assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Advance 1 month System::set_block_number((MONTH_IN_BLOCKS + 1) as u64); // 40 * 1.2 = 48 @@ -191,15 +194,17 @@ fn duration_multiplier_1_month() { #[test] fn duration_multiplier_6_months() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), - 500 * UNITS, // 40 base points - RewardDestination::Staked + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 500 * UNITS, + 0, + 0 )); assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Advance 6 months System::set_block_number((6 * MONTH_IN_BLOCKS + 1) as u64); // 40 * 1.7 = 68 @@ -211,13 +216,16 @@ fn duration_multiplier_6_months() { #[test] fn duration_multiplier_progression() { ExtBuilder::default().build_and_execute(|| { - let base_block = 100; + let base_block = 100u64; System::set_block_number(base_block); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), - 100 * UNITS, // 20 base points - RewardDestination::Staked + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 100 * UNITS, + 0, + 0 )); assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); @@ -236,13 +244,12 @@ fn duration_multiplier_progression() { } // ============================================================================ -// start_score_tracking Extrinsic Tests (3 tests) +// start_score_tracking Extrinsic Tests // ============================================================================ #[test] fn start_tracking_fails_without_stake() { ExtBuilder::default().build_and_execute(|| { - // Try to start tracking without any stake assert_noop!( StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH)), Error::::NoStakeFound @@ -253,16 +260,17 @@ fn start_tracking_fails_without_stake() { #[test] fn start_tracking_fails_if_already_started() { ExtBuilder::default().build_and_execute(|| { - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 100 * UNITS, - RewardDestination::Staked + 0, + 0 )); - // First call succeeds assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Second call fails assert_noop!( StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH)), Error::::TrackingAlreadyStarted @@ -275,15 +283,17 @@ fn start_tracking_emits_event() { ExtBuilder::default().build_and_execute(|| { System::set_block_number(1); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 100 * UNITS, - RewardDestination::Staked + 0, + 0 )); assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); - // Check event was emitted let events = System::events(); assert!(events.iter().any(|event| { matches!(event.event, RuntimeEvent::StakingScore(Event::ScoreTrackingStarted { .. })) @@ -291,55 +301,247 @@ fn start_tracking_emits_event() { }); } +#[test] +fn start_tracking_works_with_only_asset_hub_stake() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + // Only Asset Hub stake, no Relay Chain stake + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::AssetHub, + 500 * UNITS, + 3, + 0 + )); + + assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 40); + }); +} + // ============================================================================ -// Edge Cases and Integration (2 tests) +// receive_staking_details Tests +// ============================================================================ + +#[test] +fn receive_staking_details_requires_root() { + ExtBuilder::default().build_and_execute(|| { + assert_noop!( + StakingScore::receive_staking_details( + RuntimeOrigin::signed(USER_STASH), + USER_STASH, + StakingSource::RelayChain, + 100 * UNITS, + 0, + 0 + ), + pezsp_runtime::DispatchError::BadOrigin + ); + }); +} + +#[test] +fn receive_staking_details_emits_event() { + ExtBuilder::default().build_and_execute(|| { + System::set_block_number(1); + + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::AssetHub, + 500 * UNITS, + 2, + 1 + )); + + let events = System::events(); + assert!(events.iter().any(|event| { + matches!(event.event, RuntimeEvent::StakingScore(Event::StakingDetailsReceived { .. })) + })); + }); +} + +#[test] +fn receive_staking_details_overwrites_same_source() { + ExtBuilder::default().build_and_execute(|| { + // First: 100 HEZ from Relay + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 100 * UNITS, + 0, + 0 + )); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 20); + + // Update same source to 300 HEZ + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 300 * UNITS, + 0, + 0 + )); + // 300 HEZ is in 250-750 tier = 40 points + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 40); + }); +} + +// ============================================================================ +// Dual-Source Aggregation Tests (NEW) +// ============================================================================ + +#[test] +fn relay_and_asset_hub_stake_aggregated() { + ExtBuilder::default().build_and_execute(|| { + // Relay Chain: 200 HEZ + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 200 * UNITS, + 0, + 0 + )); + + // Asset Hub: 300 HEZ + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::AssetHub, + 300 * UNITS, + 1, + 0 + )); + + // Total: 500 HEZ -> 250-750 tier -> 40 points + let (score, _) = StakingScore::get_staking_score(&USER_STASH); + assert_eq!(score, 40); + }); +} + +#[test] +fn single_source_update_changes_aggregate() { + ExtBuilder::default().build_and_execute(|| { + // Relay: 100 HEZ -> <=100 tier -> 20 points + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 100 * UNITS, + 0, + 0 + )); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 20); + + // Add Asset Hub: 60 HEZ -> total 160 HEZ -> 101-250 tier -> 30 points + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::AssetHub, + 60 * UNITS, + 0, + 0 + )); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 30); + }); +} + +#[test] +fn dual_source_with_duration_multiplier() { + ExtBuilder::default().build_and_execute(|| { + let base_block = 100u64; + System::set_block_number(base_block); + + // Relay: 200 HEZ + Asset Hub: 300 HEZ = 500 HEZ -> 40 base + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, + 200 * UNITS, + 0, + 0 + )); + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::AssetHub, + 300 * UNITS, + 1, + 0 + )); + + assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(USER_STASH))); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 40); + + // After 6 months: 40 * 1.7 = 68 + System::set_block_number(base_block + (6 * MONTH_IN_BLOCKS) as u64); + assert_eq!(StakingScore::get_staking_score(&USER_STASH).0, 68); + }); +} + +// ============================================================================ +// Multiple Users and Edge Cases // ============================================================================ #[test] fn multiple_users_independent_scores() { ExtBuilder::default().build_and_execute(|| { - // Use USER_STASH (10) and account 11 which have pre-allocated balances - let user1 = USER_STASH; // Account 10 - let user2 = 11; // Account 11 (already has stake in mock) + let user1 = USER_STASH; + let user2 = 20; - // User1: Add new stake, no tracking - assert_ok!(Staking::bond( - RuntimeOrigin::signed(user1), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + user1, + StakingSource::RelayChain, 100 * UNITS, - RewardDestination::Staked + 0, + 0 )); - // User2 already has stake from mock (100 HEZ) - // Start tracking for user2 + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + user2, + StakingSource::AssetHub, + 500 * UNITS, + 2, + 0 + )); + + // User2 starts tracking assert_ok!(StakingScore::start_score_tracking(RuntimeOrigin::signed(user2))); - // User1 should have base score of 20 (100 HEZ) assert_eq!(StakingScore::get_staking_score(&user1).0, 20); - - // User2 should have base score of 20 (100 HEZ from mock) - assert_eq!(StakingScore::get_staking_score(&user2).0, 20); + assert_eq!(StakingScore::get_staking_score(&user2).0, 40); // Advance time System::set_block_number((3 * MONTH_IN_BLOCKS) as u64); - // User1 score unchanged (no tracking) + // User1 unchanged (no tracking) assert_eq!(StakingScore::get_staking_score(&user1).0, 20); - // User2 score increased (20 * 1.4 = 28) - assert_eq!(StakingScore::get_staking_score(&user2).0, 28); + // User2 increased (40 * 1.4 = 56) + assert_eq!(StakingScore::get_staking_score(&user2).0, 56); }); } #[test] fn duration_returned_correctly() { ExtBuilder::default().build_and_execute(|| { - let start_block = 100; + let start_block = 100u64; System::set_block_number(start_block); - assert_ok!(Staking::bond( - RuntimeOrigin::signed(USER_STASH), + assert_ok!(StakingScore::receive_staking_details( + RuntimeOrigin::root(), + USER_STASH, + StakingSource::RelayChain, 100 * UNITS, - RewardDestination::Staked + 0, + 0 )); // Without tracking, duration should be 0 diff --git a/pezcumulus/teyrchains/pezpallets/staking-score/src/weights.rs b/pezcumulus/teyrchains/pezpallets/staking-score/src/weights.rs index d88328ba..55e5b370 100644 --- a/pezcumulus/teyrchains/pezpallets/staking-score/src/weights.rs +++ b/pezcumulus/teyrchains/pezpallets/staking-score/src/weights.rs @@ -15,34 +15,13 @@ // See the License for the specific language governing permissions and // limitations under the License. - -//! Autogenerated weights for `pezpallet_staking_score` +//! Manually estimated weights for `pezpallet_staking_score` //! -//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-12-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `MamostePC`, CPU: `11th Gen Intel(R) Core(TM) i9-11950H @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` - -// Executed Command: -// ./target/release/frame-omni-bencher -// v1 -// benchmark -// pezpallet -// --runtime -// target/release/wbuild/people-pezkuwichain-runtime/people_pezkuwichain_runtime.compact.compressed.wasm -// --pallets -// pezpallet_staking_score -// -e -// all -// --steps -// 50 -// --repeat -// 20 -// --output -// pezcumulus/teyrchains/pezpallets/staking-score/src/weights.rs -// --template -// bizinikiwi/.maintain/frame-weight-template.hbs +//! These weights are conservative overestimates pending proper benchmark runs. +//! They account for the `OnStakingUpdate` callback cost (trust pallet update). +//! +//! DATE: 2026-02-16 +//! TODO: Run proper benchmarks to replace these estimates. #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -56,35 +35,57 @@ use core::marker::PhantomData; /// Weight functions needed for `pezpallet_staking_score`. pub trait WeightInfo { fn start_score_tracking() -> Weight; + fn receive_staking_details() -> Weight; } /// Weights for `pezpallet_staking_score` using the Bizinikiwi node and recommended hardware. pub struct BizinikiwiWeight(PhantomData); impl WeightInfo for BizinikiwiWeight { /// Storage: `StakingScore::StakingStartBlock` (r:1 w:1) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `StakingScore::CachedStakingDetails` (r:2 w:0) -- iter_prefix worst case 2 sources + /// Storage: `Trust::TrustScores` (r:1 w:1) -- OnStakingUpdate callback + /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) -- OnStakingUpdate callback + /// Storage: `IdentityKyc::KycStatuses` (r:1 w:0) -- citizenship check + /// Storage: `StakingScore` reads for score calc (r:2 w:0) + /// + /// Total: r:8 w:3 fn start_score_tracking() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `3517` - // Minimum execution time: 11_419_000 picoseconds. - Weight::from_parts(11_860_000, 3517) - .saturating_add(T::DbWeight::get().reads(1_u64)) - .saturating_add(T::DbWeight::get().writes(1_u64)) + // Conservative estimate: ~35 microseconds execution + 8 reads + 3 writes + // Proof size: StakingStartBlock(52) + CachedStakingDetails(77*2) + TrustScores(48) + + // TotalActiveTrustScore(16) + KycStatuses(34) + overhead = ~8000 + Weight::from_parts(35_000_000, 8_000) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } + + /// Storage: `StakingScore::CachedStakingDetails` (r:1 w:1) -- DoubleMap insert + /// Storage: `Trust::TrustScores` (r:1 w:1) -- OnStakingUpdate callback + /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) -- OnStakingUpdate callback + /// Storage: `IdentityKyc::KycStatuses` (r:1 w:0) -- citizenship check + /// Storage: `StakingScore` reads for score calc (r:2 w:0) + /// + /// Total: r:6 w:3 + fn receive_staking_details() -> Weight { + // Conservative estimate: ~30 microseconds execution + 6 reads + 3 writes + // Proof size: CachedStakingDetails(77) + TrustScores(48) + + // TotalActiveTrustScore(16) + KycStatuses(34) + overhead = ~7000 + Weight::from_parts(30_000_000, 7_000) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) } } // For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: `StakingScore::StakingStartBlock` (r:1 w:1) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) fn start_score_tracking() -> Weight { - // Proof Size summary in bytes: - // Measured: `76` - // Estimated: `3517` - // Minimum execution time: 11_419_000 picoseconds. - Weight::from_parts(11_860_000, 3517) - .saturating_add(RocksDbWeight::get().reads(1_u64)) - .saturating_add(RocksDbWeight::get().writes(1_u64)) + Weight::from_parts(35_000_000, 8_000) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } + + fn receive_staking_details() -> Weight { + Weight::from_parts(30_000_000, 7_000) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) } } diff --git a/pezcumulus/teyrchains/pezpallets/trust/src/lib.rs b/pezcumulus/teyrchains/pezpallets/trust/src/lib.rs index 96158c89..e6ef2f4c 100644 --- a/pezcumulus/teyrchains/pezpallets/trust/src/lib.rs +++ b/pezcumulus/teyrchains/pezpallets/trust/src/lib.rs @@ -105,7 +105,9 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -pub use pezpallet_staking_score::{RawScore as StakingRawScore, StakingScoreProvider}; +pub use pezpallet_staking_score::{ + OnStakingDataUpdate, RawScore as StakingRawScore, StakingScoreProvider, +}; /* use pezkuwi_primitives::traits::{ CitizenshipStatusProvider, PerwerdeScoreProvider, ReferralScoreProvider, RawScore, StakingDetails, StakingScoreProvider, TikiScoreProvider, TrustScoreUpdater, TrustScoreProvider @@ -255,6 +257,27 @@ pub mod pezpallet { } } + #[pezpallet::hooks] + impl Hooks> for Pezpallet { + fn on_initialize(n: BlockNumberFor) -> Weight { + let batch_in_progress = BatchUpdateInProgress::::get(); + let interval = T::UpdateInterval::get(); + + // Continue in-progress batch update + if batch_in_progress { + return Self::do_batch_update(); + } + + // Start new batch at periodic interval + if !interval.is_zero() && !n.is_zero() && (n % interval).is_zero() { + return Self::do_batch_update(); + } + + // Fast path: just reading BatchUpdateInProgress + T::DbWeight::get().reads(1) + } + } + #[pezpallet::call] impl Pezpallet { /// To manually recalculate a specific user's Trust Score. @@ -412,6 +435,54 @@ pub mod pezpallet { fn calculate_optimal_batch_size() -> u32 { T::MaxBatchSize::get() } + + /// Internal batch update logic used by both on_initialize and extrinsics. + /// Returns consumed weight. + fn do_batch_update() -> Weight { + let batch_size = Self::calculate_optimal_batch_size(); + let mut updated_count = 0u32; + let mut all_processed = true; + let mut last_account: Option = None; + + let iterator = match LastProcessedAccount::::get() { + Some(start_key) => pezpallet_identity_kyc::KycStatuses::::iter_from( + pezpallet_identity_kyc::KycStatuses::::hashed_key_for(&start_key), + ), + None => pezpallet_identity_kyc::KycStatuses::::iter(), + }; + + for (account, kyc_level) in iterator { + if updated_count >= batch_size { + last_account = Some(account); + all_processed = false; + break; + } + + if kyc_level == pezpallet_identity_kyc::types::KycLevel::Approved { + let _ = Self::update_score_for_account(&account); + updated_count += 1; + } + + last_account = Some(account); + } + + if all_processed { + LastProcessedAccount::::kill(); + BatchUpdateInProgress::::put(false); + Self::deposit_event(Event::AllTrustScoresUpdated { total_updated: updated_count }); + } else { + if let Some(ref account) = last_account { + LastProcessedAccount::::put(account.clone()); + } + BatchUpdateInProgress::::put(true); + Self::deposit_event(Event::BulkTrustScoreUpdate { count: updated_count }); + } + + // Approximate weight + let base_weight = T::DbWeight::get().reads_writes(2, 2); + let per_account = T::DbWeight::get().reads_writes(3, 2); + base_weight.saturating_add(per_account.saturating_mul(updated_count as u64)) + } } impl TrustScoreProvider for Pezpallet { @@ -427,4 +498,12 @@ pub mod pezpallet { } } } + + impl OnStakingDataUpdate for Pezpallet { + fn on_staking_data_changed(who: &T::AccountId) { + if let Err(e) = Self::update_score_for_account(who) { + log::error!("Failed to update trust score on staking change for {who:?}: {e:?}"); + } + } + } } diff --git a/pezcumulus/teyrchains/pezpallets/trust/src/weights.rs b/pezcumulus/teyrchains/pezpallets/trust/src/weights.rs index f12e40ea..f42cdb5b 100644 --- a/pezcumulus/teyrchains/pezpallets/trust/src/weights.rs +++ b/pezcumulus/teyrchains/pezpallets/trust/src/weights.rs @@ -16,13 +16,14 @@ // limitations under the License. -//! Autogenerated weights for `pezpallet_trust` +//! Weights for `pezpallet_trust` //! -//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-12-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `MamostePC`, CPU: `11th Gen Intel(R) Core(TM) i9-11950H @ 2.60GHz` -//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` +//! Originally auto-generated using BIZINIKIWI BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2025-12-08 (original), 2026-02-16 (manually adjusted) +//! +//! ADJUSTED: Added +2 reads per account for CachedStakingDetails iter_prefix +//! (staking score calculation now aggregates from StorageDoubleMap instead of noop). +//! TODO: Run proper benchmarks to replace these adjusted estimates. // Executed Command: // ./target/release/frame-omni-bencher @@ -64,145 +65,70 @@ pub trait WeightInfo { pub struct BizinikiwiWeight(PhantomData); impl WeightInfo for BizinikiwiWeight { /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `StakingScore::CachedStakingDetails` (r:2 w:0) -- iter_prefix for score calc /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// Storage: `IdentityKyc::KycStatuses` (r:1 w:0) -- citizenship check + /// + /// Total: r:8 w:2 (adjusted +3 reads from original benchmark) fn force_recalculate_trust_score() -> Weight { - // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `3534` - // Minimum execution time: 41_676_000 picoseconds. - Weight::from_parts(51_361_000, 3534) - .saturating_add(T::DbWeight::get().reads(5_u64)) + Weight::from_parts(65_000_000, 5000) + .saturating_add(T::DbWeight::get().reads(8_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } /// Storage: `Trust::LastProcessedAccount` (r:1 w:1) - /// Proof: `Trust::LastProcessedAccount` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `IdentityKyc::KycStatuses` (r:2 w:0) - /// Proof: `IdentityKyc::KycStatuses` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `StakingScore::CachedStakingDetails` (r:2 w:0) -- iter_prefix for score calc /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) /// Storage: `Trust::BatchUpdateInProgress` (r:0 w:1) - /// Proof: `Trust::BatchUpdateInProgress` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + /// + /// Total: r:10 w:4 (adjusted +2 reads from original benchmark) fn update_all_trust_scores() -> Weight { - // Proof Size summary in bytes: - // Measured: `338` - // Estimated: `6038` - // Minimum execution time: 57_062_000 picoseconds. - Weight::from_parts(71_311_000, 6038) - .saturating_add(T::DbWeight::get().reads(8_u64)) + Weight::from_parts(85_000_000, 7000) + .saturating_add(T::DbWeight::get().reads(10_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } /// Storage: `Trust::BatchUpdateInProgress` (r:1 w:1) - /// Proof: `Trust::BatchUpdateInProgress` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) /// Storage: `Trust::LastProcessedAccount` (r:1 w:1) - /// Proof: `Trust::LastProcessedAccount` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) /// Storage: `IdentityKyc::KycStatuses` (r:2 w:0) - /// Proof: `IdentityKyc::KycStatuses` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) + /// Storage: `StakingScore::CachedStakingDetails` (r:2 w:0) -- iter_prefix for score calc /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + /// + /// Total: r:11 w:4 (adjusted +2 reads from original benchmark) fn periodic_trust_score_update() -> Weight { - // Proof Size summary in bytes: - // Measured: `338` - // Estimated: `6038` - // Minimum execution time: 82_604_000 picoseconds. - Weight::from_parts(88_810_000, 6038) - .saturating_add(T::DbWeight::get().reads(9_u64)) + Weight::from_parts(100_000_000, 7000) + .saturating_add(T::DbWeight::get().reads(11_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } } // For backwards compatibility and tests. impl WeightInfo for () { - /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) - /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) - /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) fn force_recalculate_trust_score() -> Weight { - // Proof Size summary in bytes: - // Measured: `287` - // Estimated: `3534` - // Minimum execution time: 41_676_000 picoseconds. - Weight::from_parts(51_361_000, 3534) - .saturating_add(RocksDbWeight::get().reads(5_u64)) + Weight::from_parts(65_000_000, 5000) + .saturating_add(RocksDbWeight::get().reads(8_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } - /// Storage: `Trust::LastProcessedAccount` (r:1 w:1) - /// Proof: `Trust::LastProcessedAccount` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) - /// Storage: `IdentityKyc::KycStatuses` (r:2 w:0) - /// Proof: `IdentityKyc::KycStatuses` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) - /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) - /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) - /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) - /// Storage: `Trust::BatchUpdateInProgress` (r:0 w:1) - /// Proof: `Trust::BatchUpdateInProgress` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) + fn update_all_trust_scores() -> Weight { - // Proof Size summary in bytes: - // Measured: `338` - // Estimated: `6038` - // Minimum execution time: 57_062_000 picoseconds. - Weight::from_parts(71_311_000, 6038) - .saturating_add(RocksDbWeight::get().reads(8_u64)) + Weight::from_parts(85_000_000, 7000) + .saturating_add(RocksDbWeight::get().reads(10_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } - /// Storage: `Trust::BatchUpdateInProgress` (r:1 w:1) - /// Proof: `Trust::BatchUpdateInProgress` (`max_values`: Some(1), `max_size`: Some(1), added: 496, mode: `MaxEncodedLen`) - /// Storage: `Trust::LastProcessedAccount` (r:1 w:1) - /// Proof: `Trust::LastProcessedAccount` (`max_values`: Some(1), `max_size`: Some(32), added: 527, mode: `MaxEncodedLen`) - /// Storage: `IdentityKyc::KycStatuses` (r:2 w:0) - /// Proof: `IdentityKyc::KycStatuses` (`max_values`: None, `max_size`: Some(49), added: 2524, mode: `MaxEncodedLen`) - /// Storage: `Trust::TrustScores` (r:1 w:1) - /// Proof: `Trust::TrustScores` (`max_values`: None, `max_size`: Some(64), added: 2539, mode: `MaxEncodedLen`) - /// Storage: `StakingScore::StakingStartBlock` (r:1 w:0) - /// Proof: `StakingScore::StakingStartBlock` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Referral::ReferralCount` (r:1 w:0) - /// Proof: `Referral::ReferralCount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `MaxEncodedLen`) - /// Storage: `Tiki::UserTikis` (r:1 w:0) - /// Proof: `Tiki::UserTikis` (`max_values`: None, `max_size`: Some(69), added: 2544, mode: `MaxEncodedLen`) - /// Storage: `Trust::TotalActiveTrustScore` (r:1 w:1) - /// Proof: `Trust::TotalActiveTrustScore` (`max_values`: Some(1), `max_size`: Some(16), added: 511, mode: `MaxEncodedLen`) + fn periodic_trust_score_update() -> Weight { - // Proof Size summary in bytes: - // Measured: `338` - // Estimated: `6038` - // Minimum execution time: 82_604_000 picoseconds. - Weight::from_parts(88_810_000, 6038) - .saturating_add(RocksDbWeight::get().reads(9_u64)) + Weight::from_parts(100_000_000, 7000) + .saturating_add(RocksDbWeight::get().reads(11_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } } diff --git a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/lib.rs b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/lib.rs index 7df20aae..62e33ddb 100644 --- a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/lib.rs +++ b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/lib.rs @@ -157,7 +157,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: alloc::borrow::Cow::Borrowed("people-pezkuwichain"), impl_name: alloc::borrow::Cow::Borrowed("people-pezkuwichain"), authoring_version: 1, - spec_version: 1_020_004, + spec_version: 1_020_006, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/people.rs b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/people.rs index 1b54da36..00a462a9 100644 --- a/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/people.rs +++ b/pezcumulus/teyrchains/runtimes/people/people-pezkuwichain/src/people.rs @@ -469,42 +469,10 @@ parameter_types! { pub const StakingScoreUpdateInterval: BlockNumber = HOURS; } -/// Staking info provider - noop implementation for People parachain -/// On People chain, we don't have direct access to staking info from relay chain. -/// This is a placeholder that returns None, meaning users won't get staking-based scores here. -#[cfg(not(feature = "runtime-benchmarks"))] -pub struct StakingInfoProvider; -#[cfg(not(feature = "runtime-benchmarks"))] -impl pezpallet_staking_score::StakingInfoProvider for StakingInfoProvider { - fn get_staking_details( - _who: &AccountId, - ) -> Option> { - // People parachain doesn't have direct staking - return None - None - } -} - -/// Mock staking info provider for benchmarking - always returns valid stake -#[cfg(feature = "runtime-benchmarks")] -pub struct StakingInfoProvider; -#[cfg(feature = "runtime-benchmarks")] -impl pezpallet_staking_score::StakingInfoProvider for StakingInfoProvider { - fn get_staking_details( - _who: &AccountId, - ) -> Option> { - // Return mock staking data for benchmarks - Some(pezpallet_staking_score::StakingDetails { - staked_amount: 1_000_000_000_000_000u128, // 1000 units - nominations_count: 5, - unlocking_chunks_count: 2, - }) - } -} - impl pezpallet_staking_score::Config for Runtime { type WeightInfo = pezpallet_staking_score::weights::BizinikiwiWeight; type Balance = Balance; - type StakingInfo = StakingInfoProvider; + type OnStakingUpdate = Trust; } // =============================================================================