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