diff --git a/client/src/rpc_filter.rs b/client/src/rpc_filter.rs index 416742d591..500f74f9c0 100644 --- a/client/src/rpc_filter.rs +++ b/client/src/rpc_filter.rs @@ -16,10 +16,15 @@ impl RpcFilterType { match encoding { MemcmpEncoding::Binary => { let MemcmpEncodedBytes::Binary(bytes) = &compare.bytes; - bs58::decode(&bytes) - .into_vec() - .map(|_| ()) - .map_err(|e| e.into()) + + if bytes.len() > 128 { + Err(RpcFilterError::Base58DataTooLarge) + } 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 { #[error("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)] @@ -140,4 +147,36 @@ mod tests { } .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) + ); + } } diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 61de0ff504..68bb3123c8 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -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_MULTIPLE_ACCOUNTS: usize = 100; 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 pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128; diff --git a/core/src/rpc.rs b/core/src/rpc.rs index cddb7a871d..dcbe620aeb 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -28,7 +28,7 @@ use solana_client::{ rpc_request::{ TokenAccountsFilter, DELINQUENT_VALIDATOR_SLOT_DISTANCE, MAX_GET_CONFIRMED_BLOCKS_RANGE, 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, }, rpc_response::Response as RpcResponse, @@ -2223,6 +2223,12 @@ impl RpcSol for RpcSolImpl { } else { (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 { verify_filter(filter)?; } diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 465b21711f..d083262894 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -207,7 +207,7 @@ Returns all information associated with the account of provided Pubkey fields: - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - `encoding: ` - 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+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 ``. @@ -1649,7 +1649,7 @@ Returns the account information for a list of Pubkeys - `` - (optional) Configuration object containing the following optional fields: - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - `encoding: ` - 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+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 ``. @@ -1796,7 +1796,7 @@ Returns all accounts owned by the provided program Pubkey - `` - (optional) Configuration object containing the following optional fields: - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - `encoding: ` - 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+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 ``. @@ -1806,7 +1806,7 @@ Returns all accounts owned by the provided program Pubkey ##### Filters: - `memcmp: ` - compares a provided series of bytes with program account data at a particular offset. Fields: - `offset: ` - offset into program account data to start comparison - - `bytes: ` - data to match, as base-58 encoded string + - `bytes: ` - data to match, as base-58 encoded string and limited to less than 129 bytes - `dataSize: ` - compares the program account data length with the provided data size