iota_indexer/apis/
extended_api.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_json_rpc::IotaRpcModule;
6use iota_json_rpc_api::{
7    ExtendedApiServer, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS, internal_error, validate_limit,
8};
9use iota_json_rpc_types::{
10    AddressMetrics, EpochInfo, EpochMetrics, EpochMetricsPage, EpochPage, MoveCallMetrics,
11    NetworkMetrics, Page, ParticipationMetrics,
12};
13use iota_open_rpc::Module;
14use iota_types::iota_serde::BigInt;
15use jsonrpsee::{RpcModule, core::RpcResult};
16
17use crate::indexer_reader::IndexerReader;
18
19pub(crate) struct ExtendedApi {
20    inner: IndexerReader,
21}
22
23impl ExtendedApi {
24    pub fn new(inner: IndexerReader) -> Self {
25        Self { inner }
26    }
27}
28
29#[async_trait::async_trait]
30impl ExtendedApiServer for ExtendedApi {
31    async fn get_epochs(
32        &self,
33        cursor: Option<BigInt<u64>>,
34        limit: Option<usize>,
35        descending_order: Option<bool>,
36    ) -> RpcResult<EpochPage> {
37        let limit =
38            validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS).map_err(internal_error)?;
39        let mut epochs = self
40            .inner
41            .spawn_blocking(move |this| {
42                this.get_epochs(
43                    cursor.map(|x| *x),
44                    limit + 1,
45                    descending_order.unwrap_or(false),
46                )
47            })
48            .await?;
49
50        let has_next_page = epochs.len() > limit;
51        epochs.truncate(limit);
52        let next_cursor = epochs.last().map(|e| e.epoch);
53        Ok(Page {
54            data: epochs,
55            next_cursor: next_cursor.map(|id| id.into()),
56            has_next_page,
57        })
58    }
59
60    async fn get_epoch_metrics(
61        &self,
62        cursor: Option<BigInt<u64>>,
63        limit: Option<usize>,
64        descending_order: Option<bool>,
65    ) -> RpcResult<EpochMetricsPage> {
66        let limit =
67            validate_limit(limit, QUERY_MAX_RESULT_LIMIT_CHECKPOINTS).map_err(internal_error)?;
68        let epochs = self
69            .inner
70            .spawn_blocking(move |this| {
71                this.get_epochs(
72                    cursor.map(|x| *x),
73                    limit + 1,
74                    descending_order.unwrap_or(false),
75                )
76            })
77            .await?;
78
79        let mut epoch_metrics = epochs
80            .into_iter()
81            .map(|e| EpochMetrics {
82                epoch: e.epoch,
83                epoch_total_transactions: e.epoch_total_transactions,
84                first_checkpoint_id: e.first_checkpoint_id,
85                epoch_start_timestamp: e.epoch_start_timestamp,
86                end_of_epoch_info: e.end_of_epoch_info,
87            })
88            .collect::<Vec<_>>();
89
90        let has_next_page = epoch_metrics.len() > limit;
91        epoch_metrics.truncate(limit);
92        let next_cursor = epoch_metrics.last().map(|e| e.epoch);
93        Ok(Page {
94            data: epoch_metrics,
95            next_cursor: next_cursor.map(|id| id.into()),
96            has_next_page,
97        })
98    }
99
100    async fn get_current_epoch(&self) -> RpcResult<EpochInfo> {
101        let stored_epoch = self
102            .inner
103            .spawn_blocking(|this| this.get_latest_epoch_info_from_db())
104            .await?;
105        EpochInfo::try_from(stored_epoch).map_err(Into::into)
106    }
107
108    async fn get_network_metrics(&self) -> RpcResult<NetworkMetrics> {
109        let network_metrics = self
110            .inner
111            .spawn_blocking(|this| this.get_latest_network_metrics())
112            .await?;
113        Ok(network_metrics)
114    }
115
116    async fn get_move_call_metrics(&self) -> RpcResult<MoveCallMetrics> {
117        let move_call_metrics = self
118            .inner
119            .spawn_blocking(|this| this.get_latest_move_call_metrics())
120            .await?;
121        Ok(move_call_metrics)
122    }
123
124    async fn get_latest_address_metrics(&self) -> RpcResult<AddressMetrics> {
125        let latest_address_metrics = self
126            .inner
127            .spawn_blocking(|this| this.get_latest_address_metrics())
128            .await?;
129        Ok(latest_address_metrics)
130    }
131
132    async fn get_checkpoint_address_metrics(&self, checkpoint: u64) -> RpcResult<AddressMetrics> {
133        let checkpoint_address_metrics = self
134            .inner
135            .spawn_blocking(move |this| this.get_checkpoint_address_metrics(checkpoint))
136            .await?;
137        Ok(checkpoint_address_metrics)
138    }
139
140    async fn get_all_epoch_address_metrics(
141        &self,
142        descending_order: Option<bool>,
143    ) -> RpcResult<Vec<AddressMetrics>> {
144        let all_epoch_address_metrics = self
145            .inner
146            .spawn_blocking(move |this| this.get_all_epoch_address_metrics(descending_order))
147            .await?;
148        Ok(all_epoch_address_metrics)
149    }
150
151    async fn get_total_transactions(&self) -> RpcResult<BigInt<u64>> {
152        let latest_checkpoint = self
153            .inner
154            .spawn_blocking(|this| this.get_latest_checkpoint())
155            .await?;
156        Ok(latest_checkpoint.network_total_transactions.into())
157    }
158
159    async fn get_participation_metrics(&self) -> RpcResult<ParticipationMetrics> {
160        self.inner
161            .spawn_blocking(|this| this.get_participation_metrics())
162            .await
163            .map_err(Into::into)
164    }
165}
166
167impl IotaRpcModule for ExtendedApi {
168    fn rpc(self) -> RpcModule<Self> {
169        self.into_rpc()
170    }
171
172    fn rpc_doc_module() -> Module {
173        iota_json_rpc_api::ExtendedApiOpenRpc::module_doc()
174    }
175}