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                                cost_units: None,
81                            };
82                            Some(VersionedTransactionWithStatusMeta {
83                                transaction: versioned_transaction,
84                                meta: default_meta,
85                            })
86                        } else {
87                            // Drop the transaction if empty metadata is not allowed
88                            None
89                        }
90                    }
91                    TransactionWithStatusMeta::Complete(tx) => Some(tx),
92                }
93            })
94            .collect();
95
96        if txs.len() != expected_transaction_count {
97            return Err(ConvertBlockError::TransactionsMissing(
98                expected_transaction_count,
99                txs.len(),
100            ));
101        }
102
103        Ok(Self {
104            previous_blockhash: block.previous_blockhash,
105            blockhash: block.blockhash,
106            parent_slot: block.parent_slot,
107            transactions: txs,
108            rewards: block.rewards,
109            num_partitions: block.num_partitions,
110            block_time: block.block_time,
111            block_height: block.block_height,
112        })
113    }
114}
115
116#[derive(Debug, Error)]
117pub enum ConvertBlockError {
118    #[error("transactions missing after converted, before: {0}, after: {1}")]
119    TransactionsMissing(usize, usize),
120}
121
122impl From<VersionedConfirmedBlock> for solana_transaction_status::VersionedConfirmedBlock {
123    fn from(block: VersionedConfirmedBlock) -> Self {
124        Self {
125            previous_blockhash: block.previous_blockhash,
126            blockhash: block.blockhash,
127            parent_slot: block.parent_slot,
128            transactions: block.transactions.into_iter().map(Into::into).collect(),
129            rewards: block.rewards,
130            num_partitions: block.num_partitions,
131            block_time: block.block_time,
132            block_height: block.block_height,
133        }
134    }
135}