Skip to content

Commit

Permalink
Fix client get_program_accounts_with_config calls with context (backp…
Browse files Browse the repository at this point in the history
…ort #28772) (#28847)

* 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 b18ef88)

# Conflicts:
#	rpc/src/rpc.rs

* Fix conflicts

Co-authored-by: Tyera Eulberg <tyera@solana.com>
  • Loading branch information
mergify[bot] and Tyera Eulberg authored Nov 17, 2022
1 parent 0442111 commit ef5b801
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 14 deletions.
8 changes: 5 additions & 3 deletions client/src/nonblocking/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4518,12 +4518,14 @@ impl RpcClient {
if let Some(filters) = config.filters {
config.filters = Some(self.maybe_map_filters(filters).await?);
}
let accounts: Vec<RpcKeyedAccount> = self
.send(

let accounts = self
.send::<OptionalContext<Vec<RpcKeyedAccount>>>(
RpcRequest::GetProgramAccounts,
json!([pubkey.to_string(), config]),
)
.await?;
.await?
.parse_value();
parse_keyed_accounts(accounts, RpcRequest::GetProgramAccounts)
}

Expand Down
79 changes: 79 additions & 0 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4431,4 +4431,83 @@ mod tests {
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);
}
}
}
19 changes: 19 additions & 0 deletions client/src/rpc_response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,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<T> {
Context(Response<T>),
NoContext(T),
}

impl<T> OptionalContext<T> {
pub fn parse_value(self) -> T {
match self {
Self::Context(response) => response.value,
Self::NoContext(value) => value,
}
}
}

pub type RpcResult<T> = client_error::Result<Response<T>>;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
Expand Down
11 changes: 0 additions & 11 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -126,16 +125,6 @@ fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
}
}

/// 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<T> {
Context(RpcResponse<T>),
NoContext(T),
}

fn is_finalized(
block_commitment_cache: &BlockCommitmentCache,
bank: &Bank,
Expand Down

0 comments on commit ef5b801

Please sign in to comment.