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