iota_json_rpc_api/
lib.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5//! This crate provides various APIs for interacting with the IOTA blockchain.
6//! It includes methods for querying data, building and executing transactions,
7//! managing governance-related data, and more.
8
9use anyhow::anyhow;
10pub use bridge::{BridgeReadApiClient, BridgeReadApiOpenRpc, BridgeReadApiServer};
11pub use coin::{CoinReadApiClient, CoinReadApiOpenRpc, CoinReadApiServer};
12pub use extended::{ExtendedApiClient, ExtendedApiOpenRpc, ExtendedApiServer};
13pub use governance::{GovernanceReadApiClient, GovernanceReadApiOpenRpc, GovernanceReadApiServer};
14pub use indexer::{IndexerApiClient, IndexerApiOpenRpc, IndexerApiServer};
15use jsonrpsee::{
16    core::ClientError,
17    types::{
18        ErrorObjectOwned,
19        error::{INTERNAL_ERROR_CODE, UNKNOWN_ERROR_CODE},
20    },
21};
22pub use move_utils::{MoveUtilsClient, MoveUtilsOpenRpc, MoveUtilsServer};
23use once_cell::sync::Lazy;
24use prometheus::{
25    Histogram, IntCounter, register_histogram_with_registry, register_int_counter_with_registry,
26};
27pub use read::{ReadApiClient, ReadApiOpenRpc, ReadApiServer};
28use tap::TapFallible;
29use tracing::warn;
30pub use transaction_builder::{
31    TransactionBuilderClient, TransactionBuilderOpenRpc, TransactionBuilderServer,
32};
33pub use write::{WriteApiClient, WriteApiOpenRpc, WriteApiServer};
34
35mod bridge;
36mod coin;
37mod extended;
38mod governance;
39mod indexer;
40mod move_utils;
41mod read;
42mod transaction_builder;
43mod write;
44
45const RPC_QUERY_MAX_RESULT_LIMIT: &str = "RPC_QUERY_MAX_RESULT_LIMIT";
46const DEFAULT_RPC_QUERY_MAX_RESULT_LIMIT: usize = 50;
47
48pub static QUERY_MAX_RESULT_LIMIT: Lazy<usize> = Lazy::new(|| {
49    read_size_from_env(RPC_QUERY_MAX_RESULT_LIMIT).unwrap_or(DEFAULT_RPC_QUERY_MAX_RESULT_LIMIT)
50});
51
52// TODOD(chris): make this configurable
53pub const QUERY_MAX_RESULT_LIMIT_CHECKPOINTS: usize = 100;
54
55pub fn cap_page_limit(limit: Option<usize>) -> usize {
56    let limit = limit.unwrap_or_default();
57    if limit > *QUERY_MAX_RESULT_LIMIT || limit == 0 {
58        *QUERY_MAX_RESULT_LIMIT
59    } else {
60        limit
61    }
62}
63
64pub fn validate_limit(limit: Option<usize>, max: usize) -> Result<usize, anyhow::Error> {
65    match limit {
66        Some(l) if l > max => Err(anyhow!("Page size limit {l} exceeds max limit {max}")),
67        Some(0) => Err(anyhow!("Page size limit cannot be smaller than 1")),
68        Some(l) => Ok(l),
69        None => Ok(max),
70    }
71}
72
73#[derive(Clone)]
74pub struct JsonRpcMetrics {
75    pub get_objects_limit: Histogram,
76    pub get_objects_result_size: Histogram,
77    pub get_objects_result_size_total: IntCounter,
78    pub get_tx_blocks_limit: Histogram,
79    pub get_tx_blocks_result_size: Histogram,
80    pub get_tx_blocks_result_size_total: IntCounter,
81    pub get_checkpoints_limit: Histogram,
82    pub get_checkpoints_result_size: Histogram,
83    pub get_checkpoints_result_size_total: IntCounter,
84    pub get_owned_objects_limit: Histogram,
85    pub get_owned_objects_result_size: Histogram,
86    pub get_owned_objects_result_size_total: IntCounter,
87    pub get_coins_limit: Histogram,
88    pub get_coins_result_size: Histogram,
89    pub get_coins_result_size_total: IntCounter,
90    pub get_dynamic_fields_limit: Histogram,
91    pub get_dynamic_fields_result_size: Histogram,
92    pub get_dynamic_fields_result_size_total: IntCounter,
93    pub query_tx_blocks_limit: Histogram,
94    pub query_tx_blocks_result_size: Histogram,
95    pub query_tx_blocks_result_size_total: IntCounter,
96    pub query_events_limit: Histogram,
97    pub query_events_result_size: Histogram,
98    pub query_events_result_size_total: IntCounter,
99
100    pub get_stake_iota_result_size: Histogram,
101    pub get_stake_iota_result_size_total: IntCounter,
102
103    pub get_stake_iota_latency: Histogram,
104    pub get_delegated_iota_latency: Histogram,
105
106    pub orchestrator_latency_ms: Histogram,
107    pub post_orchestrator_latency_ms: Histogram,
108}
109
110impl JsonRpcMetrics {
111    pub fn new(registry: &prometheus::Registry) -> Self {
112        Self {
113            get_objects_limit: register_histogram_with_registry!(
114                "json_rpc_get_objects_limit",
115                "The input limit for multi_get_objects, after applying the cap",
116                iota_metrics::COUNT_BUCKETS.to_vec(),
117                registry,
118            )
119            .unwrap(),
120            get_objects_result_size: register_histogram_with_registry!(
121                "json_rpc_get_objects_result_size",
122                "The return size for multi_get_objects",
123                iota_metrics::COUNT_BUCKETS.to_vec(),
124                registry,
125            )
126            .unwrap(),
127            get_objects_result_size_total: register_int_counter_with_registry!(
128                "json_rpc_get_objects_result_size_total",
129                "The total return size for multi_get_objects",
130                registry
131            )
132            .unwrap(),
133            get_tx_blocks_limit: register_histogram_with_registry!(
134                "json_rpc_get_tx_blocks_limit",
135                "The input limit for get_tx_blocks, after applying the cap",
136                iota_metrics::COUNT_BUCKETS.to_vec(),
137                registry,
138            )
139            .unwrap(),
140            get_tx_blocks_result_size: register_histogram_with_registry!(
141                "json_rpc_get_tx_blocks_result_size",
142                "The return size for get_tx_blocks",
143                iota_metrics::COUNT_BUCKETS.to_vec(),
144                registry,
145            )
146            .unwrap(),
147            get_tx_blocks_result_size_total: register_int_counter_with_registry!(
148                "json_rpc_get_tx_blocks_result_size_total",
149                "The total return size for get_tx_blocks",
150                registry
151            )
152            .unwrap(),
153            get_checkpoints_limit: register_histogram_with_registry!(
154                "json_rpc_get_checkpoints_limit",
155                "The input limit for get_checkpoints, after applying the cap",
156                iota_metrics::COUNT_BUCKETS.to_vec(),
157                registry,
158            )
159            .unwrap(),
160            get_checkpoints_result_size: register_histogram_with_registry!(
161                "json_rpc_get_checkpoints_result_size",
162                "The return size for get_checkpoints",
163                iota_metrics::COUNT_BUCKETS.to_vec(),
164                registry,
165            )
166            .unwrap(),
167            get_checkpoints_result_size_total: register_int_counter_with_registry!(
168                "json_rpc_get_checkpoints_result_size_total",
169                "The total return size for get_checkpoints",
170                registry
171            )
172            .unwrap(),
173            get_owned_objects_limit: register_histogram_with_registry!(
174                "json_rpc_get_owned_objects_limit",
175                "The input limit for get_owned_objects, after applying the cap",
176                iota_metrics::COUNT_BUCKETS.to_vec(),
177                registry,
178            )
179            .unwrap(),
180            get_owned_objects_result_size: register_histogram_with_registry!(
181                "json_rpc_get_owned_objects_result_size",
182                "The return size for get_owned_objects",
183                iota_metrics::COUNT_BUCKETS.to_vec(),
184                registry,
185            )
186            .unwrap(),
187            get_owned_objects_result_size_total: register_int_counter_with_registry!(
188                "json_rpc_get_owned_objects_result_size_total",
189                "The total return size for get_owned_objects",
190                registry
191            )
192            .unwrap(),
193            get_coins_limit: register_histogram_with_registry!(
194                "json_rpc_get_coins_limit",
195                "The input limit for get_coins, after applying the cap",
196                iota_metrics::COUNT_BUCKETS.to_vec(),
197                registry,
198            )
199            .unwrap(),
200            get_coins_result_size: register_histogram_with_registry!(
201                "json_rpc_get_coins_result_size",
202                "The return size for get_coins",
203                iota_metrics::COUNT_BUCKETS.to_vec(),
204                registry,
205            )
206            .unwrap(),
207            get_coins_result_size_total: register_int_counter_with_registry!(
208                "json_rpc_get_coins_result_size_total",
209                "The total return size for get_coins",
210                registry
211            )
212            .unwrap(),
213            get_dynamic_fields_limit: register_histogram_with_registry!(
214                "json_rpc_get_dynamic_fields_limit",
215                "The input limit for get_dynamic_fields, after applying the cap",
216                iota_metrics::COUNT_BUCKETS.to_vec(),
217                registry,
218            )
219            .unwrap(),
220            get_dynamic_fields_result_size: register_histogram_with_registry!(
221                "json_rpc_get_dynamic_fields_result_size",
222                "The return size for get_dynamic_fields",
223                iota_metrics::COUNT_BUCKETS.to_vec(),
224                registry,
225            )
226            .unwrap(),
227            get_dynamic_fields_result_size_total: register_int_counter_with_registry!(
228                "json_rpc_get_dynamic_fields_result_size_total",
229                "The total return size for get_dynamic_fields",
230                registry
231            )
232            .unwrap(),
233            query_tx_blocks_limit: register_histogram_with_registry!(
234                "json_rpc_query_tx_blocks_limit",
235                "The input limit for query_tx_blocks, after applying the cap",
236                iota_metrics::COUNT_BUCKETS.to_vec(),
237                registry,
238            )
239            .unwrap(),
240            query_tx_blocks_result_size: register_histogram_with_registry!(
241                "json_rpc_query_tx_blocks_result_size",
242                "The return size for query_tx_blocks",
243                iota_metrics::COUNT_BUCKETS.to_vec(),
244                registry,
245            )
246            .unwrap(),
247            query_tx_blocks_result_size_total: register_int_counter_with_registry!(
248                "json_rpc_query_tx_blocks_result_size_total",
249                "The total return size for query_tx_blocks",
250                registry
251            )
252            .unwrap(),
253            query_events_limit: register_histogram_with_registry!(
254                "json_rpc_query_events_limit",
255                "The input limit for query_events, after applying the cap",
256                iota_metrics::COUNT_BUCKETS.to_vec(),
257                registry,
258            )
259            .unwrap(),
260            query_events_result_size: register_histogram_with_registry!(
261                "json_rpc_query_events_result_size",
262                "The return size for query_events",
263                iota_metrics::COUNT_BUCKETS.to_vec(),
264                registry,
265            )
266            .unwrap(),
267            query_events_result_size_total: register_int_counter_with_registry!(
268                "json_rpc_query_events_result_size_total",
269                "The total return size for query_events",
270                registry
271            )
272            .unwrap(),
273            get_stake_iota_result_size: register_histogram_with_registry!(
274                "json_rpc_get_stake_iota_result_size",
275                "The return size for get_stake_iota",
276                iota_metrics::COUNT_BUCKETS.to_vec(),
277                registry,
278            )
279            .unwrap(),
280            get_stake_iota_result_size_total: register_int_counter_with_registry!(
281                "json_rpc_get_stake_iota_result_size_total",
282                "The total return size for get_stake_iota",
283                registry
284            )
285            .unwrap(),
286            get_stake_iota_latency: register_histogram_with_registry!(
287                "get_stake_iota_latency",
288                "The latency of get stake iota, in ms",
289                iota_metrics::COARSE_LATENCY_SEC_BUCKETS.to_vec(),
290                registry,
291            )
292            .unwrap(),
293            get_delegated_iota_latency: register_histogram_with_registry!(
294                "get_delegated_iota_latency",
295                "The latency of get delegated iota, in ms",
296                iota_metrics::COARSE_LATENCY_SEC_BUCKETS.to_vec(),
297                registry,
298            )
299            .unwrap(),
300            orchestrator_latency_ms: register_histogram_with_registry!(
301                "json_rpc_orchestrator_latency",
302                "The latency of submitting transaction via transaction orchestrator, in ms",
303                iota_metrics::COARSE_LATENCY_SEC_BUCKETS.to_vec(),
304                registry,
305            )
306            .unwrap(),
307            post_orchestrator_latency_ms: register_histogram_with_registry!(
308                "json_rpc_post_orchestrator_latency",
309                "The latency of response processing after transaction orchestrator, in ms",
310                iota_metrics::COARSE_LATENCY_SEC_BUCKETS.to_vec(),
311                registry,
312            )
313            .unwrap(),
314        }
315    }
316
317    pub fn new_for_tests() -> Self {
318        let registry = prometheus::Registry::new();
319        Self::new(&registry)
320    }
321}
322
323pub fn read_size_from_env(var_name: &str) -> Option<usize> {
324    std::env::var(var_name)
325        .ok()?
326        .parse::<usize>()
327        .tap_err(|e| {
328            warn!(
329                "Env var {} does not contain valid usize integer: {}",
330                var_name, e
331            )
332        })
333        .ok()
334}
335
336pub const CLIENT_SDK_TYPE_HEADER: &str = "client-sdk-type";
337/// The version number of the SDK itself. This can be different from the API
338/// version.
339pub const CLIENT_SDK_VERSION_HEADER: &str = "client-sdk-version";
340/// The RPC API version that the client is targeting. Different SDK versions may
341/// target the same API version.
342pub const CLIENT_TARGET_API_VERSION_HEADER: &str = "client-target-api-version";
343
344pub const TRANSIENT_ERROR_CODE: i32 = -32050;
345pub const TRANSACTION_EXECUTION_CLIENT_ERROR_CODE: i32 = -32002;
346
347/// Convert a jsonrpsee client error into a generic error object.
348pub fn error_object_from_rpc(rpc_err: ClientError) -> ErrorObjectOwned {
349    match rpc_err {
350        ClientError::Call(e) => e,
351        _ => ErrorObjectOwned::owned::<()>(UNKNOWN_ERROR_CODE, rpc_err.to_string(), None),
352    }
353}
354
355/// Convert an internal error into a generic error object.
356pub fn internal_error(err: impl ToString) -> ErrorObjectOwned {
357    ErrorObjectOwned::owned::<()>(INTERNAL_ERROR_CODE, err.to_string(), None)
358}