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