From e608580040a6bcc9ab600c94510fce6234113e7e Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Tue, 17 May 2022 11:46:34 -0500 Subject: [PATCH] Add get_stake_minimum_delegation() to rpc_client (#25200) --- client/src/nonblocking/rpc_client.rs | 40 +++++++++++++++++++++++++- client/src/rpc_client.rs | 43 +++++++++++++++++++++++++++- 2 files changed, 81 insertions(+), 2 deletions(-) diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index c03dfec1be4b28..f899f723112d43 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -39,10 +39,11 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, + instruction::InstructionError, message::Message, pubkey::Pubkey, signature::Signature, - transaction::{self, uses_durable_nonce, Transaction}, + transaction::{self, uses_durable_nonce, Transaction, TransactionError}, }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedConfirmedTransactionWithStatusMeta, TransactionStatus, @@ -4513,6 +4514,43 @@ impl RpcClient { parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) } + /// Returns the stake minimum delegation, in lamports. + 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) + } + /// Request the transaction count. pub async fn get_transaction_count(&self) -> ClientResult { self.get_transaction_count_with_commitment(self.commitment()) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 89959de20e56fa..67d71a39cc287e 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -3712,6 +3712,11 @@ impl RpcClient { ) } + /// Returns the stake minimum delegation, in lamports. + pub fn get_stake_minimum_delegation(&self) -> ClientResult { + self.invoke(self.rpc_client.get_stake_minimum_delegation()) + } + /// Request the transaction count. pub fn get_transaction_count(&self) -> ClientResult { self.invoke(self.rpc_client.get_transaction_count()) @@ -4101,7 +4106,7 @@ mod tests { system_transaction, transaction::TransactionError, }, - std::{io, thread}, + std::{collections::HashMap, io, thread}, }; #[test] @@ -4317,4 +4322,40 @@ mod tests { let is_err = rpc_client.get_latest_blockhash().is_err(); assert!(is_err); } + + #[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 }, + 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); + } }