Intiail Commit
This commit is contained in:
commit
a3dca97d4d
|
@ -0,0 +1 @@
|
|||
/target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
[package]
|
||||
name = "grpc_banking_transactions_notifications"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
solana-sdk = "~1.16.3"
|
||||
solana-rpc-client = "~1.16.3"
|
||||
solana-rpc-client-api = "~1.16.3"
|
||||
solana-transaction-status = "~1.16.3"
|
||||
itertools = "0.10.5"
|
||||
serde = { version = "1.0.160", features = ["derive"] }
|
||||
serde_json = "1.0.96"
|
||||
bincode = "1.3.3"
|
||||
bs58 = "0.4.0"
|
||||
base64 = "0.21.0"
|
||||
thiserror = "1.0.40"
|
||||
futures = "0.3.28"
|
||||
bytes = "1.4.0"
|
||||
anyhow = "1.0.70"
|
||||
log = "0.4.17"
|
||||
clap = { version = "4.2.4", features = ["derive", "env"] }
|
||||
dashmap = "5.4.0"
|
||||
const_env = "0.1.2"
|
||||
chrono = "0.4.24"
|
||||
native-tls = "0.2.11"
|
||||
postgres-native-tls = "0.5.0"
|
||||
prometheus = "0.13.3"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
yellowstone-grpc-client = { path = "../yellowstone-grpc/yellowstone-grpc-client" }
|
||||
yellowstone-grpc-proto = { path = "../yellowstone-grpc/yellowstone-grpc-proto" }
|
|
@ -0,0 +1,67 @@
|
|||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use futures::StreamExt;
|
||||
use transaction_info::TransactionInfo;
|
||||
use yellowstone_grpc_client::GeyserGrpcClient;
|
||||
use yellowstone_grpc_proto::prelude::{SubscribeRequestFilterBlocks, CommitmentLevel, subscribe_update::UpdateOneof};
|
||||
|
||||
mod transaction_info;
|
||||
|
||||
#[tokio::main()]
|
||||
async fn main() {
|
||||
let grpc_addr = "http://127.0.0.1:10000";
|
||||
let mut client = GeyserGrpcClient::connect(grpc_addr, None::<&'static str>, None).unwrap();
|
||||
let map_of_infos = Arc::new(DashMap::<String, TransactionInfo>::new());
|
||||
|
||||
let mut blocks_subs = HashMap::new();
|
||||
blocks_subs.insert(
|
||||
"client".to_string(),
|
||||
SubscribeRequestFilterBlocks {
|
||||
account_include: Default::default(),
|
||||
include_transactions: Some(true),
|
||||
include_accounts: Some(false),
|
||||
include_entries: Some(false),
|
||||
},
|
||||
);
|
||||
let commitment_level = CommitmentLevel::Processed;
|
||||
|
||||
let mut stream = client
|
||||
.subscribe_once(
|
||||
HashMap::new(),
|
||||
Default::default(),
|
||||
HashMap::new(),
|
||||
Default::default(),
|
||||
blocks_subs,
|
||||
Default::default(),
|
||||
Some(commitment_level),
|
||||
Default::default(),
|
||||
true
|
||||
).await.unwrap();
|
||||
while let Some(message) = stream.next().await {
|
||||
let message = message.unwrap();
|
||||
|
||||
let Some(update) = message.update_oneof else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match update {
|
||||
UpdateOneof::BankingTransactionErrors(transaction) => {
|
||||
let sig = transaction.signature.to_string();
|
||||
match map_of_infos.get_mut(&sig) {
|
||||
Some(mut x) => {
|
||||
x.add_notification(&transaction);
|
||||
},
|
||||
None => {
|
||||
map_of_infos.insert(sig, TransactionInfo::new(transaction.signature, transaction.slot));
|
||||
}
|
||||
}
|
||||
},
|
||||
UpdateOneof::Block(block) => {
|
||||
},
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
use solana_sdk::{slot_history::Slot, transaction::Transaction, transaction::TransactionError};
|
||||
use yellowstone_grpc_proto::prelude::SubscribeUpdateBankingTransactionResults;
|
||||
|
||||
fn convert_transaction_error_into_int(error: &TransactionError)-> u8 {
|
||||
match error {
|
||||
TransactionError::AccountBorrowOutstanding=>0,
|
||||
TransactionError::AccountInUse=>1,
|
||||
TransactionError::AccountLoadedTwice=>2,
|
||||
TransactionError::AccountNotFound=>3,
|
||||
TransactionError::AddressLookupTableNotFound=>4,
|
||||
TransactionError::AlreadyProcessed=>5,
|
||||
TransactionError::BlockhashNotFound=>6,
|
||||
TransactionError::CallChainTooDeep=>7,
|
||||
TransactionError::ClusterMaintenance=>8,
|
||||
TransactionError::DuplicateInstruction(_)=>9,
|
||||
TransactionError::InstructionError(_, _) => 10,
|
||||
TransactionError::InsufficientFundsForFee=>11,
|
||||
TransactionError::InsufficientFundsForRent { .. } => 12,
|
||||
TransactionError::InvalidAccountForFee => 13,
|
||||
TransactionError::InvalidAccountIndex => 14,
|
||||
TransactionError::InvalidAddressLookupTableData=>15,
|
||||
TransactionError::InvalidAddressLookupTableIndex=>16,
|
||||
TransactionError::InvalidAddressLookupTableOwner=>17,
|
||||
TransactionError::InvalidLoadedAccountsDataSizeLimit=>18,
|
||||
TransactionError::InvalidProgramForExecution=>19,
|
||||
TransactionError::InvalidRentPayingAccount=>20,
|
||||
TransactionError::InvalidWritableAccount=>21,
|
||||
TransactionError::MaxLoadedAccountsDataSizeExceeded=>22,
|
||||
TransactionError::MissingSignatureForFee=>23,
|
||||
TransactionError::ProgramAccountNotFound=>24,
|
||||
TransactionError::ResanitizationNeeded=>25,
|
||||
TransactionError::SanitizeFailure=>26,
|
||||
TransactionError::SignatureFailure=>27,
|
||||
TransactionError::TooManyAccountLocks=>28,
|
||||
TransactionError::UnbalancedTransaction=>29,
|
||||
TransactionError::UnsupportedVersion=>30,
|
||||
TransactionError::WouldExceedAccountDataBlockLimit=>31,
|
||||
TransactionError::WouldExceedAccountDataTotalLimit=>32,
|
||||
TransactionError::WouldExceedMaxAccountCostLimit=>33,
|
||||
TransactionError::WouldExceedMaxBlockCostLimit=>34,
|
||||
TransactionError::WouldExceedMaxVoteCostLimit=>35,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct ErrorKey {
|
||||
error : TransactionError,
|
||||
slot: Slot,
|
||||
}
|
||||
|
||||
impl Hash for ErrorKey {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
let tmp = convert_transaction_error_into_int(&self.error);
|
||||
tmp.hash(state);
|
||||
self.slot.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ErrorKey {
|
||||
|
||||
}
|
||||
|
||||
pub struct TransactionInfo {
|
||||
pub signature : String,
|
||||
pub transaction_message: Option<Transaction>,
|
||||
pub errors: HashMap<ErrorKey, usize>,
|
||||
pub is_executed: bool,
|
||||
pub is_confirmed : bool,
|
||||
pub block_height : Option<u64>,
|
||||
pub first_notification_slot: u64,
|
||||
}
|
||||
|
||||
impl TransactionInfo {
|
||||
pub fn new(signature: String, first_notification_slot: Slot) -> Self {
|
||||
Self {
|
||||
signature,
|
||||
transaction_message: None,
|
||||
errors: HashMap::new(),
|
||||
is_executed: false,
|
||||
is_confirmed: false,
|
||||
first_notification_slot,
|
||||
block_height: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_notification(&mut self, notification: &SubscribeUpdateBankingTransactionResults) {
|
||||
match ¬ification.error {
|
||||
Some(error) => {
|
||||
let slot = notification.slot;
|
||||
let error: TransactionError = bincode::deserialize(&error.err).unwrap();
|
||||
let key = ErrorKey {
|
||||
error,
|
||||
slot,
|
||||
};
|
||||
match self.errors.get_mut(&key) {
|
||||
Some(x) => {
|
||||
*x = *x + 1;
|
||||
},
|
||||
None => {
|
||||
self.errors.insert(key, 1);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.is_executed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue