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 }
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)]
98pub enum GetAccountResult {
100 None(Pubkey),
102 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}