Skip to main content

agave_validator/
admin_rpc_service.rs

1use {
2    agave_votor::event::VotorEvent,
3    crossbeam_channel::Sender,
4    jsonrpc_core::{BoxFuture, ErrorCode, MetaIoHandler, Metadata, Result},
5    jsonrpc_core_client::{RpcError, transports::ipc},
6    jsonrpc_derive::rpc,
7    jsonrpc_ipc_server::{
8        RequestContext, ServerBuilder, tokio::sync::oneshot::channel as oneshot_channel,
9    },
10    log::*,
11    serde::{Deserialize, Serialize, de::Deserializer},
12    solana_clock::Slot,
13    solana_core::{
14        admin_rpc_post_init::AdminRpcRequestMetadataPostInit,
15        banking_stage::{
16            BankingControlMsg, BankingStage,
17            transaction_scheduler::scheduler_controller::SchedulerConfig,
18        },
19        consensus::{Tower, tower_storage::TowerStorage},
20        repair::repair_service,
21        validator::{
22            BlockProductionMethod, SchedulerPacing, TransactionStructure, ValidatorStartProgress,
23        },
24    },
25    solana_geyser_plugin_manager::GeyserPluginManagerRequest,
26    solana_gossip::contact_info::{ContactInfo, Protocol, SOCKET_ADDR_UNSPECIFIED},
27    solana_keypair::{Keypair, read_keypair_file},
28    solana_metrics::{datapoint_info, datapoint_warn},
29    solana_pubkey::Pubkey,
30    solana_runtime::snapshot_controller::SnapshotController,
31    solana_signer::Signer,
32    solana_validator_exit::Exit,
33    std::{
34        collections::{HashMap, HashSet},
35        env, error,
36        fmt::{self, Display},
37        net::{IpAddr, SocketAddr},
38        num::NonZeroUsize,
39        path::{Path, PathBuf},
40        sync::{
41            Arc, RwLock,
42            atomic::{AtomicBool, Ordering},
43        },
44        thread::{self, Builder},
45        time::{Duration, Instant, SystemTime},
46    },
47    tokio::runtime::Runtime,
48};
49
50#[derive(Clone)]
51pub struct AdminRpcRequestMetadata {
52    pub rpc_addr: Option<SocketAddr>,
53    pub start_time: SystemTime,
54    pub start_progress: Arc<RwLock<ValidatorStartProgress>>,
55    pub validator_exit: Arc<RwLock<Exit>>,
56    pub validator_exit_backpressure: HashMap<String, Arc<AtomicBool>>,
57    pub authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
58    pub tower_storage: Arc<dyn TowerStorage>,
59    pub staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
60    pub post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
61    pub rpc_to_plugin_manager_sender: Option<Sender<GeyserPluginManagerRequest>>,
62}
63
64impl Metadata for AdminRpcRequestMetadata {}
65
66impl AdminRpcRequestMetadata {
67    fn with_post_init<F, R>(&self, func: F) -> Result<R>
68    where
69        F: FnOnce(&AdminRpcRequestMetadataPostInit) -> Result<R>,
70    {
71        if let Some(post_init) = self.post_init.read().unwrap().as_ref() {
72            func(post_init)
73        } else {
74            Err(jsonrpc_core::error::Error::invalid_params(
75                "Retry once validator start up is complete",
76            ))
77        }
78    }
79
80    fn snapshot_controller(&self) -> Option<Arc<SnapshotController>> {
81        self.with_post_init(|post_init| Ok(post_init.snapshot_controller.clone()))
82            .map_err(|_| {
83                // The error from with_post_init is not relevant, as it is meant for RPC callers
84                warn!("snapshot_controller unavailable, shutting down without taking snapshot");
85            })
86            .ok()
87    }
88}
89
90#[derive(Debug, Deserialize, Serialize)]
91pub struct AdminRpcContactInfo {
92    pub id: String,
93    pub gossip: SocketAddr,
94    pub tvu: SocketAddr,
95    pub serve_repair_quic: SocketAddr,
96    pub tpu: SocketAddr,
97    pub tpu_forwards: SocketAddr,
98    pub tpu_vote: SocketAddr,
99    pub rpc: SocketAddr,
100    pub rpc_pubsub: SocketAddr,
101    pub serve_repair: SocketAddr,
102    pub last_updated_timestamp: u64,
103    pub shred_version: u16,
104}
105
106#[derive(Debug, Deserialize, Serialize)]
107pub struct AdminRpcRepairWhitelist {
108    pub whitelist: Vec<Pubkey>,
109}
110
111impl From<ContactInfo> for AdminRpcContactInfo {
112    fn from(node: ContactInfo) -> Self {
113        macro_rules! unwrap_socket {
114            ($name:ident) => {
115                node.$name().unwrap_or(SOCKET_ADDR_UNSPECIFIED)
116            };
117            ($name:ident, $protocol:expr) => {
118                node.$name($protocol).unwrap_or(SOCKET_ADDR_UNSPECIFIED)
119            };
120        }
121        Self {
122            id: node.pubkey().to_string(),
123            last_updated_timestamp: node.wallclock(),
124            gossip: unwrap_socket!(gossip),
125            tvu: unwrap_socket!(tvu, Protocol::UDP),
126            serve_repair_quic: unwrap_socket!(serve_repair, Protocol::QUIC),
127            tpu: unwrap_socket!(tpu, Protocol::UDP),
128            tpu_forwards: unwrap_socket!(tpu_forwards, Protocol::UDP),
129            tpu_vote: unwrap_socket!(tpu_vote, Protocol::UDP),
130            rpc: unwrap_socket!(rpc),
131            rpc_pubsub: unwrap_socket!(rpc_pubsub),
132            serve_repair: unwrap_socket!(serve_repair, Protocol::UDP),
133            shred_version: node.shred_version(),
134        }
135    }
136}
137
138impl Display for AdminRpcContactInfo {
139    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
140        writeln!(f, "Identity: {}", self.id)?;
141        writeln!(f, "Gossip: {}", self.gossip)?;
142        writeln!(f, "TVU: {}", self.tvu)?;
143        writeln!(f, "TPU: {}", self.tpu)?;
144        writeln!(f, "TPU Forwards: {}", self.tpu_forwards)?;
145        writeln!(f, "TPU Votes: {}", self.tpu_vote)?;
146        writeln!(f, "RPC: {}", self.rpc)?;
147        writeln!(f, "RPC Pubsub: {}", self.rpc_pubsub)?;
148        writeln!(f, "Serve Repair: {}", self.serve_repair)?;
149        writeln!(f, "Last Updated Timestamp: {}", self.last_updated_timestamp)?;
150        writeln!(f, "Shred Version: {}", self.shred_version)
151    }
152}
153impl solana_cli_output::VerboseDisplay for AdminRpcContactInfo {}
154impl solana_cli_output::QuietDisplay for AdminRpcContactInfo {}
155
156impl Display for AdminRpcRepairWhitelist {
157    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
158        writeln!(f, "Repair whitelist: {:?}", &self.whitelist)
159    }
160}
161impl solana_cli_output::VerboseDisplay for AdminRpcRepairWhitelist {}
162impl solana_cli_output::QuietDisplay for AdminRpcRepairWhitelist {}
163
164#[rpc]
165pub trait AdminRpc {
166    type Metadata;
167
168    /// Initiates validator exit; exit is asynchronous so the validator
169    /// will almost certainly still be running when this method returns
170    #[rpc(meta, name = "exit")]
171    fn exit(&self, meta: Self::Metadata) -> Result<()>;
172
173    /// Return the process id (pid)
174    #[rpc(meta, name = "pid")]
175    fn pid(&self, meta: Self::Metadata) -> Result<u32>;
176
177    #[rpc(meta, name = "reloadPlugin")]
178    fn reload_plugin(
179        &self,
180        meta: Self::Metadata,
181        name: String,
182        config_file: String,
183    ) -> BoxFuture<Result<()>>;
184
185    #[rpc(meta, name = "unloadPlugin")]
186    fn unload_plugin(&self, meta: Self::Metadata, name: String) -> BoxFuture<Result<()>>;
187
188    #[rpc(meta, name = "loadPlugin")]
189    fn load_plugin(&self, meta: Self::Metadata, config_file: String) -> BoxFuture<Result<String>>;
190
191    #[rpc(meta, name = "listPlugins")]
192    fn list_plugins(&self, meta: Self::Metadata) -> BoxFuture<Result<Vec<String>>>;
193
194    #[rpc(meta, name = "rpcAddress")]
195    fn rpc_addr(&self, meta: Self::Metadata) -> Result<Option<SocketAddr>>;
196
197    #[rpc(name = "setLogFilter")]
198    fn set_log_filter(&self, filter: String) -> Result<()>;
199
200    #[rpc(meta, name = "startTime")]
201    fn start_time(&self, meta: Self::Metadata) -> Result<SystemTime>;
202
203    #[rpc(meta, name = "startProgress")]
204    fn start_progress(&self, meta: Self::Metadata) -> Result<ValidatorStartProgress>;
205
206    #[rpc(meta, name = "addAuthorizedVoter")]
207    fn add_authorized_voter(&self, meta: Self::Metadata, keypair_file: String) -> Result<()>;
208
209    #[rpc(meta, name = "addAuthorizedVoterFromBytes")]
210    fn add_authorized_voter_from_bytes(&self, meta: Self::Metadata, keypair: Vec<u8>)
211    -> Result<()>;
212
213    #[rpc(meta, name = "removeAllAuthorizedVoters")]
214    fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()>;
215
216    #[rpc(meta, name = "setIdentity")]
217    fn set_identity(
218        &self,
219        meta: Self::Metadata,
220        keypair_file: String,
221        require_tower: bool,
222    ) -> Result<()>;
223
224    #[rpc(meta, name = "setIdentityFromBytes")]
225    fn set_identity_from_bytes(
226        &self,
227        meta: Self::Metadata,
228        identity_keypair: Vec<u8>,
229        require_tower: bool,
230    ) -> Result<()>;
231
232    #[rpc(meta, name = "setStakedNodesOverrides")]
233    fn set_staked_nodes_overrides(&self, meta: Self::Metadata, path: String) -> Result<()>;
234
235    #[rpc(meta, name = "contactInfo")]
236    fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo>;
237
238    #[rpc(meta, name = "selectActiveInterface")]
239    fn select_active_interface(&self, meta: Self::Metadata, interface: IpAddr) -> Result<()>;
240
241    #[rpc(meta, name = "repairShredFromPeer")]
242    fn repair_shred_from_peer(
243        &self,
244        meta: Self::Metadata,
245        pubkey: Option<Pubkey>,
246        slot: u64,
247        shred_index: u64,
248    ) -> Result<()>;
249
250    #[rpc(meta, name = "repairWhitelist")]
251    fn repair_whitelist(&self, meta: Self::Metadata) -> Result<AdminRpcRepairWhitelist>;
252
253    #[rpc(meta, name = "setRepairWhitelist")]
254    fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()>;
255
256    #[rpc(meta, name = "setPublicTpuAddress")]
257    fn set_public_tpu_address(
258        &self,
259        meta: Self::Metadata,
260        public_tpu_addr: SocketAddr,
261    ) -> Result<()>;
262
263    #[rpc(meta, name = "setPublicTpuForwardsAddress")]
264    fn set_public_tpu_forwards_address(
265        &self,
266        meta: Self::Metadata,
267        public_tpu_forwards_addr: SocketAddr,
268    ) -> Result<()>;
269
270    #[rpc(meta, name = "setPublicTvuAddress")]
271    fn set_public_tvu_address(
272        &self,
273        meta: Self::Metadata,
274        public_tvu_addr: SocketAddr,
275    ) -> Result<()>;
276
277    #[rpc(meta, name = "manageBlockProduction")]
278    fn manage_block_production(
279        &self,
280        meta: Self::Metadata,
281        block_production_method: BlockProductionMethod,
282        transaction_struct: TransactionStructure,
283        num_workers: NonZeroUsize,
284        scheduler_pacing: SchedulerPacing,
285    ) -> Result<()>;
286
287    #[rpc(meta, name = "isGeneratingSnapshots")]
288    fn is_generating_snapshots(&self, meta: Self::Metadata) -> Result<bool>;
289
290    #[rpc(meta, name = "blockstorePurge")]
291    fn blockstore_purge(&self, meta: Self::Metadata, maximum_purge_slot: Slot) -> Result<()>;
292}
293
294pub struct AdminRpcImpl;
295impl AdminRpc for AdminRpcImpl {
296    type Metadata = AdminRpcRequestMetadata;
297
298    fn exit(&self, meta: Self::Metadata) -> Result<()> {
299        debug!("exit admin rpc request received");
300
301        thread::Builder::new()
302            .name("solProcessExit".into())
303            .spawn(move || {
304                let start_time = Instant::now();
305
306                // Trigger a fastboot snapshot before exiting
307                if let Some(snapshot_controller) = meta.snapshot_controller() {
308                    let latest_snapshot_slot = snapshot_controller.latest_bank_snapshot_slot();
309
310                    info!("Requesting fastboot snapshot before exit");
311                    snapshot_controller.request_fastboot_snapshot();
312
313                    // Wait up to 5s for a snapshot to finish. This should allow time for the
314                    // fastboot snapshot to complete without stalling exit indefinitely.
315                    // The timeout will be hit in the event new roots are not being created.
316                    let timeout = Duration::from_secs(5);
317                    while snapshot_controller.latest_bank_snapshot_slot() == latest_snapshot_slot {
318                        if start_time.elapsed() > timeout {
319                            warn!("Timeout waiting for snapshot to complete");
320                            datapoint_warn!(
321                                "admin-rpc-snapshot-timeout",
322                                ("timeout_us", start_time.elapsed().as_micros(), i64)
323                            );
324                            break;
325                        }
326                        thread::sleep(Duration::from_millis(100));
327                    }
328                    info!(
329                        "Requesting fastboot snapshot before exit... Done in {:?}",
330                        start_time.elapsed()
331                    );
332                }
333
334                // Delay exit signal until this RPC request completes, otherwise the caller of `exit` might
335                // receive a confusing error as the validator shuts down before a response is sent back.
336                // If elapsed time has already taken 100ms, there is no need for further delay
337                if start_time.elapsed().as_millis() < 100 {
338                    thread::sleep(Duration::from_millis(100));
339                }
340
341                info!("validator exit requested");
342                meta.validator_exit.write().unwrap().exit();
343
344                if !meta.validator_exit_backpressure.is_empty() {
345                    let service_names = meta.validator_exit_backpressure.keys();
346                    info!("Wait for these services to complete: {service_names:?}");
347                    loop {
348                        // The initial sleep is a grace period to allow the services to raise their
349                        // backpressure flags.
350                        // Subsequent sleeps are to throttle how often we check and log.
351                        thread::sleep(Duration::from_secs(1));
352
353                        let mut any_flags_raised = false;
354                        for (name, flag) in meta.validator_exit_backpressure.iter() {
355                            let is_flag_raised = flag.load(Ordering::Relaxed);
356                            if is_flag_raised {
357                                info!("{name}'s exit backpressure flag is raised");
358                                any_flags_raised = true;
359                            }
360                        }
361                        if !any_flags_raised {
362                            break;
363                        }
364                    }
365                    info!("All services have completed");
366                }
367
368                // TODO: Debug why Exit doesn't always cause the validator to fully exit
369                // (rocksdb background processing or some other stuck thread perhaps?).
370                //
371                // If the process is still alive after five seconds, exit harder
372                thread::sleep(Duration::from_secs(
373                    env::var("SOLANA_VALIDATOR_EXIT_TIMEOUT")
374                        .ok()
375                        .and_then(|x| x.parse().ok())
376                        .unwrap_or(5),
377                ));
378                warn!("validator exit timeout");
379                std::process::exit(0);
380            })
381            .unwrap();
382
383        Ok(())
384    }
385
386    fn pid(&self, _meta: Self::Metadata) -> Result<u32> {
387        Ok(std::process::id())
388    }
389
390    fn reload_plugin(
391        &self,
392        meta: Self::Metadata,
393        name: String,
394        config_file: String,
395    ) -> BoxFuture<Result<()>> {
396        Box::pin(async move {
397            // Construct channel for plugin to respond to this particular rpc request instance
398            let (response_sender, response_receiver) = oneshot_channel();
399
400            // Send request to plugin manager if there is a geyser service
401            if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
402                rpc_to_manager_sender
403                    .send(GeyserPluginManagerRequest::ReloadPlugin {
404                        name,
405                        config_file,
406                        response_sender,
407                    })
408                    .expect("GeyerPluginService should never drop request receiver");
409            } else {
410                return Err(jsonrpc_core::Error {
411                    code: ErrorCode::InvalidRequest,
412                    message: "No geyser plugin service".to_string(),
413                    data: None,
414                });
415            }
416
417            // Await response from plugin manager
418            response_receiver
419                .await
420                .expect("GeyerPluginService's oneshot sender shouldn't drop early")
421        })
422    }
423
424    fn load_plugin(&self, meta: Self::Metadata, config_file: String) -> BoxFuture<Result<String>> {
425        Box::pin(async move {
426            // Construct channel for plugin to respond to this particular rpc request instance
427            let (response_sender, response_receiver) = oneshot_channel();
428
429            // Send request to plugin manager if there is a geyser service
430            if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
431                rpc_to_manager_sender
432                    .send(GeyserPluginManagerRequest::LoadPlugin {
433                        config_file,
434                        response_sender,
435                    })
436                    .expect("GeyerPluginService should never drop request receiver");
437            } else {
438                return Err(jsonrpc_core::Error {
439                    code: ErrorCode::InvalidRequest,
440                    message: "No geyser plugin service".to_string(),
441                    data: None,
442                });
443            }
444
445            // Await response from plugin manager
446            response_receiver
447                .await
448                .expect("GeyerPluginService's oneshot sender shouldn't drop early")
449        })
450    }
451
452    fn unload_plugin(&self, meta: Self::Metadata, name: String) -> BoxFuture<Result<()>> {
453        Box::pin(async move {
454            // Construct channel for plugin to respond to this particular rpc request instance
455            let (response_sender, response_receiver) = oneshot_channel();
456
457            // Send request to plugin manager if there is a geyser service
458            if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
459                rpc_to_manager_sender
460                    .send(GeyserPluginManagerRequest::UnloadPlugin {
461                        name,
462                        response_sender,
463                    })
464                    .expect("GeyerPluginService should never drop request receiver");
465            } else {
466                return Err(jsonrpc_core::Error {
467                    code: ErrorCode::InvalidRequest,
468                    message: "No geyser plugin service".to_string(),
469                    data: None,
470                });
471            }
472
473            // Await response from plugin manager
474            response_receiver
475                .await
476                .expect("GeyerPluginService's oneshot sender shouldn't drop early")
477        })
478    }
479
480    fn list_plugins(&self, meta: Self::Metadata) -> BoxFuture<Result<Vec<String>>> {
481        Box::pin(async move {
482            // Construct channel for plugin to respond to this particular rpc request instance
483            let (response_sender, response_receiver) = oneshot_channel();
484
485            // Send request to plugin manager
486            if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
487                rpc_to_manager_sender
488                    .send(GeyserPluginManagerRequest::ListPlugins { response_sender })
489                    .expect("GeyerPluginService should never drop request receiver");
490            } else {
491                return Err(jsonrpc_core::Error {
492                    code: ErrorCode::InvalidRequest,
493                    message: "No geyser plugin service".to_string(),
494                    data: None,
495                });
496            }
497
498            // Await response from plugin manager
499            response_receiver
500                .await
501                .expect("GeyerPluginService's oneshot sender shouldn't drop early")
502        })
503    }
504
505    fn rpc_addr(&self, meta: Self::Metadata) -> Result<Option<SocketAddr>> {
506        debug!("rpc_addr admin rpc request received");
507        Ok(meta.rpc_addr)
508    }
509
510    fn set_log_filter(&self, filter: String) -> Result<()> {
511        debug!("set_log_filter admin rpc request received");
512        agave_logger::setup_with(&filter);
513        Ok(())
514    }
515
516    fn start_time(&self, meta: Self::Metadata) -> Result<SystemTime> {
517        debug!("start_time admin rpc request received");
518        Ok(meta.start_time)
519    }
520
521    fn start_progress(&self, meta: Self::Metadata) -> Result<ValidatorStartProgress> {
522        debug!("start_progress admin rpc request received");
523        Ok(*meta.start_progress.read().unwrap())
524    }
525
526    fn add_authorized_voter(&self, meta: Self::Metadata, keypair_file: String) -> Result<()> {
527        debug!("add_authorized_voter request received");
528
529        let authorized_voter = read_keypair_file(keypair_file)
530            .map_err(|err| jsonrpc_core::error::Error::invalid_params(format!("{err}")))?;
531
532        AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter)
533    }
534
535    fn add_authorized_voter_from_bytes(
536        &self,
537        meta: Self::Metadata,
538        keypair: Vec<u8>,
539    ) -> Result<()> {
540        debug!("add_authorized_voter_from_bytes request received");
541
542        let authorized_voter = Keypair::try_from(keypair.as_ref()).map_err(|err| {
543            jsonrpc_core::error::Error::invalid_params(format!(
544                "Failed to read authorized voter keypair from provided byte array: {err}"
545            ))
546        })?;
547
548        AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter)
549    }
550
551    fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()> {
552        debug!("remove_all_authorized_voters received");
553        meta.authorized_voter_keypairs.write().unwrap().clear();
554        Ok(())
555    }
556
557    fn set_identity(
558        &self,
559        meta: Self::Metadata,
560        keypair_file: String,
561        require_tower: bool,
562    ) -> Result<()> {
563        debug!("set_identity request received");
564
565        let identity_keypair = read_keypair_file(&keypair_file).map_err(|err| {
566            jsonrpc_core::error::Error::invalid_params(format!(
567                "Failed to read identity keypair from {keypair_file}: {err}"
568            ))
569        })?;
570
571        AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower)
572    }
573
574    fn set_identity_from_bytes(
575        &self,
576        meta: Self::Metadata,
577        identity_keypair: Vec<u8>,
578        require_tower: bool,
579    ) -> Result<()> {
580        debug!("set_identity_from_bytes request received");
581
582        let identity_keypair = Keypair::try_from(identity_keypair.as_ref()).map_err(|err| {
583            jsonrpc_core::error::Error::invalid_params(format!(
584                "Failed to read identity keypair from provided byte array: {err}"
585            ))
586        })?;
587
588        AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower)
589    }
590
591    fn set_staked_nodes_overrides(&self, meta: Self::Metadata, path: String) -> Result<()> {
592        let loaded_config = load_staked_nodes_overrides(&path)
593            .map_err(|err| {
594                error!(
595                    "Failed to load staked nodes overrides from {}: {}",
596                    &path, err
597                );
598                jsonrpc_core::error::Error::internal_error()
599            })?
600            .staked_map_id;
601        let mut write_staked_nodes = meta.staked_nodes_overrides.write().unwrap();
602        write_staked_nodes.clear();
603        write_staked_nodes.extend(loaded_config);
604        info!("Staked nodes overrides loaded from {path}");
605        debug!("overrides map: {write_staked_nodes:?}");
606        Ok(())
607    }
608
609    fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo> {
610        meta.with_post_init(|post_init| Ok(post_init.cluster_info.my_contact_info().into()))
611    }
612
613    fn select_active_interface(&self, meta: Self::Metadata, interface: IpAddr) -> Result<()> {
614        debug!("select_active_interface received: {interface}");
615        meta.with_post_init(|post_init| {
616            let node = post_init.node.as_ref().ok_or_else(|| {
617                jsonrpc_core::Error::invalid_params("`Node` not initialized in post_init")
618            })?;
619
620            node.switch_active_interface(interface, &post_init.cluster_info)
621                .map_err(|e| {
622                    jsonrpc_core::Error::invalid_params(format!(
623                        "Switching failed due to error {e}"
624                    ))
625                })?;
626            info!("Switched primary interface to {interface}");
627            Ok(())
628        })
629    }
630
631    fn repair_shred_from_peer(
632        &self,
633        meta: Self::Metadata,
634        pubkey: Option<Pubkey>,
635        slot: u64,
636        shred_index: u64,
637    ) -> Result<()> {
638        debug!("repair_shred_from_peer request received");
639
640        meta.with_post_init(|post_init| {
641            repair_service::RepairService::request_repair_for_shred_from_peer(
642                post_init.cluster_info.clone(),
643                post_init.cluster_slots.clone(),
644                pubkey,
645                slot,
646                shred_index,
647                &post_init.repair_socket,
648                post_init.outstanding_repair_requests.clone(),
649            );
650            Ok(())
651        })
652    }
653
654    fn repair_whitelist(&self, meta: Self::Metadata) -> Result<AdminRpcRepairWhitelist> {
655        debug!("repair_whitelist request received");
656
657        meta.with_post_init(|post_init| {
658            let whitelist: Vec<_> = post_init
659                .repair_whitelist
660                .read()
661                .unwrap()
662                .iter()
663                .copied()
664                .collect();
665            Ok(AdminRpcRepairWhitelist { whitelist })
666        })
667    }
668
669    fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()> {
670        debug!("set_repair_whitelist request received");
671
672        let whitelist: HashSet<Pubkey> = whitelist.into_iter().collect();
673        meta.with_post_init(|post_init| {
674            *post_init.repair_whitelist.write().unwrap() = whitelist;
675            warn!(
676                "Repair whitelist set to {:?}",
677                &post_init.repair_whitelist.read().unwrap()
678            );
679            Ok(())
680        })
681    }
682
683    fn set_public_tpu_address(
684        &self,
685        meta: Self::Metadata,
686        public_tpu_addr: SocketAddr,
687    ) -> Result<()> {
688        debug!("set_public_tpu_address rpc request received: {public_tpu_addr}");
689
690        meta.with_post_init(|post_init| {
691            post_init
692                .cluster_info
693                .my_contact_info()
694                .tpu(Protocol::QUIC)
695                .ok_or_else(|| {
696                    error!(
697                        "The public TPU QUIC address isn't being published. The node is likely in \
698                         repair mode. See help for --restricted-repair-only-mode for more \
699                         information."
700                    );
701                    jsonrpc_core::error::Error::internal_error()
702                })?;
703            post_init
704                .cluster_info
705                .set_tpu_quic(public_tpu_addr)
706                .map_err(|err| {
707                    error!("Failed to set public TPU QUIC address to {public_tpu_addr}: {err}");
708                    jsonrpc_core::error::Error::internal_error()
709                })?;
710            let my_contact_info = post_init.cluster_info.my_contact_info();
711            warn!(
712                "Public TPU addresses set to {:?} (quic)",
713                my_contact_info.tpu(Protocol::QUIC),
714            );
715            Ok(())
716        })
717    }
718
719    fn set_public_tpu_forwards_address(
720        &self,
721        meta: Self::Metadata,
722        public_tpu_forwards_addr: SocketAddr,
723    ) -> Result<()> {
724        debug!("set_public_tpu_forwards_address rpc request received: {public_tpu_forwards_addr}");
725
726        meta.with_post_init(|post_init| {
727            post_init
728                .cluster_info
729                .my_contact_info()
730                .tpu_forwards(Protocol::QUIC)
731                .ok_or_else(|| {
732                    error!(
733                        "The public TPU Forwards address isn't being published. The node is \
734                         likely in repair mode. See help for --restricted-repair-only-mode for \
735                         more information."
736                    );
737                    jsonrpc_core::error::Error::internal_error()
738                })?;
739            post_init
740                .cluster_info
741                .set_tpu_forwards_quic(public_tpu_forwards_addr)
742                .map_err(|err| {
743                    error!(
744                        "Failed to set public TPU QUIC address to {public_tpu_forwards_addr}: \
745                         {err}"
746                    );
747                    jsonrpc_core::error::Error::internal_error()
748                })?;
749            let my_contact_info = post_init.cluster_info.my_contact_info();
750            warn!(
751                "Public TPU Forwards address set to {:?} (quic)",
752                my_contact_info.tpu_forwards(Protocol::QUIC),
753            );
754            Ok(())
755        })
756    }
757
758    fn set_public_tvu_address(
759        &self,
760        meta: Self::Metadata,
761        public_tvu_addr: SocketAddr,
762    ) -> Result<()> {
763        debug!("set_public_tvu_address rpc request received: {public_tvu_addr}");
764
765        meta.with_post_init(|post_init| {
766            post_init
767                .cluster_info
768                .my_contact_info()
769                .tvu(Protocol::UDP)
770                .ok_or_else(|| {
771                    error!(
772                        "The public TVU address isn't being published. The node is likely in \
773                         repair mode. See help for --restricted-repair-only-mode for more \
774                         information."
775                    );
776                    jsonrpc_core::error::Error::internal_error()
777                })?;
778            post_init
779                .cluster_info
780                .set_tvu_socket(public_tvu_addr)
781                .map_err(|err| {
782                    error!("Failed to set public TVU address to {public_tvu_addr}: {err}");
783                    jsonrpc_core::error::Error::internal_error()
784                })?;
785            let my_contact_info = post_init.cluster_info.my_contact_info();
786            warn!(
787                "Public TVU addresses set to {:?}",
788                my_contact_info.tvu(Protocol::UDP),
789            );
790            Ok(())
791        })
792    }
793
794    fn manage_block_production(
795        &self,
796        meta: Self::Metadata,
797        block_production_method: BlockProductionMethod,
798        transaction_struct: TransactionStructure,
799        num_workers: NonZeroUsize,
800        scheduler_pacing: SchedulerPacing,
801    ) -> Result<()> {
802        debug!("manage_block_production rpc request received");
803
804        if num_workers > BankingStage::max_num_workers() {
805            return Err(jsonrpc_core::error::Error::invalid_params(format!(
806                "Number of workers ({}) exceeds maximum allowed ({})",
807                num_workers,
808                BankingStage::max_num_workers()
809            )));
810        }
811
812        if transaction_struct != TransactionStructure::View {
813            warn!("TransactionStructure::Sdk has no effect on block production");
814        }
815
816        meta.with_post_init(|post_init| {
817            if post_init
818                .banking_control_sender
819                .try_send(BankingControlMsg::Internal {
820                    block_production_method,
821                    num_workers,
822                    config: SchedulerConfig { scheduler_pacing },
823                })
824                .is_err()
825            {
826                error!("Banking stage already switching schedulers");
827
828                return Err(jsonrpc_core::error::Error::internal_error());
829            }
830
831            Ok(())
832        })
833    }
834
835    fn is_generating_snapshots(&self, meta: Self::Metadata) -> Result<bool> {
836        if let Some(snapshot_controller) = meta.snapshot_controller() {
837            Ok(snapshot_controller.is_generating_snapshots())
838        } else {
839            Err(jsonrpc_core::error::Error::invalid_params(
840                "snapshot_controller unavailable",
841            ))
842        }
843    }
844
845    fn blockstore_purge(&self, meta: Self::Metadata, maximum_purge_slot: Slot) -> Result<()> {
846        meta.with_post_init(|post_init| {
847            post_init
848                .blockstore
849                .send_manual_purge_request(maximum_purge_slot)
850                .map_err(|err| jsonrpc_core::Error {
851                    code: ErrorCode::InvalidRequest,
852                    message: format!("{err}"),
853                    data: None,
854                })
855        })
856    }
857}
858
859impl AdminRpcImpl {
860    fn add_authorized_voter_keypair(
861        meta: AdminRpcRequestMetadata,
862        authorized_voter: Keypair,
863    ) -> Result<()> {
864        let mut authorized_voter_keypairs = meta.authorized_voter_keypairs.write().unwrap();
865
866        if authorized_voter_keypairs
867            .iter()
868            .any(|x| x.pubkey() == authorized_voter.pubkey())
869        {
870            Err(jsonrpc_core::error::Error::invalid_params(
871                "Authorized voter already present",
872            ))
873        } else {
874            authorized_voter_keypairs.push(Arc::new(authorized_voter));
875            Ok(())
876        }
877    }
878
879    fn set_identity_keypair(
880        meta: AdminRpcRequestMetadata,
881        identity_keypair: Keypair,
882        require_tower: bool,
883    ) -> Result<()> {
884        meta.with_post_init(|post_init| {
885            if require_tower {
886                let _ = Tower::restore(meta.tower_storage.as_ref(), &identity_keypair.pubkey())
887                    .map_err(|err| {
888                        jsonrpc_core::error::Error::invalid_params(format!(
889                            "Unable to load tower file for identity {}: {}",
890                            identity_keypair.pubkey(),
891                            err
892                        ))
893                    })?;
894            }
895
896            for (key, notifier) in &*post_init.notifies.read().unwrap() {
897                if let Err(err) = notifier.update_key(&identity_keypair) {
898                    error!("Error updating network layer keypair: {err} on {key:?}");
899                }
900            }
901
902            let old_identity = post_init.cluster_info.id();
903            let new_identity = identity_keypair.pubkey();
904            solana_metrics::set_host_id(new_identity.to_string());
905            // Emit the datapoint after updating metrics to emit the new pubkey
906            datapoint_info!(
907                "validator-set_identity",
908                ("old_id", old_identity.to_string(), String),
909                ("new_id", new_identity.to_string(), String),
910                ("version", solana_version::version!(), String),
911            );
912            post_init
913                .cluster_info
914                .set_keypair(Arc::new(identity_keypair));
915            post_init
916                .votor_event_sender
917                .send(VotorEvent::SetIdentity)
918                .map_err(|err| jsonrpc_core::error::Error {
919                    code: ErrorCode::InternalError,
920                    message: format!("Failed to send SetIdentity event: {err}").to_string(),
921                    data: None,
922                })?;
923
924            warn!("Identity set to {new_identity}");
925            Ok(())
926        })
927    }
928}
929
930// Start the Admin RPC interface
931pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
932    let admin_rpc_path = admin_rpc_path(ledger_path);
933
934    let event_loop = tokio::runtime::Builder::new_multi_thread()
935        .thread_name("solAdminRpcEl")
936        .worker_threads(3) // Three still seems like a lot, and better than the default of available core count
937        .enable_all()
938        .build()
939        .unwrap();
940
941    Builder::new()
942        .name("solAdminRpc".to_string())
943        .spawn(move || {
944            let mut io = MetaIoHandler::default();
945            io.extend_with(AdminRpcImpl.to_delegate());
946
947            let validator_exit = metadata.validator_exit.clone();
948            let server = ServerBuilder::with_meta_extractor(io, move |_req: &RequestContext| {
949                metadata.clone()
950            })
951            .event_loop_executor(event_loop.handle().clone())
952            .start(&format!("{}", admin_rpc_path.display()));
953
954            match server {
955                Err(err) => {
956                    warn!("Unable to start admin rpc service: {err:?}");
957                }
958                Ok(server) => {
959                    info!("started admin rpc service!");
960                    let close_handle = server.close_handle();
961                    validator_exit
962                        .write()
963                        .unwrap()
964                        .register_exit(Box::new(move || {
965                            close_handle.close();
966                        }));
967
968                    server.wait();
969                }
970            }
971        })
972        .unwrap();
973}
974
975fn admin_rpc_path(ledger_path: &Path) -> PathBuf {
976    #[cfg(target_family = "windows")]
977    {
978        // More information about the wackiness of pipe names over at
979        // https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
980        if let Some(ledger_filename) = ledger_path.file_name() {
981            PathBuf::from(format!(
982                "\\\\.\\pipe\\{}-admin.rpc",
983                ledger_filename.to_string_lossy()
984            ))
985        } else {
986            PathBuf::from("\\\\.\\pipe\\admin.rpc")
987        }
988    }
989    #[cfg(not(target_family = "windows"))]
990    {
991        ledger_path.join("admin.rpc")
992    }
993}
994
995// Connect to the Admin RPC interface
996pub async fn connect(ledger_path: &Path) -> std::result::Result<gen_client::Client, RpcError> {
997    let admin_rpc_path = admin_rpc_path(ledger_path);
998    if !admin_rpc_path.exists() {
999        Err(RpcError::Client(format!(
1000            "{} does not exist",
1001            admin_rpc_path.display()
1002        )))
1003    } else {
1004        ipc::connect::<_, gen_client::Client>(&format!("{}", admin_rpc_path.display())).await
1005    }
1006}
1007
1008// Create a runtime for use by client side admin RPC interface calls
1009pub fn runtime() -> Runtime {
1010    tokio::runtime::Builder::new_multi_thread()
1011        .thread_name("solAdminRpcRt")
1012        .enable_all()
1013        // The agave-validator subcommands make few admin RPC calls and block
1014        // on the results so two workers is plenty
1015        .worker_threads(2)
1016        .build()
1017        .expect("new tokio runtime")
1018}
1019
1020#[derive(Default, Deserialize, Clone)]
1021pub struct StakedNodesOverrides {
1022    #[serde(deserialize_with = "deserialize_pubkey_map")]
1023    pub staked_map_id: HashMap<Pubkey, u64>,
1024}
1025
1026pub fn deserialize_pubkey_map<'de, D>(des: D) -> std::result::Result<HashMap<Pubkey, u64>, D::Error>
1027where
1028    D: Deserializer<'de>,
1029{
1030    let container: HashMap<String, u64> = serde::Deserialize::deserialize(des)?;
1031    let mut container_typed: HashMap<Pubkey, u64> = HashMap::new();
1032    for (key, value) in container.iter() {
1033        let typed_key = Pubkey::try_from(key.as_str())
1034            .map_err(|_| serde::de::Error::invalid_type(serde::de::Unexpected::Map, &"PubKey"))?;
1035        container_typed.insert(typed_key, *value);
1036    }
1037    Ok(container_typed)
1038}
1039
1040pub fn load_staked_nodes_overrides(
1041    path: &String,
1042) -> std::result::Result<StakedNodesOverrides, Box<dyn error::Error>> {
1043    debug!("Loading staked nodes overrides configuration from {path}");
1044    if Path::new(&path).exists() {
1045        let file = std::fs::File::open(path)?;
1046        Ok(serde_yaml::from_reader(file)?)
1047    } else {
1048        Err(format!("Staked nodes overrides provided '{path}' a non-existing file path.").into())
1049    }
1050}
1051
1052#[cfg(test)]
1053mod tests {
1054    use {
1055        super::*,
1056        agave_snapshots::snapshot_config::SnapshotConfig,
1057        agave_votor::event::VotorEventSender,
1058        assert_matches::assert_matches,
1059        crossbeam_channel::unbounded,
1060        serde_json::Value,
1061        solana_accounts_db::{
1062            accounts_db::{ACCOUNTS_DB_CONFIG_FOR_TESTING, AccountsDbConfig},
1063            accounts_index::AccountSecondaryIndexes,
1064        },
1065        solana_core::{
1066            admin_rpc_post_init::{KeyUpdaterType, KeyUpdaters},
1067            consensus::tower_storage::NullTowerStorage,
1068            validator::{Validator, ValidatorConfig, ValidatorTpuConfig},
1069        },
1070        solana_gossip::{cluster_info::ClusterInfo, node::Node},
1071        solana_ledger::{
1072            blockstore::Blockstore,
1073            create_new_tmp_ledger,
1074            genesis_utils::{
1075                GenesisConfigInfo, create_genesis_config, create_genesis_config_with_leader,
1076            },
1077            get_tmp_ledger_path_auto_delete,
1078        },
1079        solana_net_utils::{SocketAddrSpace, sockets::bind_to_localhost_unique},
1080        solana_rpc::rpc::create_validator_exit,
1081        solana_runtime::{
1082            bank::{Bank, BankTestConfig},
1083            bank_forks::BankForks,
1084        },
1085        std::{collections::HashSet, fs::remove_dir_all, sync::atomic::AtomicBool},
1086        tokio::sync::mpsc,
1087    };
1088
1089    #[derive(Default)]
1090    struct TestConfig {
1091        account_indexes: AccountSecondaryIndexes,
1092        votor_event_sender: Option<VotorEventSender>,
1093    }
1094
1095    struct RpcHandler {
1096        io: MetaIoHandler<AdminRpcRequestMetadata>,
1097        meta: AdminRpcRequestMetadata,
1098    }
1099
1100    impl RpcHandler {
1101        fn _start() -> Self {
1102            Self::start_with_config(TestConfig::default())
1103        }
1104
1105        fn start_with_config(config: TestConfig) -> Self {
1106            let keypair = Arc::new(Keypair::new());
1107            let cluster_info = Arc::new(ClusterInfo::new(
1108                ContactInfo::new(
1109                    keypair.pubkey(),
1110                    solana_time_utils::timestamp(), // wallclock
1111                    0u16,                           // shred_version
1112                ),
1113                keypair,
1114                SocketAddrSpace::Unspecified,
1115            ));
1116            let exit = Arc::new(AtomicBool::new(false));
1117            let validator_exit = create_validator_exit(exit);
1118            let (bank_forks, vote_keypair) = new_bank_forks_with_config(BankTestConfig {
1119                accounts_db_config: AccountsDbConfig {
1120                    account_indexes: Some(config.account_indexes),
1121                    ..ACCOUNTS_DB_CONFIG_FOR_TESTING
1122                },
1123            });
1124
1125            let (snapshot_request_sender, _) = unbounded();
1126            let snapshot_controller = Arc::new(SnapshotController::new(
1127                snapshot_request_sender.clone(),
1128                SnapshotConfig::default(),
1129                bank_forks.read().unwrap().root(),
1130            ));
1131
1132            let ledger_path = get_tmp_ledger_path_auto_delete!();
1133            let blockstore = Arc::new(Blockstore::open(ledger_path.path()).unwrap());
1134
1135            let vote_account = vote_keypair.pubkey();
1136            let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
1137            let repair_whitelist = Arc::new(RwLock::new(HashSet::new()));
1138            let votor_event_sender = config.votor_event_sender.unwrap_or_else(|| {
1139                let (votor_event_sender, _) = unbounded();
1140                votor_event_sender
1141            });
1142            let meta = AdminRpcRequestMetadata {
1143                rpc_addr: None,
1144                start_time: SystemTime::now(),
1145                start_progress,
1146                validator_exit,
1147                validator_exit_backpressure: HashMap::default(),
1148                authorized_voter_keypairs: Arc::new(RwLock::new(vec![vote_keypair])),
1149                tower_storage: Arc::new(NullTowerStorage {}),
1150                post_init: Arc::new(RwLock::new(Some(AdminRpcRequestMetadataPostInit {
1151                    cluster_info,
1152                    bank_forks,
1153                    vote_account,
1154                    repair_whitelist,
1155                    notifies: Arc::new(RwLock::new(KeyUpdaters::default())),
1156                    repair_socket: Arc::new(bind_to_localhost_unique().expect("should bind")),
1157                    outstanding_repair_requests: Arc::<
1158                        RwLock<repair_service::OutstandingShredRepairs>,
1159                    >::default(),
1160                    cluster_slots: Arc::new(
1161                        solana_core::cluster_slots_service::cluster_slots::ClusterSlots::default_for_tests(),
1162                    ),
1163                    node: None,
1164                    banking_control_sender: mpsc::channel(1).0,
1165                    snapshot_controller,
1166                    blockstore,
1167                    votor_event_sender,
1168                }))),
1169                staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
1170                rpc_to_plugin_manager_sender: None,
1171            };
1172            let mut io = MetaIoHandler::default();
1173            io.extend_with(AdminRpcImpl.to_delegate());
1174
1175            Self { io, meta }
1176        }
1177    }
1178
1179    fn new_bank_forks_with_config(
1180        config: BankTestConfig,
1181    ) -> (Arc<RwLock<BankForks>>, Arc<Keypair>) {
1182        let GenesisConfigInfo {
1183            genesis_config,
1184            voting_keypair,
1185            ..
1186        } = create_genesis_config(1_000_000_000);
1187
1188        let bank = Bank::new_with_paths_for_tests(&genesis_config, Some(config), vec![], None);
1189        (BankForks::new_rw_arc(bank), Arc::new(voting_keypair))
1190    }
1191
1192    // This test checks that the rpc call to `set_identity` works a expected with
1193    // Bank but without validator.
1194    #[test]
1195    fn test_set_identity() {
1196        let (votor_event_sender, votor_event_receiver) = unbounded();
1197        let rpc = RpcHandler::start_with_config(TestConfig {
1198            account_indexes: AccountSecondaryIndexes::default(),
1199            votor_event_sender: Some(votor_event_sender),
1200        });
1201
1202        let RpcHandler { io, meta, .. } = rpc;
1203
1204        let expected_validator_id = Keypair::new();
1205        let validator_id_bytes = format!("{:?}", expected_validator_id.to_bytes());
1206
1207        let set_id_request = format!(
1208            r#"{{"jsonrpc":"2.0","id":1,"method":"setIdentityFromBytes","params":[{validator_id_bytes}, false]}}"#,
1209        );
1210        let response = io.handle_request_sync(&set_id_request, meta.clone());
1211        let actual_parsed_response: Value =
1212            serde_json::from_str(&response.expect("actual response"))
1213                .expect("actual response deserialization");
1214
1215        let expected_parsed_response: Value = serde_json::from_str(
1216            r#"{
1217                "id": 1,
1218                "jsonrpc": "2.0",
1219                "result": null
1220            }"#,
1221        )
1222        .expect("Failed to parse expected response");
1223        assert_eq!(actual_parsed_response, expected_parsed_response);
1224
1225        let contact_info_request =
1226            r#"{"jsonrpc":"2.0","id":1,"method":"contactInfo","params":[]}"#.to_string();
1227        let response = io.handle_request_sync(&contact_info_request, meta.clone());
1228        let parsed_response: Value = serde_json::from_str(&response.expect("actual response"))
1229            .expect("actual response deserialization");
1230        let actual_validator_id = parsed_response["result"]["id"]
1231            .as_str()
1232            .expect("Expected a string");
1233        assert_eq!(
1234            actual_validator_id,
1235            expected_validator_id.pubkey().to_string()
1236        );
1237        let event = votor_event_receiver
1238            .recv()
1239            .expect("Failed to receive SetIdentity event");
1240        assert_matches!(event, VotorEvent::SetIdentity);
1241    }
1242
1243    struct TestValidatorWithAdminRpc {
1244        meta: AdminRpcRequestMetadata,
1245        io: MetaIoHandler<AdminRpcRequestMetadata>,
1246        validator_ledger_path: PathBuf,
1247    }
1248
1249    impl TestValidatorWithAdminRpc {
1250        fn new() -> Self {
1251            let leader_keypair = Keypair::new();
1252            let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey());
1253
1254            let validator_keypair = Keypair::new();
1255            let validator_node = Node::new_localhost_with_pubkey(&validator_keypair.pubkey());
1256            let genesis_config =
1257                create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000)
1258                    .genesis_config;
1259            let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config);
1260
1261            let voting_keypair = Arc::new(Keypair::new());
1262            let voting_pubkey = voting_keypair.pubkey();
1263            let authorized_voter_keypairs = Arc::new(RwLock::new(vec![voting_keypair]));
1264            let validator_config = ValidatorConfig {
1265                rpc_addrs: Some((
1266                    validator_node.info.rpc().unwrap(),
1267                    validator_node.info.rpc_pubsub().unwrap(),
1268                )),
1269                ..ValidatorConfig::default_for_test()
1270            };
1271            let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
1272
1273            let post_init = Arc::new(RwLock::new(None));
1274            let meta = AdminRpcRequestMetadata {
1275                rpc_addr: validator_config.rpc_addrs.map(|(rpc_addr, _)| rpc_addr),
1276                start_time: SystemTime::now(),
1277                start_progress: start_progress.clone(),
1278                validator_exit: validator_config.validator_exit.clone(),
1279                validator_exit_backpressure: HashMap::default(),
1280                authorized_voter_keypairs: authorized_voter_keypairs.clone(),
1281                tower_storage: Arc::new(NullTowerStorage {}),
1282                post_init: post_init.clone(),
1283                staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
1284                rpc_to_plugin_manager_sender: None,
1285            };
1286
1287            let _validator = Validator::new(
1288                validator_node,
1289                Arc::new(validator_keypair),
1290                &validator_ledger_path,
1291                &voting_pubkey,
1292                authorized_voter_keypairs,
1293                vec![leader_node.info],
1294                &validator_config,
1295                None, // rpc_to_plugin_manager_receiver
1296                start_progress.clone(),
1297                SocketAddrSpace::Unspecified,
1298                ValidatorTpuConfig::new_for_tests(),
1299                post_init.clone(),
1300                None,
1301            )
1302            .expect("assume successful validator start");
1303            assert_eq!(
1304                *start_progress.read().unwrap(),
1305                ValidatorStartProgress::Running
1306            );
1307            let post_init = post_init.read().unwrap();
1308
1309            assert!(post_init.is_some());
1310            let post_init = post_init.as_ref().unwrap();
1311            let notifies = post_init.notifies.read().unwrap();
1312            let updater_keys: HashSet<KeyUpdaterType> =
1313                notifies.into_iter().map(|(key, _)| key.clone()).collect();
1314            assert_eq!(
1315                updater_keys,
1316                HashSet::from_iter(vec![
1317                    KeyUpdaterType::Tpu,
1318                    KeyUpdaterType::TpuForwards,
1319                    KeyUpdaterType::TpuVote,
1320                    KeyUpdaterType::Forward,
1321                    KeyUpdaterType::RpcService,
1322                    KeyUpdaterType::Bls,
1323                    KeyUpdaterType::BlsConnectionCache,
1324                ])
1325            );
1326            let mut io = MetaIoHandler::default();
1327            io.extend_with(AdminRpcImpl.to_delegate());
1328            Self {
1329                meta,
1330                io,
1331                validator_ledger_path,
1332            }
1333        }
1334
1335        fn handle_request(&self, request: &str) -> Option<String> {
1336            self.io.handle_request_sync(request, self.meta.clone())
1337        }
1338    }
1339
1340    impl Drop for TestValidatorWithAdminRpc {
1341        fn drop(&mut self) {
1342            remove_dir_all(self.validator_ledger_path.clone()).unwrap();
1343        }
1344    }
1345
1346    #[test]
1347    fn test_no_post_init_no_snapshot_controller() {
1348        let validator_exit = create_validator_exit(Arc::new(AtomicBool::new(false)));
1349        let voting_keypair = Arc::new(Keypair::new());
1350        let authorized_voter_keypairs = Arc::new(RwLock::new(vec![voting_keypair]));
1351        let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
1352
1353        let post_init = Arc::new(RwLock::new(None));
1354        let meta = AdminRpcRequestMetadata {
1355            rpc_addr: None,
1356            start_time: SystemTime::now(),
1357            start_progress: start_progress.clone(),
1358            validator_exit,
1359            validator_exit_backpressure: HashMap::default(),
1360            authorized_voter_keypairs: authorized_voter_keypairs.clone(),
1361            tower_storage: Arc::new(NullTowerStorage {}),
1362            post_init: post_init.clone(),
1363            staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
1364            rpc_to_plugin_manager_sender: None,
1365        };
1366
1367        let snapshot_controller = meta.snapshot_controller();
1368        assert!(snapshot_controller.is_none());
1369    }
1370
1371    // This test checks that `set_identity` call works with working validator and client.
1372    #[test]
1373    fn test_set_identity_with_validator() {
1374        let test_validator = TestValidatorWithAdminRpc::new();
1375        let expected_validator_id = Keypair::new();
1376        let validator_id_bytes = format!("{:?}", expected_validator_id.to_bytes());
1377
1378        let set_id_request = format!(
1379            r#"{{"jsonrpc":"2.0","id":1,"method":"setIdentityFromBytes","params":[{validator_id_bytes}, false]}}"#,
1380        );
1381        let response = test_validator.handle_request(&set_id_request);
1382        let actual_parsed_response: Value =
1383            serde_json::from_str(&response.expect("actual response"))
1384                .expect("actual response deserialization");
1385
1386        let expected_parsed_response: Value = serde_json::from_str(
1387            r#"{
1388                "id": 1,
1389                "jsonrpc": "2.0",
1390                "result": null
1391            }"#,
1392        )
1393        .expect("Failed to parse expected response");
1394        assert_eq!(actual_parsed_response, expected_parsed_response);
1395
1396        let contact_info_request =
1397            r#"{"jsonrpc":"2.0","id":1,"method":"contactInfo","params":[]}"#.to_string();
1398        let response = test_validator.handle_request(&contact_info_request);
1399        let parsed_response: Value = serde_json::from_str(&response.expect("actual response"))
1400            .expect("actual response deserialization");
1401        let actual_validator_id = parsed_response["result"]["id"]
1402            .as_str()
1403            .expect("Expected a string");
1404        assert_eq!(
1405            actual_validator_id,
1406            expected_validator_id.pubkey().to_string()
1407        );
1408
1409        let contact_info_request =
1410            r#"{"jsonrpc":"2.0","id":1,"method":"exit","params":[]}"#.to_string();
1411        let exit_response = test_validator.handle_request(&contact_info_request);
1412        let actual_parsed_response: Value =
1413            serde_json::from_str(&exit_response.expect("actual response"))
1414                .expect("actual response deserialization");
1415        assert_eq!(actual_parsed_response, expected_parsed_response);
1416    }
1417
1418    #[test]
1419    fn test_is_generating_snapshots() {
1420        // Test with snapshots enabled
1421        let rpc = RpcHandler::start_with_config(TestConfig::default());
1422        let RpcHandler { io, meta, .. } = rpc;
1423
1424        let request = r#"{"jsonrpc":"2.0","id":1,"method":"isGeneratingSnapshots","params":[]}"#;
1425        let response = io.handle_request_sync(request, meta.clone());
1426        let result: Value = serde_json::from_str(&response.expect("actual response"))
1427            .expect("actual response deserialization");
1428
1429        // Should return a boolean result indicating if snapshots are being generated
1430        assert!(result["result"].is_boolean());
1431        // Verify that snapshots are being generated since the test setup includes a snapshot controller
1432        assert!(result["result"].as_bool().unwrap());
1433    }
1434
1435    #[test]
1436    fn test_is_generating_snapshots_no_controller() {
1437        // Test with snapshots enabled
1438        let rpc = RpcHandler::start_with_config(TestConfig::default());
1439        let RpcHandler { io, .. } = rpc;
1440
1441        // Test with no post_init (snapshot_controller unavailable)
1442        let request = r#"{"jsonrpc":"2.0","id":1,"method":"isGeneratingSnapshots","params":[]}"#;
1443        let validator_exit = create_validator_exit(Arc::new(AtomicBool::new(false)));
1444        let authorized_voter_keypairs = Arc::new(RwLock::new(vec![Arc::new(Keypair::new())]));
1445        let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
1446
1447        let meta_no_post_init = AdminRpcRequestMetadata {
1448            rpc_addr: None,
1449            start_time: SystemTime::now(),
1450            start_progress,
1451            validator_exit,
1452            validator_exit_backpressure: HashMap::default(),
1453            authorized_voter_keypairs,
1454            tower_storage: Arc::new(NullTowerStorage {}),
1455            post_init: Arc::new(RwLock::new(None)),
1456            staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
1457            rpc_to_plugin_manager_sender: None,
1458        };
1459
1460        let response = io.handle_request_sync(request, meta_no_post_init);
1461        let result: Value = serde_json::from_str(&response.expect("actual response"))
1462            .expect("actual response deserialization");
1463
1464        // Should return an error when snapshot_controller is unavailable
1465        assert!(result["error"].is_object());
1466        assert_eq!(
1467            result["error"]["message"].as_str().unwrap(),
1468            "snapshot_controller unavailable"
1469        );
1470    }
1471}