use crate::rpc_response::RpcSimulateTransactionResult;
use derivative::Derivative;
use serde_json::{json, Value};
use solana_sdk::{clock::Slot, pubkey::Pubkey};
use std::fmt;
use thiserror::Error;
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
pub enum RpcRequest {
DeregisterNode,
ValidatorExit,
GetAccountInfo,
GetBalance,
GetBlockTime,
GetBlockCommitment,
GetClusterNodes,
GetConfirmedBlock,
GetConfirmedBlocks,
GetConfirmedBlocksWithLimit,
GetConfirmedSignaturesForAddress,
GetConfirmedSignaturesForAddress2,
GetConfirmedTransaction,
GetEpochInfo,
GetEpochSchedule,
GetFeeCalculatorForBlockhash,
GetFeeRateGovernor,
GetFees,
GetFirstAvailableBlock,
GetGenesisHash,
GetHealth,
GetIdentity,
GetInflationGovernor,
GetInflationRate,
GetLargestAccounts,
GetLeaderSchedule,
GetMinimumBalanceForRentExemption,
GetMultipleAccounts,
GetProgramAccounts,
GetRecentBlockhash,
GetSignatureConfirmation,
GetSnapshotSlot,
GetSignatureStatuses,
GetSignatureStatus,
GetSlot,
GetMaxRetransmitSlot,
GetMaxShredInsertSlot,
GetSlotLeader,
GetStakeActivation,
GetStorageTurn,
GetStorageTurnRate,
GetSlotsPerSegment,
GetStoragePubkeysForSlot,
GetSupply,
GetTokenAccountBalance,
GetTokenAccountsByDelegate,
GetTokenAccountsByOwner,
GetTokenSupply,
GetTokenLargestAccounts,
GetTotalSupply,
GetTransactionCount,
GetVersion,
GetVoteAccounts,
GetRecentPerfomanceSamples,
MinimumLedgerSlot,
RegisterNode,
RequestAirdrop,
SendTransaction,
SimulateTransaction,
SignVote,
SetLogFilter,
EthGetTransactionCount,
EthGetBalance,
EthGetBlockByNumber,
EthGetBlockByHash,
EthBlockNumber,
EthGetStorageAt,
EthGetCode,
EthGetTransactionByHash,
EthGetTransactionReceipt,
EthCall,
EthEstimateGas,
EthGetLogs,
GetVelasAccountsByOperationalKey,
GetVelasAccountsByOwnerKey,
}
impl fmt::Display for RpcRequest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let method = match self {
RpcRequest::DeregisterNode => "deregisterNode",
RpcRequest::ValidatorExit => "validatorExit",
RpcRequest::GetAccountInfo => "getAccountInfo",
RpcRequest::GetBalance => "getBalance",
RpcRequest::GetBlockCommitment => "getBlockCommitment",
RpcRequest::GetBlockTime => "getBlockTime",
RpcRequest::GetClusterNodes => "getClusterNodes",
RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress",
RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
RpcRequest::GetEpochInfo => "getEpochInfo",
RpcRequest::GetEpochSchedule => "getEpochSchedule",
RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
RpcRequest::GetFees => "getFees",
RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
RpcRequest::GetGenesisHash => "getGenesisHash",
RpcRequest::GetHealth => "getHealth",
RpcRequest::GetIdentity => "getIdentity",
RpcRequest::GetInflationGovernor => "getInflationGovernor",
RpcRequest::GetInflationRate => "getInflationRate",
RpcRequest::GetLargestAccounts => "getLargestAccounts",
RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
RpcRequest::GetProgramAccounts => "getProgramAccounts",
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetSignatureConfirmation => "getSignatureConfirmation",
RpcRequest::GetSnapshotSlot => "getSnapshotSlot",
RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetSlot => "getSlot",
RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetStakeActivation => "getStakeActivation",
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::GetTotalSupply => "getTotalSupply",
RpcRequest::GetTokenLargestAccounts => "getTokenLargestAccounts",
RpcRequest::GetTransactionCount => "getTransactionCount",
RpcRequest::GetVersion => "getVersion",
RpcRequest::GetVoteAccounts => "getVoteAccounts",
RpcRequest::GetRecentPerfomanceSamples => "getRecentPerformanceSamples",
RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
RpcRequest::RegisterNode => "registerNode",
RpcRequest::RequestAirdrop => "requestAirdrop",
RpcRequest::SendTransaction => "sendTransaction",
RpcRequest::SimulateTransaction => "simulateTransaction",
RpcRequest::SignVote => "signVote",
RpcRequest::SetLogFilter => "setLogFilter",
RpcRequest::EthGetTransactionCount => "eth_getTransactionCount",
RpcRequest::EthGetBalance => "eth_getBalance",
RpcRequest::EthGetBlockByNumber => "eth_getBlockByNumber",
RpcRequest::EthGetBlockByHash => "eth_getBlockByHash",
RpcRequest::EthBlockNumber => "eth_blockNumber",
RpcRequest::EthGetStorageAt => "eth_getStorageAt",
RpcRequest::EthGetCode => "eth_getCode",
RpcRequest::EthGetTransactionByHash => "eth_getTransactionByHash",
RpcRequest::EthGetTransactionReceipt => "eth_getTransactionReceipt",
RpcRequest::EthCall => "eth_call",
RpcRequest::EthEstimateGas => "eth_estimateGas",
RpcRequest::EthGetLogs => "eth_getLogs",
Self::GetVelasAccountsByOperationalKey => "getVelasAccountsByOperationalKey",
Self::GetVelasAccountsByOwnerKey => "getVelasAccountsByOwnerKey",
};
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 DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
impl RpcRequest {
pub(crate) 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 {
write!(f, "[{} log messages]", logs.len())
}
}
_ => Ok(()),
}
}
}
#[derive(Derivative, Error)]
#[derivative(Debug)]
pub enum RpcError {
#[error("RPC request error: {0}")]
RpcRequestError(String),
#[error("RPC response error {code}: {message} {data}")]
RpcResponseError {
code: i64,
message: String,
data: RpcResponseErrorData,
#[derivative(Debug = "ignore")]
original_err: serde_json::Value,
},
#[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::*;
use crate::rpc_config::RpcTokenAccountsFilter;
use 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::GetRecentBlockhash;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getRecentBlockhash");
let test_request = RpcRequest::GetFeeCalculatorForBlockhash;
let request = test_request.build_request_json(1, json!([addr]));
assert_eq!(request["method"], "getFeeCalculatorForBlockhash");
let test_request = RpcRequest::GetFeeRateGovernor;
let request = test_request.build_request_json(1, Value::Null);
assert_eq!(request["method"], "getFeeRateGovernor");
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");
}
#[test]
fn test_build_request_json_config_options() {
let commitment_config = CommitmentConfig {
commitment: CommitmentLevel::Finalized,
};
let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
let test_request = RpcRequest::GetRecentBlockhash;
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])
);
}
}