Skip to content

Commit

Permalink
Fix client get_program_accounts_with_config calls with context (solan…
Browse files Browse the repository at this point in the history
…a-labs#28772)

* Move OptionalContext to solana-rpc-client-api

* Add helper function

* Add failing test

* Support OptionalContext in RpcClient::get_program_accounts_with_config
  • Loading branch information
Tyera Eulberg authored and gnapoli23 committed Dec 16, 2022
1 parent 9624cc0 commit c42d892
Show file tree
Hide file tree
Showing 4 changed files with 103 additions and 14 deletions.
19 changes: 19 additions & 0 deletions rpc-client-api/src/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
8 changes: 5 additions & 3 deletions rpc-client/src/nonblocking/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4420,12 +4420,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 rpc-client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4292,4 +4292,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);
}
}
}
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 @@ -122,16 +121,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, Eq, 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 c42d892

Please sign in to comment.