use {
crate::response::RpcSimulateTransactionResult,
serde_json::{json, Value},
solana_sdk::{clock::Slot, pubkey::Pubkey},
std::fmt,
thiserror::Error,
};
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum RpcRequest {
Custom { method: &'static str },
DeregisterNode,
GetAccountInfo,
GetBalance,
GetBlock,
GetBlockHeight,
GetBlockProduction,
GetBlocks,
GetBlocksWithLimit,
GetBlockTime,
GetClusterNodes,
GetEpochInfo,
GetEpochSchedule,
GetFeeForMessage,
GetFirstAvailableBlock,
GetGenesisHash,
GetHealth,
GetIdentity,
GetInflationGovernor,
GetInflationRate,
GetInflationReward,
GetLargestAccounts,
GetLatestBlockhash,
GetLeaderSchedule,
GetMaxRetransmitSlot,
GetMaxShredInsertSlot,
GetMinimumBalanceForRentExemption,
GetMultipleAccounts,
GetProgramAccounts,
GetRecentPerformanceSamples,
GetRecentPrioritizationFees,
GetHighestSnapshotSlot,
GetSignaturesForAddress,
GetSignatureStatuses,
GetSlot,
GetSlotLeader,
GetSlotLeaders,
GetStorageTurn,
GetStorageTurnRate,
GetSlotsPerSegment,
GetStakeMinimumDelegation,
GetStoragePubkeysForSlot,
GetSupply,
GetTokenAccountBalance,
GetTokenAccountsByDelegate,
GetTokenAccountsByOwner,
GetTokenLargestAccounts,
GetTokenSupply,
GetTransaction,
GetTransactionCount,
GetVersion,
GetVoteAccounts,
IsBlockhashValid,
MinimumLedgerSlot,
RegisterNode,
RequestAirdrop,
SendTransaction,
SimulateTransaction,
SignVote,
}
#[allow(deprecated)]
impl fmt::Display for RpcRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let method = match self {
RpcRequest::Custom { method } => method,
RpcRequest::DeregisterNode => "deregisterNode",
RpcRequest::GetAccountInfo => "getAccountInfo",
RpcRequest::GetBalance => "getBalance",
RpcRequest::GetBlock => "getBlock",
RpcRequest::GetBlockHeight => "getBlockHeight",
RpcRequest::GetBlockProduction => "getBlockProduction",
RpcRequest::GetBlocks => "getBlocks",
RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
RpcRequest::GetBlockTime => "getBlockTime",
RpcRequest::GetClusterNodes => "getClusterNodes",
RpcRequest::GetEpochInfo => "getEpochInfo",
RpcRequest::GetEpochSchedule => "getEpochSchedule",
RpcRequest::GetFeeForMessage => "getFeeForMessage",
RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetHealth => "getHealth",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflationGovernor => "getInflationGovernor",
RpcRequest::GetInflationRate => "getInflationRate",
RpcRequest::GetInflationReward => "getInflationReward",
RpcRequest::GetLargestAccounts => "getLargestAccounts",
RpcRequest::GetLatestBlockhash => "getLatestBlockhash",
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
RpcRequest::GetProgramAccounts => "getProgramAccounts",
RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
RpcRequest::GetRecentPrioritizationFees => "getRecentPrioritizationFees",
RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot",
RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetSlotLeaders => "getSlotLeaders",
RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation",
RpcRequest::GetStorageTurn => "getStorageTurn",
RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
RpcRequest::GetSupply => "getSupply",
RpcRequest::GetTokenAccountBalance => "getTokenAccountBalance",
RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
RpcRequest::GetTokenSupply => "getTokenSupply",
RpcRequest::GetTokenLargestAccounts => "getTokenLargestAccounts",
RpcRequest::GetTransaction => "getTransaction",
RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::GetVersion => "getVersion",
RpcRequest::GetVoteAccounts => "getVoteAccounts",
RpcRequest::IsBlockhashValid => "isBlockhashValid",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
RpcRequest::RegisterNode => "registerNode",
RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction",
RpcRequest::SimulateTransaction => "simulateTransaction",
RpcRequest::SignVote => "signVote",
};
write!(f, "{method}")
}
}
pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
pub const NUM_LARGEST_ACCOUNTS: usize = 20;
pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4;
pub const MAX_GET_SLOT_LEADERS: usize = 5000;
pub const MAX_RPC_VOTE_ACCOUNT_INFO_EPOCH_CREDITS_HISTORY: usize = 5;
pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
impl RpcRequest {
pub fn build_request_json(self, id: u64, params: Value) -> Value {
let jsonrpc = "2.0";
json!({
"jsonrpc": jsonrpc,
"id": id,
"method": format!("{self}"),
"params": params,
})
}
}
#[derive(Debug)]
pub enum RpcResponseErrorData {
Empty,
SendTransactionPreflightFailure(RpcSimulateTransactionResult),
NodeUnhealthy { num_slots_behind: Option<Slot> },
}
impl fmt::Display for RpcResponseErrorData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
RpcResponseErrorData::SendTransactionPreflightFailure(
RpcSimulateTransactionResult {
logs: Some(logs), ..
},
) => {
if logs.is_empty() {
Ok(())
} else {
writeln!(f, "{} log messages:", logs.len())?;
for log in logs {
writeln!(f, " {log}")?;
}
Ok(())
}
}
_ => Ok(()),
}
}
}
#[derive(Debug, Error)]
pub enum RpcError {
#[error("RPC request error: {0}")]
RpcRequestError(String),
#[error("RPC response error {code}: {message}; {data}")]
RpcResponseError {
code: i64,
message: String,
data: RpcResponseErrorData,
},
#[error("parse error: expected {0}")]
ParseError(String), #[error("{0}")]
ForUser(String), }
#[derive(Serialize, Deserialize)]
pub enum TokenAccountsFilter {
Mint(Pubkey),
ProgramId(Pubkey),
}
#[cfg(test)]
mod tests {
use {
super::*,
crate::config::RpcTokenAccountsFilter,
solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel},
};
#[test]
fn test_build_request_json() {
let test_request = RpcRequest::GetAccountInfo;
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
let request = test_request.build_request_json(1, json!([addr]));
assert_eq!(request["method"], "getAccountInfo");
assert_eq!(request["params"], json!([addr]));
let test_request = RpcRequest::GetBalance;
let request = test_request.build_request_json(1, json!([addr]));
assert_eq!(request["method"], "getBalance");
let test_request = RpcRequest::GetEpochInfo;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getEpochInfo");
let test_request = RpcRequest::GetLatestBlockhash;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getLatestBlockhash");
let test_request = RpcRequest::GetSlot;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getSlot");
let test_request = RpcRequest::GetTransactionCount;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getTransactionCount");
let test_request = RpcRequest::RequestAirdrop;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "requestAirdrop");
let test_request = RpcRequest::SendTransaction;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "sendTransaction");
let test_request = RpcRequest::GetTokenLargestAccounts;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getTokenLargestAccounts");
}
#[test]
fn test_build_request_json_config_options() {
let commitment_config = CommitmentConfig {
commitment: CommitmentLevel::Finalized,
};
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
let test_request = RpcRequest::GetLatestBlockhash;
let request = test_request.build_request_json(1, json!([commitment_config]));
assert_eq!(request["params"], json!([commitment_config.clone()]));
let test_request = RpcRequest::GetBalance;
let request = test_request.build_request_json(1, json!([addr, commitment_config]));
assert_eq!(request["params"], json!([addr, commitment_config]));
let test_request = RpcRequest::GetTokenAccountsByOwner;
let mint = solana_sdk::pubkey::new_rand();
let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
let request = test_request
.build_request_json(1, json!([addr, token_account_filter, commitment_config]));
assert_eq!(
request["params"],
json!([addr, token_account_filter, commitment_config])
);
}
}