From 8773f79b7da41ebff96659715b57d9d5be701350 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 16 Nov 2022 23:34:06 +0000 Subject: [PATCH] Fix client get_program_accounts_with_config calls with context (backport #28772) (#28846) * 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 * Fix conflicts Co-authored-by: Tyera Eulberg --- client/src/nonblocking/rpc_client.rs | 7 +-- client/src/rpc_client.rs | 79 ++++++++++++++++++++++++++++ client/src/rpc_response.rs | 19 +++++++ rpc/src/rpc.rs | 11 ---- 4 files changed, 102 insertions(+), 14 deletions(-) diff --git a/client/src/nonblocking/rpc_client.rs b/client/src/nonblocking/rpc_client.rs index c03dfec1be4b28..2d300be0b7822a 100644 --- a/client/src/nonblocking/rpc_client.rs +++ b/client/src/nonblocking/rpc_client.rs @@ -4504,12 +4504,13 @@ impl RpcClient { account_config, ..config }; - let accounts: Vec = self - .send( + let accounts = self + .send::>>( 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..564494aa809908 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -4317,4 +4317,83 @@ mod tests { let is_err = rpc_client.get_latest_blockhash().is_err(); assert!(is_err); } + + #[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); + } + } } diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 008ed518fbff6a..75ea95abf7c7e2 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, 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..930303a869c4be 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,16 +124,6 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { } } -/// 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, Serialize, Deserialize)] -#[serde(untagged)] -pub enum OptionalContext { - Context(RpcResponse), - NoContext(T), -} - fn is_finalized( block_commitment_cache: &BlockCommitmentCache, bank: &Bank,