proto: add mod `convert_to`, `convert_from` (#190)
This commit is contained in:
parent
a6cb542167
commit
f559fa8f60
|
@ -12,6 +12,8 @@ The minor version will be incremented upon a breaking change and the patch versi
|
|||
|
||||
### Features
|
||||
|
||||
- proto: add mod `convert_to`, `convert_from` ([#190](https://github.com/rpcpool/yellowstone-grpc/pull/190)).
|
||||
|
||||
### Fixes
|
||||
|
||||
### Breaking
|
||||
|
|
|
@ -4466,7 +4466,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yellowstone-grpc-client"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
version = "1.11.0+solana.1.16.14"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
|
@ -4480,7 +4480,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yellowstone-grpc-client-simple"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"backoff",
|
||||
|
@ -4501,7 +4501,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yellowstone-grpc-geyser"
|
||||
version = "1.8.0+solana.1.16.14"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.2",
|
||||
|
@ -4560,11 +4560,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "yellowstone-grpc-proto"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"prost",
|
||||
"protobuf-src",
|
||||
"solana-account-decoder",
|
||||
"solana-sdk",
|
||||
"solana-transaction-status",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
]
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
[workspace]
|
||||
members = [
|
||||
"examples/rust", # 1.9.0+solana.1.16.14
|
||||
"yellowstone-grpc-client", # 1.10.0+solana.1.16.14
|
||||
"yellowstone-grpc-geyser", # 1.8.0+solana.1.16.14
|
||||
"examples/rust", # 1.10.0+solana.1.16.14
|
||||
"yellowstone-grpc-client", # 1.11.0+solana.1.16.14
|
||||
"yellowstone-grpc-geyser", # 1.9.0+solana.1.16.14
|
||||
"yellowstone-grpc-kafka", # 1.0.0+solana.1.16.14
|
||||
"yellowstone-grpc-proto", # 1.9.0+solana.1.16.14
|
||||
"yellowstone-grpc-proto", # 1.10.0+solana.1.16.14
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yellowstone-grpc-client-simple"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
authors = ["Triton One"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yellowstone-grpc-client"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
version = "1.11.0+solana.1.16.14"
|
||||
authors = ["Triton One"]
|
||||
edition = "2021"
|
||||
description = "Yellowstone gRPC Geyser Simple Client"
|
||||
|
@ -16,7 +16,7 @@ http = "0.2.8"
|
|||
thiserror = "1.0"
|
||||
tonic = { version = "0.9.2", features = ["gzip", "tls", "tls-roots"] }
|
||||
tonic-health = "0.9.2"
|
||||
yellowstone-grpc-proto = { path = "../yellowstone-grpc-proto", version = "1.9.0+solana.1.16.14" }
|
||||
yellowstone-grpc-proto = { path = "../yellowstone-grpc-proto", version = "1.10.0+solana.1.16.14" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.21.2", features = ["macros"] }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yellowstone-grpc-geyser"
|
||||
version = "1.8.0+solana.1.16.14"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
authors = ["Triton One"]
|
||||
edition = "2021"
|
||||
description = "Yellowstone gRPC Geyser Plugin"
|
||||
|
|
|
@ -9,15 +9,6 @@ use {
|
|||
Message, MessageAccount, MessageBlock, MessageBlockMeta, MessageEntry, MessageRef,
|
||||
MessageSlot, MessageTransaction,
|
||||
},
|
||||
proto::{
|
||||
subscribe_request_filter_accounts_filter::Filter as AccountsFilterDataOneof,
|
||||
subscribe_request_filter_accounts_filter_memcmp::Data as AccountsFilterMemcmpOneof,
|
||||
CommitmentLevel, SubscribeRequest, SubscribeRequestAccountsDataSlice,
|
||||
SubscribeRequestFilterAccounts, SubscribeRequestFilterAccountsFilter,
|
||||
SubscribeRequestFilterBlocks, SubscribeRequestFilterBlocksMeta,
|
||||
SubscribeRequestFilterEntry, SubscribeRequestFilterSlots,
|
||||
SubscribeRequestFilterTransactions, SubscribeUpdate,
|
||||
},
|
||||
},
|
||||
base64::{engine::general_purpose::STANDARD as base64_engine, Engine},
|
||||
solana_sdk::{pubkey::Pubkey, signature::Signature},
|
||||
|
@ -27,6 +18,15 @@ use {
|
|||
iter::FromIterator,
|
||||
str::FromStr,
|
||||
},
|
||||
yellowstone_grpc_proto::prelude::{
|
||||
subscribe_request_filter_accounts_filter::Filter as AccountsFilterDataOneof,
|
||||
subscribe_request_filter_accounts_filter_memcmp::Data as AccountsFilterMemcmpOneof,
|
||||
CommitmentLevel, SubscribeRequest, SubscribeRequestAccountsDataSlice,
|
||||
SubscribeRequestFilterAccounts, SubscribeRequestFilterAccountsFilter,
|
||||
SubscribeRequestFilterBlocks, SubscribeRequestFilterBlocksMeta,
|
||||
SubscribeRequestFilterEntry, SubscribeRequestFilterSlots,
|
||||
SubscribeRequestFilterTransactions, SubscribeUpdate,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -3,18 +3,6 @@ use {
|
|||
config::{ConfigBlockFailAction, ConfigGrpc},
|
||||
filters::{Filter, FilterAccountsDataSlice},
|
||||
prom::{self, CONNECTIONS_TOTAL, MESSAGE_QUEUE_SIZE},
|
||||
proto::{
|
||||
self,
|
||||
geyser_server::{Geyser, GeyserServer},
|
||||
subscribe_update::UpdateOneof,
|
||||
CommitmentLevel, GetBlockHeightRequest, GetBlockHeightResponse,
|
||||
GetLatestBlockhashRequest, GetLatestBlockhashResponse, GetSlotRequest, GetSlotResponse,
|
||||
GetVersionRequest, GetVersionResponse, IsBlockhashValidRequest,
|
||||
IsBlockhashValidResponse, PingRequest, PongResponse, SubscribeRequest, SubscribeUpdate,
|
||||
SubscribeUpdateAccount, SubscribeUpdateAccountInfo, SubscribeUpdateBlock,
|
||||
SubscribeUpdateBlockMeta, SubscribeUpdateEntry, SubscribeUpdatePing,
|
||||
SubscribeUpdateSlot, SubscribeUpdateTransaction, SubscribeUpdateTransactionInfo,
|
||||
},
|
||||
version::VERSION,
|
||||
},
|
||||
log::{error, info},
|
||||
|
@ -51,6 +39,20 @@ use {
|
|||
Request, Response, Result as TonicResult, Status, Streaming,
|
||||
},
|
||||
tonic_health::server::health_reporter,
|
||||
yellowstone_grpc_proto::{
|
||||
convert_to,
|
||||
prelude::{
|
||||
geyser_server::{Geyser, GeyserServer},
|
||||
subscribe_update::UpdateOneof,
|
||||
CommitmentLevel, GetBlockHeightRequest, GetBlockHeightResponse,
|
||||
GetLatestBlockhashRequest, GetLatestBlockhashResponse, GetSlotRequest, GetSlotResponse,
|
||||
GetVersionRequest, GetVersionResponse, IsBlockhashValidRequest,
|
||||
IsBlockhashValidResponse, PingRequest, PongResponse, SubscribeRequest, SubscribeUpdate,
|
||||
SubscribeUpdateAccount, SubscribeUpdateAccountInfo, SubscribeUpdateBlock,
|
||||
SubscribeUpdateBlockMeta, SubscribeUpdateEntry, SubscribeUpdatePing,
|
||||
SubscribeUpdateSlot, SubscribeUpdateTransaction, SubscribeUpdateTransactionInfo,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -155,8 +157,8 @@ impl MessageTransactionInfo {
|
|||
SubscribeUpdateTransactionInfo {
|
||||
signature: self.signature.as_ref().into(),
|
||||
is_vote: self.is_vote,
|
||||
transaction: Some(proto::convert::create_transaction(&self.transaction)),
|
||||
meta: Some(proto::convert::create_transaction_meta(&self.meta)),
|
||||
transaction: Some(convert_to::create_transaction(&self.transaction)),
|
||||
meta: Some(convert_to::create_transaction_meta(&self.meta)),
|
||||
index: self.index as u64,
|
||||
}
|
||||
}
|
||||
|
@ -412,11 +414,9 @@ impl<'a> MessageRef<'a> {
|
|||
Self::Block(message) => UpdateOneof::Block(SubscribeUpdateBlock {
|
||||
slot: message.slot,
|
||||
blockhash: message.blockhash.clone(),
|
||||
rewards: Some(proto::convert::create_rewards(message.rewards.as_slice())),
|
||||
block_time: message.block_time.map(proto::convert::create_timestamp),
|
||||
block_height: message
|
||||
.block_height
|
||||
.map(proto::convert::create_block_height),
|
||||
rewards: Some(convert_to::create_rewards(message.rewards.as_slice())),
|
||||
block_time: message.block_time.map(convert_to::create_timestamp),
|
||||
block_height: message.block_height.map(convert_to::create_block_height),
|
||||
parent_slot: message.parent_slot,
|
||||
parent_blockhash: message.parent_blockhash.clone(),
|
||||
executed_transaction_count: message.executed_transaction_count,
|
||||
|
@ -441,11 +441,9 @@ impl<'a> MessageRef<'a> {
|
|||
Self::BlockMeta(message) => UpdateOneof::BlockMeta(SubscribeUpdateBlockMeta {
|
||||
slot: message.slot,
|
||||
blockhash: message.blockhash.clone(),
|
||||
rewards: Some(proto::convert::create_rewards(message.rewards.as_slice())),
|
||||
block_time: message.block_time.map(proto::convert::create_timestamp),
|
||||
block_height: message
|
||||
.block_height
|
||||
.map(proto::convert::create_block_height),
|
||||
rewards: Some(convert_to::create_rewards(message.rewards.as_slice())),
|
||||
block_time: message.block_time.map(convert_to::create_timestamp),
|
||||
block_height: message.block_height.map(convert_to::create_block_height),
|
||||
parent_slot: message.parent_slot,
|
||||
parent_blockhash: message.parent_blockhash.clone(),
|
||||
executed_transaction_count: message.executed_transaction_count,
|
||||
|
|
|
@ -7,5 +7,4 @@ pub mod filters;
|
|||
pub mod grpc;
|
||||
pub mod plugin;
|
||||
pub mod prom;
|
||||
pub mod proto;
|
||||
pub mod version;
|
||||
|
|
|
@ -1,241 +0,0 @@
|
|||
pub use yellowstone_grpc_proto::prelude::*;
|
||||
|
||||
pub mod convert {
|
||||
use {
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
instruction::CompiledInstruction,
|
||||
message::{
|
||||
v0::{LoadedMessage, MessageAddressTableLookup},
|
||||
LegacyMessage, MessageHeader, SanitizedMessage,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::SanitizedTransaction,
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
InnerInstruction, InnerInstructions, Reward, RewardType, TransactionStatusMeta,
|
||||
TransactionTokenBalance,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn create_transaction(tx: &SanitizedTransaction) -> super::Transaction {
|
||||
super::Transaction {
|
||||
signatures: tx
|
||||
.signatures()
|
||||
.iter()
|
||||
.map(|signature| <Signature as AsRef<[u8]>>::as_ref(signature).into())
|
||||
.collect(),
|
||||
message: Some(create_message(tx.message())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_message(message: &SanitizedMessage) -> super::Message {
|
||||
match message {
|
||||
SanitizedMessage::Legacy(LegacyMessage { message, .. }) => super::Message {
|
||||
header: Some(create_header(&message.header)),
|
||||
account_keys: message
|
||||
.account_keys
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect(),
|
||||
recent_blockhash: message.recent_blockhash.to_bytes().into(),
|
||||
instructions: message
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_instruction)
|
||||
.collect(),
|
||||
versioned: false,
|
||||
address_table_lookups: vec![],
|
||||
},
|
||||
SanitizedMessage::V0(LoadedMessage { message, .. }) => super::Message {
|
||||
header: Some(create_header(&message.header)),
|
||||
account_keys: message
|
||||
.account_keys
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect(),
|
||||
recent_blockhash: message.recent_blockhash.to_bytes().into(),
|
||||
instructions: message
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_instruction)
|
||||
.collect(),
|
||||
versioned: true,
|
||||
address_table_lookups: message
|
||||
.address_table_lookups
|
||||
.iter()
|
||||
.map(create_lookup)
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn create_header(header: &MessageHeader) -> super::MessageHeader {
|
||||
super::MessageHeader {
|
||||
num_required_signatures: header.num_required_signatures as u32,
|
||||
num_readonly_signed_accounts: header.num_readonly_signed_accounts as u32,
|
||||
num_readonly_unsigned_accounts: header.num_readonly_unsigned_accounts as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_instruction(ix: &CompiledInstruction) -> super::CompiledInstruction {
|
||||
super::CompiledInstruction {
|
||||
program_id_index: ix.program_id_index as u32,
|
||||
accounts: ix.accounts.clone(),
|
||||
data: ix.data.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_lookup(lookup: &MessageAddressTableLookup) -> super::MessageAddressTableLookup {
|
||||
super::MessageAddressTableLookup {
|
||||
account_key: <Pubkey as AsRef<[u8]>>::as_ref(&lookup.account_key).into(),
|
||||
writable_indexes: lookup.writable_indexes.clone(),
|
||||
readonly_indexes: lookup.readonly_indexes.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_transaction_meta(meta: &TransactionStatusMeta) -> super::TransactionStatusMeta {
|
||||
let TransactionStatusMeta {
|
||||
status,
|
||||
fee,
|
||||
pre_balances,
|
||||
post_balances,
|
||||
inner_instructions,
|
||||
log_messages,
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
compute_units_consumed,
|
||||
} = meta;
|
||||
let err = match status {
|
||||
Ok(()) => None,
|
||||
Err(err) => Some(super::TransactionError {
|
||||
err: bincode::serialize(&err).expect("transaction error to serialize to bytes"),
|
||||
}),
|
||||
};
|
||||
let inner_instructions_none = inner_instructions.is_none();
|
||||
let inner_instructions = inner_instructions
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_inner_instructions).collect())
|
||||
.unwrap_or_default();
|
||||
let log_messages_none = log_messages.is_none();
|
||||
let log_messages = log_messages.clone().unwrap_or_default();
|
||||
let pre_token_balances = pre_token_balances
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_token_balance).collect())
|
||||
.unwrap_or_default();
|
||||
let post_token_balances = post_token_balances
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_token_balance).collect())
|
||||
.unwrap_or_default();
|
||||
let rewards = rewards
|
||||
.as_ref()
|
||||
.map(|vec| vec.iter().map(create_reward).collect())
|
||||
.unwrap_or_default();
|
||||
let loaded_writable_addresses = loaded_addresses
|
||||
.writable
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect();
|
||||
let loaded_readonly_addresses = loaded_addresses
|
||||
.readonly
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect();
|
||||
|
||||
super::TransactionStatusMeta {
|
||||
err,
|
||||
fee: *fee,
|
||||
pre_balances: pre_balances.clone(),
|
||||
post_balances: post_balances.clone(),
|
||||
inner_instructions,
|
||||
inner_instructions_none,
|
||||
log_messages,
|
||||
log_messages_none,
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
return_data: return_data.as_ref().map(create_return_data),
|
||||
return_data_none: return_data.is_none(),
|
||||
compute_units_consumed: *compute_units_consumed,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_inner_instructions(instructions: &InnerInstructions) -> super::InnerInstructions {
|
||||
super::InnerInstructions {
|
||||
index: instructions.index as u32,
|
||||
instructions: instructions
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_inner_instruction)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_inner_instruction(instruction: &InnerInstruction) -> super::InnerInstruction {
|
||||
super::InnerInstruction {
|
||||
program_id_index: instruction.instruction.program_id_index as u32,
|
||||
accounts: instruction.instruction.accounts.clone(),
|
||||
data: instruction.instruction.data.clone(),
|
||||
stack_height: instruction.stack_height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_token_balance(balance: &TransactionTokenBalance) -> super::TokenBalance {
|
||||
super::TokenBalance {
|
||||
account_index: balance.account_index as u32,
|
||||
mint: balance.mint.clone(),
|
||||
ui_token_amount: Some(super::UiTokenAmount {
|
||||
ui_amount: balance.ui_token_amount.ui_amount.unwrap_or_default(),
|
||||
decimals: balance.ui_token_amount.decimals as u32,
|
||||
amount: balance.ui_token_amount.amount.clone(),
|
||||
ui_amount_string: balance.ui_token_amount.ui_amount_string.clone(),
|
||||
}),
|
||||
owner: balance.owner.clone(),
|
||||
program_id: balance.program_id.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_reward(reward: &Reward) -> super::Reward {
|
||||
super::Reward {
|
||||
pubkey: reward.pubkey.clone(),
|
||||
lamports: reward.lamports,
|
||||
post_balance: reward.post_balance,
|
||||
reward_type: match reward.reward_type {
|
||||
None => super::RewardType::Unspecified,
|
||||
Some(RewardType::Fee) => super::RewardType::Fee,
|
||||
Some(RewardType::Rent) => super::RewardType::Rent,
|
||||
Some(RewardType::Staking) => super::RewardType::Staking,
|
||||
Some(RewardType::Voting) => super::RewardType::Voting,
|
||||
} as i32,
|
||||
commission: reward.commission.map(|c| c.to_string()).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_return_data(return_data: &TransactionReturnData) -> super::ReturnData {
|
||||
super::ReturnData {
|
||||
program_id: return_data.program_id.to_bytes().into(),
|
||||
data: return_data.data.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_rewards(rewards: &[Reward]) -> super::Rewards {
|
||||
super::Rewards {
|
||||
rewards: rewards.iter().map(create_reward).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn create_block_height(block_height: u64) -> super::BlockHeight {
|
||||
super::BlockHeight { block_height }
|
||||
}
|
||||
|
||||
pub const fn create_timestamp(timestamp: UnixTimestamp) -> super::UnixTimestamp {
|
||||
super::UnixTimestamp { timestamp }
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "yellowstone-grpc-proto"
|
||||
version = "1.9.0+solana.1.16.14"
|
||||
version = "1.10.0+solana.1.16.14"
|
||||
authors = ["Triton One"]
|
||||
edition = "2021"
|
||||
description = "Yellowstone gRPC Geyser Protobuf Definitions"
|
||||
|
@ -10,7 +10,11 @@ license = "Apache-2.0"
|
|||
keywords = ["solana"]
|
||||
|
||||
[dependencies]
|
||||
bincode = "1.3.3"
|
||||
prost = "0.11.9"
|
||||
solana-account-decoder = "=1.16.14"
|
||||
solana-sdk = "=1.16.14"
|
||||
solana-transaction-status = "=1.16.14"
|
||||
tonic = "0.9.2"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -19,3 +19,551 @@ pub mod prelude {
|
|||
|
||||
pub use prost;
|
||||
pub use tonic;
|
||||
|
||||
pub mod convert_to {
|
||||
use {
|
||||
super::prelude as proto,
|
||||
solana_sdk::{
|
||||
clock::UnixTimestamp,
|
||||
instruction::CompiledInstruction,
|
||||
message::{
|
||||
v0::{LoadedMessage, MessageAddressTableLookup},
|
||||
LegacyMessage, MessageHeader, SanitizedMessage,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::SanitizedTransaction,
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
InnerInstruction, InnerInstructions, Reward, RewardType, TransactionStatusMeta,
|
||||
TransactionTokenBalance,
|
||||
},
|
||||
};
|
||||
|
||||
pub fn create_transaction(tx: &SanitizedTransaction) -> proto::Transaction {
|
||||
proto::Transaction {
|
||||
signatures: tx
|
||||
.signatures()
|
||||
.iter()
|
||||
.map(|signature| <Signature as AsRef<[u8]>>::as_ref(signature).into())
|
||||
.collect(),
|
||||
message: Some(create_message(tx.message())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_message(message: &SanitizedMessage) -> proto::Message {
|
||||
match message {
|
||||
SanitizedMessage::Legacy(LegacyMessage { message, .. }) => proto::Message {
|
||||
header: Some(create_header(&message.header)),
|
||||
account_keys: message
|
||||
.account_keys
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect(),
|
||||
recent_blockhash: message.recent_blockhash.to_bytes().into(),
|
||||
instructions: message
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_instruction)
|
||||
.collect(),
|
||||
versioned: false,
|
||||
address_table_lookups: vec![],
|
||||
},
|
||||
SanitizedMessage::V0(LoadedMessage { message, .. }) => proto::Message {
|
||||
header: Some(create_header(&message.header)),
|
||||
account_keys: message
|
||||
.account_keys
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect(),
|
||||
recent_blockhash: message.recent_blockhash.to_bytes().into(),
|
||||
instructions: message
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_instruction)
|
||||
.collect(),
|
||||
versioned: true,
|
||||
address_table_lookups: message
|
||||
.address_table_lookups
|
||||
.iter()
|
||||
.map(create_lookup)
|
||||
.collect(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn create_header(header: &MessageHeader) -> proto::MessageHeader {
|
||||
proto::MessageHeader {
|
||||
num_required_signatures: header.num_required_signatures as u32,
|
||||
num_readonly_signed_accounts: header.num_readonly_signed_accounts as u32,
|
||||
num_readonly_unsigned_accounts: header.num_readonly_unsigned_accounts as u32,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_instruction(ix: &CompiledInstruction) -> proto::CompiledInstruction {
|
||||
proto::CompiledInstruction {
|
||||
program_id_index: ix.program_id_index as u32,
|
||||
accounts: ix.accounts.clone(),
|
||||
data: ix.data.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_lookup(lookup: &MessageAddressTableLookup) -> proto::MessageAddressTableLookup {
|
||||
proto::MessageAddressTableLookup {
|
||||
account_key: <Pubkey as AsRef<[u8]>>::as_ref(&lookup.account_key).into(),
|
||||
writable_indexes: lookup.writable_indexes.clone(),
|
||||
readonly_indexes: lookup.readonly_indexes.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_transaction_meta(meta: &TransactionStatusMeta) -> proto::TransactionStatusMeta {
|
||||
let TransactionStatusMeta {
|
||||
status,
|
||||
fee,
|
||||
pre_balances,
|
||||
post_balances,
|
||||
inner_instructions,
|
||||
log_messages,
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
compute_units_consumed,
|
||||
} = meta;
|
||||
let err = match status {
|
||||
Ok(()) => None,
|
||||
Err(err) => Some(proto::TransactionError {
|
||||
err: bincode::serialize(&err).expect("transaction error to serialize to bytes"),
|
||||
}),
|
||||
};
|
||||
let inner_instructions_none = inner_instructions.is_none();
|
||||
let inner_instructions = inner_instructions
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_inner_instructions).collect())
|
||||
.unwrap_or_default();
|
||||
let log_messages_none = log_messages.is_none();
|
||||
let log_messages = log_messages.clone().unwrap_or_default();
|
||||
let pre_token_balances = pre_token_balances
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_token_balance).collect())
|
||||
.unwrap_or_default();
|
||||
let post_token_balances = post_token_balances
|
||||
.as_ref()
|
||||
.map(|v| v.iter().map(create_token_balance).collect())
|
||||
.unwrap_or_default();
|
||||
let rewards = rewards
|
||||
.as_ref()
|
||||
.map(|vec| vec.iter().map(create_reward).collect())
|
||||
.unwrap_or_default();
|
||||
let loaded_writable_addresses = loaded_addresses
|
||||
.writable
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect();
|
||||
let loaded_readonly_addresses = loaded_addresses
|
||||
.readonly
|
||||
.iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(key).into())
|
||||
.collect();
|
||||
|
||||
proto::TransactionStatusMeta {
|
||||
err,
|
||||
fee: *fee,
|
||||
pre_balances: pre_balances.clone(),
|
||||
post_balances: post_balances.clone(),
|
||||
inner_instructions,
|
||||
inner_instructions_none,
|
||||
log_messages,
|
||||
log_messages_none,
|
||||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
return_data: return_data.as_ref().map(create_return_data),
|
||||
return_data_none: return_data.is_none(),
|
||||
compute_units_consumed: *compute_units_consumed,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_inner_instructions(instructions: &InnerInstructions) -> proto::InnerInstructions {
|
||||
proto::InnerInstructions {
|
||||
index: instructions.index as u32,
|
||||
instructions: instructions
|
||||
.instructions
|
||||
.iter()
|
||||
.map(create_inner_instruction)
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_inner_instruction(instruction: &InnerInstruction) -> proto::InnerInstruction {
|
||||
proto::InnerInstruction {
|
||||
program_id_index: instruction.instruction.program_id_index as u32,
|
||||
accounts: instruction.instruction.accounts.clone(),
|
||||
data: instruction.instruction.data.clone(),
|
||||
stack_height: instruction.stack_height,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_token_balance(balance: &TransactionTokenBalance) -> proto::TokenBalance {
|
||||
proto::TokenBalance {
|
||||
account_index: balance.account_index as u32,
|
||||
mint: balance.mint.clone(),
|
||||
ui_token_amount: Some(proto::UiTokenAmount {
|
||||
ui_amount: balance.ui_token_amount.ui_amount.unwrap_or_default(),
|
||||
decimals: balance.ui_token_amount.decimals as u32,
|
||||
amount: balance.ui_token_amount.amount.clone(),
|
||||
ui_amount_string: balance.ui_token_amount.ui_amount_string.clone(),
|
||||
}),
|
||||
owner: balance.owner.clone(),
|
||||
program_id: balance.program_id.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_reward(reward: &Reward) -> proto::Reward {
|
||||
proto::Reward {
|
||||
pubkey: reward.pubkey.clone(),
|
||||
lamports: reward.lamports,
|
||||
post_balance: reward.post_balance,
|
||||
reward_type: match reward.reward_type {
|
||||
None => proto::RewardType::Unspecified,
|
||||
Some(RewardType::Fee) => proto::RewardType::Fee,
|
||||
Some(RewardType::Rent) => proto::RewardType::Rent,
|
||||
Some(RewardType::Staking) => proto::RewardType::Staking,
|
||||
Some(RewardType::Voting) => proto::RewardType::Voting,
|
||||
} as i32,
|
||||
commission: reward.commission.map(|c| c.to_string()).unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_return_data(return_data: &TransactionReturnData) -> proto::ReturnData {
|
||||
proto::ReturnData {
|
||||
program_id: return_data.program_id.to_bytes().into(),
|
||||
data: return_data.data.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_rewards(rewards: &[Reward]) -> proto::Rewards {
|
||||
proto::Rewards {
|
||||
rewards: rewards.iter().map(create_reward).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn create_block_height(block_height: u64) -> proto::BlockHeight {
|
||||
proto::BlockHeight { block_height }
|
||||
}
|
||||
|
||||
pub const fn create_timestamp(timestamp: UnixTimestamp) -> proto::UnixTimestamp {
|
||||
proto::UnixTimestamp { timestamp }
|
||||
}
|
||||
}
|
||||
|
||||
pub mod convert_from {
|
||||
use {
|
||||
super::prelude as proto,
|
||||
solana_account_decoder::parse_token::UiTokenAmount,
|
||||
solana_sdk::{
|
||||
hash::{Hash, HASH_BYTES},
|
||||
instruction::CompiledInstruction,
|
||||
message::{
|
||||
v0::{LoadedAddresses, Message as MessageV0, MessageAddressTableLookup},
|
||||
Message, MessageHeader, VersionedMessage,
|
||||
},
|
||||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{TransactionError, VersionedTransaction},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, InnerInstruction, InnerInstructions, Reward, RewardType,
|
||||
TransactionStatusMeta, TransactionTokenBalance, TransactionWithStatusMeta,
|
||||
VersionedTransactionWithStatusMeta,
|
||||
},
|
||||
};
|
||||
|
||||
fn ensure_some<T>(maybe_value: Option<T>, message: impl Into<String>) -> Result<T, String> {
|
||||
match maybe_value {
|
||||
Some(value) => Ok(value),
|
||||
None => Err(message.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_block(block: proto::SubscribeUpdateBlock) -> Result<ConfirmedBlock, String> {
|
||||
let mut transactions = vec![];
|
||||
for tx in block.transactions {
|
||||
transactions.push(create_tx_with_meta(tx)?);
|
||||
}
|
||||
|
||||
let mut rewards = vec![];
|
||||
for reward in ensure_some(block.rewards, "failed to get rewards")?.rewards {
|
||||
rewards.push(create_reward(reward)?);
|
||||
}
|
||||
|
||||
Ok(ConfirmedBlock {
|
||||
previous_blockhash: block.parent_blockhash,
|
||||
blockhash: block.blockhash,
|
||||
parent_slot: block.parent_slot,
|
||||
transactions,
|
||||
rewards,
|
||||
block_time: Some(ensure_some(
|
||||
block.block_time.map(|wrapper| wrapper.timestamp),
|
||||
"failed to get block_time",
|
||||
)?),
|
||||
block_height: Some(ensure_some(
|
||||
block.block_height.map(|wrapper| wrapper.block_height),
|
||||
"failed to get block_height",
|
||||
)?),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_tx_with_meta(
|
||||
tx: proto::SubscribeUpdateTransactionInfo,
|
||||
) -> Result<TransactionWithStatusMeta, String> {
|
||||
let meta = ensure_some(tx.meta, "failed to get transaction meta")?;
|
||||
let tx = ensure_some(tx.transaction, "failed to get transaction transaction")?;
|
||||
|
||||
Ok(TransactionWithStatusMeta::Complete(
|
||||
VersionedTransactionWithStatusMeta {
|
||||
transaction: create_tx_versioned(tx)?,
|
||||
meta: create_tx_meta(meta)?,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn create_tx_versioned(tx: proto::Transaction) -> Result<VersionedTransaction, String> {
|
||||
let mut signatures = Vec::with_capacity(tx.signatures.len());
|
||||
for signature in tx.signatures {
|
||||
signatures.push(match Signature::try_from(signature.as_slice()) {
|
||||
Ok(signature) => signature,
|
||||
Err(_error) => return Err("failed to parse Signature".to_owned()),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(VersionedTransaction {
|
||||
signatures,
|
||||
message: create_message(ensure_some(tx.message, "failed to get message")?)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_message(message: proto::Message) -> Result<VersionedMessage, String> {
|
||||
let header = ensure_some(message.header, "failed to get MessageHeader")?;
|
||||
let header = MessageHeader {
|
||||
num_required_signatures: ensure_some(
|
||||
header.num_required_signatures.try_into().ok(),
|
||||
"failed to parse num_required_signatures",
|
||||
)?,
|
||||
num_readonly_signed_accounts: ensure_some(
|
||||
header.num_readonly_signed_accounts.try_into().ok(),
|
||||
"failed to parse num_readonly_signed_accounts",
|
||||
)?,
|
||||
num_readonly_unsigned_accounts: ensure_some(
|
||||
header.num_readonly_unsigned_accounts.try_into().ok(),
|
||||
"failed to parse num_readonly_unsigned_accounts",
|
||||
)?,
|
||||
};
|
||||
|
||||
if message.recent_blockhash.len() != HASH_BYTES {
|
||||
return Err("failed to parse hash".to_owned());
|
||||
}
|
||||
|
||||
let mut instructions = Vec::with_capacity(message.instructions.len());
|
||||
for ix in message.instructions {
|
||||
instructions.push(CompiledInstruction {
|
||||
program_id_index: ensure_some(
|
||||
ix.program_id_index.try_into().ok(),
|
||||
"failed to decode CompiledInstruction.program_id_index)",
|
||||
)?,
|
||||
accounts: ix.accounts,
|
||||
data: ix.data,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(if message.versioned {
|
||||
let mut address_table_lookups = Vec::with_capacity(message.address_table_lookups.len());
|
||||
for table in message.address_table_lookups {
|
||||
address_table_lookups.push(MessageAddressTableLookup {
|
||||
account_key: ensure_some(
|
||||
Pubkey::try_from(table.account_key.as_slice()).ok(),
|
||||
"failed to parse Pubkey",
|
||||
)?,
|
||||
writable_indexes: table.writable_indexes,
|
||||
readonly_indexes: table.readonly_indexes,
|
||||
});
|
||||
}
|
||||
|
||||
VersionedMessage::V0(MessageV0 {
|
||||
header,
|
||||
account_keys: create_pubkey_vec(message.account_keys)?,
|
||||
recent_blockhash: Hash::new(message.recent_blockhash.as_slice()),
|
||||
instructions,
|
||||
address_table_lookups,
|
||||
})
|
||||
} else {
|
||||
VersionedMessage::Legacy(Message {
|
||||
header,
|
||||
account_keys: create_pubkey_vec(message.account_keys)?,
|
||||
recent_blockhash: Hash::new(message.recent_blockhash.as_slice()),
|
||||
instructions,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_tx_meta(
|
||||
meta: proto::TransactionStatusMeta,
|
||||
) -> Result<TransactionStatusMeta, String> {
|
||||
let meta_status = match create_tx_error(meta.err.as_ref())? {
|
||||
Some(err) => Err(err),
|
||||
None => Ok(()),
|
||||
};
|
||||
let mut meta_inner_instructions = vec![];
|
||||
for ix in meta.inner_instructions {
|
||||
meta_inner_instructions.push(create_inner_instruction(ix)?);
|
||||
}
|
||||
let mut meta_rewards = vec![];
|
||||
for reward in meta.rewards {
|
||||
meta_rewards.push(create_reward(reward)?);
|
||||
}
|
||||
|
||||
Ok(TransactionStatusMeta {
|
||||
status: meta_status,
|
||||
fee: meta.fee,
|
||||
pre_balances: meta.pre_balances,
|
||||
post_balances: meta.post_balances,
|
||||
inner_instructions: Some(meta_inner_instructions),
|
||||
log_messages: Some(meta.log_messages),
|
||||
pre_token_balances: Some(create_token_balances(meta.pre_token_balances)?),
|
||||
post_token_balances: Some(create_token_balances(meta.post_token_balances)?),
|
||||
rewards: Some(meta_rewards),
|
||||
loaded_addresses: create_loaded_addresses(
|
||||
meta.loaded_writable_addresses,
|
||||
meta.loaded_readonly_addresses,
|
||||
)?,
|
||||
return_data: if meta.return_data_none {
|
||||
None
|
||||
} else {
|
||||
let data = ensure_some(meta.return_data, "failed to get return_data")?;
|
||||
Some(TransactionReturnData {
|
||||
program_id: ensure_some(
|
||||
Pubkey::try_from(data.program_id.as_slice()).ok(),
|
||||
"failed to parse program_id",
|
||||
)?,
|
||||
data: data.data,
|
||||
})
|
||||
},
|
||||
compute_units_consumed: meta.compute_units_consumed,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_tx_error(
|
||||
err: Option<&proto::TransactionError>,
|
||||
) -> Result<Option<TransactionError>, String> {
|
||||
ensure_some(
|
||||
err.map(|err| bincode::deserialize::<TransactionError>(&err.err))
|
||||
.transpose()
|
||||
.ok(),
|
||||
"failed to decode TransactionError",
|
||||
)
|
||||
}
|
||||
|
||||
fn create_inner_instruction(ix: proto::InnerInstructions) -> Result<InnerInstructions, String> {
|
||||
let mut instructions = vec![];
|
||||
for ix in ix.instructions {
|
||||
instructions.push(InnerInstruction {
|
||||
instruction: CompiledInstruction {
|
||||
program_id_index: ensure_some(
|
||||
ix.program_id_index.try_into().ok(),
|
||||
"failed to decode CompiledInstruction.program_id_index)",
|
||||
)?,
|
||||
accounts: ix.accounts,
|
||||
data: ix.data,
|
||||
},
|
||||
stack_height: ix.stack_height,
|
||||
});
|
||||
}
|
||||
Ok(InnerInstructions {
|
||||
index: ensure_some(
|
||||
ix.index.try_into().ok(),
|
||||
"failed to decode InnerInstructions.index",
|
||||
)?,
|
||||
instructions,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_reward(reward: proto::Reward) -> Result<Reward, String> {
|
||||
Ok(Reward {
|
||||
pubkey: reward.pubkey,
|
||||
lamports: reward.lamports,
|
||||
post_balance: reward.post_balance,
|
||||
reward_type: match ensure_some(
|
||||
proto::RewardType::from_i32(reward.reward_type),
|
||||
"failed to parse reward_type",
|
||||
)? {
|
||||
proto::RewardType::Unspecified => None,
|
||||
proto::RewardType::Fee => Some(RewardType::Fee),
|
||||
proto::RewardType::Rent => Some(RewardType::Rent),
|
||||
proto::RewardType::Staking => Some(RewardType::Staking),
|
||||
proto::RewardType::Voting => Some(RewardType::Voting),
|
||||
},
|
||||
commission: if reward.commission.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ensure_some(
|
||||
reward.commission.parse().ok(),
|
||||
"failed to parse reward commission",
|
||||
)?)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
fn create_token_balances(
|
||||
balances: Vec<proto::TokenBalance>,
|
||||
) -> Result<Vec<TransactionTokenBalance>, String> {
|
||||
let mut vec = Vec::with_capacity(balances.len());
|
||||
for balance in balances {
|
||||
let ui_amount = ensure_some(balance.ui_token_amount, "failed to get ui_token_amount")?;
|
||||
vec.push(TransactionTokenBalance {
|
||||
account_index: ensure_some(
|
||||
balance.account_index.try_into().ok(),
|
||||
"failed to parse account_index",
|
||||
)?,
|
||||
mint: balance.mint,
|
||||
ui_token_amount: UiTokenAmount {
|
||||
ui_amount: Some(ui_amount.ui_amount),
|
||||
decimals: ensure_some(
|
||||
ui_amount.decimals.try_into().ok(),
|
||||
"failed to parse decimals",
|
||||
)?,
|
||||
amount: ui_amount.amount,
|
||||
ui_amount_string: ui_amount.ui_amount_string,
|
||||
},
|
||||
owner: balance.owner,
|
||||
program_id: balance.program_id,
|
||||
});
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
fn create_loaded_addresses(
|
||||
writable: Vec<Vec<u8>>,
|
||||
readonly: Vec<Vec<u8>>,
|
||||
) -> Result<LoadedAddresses, String> {
|
||||
Ok(LoadedAddresses {
|
||||
writable: create_pubkey_vec(writable)?,
|
||||
readonly: create_pubkey_vec(readonly)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_pubkey_vec(pubkeys: Vec<Vec<u8>>) -> Result<Vec<Pubkey>, String> {
|
||||
let mut vec = Vec::with_capacity(pubkeys.len());
|
||||
for pubkey in pubkeys {
|
||||
vec.push(ensure_some(
|
||||
Pubkey::try_from(pubkey.as_slice()).ok(),
|
||||
"failed to parse Pubkey",
|
||||
)?)
|
||||
}
|
||||
Ok(vec)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue