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