Enable base58 and base64 encoding parameters for Memcmp filters (#26437)
* Minor refactoring of client RpcProgramAccountsConfig handling * Enable explicit base58/base64 encoding of Memcmp filters, including client backward compatibility with nodes on old software * Deprecate Memcmp::Encoding * Remove deprecation warnings in rpc * Remove deprecation warnings in cli * Update docs * Make variants self-documenting
This commit is contained in:
parent
e2e3c31250
commit
f2abbcaf9c
|
@ -1776,32 +1776,24 @@ pub fn process_show_stakes(
|
||||||
if vote_account_pubkeys.len() == 1 {
|
if vote_account_pubkeys.len() == 1 {
|
||||||
program_accounts_config.filters = Some(vec![
|
program_accounts_config.filters = Some(vec![
|
||||||
// Filter by `StakeState::Stake(_, _)`
|
// Filter by `StakeState::Stake(_, _)`
|
||||||
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
|
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp::new_base58_encoded(
|
||||||
offset: 0,
|
0,
|
||||||
bytes: rpc_filter::MemcmpEncodedBytes::Base58(
|
&[2, 0, 0, 0],
|
||||||
bs58::encode([2, 0, 0, 0]).into_string(),
|
)),
|
||||||
),
|
|
||||||
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
|
|
||||||
}),
|
|
||||||
// Filter by `Delegation::voter_pubkey`, which begins at byte offset 124
|
// Filter by `Delegation::voter_pubkey`, which begins at byte offset 124
|
||||||
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
|
rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp::new_base58_encoded(
|
||||||
offset: 124,
|
124,
|
||||||
bytes: rpc_filter::MemcmpEncodedBytes::Base58(
|
vote_account_pubkeys[0].as_ref(),
|
||||||
vote_account_pubkeys[0].to_string(),
|
)),
|
||||||
),
|
|
||||||
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
|
|
||||||
}),
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(withdraw_authority_pubkey) = withdraw_authority_pubkey {
|
if let Some(withdraw_authority_pubkey) = withdraw_authority_pubkey {
|
||||||
// withdrawer filter
|
// withdrawer filter
|
||||||
let withdrawer_filter = rpc_filter::RpcFilterType::Memcmp(rpc_filter::Memcmp {
|
let withdrawer_filter = rpc_filter::RpcFilterType::Memcmp(
|
||||||
offset: 44,
|
rpc_filter::Memcmp::new_base58_encoded(44, withdraw_authority_pubkey.as_ref()),
|
||||||
bytes: rpc_filter::MemcmpEncodedBytes::Base58(withdraw_authority_pubkey.to_string()),
|
);
|
||||||
encoding: Some(rpc_filter::MemcmpEncoding::Binary),
|
|
||||||
});
|
|
||||||
|
|
||||||
let filters = program_accounts_config.filters.get_or_insert(vec![]);
|
let filters = program_accounts_config.filters.get_or_insert(vec![]);
|
||||||
filters.push(withdrawer_filter);
|
filters.push(withdrawer_filter);
|
||||||
|
|
|
@ -22,7 +22,7 @@ use {
|
||||||
connection_cache::ConnectionCache,
|
connection_cache::ConnectionCache,
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig},
|
rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig},
|
||||||
rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType},
|
rpc_filter::{Memcmp, RpcFilterType},
|
||||||
tpu_client::{TpuClient, TpuClientConfig},
|
tpu_client::{TpuClient, TpuClientConfig},
|
||||||
},
|
},
|
||||||
solana_program_runtime::invoke_context::InvokeContext,
|
solana_program_runtime::invoke_context::InvokeContext,
|
||||||
|
@ -1203,24 +1203,19 @@ fn get_buffers(
|
||||||
authority_pubkey: Option<Pubkey>,
|
authority_pubkey: Option<Pubkey>,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
) -> Result<CliUpgradeableBuffers, Box<dyn std::error::Error>> {
|
) -> Result<CliUpgradeableBuffers, Box<dyn std::error::Error>> {
|
||||||
let mut filters = vec![RpcFilterType::Memcmp(Memcmp {
|
let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
offset: 0,
|
0,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![1, 0, 0, 0]).into_string()),
|
&[1, 0, 0, 0],
|
||||||
encoding: None,
|
))];
|
||||||
})];
|
|
||||||
if let Some(authority_pubkey) = authority_pubkey {
|
if let Some(authority_pubkey) = authority_pubkey {
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
offset: ACCOUNT_TYPE_SIZE,
|
ACCOUNT_TYPE_SIZE,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![1]).into_string()),
|
&[1],
|
||||||
encoding: None,
|
)));
|
||||||
}));
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
ACCOUNT_TYPE_SIZE + OPTION_SIZE,
|
||||||
offset: ACCOUNT_TYPE_SIZE + OPTION_SIZE,
|
authority_pubkey.as_ref(),
|
||||||
bytes: MemcmpEncodedBytes::Base58(
|
)));
|
||||||
bs58::encode(authority_pubkey.as_ref()).into_string(),
|
|
||||||
),
|
|
||||||
encoding: None,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let results = get_accounts_with_filter(
|
let results = get_accounts_with_filter(
|
||||||
|
@ -1256,24 +1251,19 @@ fn get_programs(
|
||||||
authority_pubkey: Option<Pubkey>,
|
authority_pubkey: Option<Pubkey>,
|
||||||
use_lamports_unit: bool,
|
use_lamports_unit: bool,
|
||||||
) -> Result<CliUpgradeablePrograms, Box<dyn std::error::Error>> {
|
) -> Result<CliUpgradeablePrograms, Box<dyn std::error::Error>> {
|
||||||
let mut filters = vec![RpcFilterType::Memcmp(Memcmp {
|
let mut filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
offset: 0,
|
0,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![3, 0, 0, 0]).into_string()),
|
&[3, 0, 0, 0],
|
||||||
encoding: None,
|
))];
|
||||||
})];
|
|
||||||
if let Some(authority_pubkey) = authority_pubkey {
|
if let Some(authority_pubkey) = authority_pubkey {
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
offset: ACCOUNT_TYPE_SIZE + SLOT_SIZE,
|
ACCOUNT_TYPE_SIZE + SLOT_SIZE,
|
||||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![1]).into_string()),
|
&[1],
|
||||||
encoding: None,
|
)));
|
||||||
}));
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_base58_encoded(
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE,
|
||||||
offset: ACCOUNT_TYPE_SIZE + SLOT_SIZE + OPTION_SIZE,
|
authority_pubkey.as_ref(),
|
||||||
bytes: MemcmpEncodedBytes::Base58(
|
)));
|
||||||
bs58::encode(authority_pubkey.as_ref()).into_string(),
|
|
||||||
),
|
|
||||||
encoding: None,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let results = get_accounts_with_filter(
|
let results = get_accounts_with_filter(
|
||||||
|
@ -1291,11 +1281,7 @@ fn get_programs(
|
||||||
{
|
{
|
||||||
let mut bytes = vec![2, 0, 0, 0];
|
let mut bytes = vec![2, 0, 0, 0];
|
||||||
bytes.extend_from_slice(programdata_address.as_ref());
|
bytes.extend_from_slice(programdata_address.as_ref());
|
||||||
let filters = vec![RpcFilterType::Memcmp(Memcmp {
|
let filters = vec![RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &bytes))];
|
||||||
offset: 0,
|
|
||||||
bytes: MemcmpEncodedBytes::Base58(bs58::encode(bytes).into_string()),
|
|
||||||
encoding: None,
|
|
||||||
})];
|
|
||||||
|
|
||||||
let results = get_accounts_with_filter(rpc_client, filters, 0)?;
|
let results = get_accounts_with_filter(rpc_client, filters, 0)?;
|
||||||
if results.len() != 1 {
|
if results.len() != 1 {
|
||||||
|
|
|
@ -19,6 +19,7 @@ use {
|
||||||
mock_sender::MockSender,
|
mock_sender::MockSender,
|
||||||
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig},
|
rpc_client::{GetConfirmedSignaturesForAddress2Config, RpcClientConfig},
|
||||||
rpc_config::{RpcAccountInfoConfig, *},
|
rpc_config::{RpcAccountInfoConfig, *},
|
||||||
|
rpc_filter::{MemcmpEncodedBytes, RpcFilterType},
|
||||||
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
|
||||||
rpc_response::*,
|
rpc_response::*,
|
||||||
rpc_sender::*,
|
rpc_sender::*,
|
||||||
|
@ -580,6 +581,33 @@ impl RpcClient {
|
||||||
Ok(request)
|
Ok(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
async fn maybe_map_filters(
|
||||||
|
&self,
|
||||||
|
mut filters: Vec<RpcFilterType>,
|
||||||
|
) -> Result<Vec<RpcFilterType>, RpcError> {
|
||||||
|
let node_version = self.get_node_version().await?;
|
||||||
|
if node_version < semver::Version::new(1, 11, 2) {
|
||||||
|
for filter in filters.iter_mut() {
|
||||||
|
if let RpcFilterType::Memcmp(memcmp) = filter {
|
||||||
|
match &memcmp.bytes {
|
||||||
|
MemcmpEncodedBytes::Base58(string) => {
|
||||||
|
memcmp.bytes = MemcmpEncodedBytes::Binary(string.clone());
|
||||||
|
}
|
||||||
|
MemcmpEncodedBytes::Base64(_) => {
|
||||||
|
return Err(RpcError::RpcRequestError(format!(
|
||||||
|
"RPC node on old version {} does not support base64 encoding for memcmp filters",
|
||||||
|
node_version
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(filters)
|
||||||
|
}
|
||||||
|
|
||||||
/// Submit a transaction and wait for confirmation.
|
/// Submit a transaction and wait for confirmation.
|
||||||
///
|
///
|
||||||
/// Once this function returns successfully, the given transaction is
|
/// Once this function returns successfully, the given transaction is
|
||||||
|
@ -4490,21 +4518,17 @@ impl RpcClient {
|
||||||
pub async fn get_program_accounts_with_config(
|
pub async fn get_program_accounts_with_config(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
config: RpcProgramAccountsConfig,
|
mut config: RpcProgramAccountsConfig,
|
||||||
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
) -> ClientResult<Vec<(Pubkey, Account)>> {
|
||||||
let commitment = config
|
let commitment = config
|
||||||
.account_config
|
.account_config
|
||||||
.commitment
|
.commitment
|
||||||
.unwrap_or_else(|| self.commitment());
|
.unwrap_or_else(|| self.commitment());
|
||||||
let commitment = self.maybe_map_commitment(commitment).await?;
|
let commitment = self.maybe_map_commitment(commitment).await?;
|
||||||
let account_config = RpcAccountInfoConfig {
|
config.account_config.commitment = Some(commitment);
|
||||||
commitment: Some(commitment),
|
if let Some(filters) = config.filters {
|
||||||
..config.account_config
|
config.filters = Some(self.maybe_map_filters(filters).await?);
|
||||||
};
|
}
|
||||||
let config = RpcProgramAccountsConfig {
|
|
||||||
account_config,
|
|
||||||
..config
|
|
||||||
};
|
|
||||||
let accounts: Vec<RpcKeyedAccount> = self
|
let accounts: Vec<RpcKeyedAccount> = self
|
||||||
.send(
|
.send(
|
||||||
RpcRequest::GetProgramAccounts,
|
RpcRequest::GetProgramAccounts,
|
||||||
|
|
|
@ -129,16 +129,37 @@ pub enum MemcmpEncodedBytes {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(into = "RpcMemcmp", from = "RpcMemcmp")]
|
||||||
pub struct Memcmp {
|
pub struct Memcmp {
|
||||||
/// Data offset to begin match
|
/// Data offset to begin match
|
||||||
pub offset: usize,
|
pub offset: usize,
|
||||||
/// Bytes, encoded with specified encoding, or default Binary
|
/// Bytes, encoded with specified encoding, or default Binary
|
||||||
pub bytes: MemcmpEncodedBytes,
|
pub bytes: MemcmpEncodedBytes,
|
||||||
/// Optional encoding specification
|
/// Optional encoding specification
|
||||||
|
#[deprecated(
|
||||||
|
since = "1.11.2",
|
||||||
|
note = "Field has no server-side effect. Specify encoding with `MemcmpEncodedBytes` variant instead."
|
||||||
|
)]
|
||||||
pub encoding: Option<MemcmpEncoding>,
|
pub encoding: Option<MemcmpEncoding>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Memcmp {
|
impl Memcmp {
|
||||||
|
pub fn new_raw_bytes(offset: usize, bytes: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
offset,
|
||||||
|
bytes: MemcmpEncodedBytes::Bytes(bytes),
|
||||||
|
encoding: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_base58_encoded(offset: usize, bytes: &[u8]) -> Self {
|
||||||
|
Self {
|
||||||
|
offset,
|
||||||
|
bytes: MemcmpEncodedBytes::Base58(bs58::encode(bytes).into_string()),
|
||||||
|
encoding: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bytes(&self) -> Option<Cow<Vec<u8>>> {
|
pub fn bytes(&self) -> Option<Cow<Vec<u8>>> {
|
||||||
use MemcmpEncodedBytes::*;
|
use MemcmpEncodedBytes::*;
|
||||||
match &self.bytes {
|
match &self.bytes {
|
||||||
|
@ -164,6 +185,80 @@ impl Memcmp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal struct to hold Memcmp filter data as either encoded String or raw Bytes
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
enum DataType {
|
||||||
|
Encoded(String),
|
||||||
|
Raw(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal struct used to specify explicit Base58 and Base64 encoding
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
enum RpcMemcmpEncoding {
|
||||||
|
Base58,
|
||||||
|
Base64,
|
||||||
|
// This variant exists only to preserve backward compatibility with generic `Memcmp` serde
|
||||||
|
#[serde(other)]
|
||||||
|
Binary,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Internal struct to enable Memcmp filters with explicit Base58 and Base64 encoding. The From
|
||||||
|
// implementations emulate `#[serde(tag = "encoding", content = "bytes")]` for
|
||||||
|
// `MemcmpEncodedBytes`. On the next major version, all these internal elements should be removed
|
||||||
|
// and replaced with adjacent tagging of `MemcmpEncodedBytes`.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
struct RpcMemcmp {
|
||||||
|
offset: usize,
|
||||||
|
bytes: DataType,
|
||||||
|
encoding: Option<RpcMemcmpEncoding>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Memcmp> for RpcMemcmp {
|
||||||
|
fn from(memcmp: Memcmp) -> RpcMemcmp {
|
||||||
|
let (bytes, encoding) = match memcmp.bytes {
|
||||||
|
MemcmpEncodedBytes::Binary(string) => {
|
||||||
|
(DataType::Encoded(string), Some(RpcMemcmpEncoding::Binary))
|
||||||
|
}
|
||||||
|
MemcmpEncodedBytes::Base58(string) => {
|
||||||
|
(DataType::Encoded(string), Some(RpcMemcmpEncoding::Base58))
|
||||||
|
}
|
||||||
|
MemcmpEncodedBytes::Base64(string) => {
|
||||||
|
(DataType::Encoded(string), Some(RpcMemcmpEncoding::Base64))
|
||||||
|
}
|
||||||
|
MemcmpEncodedBytes::Bytes(vector) => (DataType::Raw(vector), None),
|
||||||
|
};
|
||||||
|
RpcMemcmp {
|
||||||
|
offset: memcmp.offset,
|
||||||
|
bytes,
|
||||||
|
encoding,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<RpcMemcmp> for Memcmp {
|
||||||
|
fn from(memcmp: RpcMemcmp) -> Memcmp {
|
||||||
|
let encoding = memcmp.encoding.unwrap_or(RpcMemcmpEncoding::Binary);
|
||||||
|
let bytes = match (encoding, memcmp.bytes) {
|
||||||
|
(RpcMemcmpEncoding::Binary, DataType::Encoded(string))
|
||||||
|
| (RpcMemcmpEncoding::Base58, DataType::Encoded(string)) => {
|
||||||
|
MemcmpEncodedBytes::Base58(string)
|
||||||
|
}
|
||||||
|
(RpcMemcmpEncoding::Binary, DataType::Raw(vector)) => MemcmpEncodedBytes::Bytes(vector),
|
||||||
|
(RpcMemcmpEncoding::Base64, DataType::Encoded(string)) => {
|
||||||
|
MemcmpEncodedBytes::Base64(string)
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Memcmp {
|
||||||
|
offset: memcmp.offset,
|
||||||
|
bytes,
|
||||||
|
encoding: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -1906,7 +1906,9 @@ Returns all accounts owned by the provided program Pubkey
|
||||||
- `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 and limited to less than 129 bytes
|
- `bytes: <string>` - data to match, as encoded string
|
||||||
|
- `encoding: <string>` - encoding for filter `bytes` data, either "base58" or "base64". Data is limited in size to 128 or fewer decoded bytes.
|
||||||
|
**NEW: This field, and base64 support generally, is only available in solana-core v1.11.2 or newer. Please omit when querying nodes on earlier versions**
|
||||||
|
|
||||||
- `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
|
||||||
|
|
||||||
|
|
164
rpc/src/rpc.rs
164
rpc/src/rpc.rs
|
@ -1899,11 +1899,10 @@ impl JsonRpcRequestProcessor {
|
||||||
let mut filters = vec![];
|
let mut filters = vec![];
|
||||||
if let Some(mint) = mint {
|
if let Some(mint) = mint {
|
||||||
// Optional filter on Mint address
|
// Optional filter on Mint address
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
offset: 0,
|
0,
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().into()),
|
mint.to_bytes().into(),
|
||||||
encoding: None,
|
)));
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let keyed_accounts = self.get_filtered_spl_token_accounts_by_owner(
|
let keyed_accounts = self.get_filtered_spl_token_accounts_by_owner(
|
||||||
|
@ -1950,17 +1949,12 @@ impl JsonRpcRequestProcessor {
|
||||||
|
|
||||||
let mut filters = vec![
|
let mut filters = vec![
|
||||||
// Filter on Delegate is_some()
|
// Filter on Delegate is_some()
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
offset: 72,
|
72,
|
||||||
bytes: MemcmpEncodedBytes::Bytes(bincode::serialize(&1u32).unwrap()),
|
bincode::serialize(&1u32).unwrap(),
|
||||||
encoding: None,
|
)),
|
||||||
}),
|
|
||||||
// Filter on Delegate address
|
// Filter on Delegate address
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(76, delegate.to_bytes().into())),
|
||||||
offset: 76,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(delegate.to_bytes().into()),
|
|
||||||
encoding: None,
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
// Optional filter on Mint address, uses mint account index for scan
|
// Optional filter on Mint address, uses mint account index for scan
|
||||||
let keyed_accounts = if let Some(mint) = mint {
|
let keyed_accounts = if let Some(mint) = mint {
|
||||||
|
@ -2052,11 +2046,10 @@ impl JsonRpcRequestProcessor {
|
||||||
// Filter on Token Account state
|
// Filter on Token Account state
|
||||||
filters.push(RpcFilterType::TokenAccountState);
|
filters.push(RpcFilterType::TokenAccountState);
|
||||||
// Filter on Owner address
|
// Filter on Owner address
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
offset: SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
|
SPL_TOKEN_ACCOUNT_OWNER_OFFSET,
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner_key.to_bytes().into()),
|
owner_key.to_bytes().into(),
|
||||||
encoding: None,
|
)));
|
||||||
}));
|
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.config
|
.config
|
||||||
|
@ -2104,11 +2097,10 @@ impl JsonRpcRequestProcessor {
|
||||||
// Filter on Token Account state
|
// Filter on Token Account state
|
||||||
filters.push(RpcFilterType::TokenAccountState);
|
filters.push(RpcFilterType::TokenAccountState);
|
||||||
// Filter on Mint address
|
// Filter on Mint address
|
||||||
filters.push(RpcFilterType::Memcmp(Memcmp {
|
filters.push(RpcFilterType::Memcmp(Memcmp::new_raw_bytes(
|
||||||
offset: SPL_TOKEN_ACCOUNT_MINT_OFFSET,
|
SPL_TOKEN_ACCOUNT_MINT_OFFSET,
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint_key.to_bytes().into()),
|
mint_key.to_bytes().into(),
|
||||||
encoding: None,
|
)));
|
||||||
}));
|
|
||||||
if self
|
if self
|
||||||
.config
|
.config
|
||||||
.account_indexes
|
.account_indexes
|
||||||
|
@ -6402,6 +6394,7 @@ pub mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_rpc_verify_filter() {
|
fn test_rpc_verify_filter() {
|
||||||
|
#[allow(deprecated)]
|
||||||
let filter = RpcFilterType::Memcmp(Memcmp {
|
let filter = RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
bytes: MemcmpEncodedBytes::Base58(
|
bytes: MemcmpEncodedBytes::Base58(
|
||||||
|
@ -6411,6 +6404,7 @@ pub mod tests {
|
||||||
});
|
});
|
||||||
assert_eq!(verify_filter(&filter), Ok(()));
|
assert_eq!(verify_filter(&filter), Ok(()));
|
||||||
// Invalid base-58
|
// Invalid base-58
|
||||||
|
#[allow(deprecated)]
|
||||||
let filter = RpcFilterType::Memcmp(Memcmp {
|
let filter = RpcFilterType::Memcmp(Memcmp {
|
||||||
offset: 0,
|
offset: 0,
|
||||||
bytes: MemcmpEncodedBytes::Base58("III".to_string()),
|
bytes: MemcmpEncodedBytes::Base58("III".to_string()),
|
||||||
|
@ -7967,11 +7961,7 @@ pub mod tests {
|
||||||
get_spl_token_owner_filter(
|
get_spl_token_owner_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -7984,16 +7974,8 @@ pub mod tests {
|
||||||
get_spl_token_owner_filter(
|
get_spl_token_owner_filter(
|
||||||
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -8005,11 +7987,7 @@ pub mod tests {
|
||||||
get_spl_token_owner_filter(
|
get_spl_token_owner_filter(
|
||||||
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::TokenAccountState,
|
RpcFilterType::TokenAccountState,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8021,16 +7999,8 @@ pub mod tests {
|
||||||
assert!(get_spl_token_owner_filter(
|
assert!(get_spl_token_owner_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
|
@ -8039,11 +8009,7 @@ pub mod tests {
|
||||||
assert!(get_spl_token_owner_filter(
|
assert!(get_spl_token_owner_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, owner.to_bytes().to_vec())),
|
||||||
offset: 0,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8053,11 +8019,7 @@ pub mod tests {
|
||||||
assert!(get_spl_token_owner_filter(
|
assert!(get_spl_token_owner_filter(
|
||||||
&Pubkey::new_unique(),
|
&Pubkey::new_unique(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8065,16 +8027,8 @@ pub mod tests {
|
||||||
assert!(get_spl_token_owner_filter(
|
assert!(get_spl_token_owner_filter(
|
||||||
&Pubkey::new_unique(),
|
&Pubkey::new_unique(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, owner.to_bytes().to_vec())),
|
||||||
offset: 32,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(owner.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
|
@ -8088,11 +8042,7 @@ pub mod tests {
|
||||||
get_spl_token_mint_filter(
|
get_spl_token_mint_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8105,16 +8055,8 @@ pub mod tests {
|
||||||
get_spl_token_mint_filter(
|
get_spl_token_mint_filter(
|
||||||
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
&Pubkey::from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -8126,11 +8068,7 @@ pub mod tests {
|
||||||
get_spl_token_mint_filter(
|
get_spl_token_mint_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::TokenAccountState,
|
RpcFilterType::TokenAccountState,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8142,16 +8080,8 @@ pub mod tests {
|
||||||
assert!(get_spl_token_mint_filter(
|
assert!(get_spl_token_mint_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
|
@ -8160,11 +8090,7 @@ pub mod tests {
|
||||||
assert!(get_spl_token_mint_filter(
|
assert!(get_spl_token_mint_filter(
|
||||||
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
&Pubkey::from_str("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").unwrap(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(32, mint.to_bytes().to_vec())),
|
||||||
offset: 32,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8174,11 +8100,7 @@ pub mod tests {
|
||||||
assert!(get_spl_token_mint_filter(
|
assert!(get_spl_token_mint_filter(
|
||||||
&Pubkey::new_unique(),
|
&Pubkey::new_unique(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::DataSize(165)
|
RpcFilterType::DataSize(165)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -8186,16 +8108,8 @@ pub mod tests {
|
||||||
assert!(get_spl_token_mint_filter(
|
assert!(get_spl_token_mint_filter(
|
||||||
&Pubkey::new_unique(),
|
&Pubkey::new_unique(),
|
||||||
&[
|
&[
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(0, mint.to_bytes().to_vec())),
|
||||||
offset: 0,
|
RpcFilterType::Memcmp(Memcmp::new_raw_bytes(165, vec![ACCOUNTTYPE_ACCOUNT])),
|
||||||
bytes: MemcmpEncodedBytes::Bytes(mint.to_bytes().to_vec()),
|
|
||||||
encoding: None
|
|
||||||
}),
|
|
||||||
RpcFilterType::Memcmp(Memcmp {
|
|
||||||
offset: 165,
|
|
||||||
bytes: MemcmpEncodedBytes::Bytes(vec![ACCOUNTTYPE_ACCOUNT]),
|
|
||||||
encoding: None
|
|
||||||
})
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.is_none());
|
.is_none());
|
||||||
|
|
Loading…
Reference in New Issue