yedad_common 0.3.2

Shared types, constants, and utilities for Yedad network
Documentation
//! # yedad_common
//!
//! قوانین پایه، نسبت‌های هندسی شبکه سارگونه، محاسبات هش تجمیعی (Blake3) و مکانیزم نوت‌پد (CRC32).
//! این کریت زیربنای اصلی کل شبکه یداد محسوب می‌شود.

use serde::{Deserialize, Serialize};
use thiserror::Error;

// ============================================================================
// Constants & Hexagonal Network Settings (تنظیمات هندسه شش‌ضلعی سارها)
// ============================================================================

/// اندازه آدرس به بایت (۱۲ بایت)
pub const ADDRESS_SIZE: usize = 12;

/// اندازه هش خروجی Blake3 به بایت (۳۲ بایت)
pub const HASH_SIZE: usize = 32;

/// اندازه اسلات سریالایز شده (۶۴ بایت)
pub const SLOT_SIZE: usize = 64;

/// فاصله زمانی هر پالس به میلی‌ثانیه (۱.۵ ثانیه)
pub const PULSE_INTERVAL_MS: u64 = 1500;

/// ارتفاع پیش‌فرض نردبان (تعداد گام‌ها)
pub const DEFAULT_LADDER_HEIGHT: usize = 100_000;

/// دوره تغییر نقش نودها بر اساس تعداد پالس (هر ۴۸۰۰ پالس = ۲ ساعت)
pub const ROLE_SHUFFLE_PULSES: u64 = 4800;

// درصدهای تقسیم پاداش لایه‌ها طبق استراتژی شما
pub const DIAMOND_REWARD_PERC: u64 = 60; // ۶۰ درصد سهم نودهای الماسی
pub const GOLD_REWARD_PERC: u64 = 30; // ۳۰ درصد سهم نودهای طلایی
pub const REGULAR_REWARD_PERC: u64 = 10; // ۱۰ درصد سهم نودهای معمولی

// نسبت‌های همسایگی در شبکه مِش سارگونه (شش‌ضلعی لانه‌زنبوری)
pub const GOLD_TO_DIAMOND_RATIO: usize = 6;
pub const REGULAR_TO_GOLD_RATIO: usize = 6;

// ============================================================================
// Coin & Economics
// ============================================================================

/// تعداد ارقام اعشار سکه (۸ رقم، مانند ساتوشی در بیت‌کوین)
pub const COIN_DECIMALS: u32 = 8;

/// One YD in nanoYD
pub const ONE_YD: u64 = 100_000_000;

/// کل موجودی شبکه (19 میلیون واحد کامل به همراه اعشاری نانو‌یداد)
pub const MAX_SUPPLY: u64 = 19_000_000 * ONE_YD;

/// پاداش اولیه هر پالس در شروع شبکه (۱۲.۵ میلیون واحد نانو یداد)
pub const INITIAL_PULSE_REWARD: u64 = 12_500_000;

/// تعداد پالس‌های هر دوره هاوینگ (نصف شدن پاداش هر ۸۴ میلیون پالس)
pub const HALVING_PULSES: u64 = 84_000_000;

// ============================================================================
// Core Structures & Type Aliases
// ============================================================================

pub type Address = [u8; ADDRESS_SIZE];
pub type Hash = [u8; HASH_SIZE];

/// ساختار اسلات (شناسنامه و وضعیت هر آدرس در شبکه)
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Slot {
    pub user_address: Address,
    pub pulse_id: u32,    // ۴ بایت: نمایش آخرین پالس دریافتی از نوت‌پد
    pub notepad_crc: u32, // ۴ بایت: خروجی CRC32 جدید که سراسر شبکه منتشر می‌شود
    pub ladder_id: u32,
    pub step_number: u32,
    pub balance: u64,
    pub last_hash: Hash, // ۳۲ بایت: آخرین هش تراکنش بر مبنای هش لدر Blake3
}

impl Slot {
    pub fn new(user_address: Address, ladder_id: u32, step_number: u32, last_hash: Hash) -> Self {
        Self {
            user_address,
            pulse_id: 1, // شروع پالس اولیه اسلات از ۱ همگام با کلاک جنسیس
            notepad_crc: 0,
            ladder_id,
            step_number,
            balance: 0,
            last_hash,
        }
    }

    pub fn to_bytes(&self) -> Vec<u8> {
        bincode::serialize(self).unwrap_or_default()
    }

    pub fn from_bytes(bytes: &[u8]) -> Self {
        bincode::deserialize(bytes).unwrap()
    }
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SlotKey {
    pub user_address: Address,
    pub ladder_id: u32,
    pub last_hash: Hash,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Transaction {
    pub timestamp: u32,
    pub from: Address,
    pub to: Address,
    pub amount: u64,
    pub proof: Hash,
}

/// ساختار تاریخچه تراکنش‌ها به صورت محلی در زیر هر اسلات (مطابق با دیتابیس RocksDB شما)
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct HistoryRecord {
    pub counterparty: Address,
    pub timestamp: u32,
    pub amount: u64,
}

/// ساختار نوت‌پد عمودی و واحد شبکه برای ثبت زمان مستقل
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct NotepadEntry {
    pub pulse_id: u32,      // شماره پالس (۴ بایت)
    pub final_blake3: Hash, // هش تجمیعی نهایی ۳۲ بایتی کل شبکه محاسبه شده توسط نود الماسی
    pub notepad_crc: u32,   // خروجی نهایی CRC32 (۴ بایت) برای انتشار به اسلات‌ها
}

// ============================================================================
// Error Types
// ============================================================================

#[derive(Error, Debug, Clone, Serialize, Deserialize)]
pub enum YedadError {
    #[error("Account not found")]
    AccountNotFound,
    #[error("Insufficient balance")]
    InsufficientBalance,
    #[error("Invalid proof")]
    InvalidProof,
    #[error("Already exists")]
    AlreadyExists,
    #[error("Ladder exhausted")]
    LadderExhausted,
    #[error("Arithmetic overflow")]
    ArithmeticOverflow,
    #[error("IO error: {0}")]
    Io(String),
}

// ============================================================================
// Consensus Functions (توابع منطقی محاسبات لایه‌ای و نوت‌پد)
// ============================================================================

/// فاز ۱ (Blake3): محاسبات داخلی تجمیعی نودها در هر پالس.
/// نود معمولی هش اسلات‌های خود را فرستاده، نود طلایی آن‌ها را هش تجمیعی می‌کند و الماسی هش نهایی ۳۲ بایتی را تولید می‌کند.
pub fn aggregate_hashes(hashes: &[Hash]) -> Hash {
    let mut hasher = blake3::Hasher::new();
    for h in hashes {
        hasher.update(h);
    }
    *hasher.finalize().as_bytes()
}

/// فاز ۲ (CRC32): انتشار نهایی زمان در دیتابیس اسلات‌ها.
/// فرمول: ترکیب CRC32 قبلی نوت‌پد (۴ بایت) + CRC32 حاصل از هش لحظه‌ای ۳۲ بایتی کل شبکه (۴ بایت)
/// محاسبه مجدد CRC32 روی این آرایه ۸ بایتی انجام شده و خروجی ۴ بایت جدید هم در نوت‌پد و هم در تمام اسلات‌ها ذخیره می‌شود.
pub fn calculate_interlinked_crc(old_crc: u32, current_calc_hash: &Hash) -> u32 {
    use crc32fast::Hasher as CrcHasher;

    // ۱. محاسبه CRC32 لحظه‌ای بر روی هش ۳۲ بایتی کل شبکه
    let mut current_hasher = CrcHasher::new();
    current_hasher.update(current_calc_hash);
    let current_calc_crc = current_hasher.finalize();

    // ۲. ترکیب CRC قبلی و CRC لحظه‌ای در یک بلاک ۸ بایتی به صورت Big Endian
    let mut combined = [0u8; 8];
    combined[0..4].copy_from_slice(&old_crc.to_be_bytes());
    combined[4..8].copy_from_slice(&current_calc_crc.to_be_bytes());

    // ۳. محاسبه CRC32 نهایی زنجیره‌وار روی بلاک ۸ بایتی
    let mut final_hasher = CrcHasher::new();
    final_hasher.update(&combined);
    final_hasher.finalize()
}

/// فرمول رای‌گیری اضطراری و تعیین جانشین نود آفلاین شده در کسر ثانیه.
/// فرمول: BLAKE3( آدرس اسلات مخصوص خود نود + شماره پالس هش شده )
/// کمترین عدد به دست آمده برنده لدر ارتقایافته خواهد بود.
pub fn calculate_emergency_vote_score(slot_address: &Address, pulse_num: u64) -> Hash {
    // ۱. ابتدا شماره پالس را هش میکنیم
    let mut pulse_hasher = blake3::Hasher::new();
    pulse_hasher.update(&pulse_num.to_le_bytes());
    let pulse_hash = pulse_hasher.finalize();

    // ۲. ادغام آدرس اسلات با هش پالس و گرفتن هش نهایی با blake3
    let mut final_hasher = blake3::Hasher::new();
    final_hasher.update(slot_address);
    final_hasher.update(pulse_hash.as_bytes());
    *final_hasher.finalize().as_bytes()
}

// ============================================================================
// Helper Functions
// ============================================================================

pub fn address_to_string(address: &Address) -> String {
    hex::encode(address)
}

pub fn string_to_address(s: &str) -> Result<Address, hex::FromHexError> {
    let bytes = hex::decode(s)?;
    let mut addr = [0u8; ADDRESS_SIZE];
    addr.copy_from_slice(&bytes);
    Ok(addr)
}

// ============================================================================
// Tests
// ============================================================================

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_slot_serialization() {
        let addr = [1u8; 12];
        let last_hash = [2u8; 32];
        // آرگومان چهارم (ارتفاع پیش‌فرض یا step_number) اصلاح شد تا امضای تابع ۴ تایی تکمیل شود
        let slot = Slot::new(addr, 1, DEFAULT_LADDER_HEIGHT as u32, last_hash);
        let bytes = slot.to_bytes();
        let restored = Slot::from_bytes(&bytes);
        assert_eq!(slot.user_address, restored.user_address);
        assert_eq!(slot.pulse_id, restored.pulse_id);
        assert_eq!(slot.last_hash, restored.last_hash);
        assert_eq!(slot.balance, restored.balance);
    }

    #[test]
    fn test_blake3_aggregation() {
        let h1 = [1u8; 32];
        let h2 = [2u8; 32];
        let agg1 = aggregate_hashes(&[h1, h2]);
        let agg2 = aggregate_hashes(&[h1, h2]);
        assert_eq!(agg1, agg2);
    }

    #[test]
    fn test_interlinked_crc() {
        let old_crc = 12345u32;
        let mock_hash = [5u8; 32];
        let crc1 = calculate_interlinked_crc(old_crc, &mock_hash);
        let crc2 = calculate_interlinked_crc(old_crc, &mock_hash);
        assert_eq!(crc1, crc2);
    }

    #[test]
    fn test_emergency_vote_logic() {
        let addr = [1u8; 12];
        let score1 = calculate_emergency_vote_score(&addr, 100);
        let score2 = calculate_emergency_vote_score(&addr, 100);
        assert_eq!(score1, score2);
    }
}