gemachain_client/
rpc_request.rs

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