1#![allow(deprecated)]
2use {
3 crate::version_req::VersionReq,
4 solana_sdk::account::{AccountSharedData, ReadableAccount},
5 safe_token_2022::{generic_token_account::GenericTokenAccount, state::Account},
6 std::borrow::Cow,
7 thiserror::Error,
8};
9
10const MAX_DATA_SIZE: usize = 128;
11const MAX_DATA_BASE58_SIZE: usize = 175;
12const MAX_DATA_BASE64_SIZE: usize = 172;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub enum RpcFilterType {
17 DataSize(u64),
18 Memcmp(Memcmp),
19 TokenAccountState,
20}
21
22impl RpcFilterType {
23 pub fn verify(&self) -> Result<(), RpcFilterError> {
24 match self {
25 RpcFilterType::DataSize(_) => Ok(()),
26 RpcFilterType::Memcmp(compare) => {
27 let encoding = compare.encoding.as_ref().unwrap_or(&MemcmpEncoding::Binary);
28 match encoding {
29 MemcmpEncoding::Binary => {
30 use MemcmpEncodedBytes::*;
31 match &compare.bytes {
32 Binary(bytes) => {
34 if bytes.len() > MAX_DATA_BASE58_SIZE {
35 return Err(RpcFilterError::Base58DataTooLarge);
36 }
37 let bytes = bs58::decode(&bytes)
38 .into_vec()
39 .map_err(RpcFilterError::DecodeError)?;
40 if bytes.len() > MAX_DATA_SIZE {
41 Err(RpcFilterError::Base58DataTooLarge)
42 } else {
43 Ok(())
44 }
45 }
46 Base58(bytes) => {
47 if bytes.len() > MAX_DATA_BASE58_SIZE {
48 return Err(RpcFilterError::DataTooLarge);
49 }
50 let bytes = bs58::decode(&bytes).into_vec()?;
51 if bytes.len() > MAX_DATA_SIZE {
52 Err(RpcFilterError::DataTooLarge)
53 } else {
54 Ok(())
55 }
56 }
57 Base64(bytes) => {
58 if bytes.len() > MAX_DATA_BASE64_SIZE {
59 return Err(RpcFilterError::DataTooLarge);
60 }
61 let bytes = base64::decode(bytes)?;
62 if bytes.len() > MAX_DATA_SIZE {
63 Err(RpcFilterError::DataTooLarge)
64 } else {
65 Ok(())
66 }
67 }
68 Bytes(bytes) => {
69 if bytes.len() > MAX_DATA_SIZE {
70 return Err(RpcFilterError::DataTooLarge);
71 }
72 Ok(())
73 }
74 }
75 }
76 }
77 }
78 RpcFilterType::TokenAccountState => Ok(()),
79 }
80 }
81
82 pub fn allows(&self, account: &AccountSharedData) -> bool {
83 match self {
84 RpcFilterType::DataSize(size) => account.data().len() as u64 == *size,
85 RpcFilterType::Memcmp(compare) => compare.bytes_match(account.data()),
86 RpcFilterType::TokenAccountState => Account::valid_account_data(account.data()),
87 }
88 }
89}
90
91#[derive(Error, PartialEq, Eq, Debug)]
92pub enum RpcFilterError {
93 #[error("encoded binary data should be less than 129 bytes")]
94 DataTooLarge,
95 #[deprecated(
96 since = "1.8.1",
97 note = "Error for MemcmpEncodedBytes::Binary which is deprecated"
98 )]
99 #[error("encoded binary (base 58) data should be less than 129 bytes")]
100 Base58DataTooLarge,
101 #[deprecated(
102 since = "1.8.1",
103 note = "Error for MemcmpEncodedBytes::Binary which is deprecated"
104 )]
105 #[error("bs58 decode error")]
106 DecodeError(bs58::decode::Error),
107 #[error("base58 decode error")]
108 Base58DecodeError(#[from] bs58::decode::Error),
109 #[error("base64 decode error")]
110 Base64DecodeError(#[from] base64::DecodeError),
111}
112
113#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
114#[serde(rename_all = "camelCase")]
115pub enum MemcmpEncoding {
116 Binary,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
120#[serde(rename_all = "camelCase", untagged)]
121pub enum MemcmpEncodedBytes {
122 #[deprecated(
123 since = "1.8.1",
124 note = "Please use MemcmpEncodedBytes::Base58 instead"
125 )]
126 Binary(String),
127 Base58(String),
128 Base64(String),
129 Bytes(Vec<u8>),
130}
131
132#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
133#[serde(into = "RpcMemcmp", from = "RpcMemcmp")]
134pub struct Memcmp {
135 pub offset: usize,
137 pub bytes: MemcmpEncodedBytes,
139 #[deprecated(
141 since = "1.11.2",
142 note = "Field has no server-side effect. Specify encoding with `MemcmpEncodedBytes` variant instead."
143 )]
144 pub encoding: Option<MemcmpEncoding>,
145}
146
147impl Memcmp {
148 pub fn new_raw_bytes(offset: usize, bytes: Vec<u8>) -> Self {
149 Self {
150 offset,
151 bytes: MemcmpEncodedBytes::Bytes(bytes),
152 encoding: None,
153 }
154 }
155
156 pub fn new_base58_encoded(offset: usize, bytes: &[u8]) -> Self {
157 Self {
158 offset,
159 bytes: MemcmpEncodedBytes::Base58(bs58::encode(bytes).into_string()),
160 encoding: None,
161 }
162 }
163
164 pub fn bytes(&self) -> Option<Cow<Vec<u8>>> {
165 use MemcmpEncodedBytes::*;
166 match &self.bytes {
167 Binary(bytes) | Base58(bytes) => bs58::decode(bytes).into_vec().ok().map(Cow::Owned),
168 Base64(bytes) => base64::decode(bytes).ok().map(Cow::Owned),
169 Bytes(bytes) => Some(Cow::Borrowed(bytes)),
170 }
171 }
172
173 pub fn bytes_match(&self, data: &[u8]) -> bool {
174 match self.bytes() {
175 Some(bytes) => {
176 if self.offset > data.len() {
177 return false;
178 }
179 if data[self.offset..].len() < bytes.len() {
180 return false;
181 }
182 data[self.offset..self.offset + bytes.len()] == bytes[..]
183 }
184 None => false,
185 }
186 }
187}
188
189#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
191#[serde(untagged)]
192enum DataType {
193 Encoded(String),
194 Raw(Vec<u8>),
195}
196
197#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
199#[serde(rename_all = "camelCase")]
200enum RpcMemcmpEncoding {
201 Base58,
202 Base64,
203 #[serde(other)]
205 Binary,
206}
207
208#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
213struct RpcMemcmp {
214 offset: usize,
215 bytes: DataType,
216 encoding: Option<RpcMemcmpEncoding>,
217}
218
219impl From<Memcmp> for RpcMemcmp {
220 fn from(memcmp: Memcmp) -> RpcMemcmp {
221 let (bytes, encoding) = match memcmp.bytes {
222 MemcmpEncodedBytes::Binary(string) => {
223 (DataType::Encoded(string), Some(RpcMemcmpEncoding::Binary))
224 }
225 MemcmpEncodedBytes::Base58(string) => {
226 (DataType::Encoded(string), Some(RpcMemcmpEncoding::Base58))
227 }
228 MemcmpEncodedBytes::Base64(string) => {
229 (DataType::Encoded(string), Some(RpcMemcmpEncoding::Base64))
230 }
231 MemcmpEncodedBytes::Bytes(vector) => (DataType::Raw(vector), None),
232 };
233 RpcMemcmp {
234 offset: memcmp.offset,
235 bytes,
236 encoding,
237 }
238 }
239}
240
241impl From<RpcMemcmp> for Memcmp {
242 fn from(memcmp: RpcMemcmp) -> Memcmp {
243 let encoding = memcmp.encoding.unwrap_or(RpcMemcmpEncoding::Binary);
244 let bytes = match (encoding, memcmp.bytes) {
245 (RpcMemcmpEncoding::Binary, DataType::Encoded(string))
246 | (RpcMemcmpEncoding::Base58, DataType::Encoded(string)) => {
247 MemcmpEncodedBytes::Base58(string)
248 }
249 (RpcMemcmpEncoding::Binary, DataType::Raw(vector)) => MemcmpEncodedBytes::Bytes(vector),
250 (RpcMemcmpEncoding::Base64, DataType::Encoded(string)) => {
251 MemcmpEncodedBytes::Base64(string)
252 }
253 _ => unreachable!(),
254 };
255 Memcmp {
256 offset: memcmp.offset,
257 bytes,
258 encoding: None,
259 }
260 }
261}
262
263pub(crate) fn maybe_map_filters(
264 node_version: Option<semver::Version>,
265 filters: &mut [RpcFilterType],
266) -> Result<(), String> {
267 let version_reqs = VersionReq::from_strs(&["<1.11.2", "~1.13"])?;
268 let needs_mapping = node_version
269 .map(|version| version_reqs.matches_any(&version))
270 .unwrap_or(true);
271 if needs_mapping {
272 for filter in filters.iter_mut() {
273 if let RpcFilterType::Memcmp(memcmp) = filter {
274 match &memcmp.bytes {
275 MemcmpEncodedBytes::Base58(string) => {
276 memcmp.bytes = MemcmpEncodedBytes::Binary(string.clone());
277 }
278 MemcmpEncodedBytes::Base64(_) => {
279 return Err("RPC node on old version does not support base64 \
280 encoding for memcmp filters"
281 .to_string());
282 }
283 _ => {}
284 }
285 }
286 }
287 }
288 Ok(())
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[test]
296 fn test_worst_case_encoded_tx_goldens() {
297 let ff_data = vec![0xffu8; MAX_DATA_SIZE];
298 let data58 = bs58::encode(&ff_data).into_string();
299 assert_eq!(data58.len(), MAX_DATA_BASE58_SIZE);
300 let data64 = base64::encode(&ff_data);
301 assert_eq!(data64.len(), MAX_DATA_BASE64_SIZE);
302 }
303
304 #[test]
305 fn test_bytes_match() {
306 let data = vec![1, 2, 3, 4, 5];
307
308 assert!(Memcmp {
310 offset: 0,
311 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![1, 2, 3, 4, 5]).into_string()),
312 encoding: None,
313 }
314 .bytes_match(&data));
315
316 assert!(Memcmp {
318 offset: 0,
319 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![1, 2]).into_string()),
320 encoding: None,
321 }
322 .bytes_match(&data));
323
324 assert!(Memcmp {
326 offset: 2,
327 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![3, 4]).into_string()),
328 encoding: None,
329 }
330 .bytes_match(&data));
331
332 assert!(!Memcmp {
334 offset: 0,
335 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![2]).into_string()),
336 encoding: None,
337 }
338 .bytes_match(&data));
339
340 assert!(!Memcmp {
342 offset: 2,
343 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![3, 4, 5, 6]).into_string()),
344 encoding: None,
345 }
346 .bytes_match(&data));
347
348 assert!(!Memcmp {
350 offset: 6,
351 bytes: MemcmpEncodedBytes::Base58(bs58::encode(vec![5]).into_string()),
352 encoding: None,
353 }
354 .bytes_match(&data));
355
356 assert!(!Memcmp {
358 offset: 0,
359 bytes: MemcmpEncodedBytes::Base58("III".to_string()),
360 encoding: None,
361 }
362 .bytes_match(&data));
363 }
364
365 #[test]
366 fn test_verify_memcmp() {
367 let base58_bytes = "\
368 1111111111111111111111111111111111111111111111111111111111111111\
369 1111111111111111111111111111111111111111111111111111111111111111";
370 assert_eq!(base58_bytes.len(), 128);
371 assert_eq!(
372 RpcFilterType::Memcmp(Memcmp {
373 offset: 0,
374 bytes: MemcmpEncodedBytes::Base58(base58_bytes.to_string()),
375 encoding: None,
376 })
377 .verify(),
378 Ok(())
379 );
380
381 let base58_bytes = "\
382 1111111111111111111111111111111111111111111111111111111111111111\
383 1111111111111111111111111111111111111111111111111111111111111111\
384 1";
385 assert_eq!(base58_bytes.len(), 129);
386 assert_eq!(
387 RpcFilterType::Memcmp(Memcmp {
388 offset: 0,
389 bytes: MemcmpEncodedBytes::Base58(base58_bytes.to_string()),
390 encoding: None,
391 })
392 .verify(),
393 Err(RpcFilterError::DataTooLarge)
394 );
395 }
396}