yedad_eye 0.1.0

YEDAD EYE Matrix Engine - Real-time Network Explorer and Synchronizer
Documentation
// yedad_eye/src/providers/database.rs

#[cfg(feature = "rocksdb")]
use crate::{
    error::EyeError,
    types::{EyeCell, NodeMatrixRow},
    YedadEyeProvider,
};
#[cfg(feature = "rocksdb")]
use async_trait::async_trait;
#[cfg(feature = "rocksdb")]
use rocksdb::{Options, DB};
#[cfg(feature = "rocksdb")]
use std::{collections::HashMap, sync::Arc};
#[cfg(feature = "rocksdb")]
const ADDRESS_SIZE: usize = 12;
#[cfg(feature = "rocksdb")]
const HASH_SIZE: usize = 32;
#[cfg(feature = "rocksdb")]
const SLOT_SIZE: usize = 64;
#[cfg(feature = "rocksdb")]
pub struct DatabaseEyeProvider {
    db: Arc<DB>,
}

#[cfg(feature = "rocksdb")]
impl DatabaseEyeProvider {
    pub fn new(db_path: &str) -> Result<Self, EyeError> {
        let mut opts = Options::default();
        opts.create_if_missing(false);
        let db = DB::open_for_read_only(&opts, db_path, false)
            .map_err(|e| EyeError::DatabaseError(e.to_string()))?;
        Ok(Self { db: Arc::new(db) })
    }

    /// تبدیل ۶۴ بایت خام به ساختار Slot (مطابق با storage.rs)
    fn slot_from_bytes(bytes: &[u8]) -> Result<yedad_common::Slot, EyeError> {
        if bytes.len() != SLOT_SIZE {
            return Err(EyeError::DatabaseError("Invalid slot size".to_string()));
        }

        let pulse_id_u32 = u32::from_be_bytes(
            bytes[0..4]
                .try_into()
                .map_err(|_| EyeError::DatabaseError("Invalid pulse_id".to_string()))?,
        );
        let mut user_address = [0u8; ADDRESS_SIZE];
        user_address.copy_from_slice(&bytes[4..16]);
        let step_number = u32::from_be_bytes(
            bytes[16..20]
                .try_into()
                .map_err(|_| EyeError::DatabaseError("Invalid step_number".to_string()))?,
        );
        let ladder_id = u32::from_be_bytes(
            bytes[20..24]
                .try_into()
                .map_err(|_| EyeError::DatabaseError("Invalid ladder_id".to_string()))?,
        );
        let balance = u64::from_be_bytes(
            bytes[24..32]
                .try_into()
                .map_err(|_| EyeError::DatabaseError("Invalid balance".to_string()))?,
        );
        let mut last_hash = [0u8; HASH_SIZE];
        last_hash.copy_from_slice(&bytes[32..64]);

        Ok(yedad_common::Slot {
            pulse_id: u64::from(pulse_id_u32),
            user_address,
            step_number,
            ladder_id,
            balance,
            last_hash,
        })
    }
}

#[cfg(feature = "rocksdb")]
#[async_trait]
impl YedadEyeProvider for DatabaseEyeProvider {
    async fn fetch_all_active_addresses(&self) -> Result<Vec<String>, EyeError> {
        let mut addresses = Vec::new();
        let prefix = b"s:";
        let iter = self.db.prefix_iterator(prefix);
        for (key, _) in iter.flatten() {
            if key.starts_with(prefix) && key.len() == prefix.len() + ADDRESS_SIZE {
                let addr = &key[prefix.len()..];
                addresses.push(hex::encode(addr));
            }
        }
        Ok(addresses)
    }
    #[cfg(feature = "rocksdb")]
    async fn fetch_node_row(&self, address: &str) -> Result<NodeMatrixRow, EyeError> {
        let addr_bytes = hex::decode(address).map_err(|e| EyeError::ParsingError(e.to_string()))?;
        if addr_bytes.len() != ADDRESS_SIZE {
            return Err(EyeError::ParsingError("Invalid address length".to_string()));
        }

        // خواندن اسلات از روی دیسک (با پیشوند s:)
        let mut key = Vec::with_capacity(2 + ADDRESS_SIZE);
        key.extend_from_slice(b"s:");
        key.extend_from_slice(&addr_bytes);

        let slot_bytes = self
            .db
            .get(&key)
            .map_err(|e| EyeError::DatabaseError(e.to_string()))?
            .ok_or_else(|| EyeError::NodeNotFound(address.to_string()))?;

        let slot = Self::slot_from_bytes(&slot_bytes)?;

        // بازخوانی ساختار ردیف‌های افقی نوت‌پد ۲ از RocksDB (پیشوند n:)
        let mut history = HashMap::new();
        let mut prefix = Vec::with_capacity(2 + addr_bytes.len());
        prefix.extend_from_slice(b"n:");
        prefix.extend_from_slice(&addr_bytes);
        let mut iter = self.db.prefix_iterator(&prefix);

        while let Some(Ok((key, value))) = iter.next() {
            if key.starts_with(&prefix) && value.len() == 16 {
                let pulse_id = u32::from_be_bytes(
                    value[0..4]
                        .try_into()
                        .map_err(|_| EyeError::DatabaseError("Invalid pulse_id".to_string()))?,
                );
                let state_crc = u32::from_be_bytes(
                    value[4..8]
                        .try_into()
                        .map_err(|_| EyeError::DatabaseError("Invalid crc".to_string()))?,
                );
                // ۸ بایت بعدی prev_hash است که فعلاً نیاز نیست
                history.insert(
                    pulse_id,
                    EyeCell {
                        pulse_id,
                        state_crc,
                    },
                );
            }
        }

        Ok(NodeMatrixRow {
            address: address.to_string(),
            current_rank: "Full Node Local".to_string(),
            balance: slot.balance,
            step_number: slot.step_number,
            history,
        })
    }
}