solana_block_decoder/block/
versioned_block.rs

1
2use {
3    crate::{
4        block::{
5            confirmed_block::{
6                ConfirmedBlock,
7                TransactionWithStatusMeta
8            },
9        },
10        transaction::{
11            versioned_transaction::{VersionedTransactionWithStatusMeta, VersionedTransaction},
12            tx_status_meta::TransactionStatusMeta,
13        },
14        message::{
15            versioned_message::VersionedMessage,
16        },
17        address::{
18            loaded_addresses::LoadedAddresses,
19        },
20    },
21    solana_clock::{
22        Slot,
23        UnixTimestamp,
24    },
25    solana_transaction_status_client_types::{
26        Rewards,
27    },
28    thiserror::Error,
29};
30
31
32// Confirmed block with type guarantees that transaction metadata
33// is always present. Used for uploading to HBase.
34#[derive(Clone, Debug, PartialEq)]
35pub struct VersionedConfirmedBlock {
36    pub previous_blockhash: String,
37    pub blockhash: String,
38    pub parent_slot: Slot,
39    pub transactions: Vec<VersionedTransactionWithStatusMeta>,
40    pub rewards: Rewards,
41    pub num_partitions: Option<u64>,
42    pub block_time: Option<UnixTimestamp>,
43    pub block_height: Option<u64>,
44}
45
46
47impl TryFrom<ConfirmedBlock> for VersionedConfirmedBlock {
48    type Error = ConvertBlockError;
49
50    fn try_from(block: ConfirmedBlock) -> Result<Self, Self::Error> {
51        let expected_transaction_count = block.transactions.len();
52
53        let add_empty_tx_metadata_if_missing = crate::add_empty_tx_metadata_if_missing();
54
55        let txs: Vec<_> = block
56            .transactions
57            .into_iter()
58            .filter_map(|tx| {
59                match tx {
60                    TransactionWithStatusMeta::MissingMetadata(transaction) => {
61                        if add_empty_tx_metadata_if_missing {
62                            // Build a complete transaction with empty/default metadata
63                            let versioned_transaction = VersionedTransaction {
64                                signatures: transaction.signatures,
65                                message: VersionedMessage::Legacy(transaction.message),
66                            };
67                            let default_meta = TransactionStatusMeta {
68                                status: Ok(()),
69                                fee: 0,
70                                pre_balances: vec![],
71                                post_balances: vec![],
72                                inner_instructions: None,
73                                log_messages: None,
74                                pre_token_balances: None,
75                                post_token_balances: None,
76                                rewards: None,
77                                loaded_addresses: LoadedAddresses::default(),
78                                return_data: None,
79                                compute_units_consumed: None,
80                            };
81                            Some(VersionedTransactionWithStatusMeta {
82                                transaction: versioned_transaction,
83                                meta: default_meta,
84                            })
85                        } else {
86                            // Drop the transaction if empty metadata is not allowed
87                            None
88                        }
89                    }
90                    TransactionWithStatusMeta::Complete(tx) => Some(tx),
91                }
92            })
93            .collect();
94
95        if txs.len() != expected_transaction_count {
96            return Err(ConvertBlockError::TransactionsMissing(
97                expected_transaction_count,
98                txs.len(),
99            ));
100        }
101
102        Ok(Self {
103            previous_blockhash: block.previous_blockhash,
104            blockhash: block.blockhash,
105            parent_slot: block.parent_slot,
106            transactions: txs,
107            rewards: block.rewards,
108            num_partitions: block.num_partitions,
109            block_time: block.block_time,
110            block_height: block.block_height,
111        })
112    }
113}
114
115#[derive(Debug, Error)]
116pub enum ConvertBlockError {
117    #[error("transactions missing after converted, before: {0}, after: {1}")]
118    TransactionsMissing(usize, usize),
119}
120
121impl From<VersionedConfirmedBlock> for solana_transaction_status::VersionedConfirmedBlock {
122    fn from(block: VersionedConfirmedBlock) -> Self {
123        Self {
124            previous_blockhash: block.previous_blockhash,
125            blockhash: block.blockhash,
126            parent_slot: block.parent_slot,
127            transactions: block.transactions.into_iter().map(Into::into).collect(),
128            rewards: block.rewards,
129            num_partitions: block.num_partitions,
130            block_time: block.block_time,
131            block_height: block.block_height,
132        }
133    }
134}