surfpool_core/surfnet/
mod.rs

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