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:
Tyera Eulberg 2021-10-26 15:44:24 -06:00 committed by GitHub
parent 35d71d94c1
commit b2f6cfb9ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 29 deletions

View File

@ -22,22 +22,11 @@ impl RpcFilterType {
MemcmpEncoding::Binary => {
use MemcmpEncodedBytes::*;
match &compare.bytes {
Binary(bytes) if bytes.len() > MAX_DATA_BASE58_SIZE => {
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 {
// DEPRECATED
Binary(bytes) => {
if bytes.len() > MAX_DATA_BASE58_SIZE {
return Err(RpcFilterError::Base58DataTooLarge);
}
let bytes = bs58::decode(&bytes)
.into_vec()
.map_err(RpcFilterError::DecodeError)?;
@ -48,6 +37,9 @@ impl RpcFilterType {
}
}
Base58(bytes) => {
if bytes.len() > MAX_DATA_BASE58_SIZE {
return Err(RpcFilterError::DataTooLarge);
}
let bytes = bs58::decode(&bytes).into_vec()?;
if bytes.len() > MAX_DATA_SIZE {
Err(RpcFilterError::DataTooLarge)
@ -56,6 +48,9 @@ impl RpcFilterType {
}
}
Base64(bytes) => {
if bytes.len() > MAX_DATA_BASE64_SIZE {
return Err(RpcFilterError::DataTooLarge);
}
let bytes = base64::decode(&bytes)?;
if bytes.len() > MAX_DATA_SIZE {
Err(RpcFilterError::DataTooLarge)
@ -63,7 +58,12 @@ impl RpcFilterType {
Ok(())
}
}
Bytes(_) => Ok(()),
Bytes(bytes) => {
if bytes.len() > MAX_DATA_SIZE {
return Err(RpcFilterError::DataTooLarge);
}
Ok(())
}
}
}
}

View File

@ -60,7 +60,7 @@ use {
fee_calculator::FeeCalculator,
hash::Hash,
message::{Message, SanitizedMessage},
pubkey::Pubkey,
pubkey::{Pubkey, PUBKEY_BYTES},
signature::{Keypair, Signature, Signer},
stake::state::{StakeActivationStatus, StakeState},
stake_history::StakeHistory,
@ -377,7 +377,7 @@ impl JsonRpcRequestProcessor {
&self,
program_id: &Pubkey,
config: Option<RpcAccountInfoConfig>,
filters: Vec<RpcFilterType>,
mut filters: Vec<RpcFilterType>,
with_context: bool,
) -> Result<OptionalContext<Vec<RpcKeyedAccount>>> {
let config = config.unwrap_or_default();
@ -385,6 +385,7 @@ impl JsonRpcRequestProcessor {
let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary);
let data_slice_config = config.data_slice;
check_slice_and_encoding(&encoding, data_slice_config.is_some())?;
optimize_filters(&mut filters);
let keyed_accounts = {
if let Some(owner) = get_spl_token_owner_filter(program_id, &filters) {
self.get_filtered_spl_token_accounts_by_owner(&bank, &owner, filters)?
@ -1872,7 +1873,6 @@ impl JsonRpcRequestProcessor {
index_key: owner_key.to_string(),
});
}
optimize_filters(&mut filters);
Ok(bank
.get_filtered_indexed_accounts(&IndexKey::SplTokenOwner(*owner_key), |account| {
account.owner() == &spl_token_id_v2_0()
@ -1921,7 +1921,6 @@ impl JsonRpcRequestProcessor {
index_key: mint_key.to_string(),
});
}
optimize_filters(&mut filters);
Ok(bank
.get_filtered_indexed_accounts(&IndexKey::SplTokenMint(*mint_key), |account| {
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> {
if program_id != &spl_token_id_v2_0() {
return None;
}
let mut data_size_filter: Option<u64> = None;
let mut owner_key: Option<Pubkey> = None;
let mut incorrect_owner_len: Option<usize> = None;
for filter in filters {
match filter {
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
RpcFilterType::Memcmp(Memcmp {
offset: SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
bytes: MemcmpEncodedBytes::Base58(bytes),
bytes: MemcmpEncodedBytes::Bytes(bytes),
..
}) => {
if let Ok(key) = Pubkey::from_str(bytes) {
owner_key = Some(key)
if bytes.len() == PUBKEY_BYTES {
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 let Some(incorrect_owner_len) = incorrect_owner_len {
info!(
"Incorrect num bytes ({:?}) provided for spl_token_owner_filter",
incorrect_owner_len
);
}
owner_key
} else {
debug!("spl_token program filters do not match by-owner index requisites");
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> {
if program_id != &spl_token_id_v2_0() {
return None;
}
let mut data_size_filter: Option<u64> = None;
let mut mint: Option<Pubkey> = None;
let mut incorrect_mint_len: Option<usize> = None;
for filter in filters {
match filter {
RpcFilterType::DataSize(size) => data_size_filter = Some(*size),
RpcFilterType::Memcmp(Memcmp {
offset: SPL_TOKEN_ACCOUNT_MINT_OFFSET,
bytes: MemcmpEncodedBytes::Base58(bytes),
bytes: MemcmpEncodedBytes::Bytes(bytes),
..
}) => {
if let Ok(key) = Pubkey::from_str(bytes) {
mint = Some(key)
if bytes.len() == PUBKEY_BYTES {
mint = Some(Pubkey::new(bytes));
} else {
incorrect_mint_len = Some(bytes.len());
}
}
_ => {}
}
}
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
} else {
debug!("spl_token program filters do not match by-mint index requisites");
None
}
}
@ -7673,7 +7700,7 @@ pub mod tests {
&[
RpcFilterType::Memcmp(Memcmp {
offset: 32,
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
encoding: None
}),
RpcFilterType::DataSize(165)
@ -7689,7 +7716,7 @@ pub mod tests {
&[
RpcFilterType::Memcmp(Memcmp {
offset: 0,
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
encoding: None
}),
RpcFilterType::DataSize(165)
@ -7703,7 +7730,7 @@ pub mod tests {
&[
RpcFilterType::Memcmp(Memcmp {
offset: 32,
bytes: MemcmpEncodedBytes::Base58(owner.to_string()),
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
encoding: None
}),
RpcFilterType::DataSize(165)