iota_grpc_client/api/mod.rs
1// Copyright (c) 2026 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4//! High-level API for gRPC client operations.
5//!
6//! This module provides wrappers around the raw gRPC service clients.
7//! Proto types are exposed directly with lazy conversion methods, allowing
8//! users to convert only what they need to SDK types.
9
10use iota_sdk_types::CheckpointSequenceNumber;
11
12mod common;
13pub mod execution;
14pub mod ledger;
15mod metadata;
16pub mod move_package;
17pub mod state;
18
19pub use common::{Error, Page, Result, RpcStatus};
20pub(crate) use common::{
21 ProtoResult, TryFromProtoError, build_proto_transaction, collect_stream, define_list_query,
22 field_mask_with_default, proto_object_id, saturating_usize_to_u32,
23};
24pub use iota_grpc_types::read_masks::*;
25pub use metadata::MetadataEnvelope;
26
27/// An item from a checkpoint data stream.
28///
29/// When `filter_checkpoints` is enabled, the server may skip checkpoints that
30/// don't match the provided filters. In that case, `Progress` items are sent
31/// periodically to indicate liveness and the current scan position. When
32/// `filter_checkpoints` is disabled, only `Checkpoint` items are produced.
33///
34/// For liveness detection with `filter_checkpoints`, wrap `stream.next()` in
35/// `tokio::time::timeout()` — if neither a `Checkpoint` nor a `Progress`
36/// arrives within your chosen duration plus some buffer for connection latency,
37/// the connection is likely dead.
38#[derive(Debug, Clone)]
39pub enum CheckpointStreamItem {
40 /// A complete checkpoint with its transactions and events.
41 Checkpoint(Box<CheckpointResponse>),
42 /// A progress indicator sent during filtered scanning.
43 /// Contains the sequence number of the latest scanned checkpoint.
44 Progress {
45 latest_scanned_sequence_number: CheckpointSequenceNumber,
46 },
47}
48
49impl CheckpointStreamItem {
50 /// Returns the contained checkpoint, or `None` if this is a progress
51 /// message.
52 pub fn into_checkpoint(self) -> Option<CheckpointResponse> {
53 match self {
54 Self::Checkpoint(c) => Some(*c),
55 Self::Progress { .. } => None,
56 }
57 }
58
59 /// Returns the progress sequence number, or `None` if this is a
60 /// checkpoint.
61 pub fn into_progress(self) -> Option<CheckpointSequenceNumber> {
62 match self {
63 Self::Checkpoint(_) => None,
64 Self::Progress {
65 latest_scanned_sequence_number,
66 } => Some(latest_scanned_sequence_number),
67 }
68 }
69
70 /// Returns `true` if this is a checkpoint item.
71 pub fn is_checkpoint(&self) -> bool {
72 matches!(self, Self::Checkpoint(_))
73 }
74
75 /// Returns `true` if this is a progress item.
76 pub fn is_progress(&self) -> bool {
77 matches!(self, Self::Progress { .. })
78 }
79}
80
81/// Response for a checkpoint query.
82///
83/// Contains checkpoint summary, signature, contents, transactions, and events.
84/// Fields are proto types that can be accessed directly or converted to SDK
85/// types using their conversion methods (e.g.,
86/// `response.summary()?.summary()?`, `response.contents()?.contents()?`).
87#[derive(Debug, Clone)]
88pub struct CheckpointResponse {
89 /// The checkpoint sequence number.
90 pub sequence_number: CheckpointSequenceNumber,
91 /// Proto checkpoint summary. Use `response.summary()?.summary()` to convert
92 /// to SDK type.
93 pub summary: Option<iota_grpc_types::v1::checkpoint::CheckpointSummary>,
94 /// Proto validator signature. Use `response.signature()?.signature()` to
95 /// convert to SDK type.
96 pub signature: Option<iota_grpc_types::v1::signatures::ValidatorAggregatedSignature>,
97 /// Proto checkpoint contents. Use `response.contents()?.contents()` to
98 /// convert to SDK type.
99 pub contents: Option<iota_grpc_types::v1::checkpoint::CheckpointContents>,
100 /// Proto executed transactions. Use methods like `tx.effects()?`,
101 /// `tx.transaction()?`, etc.
102 pub executed_transactions: Vec<iota_grpc_types::v1::transaction::ExecutedTransaction>,
103 /// Proto events. Use `event.try_into()` or `event.events()` to convert to
104 /// SDK types.
105 pub events: Vec<iota_grpc_types::v1::event::Event>,
106}
107
108impl CheckpointResponse {
109 /// Get the checkpoint sequence number.
110 ///
111 /// Always available regardless of the read mask.
112 pub fn sequence_number(&self) -> CheckpointSequenceNumber {
113 self.sequence_number
114 }
115
116 /// Get the checkpoint summary.
117 ///
118 /// Returns the proto
119 /// [`CheckpointSummary`](iota_grpc_types::v1::checkpoint::CheckpointSummary)
120 /// which provides:
121 /// - [`digest()`](iota_grpc_types::v1::checkpoint::CheckpointSummary::digest) — the summary digest
122 /// - [`summary()`](iota_grpc_types::v1::checkpoint::CheckpointSummary::summary) — the deserialized SDK `CheckpointSummary`
123 ///
124 /// **Read mask:** `"checkpoint.summary"` (see
125 /// [`CHECKPOINT_RESPONSE_SUMMARY`])
126 pub fn summary(&self) -> Result<&iota_grpc_types::v1::checkpoint::CheckpointSummary> {
127 self.summary
128 .as_ref()
129 .ok_or_else(|| TryFromProtoError::missing("summary").into())
130 }
131
132 /// Build a [`SignedCheckpointSummary`](iota_sdk_types::SignedCheckpointSummary)
133 /// from the response.
134 ///
135 /// Requires the checkpoint summary and signature to be present.
136 ///
137 /// **Read mask:** see [`CHECKPOINT_RESPONSE_SIGNED_SUMMARY`]
138 pub fn signed_summary(&self) -> Result<iota_sdk_types::SignedCheckpointSummary> {
139 Ok(iota_sdk_types::SignedCheckpointSummary {
140 checkpoint: self.summary()?.summary()?,
141 signature: self.signature()?.signature()?,
142 })
143 }
144
145 /// Get the validator aggregated signature for the checkpoint.
146 ///
147 /// **Read mask:** `"checkpoint.signature"` (see
148 /// [`CHECKPOINT_RESPONSE_SIGNATURE`])
149 pub fn signature(
150 &self,
151 ) -> Result<&iota_grpc_types::v1::signatures::ValidatorAggregatedSignature> {
152 self.signature
153 .as_ref()
154 .ok_or_else(|| TryFromProtoError::missing("signature").into())
155 }
156
157 /// Get the checkpoint contents.
158 ///
159 /// Returns the proto
160 /// [`CheckpointContents`](iota_grpc_types::v1::checkpoint::CheckpointContents)
161 /// which provides:
162 /// - [`digest()`](iota_grpc_types::v1::checkpoint::CheckpointContents::digest) — the contents digest
163 /// - [`contents()`](iota_grpc_types::v1::checkpoint::CheckpointContents::contents) — the deserialized SDK `CheckpointContents`
164 ///
165 /// **Read mask:** `"checkpoint.contents"` (see
166 /// [`CHECKPOINT_RESPONSE_CONTENTS`])
167 pub fn contents(&self) -> Result<&iota_grpc_types::v1::checkpoint::CheckpointContents> {
168 self.contents
169 .as_ref()
170 .ok_or_else(|| TryFromProtoError::missing("contents").into())
171 }
172
173 /// Get the executed transactions in this checkpoint.
174 ///
175 /// Returns proto
176 /// [`ExecutedTransaction`](iota_grpc_types::v1::transaction::ExecutedTransaction)
177 /// values. Which sub-fields are populated depends on the read mask; use
178 /// paths like `"transactions.effects"` or `"transactions.transaction"`.
179 ///
180 /// **Read mask:** `"transactions"` for all sub-fields (see
181 /// [`CHECKPOINT_RESPONSE_EXECUTED_TRANSACTIONS`])
182 pub fn executed_transactions(
183 &self,
184 ) -> &Vec<iota_grpc_types::v1::transaction::ExecutedTransaction> {
185 &self.executed_transactions
186 }
187
188 /// Get the top-level events for this checkpoint.
189 ///
190 /// Returns proto [`Event`](iota_grpc_types::v1::event::Event) values.
191 /// Which sub-fields are populated depends on the read mask; use paths like
192 /// `"events.bcs"` or `"events.event_type"`.
193 ///
194 /// **Read mask:** `"events"` for all sub-fields (see
195 /// [`CHECKPOINT_RESPONSE_EVENTS`])
196 pub fn events(&self) -> &Vec<iota_grpc_types::v1::event::Event> {
197 &self.events
198 }
199
200 /// Build a full
201 /// [`CheckpointData`](iota_sdk_types::checkpoint::CheckpointData)
202 /// from the response.
203 ///
204 /// Requires the checkpoint summary, signature, contents, and all
205 /// transaction data (transaction, signatures, effects, events,
206 /// input_objects, output_objects) to be present in the response.
207 ///
208 /// **Read mask:** see [`CHECKPOINT_RESPONSE_CHECKPOINT_DATA`]
209 ///
210 /// # Example
211 ///
212 /// ```no_run
213 /// # use iota_grpc_client::Client;
214 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
215 /// use iota_grpc_client::CHECKPOINT_RESPONSE_CHECKPOINT_DATA;
216 /// let client = Client::connect("http://localhost:9000").await?;
217 /// let cp = client
218 /// .get_checkpoint_latest(Some(CHECKPOINT_RESPONSE_CHECKPOINT_DATA), None, None)
219 /// .await?;
220 /// let data = cp.body().checkpoint_data()?;
221 /// # Ok(())
222 /// # }
223 /// ```
224 pub fn checkpoint_data(&self) -> Result<iota_sdk_types::checkpoint::CheckpointData> {
225 Ok(iota_sdk_types::checkpoint::CheckpointData {
226 checkpoint_contents: self.contents()?.contents()?,
227 checkpoint_summary: iota_sdk_types::SignedCheckpointSummary {
228 checkpoint: self.summary()?.summary()?,
229 signature: self.signature()?.signature()?,
230 },
231 transactions: self
232 .executed_transactions()
233 .iter()
234 .map(TryInto::try_into)
235 .collect::<std::result::Result<Vec<_>, _>>()?,
236 })
237 }
238}