feat: dual-chain staking score with XCM data pipeline
- Rewrite pezpallet-staking-score: StorageDoubleMap (AccountId x StakingSource), remove StakingInfoProvider trait, all data via receive_staking_details() - Add StakingSource enum (RelayChain / AssetHub) for multi-source aggregation - Add OnStakingDataUpdate callback trait for trust pallet integration - Trust pallet: on_initialize hook for periodic batch updates, OnStakingDataUpdate impl triggers immediate score recalculation - People Chain runtime: remove noop StakingInfoProvider, wire OnStakingUpdate = Trust - Update weights for both pallets (conservative estimates incl. callback cost) - spec_version 1_020_005 -> 1_020_006 - 57 tests passing (25 staking-score + 32 trust)
This commit is contained in:
Generated
-8
@@ -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",
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
@@ -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::<T>::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::<T>::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::<T>::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::<T>::get(&target, StakingSource::RelayChain).is_some());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Runtime>;
|
||||
//! }
|
||||
//! ```
|
||||
//! 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<AccountId, Balance> StakingInfoProvider<AccountId, Balance> for BenchmarkStakingInfoProvider
|
||||
where
|
||||
Balance: From<u128>,
|
||||
{
|
||||
fn get_staking_details(_who: &AccountId) -> Option<StakingDetails<Balance>> {
|
||||
// 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<T>(_);
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config<RuntimeEvent: From<Event<Self>>>
|
||||
where
|
||||
// Ensuring BlockNumber is convertible from u32.
|
||||
BlockNumberFor<Self>: From<u32>,
|
||||
{
|
||||
/// 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<Output = Self::Balance> // Specifying that division result is also Balance.
|
||||
+ Div<Output = Self::Balance>
|
||||
+ From<u128>;
|
||||
/// Interface to be used for reading staking data.
|
||||
type StakingInfo: StakingInfoProvider<Self::AccountId, Self::Balance>;
|
||||
/// To provide extrinsic weights.
|
||||
|
||||
/// Callback when staking data changes for an account.
|
||||
/// Trust pallet implements this to trigger score recalculation.
|
||||
type OnStakingUpdate: OnStakingDataUpdate<Self::AccountId>;
|
||||
|
||||
/// Weight information for extrinsics.
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
// --- Depolama (Storage) ---
|
||||
// --- Storage ---
|
||||
|
||||
#[pezpallet::storage]
|
||||
#[pezpallet::getter(fn staking_start_block)]
|
||||
pub type StakingStartBlock<T: Config> =
|
||||
StorageMap<_, Blake2_128Concat, T::AccountId, BlockNumberFor<T>, 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<T: Config> =
|
||||
StorageMap<_, Blake2_128Concat, T::AccountId, StakingDetails<T::Balance>, OptionQuery>;
|
||||
pub type CachedStakingDetails<T: Config> = StorageDoubleMap<
|
||||
_,
|
||||
Blake2_128Concat,
|
||||
T::AccountId,
|
||||
Blake2_128Concat,
|
||||
StakingSource,
|
||||
StakingDetails<T::Balance>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
#[pezpallet::event]
|
||||
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
/// A user started time-based scoring.
|
||||
ScoreTrackingStarted { who: T::AccountId, start_block: BlockNumberFor<T> },
|
||||
/// 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<T> {
|
||||
/// 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<T: Config> Pezpallet<T> {
|
||||
/// 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<T>) -> 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::<T>::get(&who).is_none(),
|
||||
Error::<T>::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::<T>::NoStakeFound)?;
|
||||
ensure!(!details.staked_amount.is_zero(), Error::<T>::NoStakeFound);
|
||||
// Check if user has any stake from any source.
|
||||
let total_stake = Self::total_cached_stake(&who);
|
||||
ensure!(!total_stake.is_zero(), Error::<T>::NoStakeFound);
|
||||
|
||||
// 3. O anki blok numarasını kaydet.
|
||||
let current_block = pezframe_system::Pezpallet::<T>::block_number();
|
||||
StakingStartBlock::<T>::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<T>,
|
||||
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::<T>::insert(&who, details);
|
||||
CachedStakingDetails::<T>::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<Balance> {
|
||||
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<AccountId, BlockNumber> {
|
||||
/// 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<AccountId, Balance> {
|
||||
/// 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<StakingDetails<Balance>>;
|
||||
/// Callback trait for when staking data changes.
|
||||
/// Trust pallet implements this to recalculate scores on staking updates.
|
||||
pub trait OnStakingDataUpdate<AccountId> {
|
||||
fn on_staking_data_changed(who: &AccountId);
|
||||
}
|
||||
|
||||
// --- Trait Implementasyonu ---
|
||||
impl<AccountId> OnStakingDataUpdate<AccountId> for () {
|
||||
fn on_staking_data_changed(_who: &AccountId) {}
|
||||
}
|
||||
|
||||
// --- Helpers ---
|
||||
|
||||
impl<T: Config> Pezpallet<T> {
|
||||
/// 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::<T>::iter_prefix(who) {
|
||||
total = total.saturating_add(details.staked_amount);
|
||||
}
|
||||
total
|
||||
}
|
||||
}
|
||||
|
||||
// --- StakingScoreProvider Implementation ---
|
||||
|
||||
impl<T: Config> StakingScoreProvider<T::AccountId, BlockNumberFor<T>> for Pezpallet<T> {
|
||||
fn get_staking_score(who: &T::AccountId) -> (RawScore, BlockNumberFor<T>) {
|
||||
// 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::<T>::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::<T>::get(who)
|
||||
{
|
||||
// Eğer kullanıcı `start_score_tracking` çağırdıysa...
|
||||
// Duration-based multiplier.
|
||||
let (final_score, duration_for_return) = match StakingStartBlock::<T>::get(who) {
|
||||
Some(start_block) => {
|
||||
let current_block = pezframe_system::Pezpallet::<T>::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::<T>::get(who) {
|
||||
Some(start_block) => {
|
||||
let current_block = pezframe_system::Pezpallet::<T>::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)
|
||||
|
||||
@@ -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<Test>;
|
||||
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::<Instance1>,
|
||||
// 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<pezsp_runtime::testing::UintAuthorityId> for MockSessionKeys {
|
||||
fn from(dummy: pezsp_runtime::testing::UintAuthorityId) -> Self {
|
||||
Self { dummy }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TestSessionHandler;
|
||||
impl pezpallet_session::SessionHandler<AccountId> for TestSessionHandler {
|
||||
const KEY_TYPE_IDS: &'static [pezsp_runtime::KeyTypeId] = &[pezsp_runtime::key_types::DUMMY];
|
||||
fn on_genesis_session<T: pezsp_runtime::traits::OpaqueKeys>(_validators: &[(AccountId, T)]) {}
|
||||
fn on_new_session<T: pezsp_runtime::traits::OpaqueKeys>(
|
||||
_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<Test, Staking>;
|
||||
type Keys = MockSessionKeys;
|
||||
type ShouldEndSession = pezpallet_session::PeriodicSessions<SessionsPerEra, ConstU64<0>>;
|
||||
type SessionHandler = TestSessionHandler;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ValidatorId = AccountId;
|
||||
type ValidatorIdOf = pezsp_runtime::traits::ConvertInto;
|
||||
type NextSessionRotation = pezpallet_session::PeriodicSessions<SessionsPerEra, ConstU64<0>>;
|
||||
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<AccountId, Balance>;
|
||||
type FullIdentificationOf = pezpallet_staking::DefaultExposureOf<Test>;
|
||||
}
|
||||
|
||||
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<VoterBagsListInstance> 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<Self>;
|
||||
type MaxControllersInDeprecationBatch = ConstU32<100>;
|
||||
type AdminOrigin = EnsureRoot<AccountId>;
|
||||
type EventListeners = ();
|
||||
type HistoryDepth = HistoryDepth;
|
||||
type NominationsQuota = pezpallet_staking::FixedNominationsQuota<MAX_NOMINATIONS_CONST>;
|
||||
type MaxUnlockingChunks = MaxUnlockingChunks;
|
||||
type BenchmarkingConfig = TestBenchmarkingConfig;
|
||||
type OldCurrency = Balances;
|
||||
}
|
||||
|
||||
// --- Bizim Paletimiz ve Adaptörü ---
|
||||
pub struct StakingDataProvider;
|
||||
impl crate::StakingInfoProvider<AccountId, Balance> for StakingDataProvider {
|
||||
fn get_staking_details(who: &AccountId) -> Option<crate::StakingDetails<Balance>> {
|
||||
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<AccountId>)>,
|
||||
}
|
||||
// --- 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::<Test>::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::<Test> { balances, ..Default::default() }
|
||||
.assimilate_storage(&mut storage)
|
||||
.unwrap();
|
||||
|
||||
pezpallet_staking::GenesisConfig::<Test> {
|
||||
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::<Test> {
|
||||
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::<Test> {
|
||||
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)
|
||||
|
||||
@@ -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::<Test>::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::<Test>::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
|
||||
|
||||
@@ -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<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
|
||||
fn on_initialize(n: BlockNumberFor<T>) -> Weight {
|
||||
let batch_in_progress = BatchUpdateInProgress::<T>::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<T: Config> Pezpallet<T> {
|
||||
/// 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<T::AccountId> = None;
|
||||
|
||||
let iterator = match LastProcessedAccount::<T>::get() {
|
||||
Some(start_key) => pezpallet_identity_kyc::KycStatuses::<T>::iter_from(
|
||||
pezpallet_identity_kyc::KycStatuses::<T>::hashed_key_for(&start_key),
|
||||
),
|
||||
None => pezpallet_identity_kyc::KycStatuses::<T>::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::<T>::kill();
|
||||
BatchUpdateInProgress::<T>::put(false);
|
||||
Self::deposit_event(Event::AllTrustScoresUpdated { total_updated: updated_count });
|
||||
} else {
|
||||
if let Some(ref account) = last_account {
|
||||
LastProcessedAccount::<T>::put(account.clone());
|
||||
}
|
||||
BatchUpdateInProgress::<T>::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<T: Config> TrustScoreProvider<T::AccountId> for Pezpallet<T> {
|
||||
@@ -427,4 +498,12 @@ pub mod pezpallet {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> OnStakingDataUpdate<T::AccountId> for Pezpallet<T> {
|
||||
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:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<T>(PhantomData<T>);
|
||||
impl<T: pezframe_system::Config> WeightInfo for BizinikiwiWeight<T> {
|
||||
/// 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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<AccountId, Balance> for StakingInfoProvider {
|
||||
fn get_staking_details(
|
||||
_who: &AccountId,
|
||||
) -> Option<pezpallet_staking_score::StakingDetails<Balance>> {
|
||||
// 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<AccountId, Balance> for StakingInfoProvider {
|
||||
fn get_staking_details(
|
||||
_who: &AccountId,
|
||||
) -> Option<pezpallet_staking_score::StakingDetails<Balance>> {
|
||||
// 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<Runtime>;
|
||||
type Balance = Balance;
|
||||
type StakingInfo = StakingInfoProvider;
|
||||
type OnStakingUpdate = Trust;
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user