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