Refactoring Checkpoint: (WIP)

This commit is contained in:
2025-12-14 10:29:31 +03:00
parent 09735eb97a
commit c89d7cac55
1424 changed files with 6415 additions and 6064 deletions
@@ -0,0 +1,423 @@
#![cfg_attr(not(feature = "std"), no_std)]
//! # Trust Score Pallet
//!
//! A pallet for calculating and managing composite trust scores based on multiple ecosystem
//! metrics.
//!
//! ## Overview
//!
//! The Trust Score pallet aggregates multiple reputation and activity metrics to produce
//! a unified trust score for each citizen. This score is used throughout the ecosystem for:
//!
//! - Validator pool eligibility (trust-based validators)
//! - Reward distribution weighting (pez-rewards)
//! - Governance participation rights
//! - Social reputation tracking
//!
//! ## Trust Score Components
//!
//! The trust score is calculated from four primary sources:
//!
//! 1. **Staking Score**: Economic security through token staking
//! 2. **Referral Score**: Network growth contribution via referrals
//! 3. **Perwerde Score**: Educational achievement and verification
//! 4. **Tiki Score**: Social engagement and platform activity
//!
//! ## Score Calculation
//!
//! ```text
//! trust_score = (staking_score + referral_score + perwerde_score + tiki_score) * multiplier
//! ```
//!
//! Where:
//! - Each component score is normalized and weighted
//! - The multiplier is configurable via `ScoreMultiplierBase`
//! - Citizenship status is required (KYC approved)
//!
//! ## Update Mechanisms
//!
//! ### Automatic Updates
//! - Periodic batch updates scheduled at `UpdateInterval` (e.g., daily)
//! - Processes all citizens in batches to manage computational load
//! - Maintains update progress across blocks for large user bases
//!
//! ### Manual Updates
//! - Individual score recalculation via privileged call
//! - Full batch update trigger (root only)
//! - Component change hooks from other pallets
//!
//! ## Storage
//!
//! - `TrustScores` - Per-account trust score mapping
//! - `TotalActiveTrustScore` - Aggregate trust score across all citizens
//! - `BatchUpdateInProgress` - Flag for ongoing batch update process
//! - `LastProcessedAccount` - Checkpoint for resumable batch updates
//!
//! ## Interface
//!
//! ### Extrinsics
//!
//! - `force_recalculate_trust_score(who)` - Manually recalculate specific user's score (root)
//! - `update_all_trust_scores()` - Trigger batch update of all citizens (root)
//!
//! ### Trait Implementations
//!
//! - `TrustScoreProvider` - Query trust scores from other pallets
//! - `TrustScoreUpdater` - Receive notifications of component changes
//!
//! ## Dependencies
//!
//! This pallet requires integration with:
//! - `pezpallet-identity-kyc` - Citizenship status verification
//! - `pezpallet-staking-score` - Staking metrics provider
//! - `pezpallet-referral` - Referral score provider
//! - `pezpallet-perwerde` - Education score provider
//! - `pezpallet-tiki` - Social engagement provider
//!
//! ## Runtime Integration Example
//!
//! ```ignore
//! impl pezpallet_trust::Config for Runtime {
//! type RuntimeEvent = RuntimeEvent;
//! type WeightInfo = pezpallet_trust::weights::BizinikiwiWeight<Runtime>;
//! type Score = u128;
//! type ScoreMultiplierBase = ConstU128<100>;
//! type UpdateInterval = ConstU32<14400>; // ~1 day in blocks
//! type StakingScoreSource = StakingScore;
//! type ReferralScoreSource = Referral;
//! type PerwerdeScoreSource = Perwerde;
//! type TikiScoreSource = Tiki;
//! type CitizenshipSource = IdentityKyc;
//! }
//! ```
pub use pallet::*;
pub mod weights;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub use pezpallet_staking_score::{RawScore as StakingRawScore, StakingScoreProvider};
/* use pezkuwi_primitives::traits::{
CitizenshipStatusProvider, PerwerdeScoreProvider, ReferralScoreProvider, RawScore,
StakingDetails, StakingScoreProvider, TikiScoreProvider, TrustScoreUpdater, TrustScoreProvider
}; */
use core::convert::TryFrom;
use pezframe_system::pezpallet_prelude::BlockNumberFor;
use pezframe_support::pezpallet_prelude::{
Get, IsType, MaxEncodedLen, Member, OptionQuery, Parameter, ValueQuery,
};
pub trait ReferralScoreProvider<AccountId> {
fn get_referral_score(who: &AccountId) -> u32;
}
// Re-export from identity-kyc pallet
pub use pezpallet_identity_kyc::CitizenshipStatusProvider;
pub trait TrustScoreUpdater<AccountId> {
fn on_score_component_changed(who: &AccountId);
}
pub trait PerwerdeScoreProvider<AccountId> {
fn get_perwerde_score(who: &AccountId) -> u32;
}
pub trait TrustScoreProvider<AccountId> {
fn trust_score_of(who: &AccountId) -> u128;
}
pub trait TikiScoreProvider<AccountId> {
fn get_tiki_score(who: &AccountId) -> u32;
}
#[pezframe_support::pallet]
pub mod pallet {
use super::{weights::WeightInfo, *};
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
use pezsp_runtime::traits::{Saturating, Zero};
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config + pezpallet_identity_kyc::Config {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
type WeightInfo: WeightInfo;
type Score: Member
+ Parameter
+ MaxEncodedLen
+ Copy
+ Default
+ PartialOrd
+ Saturating
+ Zero
+ From<StakingRawScore>
+ Into<u128>
+ TryFrom<u128>;
#[pallet::constant]
type ScoreMultiplierBase: Get<u128>;
/// Block interval for Trust score updates (e.g. daily)
#[pallet::constant]
type UpdateInterval: Get<BlockNumberFor<Self>>;
/// Maximum number of accounts to process per batch update
/// Prevents DoS by limiting computation per extrinsic call
#[pallet::constant]
type MaxBatchSize: Get<u32>;
type StakingScoreSource: StakingScoreProvider<Self::AccountId, BlockNumberFor<Self>>;
type ReferralScoreSource: ReferralScoreProvider<Self::AccountId>;
type PerwerdeScoreSource: PerwerdeScoreProvider<Self::AccountId>;
type TikiScoreSource: TikiScoreProvider<Self::AccountId>;
type CitizenshipSource: CitizenshipStatusProvider<Self::AccountId>;
}
#[pallet::storage]
#[pallet::getter(fn trust_score_of)]
pub type TrustScores<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, T::Score, ValueQuery>;
#[pallet::storage]
#[pallet::getter(fn total_active_trust_score)]
pub type TotalActiveTrustScore<T: Config> = StorageValue<_, T::Score, ValueQuery>;
#[pallet::storage]
pub type LastProcessedAccount<T: Config> = StorageValue<_, T::AccountId, OptionQuery>;
#[pallet::storage]
pub type BatchUpdateInProgress<T: Config> = StorageValue<_, bool, ValueQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A user's Trust Score was successfully updated.
TrustScoreUpdated { who: T::AccountId, old_score: T::Score, new_score: T::Score },
/// Total active Trust Score on chain updated.
TotalTrustScoreUpdated { new_total: T::Score },
/// A batch Trust Score update completed.
BulkTrustScoreUpdate { count: u32 },
/// All Trust Scores update completed.
AllTrustScoresUpdated { total_updated: u32 },
/// Periodic Trust Score update scheduled for next time.
PeriodicUpdateScheduled { next_block: BlockNumberFor<T> },
}
#[pallet::error]
#[derive(PartialEq)]
pub enum Error<T> {
CalculationOverflow,
NotACitizen,
UpdateInProgress,
}
#[pallet::genesis_config]
#[derive(pezframe_support::DefaultNoBound)]
pub struct GenesisConfig<T: Config> {
pub start_periodic_updates: bool,
#[serde(skip)]
pub _phantom: core::marker::PhantomData<T>,
}
#[pallet::genesis_build]
impl<T: Config> BuildGenesisConfig for GenesisConfig<T> {
fn build(&self) {
if self.start_periodic_updates {
// Schedule first periodic update for 1 day later
let _first_update_block =
pezframe_system::Pallet::<T>::block_number() + T::UpdateInterval::get();
// Note: Scheduler may not be available during Genesis build
// In this case, manual start required or scheduled in runtime
// For now, we are just marking the flag
}
}
}
#[pallet::call]
impl<T: Config> Pallet<T> {
/// To manually recalculate a specific user's Trust Score.
#[pallet::call_index(0)]
#[pallet::weight(<T as Config>::WeightInfo::force_recalculate_trust_score())]
pub fn force_recalculate_trust_score(
origin: OriginFor<T>,
who: T::AccountId,
) -> DispatchResult {
ensure_root(origin)?;
Self::update_score_for_account(&who)?;
Ok(())
}
/// Updates Trust Scores of all citizens in bulk
/// Works in batches for large user base using efficient pagination
/// UPDATED (Gemini suggestion): Uses iter_from for true O(1) resume
#[pallet::call_index(1)]
#[pallet::weight(<T as Config>::WeightInfo::update_all_trust_scores())]
pub fn update_all_trust_scores(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
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;
// Use iter_from for efficient pagination - O(1) resume instead of O(n) scan
// This is critical for large user bases to prevent chain stalling
let iterator = match LastProcessedAccount::<T>::get() {
Some(start_key) => {
// Resume from last processed account using iter_from
pezpallet_identity_kyc::KycStatuses::<T>::iter_from(
pezpallet_identity_kyc::KycStatuses::<T>::hashed_key_for(&start_key),
)
},
None => {
// Start from beginning
pezpallet_identity_kyc::KycStatuses::<T>::iter()
},
};
// Process accounts in batch
for (account, kyc_level) in iterator {
// Is batch limit full?
if updated_count >= batch_size {
// Save last processed account for next batch
last_account = Some(account);
all_processed = false;
break;
}
// Only process accounts with Approved KYC (citizens)
if kyc_level == pezpallet_identity_kyc::types::KycLevel::Approved {
let _ = Self::update_score_for_account(&account);
updated_count += 1;
}
// Track last processed for checkpoint
last_account = Some(account);
}
// Update state based on completion
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 });
}
Ok(())
}
/// Periyodik güncellemeyi başlatan function
#[pallet::call_index(2)]
#[pallet::weight(<T as Config>::WeightInfo::periodic_trust_score_update())]
pub fn periodic_trust_score_update(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
// Eğer önceki update devam ediyorsa bekle
ensure!(!BatchUpdateInProgress::<T>::get(), Error::<T>::UpdateInProgress);
// Yeni periyodik güncellemeyi başlat
Self::update_all_trust_scores(OriginFor::<T>::root())?;
// Bir sonraki periyodik güncellemeyi schedule et
let current_block = pezframe_system::Pallet::<T>::block_number();
let next_update_block = current_block + T::UpdateInterval::get();
Self::deposit_event(Event::PeriodicUpdateScheduled { next_block: next_update_block });
Ok(())
}
}
impl<T: Config> Pallet<T> {
pub fn calculate_trust_score(who: &T::AccountId) -> Result<T::Score, Error<T>> {
ensure!(T::CitizenshipSource::is_citizen(who), Error::<T>::NotACitizen);
let (staking_score_raw, _) = T::StakingScoreSource::get_staking_score(who);
if staking_score_raw.is_zero() {
return Ok(T::Score::zero());
}
let staking_u128: u128 = staking_score_raw.into();
let referral_u128: u128 = T::ReferralScoreSource::get_referral_score(who).into();
let perwerde_u128: u128 = T::PerwerdeScoreSource::get_perwerde_score(who).into();
let tiki_u128: u128 = T::TikiScoreSource::get_tiki_score(who).into();
let base = T::ScoreMultiplierBase::get();
let weighted_sum = staking_u128
.saturating_mul(100)
.saturating_add(referral_u128.saturating_mul(300))
.saturating_add(perwerde_u128.saturating_mul(300))
.saturating_add(tiki_u128.saturating_mul(300));
let final_score_u128 = staking_u128
.saturating_mul(weighted_sum)
.checked_div(base)
.ok_or(Error::<T>::CalculationOverflow)?;
let new_trust_score = T::Score::try_from(final_score_u128)
.map_err(|_| Error::<T>::CalculationOverflow)?;
Ok(new_trust_score)
}
pub fn update_score_for_account(who: &T::AccountId) -> Result<T::Score, Error<T>> {
let old_score = Self::trust_score_of(who);
let new_score = Self::calculate_trust_score(who)?;
if old_score != new_score {
<TrustScores<T>>::insert(who, new_score);
let old_total = Self::total_active_trust_score();
let new_total = old_total.saturating_sub(old_score).saturating_add(new_score);
<TotalActiveTrustScore<T>>::put(new_total);
Self::deposit_event(Event::TrustScoreUpdated {
who: who.clone(),
old_score,
new_score,
});
Self::deposit_event(Event::TotalTrustScoreUpdated { new_total });
}
Ok(new_score)
}
/// Returns the configured batch size for trust score updates
/// Configurable via MaxBatchSize to allow governance control
fn calculate_optimal_batch_size() -> u32 {
T::MaxBatchSize::get()
}
}
impl<T: Config> TrustScoreProvider<T::AccountId> for Pallet<T> {
fn trust_score_of(who: &T::AccountId) -> u128 {
Self::trust_score_of(who).into()
}
}
impl<T: Config> TrustScoreUpdater<T::AccountId> for Pallet<T> {
fn on_score_component_changed(who: &T::AccountId) {
if let Err(e) = Self::update_score_for_account(who) {
log::error!("Failed to update trust score for {:?}: {:?}", who, e);
}
}
}
}