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 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}