From 138cfd781bef400750d931b2db7db5439a710d38 Mon Sep 17 00:00:00 2001 From: jeffhelius <jeff@helius.xyz> Date: Tue, 31 Dec 2024 11:53:41 -0800 Subject: [PATCH 1/2] add filter that returns an error for the rpc is the filter is malformed --- rpc/src/rpc.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 29cce7b1a74680..fdebbf7e8bf3c6 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -500,6 +500,10 @@ impl JsonRpcRequestProcessor { min_context_slot, })?; let encoding = encoding.unwrap_or(UiAccountEncoding::Binary); + + // Early filter verification + verify_spl_token_owner_filters(program_id, &filters)?; + optimize_filters(&mut filters); let keyed_accounts = { if let Some(owner) = get_spl_token_owner_filter(program_id, &filters) { @@ -2394,6 +2398,46 @@ fn encode_account<T: ReadableAccount>( } } +fn verify_spl_token_owner_filters( + program_id: &Pubkey, + filters: &[RpcFilterType], +) -> std::result::Result<(), Error> { + if !is_known_spl_token_id(program_id) { + return Ok(()); // Not an SPL Token program, so skip + } + + let account_packed_len = TokenAccount::get_packed_len(); + for filter in filters { + match filter { + RpcFilterType::DataSize(size) => { + // Validate the DataSize if you want to require a certain size here + if *size != account_packed_len as u64 { + return Err(Error::invalid_params(format!( + "Invalid SPL token data size filter. Expected {}, got {}", + account_packed_len, size + ))); + } + } + RpcFilterType::Memcmp(memcmp) => { + let offset = memcmp.offset(); + if let Some(bytes) = memcmp.raw_bytes_as_ref() { + // If the filter references the owner offset, ensure it’s 32 bytes long + if offset == SPL_TOKEN_ACCOUNT_OWNER_OFFSET && bytes.len() != PUBKEY_BYTES { + return Err(Error::invalid_params(format!( + "Incorrect byte length {} for SPL token owner filter, expected {}", + bytes.len(), + PUBKEY_BYTES + ))); + } + } + } + RpcFilterType::TokenAccountState => (), + } + } + + Ok(()) +} + /// 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 From d7bc400a0d420a2df51832f3aed701d82cedb8e9 Mon Sep 17 00:00:00 2001 From: jeffhelius <jeff@helius.xyz> Date: Thu, 2 Jan 2025 04:08:08 -0800 Subject: [PATCH 2/2] address nicks comments on pr --- rpc/src/rpc.rs | 95 ++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 69 deletions(-) diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index fdebbf7e8bf3c6..b8038f70e1f936 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -501,12 +501,9 @@ impl JsonRpcRequestProcessor { })?; let encoding = encoding.unwrap_or(UiAccountEncoding::Binary); - // Early filter verification - verify_spl_token_owner_filters(program_id, &filters)?; - optimize_filters(&mut filters); 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, program_id, @@ -514,7 +511,7 @@ impl JsonRpcRequestProcessor { filters, sort_results, )? - } else if let Some(mint) = get_spl_token_mint_filter(program_id, &filters) { + } else if let Some(mint) = get_spl_token_mint_filter(program_id, &filters)? { self.get_filtered_spl_token_accounts_by_mint( &bank, program_id, @@ -2398,58 +2395,20 @@ fn encode_account<T: ReadableAccount>( } } -fn verify_spl_token_owner_filters( - program_id: &Pubkey, - filters: &[RpcFilterType], -) -> std::result::Result<(), Error> { - if !is_known_spl_token_id(program_id) { - return Ok(()); // Not an SPL Token program, so skip - } - - let account_packed_len = TokenAccount::get_packed_len(); - for filter in filters { - match filter { - RpcFilterType::DataSize(size) => { - // Validate the DataSize if you want to require a certain size here - if *size != account_packed_len as u64 { - return Err(Error::invalid_params(format!( - "Invalid SPL token data size filter. Expected {}, got {}", - account_packed_len, size - ))); - } - } - RpcFilterType::Memcmp(memcmp) => { - let offset = memcmp.offset(); - if let Some(bytes) = memcmp.raw_bytes_as_ref() { - // If the filter references the owner offset, ensure it’s 32 bytes long - if offset == SPL_TOKEN_ACCOUNT_OWNER_OFFSET && bytes.len() != PUBKEY_BYTES { - return Err(Error::invalid_params(format!( - "Incorrect byte length {} for SPL token owner filter, expected {}", - bytes.len(), - PUBKEY_BYTES - ))); - } - } - } - RpcFilterType::TokenAccountState => (), - } - } - - Ok(()) -} - /// 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 requirement that `Memcmp::raw_bytes_as_ref().is_some()`. -fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> { +fn get_spl_token_owner_filter( + program_id: &Pubkey, + filters: &[RpcFilterType], +) -> Result<Option<Pubkey>> { if !is_known_spl_token_id(program_id) { - return None; + return Ok(None); } let mut data_size_filter: Option<u64> = None; let mut memcmp_filter: Option<&[u8]> = None; let mut owner_key: Option<Pubkey> = None; - let mut incorrect_owner_len: Option<usize> = None; let mut token_account_state_filter = false; let account_packed_len = TokenAccount::get_packed_len(); for filter in filters { @@ -2464,7 +2423,11 @@ fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> if bytes.len() == PUBKEY_BYTES { owner_key = Pubkey::try_from(bytes).ok(); } else { - incorrect_owner_len = Some(bytes.len()); + return Err(Error::invalid_params(format!( + "Incorrect byte length {} for SPL token owner filter, expected {}", + bytes.len(), + PUBKEY_BYTES + ))); } } } @@ -2476,16 +2439,10 @@ fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> || memcmp_filter == Some(&[ACCOUNTTYPE_ACCOUNT]) || token_account_state_filter { - if let Some(incorrect_owner_len) = incorrect_owner_len { - info!( - "Incorrect num bytes ({:?}) provided for spl_token_owner_filter", - incorrect_owner_len - ); - } - owner_key + Ok(owner_key) } else { debug!("spl_token program filters do not match by-owner index requisites"); - None + Ok(None) } } @@ -2493,14 +2450,16 @@ fn get_spl_token_owner_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> /// mint. /// NOTE: `optimize_filters()` should almost always be called before using this method because of /// the requirement that `Memcmp::raw_bytes_as_ref().is_some()`. -fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> Option<Pubkey> { +fn get_spl_token_mint_filter( + program_id: &Pubkey, + filters: &[RpcFilterType], +) -> Result<Option<Pubkey>> { if !is_known_spl_token_id(program_id) { - return None; + return Ok(None); } let mut data_size_filter: Option<u64> = None; let mut memcmp_filter: Option<&[u8]> = None; let mut mint: Option<Pubkey> = None; - let mut incorrect_mint_len: Option<usize> = None; let mut token_account_state_filter = false; let account_packed_len = TokenAccount::get_packed_len(); for filter in filters { @@ -2515,7 +2474,11 @@ fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> if bytes.len() == PUBKEY_BYTES { mint = Pubkey::try_from(bytes).ok(); } else { - incorrect_mint_len = Some(bytes.len()); + return Err(Error::invalid_params(format!( + "Incorrect byte length {} for SPL token mint filter, expected {}", + bytes.len(), + PUBKEY_BYTES + ))); } } } @@ -2527,16 +2490,10 @@ fn get_spl_token_mint_filter(program_id: &Pubkey, filters: &[RpcFilterType]) -> || memcmp_filter == Some(&[ACCOUNTTYPE_ACCOUNT]) || token_account_state_filter { - if let Some(incorrect_mint_len) = incorrect_mint_len { - info!( - "Incorrect num bytes ({:?}) provided for spl_token_mint_filter", - incorrect_mint_len - ); - } - mint + Ok(mint) } else { debug!("spl_token program filters do not match by-mint index requisites"); - None + Ok(None) } }