Restore getProgramAccounts spl-token secondary-index functionality (backport #20993) (#21004)

* 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

(cherry picked from commit b2f6cfb9ff)

# Conflicts:
#	rpc/src/rpc.rs

* Fix conflict

Co-authored-by: Tyera Eulberg <teulberg@gmail.com>
Co-authored-by: Tyera Eulberg <tyera@solana.com>
This commit is contained in:
mergify[bot]
2021-10-27 00:29:51 +00:00
committed by GitHub
parent e6bd6dd260
commit 35ab022dbe
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

@ -59,7 +59,7 @@ use {
epoch_schedule::EpochSchedule,
exit::Exit,
hash::Hash,
pubkey::Pubkey,
pubkey::{Pubkey, PUBKEY_BYTES},
sanitize::Sanitize,
signature::{Keypair, Signature, Signer},
stake::state::{StakeActivationStatus, StakeState},
@ -364,7 +364,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();
@ -372,6 +372,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)?
@ -1850,7 +1851,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()
@ -1899,7 +1899,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()
@ -2090,58 +2089,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
}
}
@ -7419,7 +7446,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)
@ -7435,7 +7462,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)
@ -7449,7 +7476,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)