From 93036bec0194754359048b09d5f550cb8829c341 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 21 Jan 2020 13:05:04 -0700 Subject: [PATCH] Add minimumLedgerSlot RPC API --- book/src/api-reference/jsonrpc-api.md | 28 +++++++++++++++++++-- client/src/rpc_client.rs | 19 +++++++++++++++ client/src/rpc_request.rs | 2 ++ core/src/rpc.rs | 35 +++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 2 deletions(-) diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 584ea1eac..acd9b6ac9 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -40,6 +40,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana- * [getTotalSupply](jsonrpc-api.md#gettotalsupply) * [getVersion](jsonrpc-api.md#getversion) * [getVoteAccounts](jsonrpc-api.md#getvoteaccounts) +* [minimumLedgerSlot](jsonrpc-api.md#minimumledgerslot) * [requestAirdrop](jsonrpc-api.md#requestairdrop) * [sendTransaction](jsonrpc-api.md#sendtransaction) * [startSubscriptionChannel](jsonrpc-api.md#startsubscriptionchannel) @@ -585,7 +586,7 @@ Returns the current slot the node is processing curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getSlot"}' http://localhost:8899 // Result -{"jsonrpc":"2.0","result":"1234","id":1} +{"jsonrpc":"2.0","result":1234,"id":1} ``` ### getSlotLeader @@ -628,7 +629,7 @@ Returns the current storage segment size in terms of slots // Request curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getSlotsPerSegment"}' http://localhost:8899 // Result -{"jsonrpc":"2.0","result":"1024","id":1} +{"jsonrpc":"2.0","result":1024,"id":1} ``` ### getStorageTurn @@ -772,6 +773,29 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m {"jsonrpc":"2.0","result":{"current":[{"commission":0,"epochVoteAccount":true,"nodePubkey":"B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD","lastVote":147,"activatedStake":42,"votePubkey":"3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"}],"delinquent":[{"commission":127,"epochVoteAccount":false,"nodePubkey":"6ZPxeQaDo4bkZLRsdNrCzchNQr5LN9QMc9sipXv9Kw8f","lastVote":0,"activatedStake":0,"votePubkey":"CmgCk4aMS7KW1SHX3s9K5tBJ6Yng2LBaC8MFov4wx9sm"}]},"id":1} ``` +### minimumLedgerSlot + +Returns the lowest slot that the node has information about in its ledger. This +value may increase over time if the node is configured to purge older ledger data + +#### Parameters: + +None + +#### Results: + +* `u64` - Minimum ledger slot + +#### Example: + +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"minimumLedgerSlot"}' http://localhost:8899 + +// Result +{"jsonrpc":"2.0","result":1234,"id":1} +``` + ### requestAirdrop Requests an airdrop of lamports to a Pubkey diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 38ec7d68c..eacfb5195 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -386,6 +386,25 @@ impl RpcClient { }) } + pub fn minimum_ledger_slot(&self) -> io::Result { + let response = self + .client + .send(&RpcRequest::MinimumLedgerSlot, Value::Null, 0) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("MinimumLedgerSlot request failure: {:?}", err), + ) + })?; + + serde_json::from_value(response).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("MinimumLedgerSlot parse failure: {}", err), + ) + }) + } + pub fn send_and_confirm_transaction( &self, transaction: &mut Transaction, diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index e2208ad38..be0b53952 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -35,6 +35,7 @@ pub enum RpcRequest { SendTransaction, SignVote, GetMinimumBalanceForRentExemption, + MinimumLedgerSlot, } impl RpcRequest { @@ -75,6 +76,7 @@ impl RpcRequest { RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SignVote => "signVote", RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption", + RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot", }; json!({ "jsonrpc": jsonrpc, diff --git a/core/src/rpc.rs b/core/src/rpc.rs index d8d8cae73..30102289c 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -229,6 +229,19 @@ impl JsonRpcRequestProcessor { Ok(self.bank(commitment).collector_id().to_string()) } + fn minimum_ledger_slot(&self) -> Result { + match self.blockstore.slot_meta_iterator(0) { + Ok(mut metas) => match metas.next() { + Some((slot, _meta)) => Ok(slot), + None => Err(Error::invalid_request()), + }, + Err(err) => { + warn!("slot_meta_iterator failed: {:?}", err); + Err(Error::invalid_request()) + } + } + } + fn get_transaction_count(&self, commitment: Option) -> Result { Ok(self.bank(commitment).transaction_count() as u64) } @@ -530,6 +543,9 @@ pub trait RpcSol { commitment: Option, ) -> Result; + #[rpc(meta, name = "minimumLedgerSlot")] + fn minimum_ledger_slot(&self, meta: Self::Metadata) -> Result; + #[rpc(meta, name = "getVoteAccounts")] fn get_vote_accounts( &self, @@ -990,6 +1006,10 @@ impl RpcSol for RpcSolImpl { .get_slot_leader(commitment) } + fn minimum_ledger_slot(&self, meta: Self::Metadata) -> Result { + meta.request_processor.read().unwrap().minimum_ledger_slot() + } + fn get_vote_accounts( &self, meta: Self::Metadata, @@ -1379,6 +1399,21 @@ pub mod tests { assert_eq!(expected, result); } + #[test] + fn test_rpc_minimum_ledger_slot() { + 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":"minimumLedgerSlot"}}"#); + let res = io.handle_request_sync(&req, meta); + let expected = r#"{"jsonrpc":"2.0","result":0,"id":1}"#; + let expected: Response = + serde_json::from_str(&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_total_supply() { let bob_pubkey = Pubkey::new_rand();