miraland_validator/admin_rpc_service.rs
1use {
2 crossbeam_channel::Sender,
3 jsonrpc_core::{BoxFuture, ErrorCode, MetaIoHandler, Metadata, Result},
4 jsonrpc_core_client::{transports::ipc, RpcError},
5 jsonrpc_derive::rpc,
6 jsonrpc_ipc_server::{
7 tokio::sync::oneshot::channel as oneshot_channel, RequestContext, ServerBuilder,
8 },
9 jsonrpc_server_utils::tokio,
10 log::*,
11 miraland_accounts_db::accounts_index::AccountIndex,
12 miraland_core::{
13 admin_rpc_post_init::AdminRpcRequestMetadataPostInit,
14 consensus::{tower_storage::TowerStorage, Tower},
15 repair::repair_service,
16 validator::ValidatorStartProgress,
17 },
18 miraland_geyser_plugin_manager::GeyserPluginManagerRequest,
19 miraland_gossip::contact_info::{ContactInfo, Protocol, SOCKET_ADDR_UNSPECIFIED},
20 miraland_rpc::rpc::verify_pubkey,
21 miraland_rpc_client_api::{config::RpcAccountIndex, custom_error::RpcCustomError},
22 miraland_sdk::{
23 exit::Exit,
24 pubkey::Pubkey,
25 signature::{read_keypair_file, Keypair, Signer},
26 },
27 serde::{de::Deserializer, Deserialize, Serialize},
28 std::{
29 collections::{HashMap, HashSet},
30 error,
31 fmt::{self, Display},
32 net::SocketAddr,
33 path::{Path, PathBuf},
34 sync::{Arc, RwLock},
35 thread::{self, Builder},
36 time::{Duration, SystemTime},
37 },
38};
39
40#[derive(Clone)]
41pub struct AdminRpcRequestMetadata {
42 pub rpc_addr: Option<SocketAddr>,
43 pub start_time: SystemTime,
44 pub start_progress: Arc<RwLock<ValidatorStartProgress>>,
45 pub validator_exit: Arc<RwLock<Exit>>,
46 pub authorized_voter_keypairs: Arc<RwLock<Vec<Arc<Keypair>>>>,
47 pub tower_storage: Arc<dyn TowerStorage>,
48 pub staked_nodes_overrides: Arc<RwLock<HashMap<Pubkey, u64>>>,
49 pub post_init: Arc<RwLock<Option<AdminRpcRequestMetadataPostInit>>>,
50 pub rpc_to_plugin_manager_sender: Option<Sender<GeyserPluginManagerRequest>>,
51}
52
53impl Metadata for AdminRpcRequestMetadata {}
54
55impl AdminRpcRequestMetadata {
56 fn with_post_init<F, R>(&self, func: F) -> Result<R>
57 where
58 F: FnOnce(&AdminRpcRequestMetadataPostInit) -> Result<R>,
59 {
60 if let Some(post_init) = self.post_init.read().unwrap().as_ref() {
61 func(post_init)
62 } else {
63 Err(jsonrpc_core::error::Error::invalid_params(
64 "Retry once validator start up is complete",
65 ))
66 }
67 }
68}
69
70#[derive(Debug, Deserialize, Serialize)]
71pub struct AdminRpcContactInfo {
72 pub id: String,
73 pub gossip: SocketAddr,
74 pub tvu: SocketAddr,
75 pub tvu_quic: SocketAddr,
76 pub serve_repair_quic: SocketAddr,
77 pub tpu: SocketAddr,
78 pub tpu_forwards: SocketAddr,
79 pub tpu_vote: SocketAddr,
80 pub rpc: SocketAddr,
81 pub rpc_pubsub: SocketAddr,
82 pub serve_repair: SocketAddr,
83 pub last_updated_timestamp: u64,
84 pub shred_version: u16,
85}
86
87#[derive(Debug, Deserialize, Serialize)]
88pub struct AdminRpcRepairWhitelist {
89 pub whitelist: Vec<Pubkey>,
90}
91
92impl From<ContactInfo> for AdminRpcContactInfo {
93 fn from(node: ContactInfo) -> Self {
94 macro_rules! unwrap_socket {
95 ($name:ident) => {
96 node.$name().unwrap_or(SOCKET_ADDR_UNSPECIFIED)
97 };
98 ($name:ident, $protocol:expr) => {
99 node.$name($protocol).unwrap_or(SOCKET_ADDR_UNSPECIFIED)
100 };
101 }
102 Self {
103 id: node.pubkey().to_string(),
104 last_updated_timestamp: node.wallclock(),
105 gossip: unwrap_socket!(gossip),
106 tvu: unwrap_socket!(tvu, Protocol::UDP),
107 tvu_quic: unwrap_socket!(tvu, Protocol::QUIC),
108 serve_repair_quic: unwrap_socket!(serve_repair, Protocol::QUIC),
109 tpu: unwrap_socket!(tpu, Protocol::UDP),
110 tpu_forwards: unwrap_socket!(tpu_forwards, Protocol::UDP),
111 tpu_vote: unwrap_socket!(tpu_vote),
112 rpc: unwrap_socket!(rpc),
113 rpc_pubsub: unwrap_socket!(rpc_pubsub),
114 serve_repair: unwrap_socket!(serve_repair, Protocol::UDP),
115 shred_version: node.shred_version(),
116 }
117 }
118}
119
120impl Display for AdminRpcContactInfo {
121 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
122 writeln!(f, "Identity: {}", self.id)?;
123 writeln!(f, "Gossip: {}", self.gossip)?;
124 writeln!(f, "TVU: {}", self.tvu)?;
125 writeln!(f, "TVU QUIC: {}", self.tvu_quic)?;
126 writeln!(f, "TPU: {}", self.tpu)?;
127 writeln!(f, "TPU Forwards: {}", self.tpu_forwards)?;
128 writeln!(f, "TPU Votes: {}", self.tpu_vote)?;
129 writeln!(f, "RPC: {}", self.rpc)?;
130 writeln!(f, "RPC Pubsub: {}", self.rpc_pubsub)?;
131 writeln!(f, "Serve Repair: {}", self.serve_repair)?;
132 writeln!(f, "Last Updated Timestamp: {}", self.last_updated_timestamp)?;
133 writeln!(f, "Shred Version: {}", self.shred_version)
134 }
135}
136
137impl Display for AdminRpcRepairWhitelist {
138 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
139 writeln!(f, "Repair whitelist: {:?}", &self.whitelist)
140 }
141}
142
143#[rpc]
144pub trait AdminRpc {
145 type Metadata;
146
147 #[rpc(meta, name = "exit")]
148 fn exit(&self, meta: Self::Metadata) -> Result<()>;
149
150 #[rpc(meta, name = "reloadPlugin")]
151 fn reload_plugin(
152 &self,
153 meta: Self::Metadata,
154 name: String,
155 config_file: String,
156 ) -> BoxFuture<Result<()>>;
157
158 #[rpc(meta, name = "unloadPlugin")]
159 fn unload_plugin(&self, meta: Self::Metadata, name: String) -> BoxFuture<Result<()>>;
160
161 #[rpc(meta, name = "loadPlugin")]
162 fn load_plugin(&self, meta: Self::Metadata, config_file: String) -> BoxFuture<Result<String>>;
163
164 #[rpc(meta, name = "listPlugins")]
165 fn list_plugins(&self, meta: Self::Metadata) -> BoxFuture<Result<Vec<String>>>;
166
167 #[rpc(meta, name = "rpcAddress")]
168 fn rpc_addr(&self, meta: Self::Metadata) -> Result<Option<SocketAddr>>;
169
170 #[rpc(name = "setLogFilter")]
171 fn set_log_filter(&self, filter: String) -> Result<()>;
172
173 #[rpc(meta, name = "startTime")]
174 fn start_time(&self, meta: Self::Metadata) -> Result<SystemTime>;
175
176 #[rpc(meta, name = "startProgress")]
177 fn start_progress(&self, meta: Self::Metadata) -> Result<ValidatorStartProgress>;
178
179 #[rpc(meta, name = "addAuthorizedVoter")]
180 fn add_authorized_voter(&self, meta: Self::Metadata, keypair_file: String) -> Result<()>;
181
182 #[rpc(meta, name = "addAuthorizedVoterFromBytes")]
183 fn add_authorized_voter_from_bytes(&self, meta: Self::Metadata, keypair: Vec<u8>)
184 -> Result<()>;
185
186 #[rpc(meta, name = "removeAllAuthorizedVoters")]
187 fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()>;
188
189 #[rpc(meta, name = "setIdentity")]
190 fn set_identity(
191 &self,
192 meta: Self::Metadata,
193 keypair_file: String,
194 require_tower: bool,
195 ) -> Result<()>;
196
197 #[rpc(meta, name = "setIdentityFromBytes")]
198 fn set_identity_from_bytes(
199 &self,
200 meta: Self::Metadata,
201 identity_keypair: Vec<u8>,
202 require_tower: bool,
203 ) -> Result<()>;
204
205 #[rpc(meta, name = "setStakedNodesOverrides")]
206 fn set_staked_nodes_overrides(&self, meta: Self::Metadata, path: String) -> Result<()>;
207
208 #[rpc(meta, name = "contactInfo")]
209 fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo>;
210
211 #[rpc(meta, name = "repairShredFromPeer")]
212 fn repair_shred_from_peer(
213 &self,
214 meta: Self::Metadata,
215 pubkey: Option<Pubkey>,
216 slot: u64,
217 shred_index: u64,
218 ) -> Result<()>;
219
220 #[rpc(meta, name = "repairWhitelist")]
221 fn repair_whitelist(&self, meta: Self::Metadata) -> Result<AdminRpcRepairWhitelist>;
222
223 #[rpc(meta, name = "setRepairWhitelist")]
224 fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()>;
225
226 #[rpc(meta, name = "getSecondaryIndexKeySize")]
227 fn get_secondary_index_key_size(
228 &self,
229 meta: Self::Metadata,
230 pubkey_str: String,
231 ) -> Result<HashMap<RpcAccountIndex, usize>>;
232
233 #[rpc(meta, name = "setPublicTpuAddress")]
234 fn set_public_tpu_address(
235 &self,
236 meta: Self::Metadata,
237 public_tpu_addr: SocketAddr,
238 ) -> Result<()>;
239
240 #[rpc(meta, name = "setPublicTpuForwardsAddress")]
241 fn set_public_tpu_forwards_address(
242 &self,
243 meta: Self::Metadata,
244 public_tpu_forwards_addr: SocketAddr,
245 ) -> Result<()>;
246}
247
248pub struct AdminRpcImpl;
249impl AdminRpc for AdminRpcImpl {
250 type Metadata = AdminRpcRequestMetadata;
251
252 fn exit(&self, meta: Self::Metadata) -> Result<()> {
253 debug!("exit admin rpc request received");
254
255 thread::Builder::new()
256 .name("mlnProcessExit".into())
257 .spawn(move || {
258 // Delay exit signal until this RPC request completes, otherwise the caller of `exit` might
259 // receive a confusing error as the validator shuts down before a response is sent back.
260 thread::sleep(Duration::from_millis(100));
261
262 warn!("validator exit requested");
263 meta.validator_exit.write().unwrap().exit();
264
265 // TODO: Debug why Exit doesn't always cause the validator to fully exit
266 // (rocksdb background processing or some other stuck thread perhaps?).
267 //
268 // If the process is still alive after five seconds, exit harder
269 thread::sleep(Duration::from_secs(5));
270 warn!("validator exit timeout");
271 std::process::exit(0);
272 })
273 .unwrap();
274 Ok(())
275 }
276
277 fn reload_plugin(
278 &self,
279 meta: Self::Metadata,
280 name: String,
281 config_file: String,
282 ) -> BoxFuture<Result<()>> {
283 Box::pin(async move {
284 // Construct channel for plugin to respond to this particular rpc request instance
285 let (response_sender, response_receiver) = oneshot_channel();
286
287 // Send request to plugin manager if there is a geyser service
288 if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
289 rpc_to_manager_sender
290 .send(GeyserPluginManagerRequest::ReloadPlugin {
291 name,
292 config_file,
293 response_sender,
294 })
295 .expect("GeyerPluginService should never drop request receiver");
296 } else {
297 return Err(jsonrpc_core::Error {
298 code: ErrorCode::InvalidRequest,
299 message: "No geyser plugin service".to_string(),
300 data: None,
301 });
302 }
303
304 // Await response from plugin manager
305 response_receiver
306 .await
307 .expect("GeyerPluginService's oneshot sender shouldn't drop early")
308 })
309 }
310
311 fn load_plugin(&self, meta: Self::Metadata, config_file: String) -> BoxFuture<Result<String>> {
312 Box::pin(async move {
313 // Construct channel for plugin to respond to this particular rpc request instance
314 let (response_sender, response_receiver) = oneshot_channel();
315
316 // Send request to plugin manager if there is a geyser service
317 if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
318 rpc_to_manager_sender
319 .send(GeyserPluginManagerRequest::LoadPlugin {
320 config_file,
321 response_sender,
322 })
323 .expect("GeyerPluginService should never drop request receiver");
324 } else {
325 return Err(jsonrpc_core::Error {
326 code: ErrorCode::InvalidRequest,
327 message: "No geyser plugin service".to_string(),
328 data: None,
329 });
330 }
331
332 // Await response from plugin manager
333 response_receiver
334 .await
335 .expect("GeyerPluginService's oneshot sender shouldn't drop early")
336 })
337 }
338
339 fn unload_plugin(&self, meta: Self::Metadata, name: String) -> BoxFuture<Result<()>> {
340 Box::pin(async move {
341 // Construct channel for plugin to respond to this particular rpc request instance
342 let (response_sender, response_receiver) = oneshot_channel();
343
344 // Send request to plugin manager if there is a geyser service
345 if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
346 rpc_to_manager_sender
347 .send(GeyserPluginManagerRequest::UnloadPlugin {
348 name,
349 response_sender,
350 })
351 .expect("GeyerPluginService should never drop request receiver");
352 } else {
353 return Err(jsonrpc_core::Error {
354 code: ErrorCode::InvalidRequest,
355 message: "No geyser plugin service".to_string(),
356 data: None,
357 });
358 }
359
360 // Await response from plugin manager
361 response_receiver
362 .await
363 .expect("GeyerPluginService's oneshot sender shouldn't drop early")
364 })
365 }
366
367 fn list_plugins(&self, meta: Self::Metadata) -> BoxFuture<Result<Vec<String>>> {
368 Box::pin(async move {
369 // Construct channel for plugin to respond to this particular rpc request instance
370 let (response_sender, response_receiver) = oneshot_channel();
371
372 // Send request to plugin manager
373 if let Some(ref rpc_to_manager_sender) = meta.rpc_to_plugin_manager_sender {
374 rpc_to_manager_sender
375 .send(GeyserPluginManagerRequest::ListPlugins { response_sender })
376 .expect("GeyerPluginService should never drop request receiver");
377 } else {
378 return Err(jsonrpc_core::Error {
379 code: ErrorCode::InvalidRequest,
380 message: "No geyser plugin service".to_string(),
381 data: None,
382 });
383 }
384
385 // Await response from plugin manager
386 response_receiver
387 .await
388 .expect("GeyerPluginService's oneshot sender shouldn't drop early")
389 })
390 }
391
392 fn rpc_addr(&self, meta: Self::Metadata) -> Result<Option<SocketAddr>> {
393 debug!("rpc_addr admin rpc request received");
394 Ok(meta.rpc_addr)
395 }
396
397 fn set_log_filter(&self, filter: String) -> Result<()> {
398 debug!("set_log_filter admin rpc request received");
399 miraland_logger::setup_with(&filter);
400 Ok(())
401 }
402
403 fn start_time(&self, meta: Self::Metadata) -> Result<SystemTime> {
404 debug!("start_time admin rpc request received");
405 Ok(meta.start_time)
406 }
407
408 fn start_progress(&self, meta: Self::Metadata) -> Result<ValidatorStartProgress> {
409 debug!("start_progress admin rpc request received");
410 Ok(*meta.start_progress.read().unwrap())
411 }
412
413 fn add_authorized_voter(&self, meta: Self::Metadata, keypair_file: String) -> Result<()> {
414 debug!("add_authorized_voter request received");
415
416 let authorized_voter = read_keypair_file(keypair_file)
417 .map_err(|err| jsonrpc_core::error::Error::invalid_params(format!("{err}")))?;
418
419 AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter)
420 }
421
422 fn add_authorized_voter_from_bytes(
423 &self,
424 meta: Self::Metadata,
425 keypair: Vec<u8>,
426 ) -> Result<()> {
427 debug!("add_authorized_voter_from_bytes request received");
428
429 let authorized_voter = Keypair::from_bytes(&keypair).map_err(|err| {
430 jsonrpc_core::error::Error::invalid_params(format!(
431 "Failed to read authorized voter keypair from provided byte array: {err}"
432 ))
433 })?;
434
435 AdminRpcImpl::add_authorized_voter_keypair(meta, authorized_voter)
436 }
437
438 fn remove_all_authorized_voters(&self, meta: Self::Metadata) -> Result<()> {
439 debug!("remove_all_authorized_voters received");
440 meta.authorized_voter_keypairs.write().unwrap().clear();
441 Ok(())
442 }
443
444 fn set_identity(
445 &self,
446 meta: Self::Metadata,
447 keypair_file: String,
448 require_tower: bool,
449 ) -> Result<()> {
450 debug!("set_identity request received");
451
452 let identity_keypair = read_keypair_file(&keypair_file).map_err(|err| {
453 jsonrpc_core::error::Error::invalid_params(format!(
454 "Failed to read identity keypair from {keypair_file}: {err}"
455 ))
456 })?;
457
458 AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower)
459 }
460
461 fn set_identity_from_bytes(
462 &self,
463 meta: Self::Metadata,
464 identity_keypair: Vec<u8>,
465 require_tower: bool,
466 ) -> Result<()> {
467 debug!("set_identity_from_bytes request received");
468
469 let identity_keypair = Keypair::from_bytes(&identity_keypair).map_err(|err| {
470 jsonrpc_core::error::Error::invalid_params(format!(
471 "Failed to read identity keypair from provided byte array: {err}"
472 ))
473 })?;
474
475 AdminRpcImpl::set_identity_keypair(meta, identity_keypair, require_tower)
476 }
477
478 fn set_staked_nodes_overrides(&self, meta: Self::Metadata, path: String) -> Result<()> {
479 let loaded_config = load_staked_nodes_overrides(&path)
480 .map_err(|err| {
481 error!(
482 "Failed to load staked nodes overrides from {}: {}",
483 &path, err
484 );
485 jsonrpc_core::error::Error::internal_error()
486 })?
487 .staked_map_id;
488 let mut write_staked_nodes = meta.staked_nodes_overrides.write().unwrap();
489 write_staked_nodes.clear();
490 write_staked_nodes.extend(loaded_config);
491 info!("Staked nodes overrides loaded from {}", path);
492 debug!("overrides map: {:?}", write_staked_nodes);
493 Ok(())
494 }
495
496 fn contact_info(&self, meta: Self::Metadata) -> Result<AdminRpcContactInfo> {
497 meta.with_post_init(|post_init| Ok(post_init.cluster_info.my_contact_info().into()))
498 }
499
500 fn repair_shred_from_peer(
501 &self,
502 meta: Self::Metadata,
503 pubkey: Option<Pubkey>,
504 slot: u64,
505 shred_index: u64,
506 ) -> Result<()> {
507 debug!("repair_shred_from_peer request received");
508
509 meta.with_post_init(|post_init| {
510 repair_service::RepairService::request_repair_for_shred_from_peer(
511 post_init.cluster_info.clone(),
512 post_init.cluster_slots.clone(),
513 pubkey,
514 slot,
515 shred_index,
516 &post_init.repair_socket.clone(),
517 post_init.outstanding_repair_requests.clone(),
518 );
519 Ok(())
520 })
521 }
522
523 fn repair_whitelist(&self, meta: Self::Metadata) -> Result<AdminRpcRepairWhitelist> {
524 debug!("repair_whitelist request received");
525
526 meta.with_post_init(|post_init| {
527 let whitelist: Vec<_> = post_init
528 .repair_whitelist
529 .read()
530 .unwrap()
531 .iter()
532 .copied()
533 .collect();
534 Ok(AdminRpcRepairWhitelist { whitelist })
535 })
536 }
537
538 fn set_repair_whitelist(&self, meta: Self::Metadata, whitelist: Vec<Pubkey>) -> Result<()> {
539 debug!("set_repair_whitelist request received");
540
541 let whitelist: HashSet<Pubkey> = whitelist.into_iter().collect();
542 meta.with_post_init(|post_init| {
543 *post_init.repair_whitelist.write().unwrap() = whitelist;
544 warn!(
545 "Repair whitelist set to {:?}",
546 &post_init.repair_whitelist.read().unwrap()
547 );
548 Ok(())
549 })
550 }
551
552 fn get_secondary_index_key_size(
553 &self,
554 meta: Self::Metadata,
555 pubkey_str: String,
556 ) -> Result<HashMap<RpcAccountIndex, usize>> {
557 debug!(
558 "get_secondary_index_key_size rpc request received: {:?}",
559 pubkey_str
560 );
561 let index_key = verify_pubkey(&pubkey_str)?;
562 meta.with_post_init(|post_init| {
563 let bank = post_init.bank_forks.read().unwrap().root_bank();
564
565 // Take ref to enabled AccountSecondaryIndexes
566 let enabled_account_indexes = &bank.accounts().accounts_db.account_indexes;
567
568 // Exit if secondary indexes are not enabled
569 if enabled_account_indexes.is_empty() {
570 debug!("get_secondary_index_key_size: secondary index not enabled.");
571 return Ok(HashMap::new());
572 };
573
574 // Make sure the requested key is not explicitly excluded
575 if !enabled_account_indexes.include_key(&index_key) {
576 return Err(RpcCustomError::KeyExcludedFromSecondaryIndex {
577 index_key: index_key.to_string(),
578 }
579 .into());
580 }
581
582 // Grab a ref to the AccountsDbfor this Bank
583 let accounts_index = &bank.accounts().accounts_db.accounts_index;
584
585 // Find the size of the key in every index where it exists
586 let found_sizes = enabled_account_indexes
587 .indexes
588 .iter()
589 .filter_map(|index| {
590 accounts_index
591 .get_index_key_size(index, &index_key)
592 .map(|size| (rpc_account_index_from_account_index(index), size))
593 })
594 .collect::<HashMap<_, _>>();
595
596 // Note: Will return an empty HashMap if no keys are found.
597 if found_sizes.is_empty() {
598 debug!("get_secondary_index_key_size: key not found in the secondary index.");
599 }
600 Ok(found_sizes)
601 })
602 }
603
604 fn set_public_tpu_address(
605 &self,
606 meta: Self::Metadata,
607 public_tpu_addr: SocketAddr,
608 ) -> Result<()> {
609 debug!("set_public_tpu_address rpc request received: {public_tpu_addr}");
610
611 meta.with_post_init(|post_init| {
612 post_init
613 .cluster_info
614 .my_contact_info()
615 .tpu(Protocol::UDP)
616 .map_err(|err| {
617 error!(
618 "The public TPU address isn't being published. The node is likely in \
619 repair mode. See help for --restricted-repair-only-mode for more \
620 information. {err}"
621 );
622 jsonrpc_core::error::Error::internal_error()
623 })?;
624 post_init
625 .cluster_info
626 .set_tpu(public_tpu_addr)
627 .map_err(|err| {
628 error!("Failed to set public TPU address to {public_tpu_addr}: {err}");
629 jsonrpc_core::error::Error::internal_error()
630 })?;
631 let my_contact_info = post_init.cluster_info.my_contact_info();
632 warn!(
633 "Public TPU addresses set to {:?} (udp) and {:?} (quic)",
634 my_contact_info.tpu(Protocol::UDP),
635 my_contact_info.tpu(Protocol::QUIC),
636 );
637 Ok(())
638 })
639 }
640
641 fn set_public_tpu_forwards_address(
642 &self,
643 meta: Self::Metadata,
644 public_tpu_forwards_addr: SocketAddr,
645 ) -> Result<()> {
646 debug!("set_public_tpu_forwards_address rpc request received: {public_tpu_forwards_addr}");
647
648 meta.with_post_init(|post_init| {
649 post_init
650 .cluster_info
651 .my_contact_info()
652 .tpu_forwards(Protocol::UDP)
653 .map_err(|err| {
654 error!(
655 "The public TPU Forwards address isn't being published. The node is \
656 likely in repair mode. See help for --restricted-repair-only-mode for \
657 more information. {err}"
658 );
659 jsonrpc_core::error::Error::internal_error()
660 })?;
661 post_init
662 .cluster_info
663 .set_tpu_forwards(public_tpu_forwards_addr)
664 .map_err(|err| {
665 error!("Failed to set public TPU address to {public_tpu_forwards_addr}: {err}");
666 jsonrpc_core::error::Error::internal_error()
667 })?;
668 let my_contact_info = post_init.cluster_info.my_contact_info();
669 warn!(
670 "Public TPU Forwards addresses set to {:?} (udp) and {:?} (quic)",
671 my_contact_info.tpu_forwards(Protocol::UDP),
672 my_contact_info.tpu_forwards(Protocol::QUIC),
673 );
674 Ok(())
675 })
676 }
677}
678
679impl AdminRpcImpl {
680 fn add_authorized_voter_keypair(
681 meta: AdminRpcRequestMetadata,
682 authorized_voter: Keypair,
683 ) -> Result<()> {
684 let mut authorized_voter_keypairs = meta.authorized_voter_keypairs.write().unwrap();
685
686 if authorized_voter_keypairs
687 .iter()
688 .any(|x| x.pubkey() == authorized_voter.pubkey())
689 {
690 Err(jsonrpc_core::error::Error::invalid_params(
691 "Authorized voter already present",
692 ))
693 } else {
694 authorized_voter_keypairs.push(Arc::new(authorized_voter));
695 Ok(())
696 }
697 }
698
699 fn set_identity_keypair(
700 meta: AdminRpcRequestMetadata,
701 identity_keypair: Keypair,
702 require_tower: bool,
703 ) -> Result<()> {
704 meta.with_post_init(|post_init| {
705 if require_tower {
706 let _ = Tower::restore(meta.tower_storage.as_ref(), &identity_keypair.pubkey())
707 .map_err(|err| {
708 jsonrpc_core::error::Error::invalid_params(format!(
709 "Unable to load tower file for identity {}: {}",
710 identity_keypair.pubkey(),
711 err
712 ))
713 })?;
714 }
715
716 for n in post_init.notifies.iter() {
717 if let Err(err) = n.update_key(&identity_keypair) {
718 error!("Error updating network layer keypair: {err}");
719 }
720 }
721
722 miraland_metrics::set_host_id(identity_keypair.pubkey().to_string());
723 post_init
724 .cluster_info
725 .set_keypair(Arc::new(identity_keypair));
726 warn!("Identity set to {}", post_init.cluster_info.id());
727 Ok(())
728 })
729 }
730}
731
732fn rpc_account_index_from_account_index(account_index: &AccountIndex) -> RpcAccountIndex {
733 match account_index {
734 AccountIndex::ProgramId => RpcAccountIndex::ProgramId,
735 AccountIndex::SolartiTokenOwner => RpcAccountIndex::SolartiTokenOwner,
736 AccountIndex::SolartiTokenMint => RpcAccountIndex::SolartiTokenMint,
737 }
738}
739
740// Start the Admin RPC interface
741pub fn run(ledger_path: &Path, metadata: AdminRpcRequestMetadata) {
742 let admin_rpc_path = admin_rpc_path(ledger_path);
743
744 let event_loop = tokio::runtime::Builder::new_multi_thread()
745 .thread_name("mlnAdminRpcEl")
746 .worker_threads(3) // Three still seems like a lot, and better than the default of available core count
747 .enable_all()
748 .build()
749 .unwrap();
750
751 Builder::new()
752 .name("mlnAdminRpc".to_string())
753 .spawn(move || {
754 let mut io = MetaIoHandler::default();
755 io.extend_with(AdminRpcImpl.to_delegate());
756
757 let validator_exit = metadata.validator_exit.clone();
758 let server = ServerBuilder::with_meta_extractor(io, move |_req: &RequestContext| {
759 metadata.clone()
760 })
761 .event_loop_executor(event_loop.handle().clone())
762 .start(&format!("{}", admin_rpc_path.display()));
763
764 match server {
765 Err(err) => {
766 warn!("Unable to start admin rpc service: {:?}", err);
767 }
768 Ok(server) => {
769 info!("started admin rpc service!");
770 let close_handle = server.close_handle();
771 validator_exit
772 .write()
773 .unwrap()
774 .register_exit(Box::new(move || {
775 close_handle.close();
776 }));
777
778 server.wait();
779 }
780 }
781 })
782 .unwrap();
783}
784
785fn admin_rpc_path(ledger_path: &Path) -> PathBuf {
786 #[cfg(target_family = "windows")]
787 {
788 // More information about the wackiness of pipe names over at
789 // https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names
790 if let Some(ledger_filename) = ledger_path.file_name() {
791 PathBuf::from(format!(
792 "\\\\.\\pipe\\{}-admin.rpc",
793 ledger_filename.to_string_lossy()
794 ))
795 } else {
796 PathBuf::from("\\\\.\\pipe\\admin.rpc")
797 }
798 }
799 #[cfg(not(target_family = "windows"))]
800 {
801 ledger_path.join("admin.rpc")
802 }
803}
804
805// Connect to the Admin RPC interface
806pub async fn connect(ledger_path: &Path) -> std::result::Result<gen_client::Client, RpcError> {
807 let admin_rpc_path = admin_rpc_path(ledger_path);
808 if !admin_rpc_path.exists() {
809 Err(RpcError::Client(format!(
810 "{} does not exist",
811 admin_rpc_path.display()
812 )))
813 } else {
814 ipc::connect::<_, gen_client::Client>(&format!("{}", admin_rpc_path.display())).await
815 }
816}
817
818pub fn runtime() -> jsonrpc_server_utils::tokio::runtime::Runtime {
819 jsonrpc_server_utils::tokio::runtime::Runtime::new().expect("new tokio runtime")
820}
821
822#[derive(Default, Deserialize, Clone)]
823pub struct StakedNodesOverrides {
824 #[serde(deserialize_with = "deserialize_pubkey_map")]
825 pub staked_map_id: HashMap<Pubkey, u64>,
826}
827
828pub fn deserialize_pubkey_map<'de, D>(des: D) -> std::result::Result<HashMap<Pubkey, u64>, D::Error>
829where
830 D: Deserializer<'de>,
831{
832 let container: HashMap<String, u64> = serde::Deserialize::deserialize(des)?;
833 let mut container_typed: HashMap<Pubkey, u64> = HashMap::new();
834 for (key, value) in container.iter() {
835 let typed_key = Pubkey::try_from(key.as_str())
836 .map_err(|_| serde::de::Error::invalid_type(serde::de::Unexpected::Map, &"PubKey"))?;
837 container_typed.insert(typed_key, *value);
838 }
839 Ok(container_typed)
840}
841
842pub fn load_staked_nodes_overrides(
843 path: &String,
844) -> std::result::Result<StakedNodesOverrides, Box<dyn error::Error>> {
845 debug!("Loading staked nodes overrides configuration from {}", path);
846 if Path::new(&path).exists() {
847 let file = std::fs::File::open(path)?;
848 Ok(serde_yaml::from_reader(file)?)
849 } else {
850 Err(format!("Staked nodes overrides provided '{path}' a non-existing file path.").into())
851 }
852}
853
854// #[cfg(test)]
855// mod tests {
856// use {
857// super::*,
858// miraland_accounts_db::{accounts_index::AccountSecondaryIndexes, inline_spl_token},
859// miraland_core::consensus::tower_storage::NullTowerStorage,
860// miraland_gossip::cluster_info::ClusterInfo,
861// miraland_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo},
862// miraland_rpc::rpc::create_validator_exit,
863// miraland_streamer::socket::SocketAddrSpace,
864// serde_json::Value,
865// miraland_runtime::{
866// bank::{Bank, BankTestConfig},
867// bank_forks::BankForks,
868// },
869// miraland_sdk::{
870// account::{Account, AccountSharedData},
871// pubkey::Pubkey,
872// system_program,
873// },
874// spl_token_2022::{
875// miraland_program::{program_option::COption, program_pack::Pack},
876// state::{Account as TokenAccount, AccountState as TokenAccountState, Mint},
877// },
878// std::{collections::HashSet, sync::atomic::AtomicBool},
879// };
880
881// #[derive(Default)]
882// struct TestConfig {
883// account_indexes: AccountSecondaryIndexes,
884// }
885
886// struct RpcHandler {
887// io: MetaIoHandler<AdminRpcRequestMetadata>,
888// meta: AdminRpcRequestMetadata,
889// bank_forks: Arc<RwLock<BankForks>>,
890// }
891
892// impl RpcHandler {
893// fn _start() -> Self {
894// Self::start_with_config(TestConfig::default())
895// }
896
897// fn start_with_config(config: TestConfig) -> Self {
898// let keypair = Arc::new(Keypair::new());
899// let cluster_info = Arc::new(ClusterInfo::new(
900// ContactInfo::new(
901// keypair.pubkey(),
902// miraland_sdk::timing::timestamp(), // wallclock
903// 0u16, // shred_version
904// ),
905// keypair,
906// SocketAddrSpace::Unspecified,
907// ));
908// let exit = Arc::new(AtomicBool::new(false));
909// let validator_exit = create_validator_exit(exit);
910// let (bank_forks, vote_keypair) = new_bank_forks_with_config(BankTestConfig {
911// secondary_indexes: config.account_indexes,
912// });
913// let vote_account = vote_keypair.pubkey();
914// let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default()));
915// let repair_whitelist = Arc::new(RwLock::new(HashSet::new()));
916// let meta = AdminRpcRequestMetadata {
917// rpc_addr: None,
918// start_time: SystemTime::now(),
919// start_progress,
920// validator_exit,
921// authorized_voter_keypairs: Arc::new(RwLock::new(vec![vote_keypair])),
922// tower_storage: Arc::new(NullTowerStorage {}),
923// post_init: Arc::new(RwLock::new(Some(AdminRpcRequestMetadataPostInit {
924// cluster_info,
925// bank_forks: bank_forks.clone(),
926// vote_account,
927// repair_whitelist,
928// notifies: Vec::new(),
929// repair_socket: Arc::new(std::net::UdpSocket::bind("0.0.0.0:0").unwrap()),
930// outstanding_repair_requests: Arc::<
931// RwLock<repair_service::OutstandingShredRepairs>,
932// >::default(),
933// cluster_slots: Arc::new(
934// miraland_core::cluster_slots_service::cluster_slots::ClusterSlots::default(
935// ),
936// ),
937// }))),
938// staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())),
939// rpc_to_plugin_manager_sender: None,
940// };
941// let mut io = MetaIoHandler::default();
942// io.extend_with(AdminRpcImpl.to_delegate());
943
944// Self {
945// io,
946// meta,
947// bank_forks,
948// }
949// }
950
951// fn root_bank(&self) -> Arc<Bank> {
952// self.bank_forks.read().unwrap().root_bank()
953// }
954// }
955
956// fn new_bank_forks_with_config(
957// config: BankTestConfig,
958// ) -> (Arc<RwLock<BankForks>>, Arc<Keypair>) {
959// let GenesisConfigInfo {
960// genesis_config,
961// voting_keypair,
962// ..
963// } = create_genesis_config(1_000_000_000);
964
965// let bank = Bank::new_for_tests_with_config(&genesis_config, config);
966// (BankForks::new_rw_arc(bank), Arc::new(voting_keypair))
967// }
968
969// #[test]
970// fn test_secondary_index_key_sizes() {
971// for secondary_index_enabled in [true, false] {
972// let account_indexes = if secondary_index_enabled {
973// AccountSecondaryIndexes {
974// keys: None,
975// indexes: HashSet::from([
976// AccountIndex::ProgramId,
977// AccountIndex::SolartiTokenMint,
978// AccountIndex::SolartiTokenOwner,
979// ]),
980// }
981// } else {
982// AccountSecondaryIndexes::default()
983// };
984
985// // RPC & Bank Setup
986// let rpc = RpcHandler::start_with_config(TestConfig { account_indexes });
987
988// let bank = rpc.root_bank();
989// let RpcHandler { io, meta, .. } = rpc;
990
991// // Pubkeys
992// let token_account1_pubkey = Pubkey::new_unique();
993// let token_account2_pubkey = Pubkey::new_unique();
994// let token_account3_pubkey = Pubkey::new_unique();
995// let mint1_pubkey = Pubkey::new_unique();
996// let mint2_pubkey = Pubkey::new_unique();
997// let wallet1_pubkey = Pubkey::new_unique();
998// let wallet2_pubkey = Pubkey::new_unique();
999// let non_existent_pubkey = Pubkey::new_unique();
1000// let delegate = Pubkey::new_unique();
1001
1002// let mut num_default_spl_token_program_accounts = 0;
1003// let mut num_default_system_program_accounts = 0;
1004
1005// if !secondary_index_enabled {
1006// // Test first with no accounts added & no secondary indexes enabled:
1007// let req = format!(
1008// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account1_pubkey}"]}}"#,
1009// );
1010// let res = io.handle_request_sync(&req, meta.clone());
1011// let result: Value = serde_json::from_str(&res.expect("actual response"))
1012// .expect("actual response deserialization");
1013// let sizes: HashMap<RpcAccountIndex, usize> =
1014// serde_json::from_value(result["result"].clone()).unwrap();
1015// assert!(sizes.is_empty());
1016// } else {
1017// // Count Solarti Token Program Default Accounts
1018// let req = format!(
1019// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
1020// inline_spl_token::id(),
1021// );
1022// let res = io.handle_request_sync(&req, meta.clone());
1023// let result: Value = serde_json::from_str(&res.expect("actual response"))
1024// .expect("actual response deserialization");
1025// let sizes: HashMap<RpcAccountIndex, usize> =
1026// serde_json::from_value(result["result"].clone()).unwrap();
1027// assert_eq!(sizes.len(), 1);
1028// num_default_spl_token_program_accounts =
1029// *sizes.get(&RpcAccountIndex::ProgramId).unwrap();
1030// // Count System Program Default Accounts
1031// let req = format!(
1032// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
1033// system_program::id(),
1034// );
1035// let res = io.handle_request_sync(&req, meta.clone());
1036// let result: Value = serde_json::from_str(&res.expect("actual response"))
1037// .expect("actual response deserialization");
1038// let sizes: HashMap<RpcAccountIndex, usize> =
1039// serde_json::from_value(result["result"].clone()).unwrap();
1040// assert_eq!(sizes.len(), 1);
1041// num_default_system_program_accounts =
1042// *sizes.get(&RpcAccountIndex::ProgramId).unwrap();
1043// }
1044
1045// // Add 2 basic wallet accounts
1046// let wallet1_account = AccountSharedData::from(Account {
1047// lamports: 11111111,
1048// owner: system_program::id(),
1049// ..Account::default()
1050// });
1051// bank.store_account(&wallet1_pubkey, &wallet1_account);
1052// let wallet2_account = AccountSharedData::from(Account {
1053// lamports: 11111111,
1054// owner: system_program::id(),
1055// ..Account::default()
1056// });
1057// bank.store_account(&wallet2_pubkey, &wallet2_account);
1058
1059// // Add a token account
1060// let mut account1_data = vec![0; TokenAccount::get_packed_len()];
1061// let token_account1 = TokenAccount {
1062// mint: mint1_pubkey,
1063// owner: wallet1_pubkey,
1064// delegate: COption::Some(delegate),
1065// amount: 420,
1066// state: TokenAccountState::Initialized,
1067// is_native: COption::None,
1068// delegated_amount: 30,
1069// close_authority: COption::Some(wallet1_pubkey),
1070// };
1071// TokenAccount::pack(token_account1, &mut account1_data).unwrap();
1072// let token_account1 = AccountSharedData::from(Account {
1073// lamports: 111,
1074// data: account1_data.to_vec(),
1075// owner: inline_spl_token::id(),
1076// ..Account::default()
1077// });
1078// bank.store_account(&token_account1_pubkey, &token_account1);
1079
1080// // Add the mint
1081// let mut mint1_data = vec![0; Mint::get_packed_len()];
1082// let mint1_state = Mint {
1083// mint_authority: COption::Some(wallet1_pubkey),
1084// supply: 500,
1085// decimals: 2,
1086// is_initialized: true,
1087// freeze_authority: COption::Some(wallet1_pubkey),
1088// };
1089// Mint::pack(mint1_state, &mut mint1_data).unwrap();
1090// let mint_account1 = AccountSharedData::from(Account {
1091// lamports: 222,
1092// data: mint1_data.to_vec(),
1093// owner: inline_spl_token::id(),
1094// ..Account::default()
1095// });
1096// bank.store_account(&mint1_pubkey, &mint_account1);
1097
1098// // Add another token account with the different owner, but same delegate, and mint
1099// let mut account2_data = vec![0; TokenAccount::get_packed_len()];
1100// let token_account2 = TokenAccount {
1101// mint: mint1_pubkey,
1102// owner: wallet2_pubkey,
1103// delegate: COption::Some(delegate),
1104// amount: 420,
1105// state: TokenAccountState::Initialized,
1106// is_native: COption::None,
1107// delegated_amount: 30,
1108// close_authority: COption::Some(wallet2_pubkey),
1109// };
1110// TokenAccount::pack(token_account2, &mut account2_data).unwrap();
1111// let token_account2 = AccountSharedData::from(Account {
1112// lamports: 333,
1113// data: account2_data.to_vec(),
1114// owner: inline_spl_token::id(),
1115// ..Account::default()
1116// });
1117// bank.store_account(&token_account2_pubkey, &token_account2);
1118
1119// // Add another token account with the same owner and delegate but different mint
1120// let mut account3_data = vec![0; TokenAccount::get_packed_len()];
1121// let token_account3 = TokenAccount {
1122// mint: mint2_pubkey,
1123// owner: wallet2_pubkey,
1124// delegate: COption::Some(delegate),
1125// amount: 42,
1126// state: TokenAccountState::Initialized,
1127// is_native: COption::None,
1128// delegated_amount: 30,
1129// close_authority: COption::Some(wallet2_pubkey),
1130// };
1131// TokenAccount::pack(token_account3, &mut account3_data).unwrap();
1132// let token_account3 = AccountSharedData::from(Account {
1133// lamports: 444,
1134// data: account3_data.to_vec(),
1135// owner: inline_spl_token::id(),
1136// ..Account::default()
1137// });
1138// bank.store_account(&token_account3_pubkey, &token_account3);
1139
1140// // Add the new mint
1141// let mut mint2_data = vec![0; Mint::get_packed_len()];
1142// let mint2_state = Mint {
1143// mint_authority: COption::Some(wallet2_pubkey),
1144// supply: 200,
1145// decimals: 3,
1146// is_initialized: true,
1147// freeze_authority: COption::Some(wallet2_pubkey),
1148// };
1149// Mint::pack(mint2_state, &mut mint2_data).unwrap();
1150// let mint_account2 = AccountSharedData::from(Account {
1151// lamports: 555,
1152// data: mint2_data.to_vec(),
1153// owner: inline_spl_token::id(),
1154// ..Account::default()
1155// });
1156// bank.store_account(&mint2_pubkey, &mint_account2);
1157
1158// // Accounts should now look like the following:
1159// //
1160// // -----system_program------
1161// // / \
1162// // /-(owns) \-(owns)
1163// // / \
1164// // wallet1 ---wallet2---
1165// // / / \
1166// // /-(SPL::owns) /-(SPL::owns) \-(SPL::owns)
1167// // / / \
1168// // token_account1 token_account2 token_account3
1169// // \ / /
1170// // \-(SPL::mint) /-(SPL::mint) /-(SPL::mint)
1171// // \ / /
1172// // --mint_account1-- mint_account2
1173
1174// if secondary_index_enabled {
1175// // ----------- Test for a non-existent key -----------
1176// let req = format!(
1177// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{non_existent_pubkey}"]}}"#,
1178// );
1179// let res = io.handle_request_sync(&req, meta.clone());
1180// let result: Value = serde_json::from_str(&res.expect("actual response"))
1181// .expect("actual response deserialization");
1182// let sizes: HashMap<RpcAccountIndex, usize> =
1183// serde_json::from_value(result["result"].clone()).unwrap();
1184// assert!(sizes.is_empty());
1185// // --------------- Test Queries ---------------
1186// // 1) Wallet1 - Owns 1 Solarti Token
1187// let req = format!(
1188// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet1_pubkey}"]}}"#,
1189// );
1190// let res = io.handle_request_sync(&req, meta.clone());
1191// let result: Value = serde_json::from_str(&res.expect("actual response"))
1192// .expect("actual response deserialization");
1193// let sizes: HashMap<RpcAccountIndex, usize> =
1194// serde_json::from_value(result["result"].clone()).unwrap();
1195// assert_eq!(sizes.len(), 1);
1196// assert_eq!(*sizes.get(&RpcAccountIndex::SolartiTokenOwner).unwrap(), 1);
1197// // 2) Wallet2 - Owns 2 Solarti Tokens
1198// let req = format!(
1199// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{wallet2_pubkey}"]}}"#,
1200// );
1201// let res = io.handle_request_sync(&req, meta.clone());
1202// let result: Value = serde_json::from_str(&res.expect("actual response"))
1203// .expect("actual response deserialization");
1204// let sizes: HashMap<RpcAccountIndex, usize> =
1205// serde_json::from_value(result["result"].clone()).unwrap();
1206// assert_eq!(sizes.len(), 1);
1207// assert_eq!(*sizes.get(&RpcAccountIndex::SolartiTokenOwner).unwrap(), 2);
1208// // 3) Mint1 - Is in 2 SPL Accounts
1209// let req = format!(
1210// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint1_pubkey}"]}}"#,
1211// );
1212// let res = io.handle_request_sync(&req, meta.clone());
1213// let result: Value = serde_json::from_str(&res.expect("actual response"))
1214// .expect("actual response deserialization");
1215// let sizes: HashMap<RpcAccountIndex, usize> =
1216// serde_json::from_value(result["result"].clone()).unwrap();
1217// assert_eq!(sizes.len(), 1);
1218// assert_eq!(*sizes.get(&RpcAccountIndex::SolartiTokenMint).unwrap(), 2);
1219// // 4) Mint2 - Is in 1 SPL Account
1220// let req = format!(
1221// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{mint2_pubkey}"]}}"#,
1222// );
1223// let res = io.handle_request_sync(&req, meta.clone());
1224// let result: Value = serde_json::from_str(&res.expect("actual response"))
1225// .expect("actual response deserialization");
1226// let sizes: HashMap<RpcAccountIndex, usize> =
1227// serde_json::from_value(result["result"].clone()).unwrap();
1228// assert_eq!(sizes.len(), 1);
1229// assert_eq!(*sizes.get(&RpcAccountIndex::SolartiTokenMint).unwrap(), 1);
1230// // 5) Solarti Token Program Owns 6 Accounts - 1 Default, 5 created above.
1231// let req = format!(
1232// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
1233// inline_spl_token::id(),
1234// );
1235// let res = io.handle_request_sync(&req, meta.clone());
1236// let result: Value = serde_json::from_str(&res.expect("actual response"))
1237// .expect("actual response deserialization");
1238// let sizes: HashMap<RpcAccountIndex, usize> =
1239// serde_json::from_value(result["result"].clone()).unwrap();
1240// assert_eq!(sizes.len(), 1);
1241// assert_eq!(
1242// *sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
1243// (num_default_spl_token_program_accounts + 5)
1244// );
1245// // 5) System Program Owns 4 Accounts + 2 Default, 2 created above.
1246// let req = format!(
1247// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{}"]}}"#,
1248// system_program::id(),
1249// );
1250// let res = io.handle_request_sync(&req, meta.clone());
1251// let result: Value = serde_json::from_str(&res.expect("actual response"))
1252// .expect("actual response deserialization");
1253// let sizes: HashMap<RpcAccountIndex, usize> =
1254// serde_json::from_value(result["result"].clone()).unwrap();
1255// assert_eq!(sizes.len(), 1);
1256// assert_eq!(
1257// *sizes.get(&RpcAccountIndex::ProgramId).unwrap(),
1258// (num_default_system_program_accounts + 2)
1259// );
1260// } else {
1261// // ------------ Secondary Indexes Disabled ------------
1262// let req = format!(
1263// r#"{{"jsonrpc":"2.0","id":1,"method":"getSecondaryIndexKeySize","params":["{token_account2_pubkey}"]}}"#,
1264// );
1265// let res = io.handle_request_sync(&req, meta.clone());
1266// let result: Value = serde_json::from_str(&res.expect("actual response"))
1267// .expect("actual response deserialization");
1268// let sizes: HashMap<RpcAccountIndex, usize> =
1269// serde_json::from_value(result["result"].clone()).unwrap();
1270// assert!(sizes.is_empty());
1271// }
1272// }
1273// }
1274// }