yellowstone_grpc_proto/
lib.rs

1#![allow(clippy::large_enum_variant)]
2
3pub mod geyser {
4    #![allow(clippy::clone_on_ref_ptr)]
5    #![allow(clippy::missing_const_for_fn)]
6
7    #[cfg(feature = "tonic")]
8    include!(concat!(env!("OUT_DIR"), "/geyser.rs"));
9    #[cfg(not(feature = "tonic"))]
10    include!(concat!(env!("OUT_DIR"), "/no-tonic/geyser.rs"));
11}
12
13pub mod solana {
14    #![allow(clippy::missing_const_for_fn)]
15
16    pub mod storage {
17        pub mod confirmed_block {
18            #[cfg(feature = "tonic")]
19            include!(concat!(
20                env!("OUT_DIR"),
21                "/solana.storage.confirmed_block.rs"
22            ));
23            #[cfg(not(feature = "tonic"))]
24            include!(concat!(
25                env!("OUT_DIR"),
26                "/no-tonic/solana.storage.confirmed_block.rs"
27            ));
28        }
29    }
30}
31
32pub mod prelude {
33    pub use super::{geyser::*, solana::storage::confirmed_block::*};
34}
35
36#[cfg(feature = "tonic")]
37pub use tonic;
38pub use {prost, prost_types};
39
40#[cfg(feature = "plugin")]
41pub mod plugin;
42
43#[cfg(feature = "convert")]
44pub mod convert_to {
45    use {
46        super::prelude as proto,
47        solana_clock::UnixTimestamp,
48        solana_message::{
49            compiled_instruction::CompiledInstruction, v0::MessageAddressTableLookup,
50            MessageHeader, VersionedMessage,
51        },
52        solana_pubkey::Pubkey,
53        solana_signature::Signature,
54        solana_transaction::versioned::VersionedTransaction,
55        solana_transaction_context::TransactionReturnData,
56        solana_transaction_error::TransactionError,
57        solana_transaction_status::{
58            InnerInstruction, InnerInstructions, Reward, RewardType, TransactionStatusMeta,
59            TransactionTokenBalance,
60        },
61    };
62
63    pub fn create_transaction(tx: &VersionedTransaction) -> proto::Transaction {
64        proto::Transaction {
65            signatures: tx
66                .signatures
67                .iter()
68                .map(|signature| <Signature as AsRef<[u8]>>::as_ref(signature).into())
69                .collect(),
70            message: Some(create_message(&tx.message)),
71        }
72    }
73
74    pub fn create_message(message: &VersionedMessage) -> proto::Message {
75        match message {
76            VersionedMessage::Legacy(message) => proto::Message {
77                header: Some(create_header(&message.header)),
78                account_keys: create_pubkeys(&message.account_keys),
79                recent_blockhash: message.recent_blockhash.to_bytes().into(),
80                instructions: create_instructions(&message.instructions),
81                versioned: false,
82                address_table_lookups: vec![],
83            },
84            VersionedMessage::V0(message) => proto::Message {
85                header: Some(create_header(&message.header)),
86                account_keys: create_pubkeys(&message.account_keys),
87                recent_blockhash: message.recent_blockhash.to_bytes().into(),
88                instructions: create_instructions(&message.instructions),
89                versioned: true,
90                address_table_lookups: create_lookups(&message.address_table_lookups),
91            },
92        }
93    }
94
95    pub const fn create_header(header: &MessageHeader) -> proto::MessageHeader {
96        proto::MessageHeader {
97            num_required_signatures: header.num_required_signatures as u32,
98            num_readonly_signed_accounts: header.num_readonly_signed_accounts as u32,
99            num_readonly_unsigned_accounts: header.num_readonly_unsigned_accounts as u32,
100        }
101    }
102
103    pub fn create_pubkeys(pubkeys: &[Pubkey]) -> Vec<Vec<u8>> {
104        pubkeys
105            .iter()
106            .map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
107            .collect()
108    }
109
110    pub fn create_instructions(ixs: &[CompiledInstruction]) -> Vec<proto::CompiledInstruction> {
111        ixs.iter().map(create_instruction).collect()
112    }
113
114    pub fn create_instruction(ix: &CompiledInstruction) -> proto::CompiledInstruction {
115        proto::CompiledInstruction {
116            program_id_index: ix.program_id_index as u32,
117            accounts: ix.accounts.clone(),
118            data: ix.data.clone(),
119        }
120    }
121
122    pub fn create_lookups(
123        lookups: &[MessageAddressTableLookup],
124    ) -> Vec<proto::MessageAddressTableLookup> {
125        lookups.iter().map(create_lookup).collect()
126    }
127
128    pub fn create_lookup(lookup: &MessageAddressTableLookup) -> proto::MessageAddressTableLookup {
129        proto::MessageAddressTableLookup {
130            account_key: <Pubkey as AsRef<[u8]>>::as_ref(&lookup.account_key).into(),
131            writable_indexes: lookup.writable_indexes.clone(),
132            readonly_indexes: lookup.readonly_indexes.clone(),
133        }
134    }
135
136    pub fn create_transaction_meta(meta: &TransactionStatusMeta) -> proto::TransactionStatusMeta {
137        let TransactionStatusMeta {
138            status,
139            fee,
140            pre_balances,
141            post_balances,
142            inner_instructions,
143            log_messages,
144            pre_token_balances,
145            post_token_balances,
146            rewards,
147            loaded_addresses,
148            return_data,
149            compute_units_consumed,
150            cost_units,
151        } = meta;
152        let err = create_transaction_error(status);
153        let inner_instructions_none = inner_instructions.is_none();
154        let inner_instructions = inner_instructions
155            .as_deref()
156            .map(create_inner_instructions_vec)
157            .unwrap_or_default();
158        let log_messages_none = log_messages.is_none();
159        let log_messages = log_messages.clone().unwrap_or_default();
160        let pre_token_balances = pre_token_balances
161            .as_deref()
162            .map(create_token_balances)
163            .unwrap_or_default();
164        let post_token_balances = post_token_balances
165            .as_deref()
166            .map(create_token_balances)
167            .unwrap_or_default();
168        let rewards = rewards.as_deref().map(create_rewards).unwrap_or_default();
169        let loaded_writable_addresses = create_pubkeys(&loaded_addresses.writable);
170        let loaded_readonly_addresses = create_pubkeys(&loaded_addresses.readonly);
171
172        proto::TransactionStatusMeta {
173            err,
174            fee: *fee,
175            pre_balances: pre_balances.clone(),
176            post_balances: post_balances.clone(),
177            inner_instructions,
178            inner_instructions_none,
179            log_messages,
180            log_messages_none,
181            pre_token_balances,
182            post_token_balances,
183            rewards,
184            loaded_writable_addresses,
185            loaded_readonly_addresses,
186            return_data: return_data.as_ref().map(create_return_data),
187            return_data_none: return_data.is_none(),
188            compute_units_consumed: *compute_units_consumed,
189            cost_units: *cost_units,
190        }
191    }
192
193    pub fn create_transaction_error(
194        status: &Result<(), TransactionError>,
195    ) -> Option<proto::TransactionError> {
196        match status {
197            Ok(()) => None,
198            Err(err) => Some(proto::TransactionError {
199                err: bincode::serialize(&err).expect("transaction error to serialize to bytes"),
200            }),
201        }
202    }
203
204    pub fn create_inner_instructions_vec(
205        ixs: &[InnerInstructions],
206    ) -> Vec<proto::InnerInstructions> {
207        ixs.iter().map(create_inner_instructions).collect()
208    }
209
210    pub fn create_inner_instructions(instructions: &InnerInstructions) -> proto::InnerInstructions {
211        proto::InnerInstructions {
212            index: instructions.index as u32,
213            instructions: create_inner_instruction_vec(&instructions.instructions),
214        }
215    }
216
217    pub fn create_inner_instruction_vec(ixs: &[InnerInstruction]) -> Vec<proto::InnerInstruction> {
218        ixs.iter().map(create_inner_instruction).collect()
219    }
220
221    pub fn create_inner_instruction(instruction: &InnerInstruction) -> proto::InnerInstruction {
222        proto::InnerInstruction {
223            program_id_index: instruction.instruction.program_id_index as u32,
224            accounts: instruction.instruction.accounts.clone(),
225            data: instruction.instruction.data.clone(),
226            stack_height: instruction.stack_height,
227        }
228    }
229
230    pub fn create_token_balances(balances: &[TransactionTokenBalance]) -> Vec<proto::TokenBalance> {
231        balances.iter().map(create_token_balance).collect()
232    }
233
234    pub fn create_token_balance(balance: &TransactionTokenBalance) -> proto::TokenBalance {
235        proto::TokenBalance {
236            account_index: balance.account_index as u32,
237            mint: balance.mint.clone(),
238            ui_token_amount: Some(proto::UiTokenAmount {
239                ui_amount: balance.ui_token_amount.ui_amount.unwrap_or_default(),
240                decimals: balance.ui_token_amount.decimals as u32,
241                amount: balance.ui_token_amount.amount.clone(),
242                ui_amount_string: balance.ui_token_amount.ui_amount_string.clone(),
243            }),
244            owner: balance.owner.clone(),
245            program_id: balance.program_id.clone(),
246        }
247    }
248
249    pub fn create_rewards_obj(rewards: &[Reward], num_partitions: Option<u64>) -> proto::Rewards {
250        proto::Rewards {
251            rewards: create_rewards(rewards),
252            num_partitions: num_partitions.map(create_num_partitions),
253        }
254    }
255
256    pub fn create_rewards(rewards: &[Reward]) -> Vec<proto::Reward> {
257        rewards.iter().map(create_reward).collect()
258    }
259
260    pub fn create_reward(reward: &Reward) -> proto::Reward {
261        proto::Reward {
262            pubkey: reward.pubkey.clone(),
263            lamports: reward.lamports,
264            post_balance: reward.post_balance,
265            reward_type: create_reward_type(reward.reward_type) as i32,
266            commission: reward.commission.map(|c| c.to_string()).unwrap_or_default(),
267        }
268    }
269
270    pub const fn create_reward_type(reward_type: Option<RewardType>) -> proto::RewardType {
271        match reward_type {
272            None => proto::RewardType::Unspecified,
273            Some(RewardType::Fee) => proto::RewardType::Fee,
274            Some(RewardType::Rent) => proto::RewardType::Rent,
275            Some(RewardType::Staking) => proto::RewardType::Staking,
276            Some(RewardType::Voting) => proto::RewardType::Voting,
277        }
278    }
279
280    pub const fn create_num_partitions(num_partitions: u64) -> proto::NumPartitions {
281        proto::NumPartitions { num_partitions }
282    }
283
284    pub fn create_return_data(return_data: &TransactionReturnData) -> proto::ReturnData {
285        proto::ReturnData {
286            program_id: return_data.program_id.to_bytes().into(),
287            data: return_data.data.clone(),
288        }
289    }
290
291    pub const fn create_block_height(block_height: u64) -> proto::BlockHeight {
292        proto::BlockHeight { block_height }
293    }
294
295    pub const fn create_timestamp(timestamp: UnixTimestamp) -> proto::UnixTimestamp {
296        proto::UnixTimestamp { timestamp }
297    }
298}
299
300#[cfg(feature = "convert")]
301pub mod convert_from {
302    use {
303        super::prelude as proto,
304        solana_account::Account,
305        solana_account_decoder::parse_token::UiTokenAmount,
306        solana_hash::{Hash, HASH_BYTES},
307        solana_message::{
308            compiled_instruction::CompiledInstruction,
309            v0::{LoadedAddresses, Message as MessageV0, MessageAddressTableLookup},
310            Message, MessageHeader, VersionedMessage,
311        },
312        solana_pubkey::Pubkey,
313        solana_signature::Signature,
314        solana_transaction::versioned::VersionedTransaction,
315        solana_transaction_context::TransactionReturnData,
316        solana_transaction_error::TransactionError,
317        solana_transaction_status::{
318            ConfirmedBlock, InnerInstruction, InnerInstructions, Reward, RewardType,
319            RewardsAndNumPartitions, TransactionStatusMeta, TransactionTokenBalance,
320            TransactionWithStatusMeta, VersionedTransactionWithStatusMeta,
321        },
322    };
323
324    type CreateResult<T> = Result<T, &'static str>;
325
326    pub fn create_block(block: proto::SubscribeUpdateBlock) -> CreateResult<ConfirmedBlock> {
327        let mut transactions = vec![];
328        for tx in block.transactions {
329            transactions.push(create_tx_with_meta(tx)?);
330        }
331
332        let block_rewards = block.rewards.ok_or("failed to get rewards")?;
333        let mut rewards = vec![];
334        for reward in block_rewards.rewards {
335            rewards.push(create_reward(reward)?);
336        }
337
338        Ok(ConfirmedBlock {
339            previous_blockhash: block.parent_blockhash,
340            blockhash: block.blockhash,
341            parent_slot: block.parent_slot,
342            transactions,
343            rewards,
344            num_partitions: block_rewards.num_partitions.map(|msg| msg.num_partitions),
345            block_time: Some(
346                block
347                    .block_time
348                    .map(|wrapper| wrapper.timestamp)
349                    .ok_or("failed to get block_time")?,
350            ),
351            block_height: Some(
352                block
353                    .block_height
354                    .map(|wrapper| wrapper.block_height)
355                    .ok_or("failed to get block_height")?,
356            ),
357        })
358    }
359
360    pub fn create_tx_with_meta(
361        tx: proto::SubscribeUpdateTransactionInfo,
362    ) -> CreateResult<TransactionWithStatusMeta> {
363        let meta = tx.meta.ok_or("failed to get transaction meta")?;
364        let tx = tx
365            .transaction
366            .ok_or("failed to get transaction transaction")?;
367
368        Ok(TransactionWithStatusMeta::Complete(
369            VersionedTransactionWithStatusMeta {
370                transaction: create_tx_versioned(tx)?,
371                meta: create_tx_meta(meta)?,
372            },
373        ))
374    }
375
376    pub fn create_tx_versioned(tx: proto::Transaction) -> CreateResult<VersionedTransaction> {
377        let mut signatures = Vec::with_capacity(tx.signatures.len());
378        for signature in tx.signatures {
379            signatures.push(match Signature::try_from(signature.as_slice()) {
380                Ok(signature) => signature,
381                Err(_error) => return Err("failed to parse Signature"),
382            });
383        }
384
385        Ok(VersionedTransaction {
386            signatures,
387            message: create_message(tx.message.ok_or("failed to get message")?)?,
388        })
389    }
390
391    pub fn create_message(message: proto::Message) -> CreateResult<VersionedMessage> {
392        let header = message.header.ok_or("failed to get MessageHeader")?;
393        let header = MessageHeader {
394            num_required_signatures: header
395                .num_required_signatures
396                .try_into()
397                .map_err(|_| "failed to parse num_required_signatures")?,
398            num_readonly_signed_accounts: header
399                .num_readonly_signed_accounts
400                .try_into()
401                .map_err(|_| "failed to parse num_readonly_signed_accounts")?,
402            num_readonly_unsigned_accounts: header
403                .num_readonly_unsigned_accounts
404                .try_into()
405                .map_err(|_| "failed to parse num_readonly_unsigned_accounts")?,
406        };
407
408        if message.recent_blockhash.len() != HASH_BYTES {
409            return Err("failed to parse hash");
410        }
411
412        Ok(if message.versioned {
413            let mut address_table_lookups = Vec::with_capacity(message.address_table_lookups.len());
414            for table in message.address_table_lookups {
415                address_table_lookups.push(MessageAddressTableLookup {
416                    account_key: Pubkey::try_from(table.account_key.as_slice())
417                        .map_err(|_| "failed to parse Pubkey")?,
418                    writable_indexes: table.writable_indexes,
419                    readonly_indexes: table.readonly_indexes,
420                });
421            }
422
423            VersionedMessage::V0(MessageV0 {
424                header,
425                account_keys: create_pubkey_vec(message.account_keys)?,
426                recent_blockhash: Hash::new_from_array(
427                    <[u8; HASH_BYTES]>::try_from(message.recent_blockhash.as_slice()).unwrap(),
428                ),
429                instructions: create_message_instructions(message.instructions)?,
430                address_table_lookups,
431            })
432        } else {
433            VersionedMessage::Legacy(Message {
434                header,
435                account_keys: create_pubkey_vec(message.account_keys)?,
436                recent_blockhash: Hash::new_from_array(
437                    <[u8; HASH_BYTES]>::try_from(message.recent_blockhash.as_slice()).unwrap(),
438                ),
439                instructions: create_message_instructions(message.instructions)?,
440            })
441        })
442    }
443
444    pub fn create_message_instructions(
445        ixs: Vec<proto::CompiledInstruction>,
446    ) -> CreateResult<Vec<CompiledInstruction>> {
447        ixs.into_iter().map(create_message_instruction).collect()
448    }
449
450    pub fn create_message_instruction(
451        ix: proto::CompiledInstruction,
452    ) -> CreateResult<CompiledInstruction> {
453        Ok(CompiledInstruction {
454            program_id_index: ix
455                .program_id_index
456                .try_into()
457                .map_err(|_| "failed to decode CompiledInstruction.program_id_index)")?,
458            accounts: ix.accounts,
459            data: ix.data,
460        })
461    }
462
463    pub fn create_tx_meta(
464        meta: proto::TransactionStatusMeta,
465    ) -> CreateResult<TransactionStatusMeta> {
466        let meta_status = match create_tx_error(meta.err.as_ref())? {
467            Some(err) => Err(err),
468            None => Ok(()),
469        };
470        let meta_rewards = meta
471            .rewards
472            .into_iter()
473            .map(create_reward)
474            .collect::<Result<Vec<_>, _>>()?;
475
476        Ok(TransactionStatusMeta {
477            status: meta_status,
478            fee: meta.fee,
479            pre_balances: meta.pre_balances,
480            post_balances: meta.post_balances,
481            inner_instructions: Some(create_meta_inner_instructions(meta.inner_instructions)?),
482            log_messages: Some(meta.log_messages),
483            pre_token_balances: Some(create_token_balances(meta.pre_token_balances)?),
484            post_token_balances: Some(create_token_balances(meta.post_token_balances)?),
485            rewards: Some(meta_rewards),
486            loaded_addresses: create_loaded_addresses(
487                meta.loaded_writable_addresses,
488                meta.loaded_readonly_addresses,
489            )?,
490            return_data: if meta.return_data_none {
491                None
492            } else {
493                let data = meta.return_data.ok_or("failed to get return_data")?;
494                Some(TransactionReturnData {
495                    program_id: Pubkey::try_from(data.program_id.as_slice())
496                        .map_err(|_| "failed to parse program_id")?,
497                    data: data.data,
498                })
499            },
500            compute_units_consumed: meta.compute_units_consumed,
501            cost_units: meta.cost_units,
502        })
503    }
504
505    pub fn create_tx_error(
506        err: Option<&proto::TransactionError>,
507    ) -> CreateResult<Option<TransactionError>> {
508        err.map(|err| bincode::deserialize::<TransactionError>(&err.err))
509            .transpose()
510            .map_err(|_| "failed to decode TransactionError")
511    }
512
513    pub fn create_meta_inner_instructions(
514        ixs: Vec<proto::InnerInstructions>,
515    ) -> CreateResult<Vec<InnerInstructions>> {
516        ixs.into_iter().map(create_meta_inner_instruction).collect()
517    }
518
519    pub fn create_meta_inner_instruction(
520        ix: proto::InnerInstructions,
521    ) -> CreateResult<InnerInstructions> {
522        let mut instructions = vec![];
523        for ix in ix.instructions {
524            instructions.push(InnerInstruction {
525                instruction: CompiledInstruction {
526                    program_id_index: ix
527                        .program_id_index
528                        .try_into()
529                        .map_err(|_| "failed to decode CompiledInstruction.program_id_index)")?,
530                    accounts: ix.accounts,
531                    data: ix.data,
532                },
533                stack_height: ix.stack_height,
534            });
535        }
536        Ok(InnerInstructions {
537            index: ix
538                .index
539                .try_into()
540                .map_err(|_| "failed to decode InnerInstructions.index")?,
541            instructions,
542        })
543    }
544
545    pub fn create_rewards_obj(rewards: proto::Rewards) -> CreateResult<RewardsAndNumPartitions> {
546        Ok(RewardsAndNumPartitions {
547            rewards: rewards
548                .rewards
549                .into_iter()
550                .map(create_reward)
551                .collect::<Result<_, _>>()?,
552            num_partitions: rewards.num_partitions.map(|wrapper| wrapper.num_partitions),
553        })
554    }
555
556    pub fn create_reward(reward: proto::Reward) -> CreateResult<Reward> {
557        Ok(Reward {
558            pubkey: reward.pubkey,
559            lamports: reward.lamports,
560            post_balance: reward.post_balance,
561            reward_type: match proto::RewardType::try_from(reward.reward_type)
562                .map_err(|_| "failed to parse reward_type")?
563            {
564                proto::RewardType::Unspecified => None,
565                proto::RewardType::Fee => Some(RewardType::Fee),
566                proto::RewardType::Rent => Some(RewardType::Rent),
567                proto::RewardType::Staking => Some(RewardType::Staking),
568                proto::RewardType::Voting => Some(RewardType::Voting),
569            },
570            commission: if reward.commission.is_empty() {
571                None
572            } else {
573                Some(
574                    reward
575                        .commission
576                        .parse()
577                        .map_err(|_| "failed to parse reward commission")?,
578                )
579            },
580        })
581    }
582
583    pub fn create_token_balances(
584        balances: Vec<proto::TokenBalance>,
585    ) -> CreateResult<Vec<TransactionTokenBalance>> {
586        let mut vec = Vec::with_capacity(balances.len());
587        for balance in balances {
588            let ui_amount = balance
589                .ui_token_amount
590                .ok_or("failed to get ui_token_amount")?;
591            vec.push(TransactionTokenBalance {
592                account_index: balance
593                    .account_index
594                    .try_into()
595                    .map_err(|_| "failed to parse account_index")?,
596                mint: balance.mint,
597                ui_token_amount: UiTokenAmount {
598                    ui_amount: Some(ui_amount.ui_amount),
599                    decimals: ui_amount
600                        .decimals
601                        .try_into()
602                        .map_err(|_| "failed to parse decimals")?,
603                    amount: ui_amount.amount,
604                    ui_amount_string: ui_amount.ui_amount_string,
605                },
606                owner: balance.owner,
607                program_id: balance.program_id,
608            });
609        }
610        Ok(vec)
611    }
612
613    pub fn create_loaded_addresses(
614        writable: Vec<Vec<u8>>,
615        readonly: Vec<Vec<u8>>,
616    ) -> CreateResult<LoadedAddresses> {
617        Ok(LoadedAddresses {
618            writable: create_pubkey_vec(writable)?,
619            readonly: create_pubkey_vec(readonly)?,
620        })
621    }
622
623    pub fn create_pubkey_vec(pubkeys: Vec<Vec<u8>>) -> CreateResult<Vec<Pubkey>> {
624        pubkeys
625            .iter()
626            .map(|pubkey| create_pubkey(pubkey.as_slice()))
627            .collect()
628    }
629
630    pub fn create_pubkey(pubkey: &[u8]) -> CreateResult<Pubkey> {
631        Pubkey::try_from(pubkey).map_err(|_| "failed to parse Pubkey")
632    }
633
634    #[cfg(feature = "account-data-as-bytes")]
635    fn take_account_data(account: &mut proto::SubscribeUpdateAccountInfo) -> Vec<u8> {
636        // By taking the data, we make sure the reference count goes quicker to 1.
637        // If only one reference remains (say this instance), during `into()`, it won't copy the vector
638        // Compared to `Bytes:to_vec`, it should not allocate new memory.
639        let bytes = std::mem::take(&mut account.data);
640        bytes.into()
641    }
642
643    #[cfg(not(feature = "account-data-as-bytes"))]
644    fn take_account_data(account: &mut proto::SubscribeUpdateAccountInfo) -> Vec<u8> {
645        std::mem::take(&mut account.data)
646    }
647
648    pub fn create_account(
649        mut account: proto::SubscribeUpdateAccountInfo,
650    ) -> CreateResult<(Pubkey, Account)> {
651        let pubkey = create_pubkey(&account.pubkey)?;
652        let account_data = take_account_data(&mut account);
653        let account = Account {
654            lamports: account.lamports,
655            data: account_data,
656            owner: create_pubkey(&account.owner)?,
657            executable: account.executable,
658            rent_epoch: account.rent_epoch,
659        };
660        Ok((pubkey, account))
661    }
662}