From 2dc19b31c684675e227c905649ddf5f6406dab61 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 15 Jul 2022 10:20:21 -0500 Subject: [PATCH 1/6] Add RPC getStakeMinimumDelegation --- client/src/mock_sender.rs | 1 + client/src/nonblocking/rpc_client.rs | 59 +++++++++------------- client/src/rpc_client.rs | 57 ++++++++------------- client/src/rpc_request.rs | 2 + docs/src/developing/clients/jsonrpc-api.md | 29 +++++++++++ programs/bpf/Cargo.lock | 1 + rpc/Cargo.toml | 1 + rpc/src/rpc.rs | 49 ++++++++++++++++++ 8 files changed, 129 insertions(+), 70 deletions(-) diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index 5ac3fb5bd68664..84d7327fe204d0 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -288,6 +288,7 @@ impl RpcSender for MockSender { active: 123, inactive: 12, }), + "getStakeMinimumDelegation" => Value::Number(Number::from(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..77c4fc65bd776d 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,30 @@ 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) + self.send(RpcRequest::GetStakeMinimumDelegation, Value::Null) + .await } /// 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..d85c7693671f6f 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -111,6 +111,7 @@ pub enum RpcRequest { SendTransaction, SimulateTransaction, SignVote, + GetStakeMinimumDelegation, } #[allow(deprecated)] @@ -184,6 +185,7 @@ impl fmt::Display for RpcRequest { RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SimulateTransaction => "simulateTransaction", RpcRequest::SignVote => "signVote", + RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation", }; write!(f, "{}", method) diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index e3d98198a0b578..ae9764c956b50c 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,34 @@ Result: } ``` +### getStakeMinimumDelegation + +Returns the stake minimum delegation, in lamports. + +#### Parameters: + +None + +#### Results: + +- `` - 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": 1000000000, "id": 1 } +``` + ### getSupply Returns information about the current supply. diff --git a/programs/bpf/Cargo.lock b/programs/bpf/Cargo.lock index 22e991228dbe9e..72f4f57f38845a 100644 --- a/programs/bpf/Cargo.lock +++ b/programs/bpf/Cargo.lock @@ -5278,6 +5278,7 @@ dependencies = [ "solana-runtime", "solana-sdk 1.11.4", "solana-send-transaction-service", + "solana-stake-program", "solana-storage-bigtable", "solana-streamer", "solana-transaction-status", diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index d73dbc1f5e1486..3fde4d2518d0b9 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -43,6 +43,7 @@ solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.11.4" solana-runtime = { path = "../runtime", version = "=1.11.4" } solana-sdk = { path = "../sdk", version = "=1.11.4" } solana-send-transaction-service = { path = "../send-transaction-service", version = "=1.11.4" } +solana-stake-program = { path = "../programs/stake", version = "=1.11.4" } solana-storage-bigtable = { path = "../storage-bigtable", version = "=1.11.4" } solana-streamer = { path = "../streamer", version = "=1.11.4" } solana-transaction-status = { path = "../transaction-status", version = "=1.11.4" } diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 46a515e9a5b39f..e217e3b13b16e8 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,29 @@ pub mod tests { ) ); } + + #[test] + fn test_rpc_get_stake_minimum_delegation() { + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let stake_minimum_delegation = + solana_stake_program::get_minimum_delegation(&bank.feature_set); + let RpcHandler { meta, io, .. } = rpc; + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getStakeMinimumDelegation"}"#; + let res = io.handle_request_sync(req, meta); + let expected = json!({ + "jsonrpc": "2.0", + "result": { + "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, + "value": stake_minimum_delegation, + }, + "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!(result, expected); + } } From 029224122c8c2f5f2f8a9b0a1c56e9e66cd1d885 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 15 Jul 2022 14:42:04 -0500 Subject: [PATCH 2/6] fixup! rpc --- client/src/nonblocking/rpc_client.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index 77c4fc65bd776d..a06caa976b19fb 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -4544,8 +4544,10 @@ impl RpcClient { /// # Ok::<(), ClientError>(()) /// ``` pub async fn get_stake_minimum_delegation(&self) -> ClientResult { - self.send(RpcRequest::GetStakeMinimumDelegation, Value::Null) - .await + Ok(self + .send::>(RpcRequest::GetStakeMinimumDelegation, Value::Null) + .await? + .value) } /// Request the transaction count. From 4d87ea3ad007a56cda4ea2a821685eaf2dcdf4b7 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 15 Jul 2022 15:23:27 -0500 Subject: [PATCH 3/6] fixup rpc --- rpc/src/rpc.rs | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index e217e3b13b16e8..f36f47123abea7 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -8387,24 +8387,16 @@ pub mod tests { fn test_rpc_get_stake_minimum_delegation() { let rpc = RpcHandler::start(); let bank = rpc.working_bank(); - let stake_minimum_delegation = + let expected_stake_minimum_delegation = solana_stake_program::get_minimum_delegation(&bank.feature_set); - let RpcHandler { meta, io, .. } = rpc; - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getStakeMinimumDelegation"}"#; - let res = io.handle_request_sync(req, meta); - let expected = json!({ - "jsonrpc": "2.0", - "result": { - "context": {"slot": 0, "apiVersion": RpcApiVersion::default()}, - "value": stake_minimum_delegation, - }, - "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!(result, expected); + 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 + ); } } From 8b922e4d8d9c5a9cb162c6aaa88a270cd5a7d683 Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 15 Jul 2022 15:24:56 -0500 Subject: [PATCH 4/6] fixup rpc client mock sender --- client/src/mock_sender.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index 84d7327fe204d0..bfef4350404458 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -288,7 +288,10 @@ impl RpcSender for MockSender { active: 123, inactive: 12, }), - "getStakeMinimumDelegation" => Value::Number(Number::from(123_456_789)), + "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 { From d013ae10f2f457197b60becdd6467d67aaadb53b Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Fri, 15 Jul 2022 16:20:29 -0500 Subject: [PATCH 5/6] fixup docs --- docs/src/developing/clients/jsonrpc-api.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index ae9764c956b50c..8cdd2b5a7d50cc 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -2462,6 +2462,8 @@ None #### Results: +The result will be an RpcResponse JSON object with `value` equal to: + - `` - The stake minimum delegation, in lamports #### Example: @@ -2477,7 +2479,16 @@ curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d ' Result: ```json -{ "jsonrpc": "2.0", "result": 1000000000, "id": 1 } +{ + "jsonrpc": "2.0", + "result": { + "context": { + "slot": 501 + }, + "value": 1000000000 + }, + "id": 1 +} ``` ### getSupply From 97f503d23363b4bd15df1a44d911ba39e948f3dd Mon Sep 17 00:00:00 2001 From: Brooks Prumo Date: Sun, 17 Jul 2022 11:51:59 -0500 Subject: [PATCH 6/6] pr: sort --- client/src/rpc_request.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index d85c7693671f6f..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, @@ -111,7 +112,6 @@ pub enum RpcRequest { SendTransaction, SimulateTransaction, SignVote, - GetStakeMinimumDelegation, } #[allow(deprecated)] @@ -165,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", @@ -185,7 +186,6 @@ impl fmt::Display for RpcRequest { RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SimulateTransaction => "simulateTransaction", RpcRequest::SignVote => "signVote", - RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation", }; write!(f, "{}", method)