use chrono::Utc;
use solana_client::rpc_request::RpcRequest;
use std::collections::{HashMap, HashSet};
const ALL_ENDPOINTS: [RpcRequest; 57] = [
RpcRequest::DeregisterNode,
RpcRequest::GetAccountInfo,
RpcRequest::GetBalance,
RpcRequest::GetBlock,
RpcRequest::GetBlockHeight,
RpcRequest::GetBlockProduction,
RpcRequest::GetBlocks,
RpcRequest::GetBlocksWithLimit,
RpcRequest::GetBlockTime,
RpcRequest::GetClusterNodes,
RpcRequest::GetEpochInfo,
RpcRequest::GetEpochSchedule,
RpcRequest::GetFeeForMessage,
RpcRequest::GetFirstAvailableBlock,
RpcRequest::GetGenesisHash,
RpcRequest::GetHealth,
RpcRequest::GetIdentity,
RpcRequest::GetInflationGovernor,
RpcRequest::GetInflationRate,
RpcRequest::GetInflationReward,
RpcRequest::GetLargestAccounts,
RpcRequest::GetLatestBlockhash,
RpcRequest::GetLeaderSchedule,
RpcRequest::GetMaxRetransmitSlot,
RpcRequest::GetMaxShredInsertSlot,
RpcRequest::GetMinimumBalanceForRentExemption,
RpcRequest::GetMultipleAccounts,
RpcRequest::GetProgramAccounts,
RpcRequest::GetRecentPerformanceSamples,
RpcRequest::GetHighestSnapshotSlot,
RpcRequest::GetSignaturesForAddress,
RpcRequest::GetSignatureStatuses,
RpcRequest::GetSlot,
RpcRequest::GetSlotLeader,
RpcRequest::GetSlotLeaders,
RpcRequest::GetStorageTurn,
RpcRequest::GetStorageTurnRate,
RpcRequest::GetSlotsPerSegment,
RpcRequest::GetStakeActivation,
RpcRequest::GetStakeMinimumDelegation,
RpcRequest::GetStoragePubkeysForSlot,
RpcRequest::GetSupply,
RpcRequest::GetTokenAccountBalance,
RpcRequest::GetTokenAccountsByDelegate,
RpcRequest::GetTokenAccountsByOwner,
RpcRequest::GetTokenSupply,
RpcRequest::GetTransaction,
RpcRequest::GetTransactionCount,
RpcRequest::GetVersion,
RpcRequest::GetVoteAccounts,
RpcRequest::IsBlockhashValid,
RpcRequest::MinimumLedgerSlot,
RpcRequest::RegisterNode,
RpcRequest::RequestAirdrop,
RpcRequest::SendTransaction,
RpcRequest::SimulateTransaction,
RpcRequest::SignVote,
];
#[derive(Debug)]
pub struct SolanaClientRateLimit {
pub endpoint_amounts: HashMap<RpcRequest, u64>,
pub ignore_endpoints: HashSet<RpcRequest>,
pub last_datetime: i64,
pub interval: i64,
pub consumed_amount: u64,
pub maximum_amount: u64,
pub default_amount_per_endpoint: u64,
}
impl SolanaClientRateLimit {
pub fn new(interval: u64, maximum_amount: u64, default_amount_per_endpoint: u64) -> Self {
Self {
endpoint_amounts: HashMap::new(),
ignore_endpoints: HashSet::new(),
last_datetime: 0,
interval: interval as i64,
consumed_amount: 0,
maximum_amount,
default_amount_per_endpoint,
}
}
pub fn add_endpoint_amount(mut self, endpoint: RpcRequest, amount: u64) -> Self {
self.endpoint_amounts.insert(endpoint, amount);
self.ignore_endpoints.remove(&endpoint);
self
}
pub fn ignore_endpoint(mut self, endpoint: RpcRequest) -> Self {
self.ignore_endpoints.insert(endpoint);
self.endpoint_amounts.remove(&endpoint);
self
}
pub fn ignores_all_endpoints(mut self) -> Self {
if self.ignore_endpoints.len() < ALL_ENDPOINTS.len() {
self.ignore_endpoints
.reserve(ALL_ENDPOINTS.len() - self.ignore_endpoints.len());
}
for endpoint in ALL_ENDPOINTS {
self.ignore_endpoints.insert(endpoint);
self.endpoint_amounts.remove(&endpoint);
}
self
}
pub fn check_endpoint(&mut self, endpoint: RpcRequest) -> bool {
self.check_endpoints(&[endpoint])
}
pub fn check_endpoints(&mut self, endpoints: &[RpcRequest]) -> bool {
let now = Utc::now().timestamp_millis() / self.interval;
if now != self.last_datetime {
self.last_datetime = now;
self.consumed_amount = 0;
}
let mut consumed_amount = self.consumed_amount;
for endpoint in endpoints {
if self.ignore_endpoints.contains(endpoint) {
continue;
}
let amount = *self
.endpoint_amounts
.get(endpoint)
.unwrap_or(&self.default_amount_per_endpoint);
consumed_amount += amount;
if consumed_amount > self.maximum_amount {
return false;
}
}
true
}
pub fn apply_endpoint(&mut self, endpoint: RpcRequest) {
self.apply_endpoints(&[endpoint]);
}
pub fn apply_endpoints(&mut self, endpoints: &[RpcRequest]) {
for endpoint in endpoints {
if self.ignore_endpoints.contains(endpoint) {
continue;
}
let amount = *self
.endpoint_amounts
.get(endpoint)
.unwrap_or(&self.default_amount_per_endpoint);
self.consumed_amount += amount;
}
}
}