diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 3fc3197c30..0608f9498c 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -274,6 +274,10 @@ impl RpcClient { ) } + pub fn get_snapshot_slot(&self) -> ClientResult { + self.send(RpcRequest::GetSnapshotSlot, Value::Null) + } + pub fn get_signature_status( &self, signature: &Signature, diff --git a/client/src/rpc_custom_error.rs b/client/src/rpc_custom_error.rs index db47e496ae..6d9abf6f3a 100644 --- a/client/src/rpc_custom_error.rs +++ b/client/src/rpc_custom_error.rs @@ -11,6 +11,7 @@ pub const JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE: i64 = -32004; pub const JSON_RPC_SERVER_ERROR_NODE_UNHEALTHLY: i64 = -32005; pub const JSON_RPC_SERVER_ERROR_TRANSACTION_PRECOMPILE_VERIFICATION_FAILURE: i64 = -32006; pub const JSON_RPC_SERVER_ERROR_SLOT_SKIPPED: i64 = -32007; +pub const JSON_RPC_SERVER_ERROR_NO_SNAPSHOT: i64 = -32008; pub enum RpcCustomError { BlockCleanedUp { @@ -32,6 +33,7 @@ pub enum RpcCustomError { SlotSkipped { slot: Slot, }, + NoSnapshot, } #[derive(Debug, Serialize, Deserialize)] @@ -95,6 +97,11 @@ impl From for Error { ), data: None, }, + RpcCustomError::NoSnapshot => Self { + code: ErrorCode::ServerError(JSON_RPC_SERVER_ERROR_NO_SNAPSHOT), + message: "No snapshot".to_string(), + data: None, + }, } } } diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 437649490b..1d2c85643e 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -35,6 +35,7 @@ pub enum RpcRequest { GetMultipleAccounts, GetProgramAccounts, GetRecentBlockhash, + GetSnapshotSlot, GetSignatureStatuses, GetSlot, GetSlotLeader, @@ -91,6 +92,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetMultipleAccounts => "getMultipleAccounts", RpcRequest::GetProgramAccounts => "getProgramAccounts", RpcRequest::GetRecentBlockhash => "getRecentBlockhash", + RpcRequest::GetSnapshotSlot => "getSnapshotSlot", RpcRequest::GetSignatureStatuses => "getSignatureStatuses", RpcRequest::GetSlot => "getSlot", RpcRequest::GetSlotLeader => "getSlotLeader", diff --git a/core/src/rpc.rs b/core/src/rpc.rs index b4c9910c7f..de1dc73762 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -41,9 +41,10 @@ use solana_runtime::{ accounts::AccountAddressFilter, accounts_index::{AccountIndex, IndexKey}, bank::Bank, - bank_forks::BankForks, + bank_forks::{BankForks, SnapshotConfig}, commitment::{BlockCommitmentArray, BlockCommitmentCache, CommitmentSlots}, inline_spl_token_v2_0::{SPL_TOKEN_ACCOUNT_MINT_OFFSET, SPL_TOKEN_ACCOUNT_OWNER_OFFSET}, + snapshot_utils::get_highest_snapshot_archive_path, }; use solana_sdk::{ account::Account, @@ -122,6 +123,7 @@ pub struct JsonRpcRequestProcessor { block_commitment_cache: Arc>, blockstore: Arc, config: JsonRpcConfig, + snapshot_config: Option, validator_exit: Arc>>, health: Arc, cluster_info: Arc, @@ -199,6 +201,7 @@ impl JsonRpcRequestProcessor { #[allow(clippy::too_many_arguments)] pub fn new( config: JsonRpcConfig, + snapshot_config: Option, bank_forks: Arc>, block_commitment_cache: Arc>, blockstore: Arc, @@ -214,6 +217,7 @@ impl JsonRpcRequestProcessor { ( Self { config, + snapshot_config, bank_forks, block_commitment_cache, blockstore, @@ -246,6 +250,7 @@ impl JsonRpcRequestProcessor { Self { config: JsonRpcConfig::default(), + snapshot_config: None, bank_forks, block_commitment_cache: Arc::new(RwLock::new(BlockCommitmentCache::new( HashMap::new(), @@ -463,7 +468,7 @@ impl JsonRpcRequestProcessor { } } - fn get_slot(&self, commitment: Option) -> u64 { + fn get_slot(&self, commitment: Option) -> Slot { self.bank(commitment).slot() } @@ -1799,6 +1804,9 @@ pub trait RpcSol { meta: Self::Metadata, ) -> Result>; + #[rpc(meta, name = "getSnapshotSlot")] + fn get_snapshot_slot(&self, meta: Self::Metadata) -> Result; + #[rpc(meta, name = "getSignatureStatuses")] fn get_signature_statuses( &self, @@ -1808,7 +1816,7 @@ pub trait RpcSol { ) -> Result>>>; #[rpc(meta, name = "getSlot")] - fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result; + fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result; #[rpc(meta, name = "getTransactionCount")] fn get_transaction_count( @@ -2354,6 +2362,17 @@ impl RpcSol for RpcSolImpl { Ok(meta.get_signature_status(signature, commitment)) } + fn get_snapshot_slot(&self, meta: Self::Metadata) -> Result { + debug!("get_snapshot_slot rpc request received"); + + meta.snapshot_config + .and_then(|snapshot_config| { + get_highest_snapshot_archive_path(&snapshot_config.snapshot_package_output_path) + .map(|(_, (slot, _, _))| slot) + }) + .ok_or_else(|| RpcCustomError::NoSnapshot.into()) + } + fn get_signature_statuses( &self, meta: Self::Metadata, @@ -2377,7 +2396,7 @@ impl RpcSol for RpcSolImpl { meta.get_signature_statuses(signatures, config) } - fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result { + fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result { debug!("get_slot rpc request received"); Ok(meta.get_slot(commitment)) } @@ -3080,6 +3099,7 @@ pub mod tests { identity_pubkey: *pubkey, ..JsonRpcConfig::default() }, + None, bank_forks.clone(), block_commitment_cache.clone(), blockstore, @@ -4480,6 +4500,7 @@ pub mod tests { let tpu_address = cluster_info.my_contact_info().tpu; let (meta, receiver) = JsonRpcRequestProcessor::new( JsonRpcConfig::default(), + None, bank_forks.clone(), block_commitment_cache, blockstore, @@ -4676,6 +4697,7 @@ pub mod tests { let bank_forks = new_bank_forks().0; let (request_processor, receiver) = JsonRpcRequestProcessor::new( JsonRpcConfig::default(), + None, bank_forks.clone(), block_commitment_cache, blockstore, @@ -4708,6 +4730,7 @@ pub mod tests { let tpu_address = cluster_info.my_contact_info().tpu; let (request_processor, receiver) = JsonRpcRequestProcessor::new( config, + None, bank_forks.clone(), block_commitment_cache, blockstore, @@ -4799,6 +4822,7 @@ pub mod tests { let tpu_address = cluster_info.my_contact_info().tpu; let (request_processor, receiver) = JsonRpcRequestProcessor::new( config, + None, bank_forks.clone(), block_commitment_cache, blockstore, @@ -6027,6 +6051,7 @@ pub mod tests { let (meta, _receiver) = JsonRpcRequestProcessor::new( JsonRpcConfig::default(), + None, bank_forks.clone(), block_commitment_cache, blockstore, diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index 1308032af2..99ee1cfc33 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -329,6 +329,7 @@ impl JsonRpcService { let (request_processor, receiver) = JsonRpcRequestProcessor::new( config, + snapshot_config.clone(), bank_forks.clone(), block_commitment_cache, blockstore, diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 4c126b9772..71cd1a0cc0 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -1968,6 +1968,38 @@ Result: } ``` + +### getSnapshotSlot + +Returns the highest slot that the node has a snapshot for + +#### Parameters: + +None + +#### Results: + +- `` - Snapshot slot + +#### Example: + +Request: +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1, "method":"getSnapshotSlot"} +' +``` + +Result: +```json +{"jsonrpc":"2.0","result":100,"id":1} +``` + +Result when the node has no snapshot: +```json +{"jsonrpc":"2.0","error":{"code":-32008,"message":"No snapshot"},"id":1} +``` + ### getSignatureStatuses Returns the statuses of a list of signatures. Unless the diff --git a/validator/src/bin/solana-test-validator.rs b/validator/src/bin/solana-test-validator.rs index 2b02ed1035..a6a9cc1dbf 100644 --- a/validator/src/bin/solana-test-validator.rs +++ b/validator/src/bin/solana-test-validator.rs @@ -380,10 +380,7 @@ fn main() { } loop { - let snapshot_slot = - solana_runtime::snapshot_utils::get_highest_snapshot_archive_path(&ledger_path) - .map(|(_, (slot, _, _))| slot) - .unwrap_or(0); + let snapshot_slot = rpc_client.get_snapshot_slot().ok(); for _i in 0..10 { match get_validator_stats(&rpc_client, &identity) { @@ -400,16 +397,25 @@ fn main() { progress_bar.set_message(&format!( "{:02}:{:02}:{:02} \ {}| \ - Processed Slot: {} | Confirmed Slot: {} | Finalized Slot: {} | Snapshot Slot: {} | \ + Processed Slot: {} | Confirmed Slot: {} | Finalized Slot: {} | \ + Snapshot Slot: {} | \ Transactions: {} | {}", - uptime.num_hours(), uptime.num_minutes() % 60, uptime.num_seconds() % 60, + uptime.num_hours(), + uptime.num_minutes() % 60, + uptime.num_seconds() % 60, if health == "ok" { "".to_string() } else { format!("| {} ", style(health).bold().red()) }, - processed_slot, confirmed_slot, finalized_slot, snapshot_slot, - transaction_count, identity_balance + processed_slot, + confirmed_slot, + finalized_slot, + snapshot_slot + .map(|s| s.to_string()) + .unwrap_or_else(|| "-".to_string()), + transaction_count, + identity_balance )); } Err(err) => {