diff --git a/validator/src/admin_rpc_service.rs b/validator/src/admin_rpc_service.rs index 2bdf32a5626cfa..021f169fe72c1d 100644 --- a/validator/src/admin_rpc_service.rs +++ b/validator/src/admin_rpc_service.rs @@ -869,10 +869,18 @@ mod tests { accounts_db::{AccountsDbConfig, ACCOUNTS_DB_CONFIG_FOR_TESTING}, accounts_index::AccountSecondaryIndexes, }, - solana_core::consensus::tower_storage::NullTowerStorage, - solana_gossip::cluster_info::ClusterInfo, + solana_core::{ + consensus::tower_storage::NullTowerStorage, + validator::{Validator, ValidatorConfig}, + }, + solana_gossip::cluster_info::{ClusterInfo, Node}, solana_inline_spl::token, - solana_ledger::genesis_utils::{create_genesis_config, GenesisConfigInfo}, + solana_ledger::{ + create_new_tmp_ledger, + genesis_utils::{ + create_genesis_config, create_genesis_config_with_leader, GenesisConfigInfo, + }, + }, solana_net_utils::bind_to_unspecified, solana_rpc::rpc::create_validator_exit, solana_runtime::{ @@ -885,11 +893,14 @@ mod tests { system_program, }, solana_streamer::socket::SocketAddrSpace, + solana_tpu_client::tpu_client::{ + DEFAULT_TPU_CONNECTION_POOL_SIZE, DEFAULT_TPU_ENABLE_UDP, DEFAULT_TPU_USE_QUIC, + }, spl_token_2022::{ solana_program::{program_option::COption, program_pack::Pack}, state::{Account as TokenAccount, AccountState as TokenAccountState, Mint}, }, - std::{collections::HashSet, sync::atomic::AtomicBool}, + std::{collections::HashSet, fs::remove_dir_all, sync::atomic::AtomicBool}, }; #[derive(Default)] @@ -1287,4 +1298,180 @@ mod tests { } } } + + // This test checks that the rpc call to `set_identity` works a expected with + // Bank but without validator. + #[test] + fn test_set_identity() { + let rpc = RpcHandler::start_with_config(TestConfig::default()); + + let RpcHandler { io, meta, .. } = rpc; + + let expected_validator_id = Keypair::new(); + let validator_id_bytes = format!("{:?}", expected_validator_id.to_bytes()); + + let set_id_request = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"setIdentityFromBytes","params":[{validator_id_bytes}, false]}}"#, + ); + let response = io.handle_request_sync(&set_id_request, meta.clone()); + let actual_parsed_response: Value = + serde_json::from_str(&response.expect("actual response")) + .expect("actual response deserialization"); + + let expected_parsed_response: Value = serde_json::from_str( + r#"{ + "id": 1, + "jsonrpc": "2.0", + "result": null + }"#, + ) + .expect("Failed to parse expected response"); + assert_eq!(actual_parsed_response, expected_parsed_response); + + let contact_info_request = + r#"{"jsonrpc":"2.0","id":1,"method":"contactInfo","params":[]}"#.to_string(); + let response = io.handle_request_sync(&contact_info_request, meta.clone()); + let parsed_response: Value = serde_json::from_str(&response.expect("actual response")) + .expect("actual response deserialization"); + let actual_validator_id = parsed_response["result"]["id"] + .as_str() + .expect("Expected a string"); + assert_eq!( + actual_validator_id, + expected_validator_id.pubkey().to_string() + ); + } + + struct TestValidatorWithAdminRpc { + meta: AdminRpcRequestMetadata, + io: MetaIoHandler, + validator_ledger_path: PathBuf, + } + + impl TestValidatorWithAdminRpc { + fn new() -> Self { + let leader_keypair = Keypair::new(); + let leader_node = Node::new_localhost_with_pubkey(&leader_keypair.pubkey()); + + let validator_keypair = Keypair::new(); + let validator_node = Node::new_localhost_with_pubkey(&validator_keypair.pubkey()); + let genesis_config = + create_genesis_config_with_leader(10_000, &leader_keypair.pubkey(), 1000) + .genesis_config; + let (validator_ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_config); + + let voting_keypair = Arc::new(Keypair::new()); + let voting_pubkey = voting_keypair.pubkey(); + let authorized_voter_keypairs = Arc::new(RwLock::new(vec![voting_keypair])); + let validator_config = ValidatorConfig { + rpc_addrs: Some(( + validator_node.info.rpc().unwrap(), + validator_node.info.rpc_pubsub().unwrap(), + )), + ..ValidatorConfig::default_for_test() + }; + let start_progress = Arc::new(RwLock::new(ValidatorStartProgress::default())); + + let post_init = Arc::new(RwLock::new(None)); + let meta = AdminRpcRequestMetadata { + rpc_addr: validator_config.rpc_addrs.map(|(rpc_addr, _)| rpc_addr), + start_time: SystemTime::now(), + start_progress: start_progress.clone(), + validator_exit: validator_config.validator_exit.clone(), + authorized_voter_keypairs: authorized_voter_keypairs.clone(), + tower_storage: Arc::new(NullTowerStorage {}), + post_init: post_init.clone(), + staked_nodes_overrides: Arc::new(RwLock::new(HashMap::new())), + rpc_to_plugin_manager_sender: None, + }; + + let _validator = Validator::new( + validator_node, + Arc::new(validator_keypair), + &validator_ledger_path, + &voting_pubkey, + authorized_voter_keypairs, + vec![leader_node.info], + &validator_config, + true, // should_check_duplicate_instance + None, // rpc_to_plugin_manager_receiver + start_progress.clone(), + SocketAddrSpace::Unspecified, + DEFAULT_TPU_USE_QUIC, + DEFAULT_TPU_CONNECTION_POOL_SIZE, + DEFAULT_TPU_ENABLE_UDP, + 32, // max connections per IpAddr per minute for test + post_init, + ) + .expect("assume successful validator start"); + assert_eq!( + *start_progress.read().unwrap(), + ValidatorStartProgress::Running + ); + let mut io = MetaIoHandler::default(); + io.extend_with(AdminRpcImpl.to_delegate()); + Self { + meta, + io, + validator_ledger_path, + } + } + + fn handle_request(&self, request: &str) -> Option { + self.io.handle_request_sync(request, self.meta.clone()) + } + } + + impl Drop for TestValidatorWithAdminRpc { + fn drop(&mut self) { + remove_dir_all(self.validator_ledger_path.clone()).unwrap(); + } + } + + // This test checks that `set_identity` call works with working validator and client. + #[test] + fn test_set_identity_with_validator() { + let test_validator = TestValidatorWithAdminRpc::new(); + let expected_validator_id = Keypair::new(); + let validator_id_bytes = format!("{:?}", expected_validator_id.to_bytes()); + + let set_id_request = format!( + r#"{{"jsonrpc":"2.0","id":1,"method":"setIdentityFromBytes","params":[{validator_id_bytes}, false]}}"#, + ); + let response = test_validator.handle_request(&set_id_request); + let actual_parsed_response: Value = + serde_json::from_str(&response.expect("actual response")) + .expect("actual response deserialization"); + + let expected_parsed_response: Value = serde_json::from_str( + r#"{ + "id": 1, + "jsonrpc": "2.0", + "result": null + }"#, + ) + .expect("Failed to parse expected response"); + assert_eq!(actual_parsed_response, expected_parsed_response); + + let contact_info_request = + r#"{"jsonrpc":"2.0","id":1,"method":"contactInfo","params":[]}"#.to_string(); + let response = test_validator.handle_request(&contact_info_request); + let parsed_response: Value = serde_json::from_str(&response.expect("actual response")) + .expect("actual response deserialization"); + let actual_validator_id = parsed_response["result"]["id"] + .as_str() + .expect("Expected a string"); + assert_eq!( + actual_validator_id, + expected_validator_id.pubkey().to_string() + ); + + let contact_info_request = + r#"{"jsonrpc":"2.0","id":1,"method":"exit","params":[]}"#.to_string(); + let exit_response = test_validator.handle_request(&contact_info_request); + let actual_parsed_response: Value = + serde_json::from_str(&exit_response.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(actual_parsed_response, expected_parsed_response); + } }