safecoin_client/
rpc_request.rs

1use {
2    crate::rpc_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 {
12        method: &'static str,
13    },
14    DeregisterNode,
15    GetAccountInfo,
16    GetBalance,
17    GetBlock,
18    GetBlockHeight,
19    GetBlockProduction,
20    GetBlocks,
21    GetBlocksWithLimit,
22    GetBlockTime,
23    GetClusterNodes,
24    #[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlock instead")]
25    GetConfirmedBlock,
26    #[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlocks instead")]
27    GetConfirmedBlocks,
28    #[deprecated(
29        since = "1.7.0",
30        note = "Please use RpcRequest::GetBlocksWithLimit instead"
31    )]
32    GetConfirmedBlocksWithLimit,
33    #[deprecated(
34        since = "1.7.0",
35        note = "Please use RpcRequest::GetSignaturesForAddress instead"
36    )]
37    GetConfirmedSignaturesForAddress2,
38    #[deprecated(
39        since = "1.7.0",
40        note = "Please use RpcRequest::GetTransaction instead"
41    )]
42    GetConfirmedTransaction,
43    GetEpochInfo,
44    GetEpochSchedule,
45    #[deprecated(
46        since = "1.9.0",
47        note = "Please use RpcRequest::GetFeeForMessage instead"
48    )]
49    GetFeeCalculatorForBlockhash,
50    GetFeeForMessage,
51    #[deprecated(
52        since = "1.9.0",
53        note = "Please do not use, will no longer be available in the future"
54    )]
55    GetFeeRateGovernor,
56    #[deprecated(
57        since = "1.9.0",
58        note = "Please use RpcRequest::GetFeeForMessage instead"
59    )]
60    GetFees,
61    GetFirstAvailableBlock,
62    GetGenesisHash,
63    GetHealth,
64    GetIdentity,
65    GetInflationGovernor,
66    GetInflationRate,
67    GetInflationReward,
68    GetLargestAccounts,
69    GetLatestBlockhash,
70    GetLeaderSchedule,
71    GetMaxRetransmitSlot,
72    GetMaxShredInsertSlot,
73    GetMinimumBalanceForRentExemption,
74    GetMultipleAccounts,
75    GetProgramAccounts,
76    #[deprecated(
77        since = "1.9.0",
78        note = "Please use RpcRequest::GetLatestBlockhash instead"
79    )]
80    GetRecentBlockhash,
81    GetRecentPerformanceSamples,
82    GetRecentPrioritizationFees,
83    GetHighestSnapshotSlot,
84    #[deprecated(
85        since = "1.9.0",
86        note = "Please use RpcRequest::GetHighestSnapshotSlot instead"
87    )]
88    GetSnapshotSlot,
89    GetSignaturesForAddress,
90    GetSignatureStatuses,
91    GetSlot,
92    GetSlotLeader,
93    GetSlotLeaders,
94    GetStorageTurn,
95    GetStorageTurnRate,
96    GetSlotsPerSegment,
97    GetStakeActivation,
98    GetStakeMinimumDelegation,
99    GetStoragePubkeysForSlot,
100    GetSupply,
101    GetTokenAccountBalance,
102    GetTokenAccountsByDelegate,
103    GetTokenAccountsByOwner,
104    GetTokenSupply,
105    GetTransaction,
106    GetTransactionCount,
107    GetVersion,
108    GetVoteAccounts,
109    IsBlockhashValid,
110    MinimumLedgerSlot,
111    RegisterNode,
112    RequestAirdrop,
113    SendTransaction,
114    SimulateTransaction,
115    SignVote,
116}
117
118#[allow(deprecated)]
119impl fmt::Display for RpcRequest {
120    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
121        let method = match self {
122            RpcRequest::Custom { method } => method,
123            RpcRequest::DeregisterNode => "deregisterNode",
124            RpcRequest::GetAccountInfo => "getAccountInfo",
125            RpcRequest::GetBalance => "getBalance",
126            RpcRequest::GetBlock => "getBlock",
127            RpcRequest::GetBlockHeight => "getBlockHeight",
128            RpcRequest::GetBlockProduction => "getBlockProduction",
129            RpcRequest::GetBlocks => "getBlocks",
130            RpcRequest::GetBlocksWithLimit => "getBlocksWithLimit",
131            RpcRequest::GetBlockTime => "getBlockTime",
132            RpcRequest::GetClusterNodes => "getClusterNodes",
133            RpcRequest::GetConfirmedBlock => "getConfirmedBlock",
134            RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks",
135            RpcRequest::GetConfirmedBlocksWithLimit => "getConfirmedBlocksWithLimit",
136            RpcRequest::GetConfirmedSignaturesForAddress2 => "getConfirmedSignaturesForAddress2",
137            RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction",
138            RpcRequest::GetEpochInfo => "getEpochInfo",
139            RpcRequest::GetEpochSchedule => "getEpochSchedule",
140            RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash",
141            RpcRequest::GetFeeForMessage => "getFeeForMessage",
142            RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor",
143            RpcRequest::GetFees => "getFees",
144            RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock",
145            RpcRequest::GetGenesisHash => "getGenesisHash",
146            RpcRequest::GetHealth => "getHealth",
147            RpcRequest::GetIdentity => "getIdentity",
148            RpcRequest::GetInflationGovernor => "getInflationGovernor",
149            RpcRequest::GetInflationRate => "getInflationRate",
150            RpcRequest::GetInflationReward => "getInflationReward",
151            RpcRequest::GetLargestAccounts => "getLargestAccounts",
152            RpcRequest::GetLatestBlockhash => "getLatestBlockhash",
153            RpcRequest::GetLeaderSchedule => "getLeaderSchedule",
154            RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot",
155            RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot",
156            RpcRequest::GetMinimumBalanceForRentExemption => "getMinimumBalanceForRentExemption",
157            RpcRequest::GetMultipleAccounts => "getMultipleAccounts",
158            RpcRequest::GetProgramAccounts => "getProgramAccounts",
159            RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
160            RpcRequest::GetRecentPerformanceSamples => "getRecentPerformanceSamples",
161            RpcRequest::GetRecentPrioritizationFees => "getRecentPrioritizationFees",
162            RpcRequest::GetHighestSnapshotSlot => "getHighestSnapshotSlot",
163            RpcRequest::GetSnapshotSlot => "getSnapshotSlot",
164            RpcRequest::GetSignaturesForAddress => "getSignaturesForAddress",
165            RpcRequest::GetSignatureStatuses => "getSignatureStatuses",
166            RpcRequest::GetSlot => "getSlot",
167            RpcRequest::GetSlotLeader => "getSlotLeader",
168            RpcRequest::GetSlotLeaders => "getSlotLeaders",
169            RpcRequest::GetStakeActivation => "getStakeActivation",
170            RpcRequest::GetStakeMinimumDelegation => "getStakeMinimumDelegation",
171            RpcRequest::GetStorageTurn => "getStorageTurn",
172            RpcRequest::GetStorageTurnRate => "getStorageTurnRate",
173            RpcRequest::GetSlotsPerSegment => "getSlotsPerSegment",
174            RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",
175            RpcRequest::GetSupply => "getSupply",
176            RpcRequest::GetTokenAccountBalance => "getTokenAccountBalance",
177            RpcRequest::GetTokenAccountsByDelegate => "getTokenAccountsByDelegate",
178            RpcRequest::GetTokenAccountsByOwner => "getTokenAccountsByOwner",
179            RpcRequest::GetTokenSupply => "getTokenSupply",
180            RpcRequest::GetTransaction => "getTransaction",
181            RpcRequest::GetTransactionCount => "getTransactionCount",
182            RpcRequest::GetVersion => "getVersion",
183            RpcRequest::GetVoteAccounts => "getVoteAccounts",
184            RpcRequest::IsBlockhashValid => "isBlockhashValid",
185            RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot",
186            RpcRequest::RegisterNode => "registerNode",
187            RpcRequest::RequestAirdrop => "requestAirdrop",
188            RpcRequest::SendTransaction => "sendTransaction",
189            RpcRequest::SimulateTransaction => "simulateTransaction",
190            RpcRequest::SignVote => "signVote",
191        };
192
193        write!(f, "{}", method)
194    }
195}
196
197// Changing any of these? Update the JSON RPC docs!
198pub const MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS: usize = 256;
199pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS_SLOT_RANGE: u64 = 10_000;
200pub const MAX_GET_CONFIRMED_BLOCKS_RANGE: u64 = 500_000;
201pub const MAX_GET_CONFIRMED_SIGNATURES_FOR_ADDRESS2_LIMIT: usize = 1_000;
202pub const MAX_MULTIPLE_ACCOUNTS: usize = 100;
203pub const NUM_LARGEST_ACCOUNTS: usize = 20;
204pub const MAX_GET_PROGRAM_ACCOUNT_FILTERS: usize = 4;
205pub const MAX_GET_SLOT_LEADERS: usize = 5000;
206
207// Validators that are this number of slots behind are considered delinquent
208pub const DELINQUENT_VALIDATOR_SLOT_DISTANCE: u64 = 128;
209
210impl RpcRequest {
211    pub(crate) fn build_request_json(self, id: u64, params: Value) -> Value {
212        let jsonrpc = "2.0";
213        json!({
214           "jsonrpc": jsonrpc,
215           "id": id,
216           "method": format!("{}", self),
217           "params": params,
218        })
219    }
220}
221
222#[derive(Debug)]
223pub enum RpcResponseErrorData {
224    Empty,
225    SendTransactionPreflightFailure(RpcSimulateTransactionResult),
226    NodeUnhealthy { num_slots_behind: Option<Slot> },
227}
228
229impl fmt::Display for RpcResponseErrorData {
230    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231        match self {
232            RpcResponseErrorData::SendTransactionPreflightFailure(
233                RpcSimulateTransactionResult {
234                    logs: Some(logs), ..
235                },
236            ) => {
237                if logs.is_empty() {
238                    Ok(())
239                } else {
240                    // Give the user a hint that there is more useful logging information available...
241                    write!(f, "[{} log messages]", logs.len())
242                }
243            }
244            _ => Ok(()),
245        }
246    }
247}
248
249#[derive(Debug, Error)]
250pub enum RpcError {
251    #[error("RPC request error: {0}")]
252    RpcRequestError(String),
253    #[error("RPC response error {code}: {message} {data}")]
254    RpcResponseError {
255        code: i64,
256        message: String,
257        data: RpcResponseErrorData,
258    },
259    #[error("parse error: expected {0}")]
260    ParseError(String), /* "expected" */
261    // Anything in a `ForUser` needs to die.  The caller should be
262    // deciding what to tell their user
263    #[error("{0}")]
264    ForUser(String), /* "direct-to-user message" */
265}
266
267#[derive(Serialize, Deserialize)]
268pub enum TokenAccountsFilter {
269    Mint(Pubkey),
270    ProgramId(Pubkey),
271}
272
273#[cfg(test)]
274mod tests {
275    use {
276        super::*,
277        crate::rpc_config::RpcTokenAccountsFilter,
278        solana_sdk::commitment_config::{CommitmentConfig, CommitmentLevel},
279    };
280
281    #[test]
282    fn test_build_request_json() {
283        let test_request = RpcRequest::GetAccountInfo;
284        let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
285        let request = test_request.build_request_json(1, json!([addr]));
286        assert_eq!(request["method"], "getAccountInfo");
287        assert_eq!(request["params"], json!([addr]));
288
289        let test_request = RpcRequest::GetBalance;
290        let request = test_request.build_request_json(1, json!([addr]));
291        assert_eq!(request["method"], "getBalance");
292
293        let test_request = RpcRequest::GetEpochInfo;
294        let request = test_request.build_request_json(1, Value::Null);
295        assert_eq!(request["method"], "getEpochInfo");
296
297        #[allow(deprecated)]
298        let test_request = RpcRequest::GetRecentBlockhash;
299        let request = test_request.build_request_json(1, Value::Null);
300        assert_eq!(request["method"], "getRecentBlockhash");
301
302        #[allow(deprecated)]
303        let test_request = RpcRequest::GetFeeCalculatorForBlockhash;
304        let request = test_request.build_request_json(1, json!([addr]));
305        assert_eq!(request["method"], "getFeeCalculatorForBlockhash");
306
307        #[allow(deprecated)]
308        let test_request = RpcRequest::GetFeeRateGovernor;
309        let request = test_request.build_request_json(1, Value::Null);
310        assert_eq!(request["method"], "getFeeRateGovernor");
311
312        let test_request = RpcRequest::GetSlot;
313        let request = test_request.build_request_json(1, Value::Null);
314        assert_eq!(request["method"], "getSlot");
315
316        let test_request = RpcRequest::GetTransactionCount;
317        let request = test_request.build_request_json(1, Value::Null);
318        assert_eq!(request["method"], "getTransactionCount");
319
320        let test_request = RpcRequest::RequestAirdrop;
321        let request = test_request.build_request_json(1, Value::Null);
322        assert_eq!(request["method"], "requestAirdrop");
323
324        let test_request = RpcRequest::SendTransaction;
325        let request = test_request.build_request_json(1, Value::Null);
326        assert_eq!(request["method"], "sendTransaction");
327    }
328
329    #[test]
330    fn test_build_request_json_config_options() {
331        let commitment_config = CommitmentConfig {
332            commitment: CommitmentLevel::Finalized,
333        };
334        let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx");
335
336        // Test request with CommitmentConfig and no params
337        #[allow(deprecated)]
338        let test_request = RpcRequest::GetRecentBlockhash;
339        let request = test_request.build_request_json(1, json!([commitment_config]));
340        assert_eq!(request["params"], json!([commitment_config.clone()]));
341
342        // Test request with CommitmentConfig and params
343        let test_request = RpcRequest::GetBalance;
344        let request = test_request.build_request_json(1, json!([addr, commitment_config]));
345        assert_eq!(request["params"], json!([addr, commitment_config]));
346
347        // Test request with CommitmentConfig and params
348        let test_request = RpcRequest::GetTokenAccountsByOwner;
349        let mint = solana_sdk::pubkey::new_rand();
350        let token_account_filter = RpcTokenAccountsFilter::Mint(mint.to_string());
351        let request = test_request
352            .build_request_json(1, json!([addr, token_account_filter, commitment_config]));
353        assert_eq!(
354            request["params"],
355            json!([addr, token_account_filter, commitment_config])
356        );
357    }
358}