surfpool_core/surfnet/
mod.rs

1use crossbeam_channel::Sender;
2use jsonrpc_core::Result as RpcError;
3use locker::SurfnetSvmLocker;
4use solana_account::Account;
5use solana_account_decoder::{encode_ui_account, UiAccount, UiAccountEncoding, UiDataSliceConfig};
6use solana_clock::Slot;
7use solana_commitment_config::CommitmentLevel;
8use solana_epoch_info::EpochInfo;
9use solana_pubkey::Pubkey;
10use solana_sdk::transaction::VersionedTransaction;
11use solana_signature::Signature;
12use solana_transaction_error::TransactionError;
13use solana_transaction_status::{EncodedConfirmedTransactionWithStatusMeta, TransactionStatus};
14use surfpool_types::TransactionMetadata;
15use svm::SurfnetSvm;
16
17use crate::error::{SurfpoolError, SurfpoolResult};
18
19pub mod locker;
20pub mod remote;
21pub mod svm;
22
23pub const FINALIZATION_SLOT_THRESHOLD: u64 = 31;
24pub const SLOTS_PER_EPOCH: u64 = 432000;
25// #[cfg(clippy)]
26// const SUBGRAPH_PLUGIN_BYTES: &[u8] = &[0];
27
28// #[cfg(not(clippy))]
29// const SUBGRAPH_PLUGIN_BYTES: &[u8] =
30//     include_bytes!("../../../../target/release/libsurfpool_subgraph.dylib");
31
32pub type AccountFactory = Box<dyn Fn(SurfnetSvmLocker) -> GetAccountResult + Send + Sync>;
33
34pub enum GeyserEvent {
35    NewTransaction(VersionedTransaction, TransactionMetadata, Slot),
36}
37
38#[derive(Debug, Eq, PartialEq, Hash, Clone)]
39pub struct BlockIdentifier {
40    pub index: u64,
41    pub hash: String,
42}
43
44impl BlockIdentifier {
45    pub fn zero() -> Self {
46        Self::new(0, "")
47    }
48
49    pub fn new(index: u64, hash: &str) -> Self {
50        Self {
51            index,
52            hash: hash.to_string(),
53        }
54    }
55}
56
57pub struct BlockHeader {
58    pub hash: String,
59    pub previous_blockhash: String,
60    pub parent_slot: Slot,
61    pub block_time: i64,
62    pub block_height: u64,
63    pub signatures: Vec<Signature>,
64}
65
66#[derive(PartialEq, Eq, Clone)]
67pub enum SurfnetDataConnection {
68    Offline,
69    Connected(String, EpochInfo),
70}
71
72pub type SignatureSubscriptionData = (
73    SignatureSubscriptionType,
74    Sender<(Slot, Option<TransactionError>)>,
75);
76
77#[derive(Debug, Clone, PartialEq)]
78pub enum SignatureSubscriptionType {
79    Received,
80    Commitment(CommitmentLevel),
81}
82
83type DoUpdateSvm = bool;
84
85#[derive(Clone, Debug)]
86/// Represents the result of a get_account operation.
87pub enum GetAccountResult {
88    /// Represents that the account was not found.
89    None(Pubkey),
90    /// Represents that the account was found.
91    /// The `DoUpdateSvm` flag indicates whether the SVM should be updated after this account is found.
92    /// This is useful for cases where the account was fetched from a remote source and needs to be
93    /// updated in the SVM to reflect the latest state. However, when the account is found locally,
94    /// it likely does not need to be updated in the SVM.
95    FoundAccount(Pubkey, Account, DoUpdateSvm),
96    FoundProgramAccount((Pubkey, Account), (Pubkey, Option<Account>)),
97}
98
99impl GetAccountResult {
100    pub fn try_into_ui_account(
101        &self,
102        encoding: Option<UiAccountEncoding>,
103        data_slice: Option<UiDataSliceConfig>,
104    ) -> Option<UiAccount> {
105        match &self {
106            Self::None(_) => None,
107            Self::FoundAccount(pubkey, account, _)
108            | Self::FoundProgramAccount((pubkey, account), _) => Some(encode_ui_account(
109                pubkey,
110                account,
111                encoding.unwrap_or(UiAccountEncoding::Base64),
112                None,
113                data_slice,
114            )),
115        }
116    }
117
118    pub fn expected_data(&self) -> &Vec<u8> {
119        match &self {
120            Self::None(_) => unreachable!(),
121            Self::FoundAccount(_, account, _) | Self::FoundProgramAccount((_, account), _) => {
122                &account.data
123            }
124        }
125    }
126
127    pub fn apply_update<T>(&mut self, update: T) -> RpcError<()>
128    where
129        T: Fn(&mut Account) -> RpcError<()>,
130    {
131        match self {
132            Self::None(_) => unreachable!(),
133            Self::FoundAccount(_, ref mut account, ref mut do_update_account) => {
134                update(account)?;
135                *do_update_account = true;
136            }
137            Self::FoundProgramAccount((_, ref mut account), _) => {
138                update(account)?;
139            }
140        }
141        Ok(())
142    }
143
144    pub fn map_found_account(self) -> Result<Account, SurfpoolError> {
145        match self {
146            Self::None(pubkey) => Err(SurfpoolError::account_not_found(pubkey)),
147            Self::FoundAccount(_, account, _) => Ok(account),
148            Self::FoundProgramAccount((pubkey, _), _) => Err(SurfpoolError::invalid_account_data(
149                pubkey,
150                "account should not be executable",
151                None::<String>,
152            )),
153        }
154    }
155
156    pub fn map_account(self) -> SurfpoolResult<Account> {
157        match self {
158            Self::None(pubkey) => Err(SurfpoolError::account_not_found(pubkey)),
159            Self::FoundAccount(_, account, _) => Ok(account),
160            Self::FoundProgramAccount((_, account), _) => Ok(account),
161        }
162    }
163
164    pub fn is_none(&self) -> bool {
165        matches!(self, Self::None(_))
166    }
167
168    pub fn requires_update(&self) -> bool {
169        match self {
170            Self::None(_) => false,
171            Self::FoundAccount(_, _, do_update) => *do_update,
172            Self::FoundProgramAccount(_, _) => true,
173        }
174    }
175}
176
177impl From<GetAccountResult> for Result<Account, SurfpoolError> {
178    fn from(value: GetAccountResult) -> Self {
179        value.map_account()
180    }
181}
182
183impl SignatureSubscriptionType {
184    pub fn received() -> Self {
185        SignatureSubscriptionType::Received
186    }
187
188    pub fn processed() -> Self {
189        SignatureSubscriptionType::Commitment(CommitmentLevel::Processed)
190    }
191
192    pub fn confirmed() -> Self {
193        SignatureSubscriptionType::Commitment(CommitmentLevel::Confirmed)
194    }
195
196    pub fn finalized() -> Self {
197        SignatureSubscriptionType::Commitment(CommitmentLevel::Finalized)
198    }
199}
200
201pub enum GetTransactionResult {
202    None(Signature),
203    FoundTransaction(
204        Signature,
205        EncodedConfirmedTransactionWithStatusMeta,
206        TransactionStatus,
207    ),
208}
209
210impl GetTransactionResult {
211    pub fn found_transaction(
212        signature: Signature,
213        tx: EncodedConfirmedTransactionWithStatusMeta,
214        latest_absolute_slot: u64,
215    ) -> Self {
216        let status = TransactionStatus {
217            slot: tx.slot,
218            confirmations: Some((latest_absolute_slot - tx.slot) as usize),
219            status: tx.transaction.clone().meta.map_or(Ok(()), |m| m.status),
220            err: tx.transaction.clone().meta.and_then(|m| m.err),
221            confirmation_status: Some(
222                solana_transaction_status::TransactionConfirmationStatus::Confirmed,
223            ),
224        };
225
226        Self::FoundTransaction(signature, tx, status)
227    }
228
229    pub fn is_none(&self) -> bool {
230        matches!(self, Self::None(_))
231    }
232
233    pub fn map_found_transaction(&self) -> SurfpoolResult<TransactionStatus> {
234        match self {
235            Self::None(sig) => Err(SurfpoolError::transaction_not_found(sig)),
236            Self::FoundTransaction(_, _, status) => Ok(status.clone()),
237        }
238    }
239
240    pub fn map_some_transaction_status(&self) -> Option<TransactionStatus> {
241        match self {
242            Self::None(_) => None,
243            Self::FoundTransaction(_, _, status) => Some(status.clone()),
244        }
245    }
246}