* Limit getProgramAccounts memcpy filter string to 128 bytes (cherry picked from commit65f1afe5e1) * Limit the number of getProgramAccounts filters (cherry picked from commit4b0114b991) Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
		| @@ -16,10 +16,15 @@ impl RpcFilterType { | |||||||
|                 match encoding { |                 match encoding { | ||||||
|                     MemcmpEncoding::Binary => { |                     MemcmpEncoding::Binary => { | ||||||
|                         let MemcmpEncodedBytes::Binary(bytes) = &compare.bytes; |                         let MemcmpEncodedBytes::Binary(bytes) = &compare.bytes; | ||||||
|                         bs58::decode(&bytes) |  | ||||||
|                             .into_vec() |                         if bytes.len() > 128 { | ||||||
|                             .map(|_| ()) |                             Err(RpcFilterError::Base58DataTooLarge) | ||||||
|                             .map_err(|e| e.into()) |                         } else { | ||||||
|  |                             bs58::decode(&bytes) | ||||||
|  |                                 .into_vec() | ||||||
|  |                                 .map(|_| ()) | ||||||
|  |                                 .map_err(|e| e.into()) | ||||||
|  |                         } | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @@ -27,10 +32,12 @@ impl RpcFilterType { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Error, Debug)] | #[derive(Error, PartialEq, Debug)] | ||||||
| pub enum RpcFilterError { | pub enum RpcFilterError { | ||||||
|     #[error("bs58 decode error")] |     #[error("bs58 decode error")] | ||||||
|     DecodeError(#[from] bs58::decode::Error), |     DecodeError(#[from] bs58::decode::Error), | ||||||
|  |     #[error("encoded binary (base 58) data should be less than 129 bytes")] | ||||||
|  |     Base58DataTooLarge, | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] | ||||||
| @@ -140,4 +147,36 @@ mod tests { | |||||||
|         } |         } | ||||||
|         .bytes_match(&data)); |         .bytes_match(&data)); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     #[test] | ||||||
|  |     fn test_verify_memcmp() { | ||||||
|  |         let base58_bytes = "\ | ||||||
|  |             1111111111111111111111111111111111111111111111111111111111111111\ | ||||||
|  |             1111111111111111111111111111111111111111111111111111111111111111"; | ||||||
|  |         assert_eq!(base58_bytes.len(), 128); | ||||||
|  |         assert_eq!( | ||||||
|  |             RpcFilterType::Memcmp(Memcmp { | ||||||
|  |                 offset: 0, | ||||||
|  |                 bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()), | ||||||
|  |                 encoding: None, | ||||||
|  |             }) | ||||||
|  |             .verify(), | ||||||
|  |             Ok(()) | ||||||
|  |         ); | ||||||
|  |  | ||||||
|  |         let base58_bytes = "\ | ||||||
|  |             1111111111111111111111111111111111111111111111111111111111111111\ | ||||||
|  |             1111111111111111111111111111111111111111111111111111111111111111\ | ||||||
|  |             1"; | ||||||
|  |         assert_eq!(base58_bytes.len(), 129); | ||||||
|  |         assert_eq!( | ||||||
|  |             RpcFilterType::Memcmp(Memcmp { | ||||||
|  |                 offset: 0, | ||||||
|  |                 bytes: MemcmpEncodedBytes::Binary(base58_bytes.to_string()), | ||||||
|  |                 encoding: None, | ||||||
|  |             }) | ||||||
|  |             .verify(), | ||||||
|  |             Err(RpcFilterError::Base58DataTooLarge) | ||||||
|  |         ); | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -127,6 +127,7 @@ pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000; | |||||||
| pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000; | pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000; | ||||||
| pub const MAX_MULTIPLE_ACCOUNTS: usize = 100; | pub const MAX_MULTIPLE_ACCOUNTS: usize = 100; | ||||||
| pub const NUM_LARGEST_ACCOUNTS: usize = 20; | pub const NUM_LARGEST_ACCOUNTS: usize = 20; | ||||||
|  | pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4; | ||||||
|  |  | ||||||
| // Validators that are this number of slots behind are considered delinquent | // Validators that are this number of slots behind are considered delinquent | ||||||
| pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128; | pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128; | ||||||
|   | |||||||
| @@ -28,7 +28,7 @@ use solana_client::{ | |||||||
|     rpc_request::{ |     rpc_request::{ | ||||||
|         TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE, |         TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE, | ||||||
|         MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT, |         MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT, | ||||||
|         MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE, |         MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE, MAX_GET_PROGRAM_ACCOUNT_FILTERS, | ||||||
|         MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, MAX_MULTIPLE_ACCOUNTS, NUM_LARGEST_ACCOUNTS, |         MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, MAX_MULTIPLE_ACCOUNTS, NUM_LARGEST_ACCOUNTS, | ||||||
|     }, |     }, | ||||||
|     rpc_response::Response as RpcResponse, |     rpc_response::Response as RpcResponse, | ||||||
| @@ -2223,6 +2223,12 @@ impl RpcSol for RpcSolImpl { | |||||||
|         } else { |         } else { | ||||||
|             (None, vec![]) |             (None, vec![]) | ||||||
|         }; |         }; | ||||||
|  |         if filters.len() > MAX_GET_PROGRAM_ACCOUNT_FILTERS { | ||||||
|  |             return Err(Error::invalid_params(format!( | ||||||
|  |                 "Too many filters provided; max {}", | ||||||
|  |                 MAX_GET_PROGRAM_ACCOUNT_FILTERS | ||||||
|  |             ))); | ||||||
|  |         } | ||||||
|         for filter in &filters { |         for filter in &filters { | ||||||
|             verify_filter(filter)?; |             verify_filter(filter)?; | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -207,7 +207,7 @@ Returns all information associated with the account of provided Pubkey | |||||||
| fields: | fields: | ||||||
|   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) |   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||||
|   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". |   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". | ||||||
|     "base58" is limited to Account data of less than 128 bytes. |     "base58" is limited to Account data of less than 129 bytes. | ||||||
|     "base64" will return base64 encoded data for Account data of any size. |     "base64" will return base64 encoded data for Account data of any size. | ||||||
|     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. |     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. | ||||||
|     "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>`. | ||||||
| @@ -1649,7 +1649,7 @@ Returns the account information for a list of Pubkeys | |||||||
| - `<object>` - (optional) Configuration object containing the following optional fields: | - `<object>` - (optional) Configuration object containing the following optional fields: | ||||||
|   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) |   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||||
|   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". |   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". | ||||||
|     "base58" is limited to Account data of less than 128 bytes. |     "base58" is limited to Account data of less than 129 bytes. | ||||||
|     "base64" will return base64 encoded data for Account data of any size. |     "base64" will return base64 encoded data for Account data of any size. | ||||||
|     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. |     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. | ||||||
|     "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>`. | ||||||
| @@ -1796,7 +1796,7 @@ Returns all accounts owned by the provided program Pubkey | |||||||
| - `<object>` - (optional) Configuration object containing the following optional fields: | - `<object>` - (optional) Configuration object containing the following optional fields: | ||||||
|   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) |   - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) | ||||||
|   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". |   - `encoding: <string>` - encoding for Account data, either "base58" (*slow*), "base64", "base64+zstd", or "jsonParsed". | ||||||
|     "base58" is limited to Account data of less than 128 bytes. |     "base58" is limited to Account data of less than 129 bytes. | ||||||
|     "base64" will return base64 encoded data for Account data of any size. |     "base64" will return base64 encoded data for Account data of any size. | ||||||
|     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. |     "base64+zstd" compresses the Account data using [Zstandard](https://facebook.github.io/zstd/) and base64-encodes the result. | ||||||
|     "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>`. | ||||||
| @@ -1806,7 +1806,7 @@ Returns all accounts owned by the provided program Pubkey | |||||||
| ##### 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 | ||||||
|   - `bytes: <string>` - data to match, as base-58 encoded string |   - `bytes: <string>` - data to match, as base-58 encoded string and limited to less than 129 bytes | ||||||
|  |  | ||||||
| - `dataSize: <u64>` - compares the program account data length with the provided data size | - `dataSize: <u64>` - compares the program account data length with the provided data size | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user