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::{UiAccount, UiAccountEncoding};
8use solana_client::{rpc_config::RpcTransactionLogsFilter, rpc_response::RpcLogsResponse};
9use solana_clock::Slot;
10use solana_commitment_config::CommitmentLevel;
11use solana_epoch_info::EpochInfo;
12use solana_pubkey::Pubkey;
13use solana_signature::Signature;
14use solana_transaction_error::TransactionError;
15use solana_transaction_status::{EncodedConfirmedTransactionWithStatusMeta, TransactionStatus};
16use svm::SurfnetSvm;
17
18use crate::{
19    error::{SurfpoolError, SurfpoolResult},
20    types::{GeyserAccountUpdate, TransactionWithStatusMeta},
21};
22
23pub mod locker;
24pub mod remote;
25pub mod svm;
26
27pub const SURFPOOL_IDENTITY_PUBKEY: Pubkey =
28    Pubkey::from_str_const("SUrFPooLSUrFPooLSUrFPooLSUrFPooLSUrFPooLSUr");
29pub const FINALIZATION_SLOT_THRESHOLD: u64 = 31;
30pub const SLOTS_PER_EPOCH: u64 = 432000;
31
32pub type AccountFactory = Box<dyn Fn(SurfnetSvmLocker) -> GetAccountResult + Send + Sync>;
33
34pub enum GeyserEvent {
35    NotifyTransaction(TransactionWithStatusMeta),
36    UpdateAccount(GeyserAccountUpdate),
37    // todo: add more events
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
83pub type LogsSubscriptionData = (
84    CommitmentLevel,
85    RpcTransactionLogsFilter,
86    Sender<(Slot, RpcLogsResponse)>,
87);
88
89#[derive(Debug, Clone, PartialEq)]
90pub enum SignatureSubscriptionType {
91    Received,
92    Commitment(CommitmentLevel),
93}
94
95type DoUpdateSvm = bool;
96
97#[derive(Clone, Debug)]
98/// Represents the result of a get_account operation.
99pub enum GetAccountResult {
100    /// Represents that the account was not found.
101    None(Pubkey),
102    /// Represents that the account was found.
103    /// The `DoUpdateSvm` flag indicates whether the SVM should be updated after this account is found.
104    /// This is useful for cases where the account was fetched from a remote source and needs to be
105    /// updated in the SVM to reflect the latest state. However, when the account is found locally,
106    /// it likely does not need to be updated in the SVM.
107    FoundAccount(Pubkey, Account, DoUpdateSvm),
108    FoundProgramAccount((Pubkey, Account), (Pubkey, Option<Account>)),
109    FoundTokenAccount((Pubkey, Account), (Pubkey, Option<Account>)),
110}
111
112impl GetAccountResult {
113    pub fn expected_data(&self) -> &Vec<u8> {
114        match &self {
115            Self::None(_) => unreachable!(),
116            Self::FoundAccount(_, account, _)
117            | Self::FoundProgramAccount((_, account), _)
118            | Self::FoundTokenAccount((_, account), _) => &account.data,
119        }
120    }
121
122    pub fn apply_update<T>(&mut self, update: T) -> RpcError<()>
123    where
124        T: Fn(&mut Account) -> RpcError<()>,
125    {
126        match self {
127            Self::None(_) => unreachable!(),
128            Self::FoundAccount(_, account, do_update_account) => {
129                update(account)?;
130                *do_update_account = true;
131            }
132            Self::FoundProgramAccount((_, account), _) => {
133                update(account)?;
134            }
135            Self::FoundTokenAccount((_, account), _) => {
136                update(account)?;
137            }
138        }
139        Ok(())
140    }
141
142    pub fn map_account(self) -> SurfpoolResult<Account> {
143        match self {
144            Self::None(pubkey) => Err(SurfpoolError::account_not_found(pubkey)),
145            Self::FoundAccount(_, account, _)
146            | Self::FoundProgramAccount((_, account), _)
147            | Self::FoundTokenAccount((_, account), _) => Ok(account),
148        }
149    }
150
151    pub fn map_account_with_token_data(
152        self,
153    ) -> Option<((Pubkey, Account), Option<(Pubkey, Option<Account>)>)> {
154        match self {
155            Self::None(_) => None,
156            Self::FoundAccount(pubkey, account, _) => Some(((pubkey, account), None)),
157            Self::FoundProgramAccount((pubkey, account), _) => Some(((pubkey, account), None)),
158            Self::FoundTokenAccount((pubkey, account), token_data) => {
159                Some(((pubkey, account), Some(token_data)))
160            }
161        }
162    }
163
164    pub const fn is_none(&self) -> bool {
165        matches!(self, Self::None(_))
166    }
167
168    pub const fn requires_update(&self) -> bool {
169        match self {
170            Self::None(_) => false,
171            Self::FoundAccount(_, _, do_update) => *do_update,
172            Self::FoundProgramAccount(_, _) => true,
173            Self::FoundTokenAccount(_, _) => true,
174        }
175    }
176}
177
178impl From<GetAccountResult> for Result<Account, SurfpoolError> {
179    fn from(value: GetAccountResult) -> Self {
180        value.map_account()
181    }
182}
183
184impl SignatureSubscriptionType {
185    pub const fn received() -> Self {
186        SignatureSubscriptionType::Received
187    }
188
189    pub const fn processed() -> Self {
190        SignatureSubscriptionType::Commitment(CommitmentLevel::Processed)
191    }
192
193    pub const fn confirmed() -> Self {
194        SignatureSubscriptionType::Commitment(CommitmentLevel::Confirmed)
195    }
196
197    pub const fn finalized() -> Self {
198        SignatureSubscriptionType::Commitment(CommitmentLevel::Finalized)
199    }
200}
201
202pub enum GetTransactionResult {
203    None(Signature),
204    FoundTransaction(
205        Signature,
206        EncodedConfirmedTransactionWithStatusMeta,
207        TransactionStatus,
208    ),
209}
210
211impl GetTransactionResult {
212    pub fn found_transaction(
213        signature: Signature,
214        tx: EncodedConfirmedTransactionWithStatusMeta,
215        latest_absolute_slot: u64,
216    ) -> Self {
217        let is_finalized = latest_absolute_slot >= tx.slot + FINALIZATION_SLOT_THRESHOLD;
218        let (confirmation_status, confirmations) = if is_finalized {
219            (
220                Some(solana_transaction_status::TransactionConfirmationStatus::Finalized),
221                None,
222            )
223        } else {
224            (
225                Some(solana_transaction_status::TransactionConfirmationStatus::Confirmed),
226                Some((latest_absolute_slot - tx.slot) as usize),
227            )
228        };
229        let status = TransactionStatus {
230            slot: tx.slot,
231            confirmations,
232            status: tx.transaction.clone().meta.map_or(Ok(()), |m| m.status),
233            err: tx.transaction.clone().meta.and_then(|m| m.err),
234            confirmation_status,
235        };
236
237        Self::FoundTransaction(signature, tx, status)
238    }
239
240    pub const fn is_none(&self) -> bool {
241        matches!(self, Self::None(_))
242    }
243
244    pub fn map_found_transaction(&self) -> SurfpoolResult<TransactionStatus> {
245        match self {
246            Self::None(sig) => Err(SurfpoolError::transaction_not_found(sig)),
247            Self::FoundTransaction(_, _, status) => Ok(status.clone()),
248        }
249    }
250
251    pub fn map_some_transaction_status(&self) -> Option<TransactionStatus> {
252        match self {
253            Self::None(_) => None,
254            Self::FoundTransaction(_, _, status) => Some(status.clone()),
255        }
256    }
257}