From 898565cb49cfc133ded9db78063494918d26f56c Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 16 Nov 2022 14:35:35 -0700 Subject: [PATCH] Fix client get_program_accounts_with_config calls with context (#28772) * Move OptionalContext to solana-rpc-client-api * Add helper function * Add failing test * Support OptionalContext in RpcClient::get_program_accounts_with_config (cherry picked from commit b18ef88c4035f5e8a3d1667dadf8202581897f83) # Conflicts: # client/src/nonblocking/rpc_client.rs # client/src/rpc_client.rs # rpc/src/rpc.rs --- client/src/nonblocking/rpc_client.rs | 13 +++- client/src/rpc_client.rs | 102 +++++++++++++++++++++++++++ client/src/rpc_response.rs | 19 +++++ rpc/src/rpc.rs | 4 +- 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index c03dfec1be4b28..6e42de803e6f62 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -4496,6 +4496,7 @@ impl RpcClient { .commitment .unwrap_or_else(|| self.commitment()); let commitment = self.maybe_map_commitment(commitment).await?; +<<<<<<< HEAD:client/src/nonblocking/rpc_client.rs let account_config = RpcAccountInfoConfig { commitment: Some(commitment), ..config.account_config @@ -4506,10 +4507,20 @@ impl RpcClient { }; let accounts: Vec = self .send( +======= + config.account_config.commitment = Some(commitment); + if let Some(filters) = config.filters { + config.filters = Some(self.maybe_map_filters(filters).await?); + } + + let accounts = self + .send::>>( +>>>>>>> b18ef88c4 (Fix client get_program_accounts_with_config calls with context (#28772)):rpc-client/src/nonblocking/rpc_client.rs RpcRequest::GetProgramAccounts, json!([pubkey.to_string(), config]), ) - .await?; + .await? + .parse_value(); parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts) } diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 89959de20e56fa..f3b712db9e7638 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -4317,4 +4317,106 @@ mod tests { let is_err = rpc_client.get_latest_blockhash().is_err(); assert!(is_err); } +<<<<<<< HEAD:client/src/rpc_client.rs +======= + + #[test] + fn test_get_stake_minimum_delegation() { + let expected_minimum_delegation: u64 = 123_456_789; + let rpc_client = RpcClient::new_mock("succeeds".to_string()); + + // Test: without commitment + { + let actual_minimum_delegation = rpc_client.get_stake_minimum_delegation().unwrap(); + assert_eq!(expected_minimum_delegation, actual_minimum_delegation); + } + + // Test: with commitment + { + let actual_minimum_delegation = rpc_client + .get_stake_minimum_delegation_with_commitment(CommitmentConfig::confirmed()) + .unwrap(); + assert_eq!(expected_minimum_delegation, actual_minimum_delegation); + } + } + + #[test] + fn test_get_program_accounts_with_config() { + let program_id = Pubkey::new_unique(); + let pubkey = Pubkey::new_unique(); + let account = Account { + lamports: 1_000_000, + data: vec![], + owner: program_id, + executable: false, + rent_epoch: 0, + }; + let keyed_account = RpcKeyedAccount { + pubkey: pubkey.to_string(), + account: UiAccount::encode(&pubkey, &account, UiAccountEncoding::Base64, None, None), + }; + let expected_result = vec![(pubkey, account)]; + // Test: without context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::NoContext(vec![keyed_account.clone()])) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: None, + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + + // Test: with context + { + let mocks: Mocks = [( + RpcRequest::GetProgramAccounts, + serde_json::to_value(OptionalContext::Context(Response { + context: RpcResponseContext { + slot: 1, + api_version: None, + }, + value: vec![keyed_account], + })) + .unwrap(), + )] + .into_iter() + .collect(); + let rpc_client = RpcClient::new_mock_with_mocks("mock_client".to_string(), mocks); + let result = rpc_client + .get_program_accounts_with_config( + &program_id, + RpcProgramAccountsConfig { + filters: None, + account_config: RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Base64), + data_slice: None, + commitment: None, + min_context_slot: None, + }, + with_context: Some(true), + }, + ) + .unwrap(); + assert_eq!(expected_result, result); + } + } +>>>>>>> b18ef88c4 (Fix client get_program_accounts_with_config calls with context (#28772)):rpc-client/src/rpc_client.rs } diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 008ed518fbff6a..fc75e7f3189128 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -16,6 +16,25 @@ use { thiserror::Error, }; +/// Wrapper for rpc return types of methods that provide responses both with and without context. +/// Main purpose of this is to fix methods that lack context information in their return type, +/// without breaking backwards compatibility. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum OptionalContext { + Context(Response), + NoContext(T), +} + +impl OptionalContext { + pub fn parse_value(self) -> T { + match self { + Self::Context(response) => response.value, + Self::NoContext(value) => value, + } + } +} + pub type RpcResult = client_error::Result>; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 312737670b795b..f02b168ba8ead1 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -9,7 +9,6 @@ use { crossbeam_channel::{unbounded, Receiver, Sender}, jsonrpc_core::{futures::future, types::error, BoxFuture, Error, Metadata, Result}, jsonrpc_derive::rpc, - serde::{Deserialize, Serialize}, solana_account_decoder::{ parse_token::{is_known_spl_token_id, token_amount_to_ui_amount, UiTokenAmount}, UiAccount, UiAccountEncoding, UiDataSliceConfig, MAX_BASE58_BYTES, @@ -125,6 +124,7 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { } } +<<<<<<< HEAD /// Wrapper for rpc return types of methods that provide responses both with and without context. /// Main purpose of this is to fix methods that lack context information in their return type, /// without breaking backwards compatibility. @@ -135,6 +135,8 @@ pub enum OptionalContext { NoContext(T), } +======= +>>>>>>> b18ef88c4 (Fix client get_program_accounts_with_config calls with context (#28772)) fn is_finalized( block_commitment_cache: &BlockCommitmentCache, bank: &Bank,