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;
32pub 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)]
97pub enum GetAccountResult {
99 None(Pubkey),
101 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}