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//! ```rust
25//!
26//! struct MyAccountDecoder;
27//!
28//! impl<'a> AccountDecoder<'a> for MyAccountDecoder {
29//!     type AccountType = (AccountMetadata, DecodedAccount<PumpAccount>);
30//!
31//!     fn decode_account(
32//!         &self,
33//!         account: &'a solana_account::Account,
34//!     ) -> Option<DecodedAccount<Self::AccountType>> {
35//!         // Custom decoding logic here
36//!         Some(DecodedAccount {
37//!             lamports: account.lamports,
38//!             data: String::from_utf8(account.data.clone()).ok()?,
39//!             owner: account.owner,
40//!             executable: account.executable,
41//!             rent_epoch: account.rent_epoch,
42//!         })
43//!     }
44//! }
45//! ```
46//!
47//! # Notes
48//!
49//! - This module requires access to Solana SDK structures, specifically
50//!   `Account` and `Pubkey`.
51//! - All components support asynchronous processing to enable concurrent data
52//!   handling in the pipeline.
53
54use {
55    crate::{error::CarbonResult, metrics::MetricsCollection, processor::Processor},
56    async_trait::async_trait,
57    solana_pubkey::Pubkey,
58    std::sync::Arc,
59};
60
61/// Holds metadata for an account update, including the slot and public key.
62///
63/// `AccountMetadata` provides essential information about an account update,
64/// such as the slot number where the account was updated and the account's
65/// public key. This metadata is used within the pipeline to identify and
66/// process account updates.
67///
68/// # Fields
69///
70/// - `slot`: The Solana slot number where the account was updated.
71/// - `pubkey`: The public key of the account.
72#[derive(Debug, Clone)]
73pub struct AccountMetadata {
74    pub slot: u64,
75    pub pubkey: Pubkey,
76}
77
78/// Represents the decoded data of a Solana account, including account-specific
79/// details.
80///
81/// `DecodedAccount` holds the detailed data of a Solana account after it has
82/// been decoded. It includes the account's lamports, owner, executable status,
83/// and rent epoch, as well as any decoded data specific to the account.
84///
85/// # Type Parameters
86///
87/// - `T`: The type of data specific to the account, which is determined by the
88///   decoder used.
89///
90/// # Fields
91///
92/// - `lamports`: The number of lamports in the account.
93/// - `data`: The decoded data specific to the account.
94/// - `owner`: The public key of the account's owner.
95/// - `executable`: Whether the account is executable.
96/// - `rent_epoch`: The rent epoch of the account.
97#[derive(Debug, Clone)]
98pub struct DecodedAccount<T> {
99    pub lamports: u64,
100    pub data: T,
101    pub owner: Pubkey,
102    pub executable: bool,
103    pub rent_epoch: u64,
104}
105
106/// Defines a trait for decoding Solana accounts into structured data types.
107///
108/// `AccountDecoder` provides a way to convert raw Solana `Account` data into
109/// structured `DecodedAccount` instances. By implementing this trait, you can
110/// define custom decoding logic to interpret account data in a way that suits
111/// your application's requirements.
112///
113/// # Associated Types
114///
115/// - `AccountType`: The data type resulting from decoding the account, specific
116///   to the application.
117pub trait AccountDecoder<'a> {
118    type AccountType;
119
120    fn decode_account(
121        &self,
122        account: &'a solana_account::Account,
123    ) -> Option<DecodedAccount<Self::AccountType>>;
124}
125
126/// The input type for the account processor.
127///
128/// - `T`: The account type, as determined by the decoder.
129pub type AccountProcessorInputType<T> = (AccountMetadata, DecodedAccount<T>);
130
131/// A processing pipe that decodes and processes Solana account updates.
132///
133/// `AccountPipe` combines an `AccountDecoder` and a `Processor` to manage
134/// account updates in the pipeline. This struct decodes the raw account data
135/// and then processes the resulting `DecodedAccount` with the specified
136/// processing logic.
137///
138/// # Type Parameters
139///
140/// - `T`: The data type of the decoded account information, as determined by
141///   the decoder.
142///
143/// # Fields
144///
145/// - `decoder`: An `AccountDecoder` that decodes raw account data into
146///   structured form.
147/// - `processor`: A `Processor` that handles the processing logic for decoded
148///   accounts.
149pub struct AccountPipe<T: Send> {
150    pub decoder: Box<dyn for<'a> AccountDecoder<'a, AccountType = T> + Send + Sync + 'static>,
151    pub processor: Box<dyn Processor<InputType = AccountProcessorInputType<T>> + Send + Sync>,
152}
153
154/// A trait for processing account updates in the pipeline asynchronously.
155///
156/// `AccountPipes` defines the `run` method for processing account updates in
157/// the pipeline. Implementations should handle the decoding and processing of
158/// the account data, and update metrics as needed.
159///
160/// # Example
161///
162/// ```rust
163/// #[async_trait]
164/// impl AccountPipes for MyAccountPipe {
165///     async fn run(
166///         &mut self,
167///         account_with_metadata: (AccountMetadata, solana_account::Account),
168///         metrics: Arc<MetricsCollection>,
169///     ) -> CarbonResult<()> {
170///         // Custom processing logic here
171///         Ok(())
172///     }
173/// }
174/// ```
175///
176/// # Parameters
177///
178/// - `account_with_metadata`: A tuple containing account metadata and the
179///   Solana account data.
180/// - `metrics`: A list of `Metrics` objects for recording and tracking metrics.
181#[async_trait]
182pub trait AccountPipes: Send + Sync {
183    async fn run(
184        &mut self,
185        account_with_metadata: (AccountMetadata, solana_account::Account),
186        metrics: Arc<MetricsCollection>,
187    ) -> CarbonResult<()>;
188}
189
190#[async_trait]
191impl<T: Send> AccountPipes for AccountPipe<T> {
192    async fn run(
193        &mut self,
194        account_with_metadata: (AccountMetadata, solana_account::Account),
195        metrics: Arc<MetricsCollection>,
196    ) -> CarbonResult<()> {
197        log::trace!(
198            "AccountPipe::run(account_with_metadata: {:?}, metrics)",
199            account_with_metadata,
200        );
201
202        if let Some(decoded_account) = self.decoder.decode_account(&account_with_metadata.1) {
203            self.processor
204                .process((account_with_metadata.0, decoded_account), metrics)
205                .await?;
206        }
207        Ok(())
208    }
209}