solana-validator-optimizer 2.3.0

Optimized Solana validator tool with snapshot prefetching, RPC caching, and Prometheus metrics.
Documentation
use crate::{config::AppConfig, rpc_health, system_health};
use anyhow::Result;
use serde::Serialize;

#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum CheckStatus {
    Pass,
    Warn,
    Fail,
}

#[derive(Debug, Clone, Serialize)]
pub struct PreflightCheck {
    pub name: String,
    pub status: CheckStatus,
    pub message: String,
}

#[derive(Debug, Clone, Serialize)]
pub struct PreflightReport {
    pub endpoint: String,
    pub ready: bool,
    pub checks: Vec<PreflightCheck>,
    pub warnings: Vec<String>,
}

pub async fn run(config: &AppConfig) -> Result<PreflightReport> {
    run_with_expected_genesis(config, None).await
}

pub async fn run_with_expected_genesis(
    config: &AppConfig,
    expected_genesis_hash: Option<&str>,
) -> Result<PreflightReport> {
    let rpc = rpc_health::check_rpc_health(config).await?;
    let system = system_health::check_system_health();

    Ok(evaluate_reports(rpc, system, expected_genesis_hash))
}

fn evaluate_reports(
    rpc: rpc_health::RpcHealthReport,
    system: system_health::SystemHealthReport,
    expected_genesis_hash: Option<&str>,
) -> PreflightReport {
    let mut checks = Vec::new();

    checks.push(PreflightCheck {
        name: "rpc_health".to_string(),
        status: if rpc.healthy {
            CheckStatus::Pass
        } else {
            CheckStatus::Fail
        },
        message: format!("RPC health status: {}", rpc.health_status),
    });

    checks.push(match &rpc.solana_version {
        Some(version) => PreflightCheck {
            name: "solana_version".to_string(),
            status: CheckStatus::Pass,
            message: format!("Detected Solana/Agave version: {version}"),
        },
        None => PreflightCheck {
            name: "solana_version".to_string(),
            status: CheckStatus::Fail,
            message: "Unable to determine Solana/Agave version".to_string(),
        },
    });

    checks.push(match (&rpc.genesis_hash, expected_genesis_hash) {
        (Some(observed), Some(expected)) if observed == expected => PreflightCheck {
            name: "genesis_hash".to_string(),
            status: CheckStatus::Pass,
            message: format!("Genesis hash matches expected value: {observed}"),
        },
        (Some(observed), Some(expected)) => PreflightCheck {
            name: "genesis_hash".to_string(),
            status: CheckStatus::Fail,
            message: format!("Genesis hash mismatch: expected {expected}, observed {observed}"),
        },
        (Some(observed), None) => PreflightCheck {
            name: "genesis_hash".to_string(),
            status: CheckStatus::Pass,
            message: format!("Genesis hash: {observed}"),
        },
        (None, _) => PreflightCheck {
            name: "genesis_hash".to_string(),
            status: CheckStatus::Fail,
            message: "Unable to retrieve genesis hash".to_string(),
        },
    });

    checks.push(match rpc.slot {
        Some(slot) => PreflightCheck {
            name: "current_slot".to_string(),
            status: CheckStatus::Pass,
            message: format!("Current slot: {slot}"),
        },
        None => PreflightCheck {
            name: "current_slot".to_string(),
            status: CheckStatus::Fail,
            message: "Unable to retrieve current slot".to_string(),
        },
    });

    checks.push(match &rpc.latest_blockhash {
        Some(blockhash) => PreflightCheck {
            name: "latest_blockhash".to_string(),
            status: CheckStatus::Pass,
            message: format!("Latest blockhash available: {blockhash}"),
        },
        None => PreflightCheck {
            name: "latest_blockhash".to_string(),
            status: CheckStatus::Fail,
            message: "Unable to retrieve latest blockhash".to_string(),
        },
    });

    let latency_values = [
        ("getHealth", Some(rpc.get_health_latency_ms)),
        ("getVersion", rpc.get_version_latency_ms),
        ("getSlot", rpc.get_slot_latency_ms),
        ("getLatestBlockhash", rpc.get_latest_blockhash_latency_ms),
    ];

    let slow_calls: Vec<String> = latency_values
        .iter()
        .filter_map(|(name, latency)| match latency {
            Some(ms) if *ms > 1_000 => Some(format!("{name}: {ms} ms")),
            _ => None,
        })
        .collect();

    checks.push(if slow_calls.is_empty() {
        PreflightCheck {
            name: "rpc_latency".to_string(),
            status: CheckStatus::Pass,
            message: "RPC latency is within the 1000 ms threshold".to_string(),
        }
    } else {
        PreflightCheck {
            name: "rpc_latency".to_string(),
            status: CheckStatus::Warn,
            message: format!("High RPC latency detected: {}", slow_calls.join(", ")),
        }
    });

    checks.push(PreflightCheck {
        name: "cpu".to_string(),
        status: if system.logical_cpu_count < 2 {
            CheckStatus::Fail
        } else if system.logical_cpu_count < 8 {
            CheckStatus::Warn
        } else {
            CheckStatus::Pass
        },
        message: format!("Logical CPU count: {}", system.logical_cpu_count),
    });

    checks.push(PreflightCheck {
        name: "memory_total".to_string(),
        status: if system.total_memory_gib() < 4.0 {
            CheckStatus::Fail
        } else if system.total_memory_gib() < 16.0 {
            CheckStatus::Warn
        } else {
            CheckStatus::Pass
        },
        message: format!("Total memory: {:.1} GiB", system.total_memory_gib()),
    });

    checks.push(PreflightCheck {
        name: "memory_available".to_string(),
        status: if system.available_memory_gib() < 4.0 {
            CheckStatus::Warn
        } else {
            CheckStatus::Pass
        },
        message: format!("Available memory: {:.2} GiB", system.available_memory_gib()),
    });

    checks.push(match system.disk_available_gib() {
        Some(available) => PreflightCheck {
            name: "disk_available".to_string(),
            status: if available < 10.0 {
                CheckStatus::Fail
            } else if available < 50.0 {
                CheckStatus::Warn
            } else {
                CheckStatus::Pass
            },
            message: match system.disk_total_gib() {
                Some(total) => {
                    format!("Disk space: {available:.1} GiB available / {total:.1} GiB total")
                }
                None => format!("Disk space available: {available:.1} GiB"),
            },
        },
        None => PreflightCheck {
            name: "disk_available".to_string(),
            status: CheckStatus::Warn,
            message: "Unable to determine disk capacity".to_string(),
        },
    });

    let ready = !checks.iter().any(|check| check.status == CheckStatus::Fail);

    PreflightReport {
        endpoint: rpc.endpoint,
        ready,
        checks,
        warnings: rpc
            .warnings
            .into_iter()
            .filter(|warning| !warning.contains("latency is high"))
            .collect(),
    }
}

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

    fn healthy_rpc_report() -> rpc_health::RpcHealthReport {
        rpc_health::RpcHealthReport {
            endpoint: "http://localhost:8899".to_string(),
            healthy: true,
            health_status: "ok".to_string(),
            solana_version: Some("4.2.0-rc.1".to_string()),
            genesis_hash: Some("test-genesis-hash".to_string()),
            slot: Some(438_200_491),
            latest_blockhash: Some("test-blockhash".to_string()),
            get_health_latency_ms: 100,
            get_version_latency_ms: Some(100),
            get_genesis_hash_latency_ms: Some(100),
            get_slot_latency_ms: Some(100),
            get_latest_blockhash_latency_ms: Some(100),
            warnings: Vec::new(),
        }
    }

    fn healthy_system_report() -> system_health::SystemHealthReport {
        system_health::SystemHealthReport {
            logical_cpu_count: 16,
            total_memory_bytes: 64 * 1024 * 1024 * 1024,
            available_memory_bytes: 32 * 1024 * 1024 * 1024,
            disk_total_bytes: Some(2 * 1024 * 1024 * 1024 * 1024),
            disk_available_bytes: Some(500 * 1024 * 1024 * 1024),
        }
    }

    #[test]
    fn healthy_rpc_is_ready() {
        let report = evaluate_reports(healthy_rpc_report(), healthy_system_report(), None);

        assert!(report.ready);
        assert!(report
            .checks
            .iter()
            .all(|check| check.status != CheckStatus::Fail));
    }

    #[test]
    fn high_latency_warns_but_remains_ready() {
        let mut rpc = healthy_rpc_report();
        rpc.get_health_latency_ms = 1_500;
        rpc.warnings
            .push("getHealth latency is high: 1500 ms".to_string());

        let report = evaluate_reports(rpc, healthy_system_report(), None);

        assert!(report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "rpc_latency" && check.status == CheckStatus::Warn }));
    }

    #[test]
    fn unhealthy_rpc_is_not_ready() {
        let mut rpc = healthy_rpc_report();
        rpc.healthy = false;
        rpc.health_status = "unhealthy".to_string();

        let report = evaluate_reports(rpc, healthy_system_report(), None);

        assert!(!report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "rpc_health" && check.status == CheckStatus::Fail }));
    }

    #[test]
    fn matching_expected_genesis_is_ready() {
        let rpc = healthy_rpc_report();

        let report = evaluate_reports(rpc, healthy_system_report(), Some("test-genesis-hash"));

        assert!(report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "genesis_hash" && check.status == CheckStatus::Pass }));
    }

    #[test]
    fn mismatched_expected_genesis_is_not_ready() {
        let rpc = healthy_rpc_report();

        let report = evaluate_reports(rpc, healthy_system_report(), Some("wrong-genesis-hash"));

        assert!(!report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "genesis_hash" && check.status == CheckStatus::Fail }));
    }

    #[test]
    fn missing_genesis_hash_is_not_ready() {
        let mut rpc = healthy_rpc_report();
        rpc.genesis_hash = None;

        let report = evaluate_reports(rpc, healthy_system_report(), None);

        assert!(!report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "genesis_hash" && check.status == CheckStatus::Fail }));
    }

    #[test]
    fn missing_slot_is_not_ready() {
        let mut rpc = healthy_rpc_report();
        rpc.slot = None;

        let report = evaluate_reports(rpc, healthy_system_report(), None);

        assert!(!report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "current_slot" && check.status == CheckStatus::Fail }));
    }
    #[test]
    fn low_memory_is_not_ready() {
        let mut system = healthy_system_report();
        system.total_memory_bytes = 2 * 1024 * 1024 * 1024;

        let report = evaluate_reports(healthy_rpc_report(), system, None);

        assert!(!report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "memory_total" && check.status == CheckStatus::Fail }));
    }

    #[test]
    fn low_available_memory_warns_but_remains_ready() {
        let mut system = healthy_system_report();
        system.available_memory_bytes = 512 * 1024 * 1024;

        let report = evaluate_reports(healthy_rpc_report(), system, None);

        assert!(report.ready);
        assert!(report.checks.iter().any(|check| {
            check.name == "memory_available" && check.status == CheckStatus::Warn
        }));
    }

    #[test]
    fn low_disk_warns_but_remains_ready() {
        let mut system = healthy_system_report();
        system.disk_available_bytes = Some(25 * 1024 * 1024 * 1024);

        let report = evaluate_reports(healthy_rpc_report(), system, None);

        assert!(report.ready);
        assert!(report
            .checks
            .iter()
            .any(|check| { check.name == "disk_available" && check.status == CheckStatus::Warn }));
    }
}