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// }