solana_storage_reader/
lib.rs

1
2use {
3    async_trait::async_trait,
4    log::*,
5    serde::{Deserialize, Serialize},
6    solana_sdk::{
7        clock::{Slot, UnixTimestamp},
8        pubkey::Pubkey,
9        signature::Signature,
10        message::v0::LoadedAddresses,
11        deserialize_utils::default_on_eof,
12        transaction::{TransactionError, VersionedTransaction},
13    },
14    solana_transaction_status::{
15        ConfirmedBlock,
16        ConfirmedTransactionStatusWithSignature,
17        ConfirmedTransactionWithStatusMeta,
18        TransactionStatus,
19        TransactionStatusMeta,
20        TransactionWithStatusMeta,
21        VersionedTransactionWithStatusMeta,
22        TransactionByAddrInfo,
23        Reward,
24    },
25    std::{
26        boxed::Box,
27    },
28    thiserror::Error,
29    tokio::task::JoinError,
30};
31
32#[macro_use]
33extern crate serde_derive;
34
35pub mod compression;
36
37#[derive(Debug, Error)]
38pub enum Error {
39    #[error("Storage Error: {0}")]
40    StorageBackendError(Box<dyn std::error::Error + Send>),
41
42    #[error("I/O Error: {0}")]
43    IoError(std::io::Error),
44
45    #[error("Transaction encoded is not supported")]
46    UnsupportedTransactionEncoding,
47
48    #[error("Block not found: {0}")]
49    BlockNotFound(Slot),
50
51    #[error("Signature not found")]
52    SignatureNotFound,
53
54    #[error("tokio error")]
55    TokioJoinError(JoinError),
56
57    #[error("Cache Error: {0}")]
58    CacheError(String),
59}
60
61impl From<std::io::Error> for Error {
62    fn from(err: std::io::Error) -> Self {
63        Self::IoError(err)
64    }
65}
66
67pub type Result<T> = std::result::Result<T, Error>;
68
69
70// A serialized `StoredConfirmedBlock` is stored in the `block` table
71//
72// StoredConfirmedBlock holds the same contents as ConfirmedBlock, but is slightly compressed and avoids
73// some serde JSON directives that cause issues with bincode
74//
75// Note: in order to continue to support old bincode-serialized bigtable entries, if new fields are
76// added to ConfirmedBlock, they must either be excluded or set to `default_on_eof` here
77//
78#[derive(Serialize, Deserialize)]
79pub struct StoredConfirmedBlock {
80    previous_blockhash: String,
81    blockhash: String,
82    parent_slot: Slot,
83    transactions: Vec<StoredConfirmedBlockTransaction>,
84    rewards: StoredConfirmedBlockRewards,
85    // num_partitions: Option<u64>,
86    block_time: Option<UnixTimestamp>,
87    #[serde(deserialize_with = "default_on_eof")]
88    block_height: Option<u64>,
89}
90
91#[derive(Serialize, Deserialize)]
92pub struct StoredConfirmedTransactionWithStatusMeta {
93    pub slot: Slot,
94    pub tx_with_meta: StoredConfirmedBlockTransaction,
95    pub block_time: Option<UnixTimestamp>,
96}
97
98impl From<ConfirmedTransactionWithStatusMeta> for StoredConfirmedTransactionWithStatusMeta {
99    fn from(value: ConfirmedTransactionWithStatusMeta) -> Self {
100        Self {
101            slot: value.slot,
102            tx_with_meta: value.tx_with_meta.into(),
103            block_time: value.block_time,
104        }
105    }
106}
107
108impl From<StoredConfirmedTransactionWithStatusMeta> for ConfirmedTransactionWithStatusMeta {
109    fn from(value: StoredConfirmedTransactionWithStatusMeta) -> Self {
110        Self {
111            slot: value.slot,
112            tx_with_meta: value.tx_with_meta.into(),
113            block_time: value.block_time,
114        }
115    }
116}
117
118impl From<ConfirmedBlock> for StoredConfirmedBlock {
119    fn from(confirmed_block: ConfirmedBlock) -> Self {
120        let ConfirmedBlock {
121            previous_blockhash,
122            blockhash,
123            parent_slot,
124            transactions,
125            rewards,
126            num_partitions: _num_partitions,
127            block_time,
128            block_height,
129        } = confirmed_block;
130
131        Self {
132            previous_blockhash,
133            blockhash,
134            parent_slot,
135            transactions: transactions.into_iter().map(|tx| tx.into()).collect(),
136            rewards: rewards.into_iter().map(|reward| reward.into()).collect(),
137            block_time,
138            block_height,
139        }
140    }
141}
142
143impl From<StoredConfirmedBlock> for ConfirmedBlock {
144    fn from(confirmed_block: StoredConfirmedBlock) -> Self {
145        let StoredConfirmedBlock {
146            previous_blockhash,
147            blockhash,
148            parent_slot,
149            transactions,
150            rewards,
151            block_time,
152            block_height,
153        } = confirmed_block;
154
155        Self {
156            previous_blockhash,
157            blockhash,
158            parent_slot,
159            transactions: transactions.into_iter().map(|tx| tx.into()).collect(),
160            rewards: rewards.into_iter().map(|reward| reward.into()).collect(),
161            num_partitions: None,
162            block_time,
163            block_height,
164        }
165    }
166}
167
168#[derive(Serialize, Deserialize)]
169pub struct StoredConfirmedBlockTransaction {
170    transaction: VersionedTransaction,
171    meta: Option<StoredConfirmedBlockTransactionStatusMeta>,
172}
173
174impl From<TransactionWithStatusMeta> for StoredConfirmedBlockTransaction {
175    fn from(value: TransactionWithStatusMeta) -> Self {
176        match value {
177            TransactionWithStatusMeta::MissingMetadata(transaction) => Self {
178                transaction: VersionedTransaction::from(transaction),
179                meta: None,
180            },
181            TransactionWithStatusMeta::Complete(VersionedTransactionWithStatusMeta {
182                                                    transaction,
183                                                    meta,
184                                                }) => Self {
185                transaction,
186                meta: Some(meta.into()),
187            },
188        }
189    }
190}
191
192impl From<StoredConfirmedBlockTransaction> for TransactionWithStatusMeta {
193    fn from(tx_with_meta: StoredConfirmedBlockTransaction) -> Self {
194        let StoredConfirmedBlockTransaction { transaction, meta } = tx_with_meta;
195        match meta {
196            None => Self::MissingMetadata(
197                transaction
198                    .into_legacy_transaction()
199                    .expect("versioned transactions always have meta"),
200            ),
201            Some(meta) => Self::Complete(VersionedTransactionWithStatusMeta {
202                transaction,
203                meta: meta.into(),
204            }),
205        }
206    }
207}
208
209#[derive(Serialize, Deserialize)]
210pub struct StoredConfirmedBlockTransactionStatusMeta {
211    err: Option<TransactionError>,
212    fee: u64,
213    pre_balances: Vec<u64>,
214    post_balances: Vec<u64>,
215}
216
217impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
218    fn from(value: StoredConfirmedBlockTransactionStatusMeta) -> Self {
219        let StoredConfirmedBlockTransactionStatusMeta {
220            err,
221            fee,
222            pre_balances,
223            post_balances,
224        } = value;
225        let status = match &err {
226            None => Ok(()),
227            Some(err) => Err(err.clone()),
228        };
229        Self {
230            status,
231            fee,
232            pre_balances,
233            post_balances,
234            inner_instructions: None,
235            log_messages: None,
236            pre_token_balances: None,
237            post_token_balances: None,
238            rewards: None,
239            loaded_addresses: LoadedAddresses::default(),
240            return_data: None,
241            compute_units_consumed: None,
242        }
243    }
244}
245
246impl From<TransactionStatusMeta> for StoredConfirmedBlockTransactionStatusMeta {
247    fn from(value: TransactionStatusMeta) -> Self {
248        let TransactionStatusMeta {
249            status,
250            fee,
251            pre_balances,
252            post_balances,
253            ..
254        } = value;
255        Self {
256            err: status.err(),
257            fee,
258            pre_balances,
259            post_balances,
260        }
261    }
262}
263
264pub type StoredConfirmedBlockRewards = Vec<StoredConfirmedBlockReward>;
265
266#[derive(Serialize, Deserialize)]
267pub struct StoredConfirmedBlockReward {
268    pubkey: String,
269    lamports: i64,
270}
271
272impl From<StoredConfirmedBlockReward> for Reward {
273    fn from(value: StoredConfirmedBlockReward) -> Self {
274        let StoredConfirmedBlockReward { pubkey, lamports } = value;
275        Self {
276            pubkey,
277            lamports,
278            post_balance: 0,
279            reward_type: None,
280            commission: None,
281        }
282    }
283}
284
285impl From<Reward> for StoredConfirmedBlockReward {
286    fn from(value: Reward) -> Self {
287        let Reward {
288            pubkey, lamports, ..
289        } = value;
290        Self { pubkey, lamports }
291    }
292}
293
294#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
295pub struct LegacyTransactionByAddrInfo {
296    pub signature: Signature,          // The transaction signature
297    pub err: Option<TransactionError>, // None if the transaction executed successfully
298    pub index: u32,                    // Where the transaction is located in the block
299    pub memo: Option<String>,          // Transaction memo
300}
301
302impl From<LegacyTransactionByAddrInfo> for TransactionByAddrInfo {
303    fn from(legacy: LegacyTransactionByAddrInfo) -> Self {
304        let LegacyTransactionByAddrInfo {
305            signature,
306            err,
307            index,
308            memo,
309        } = legacy;
310
311        Self {
312            signature,
313            err,
314            index,
315            memo,
316            block_time: None,
317        }
318    }
319}
320
321#[async_trait]
322pub trait LedgerStorageAdapter: Send + Sync {
323    async fn get_first_available_block(&self) -> Result<Option<Slot>>;
324
325    async fn get_confirmed_blocks(&self, start_slot: Slot, limit: usize) -> Result<Vec<Slot>>;
326
327    async fn get_confirmed_block(&self, slot: Slot, use_cache: bool) -> Result<ConfirmedBlock>;
328
329    async fn get_signature_status(&self, signature: &Signature) -> Result<TransactionStatus>;
330
331    async fn get_full_transaction(
332        &self,
333        signature: &Signature,
334    ) -> Result<Option<ConfirmedTransactionWithStatusMeta>>;
335
336    async fn get_confirmed_transaction(
337        &self,
338        signature: &Signature,
339    ) -> Result<Option<ConfirmedTransactionWithStatusMeta>>;
340
341    async fn get_confirmed_signatures_for_address(
342        &self,
343        address: &Pubkey,
344        before_signature: Option<&Signature>,
345        until_signature: Option<&Signature>,
346        limit: usize,
347    ) -> Result<Vec<(ConfirmedTransactionStatusWithSignature, u32)>>;
348
349    async fn get_latest_stored_slot(&self) -> Result<Slot>;
350
351    fn clone_box(&self) -> Box<dyn LedgerStorageAdapter>;
352}