1#![deny(
2 clippy::disallowed_methods,
3 clippy::suspicious,
4 clippy::style,
5 clippy::clone_on_ref_ptr,
6 missing_debug_implementations,
7 missing_copy_implementations
8)]
9#![warn(clippy::pedantic, missing_docs)]
10#![allow(clippy::module_name_repetitions)]
11
12use std::{
19 borrow::Cow,
20 collections::{HashMap, HashSet},
21 fmt::{self, Debug},
22 future::Future,
23 ops,
24 str::FromStr,
25};
26
27use yellowstone_grpc_proto::geyser::{
28 SubscribeRequest, SubscribeRequestFilterAccounts, SubscribeRequestFilterTransactions,
29 SubscribeUpdateAccount, SubscribeUpdateTransaction,
30};
31
32pub extern crate bs58;
33#[cfg(feature = "proto")]
34pub extern crate yellowstone_vixen_proto;
35
36pub mod instruction;
37#[cfg(feature = "proto")]
38pub mod proto;
39
40type BoxedError = Box<dyn std::error::Error + Send + Sync + 'static>;
41
42#[derive(Debug)]
44pub enum ParseError {
45 Filtered,
49 Other(BoxedError),
51}
52
53impl<T: Into<BoxedError>> From<T> for ParseError {
54 #[inline]
55 fn from(value: T) -> Self { Self::Other(value.into()) }
56}
57
58pub type ParseResult<T> = Result<T, ParseError>;
60
61pub type AccountUpdate = SubscribeUpdateAccount;
63pub type TransactionUpdate = SubscribeUpdateTransaction;
65
66pub trait Parser {
70 type Input;
72 type Output;
74
75 fn id(&self) -> Cow<str>;
85
86 fn prefilter(&self) -> Prefilter;
89
90 fn parse(&self, value: &Self::Input) -> impl Future<Output = ParseResult<Self::Output>> + Send;
92}
93
94pub trait ProgramParser: Parser {
96 fn program_id(&self) -> Pubkey;
98}
99
100pub trait ParserId {
102 fn id(&self) -> Cow<str>;
104}
105
106impl ParserId for std::convert::Infallible {
107 #[inline]
108 fn id(&self) -> Cow<str> { match *self {} }
109}
110
111impl<T: Parser> ParserId for T {
112 #[inline]
113 fn id(&self) -> Cow<str> { Parser::id(self) }
114}
115
116pub trait GetPrefilter {
118 fn prefilter(&self) -> Prefilter;
120}
121
122impl GetPrefilter for std::convert::Infallible {
123 #[inline]
124 fn prefilter(&self) -> Prefilter { match *self {} }
125}
126
127impl<T: Parser> GetPrefilter for T {
128 #[inline]
129 fn prefilter(&self) -> Prefilter { Parser::prefilter(self) }
130}
131
132#[derive(Debug, Default)]
135pub struct Prefilter {
136 pub(crate) account: Option<AccountPrefilter>,
137 pub(crate) transaction: Option<TransactionPrefilter>,
138}
139
140fn merge_opt<T, F: FnOnce(&mut T, T)>(lhs: &mut Option<T>, rhs: Option<T>, f: F) {
141 match (lhs.as_mut(), rhs) {
142 (None, r) => *lhs = r,
143 (Some(_), None) => (),
144 (Some(l), Some(r)) => f(l, r),
145 }
146}
147
148impl Prefilter {
149 #[inline]
151 pub fn builder() -> PrefilterBuilder { PrefilterBuilder::default() }
152
153 pub fn merge(&mut self, other: Prefilter) {
156 let Self {
157 account,
158 transaction,
159 } = self;
160 merge_opt(account, other.account, AccountPrefilter::merge);
161 merge_opt(transaction, other.transaction, TransactionPrefilter::merge);
162 }
163}
164
165impl FromIterator<Prefilter> for Prefilter {
166 fn from_iter<T: IntoIterator<Item = Prefilter>>(iter: T) -> Self {
167 let mut iter = iter.into_iter();
168 let Some(ret) = iter.next() else {
169 return Self::default();
170 };
171 iter.fold(ret, |mut l, r| {
172 l.merge(r);
173 l
174 })
175 }
176}
177
178#[derive(Debug, Default, Clone, PartialEq)]
179pub(crate) struct AccountPrefilter {
180 pub accounts: HashSet<Pubkey>,
181 pub owners: HashSet<Pubkey>,
182}
183
184impl AccountPrefilter {
185 pub fn merge(&mut self, other: AccountPrefilter) {
186 let Self { accounts, owners } = self;
187 accounts.extend(other.accounts);
188 owners.extend(other.owners);
189 }
190}
191
192#[derive(Debug, Default, Clone, PartialEq)]
193pub(crate) struct TransactionPrefilter {
194 pub accounts: HashSet<Pubkey>,
195}
196
197impl TransactionPrefilter {
198 pub fn merge(&mut self, other: TransactionPrefilter) {
199 let Self { accounts } = self;
200 accounts.extend(other.accounts);
201 }
202}
203
204#[macro_export]
220macro_rules! pubkey_convert_helpers {
221 ($ty:ty) => {
222 pub(crate) fn into_vixen_pubkey(value: $ty) -> $crate::Pubkey { value.to_bytes().into() }
223
224 pub(crate) fn from_vixen_pubkey(value: $crate::Pubkey) -> $ty { value.into_bytes().into() }
225 };
226}
227
228pub type Pubkey = KeyBytes<32>;
236
237#[derive(Clone, Copy, PartialEq, Eq, Hash)]
240#[repr(transparent)]
241pub struct KeyBytes<const LEN: usize>(pub [u8; LEN]);
242
243impl<const LEN: usize> Debug for KeyBytes<LEN> {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 f.debug_tuple("KeyBytes")
246 .field(&bs58::encode(self.0).into_string())
247 .finish()
248 }
249}
250
251impl<const LEN: usize> fmt::Display for KeyBytes<LEN> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 f.write_str(&bs58::encode(self.0).into_string())
254 }
255}
256
257impl<const LEN: usize> From<[u8; LEN]> for KeyBytes<LEN> {
258 #[inline]
259 fn from(value: [u8; LEN]) -> Self { Self(value) }
260}
261
262impl<const LEN: usize> From<KeyBytes<LEN>> for [u8; LEN] {
263 #[inline]
264 fn from(value: KeyBytes<LEN>) -> Self { value.0 }
265}
266
267impl<const LEN: usize> std::ops::Deref for KeyBytes<LEN> {
268 type Target = [u8; LEN];
269
270 fn deref(&self) -> &Self::Target { &self.0 }
271}
272
273impl<const LEN: usize> std::ops::DerefMut for KeyBytes<LEN> {
274 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
275}
276
277impl<const LEN: usize> AsRef<[u8; LEN]> for KeyBytes<LEN> {
278 fn as_ref(&self) -> &[u8; LEN] { self }
279}
280
281impl<const LEN: usize> AsMut<[u8; LEN]> for KeyBytes<LEN> {
282 fn as_mut(&mut self) -> &mut [u8; LEN] { self }
283}
284
285impl<const LEN: usize> std::borrow::Borrow<[u8; LEN]> for KeyBytes<LEN> {
286 fn borrow(&self) -> &[u8; LEN] { self }
287}
288
289impl<const LEN: usize> std::borrow::BorrowMut<[u8; LEN]> for KeyBytes<LEN> {
290 fn borrow_mut(&mut self) -> &mut [u8; LEN] { self }
291}
292
293impl<const LEN: usize> AsRef<[u8]> for KeyBytes<LEN> {
294 fn as_ref(&self) -> &[u8] { self.as_slice() }
295}
296
297impl<const LEN: usize> AsMut<[u8]> for KeyBytes<LEN> {
298 fn as_mut(&mut self) -> &mut [u8] { self.as_mut_slice() }
299}
300
301impl<const LEN: usize> std::borrow::Borrow<[u8]> for KeyBytes<LEN> {
302 fn borrow(&self) -> &[u8] { self.as_ref() }
303}
304
305impl<const LEN: usize> std::borrow::BorrowMut<[u8]> for KeyBytes<LEN> {
306 fn borrow_mut(&mut self) -> &mut [u8] { self.as_mut() }
307}
308
309type KeyFromSliceError = std::array::TryFromSliceError;
310
311impl<const LEN: usize> TryFrom<&[u8]> for KeyBytes<LEN> {
312 type Error = KeyFromSliceError;
313
314 #[inline]
315 fn try_from(value: &[u8]) -> Result<Self, Self::Error> { value.try_into().map(Self) }
316}
317
318impl<const LEN: usize> KeyBytes<LEN> {
319 #[must_use]
321 pub fn new(bytes: [u8; LEN]) -> Self { bytes.into() }
322
323 #[must_use]
325 pub fn into_bytes(self) -> [u8; LEN] { self.into() }
326
327 pub fn try_from_ref<T: AsRef<[u8]>>(key: T) -> Result<Self, KeyFromSliceError> {
333 key.as_ref().try_into()
334 }
335
336 pub fn equals_ref<T: AsRef<[u8]>>(&self, other: T) -> bool {
339 self.as_slice().eq(other.as_ref())
340 }
341}
342
343#[derive(Debug, Clone, Copy, thiserror::Error)]
345pub enum KeyFromStrError<const LEN: usize = 32> {
346 #[error("Invalid base58 string")]
348 Bs58(#[from] bs58::decode::Error),
349 #[error("Invalid key length, must be {LEN} bytes")]
351 Len(#[from] std::array::TryFromSliceError),
352}
353
354impl<const LEN: usize> FromStr for KeyBytes<LEN> {
355 type Err = KeyFromStrError<LEN>;
356
357 fn from_str(s: &str) -> Result<Self, Self::Err> {
358 bs58::decode(s)
359 .into_vec()?
360 .as_slice()
361 .try_into()
362 .map_err(Into::into)
363 }
364}
365
366impl<const LEN: usize> TryFrom<&str> for KeyBytes<LEN> {
367 type Error = KeyFromStrError<LEN>;
368
369 fn try_from(value: &str) -> Result<Self, Self::Error> { value.parse() }
370}
371
372impl<const LEN: usize> TryFrom<String> for KeyBytes<LEN> {
373 type Error = KeyFromStrError<LEN>;
374
375 fn try_from(value: String) -> Result<Self, Self::Error> { value.parse() }
376}
377
378impl<const LEN: usize> TryFrom<Cow<'_, str>> for KeyBytes<LEN> {
379 type Error = KeyFromStrError<LEN>;
380
381 fn try_from(value: Cow<str>) -> Result<Self, Self::Error> { value.parse() }
382}
383
384#[derive(Debug, Clone, thiserror::Error)]
386pub enum PrefilterError {
387 #[error("Value already given for field {0}")]
389 AlreadySet(&'static str),
390 #[error("Invalid pubkey {}", bs58::encode(.0).into_string())]
392 BadPubkey(Vec<u8>, std::array::TryFromSliceError),
393}
394
395#[derive(Debug, Default)]
397#[must_use = "Consider calling .build() on this builder"]
398pub struct PrefilterBuilder {
399 error: Option<PrefilterError>,
400 accounts: Option<HashSet<Pubkey>>,
401 account_owners: Option<HashSet<Pubkey>>,
402 transaction_accounts: Option<HashSet<Pubkey>>,
403}
404
405fn set_opt<T>(opt: &mut Option<T>, field: &'static str, val: T) -> Result<(), PrefilterError> {
406 if opt.is_some() {
407 return Err(PrefilterError::AlreadySet(field));
408 }
409
410 *opt = Some(val);
411 Ok(())
412}
413
414fn collect_pubkeys<I: IntoIterator>(it: I) -> Result<HashSet<Pubkey>, PrefilterError>
416where I::Item: AsRef<[u8]> {
417 it.into_iter()
418 .map(|p| {
419 let p = p.as_ref();
420 p.try_into()
421 .map_err(|e| PrefilterError::BadPubkey(p.to_vec(), e))
422 })
423 .collect()
424}
425
426impl PrefilterBuilder {
427 pub fn build(self) -> Result<Prefilter, PrefilterError> {
432 let PrefilterBuilder {
433 error,
434 accounts,
435 account_owners,
436 transaction_accounts,
437 } = self;
438 if let Some(err) = error {
439 return Err(err);
440 }
441
442 let account = AccountPrefilter {
443 accounts: accounts.unwrap_or_default(),
444 owners: account_owners.unwrap_or_default(),
445 };
446
447 let transaction = TransactionPrefilter {
448 accounts: transaction_accounts.unwrap_or_default(),
449 };
450
451 Ok(Prefilter {
452 account: (account != AccountPrefilter::default()).then_some(account),
453 transaction: (transaction != TransactionPrefilter::default()).then_some(transaction),
454 })
455 }
456
457 fn mutate<F: FnOnce(&mut Self) -> Result<(), PrefilterError>>(mut self, f: F) -> Self {
458 if self.error.is_none() {
459 self.error = f(&mut self).err();
460 }
461
462 self
463 }
464
465 pub fn accounts<I: IntoIterator>(self, it: I) -> Self
467 where I::Item: AsRef<[u8]> {
468 self.mutate(|this| set_opt(&mut this.accounts, "accounts", collect_pubkeys(it)?))
469 }
470
471 pub fn account_owners<I: IntoIterator>(self, it: I) -> Self
473 where I::Item: AsRef<[u8]> {
474 self.mutate(|this| {
475 set_opt(
476 &mut this.account_owners,
477 "account_owners",
478 collect_pubkeys(it)?,
479 )
480 })
481 }
482
483 pub fn transaction_accounts<I: IntoIterator>(self, it: I) -> Self
486 where I::Item: AsRef<[u8]> {
487 self.mutate(|this| {
488 set_opt(
489 &mut this.transaction_accounts,
490 "transaction_accounts",
491 collect_pubkeys(it)?,
492 )
493 })
494 }
495}
496
497#[derive(Debug)]
499#[repr(transparent)]
500pub struct Filters<'a>(HashMap<&'a str, Prefilter>);
501
502impl<'a> Filters<'a> {
503 #[inline]
505 #[must_use]
506 pub const fn new(filters: HashMap<&'a str, Prefilter>) -> Self { Self(filters) }
507}
508
509impl<'a> ops::Deref for Filters<'a> {
510 type Target = HashMap<&'a str, Prefilter>;
511
512 #[inline]
513 fn deref(&self) -> &Self::Target { &self.0 }
514}
515
516impl<'a> From<Filters<'a>> for SubscribeRequest {
517 fn from(value: Filters<'a>) -> Self {
518 SubscribeRequest {
519 accounts: value
520 .iter()
521 .filter_map(|(k, v)| {
522 let v = v.account.as_ref()?;
523
524 Some((k.to_owned().into(), SubscribeRequestFilterAccounts {
525 account: v.accounts.iter().map(ToString::to_string).collect(),
526 owner: v.owners.iter().map(ToString::to_string).collect(),
527 filters: vec![],
529 }))
530 })
531 .collect(),
532 slots: [].into_iter().collect(),
533 transactions: value
534 .iter()
535 .filter_map(|(k, v)| {
536 let v = v.transaction.as_ref()?;
537
538 Some((k.to_owned().into(), SubscribeRequestFilterTransactions {
539 vote: None,
540 failed: Some(false),
542 signature: None,
543 account_include: v.accounts.iter().map(ToString::to_string).collect(),
545 account_exclude: [].into_iter().collect(),
546 account_required: [].into_iter().collect(),
547 }))
548 })
549 .collect(),
550 transactions_status: [].into_iter().collect(),
551 blocks: [].into_iter().collect(),
552 blocks_meta: [].into_iter().collect(),
553 entry: [].into_iter().collect(),
554 commitment: None,
555 accounts_data_slice: vec![],
556 ping: None,
557 }
558 }
559}