solana_block_decoder/block/
encoded_block.rs1use {
2 solana_clock::{
3 Slot,
4 UnixTimestamp,
5 },
6 solana_transaction::{
7 versioned::{
8 TransactionVersion,
9 VersionedTransaction,
10 },
11 },
12 solana_transaction_status_client_types::{
13 TransactionBinaryEncoding,
14 Rewards,
15 UiTransaction,
16 UiAccountsList,
17 UiTransactionStatusMeta,
18 },
19 serde_derive::{Serialize,Deserialize},
20 base64::{Engine, prelude::BASE64_STANDARD},
21};
22
23#[derive(Debug, PartialEq, Serialize, Deserialize)]
24#[serde(rename_all = "camelCase")]
25pub struct EncodedConfirmedBlock {
26 pub previous_blockhash: String,
27 pub blockhash: String,
28 pub parent_slot: Slot,
29 pub transactions: Vec<EncodedTransactionWithStatusMeta>,
30 pub rewards: Rewards,
31 pub num_partitions: Option<u64>,
32 pub block_time: Option<UnixTimestamp>,
33 pub block_height: Option<u64>,
34}
35
36#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct EncodedTransactionWithStatusMeta {
39 pub transaction: EncodedTransaction,
40 pub meta: Option<UiTransactionStatusMeta>,
41 #[serde(default, skip_serializing_if = "Option::is_none")]
42 pub version: Option<TransactionVersion>,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(rename_all = "camelCase", untagged)]
47pub enum EncodedTransaction {
48 LegacyBinary(String), Binary(String, TransactionBinaryEncoding),
50 Json(UiTransaction),
51 Accounts(UiAccountsList),
52}
53
54impl EncodedTransaction {
55 pub fn decode(&self) -> Option<VersionedTransaction> {
56 let (blob, encoding) = match self {
57 Self::Json(_) | Self::Accounts(_) => return None,
58 Self::LegacyBinary(blob) => (blob, TransactionBinaryEncoding::Base58),
59 Self::Binary(blob, encoding) => (blob, *encoding),
60 };
61
62 let transaction: Option<VersionedTransaction> = match encoding {
63 TransactionBinaryEncoding::Base58 => bs58::decode(blob)
64 .into_vec()
65 .ok()
66 .and_then(|bytes| bincode::deserialize(&bytes).ok()),
67 TransactionBinaryEncoding::Base64 => BASE64_STANDARD.decode(blob)
68 .ok()
69 .and_then(|bytes| bincode::deserialize(&bytes).ok()),
70 };
71
72 transaction.filter(|transaction| {
73 transaction
74 .sanitize(
75 )
77 .is_ok()
78 })
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use {super::*, serde_json::json};
85
86 #[test]
87 fn test_decode_invalid_transaction() {
88 let unsanitary_transaction = EncodedTransaction::Binary(
90 "ju9xZWuDBX4pRxX2oZkTjxU5jB4SSTgEGhX8bQ8PURNzyzqKMPPpNvWihx8zUe\
91 FfrbVNoAaEsNKZvGzAnTDy5bhNT9kt6KFCTBixpvrLCzg4M5UdFUQYrn1gdgjX\
92 pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\
93 hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK"
94 .to_string(),
95 TransactionBinaryEncoding::Base58,
96 );
97 assert!(unsanitary_transaction.decode().is_none());
98 }
99
100}