From f89c22b5eeb05a1119c9a5512faa512580214ece Mon Sep 17 00:00:00 2001 From: Grimes <39311140+solana-grimes@users.noreply.github.com> Date: Wed, 4 Mar 2020 14:44:21 -0800 Subject: [PATCH] `solana catchup` now detects when you try to catchup to yourself (#8635) automerge --- cli/src/cluster_query.rs | 13 +++++++++++ client/src/rpc_client.rs | 32 ++++++++++++++++++++++++-- client/src/rpc_request.rs | 2 ++ client/src/rpc_response.rs | 7 ++++++ core/src/rpc.rs | 44 +++++++++++++++++++++++++++++++++--- docs/src/apps/jsonrpc-api.md | 24 ++++++++++++++++++++ validator/src/main.rs | 1 + 7 files changed, 118 insertions(+), 5 deletions(-) diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index f156d0983..883e1c49b 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -394,6 +394,19 @@ pub fn process_catchup( ) }; + let reported_node_pubkey = node_client.get_identity()?; + if reported_node_pubkey != *node_pubkey { + return Err(format!( + "The identity reported by node RPC URL does not match. Expected: {:?}. Reported: {:?}", + node_pubkey, reported_node_pubkey + ) + .into()); + } + + if rpc_client.get_identity()? == *node_pubkey { + return Err("Both RPC URLs reference the same node, unable to monitor for catchup. Try a different --url".into()); + } + let progress_bar = new_spinner_progress_bar(); progress_bar.set_message("Connecting..."); diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 67bb9c370..c09f8f13c 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -6,8 +6,8 @@ use crate::{ rpc_request::RpcRequest, rpc_response::{ Response, RpcAccount, RpcBlockhashFeeCalculator, RpcConfirmedBlock, RpcContactInfo, - RpcEpochInfo, RpcFeeRateGovernor, RpcKeyedAccount, RpcLeaderSchedule, RpcResponse, - RpcVersionInfo, RpcVoteAccountStatus, + RpcEpochInfo, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount, RpcLeaderSchedule, + RpcResponse, RpcVersionInfo, RpcVoteAccountStatus, }, }; use bincode::serialize; @@ -356,6 +356,34 @@ impl RpcClient { }) } + pub fn get_identity(&self) -> io::Result { + let response = self + .client + .send(&RpcRequest::GetIdentity, Value::Null, 0) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetIdentity request failure: {:?}", err), + ) + })?; + + serde_json::from_value(response) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetIdentity failure: {:?}", err), + ) + }) + .and_then(|rpc_identity: RpcIdentity| { + rpc_identity.identity.parse::().map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetIdentity invalid pubkey failure: {:?}", err), + ) + }) + }) + } + pub fn get_inflation(&self) -> io::Result { let response = self .client diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 15fa38a0a..391f19800 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -15,6 +15,7 @@ pub enum RpcRequest { GetEpochInfo, GetEpochSchedule, GetGenesisHash, + GetIdentity, GetInflation, GetLeaderSchedule, GetNumBlocksSinceSignatureConfirmation, @@ -55,6 +56,7 @@ impl RpcRequest { RpcRequest::GetEpochInfo => "getEpochInfo", RpcRequest::GetEpochSchedule => "getEpochSchedule", RpcRequest::GetGenesisHash => "getGenesisHash", + RpcRequest::GetIdentity => "getIdentity", RpcRequest::GetInflation => "getInflation", RpcRequest::GetLeaderSchedule => "getLeaderSchedule", RpcRequest::GetNumBlocksSinceSignatureConfirmation => { diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 0ca274bcf..a0e6cc4b4 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -241,6 +241,13 @@ pub struct RpcVersionInfo { pub solana_core: String, } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "kebab-case")] +pub struct RpcIdentity { + /// The current node identity pubkey + pub identity: String, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct RpcVoteAccountStatus { diff --git a/core/src/rpc.rs b/core/src/rpc.rs index a031cc135..934e8e3f7 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -9,9 +9,9 @@ use jsonrpc_core::{Error, Metadata, Result}; use jsonrpc_derive::rpc; use solana_client::rpc_response::{ Response, RpcAccount, RpcBlockCommitment, RpcBlockhashFeeCalculator, RpcConfirmedBlock, - RpcContactInfo, RpcEpochInfo, RpcFeeRateGovernor, RpcKeyedAccount, RpcLeaderSchedule, - RpcResponseContext, RpcSignatureConfirmation, RpcStorageTurn, RpcTransactionEncoding, - RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus, + RpcContactInfo, RpcEpochInfo, RpcFeeRateGovernor, RpcIdentity, RpcKeyedAccount, + RpcLeaderSchedule, RpcResponseContext, RpcSignatureConfirmation, RpcStorageTurn, + RpcTransactionEncoding, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus, }; use solana_faucet::faucet::request_airdrop_transaction; use solana_ledger::{ @@ -49,6 +49,7 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { pub struct JsonRpcConfig { pub enable_validator_exit: bool, pub enable_get_confirmed_block: bool, + pub identity_pubkey: Pubkey, pub faucet_addr: Option, } @@ -594,6 +595,9 @@ pub trait RpcSol { commitment: Option, ) -> Result>; + #[rpc(meta, name = "getIdentity")] + fn get_identity(&self, meta: Self::Metadata) -> Result; + #[rpc(meta, name = "getVersion")] fn get_version(&self, meta: Self::Metadata) -> Result; @@ -1076,6 +1080,18 @@ impl RpcSol for RpcSolImpl { meta.request_processor.read().unwrap().validator_exit() } + fn get_identity(&self, meta: Self::Metadata) -> Result { + Ok(RpcIdentity { + identity: meta + .request_processor + .read() + .unwrap() + .config + .identity_pubkey + .to_string(), + }) + } + fn get_version(&self, _: Self::Metadata) -> Result { Ok(RpcVersionInfo { solana_core: solana_clap_utils::version!().to_string(), @@ -1269,6 +1285,7 @@ pub mod tests { let request_processor = Arc::new(RwLock::new(JsonRpcRequestProcessor::new( JsonRpcConfig { enable_get_confirmed_block: true, + identity_pubkey: *pubkey, ..JsonRpcConfig::default() }, bank_forks.clone(), @@ -1996,6 +2013,27 @@ pub mod tests { assert_eq!(exit.load(Ordering::Relaxed), true); } + #[test] + fn test_rpc_get_identity() { + let bob_pubkey = Pubkey::new_rand(); + let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); + + let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getIdentity"}}"#); + let res = io.handle_request_sync(&req, meta); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "identity": bob_pubkey.to_string() + }, + "id": 1 + }); + let expected: Response = + serde_json::from_value(expected).expect("expected response deserialization"); + let result: Response = serde_json::from_str(&res.expect("actual response")) + .expect("actual response deserialization"); + assert_eq!(expected, result); + } + #[test] fn test_rpc_get_version() { let bob_pubkey = Pubkey::new_rand(); diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 382cd9bd5..bce34dbdd 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -26,6 +26,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana- * [getEpochSchedule](jsonrpc-api.md#getepochschedule) * [getFeeRateGovernor](jsonrpc-api.md#getfeerategovernor) * [getGenesisHash](jsonrpc-api.md#getgenesishash) +* [getIdentity](jsonrpc-api.md#getidentity) * [getInflation](jsonrpc-api.md#getinflation) * [getLeaderSchedule](jsonrpc-api.md#getleaderschedule) * [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption) @@ -454,6 +455,29 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m {"jsonrpc":"2.0","result":"GH7ome3EiwEr7tu9JuTh2dpYWBJK3z69Xm1ZE3MEE6JC","id":1} ``` +### getIdentity + +Returns the identity pubkey for the current node + +#### Parameters: + +None + +#### Results: + +The result field will be a JSON object with the following fields: + +* `identity`, the identity pubkey of the current node \(as a base-58 encoded string\) + +#### Example: + +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getIdentity"}' http://localhost:8899 +// Result +{"jsonrpc":"2.0","result":{"identity": "2r1F4iWqVcb8M1DbAjQuFpebkQHY9hcVU4WuW2DJBppN"},"id":1} +``` + ### getInflation Returns the inflation configuration of the cluster diff --git a/validator/src/main.rs b/validator/src/main.rs index 428f31923..de15fb05f 100644 --- a/validator/src/main.rs +++ b/validator/src/main.rs @@ -903,6 +903,7 @@ pub fn main() { rpc_config: JsonRpcConfig { enable_validator_exit: matches.is_present("enable_rpc_exit"), enable_get_confirmed_block: matches.is_present("enable_rpc_get_confirmed_block"), + identity_pubkey: identity_keypair.pubkey(), faucet_addr: matches.value_of("rpc_faucet_addr").map(|address| { solana_net_utils::parse_host_port(address).expect("failed to parse faucet address") }),