From f771049b3aeb094732e9301c82f69776cd37d3d1 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Sun, 17 Jul 2022 13:39:39 -0500 Subject: [PATCH] Add RPC getStakeMinimumDelegation (#26638) * Add RPC getStakeMinimumDelegation * fixup! rpc * fixup rpc * fixup rpc client mock sender * fixup docs * pr: sort --- client/src/mock_sender.rs | 4 ++ client/src/nonblocking/rpc_client.rs | 61 +++++++++------------- client/src/rpc_client.rs | 57 ++++++++------------ client/src/rpc_request.rs | 2 + docs/src/developing/clients/jsonrpc-api.md | 40 ++++++++++++++ programs/bpf/Cargo.lock | 1 + rpc/src/rpc.rs | 41 +++++++++++++++ 7 files changed, 136 insertions(+), 70 deletions(-) diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index 5ac3fb5bd68664..bfef4350404458 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -288,6 +288,10 @@ impl RpcSender for MockSender { active: 123, inactive: 12, }), + "getStakeMinimumDelegation" => json!(Response { + context: RpcResponseContext { slot: 1, api_version: None }, + value: 123_456_789, + }), "getSupply" => json!(Response { context: RpcResponseContext { slot: 1, api_version: None }, value: RpcSupply { diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index e4afd7dae939de..a06caa976b19fb 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -40,11 +40,10 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, - instruction::InstructionError, message::Message, pubkey::Pubkey, signature::Signature, - transaction::{self, uses_durable_nonce, Transaction, TransactionError}, + transaction::{self, uses_durable_nonce, Transaction}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -4523,40 +4522,32 @@ impl RpcClient { } /// Returns the stake minimum delegation, in lamports. + /// + /// # RPC Reference + /// + /// This method corresponds directly to the [`getStakeMinimumDelegation`] RPC method. + /// + /// [`getStakeMinimumDelegation`]: https://docs.solana.com/developing/clients/jsonrpc-api#getstakeminimumdelegation + /// + /// # Examples + /// + /// ``` + /// # use solana_client::{ + /// # nonblocking::rpc_client::RpcClient, + /// # client_error::ClientError, + /// # }; + /// # futures::executor::block_on(async { + /// # let rpc_client = RpcClient::new_mock("succeeds".to_string()); + /// let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation().await?; + /// # Ok::<(), ClientError>(()) + /// # })?; + /// # Ok::<(), ClientError>(()) + /// ``` pub async fn get_stake_minimum_delegation(&self) -> ClientResult { - let instruction = solana_sdk::stake::instruction::get_minimum_delegation(); - let transaction = Transaction::new_with_payer(&[instruction], None); - let response = self.simulate_transaction(&transaction).await?; - let RpcTransactionReturnData { - program_id, - data: (data, encoding), - } = response - .value - .return_data - .ok_or_else(|| ClientErrorKind::Custom("return data was empty".to_string()))?; - if Pubkey::from_str(&program_id) != Ok(solana_sdk::stake::program::id()) { - return Err(TransactionError::InstructionError( - 0, - InstructionError::IncorrectProgramId, - ) - .into()); - } - if encoding != ReturnDataEncoding::Base64 { - return Err( - ClientErrorKind::Custom("return data encoding is invalid".to_string()).into(), - ); - } - let data = base64::decode(data).map_err(|err| { - ClientErrorKind::Custom(format!("failed to decode return data: {}", err)) - })?; - let minimum_delegation = u64::from_le_bytes(data.try_into().map_err(|data: Vec| { - ClientErrorKind::Custom(format!( - "return data cannot be represented as a u64: expected size: {}, actual size: {}", - std::mem::size_of::(), - data.len() - )) - })?); - Ok(minimum_delegation) + Ok(self + .send::>(RpcRequest::GetStakeMinimumDelegation, Value::Null) + .await? + .value) } /// Request the transaction count. diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index fa249dc1b79104..a80ff8f75f8c3b 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -3713,6 +3713,24 @@ impl RpcClient { } /// Returns the stake minimum delegation, in lamports. + /// + /// # RPC Reference + /// + /// This method corresponds directly to the [`getStakeMinimumDelegation`] RPC method. + /// + /// [`getStakeMinimumDelegation`]: https://docs.solana.com/developing/clients/jsonrpc-api#getstakeminimumdelegation + /// + /// # Examples + /// + /// ``` + /// # use solana_client::{ + /// # rpc_client::RpcClient, + /// # client_error::ClientError, + /// # }; + /// # let rpc_client = RpcClient::new_mock("succeeds".to_string()); + /// let stake_minimum_delegation = rpc_client.get_stake_minimum_delegation()?; + /// # Ok::<(), ClientError>(()) + /// ``` pub fn get_stake_minimum_delegation(&self) -> ClientResult { self.invoke(self.rpc_client.get_stake_minimum_delegation()) } @@ -4106,7 +4124,7 @@ mod tests { system_transaction, transaction::TransactionError, }, - std::{collections::HashMap, io, thread}, + std::{io, thread}, }; #[test] @@ -4326,39 +4344,8 @@ mod tests { #[test] fn test_get_stake_minimum_delegation() { let expected_minimum_delegation: u64 = 123_456_789; - let rpc_client = { - let mocks = { - let rpc_response = { - let program_id = solana_sdk::stake::program::id().to_string(); - let data = ( - base64::encode(expected_minimum_delegation.to_le_bytes()), - ReturnDataEncoding::Base64, - ); - serde_json::to_value(Response { - context: RpcResponseContext { - slot: 1, - api_version: None, - }, - value: RpcSimulateTransactionResult { - err: None, - logs: None, - accounts: None, - units_consumed: None, - return_data: Some(RpcTransactionReturnData { program_id, data }), - }, - }) - .unwrap() - }; - let mut mocks = HashMap::new(); - mocks.insert(RpcRequest::SimulateTransaction, rpc_response); - mocks - }; - RpcClient::new_mock_with_mocks("succeeds".to_string(), mocks) - }; - - let client_result = rpc_client.get_stake_minimum_delegation(); - assert!(client_result.is_ok()); - let actual_minimum_delegation = client_result.unwrap(); - assert_eq!(actual_minimum_delegation, expected_minimum_delegation); + let rpc_client = RpcClient::new_mock("succeeds".to_string()); + let actual_minimum_delegation = rpc_client.get_stake_minimum_delegation().unwrap(); + assert_eq!(expected_minimum_delegation, actual_minimum_delegation); } } diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index d62c1207eedb5e..d3f0ceb1c0ad54 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -94,6 +94,7 @@ pub enum RpcRequest { GetStorageTurnRate, GetSlotsPerSegment, GetStakeActivation, + GetStakeMinimumDelegation, GetStoragePubkeysForSlot, GetSupply, GetTokenAccountBalance, @@ -164,6 +165,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetSlotLeader => "getSlotLeader", RpcRequest::GetSlotLeaders => "getSlotLeaders", RpcRequest::GetStakeActivation => "getStakeActivation", + RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation", RpcRequest::GetStorageTurn => "getStorageTurn", RpcRequest::GetStorageTurnRate => "getStorageTurnRate", RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment", diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 5d93d5b93ac9a8..f7d395a8b2c868 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -54,6 +54,7 @@ gives a convenient interface for the RPC methods. - [getSlotLeader](jsonrpc-api.md#getslotleader) - [getSlotLeaders](jsonrpc-api.md#getslotleaders) - [getStakeActivation](jsonrpc-api.md#getstakeactivation) +- [getStakeMinimumDelegation](jsonrpc-api.md#getstakeminimumdelegation) - [getSupply](jsonrpc-api.md#getsupply) - [getTokenAccountBalance](jsonrpc-api.md#gettokenaccountbalance) - [getTokenAccountsByDelegate](jsonrpc-api.md#gettokenaccountsbydelegate) @@ -2451,6 +2452,45 @@ Result: } ``` +### getStakeMinimumDelegation + +Returns the stake minimum delegation, in lamports. + +#### Parameters: + +None + +#### Results: + +The result will be an RpcResponse JSON object with `value` equal to: + +- `` - The stake minimum delegation, in lamports + +#### Example: + +Request: + +```bash +curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' + {"jsonrpc":"2.0","id":1,"method":"getStakeMinimumDelegation"} +' +``` + +Result: + +```json +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 501 + }, + "value": 1000000000 + }, + "id": 1 +} +``` + ### getSupply Returns information about the current supply. diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 50d8f425af0eaa..fb68c6c5f2b5d6 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -5278,6 +5278,7 @@ dependencies = [ "solana-runtime", "solana-sdk 1.11.3", "solana-send-transaction-service", + "solana-stake-program", "solana-storage-bigtable", "solana-streamer", "solana-transaction-status", diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 46a515e9a5b39f..f36f47123abea7 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -80,6 +80,7 @@ use { send_transaction_service::{SendTransactionService, TransactionInfo}, tpu_info::NullTpuInfo, }, + solana_stake_program, solana_storage_bigtable::Error as StorageError, solana_streamer::socket::SocketAddrSpace, solana_transaction_status::{ @@ -2169,6 +2170,13 @@ impl JsonRpcRequestProcessor { let fee = bank.get_fee_for_message(message); Ok(new_response(&bank, fee)) } + + fn get_stake_minimum_delegation(&self, config: RpcContextConfig) -> Result> { + let bank = self.get_bank_with_config(config)?; + let stake_minimum_delegation = + solana_stake_program::get_minimum_delegation(&bank.feature_set); + Ok(new_response(&bank, stake_minimum_delegation)) + } } fn optimize_filters(filters: &mut [RpcFilterType]) { @@ -3401,6 +3409,13 @@ pub mod rpc_full { data: String, config: Option, ) -> Result>>; + + #[rpc(meta, name = "getStakeMinimumDelegation")] + fn get_stake_minimum_delegation( + &self, + meta: Self::Metadata, + config: Option, + ) -> Result>; } pub struct FullImpl; @@ -3970,6 +3985,15 @@ pub mod rpc_full { })?; meta.get_fee_for_message(&sanitized_message, config.unwrap_or_default()) } + + fn get_stake_minimum_delegation( + &self, + meta: Self::Metadata, + config: Option, + ) -> Result> { + debug!("get_stake_minimum_delegation rpc request received"); + meta.get_stake_minimum_delegation(config.unwrap_or_default()) + } } } @@ -8358,4 +8382,21 @@ pub mod tests { ) ); } + + #[test] + fn test_rpc_get_stake_minimum_delegation() { + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let expected_stake_minimum_delegation = + solana_stake_program::get_minimum_delegation(&bank.feature_set); + + let request = create_test_request("getStakeMinimumDelegation", None); + let response: RpcResponse = parse_success_result(rpc.handle_request_sync(request)); + let actual_stake_minimum_delegation = response.value; + + assert_eq!( + actual_stake_minimum_delegation, + expected_stake_minimum_delegation + ); + } }