carbon_core/
account.rs

1//! Provides structures and traits for processing and decoding Solana accounts
2//! within the pipeline.
3//!
4//! This module includes the necessary components for handling account data
5//! updates in the `carbon-core` pipeline. It provides abstractions for decoding
6//! accounts, processing account metadata, and implementing account-specific
7//! pipes, which are integral to the pipeline's account update processing.
8//!
9//! # Overview
10//!
11//! The `account` module supports various tasks related to Solana account
12//! processing:
13//! - **Account Metadata**: Metadata about accounts, including slot and public
14//!   key information.
15//! - **Decoded Account**: Holds detailed account data after decoding, such as
16//!   lamports, owner, and rent epoch.
17//! - **Account Decoders**: A trait-based mechanism to decode raw Solana account
18//!   data into structured formats for processing.
19//! - **Account Pipes**: Encapsulates account processing logic, allowing custom
20//!   processing of decoded account data in the pipeline.
21//!
22//! # Example
23//!
24//! ```ignore
25//! struct MyAccountDecoder;
26//!
27//! impl<'a> AccountDecoder<'a> for MyAccountDecoder {
28//!     type AccountType = (AccountMetadata, DecodedAccount<PumpAccount>);
29//!
30//!     fn decode_account(
31//!         &self,
32//!         account: &'a solana_account::Account,
33//!     ) -> Option<DecodedAccount<Self::AccountType>> {
34//!         // Custom decoding logic here
35//!         Some(DecodedAccount {
36//!             lamports: account.lamports,
37//!             data: String::from_utf8(account.data.clone()).ok()?,
38//!             owner: account.owner,
39//!             executable: account.executable,
40//!             rent_epoch: account.rent_epoch,
41//!         })
42//!     }
43//! }
44//! ```
45//!
46//! # Notes
47//!
48//! - This module requires access to Solana SDK structures, specifically
49//!   `Account` and `Pubkey`.
50//! - All components support asynchronous processing to enable concurrent data
51//!   handling in the pipeline.
52
53use {
54    crate::{
55        error::CarbonResult, filter::Filter, metrics::MetricsCollection, processor::Processor,
56    },
57    async_trait::async_trait,
58    solana_pubkey::Pubkey,
59    solana_signature::Signature,
60    std::sync::Arc,
61};
62
63/// Holds metadata for an account update, including the slot and public key.
64///
65/// `AccountMetadata` provides essential information about an account update,
66/// such as the slot number where the account was updated and the account's
67/// public key. This metadata is used within the pipeline to identify and
68/// process account updates.
69///
70/// # Fields
71///
72/// - `slot`: The Solana slot number where the account was updated.
73/// - `pubkey`: The public key of the account.
74/// - `transaction_signature`: Signature of the transaction that caused the update.
75#[derive(Debug, Clone)]
76pub struct AccountMetadata {
77    pub slot: u64,
78    pub pubkey: Pubkey,
79    pub transaction_signature: Option<Signature>,
80}
81
82/// Represents the decoded data of a Solana account, including account-specific
83/// details.
84///
85/// `DecodedAccount` holds the detailed data of a Solana account after it has
86/// been decoded. It includes the account's lamports, owner, executable status,
87/// and rent epoch, as well as any decoded data specific to the account.
88///
89/// # Type Parameters
90///
91/// - `T`: The type of data specific to the account, which is determined by the
92///   decoder used.
93///
94/// # Fields
95///
96/// - `lamports`: The number of lamports in the account.
97/// - `data`: The decoded data specific to the account.
98/// - `owner`: The public key of the account's owner.
99/// - `executable`: Whether the account is executable.
100/// - `rent_epoch`: The rent epoch of the account.
101#[derive(Debug, Clone)]
102pub struct DecodedAccount<T> {
103    pub lamports: u64,
104    pub data: T,
105    pub owner: Pubkey,
106    pub executable: bool,
107    pub rent_epoch: u64,
108}
109
110/// Defines a trait for decoding Solana accounts into structured data types.
111///
112/// `AccountDecoder` provides a way to convert raw Solana `Account` data into
113/// structured `DecodedAccount` instances. By implementing this trait, you can
114/// define custom decoding logic to interpret account data in a way that suits
115/// your application's requirements.
116///
117/// # Associated Types
118///
119/// - `AccountType`: The data type resulting from decoding the account, specific
120///   to the application.
121pub trait AccountDecoder<'a> {
122    type AccountType;
123
124    fn decode_account(
125        &self,
126        account: &'a solana_account::Account,
127    ) -> Option<DecodedAccount<Self::AccountType>>;
128}
129
130/// The input type for the account processor.
131///
132/// - `T`: The account type, as determined by the decoder.
133pub type AccountProcessorInputType<T> =
134    (AccountMetadata, DecodedAccount<T>, solana_account::Account);
135
136/// A processing pipe that decodes and processes Solana account updates.
137///
138/// `AccountPipe` combines an `AccountDecoder` and a `Processor` to manage
139/// account updates in the pipeline. This struct decodes the raw account data
140/// and then processes the resulting `DecodedAccount` with the specified
141/// processing logic.
142///
143/// # Type Parameters
144///
145/// - `T`: The data type of the decoded account information, as determined by
146///   the decoder.
147///
148/// # Fields
149///
150/// - `decoder`: An `AccountDecoder` that decodes raw account data into
151///   structured form.
152/// - `processor`: A `Processor` that handles the processing logic for decoded
153///   accounts.
154/// - `filters`: A collection of filters that determine which account updates
155///   should be processed. Each filter in this collection is applied to incoming
156///   account updates, and only updates that pass all filters (return `true`)
157///   will be processed. If this collection is empty, all updates are processed.
158pub struct AccountPipe<T: Send> {
159    pub decoder: Box<dyn for<'a> AccountDecoder<'a, AccountType = T> + Send + Sync + 'static>,
160    pub processor: Box<dyn Processor<InputType = AccountProcessorInputType<T>> + Send + Sync>,
161    pub filters: Vec<Box<dyn Filter + Send + Sync + 'static>>,
162}
163
164/// An async trait for processing account updates.
165///
166/// The `AccountPipes` trait allows for processing of account updates.
167///
168/// # Required Methods
169///
170/// - `run`: Processes an account update and tracks the operation with metrics.
171/// - `filters`: Returns a reference to the filters associated with this pipe,
172///   which are used by the pipeline to determine which account updates should
173///   be processed.
174#[async_trait]
175pub trait AccountPipes: Send + Sync {
176    async fn run(
177        &mut self,
178        account_with_metadata: (AccountMetadata, solana_account::Account),
179        metrics: Arc<MetricsCollection>,
180    ) -> CarbonResult<()>;
181
182    fn filters(&self) -> &Vec<Box<dyn Filter + Send + Sync + 'static>>;
183}
184
185#[async_trait]
186impl<T: Send> AccountPipes for AccountPipe<T> {
187    async fn run(
188        &mut self,
189        account_with_metadata: (AccountMetadata, solana_account::Account),
190        metrics: Arc<MetricsCollection>,
191    ) -> CarbonResult<()> {
192        log::trace!(
193            "AccountPipe::run(account_with_metadata: {:?}, metrics)",
194            account_with_metadata,
195        );
196
197        if let Some(decoded_account) = self.decoder.decode_account(&account_with_metadata.1) {
198            self.processor
199                .process(
200                    (
201                        account_with_metadata.0.clone(),
202                        decoded_account,
203                        account_with_metadata.1,
204                    ),
205                    metrics.clone(),
206                )
207                .await?;
208        }
209        Ok(())
210    }
211
212    fn filters(&self) -> &Vec<Box<dyn Filter + Send + Sync + 'static>> {
213        &self.filters
214    }
215}