yedad_common 0.1.0

Shared types, constants, and utilities for Yedad network
Documentation
//! yedad_common
//! Shared types, constants and errors for Yedad network

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

// ==================== Constants ====================
pub const ADDRESS_SIZE: usize = 12;
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;

// ==================== Coin & Economics ====================
pub const COIN_DECIMALS: u32 = 8; // 8 decimal places (like Bitcoin)
pub const COIN: u64 = 10_u64.pow(COIN_DECIMALS); // 100,000,000 nanoYD per YD
pub const MAX_SUPPLY: u64 = 19_000_000 * COIN; // 19 million YD in nanoYD
pub const HALVING_PULSES: u64 = 84_000_000;
pub const GENESIS_REWARD: u64 = MAX_SUPPLY / 2 / HALVING_PULSES;

// ==================== Types ====================
pub type Address = [u8; ADDRESS_SIZE];
pub type Hash = [u8; HASH_SIZE];
pub type NanoYD = u64; // 1 YD = 100,000,000 nanoYD

// ==================== Slot ====================
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub struct Slot {
    pub pulse_id: u32,
    pub user_address: Address,
    pub ladder_id: u32,
    pub step_number: u32,
    pub balance: NanoYD,
    pub last_hash: Hash,
}

impl Slot {
    pub fn new(user_address: Address, ladder_id: u32, step_number: u32, last_hash: Hash) -> Self {
        Self {
            pulse_id: 0,
            user_address,
            ladder_id,
            step_number,
            balance: 0,
            last_hash,
        }
    }

    pub fn to_bytes(&self) -> [u8; SLOT_SIZE] {
        let mut bytes = [0u8; SLOT_SIZE];
        bytes[0..4].copy_from_slice(&self.pulse_id.to_le_bytes());
        bytes[4..16].copy_from_slice(&self.user_address);
        bytes[16..20].copy_from_slice(&self.ladder_id.to_le_bytes());
        bytes[20..24].copy_from_slice(&self.step_number.to_le_bytes());
        bytes[24..32].copy_from_slice(&self.balance.to_le_bytes());
        bytes[32..64].copy_from_slice(&self.last_hash);
        bytes
    }

    pub fn from_bytes(bytes: &[u8; SLOT_SIZE]) -> Self {
        let pulse_id = u32::from_le_bytes(bytes[0..4].try_into().unwrap());
        let mut user_address = [0u8; ADDRESS_SIZE];
        user_address.copy_from_slice(&bytes[4..16]);
        let ladder_id = u32::from_le_bytes(bytes[16..20].try_into().unwrap());
        let step_number = u32::from_le_bytes(bytes[20..24].try_into().unwrap());
        let balance = u64::from_le_bytes(bytes[24..32].try_into().unwrap());
        let mut last_hash = [0u8; HASH_SIZE];
        last_hash.copy_from_slice(&bytes[32..64]);
        Self {
            pulse_id,
            user_address,
            ladder_id,
            step_number,
            balance,
            last_hash,
        }
    }
}

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

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

// ==================== Notepad Entry ====================
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NotepadEntry {
    pub pulse_id: u32,
    pub timestamp: u64,  // milliseconds since epoch
    pub root_hash: Hash, // root hash of all slots
    pub pulse_hash: u32, // 4-byte output (CRC32-like)
}

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

// ==================== 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)
}