rpc: add context toggle to getProgramAccounts (#17399)
* fix(rpc): return context in get_program_accounts * doc(rpc): document withContext flag * fix(rpc): fix comment Co-authored-by: Michael Vines <mvines@gmail.com> * fix(rpc): fix doc Co-authored-by: Michael Vines <mvines@gmail.com> Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
@ -1676,11 +1676,11 @@ pub fn process_show_stakes(
|
|||||||
progress_bar.set_message("Fetching stake accounts...");
|
progress_bar.set_message("Fetching stake accounts...");
|
||||||
|
|
||||||
let mut program_accounts_config = RpcProgramAccountsConfig {
|
let mut program_accounts_config = RpcProgramAccountsConfig {
|
||||||
filters: None,
|
|
||||||
account_config: RpcAccountInfoConfig {
|
account_config: RpcAccountInfoConfig {
|
||||||
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
|
encoding: Some(solana_account_decoder::UiAccountEncoding::Base64),
|
||||||
..RpcAccountInfoConfig::default()
|
..RpcAccountInfoConfig::default()
|
||||||
},
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
if let Some(vote_account_pubkeys) = vote_account_pubkeys {
|
||||||
|
@ -1102,6 +1102,7 @@ fn get_buffers(
|
|||||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||||
..RpcAccountInfoConfig::default()
|
..RpcAccountInfoConfig::default()
|
||||||
},
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
Ok(results)
|
Ok(results)
|
||||||
@ -1406,6 +1407,7 @@ fn process_close(
|
|||||||
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
data_slice: Some(UiDataSliceConfig { offset: 0, length }),
|
||||||
..RpcAccountInfoConfig::default()
|
..RpcAccountInfoConfig::default()
|
||||||
},
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
},
|
},
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
@ -1163,11 +1163,11 @@ impl RpcClient {
|
|||||||
self.get_program_accounts_with_config(
|
self.get_program_accounts_with_config(
|
||||||
pubkey,
|
pubkey,
|
||||||
RpcProgramAccountsConfig {
|
RpcProgramAccountsConfig {
|
||||||
filters: None,
|
|
||||||
account_config: RpcAccountInfoConfig {
|
account_config: RpcAccountInfoConfig {
|
||||||
encoding: Some(UiAccountEncoding::Base64Zstd),
|
encoding: Some(UiAccountEncoding::Base64Zstd),
|
||||||
..RpcAccountInfoConfig::default()
|
..RpcAccountInfoConfig::default()
|
||||||
},
|
},
|
||||||
|
..RpcProgramAccountsConfig::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,7 @@ pub struct RpcProgramAccountsConfig {
|
|||||||
pub filters: Option<Vec<RpcFilterType>>,
|
pub filters: Option<Vec<RpcFilterType>>,
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub account_config: RpcAccountInfoConfig,
|
pub account_config: RpcAccountInfoConfig,
|
||||||
|
pub with_context: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -10,6 +10,7 @@ use crate::{
|
|||||||
use bincode::{config::Options, serialize};
|
use bincode::{config::Options, serialize};
|
||||||
use jsonrpc_core::{types::error, Error, Metadata, Result};
|
use jsonrpc_core::{types::error, Error, Metadata, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use solana_account_decoder::{
|
use solana_account_decoder::{
|
||||||
parse_token::{spl_token_id_v2_0, token_amount_to_ui_amount, UiTokenAmount},
|
parse_token::{spl_token_id_v2_0, token_amount_to_ui_amount, UiTokenAmount},
|
||||||
UiAccount, UiAccountEncoding, UiDataSliceConfig,
|
UiAccount, UiAccountEncoding, UiDataSliceConfig,
|
||||||
@ -103,6 +104,16 @@ fn new_response<T>(bank: &Bank, value: T) -> RpcResponse<T> {
|
|||||||
Response { context, value }
|
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<T> {
|
||||||
|
Context(RpcResponse<T>),
|
||||||
|
NoContext(T),
|
||||||
|
}
|
||||||
|
|
||||||
fn is_finalized(
|
fn is_finalized(
|
||||||
block_commitment_cache: &BlockCommitmentCache,
|
block_commitment_cache: &BlockCommitmentCache,
|
||||||
bank: &Bank,
|
bank: &Bank,
|
||||||
@ -350,7 +361,8 @@ impl JsonRpcRequestProcessor {
|
|||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
config: Option<RpcAccountInfoConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
filters: Vec<RpcFilterType>,
|
filters: Vec<RpcFilterType>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>> {
|
with_context: bool,
|
||||||
|
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>> {
|
||||||
let config = config.unwrap_or_default();
|
let config = config.unwrap_or_default();
|
||||||
let bank = self.bank(config.commitment);
|
let bank = self.bank(config.commitment);
|
||||||
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
|
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
|
||||||
@ -367,7 +379,7 @@ impl JsonRpcRequestProcessor {
|
|||||||
};
|
};
|
||||||
let result =
|
let result =
|
||||||
if program_id == &spl_token_id_v2_0() && encoding == UiAccountEncoding::JsonParsed {
|
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 {
|
} else {
|
||||||
keyed_accounts
|
keyed_accounts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -383,7 +395,10 @@ impl JsonRpcRequestProcessor {
|
|||||||
})
|
})
|
||||||
.collect()
|
.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(
|
pub fn get_inflation_reward(
|
||||||
@ -2274,7 +2289,7 @@ pub mod rpc_full {
|
|||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
program_id_str: String,
|
program_id_str: String,
|
||||||
config: Option<RpcProgramAccountsConfig>,
|
config: Option<RpcProgramAccountsConfig>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>>;
|
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getMinimumBalanceForRentExemption")]
|
#[rpc(meta, name = "getMinimumBalanceForRentExemption")]
|
||||||
fn get_minimum_balance_for_rent_exemption(
|
fn get_minimum_balance_for_rent_exemption(
|
||||||
@ -2599,19 +2614,20 @@ pub mod rpc_full {
|
|||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
program_id_str: String,
|
program_id_str: String,
|
||||||
config: Option<RpcProgramAccountsConfig>,
|
config: Option<RpcProgramAccountsConfig>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>> {
|
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>> {
|
||||||
debug!(
|
debug!(
|
||||||
"get_program_accounts rpc request received: {:?}",
|
"get_program_accounts rpc request received: {:?}",
|
||||||
program_id_str
|
program_id_str
|
||||||
);
|
);
|
||||||
let program_id = verify_pubkey(&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),
|
Some(config.account_config),
|
||||||
config.filters.unwrap_or_default(),
|
config.filters.unwrap_or_default(),
|
||||||
|
config.with_context.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(None, vec![])
|
(None, vec![], false)
|
||||||
};
|
};
|
||||||
if filters.len() > MAX_GET_PROGRAM_ACCOUNT_FILTERS {
|
if filters.len() > MAX_GET_PROGRAM_ACCOUNT_FILTERS {
|
||||||
return Err(Error::invalid_params(format!(
|
return Err(Error::invalid_params(format!(
|
||||||
@ -2622,7 +2638,7 @@ pub mod rpc_full {
|
|||||||
for filter in &filters {
|
for filter in &filters {
|
||||||
verify_filter(filter)?;
|
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(
|
fn get_inflation_governor(
|
||||||
@ -4697,6 +4713,26 @@ pub mod tests {
|
|||||||
.expect("actual response deserialization");
|
.expect("actual response deserialization");
|
||||||
assert_eq!(expected, result);
|
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
|
// Set up nonce accounts to test filters
|
||||||
let nonce_keypair0 = Keypair::new();
|
let nonce_keypair0 = Keypair::new();
|
||||||
let instruction = system_instruction::create_nonce_account(
|
let instruction = system_instruction::create_nonce_account(
|
||||||
|
@ -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 `<string>`.
|
"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 `<string>`.
|
||||||
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "base58", "base64" or "base64+zstd" encodings.
|
- (optional) `dataSlice: <object>` - limit the returned account data using the provided `offset: <usize>` and `length: <usize>` fields; only available for "base58", "base64" or "base64+zstd" encodings.
|
||||||
- (optional) `filters: <array>` - filter results using various [filter objects](jsonrpc-api.md#filters); account must meet all filter criteria to be included in results
|
- (optional) `filters: <array>` - 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:
|
##### Filters:
|
||||||
- `memcmp: <object>` - compares a provided series of bytes with program account data at a particular offset. Fields:
|
- `memcmp: <object>` - compares a provided series of bytes with program account data at a particular offset. Fields:
|
||||||
- `offset: <usize>` - offset into program account data to start comparison
|
- `offset: <usize>` - offset into program account data to start comparison
|
||||||
@ -1790,7 +1790,9 @@ Returns all accounts owned by the provided program Pubkey
|
|||||||
|
|
||||||
#### Results:
|
#### 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: <string>` - the account Pubkey as base-58 encoded string
|
- `pubkey: <string>` - the account Pubkey as base-58 encoded string
|
||||||
- `account: <object>` - a JSON object, with the following sub fields:
|
- `account: <object>` - a JSON object, with the following sub fields:
|
||||||
|
Reference in New Issue
Block a user