Skip to main content

light_client/
local_test_validator.rs

1use std::process::{Command, Stdio};
2
3use light_prover_client::helpers::get_project_root;
4
5/// Configuration for an upgradeable program to deploy to the validator.
6#[derive(Debug, Clone)]
7pub struct UpgradeableProgramConfig {
8    /// The program ID (public key) of the program
9    pub program_id: String,
10    /// Path to the compiled program (.so file)
11    pub program_path: String,
12    /// The upgrade authority for the program
13    pub upgrade_authority: String,
14}
15
16impl UpgradeableProgramConfig {
17    pub fn new(program_id: String, program_path: String, upgrade_authority: String) -> Self {
18        Self {
19            program_id,
20            program_path,
21            upgrade_authority,
22        }
23    }
24}
25
26#[derive(Debug)]
27pub struct LightValidatorConfig {
28    pub enable_indexer: bool,
29    pub enable_prover: bool,
30    pub wait_time: u64,
31    /// Non-upgradeable programs: (program_id, program_path)
32    pub sbf_programs: Vec<(String, String)>,
33    /// Upgradeable programs to deploy with a valid upgrade authority.
34    /// Use this when the program needs a valid upgrade authority (e.g., for compression config).
35    pub upgradeable_programs: Vec<UpgradeableProgramConfig>,
36    pub limit_ledger_size: Option<u64>,
37    /// Use surfpool instead of solana-test-validator
38    pub use_surfpool: bool,
39    /// Additional arguments to pass to the validator (e.g., "--account <ADDRESS> <FILEPATH>")
40    pub validator_args: Vec<String>,
41}
42
43impl Default for LightValidatorConfig {
44    fn default() -> Self {
45        Self {
46            enable_indexer: false,
47            enable_prover: false,
48            wait_time: 35,
49            sbf_programs: vec![],
50            upgradeable_programs: vec![],
51            limit_ledger_size: None,
52            use_surfpool: true,
53            validator_args: vec![],
54        }
55    }
56}
57
58pub async fn spawn_validator(config: LightValidatorConfig) {
59    if let Some(project_root) = get_project_root() {
60        let path = "cli/test_bin/run test-validator";
61        let mut path = format!("{}/{}", project_root.trim(), path);
62        if !config.enable_indexer {
63            path.push_str(" --skip-indexer");
64        }
65
66        if let Some(limit_ledger_size) = config.limit_ledger_size {
67            path.push_str(&format!(" --limit-ledger-size {}", limit_ledger_size));
68        }
69
70        for sbf_program in config.sbf_programs.iter() {
71            path.push_str(&format!(
72                " --sbf-program {} {}",
73                sbf_program.0, sbf_program.1
74            ));
75        }
76
77        for upgradeable_program in config.upgradeable_programs.iter() {
78            path.push_str(&format!(
79                " --upgradeable-program {} {} {}",
80                upgradeable_program.program_id,
81                upgradeable_program.program_path,
82                upgradeable_program.upgrade_authority
83            ));
84        }
85
86        if !config.enable_prover {
87            path.push_str(" --skip-prover");
88        }
89
90        if config.use_surfpool {
91            path.push_str(" --use-surfpool");
92        }
93
94        for arg in config.validator_args.iter() {
95            path.push_str(&format!(" {}", arg));
96        }
97
98        println!("Starting validator with command: {}", path);
99
100        if config.use_surfpool {
101            // The CLI starts surfpool, prover, and photon, then exits once all
102            // services are ready. Wait for it to finish so we know everything
103            // is up before the test proceeds.
104            let mut child = Command::new("sh")
105                .arg("-c")
106                .arg(path)
107                .stdin(Stdio::null())
108                .stdout(Stdio::inherit())
109                .stderr(Stdio::inherit())
110                .spawn()
111                .expect("Failed to start server process");
112            let status = child.wait().expect("Failed to wait for CLI process");
113            assert!(status.success(), "CLI exited with error: {}", status);
114        } else {
115            let child = Command::new("sh")
116                .arg("-c")
117                .arg(path)
118                .stdin(Stdio::null())
119                .stdout(Stdio::null())
120                .stderr(Stdio::null())
121                .spawn()
122                .expect("Failed to start server process");
123            std::mem::drop(child);
124            tokio::time::sleep(tokio::time::Duration::from_secs(config.wait_time)).await;
125        }
126    }
127}