diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 9ab426fa542ff7..0161f29ece07c9 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -1676,11 +1676,11 @@ pub fn process_show_stakes( progress_bar.set_message("Fetching stake accounts..."); let mut program_accounts_config = RpcProgramAccountsConfig { - filters: None, account_config: RpcAccountInfoConfig { encoding: Some(solana_account_decoder::UiAccountEncoding::Base64), ..RpcAccountInfoConfig::default() }, + ..RpcProgramAccountsConfig::default() }; if let Some(vote_account_pubkeys) = vote_account_pubkeys { diff --git a/cli/src/program.rs b/cli/src/program.rs index 60778b29fd5932..bcb011ea9c5134 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -1102,6 +1102,7 @@ fn get_buffers( data_slice: Some(UiDataSliceConfig { offset: 0, length }), ..RpcAccountInfoConfig::default() }, + ..RpcProgramAccountsConfig::default() }, )?; Ok(results) @@ -1406,6 +1407,7 @@ fn process_close( data_slice: Some(UiDataSliceConfig { offset: 0, length }), ..RpcAccountInfoConfig::default() }, + ..RpcProgramAccountsConfig::default() }, )?; diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 5f1d92740b87f7..76db5a4499b49d 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -1163,11 +1163,11 @@ impl RpcClient { self.get_program_accounts_with_config( pubkey, RpcProgramAccountsConfig { - filters: None, account_config: RpcAccountInfoConfig { encoding: Some(UiAccountEncoding::Base64Zstd), ..RpcAccountInfoConfig::default() }, + ..RpcProgramAccountsConfig::default() }, ) } diff --git a/client/src/rpc_config.rs b/client/src/rpc_config.rs index 18ebda1ae2f5ab..dba7bdd200a044 100644 --- a/client/src/rpc_config.rs +++ b/client/src/rpc_config.rs @@ -127,6 +127,7 @@ pub struct RpcProgramAccountsConfig { pub filters: Option>, #[serde(flatten)] pub account_config: RpcAccountInfoConfig, + pub with_context: Option, } #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 366c19895c75a8..f6e05b89e6aac4 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -10,6 +10,7 @@ use crate::{ use bincode::{config::Options, serialize}; use jsonrpc_core::{types::error, Error, Metadata, Result}; use jsonrpc_derive::rpc; +use serde::{Deserialize, Serialize}; use solana_account_decoder::{ parse_token::{spl_token_id_v2_0, token_amount_to_ui_amount, UiTokenAmount}, UiAccount, UiAccountEncoding, UiDataSliceConfig, @@ -103,6 +104,16 @@ fn new_response(bank: &Bank, value: T) -> RpcResponse { Response { context, value } } +/// 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, @@ -350,7 +361,8 @@ impl JsonRpcRequestProcessor { program_id: &Pubkey, config: Option, filters: Vec, - ) -> Result> { + with_context: bool, + ) -> Result>> { let config = config.unwrap_or_default(); let bank = self.bank(config.commitment); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); @@ -367,7 +379,7 @@ impl JsonRpcRequestProcessor { }; let result = if program_id == &spl_token_id_v2_0() && encoding == UiAccountEncoding::JsonParsed { - get_parsed_token_accounts(bank, keyed_accounts.into_iter()).collect() + get_parsed_token_accounts(bank.clone(), keyed_accounts.into_iter()).collect() } else { keyed_accounts .into_iter() @@ -383,7 +395,10 @@ impl JsonRpcRequestProcessor { }) .collect() }; - Ok(result) + Ok(result).map(|result| match with_context { + true => OptionalContext::Context(new_response(&bank, result)), + false => OptionalContext::NoContext(result), + }) } pub fn get_inflation_reward( @@ -2274,7 +2289,7 @@ pub mod rpc_full { meta: Self::Metadata, program_id_str: String, config: Option, - ) -> Result>; + ) -> Result>>; #[rpc(meta, name = "getMinimumBalanceForRentExemption")] fn get_minimum_balance_for_rent_exemption( @@ -2599,19 +2614,20 @@ pub mod rpc_full { meta: Self::Metadata, program_id_str: String, config: Option, - ) -> Result> { + ) -> Result>> { debug!( "get_program_accounts rpc request received: {:?}", program_id_str ); let program_id = verify_pubkey(&program_id_str)?; - let (config, filters) = if let Some(config) = config { + let (config, filters, with_context) = if let Some(config) = config { ( Some(config.account_config), config.filters.unwrap_or_default(), + config.with_context.unwrap_or_default(), ) } else { - (None, vec![]) + (None, vec![], false) }; if filters.len() > MAX_GET_PROGRAM_ACCOUNT_FILTERS { return Err(Error::invalid_params(format!( @@ -2622,7 +2638,7 @@ pub mod rpc_full { for filter in &filters { verify_filter(filter)?; } - meta.get_program_accounts(&program_id, config, filters) + meta.get_program_accounts(&program_id, config, filters, with_context) } fn get_inflation_governor( @@ -4697,6 +4713,26 @@ pub mod tests { .expect("actual response deserialization"); assert_eq!(expected, result); + // Test returns context + let req = format!( + r#"{{ + "jsonrpc":"2.0", + "id":1, + "method":"getProgramAccounts", + "params":["{}",{{ + "withContext": true + }}] + }}"#, + system_program::id(), + ); + let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = serde_json::from_str(&res.expect("actual response")).unwrap(); + let contains_slot = result["result"]["context"] + .as_object() + .expect("must contain context") + .contains_key("slot"); + assert!(contains_slot); + // Set up nonce accounts to test filters let nonce_keypair0 = Keypair::new(); let instruction = system_instruction::create_nonce_account( diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 9894e6e0447f3c..6fb0113e0c08eb 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -1780,7 +1780,7 @@ Returns all accounts owned by the provided program Pubkey "jsonParsed" encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If "jsonParsed" is requested but a parser cannot be found, the field falls back to "base64" encoding, detectable when the `data` field is type ``. - (optional) `dataSlice: ` - limit the returned account data using the provided `offset: ` and `length: ` fields; only available for "base58", "base64" or "base64+zstd" encodings. - (optional) `filters: ` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results - + - (optional) `withContext: bool` - wrap the result in an RpcResponse JSON object. ##### Filters: - `memcmp: ` - compares a provided series of bytes with program account data at a particular offset. Fields: - `offset: ` - offset into program account data to start comparison @@ -1790,7 +1790,9 @@ Returns all accounts owned by the provided program Pubkey #### Results: -The result field will be an array of JSON objects, which will contain: +By default the result field will be an array of JSON objects. If `withContext` flag is set the array will be wrapped in an RpcResponse JSON object. + +The array will contain: - `pubkey: ` - the account Pubkey as base-58 encoded string - `account: ` - a JSON object, with the following sub fields: