1use {
2 crate::response::RpcSimulateTransactionResult,
3 serde_json::{json, Value},
4 solana_clock::Slot,
5 solana_pubkey::Pubkey,
6 std::fmt,
7 thiserror::Error,
8};
9
10#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
11pub enum RpcRequest {
12 Custom { method: &'static str },
13 DeregisterNode,
14 GetAccountInfo,
15 GetBalance,
16 GetBlock,
17 GetBlockHeight,
18 GetBlockProduction,
19 GetBlocks,
20 GetBlocksWithLimit,
21 GetBlockTime,
22 GetClusterNodes,
23 GetEpochInfo,
24 GetEpochSchedule,
25 GetFeeForMessage,
26 GetFirstAvailableBlock,
27 GetGenesisHash,
28 GetHealth,
29 GetIdentity,
30 GetInflationGovernor,
31 GetInflationRate,
32 GetInflationReward,
33 GetLargestAccounts,
34 GetLatestBlockhash,
35 GetLeaderSchedule,
36 GetMaxRetransmitSlot,
37 GetMaxShredInsertSlot,
38 GetMinimumBalanceForRentExemption,
39 GetMultipleAccounts,
40 GetProgramAccounts,
41 GetRecentPerformanceSamples,
42 GetRecentPrioritizationFees,
43 GetHighestSnapshotSlot,
44 GetSignaturesForAddress,
45 GetSignatureStatuses,
46 GetSlot,
47 GetSlotLeader,
48 GetSlotLeaders,
49 GetStorageTurn,
50 GetStorageTurnRate,
51 GetSlotsPerSegment,
52 GetStakeMinimumDelegation,
53 GetStoragePubkeysForSlot,
54 GetSupply,
55 GetTokenAccountBalance,
56 GetTokenAccountsByDelegate,
57 GetTokenAccountsByOwner,
58 GetTokenLargestAccounts,
59 GetTokenSupply,
60 GetTransaction,
61 GetTransactionCount,
62 GetVersion,
63 GetVoteAccounts,
64 IsBlockhashValid,
65 MinimumLedgerSlot,
66 RegisterNode,
67 RequestAirdrop,
68 SendTransaction,
69 SimulateTransaction,
70 SignVote,
71}
72
73#[allow(deprecated)]
74impl fmt::Display for RpcRequest {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 let method = match self {
77 RpcRequest::Custom { method } => method,
78 RpcRequest::DeregisterNode => "deregisterNode",
79 RpcRequest::GetAccountInfo => "getAccountInfo",
80 RpcRequest::GetBalance => "getBalance",
81 RpcRequest::GetBlock => "getBlock",
82 RpcRequest::GetBlockHeight => "getBlockHeight",
83 RpcRequest::GetBlockProduction => "getBlockProduction",
84 RpcRequest::GetBlocks => "getBlocks",
85 RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
86 RpcRequest::GetBlockTime => "getBlockTime",
87 RpcRequest::GetClusterNodes => "getClusterNodes",
88 RpcRequest::GetEpochInfo => "getEpochInfo",
89 RpcRequest::GetEpochSchedule => "getEpochSchedule",
90 RpcRequest::GetFeeForMessage => "getFeeForMessage",
91 RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
92 RpcRequest::GetGenesisHash => "getGenesisHash",
93 RpcRequest::GetHealth => "getHealth",
94 RpcRequest::GetIdentity => "getIdentity",
95 RpcRequest::GetInflationGovernor => "getInflationGovernor",
96 RpcRequest::GetInflationRate => "getInflationRate",
97 RpcRequest::GetInflationReward => "getInflationReward",
98 RpcRequest::GetLargestAccounts => "getLargestAccounts",
99 RpcRequest::GetLatestBlockhash => "getLatestBlockhash",
100 RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
101 RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
102 RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
103 RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
104 RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
105 RpcRequest::GetProgramAccounts => "getProgramAccounts",
106 RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
107 RpcRequest::GetRecentPrioritizationFees => "getRecentPrioritizationFees",
108 RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot",
109 RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
110 RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
111 RpcRequest::GetSlot => "getSlot",
112 RpcRequest::GetSlotLeader => "getSlotLeader",
113 RpcRequest::GetSlotLeaders => "getSlotLeaders",
114 RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation",
115 RpcRequest::GetStorageTurn => "getStorageTurn",
116 RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
117 RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
118 RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
119 RpcRequest::GetSupply => "getSupply",
120 RpcRequest::GetTokenAccountBalance => "getTokenAccountBalance",
121 RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
122 RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
123 RpcRequest::GetTokenSupply => "getTokenSupply",
124 RpcRequest::GetTokenLargestAccounts => "getTokenLargestAccounts",
125 RpcRequest::GetTransaction => "getTransaction",
126 RpcRequest::GetTransactionCount => "getTransactionCount",
127 RpcRequest::GetVersion => "getVersion",
128 RpcRequest::GetVoteAccounts => "getVoteAccounts",
129 RpcRequest::IsBlockhashValid => "isBlockhashValid",
130 RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
131 RpcRequest::RegisterNode => "registerNode",
132 RpcRequest::RequestAirdrop => "requestAirdrop",
133 RpcRequest::SendTransaction => "sendTransaction",
134 RpcRequest::SimulateTransaction => "simulateTransaction",
135 RpcRequest::SignVote => "signVote",
136 };
137
138 write!(f, "{method}")
139 }
140}
141
142pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
144pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
145pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
146pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
147pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
148pub const NUM_LARGEST_ACCOUNTS: usize = 20;
149pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4;
150pub const MAX_GET_SLOT_LEADERS: usize = 5000;
151
152pub const MAX_RPC_VOTE_ACCOUNT_INFO_EPOCH_CREDITS_HISTORY: usize = 5;
155
156pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
158
159impl RpcRequest {
160 pub fn build_request_json(self, id: u64, params: Value) -> Value {
161 let jsonrpc = "2.0";
162 json!({
163 "jsonrpc": jsonrpc,
164 "id": id,
165 "method": format!("{self}"),
166 "params": params,
167 })
168 }
169}
170
171#[derive(Debug)]
172pub enum RpcResponseErrorData {
173 Empty,
174 SendTransactionPreflightFailure(RpcSimulateTransactionResult),
175 NodeUnhealthy { num_slots_behind: Option<Slot> },
176}
177
178impl fmt::Display for RpcResponseErrorData {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 match self {
181 RpcResponseErrorData::SendTransactionPreflightFailure(
182 RpcSimulateTransactionResult {
183 logs: Some(logs), ..
184 },
185 ) => {
186 if logs.is_empty() {
187 Ok(())
188 } else {
189 writeln!(f, "{} log messages:", logs.len())?;
190 for log in logs {
191 writeln!(f, " {log}")?;
192 }
193 Ok(())
194 }
195 }
196 _ => Ok(()),
197 }
198 }
199}
200
201#[derive(Debug, Error)]
202#[allow(clippy::large_enum_variant)]
203pub enum RpcError {
204 #[error("RPC request error: {0}")]
205 RpcRequestError(String),
206 #[error("RPC response error {code}: {message}; {data}")]
207 RpcResponseError {
208 code: i64,
209 message: String,
210 data: RpcResponseErrorData,
211 },
212 #[error("parse error: expected {0}")]
213 ParseError(String), #[error("{0}")]
217 ForUser(String), }
219
220#[derive(Serialize, Deserialize)]
221pub enum TokenAccountsFilter {
222 Mint(Pubkey),
223 ProgramId(Pubkey),
224}
225
226#[cfg(test)]
227mod tests {
228 use {
229 super::*,
230 crate::config::RpcTokenAccountsFilter,
231 solana_commitment_config::{CommitmentConfig, CommitmentLevel},
232 };
233
234 #[test]
235 fn test_build_request_json() {
236 let test_request = RpcRequest::GetAccountInfo;
237 let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
238 let request = test_request.build_request_json(1, json!([addr]));
239 assert_eq!(request["method"], "getAccountInfo");
240 assert_eq!(request["params"], json!([addr]));
241
242 let test_request = RpcRequest::GetBalance;
243 let request = test_request.build_request_json(1, json!([addr]));
244 assert_eq!(request["method"], "getBalance");
245
246 let test_request = RpcRequest::GetEpochInfo;
247 let request = test_request.build_request_json(1, Value::Null);
248 assert_eq!(request["method"], "getEpochInfo");
249
250 let test_request = RpcRequest::GetLatestBlockhash;
251 let request = test_request.build_request_json(1, Value::Null);
252 assert_eq!(request["method"], "getLatestBlockhash");
253
254 let test_request = RpcRequest::GetSlot;
255 let request = test_request.build_request_json(1, Value::Null);
256 assert_eq!(request["method"], "getSlot");
257
258 let test_request = RpcRequest::GetTransactionCount;
259 let request = test_request.build_request_json(1, Value::Null);
260 assert_eq!(request["method"], "getTransactionCount");
261
262 let test_request = RpcRequest::RequestAirdrop;
263 let request = test_request.build_request_json(1, Value::Null);
264 assert_eq!(request["method"], "requestAirdrop");
265
266 let test_request = RpcRequest::SendTransaction;
267 let request = test_request.build_request_json(1, Value::Null);
268 assert_eq!(request["method"], "sendTransaction");
269
270 let test_request = RpcRequest::GetTokenLargestAccounts;
271 let request = test_request.build_request_json(1, Value::Null);
272 assert_eq!(request["method"], "getTokenLargestAccounts");
273 }
274
275 #[test]
276 fn test_build_request_json_config_options() {
277 let commitment_config = CommitmentConfig {
278 commitment: CommitmentLevel::Finalized,
279 };
280 let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
281
282 let test_request = RpcRequest::GetLatestBlockhash;
284 let request = test_request.build_request_json(1, json!([commitment_config]));
285 assert_eq!(request["params"], json!([commitment_config.clone()]));
286
287 let test_request = RpcRequest::GetBalance;
289 let request = test_request.build_request_json(1, json!([addr, commitment_config]));
290 assert_eq!(request["params"], json!([addr, commitment_config]));
291
292 let test_request = RpcRequest::GetTokenAccountsByOwner;
294 let mint = solana_pubkey::new_rand();
295 let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
296 let request = test_request
297 .build_request_json(1, json!([addr, token_account_filter, commitment_config]));
298 assert_eq!(
299 request["params"],
300 json!([addr, token_account_filter, commitment_config])
301 );
302 }
303}