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