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 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 #[rpc(meta, name = "exit")]
171 fn exit(&self, meta: Self::Metadata) -> Result<()>;
172
173 #[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 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 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 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 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 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 let (response_sender, response_receiver) = oneshot_channel();
399
400 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 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 let (response_sender, response_receiver) = oneshot_channel();
428
429 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 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 let (response_sender, response_receiver) = oneshot_channel();
456
457 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 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 let (response_sender, response_receiver) = oneshot_channel();
484
485 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 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 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
930pub 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) .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 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
995pub 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
1008pub fn runtime() -> Runtime {
1010 tokio::runtime::Builder::new_multi_thread()
1011 .thread_name("solAdminRpcRt")
1012 .enable_all()
1013 .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(), 0u16, ),
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 #[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, 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 #[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 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 assert!(result["result"].is_boolean());
1431 assert!(result["result"].as_bool().unwrap());
1433 }
1434
1435 #[test]
1436 fn test_is_generating_snapshots_no_controller() {
1437 let rpc = RpcHandler::start_with_config(TestConfig::default());
1439 let RpcHandler { io, .. } = rpc;
1440
1441 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 assert!(result["error"].is_object());
1466 assert_eq!(
1467 result["error"]["message"].as_str().unwrap(),
1468 "snapshot_controller unavailable"
1469 );
1470 }
1471}