From ed4350d31670864392669f58d4292df3da285d17 Mon Sep 17 00:00:00 2001 From: Kirill Fomichev Date: Tue, 3 Oct 2023 18:09:05 +0400 Subject: [PATCH] client: add tx pretty print to rust (#189) --- CHANGELOG.md | 1 + Cargo.lock | 2 ++ examples/rust/Cargo.toml | 2 ++ examples/rust/src/bin/client.rs | 57 +++++++++++++++++++++++++++++++-- 4 files changed, 59 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9aa1ffe..d4e9502 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ 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)). +- client: add tx pretty print to rust ([#189](https://github.com/rpcpool/yellowstone-grpc/pull/189)). ### Fixes diff --git a/Cargo.lock b/Cargo.lock index 6d8cb91..7955d87 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4493,7 +4493,9 @@ dependencies = [ "hex", "log", "maplit", + "serde_json", "solana-sdk", + "solana-transaction-status", "tokio", "yellowstone-grpc-client", "yellowstone-grpc-proto", diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index 19252e4..9732135 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -20,7 +20,9 @@ futures = "0.3.24" hex = "0.4.3" log = { version = "0.4.14", features = ["std"] } maplit = "1.0.2" +serde_json = "1.0.86" solana-sdk = "=1.16.14" +solana-transaction-status = "=1.16.14" tokio = { version = "1.21.2", features = ["rt-multi-thread", "macros", "time"] } yellowstone-grpc-client = { path = "../../yellowstone-grpc-client" } yellowstone-grpc-proto = { path = "../../yellowstone-grpc-proto" } diff --git a/examples/rust/src/bin/client.rs b/examples/rust/src/bin/client.rs index 109b2fc..afb631e 100644 --- a/examples/rust/src/bin/client.rs +++ b/examples/rust/src/bin/client.rs @@ -3,10 +3,11 @@ use { clap::{Parser, Subcommand, ValueEnum}, futures::{future::TryFutureExt, sink::SinkExt, stream::StreamExt}, log::{error, info}, - solana_sdk::pubkey::Pubkey, + solana_sdk::{pubkey::Pubkey, signature::Signature}, + solana_transaction_status::{EncodedTransactionWithStatusMeta, UiTransactionEncoding}, std::{ collections::HashMap, - env, + env, fmt, sync::{Arc, Mutex}, time::Duration, }, @@ -20,7 +21,7 @@ use { SubscribeRequestFilterAccountsFilter, SubscribeRequestFilterAccountsFilterMemcmp, SubscribeRequestFilterBlocks, SubscribeRequestFilterBlocksMeta, SubscribeRequestFilterEntry, SubscribeRequestFilterSlots, - SubscribeRequestFilterTransactions, SubscribeUpdateAccount, + SubscribeRequestFilterTransactions, SubscribeUpdateAccount, SubscribeUpdateTransaction, }, tonic::service::Interceptor, }, @@ -353,6 +354,48 @@ impl From for AccountPretty { } } +#[allow(dead_code)] +pub struct TransactionPretty { + slot: u64, + signature: Signature, + is_vote: bool, + tx: EncodedTransactionWithStatusMeta, +} + +impl fmt::Debug for TransactionPretty { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + struct TxWrap<'a>(&'a EncodedTransactionWithStatusMeta); + impl<'a> fmt::Debug for TxWrap<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let serialized = serde_json::to_string(self.0).expect("failed to serialize"); + fmt::Display::fmt(&serialized, f) + } + } + + f.debug_struct("TransactionPretty") + .field("slot", &self.slot) + .field("signature", &self.signature) + .field("is_vote", &self.is_vote) + .field("tx", &TxWrap(&self.tx)) + .finish() + } +} + +impl From for TransactionPretty { + fn from(SubscribeUpdateTransaction { transaction, slot }: SubscribeUpdateTransaction) -> Self { + let tx = transaction.expect("should be defined"); + Self { + slot, + signature: Signature::try_from(tx.signature.as_slice()).expect("valid signature"), + is_vote: tx.is_vote, + tx: yellowstone_grpc_proto::convert_from::create_tx_with_meta(tx) + .expect("valid tx with meta") + .encode(UiTransactionEncoding::Base64, Some(u8::MAX), true) + .expect("failed to encode"), + } + } +} + #[tokio::main] async fn main() -> anyhow::Result<()> { env::set_var( @@ -485,6 +528,14 @@ async fn geyser_subscribe( ); continue; } + Some(UpdateOneof::Transaction(tx)) => { + let tx: TransactionPretty = tx.into(); + info!( + "new transaction update: filters {:?}, transaction: {:#?}", + msg.filters, tx + ); + continue; + } _ => {} } info!("new message: {msg:?}")