Restore getProgramAccounts spl-token secondary-index functionality (#20993)
* Allow get_spl_token_X_filters to match on any encoding, and optimize earlier * Remove redundant optimize calls * Compress match statements * Add method docs, including note to use optimize_filters before spl-token checks * Add logs
This commit is contained in:
parent
35d71d94c1
commit
b2f6cfb9ff
|
@ -22,22 +22,11 @@ impl RpcFilterType {
|
||||||
MemcmpEncoding::Binary => {
|
MemcmpEncoding::Binary => {
|
||||||
use MemcmpEncodedBytes::*;
|
use MemcmpEncodedBytes::*;
|
||||||
match &compare.bytes {
|
match &compare.bytes {
|
||||||
Binary(bytes) if bytes.len() > MAX_DATA_BASE58_SIZE => {
|
// DEPRECATED
|
||||||
Err(RpcFilterError::Base58DataTooLarge)
|
|
||||||
}
|
|
||||||
Base58(bytes) if bytes.len() > MAX_DATA_BASE58_SIZE => {
|
|
||||||
Err(RpcFilterError::DataTooLarge)
|
|
||||||
}
|
|
||||||
Base64(bytes) if bytes.len() > MAX_DATA_BASE64_SIZE => {
|
|
||||||
Err(RpcFilterError::DataTooLarge)
|
|
||||||
}
|
|
||||||
Bytes(bytes) if bytes.len() > MAX_DATA_SIZE => {
|
|
||||||
Err(RpcFilterError::DataTooLarge)
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
|
||||||
}?;
|
|
||||||
match &compare.bytes {
|
|
||||||
Binary(bytes) => {
|
Binary(bytes) => {
|
||||||
|
if bytes.len() > MAX_DATA_BASE58_SIZE {
|
||||||
|
return Err(RpcFilterError::Base58DataTooLarge);
|
||||||
|
}
|
||||||
let bytes = bs58::decode(&bytes)
|
let bytes = bs58::decode(&bytes)
|
||||||
.into_vec()
|
.into_vec()
|
||||||
.map_err(RpcFilterError::DecodeError)?;
|
.map_err(RpcFilterError::DecodeError)?;
|
||||||
|
@ -48,6 +37,9 @@ impl RpcFilterType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Base58(bytes) => {
|
Base58(bytes) => {
|
||||||
|
if bytes.len() > MAX_DATA_BASE58_SIZE {
|
||||||
|
return Err(RpcFilterError::DataTooLarge);
|
||||||
|
}
|
||||||
let bytes = bs58::decode(&bytes).into_vec()?;
|
let bytes = bs58::decode(&bytes).into_vec()?;
|
||||||
if bytes.len() > MAX_DATA_SIZE {
|
if bytes.len() > MAX_DATA_SIZE {
|
||||||
Err(RpcFilterError::DataTooLarge)
|
Err(RpcFilterError::DataTooLarge)
|
||||||
|
@ -56,6 +48,9 @@ impl RpcFilterType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Base64(bytes) => {
|
Base64(bytes) => {
|
||||||
|
if bytes.len() > MAX_DATA_BASE64_SIZE {
|
||||||
|
return Err(RpcFilterError::DataTooLarge);
|
||||||
|
}
|
||||||
let bytes = base64::decode(&bytes)?;
|
let bytes = base64::decode(&bytes)?;
|
||||||
if bytes.len() > MAX_DATA_SIZE {
|
if bytes.len() > MAX_DATA_SIZE {
|
||||||
Err(RpcFilterError::DataTooLarge)
|
Err(RpcFilterError::DataTooLarge)
|
||||||
|
@ -63,7 +58,12 @@ impl RpcFilterType {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Bytes(_) => Ok(()),
|
Bytes(bytes) => {
|
||||||
|
if bytes.len() > MAX_DATA_SIZE {
|
||||||
|
return Err(RpcFilterError::DataTooLarge);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ use {
|
||||||
fee_calculator::FeeCalculator,
|
fee_calculator::FeeCalculator,
|
||||||
hash::Hash,
|
hash::Hash,
|
||||||
message::{Message, SanitizedMessage},
|
message::{Message, SanitizedMessage},
|
||||||
pubkey::Pubkey,
|
pubkey::{Pubkey, PUBKEY_BYTES},
|
||||||
signature::{Keypair, Signature, Signer},
|
signature::{Keypair, Signature, Signer},
|
||||||
stake::state::{StakeActivationStatus, StakeState},
|
stake::state::{StakeActivationStatus, StakeState},
|
||||||
stake_history::StakeHistory,
|
stake_history::StakeHistory,
|
||||||
|
@ -377,7 +377,7 @@ impl JsonRpcRequestProcessor {
|
||||||
&self,
|
&self,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
config: Option<RpcAccountInfoConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
filters: Vec<RpcFilterType>,
|
mut filters: Vec<RpcFilterType>,
|
||||||
with_context: bool,
|
with_context: bool,
|
||||||
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>> {
|
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>> {
|
||||||
let config = config.unwrap_or_default();
|
let config = config.unwrap_or_default();
|
||||||
|
@ -385,6 +385,7 @@ impl JsonRpcRequestProcessor {
|
||||||
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
|
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
|
||||||
let data_slice_config = config.data_slice;
|
let data_slice_config = config.data_slice;
|
||||||
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
|
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
|
||||||
|
optimize_filters(&mut filters);
|
||||||
let keyed_accounts = {
|
let keyed_accounts = {
|
||||||
if let Some(owner) = get_spl_token_owner_filter(program_id, &filters) {
|
if let Some(owner) = get_spl_token_owner_filter(program_id, &filters) {
|
||||||
self.get_filtered_spl_token_accounts_by_owner(&bank, &owner, filters)?
|
self.get_filtered_spl_token_accounts_by_owner(&bank, &owner, filters)?
|
||||||
|
@ -1872,7 +1873,6 @@ impl JsonRpcRequestProcessor {
|
||||||
index_key: owner_key.to_string(),
|
index_key: owner_key.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
optimize_filters(&mut filters);
|
|
||||||
Ok(bank
|
Ok(bank
|
||||||
.get_filtered_indexed_accounts(&IndexKey::SplTokenOwner(*owner_key), |account| {
|
.get_filtered_indexed_accounts(&IndexKey::SplTokenOwner(*owner_key), |account| {
|
||||||
account.owner() == &spl_token_id_v2_0()
|
account.owner() == &spl_token_id_v2_0()
|
||||||
|
@ -1921,7 +1921,6 @@ impl JsonRpcRequestProcessor {
|
||||||
index_key: mint_key.to_string(),
|
index_key: mint_key.to_string(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
optimize_filters(&mut filters);
|
|
||||||
Ok(bank
|
Ok(bank
|
||||||
.get_filtered_indexed_accounts(&IndexKey::SplTokenMint(*mint_key), |account| {
|
.get_filtered_indexed_accounts(&IndexKey::SplTokenMint(*mint_key), |account| {
|
||||||
account.owner() == &spl_token_id_v2_0()
|
account.owner() == &spl_token_id_v2_0()
|
||||||
|
@ -2151,58 +2150,86 @@ fn encode_account<T: ReadableAccount>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Analyze custom filters to determine if the result will be a subset of spl-token accounts by
|
||||||
|
/// owner.
|
||||||
|
/// NOTE: `optimize_filters()` should almost always be called before using this method because of
|
||||||
|
/// the strict match on `MemcmpEncodedBytes::Bytes`.
|
||||||
fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> {
|
fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> {
|
||||||
if program_id != &spl_token_id_v2_0() {
|
if program_id != &spl_token_id_v2_0() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut data_size_filter: Option<u64> = None;
|
let mut data_size_filter: Option<u64> = None;
|
||||||
let mut owner_key: Option<Pubkey> = None;
|
let mut owner_key: Option<Pubkey> = None;
|
||||||
|
let mut incorrect_owner_len: Option<usize> = None;
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
match filter {
|
match filter {
|
||||||
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
|
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
|
offset: SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bytes),
|
bytes: MemcmpEncodedBytes::Bytes(bytes),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if let Ok(key) = Pubkey::from_str(bytes) {
|
if bytes.len() == PUBKEY_BYTES {
|
||||||
owner_key = Some(key)
|
owner_key = Some(Pubkey::new(bytes));
|
||||||
|
} else {
|
||||||
|
incorrect_owner_len = Some(bytes.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if data_size_filter == Some(TokenAccount::get_packed_len() as u64) {
|
if data_size_filter == Some(TokenAccount::get_packed_len() as u64) {
|
||||||
|
if let Some(incorrect_owner_len) = incorrect_owner_len {
|
||||||
|
info!(
|
||||||
|
"Incorrect num bytes ({:?}) provided for spl_token_owner_filter",
|
||||||
|
incorrect_owner_len
|
||||||
|
);
|
||||||
|
}
|
||||||
owner_key
|
owner_key
|
||||||
} else {
|
} else {
|
||||||
|
debug!("spl_token program filters do not match by-owner index requisites");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Analyze custom filters to determine if the result will be a subset of spl-token accounts by
|
||||||
|
/// mint.
|
||||||
|
/// NOTE: `optimize_filters()` should almost always be called before using this method because of
|
||||||
|
/// the strict match on `MemcmpEncodedBytes::Bytes`.
|
||||||
fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> {
|
fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> {
|
||||||
if program_id != &spl_token_id_v2_0() {
|
if program_id != &spl_token_id_v2_0() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut data_size_filter: Option<u64> = None;
|
let mut data_size_filter: Option<u64> = None;
|
||||||
let mut mint: Option<Pubkey> = None;
|
let mut mint: Option<Pubkey> = None;
|
||||||
|
let mut incorrect_mint_len: Option<usize> = None;
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
match filter {
|
match filter {
|
||||||
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
|
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: SPL_TOKEN_ACCOUNT_MINT_OFFSET,
|
offset: SPL_TOKEN_ACCOUNT_MINT_OFFSET,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bytes),
|
bytes: MemcmpEncodedBytes::Bytes(bytes),
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
if let Ok(key) = Pubkey::from_str(bytes) {
|
if bytes.len() == PUBKEY_BYTES {
|
||||||
mint = Some(key)
|
mint = Some(Pubkey::new(bytes));
|
||||||
|
} else {
|
||||||
|
incorrect_mint_len = Some(bytes.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if data_size_filter == Some(TokenAccount::get_packed_len() as u64) {
|
if data_size_filter == Some(TokenAccount::get_packed_len() as u64) {
|
||||||
|
if let Some(incorrect_mint_len) = incorrect_mint_len {
|
||||||
|
info!(
|
||||||
|
"Incorrect num bytes ({:?}) provided for spl_token_mint_filter",
|
||||||
|
incorrect_mint_len
|
||||||
|
);
|
||||||
|
}
|
||||||
mint
|
mint
|
||||||
} else {
|
} else {
|
||||||
|
debug!("spl_token program filters do not match by-mint index requisites");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7673,7 +7700,7 @@ pub mod tests {
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: 32,
|
offset: 32,
|
||||||
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
|
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
||||||
encoding: None
|
encoding: None
|
||||||
}),
|
}),
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
|
@ -7689,7 +7716,7 @@ pub mod tests {
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
|
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
||||||
encoding: None
|
encoding: None
|
||||||
}),
|
}),
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
|
@ -7703,7 +7730,7 @@ pub mod tests {
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: 32,
|
offset: 32,
|
||||||
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
|
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
||||||
encoding: None
|
encoding: None
|
||||||
}),
|
}),
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
|
|
Loading…
Reference in New Issue