Skip to main content

celestia_client/
share.rs

1use std::sync::Arc;
2
3use celestia_rpc::ShareClient;
4
5use crate::Result;
6use crate::api::share::{GetRangeResponse, GetRowResponse, SampleCoordinates};
7use crate::client::ClientInner;
8use crate::types::namespace_data::NamespaceData;
9use crate::types::nmt::Namespace;
10use crate::types::sample::Sample;
11use crate::types::{ExtendedDataSquare, Share};
12
13/// Share API for quering bridge nodes.
14pub struct ShareApi {
15    inner: Arc<ClientInner>,
16}
17
18impl ShareApi {
19    pub(crate) fn new(inner: Arc<ClientInner>) -> ShareApi {
20        ShareApi { inner }
21    }
22
23    /// Performs a subjective validation to check if the shares committed to the
24    /// header at the specified height are available and retrievable from the network.
25    ///
26    /// Returns `Ok(())` if shares are available.
27    pub async fn shares_available(&self, height: u64) -> Result<()> {
28        Ok(self.inner.rpc.share_shares_available(height).await?)
29    }
30
31    /// Retrieves a specific share from the [`ExtendedDataSquare`] at the given
32    /// height  using its row and column coordinates.
33    pub async fn get(
34        &self,
35        height: u64,
36        square_width: u16,
37        row: u16,
38        column: u16,
39    ) -> Result<Share> {
40        Ok(self
41            .inner
42            .rpc
43            .share_get_share(height, square_width, row, column)
44            .await?)
45    }
46
47    /// Retrieves a specific share from the [`ExtendedDataSquare`] at the given
48    /// height  using its row and column coordinates.
49    ///
50    /// # NOTE
51    ///
52    /// This method will first fetch and validate the header at a given height and
53    /// then use it to provide necessary data for validation and post-processing.
54    ///
55    /// If you already have access to the necessary data, it is recommended to use
56    /// the equivalent method without the `_with_root` suffix.
57    pub async fn get_with_root(&self, height: u64, row: u16, column: u16) -> Result<Share> {
58        let header = self.inner.get_header_validated(height).await?;
59        Ok(self
60            .inner
61            .rpc
62            .share_get_share(header.height(), header.square_width(), row, column)
63            .await?)
64    }
65
66    /// Retrieves multiple shares from the [`ExtendedDataSquare`] at the given
67    /// sample coordinates.
68    ///
69    /// `coordinates` is a list of `(row, column)`.
70    pub async fn get_samples<I, C>(&self, height: u64, coordinates: I) -> Result<Vec<Sample>>
71    where
72        I: IntoIterator<Item = C>,
73        C: Into<SampleCoordinates>,
74    {
75        Ok(self
76            .inner
77            .rpc
78            .share_get_samples(height, coordinates)
79            .await?)
80    }
81
82    /// Retrieves multiple shares from the [`ExtendedDataSquare`] at the given
83    /// sample coordinates.
84    ///
85    /// `coordinates` is a list of `(row, column)`.
86    ///
87    /// # NOTE
88    ///
89    /// This method will first fetch and validate the header at a given height and
90    /// then use it to provide necessary data for validation and post-processing.
91    ///
92    /// If you already have access to the necessary data, it is recommended to use
93    /// the equivalent method without the `_with_root` suffix.
94    pub async fn get_samples_with_root<I, C>(
95        &self,
96        height: u64,
97        coordinates: I,
98    ) -> Result<Vec<Sample>>
99    where
100        I: IntoIterator<Item = C>,
101        C: Into<SampleCoordinates>,
102    {
103        let header = self.inner.get_header_validated(height).await?;
104        Ok(self
105            .inner
106            .rpc
107            .share_get_samples(header.height(), coordinates)
108            .await?)
109    }
110
111    /// Retrieves the complete [`ExtendedDataSquare`] for the specified height.
112    pub async fn get_eds(&self, height: u64) -> Result<ExtendedDataSquare> {
113        Ok(self.inner.rpc.share_get_eds(height).await?)
114    }
115
116    /// Retrieves the complete [`ExtendedDataSquare`] for the specified height.
117    ///
118    /// # NOTE
119    ///
120    /// This method will first fetch and validate the header at a given height and
121    /// then use it to provide necessary data for validation and post-processing.
122    ///
123    /// If you already have access to the necessary data, it is recommended to use
124    /// the equivalent method without the `_with_root` suffix.
125    pub async fn get_eds_with_root(&self, height: u64) -> Result<ExtendedDataSquare> {
126        let header = self.inner.get_header_validated(height).await?;
127        Ok(self.inner.rpc.share_get_eds(header.height()).await?)
128    }
129
130    /// Retrieves all shares from a specific row of the [`ExtendedDataSquare`]
131    /// at the given height.
132    pub async fn get_row(
133        &self,
134        height: u64,
135        square_width: u16,
136        row: u16,
137    ) -> Result<GetRowResponse> {
138        Ok(self
139            .inner
140            .rpc
141            .share_get_row(height, square_width, row)
142            .await?)
143    }
144
145    /// Retrieves all shares from a specific row of the [`ExtendedDataSquare`]
146    /// at the given height.
147    ///
148    /// # NOTE
149    ///
150    /// This method will first fetch and validate the header at a given height and
151    /// then use it to provide necessary data for validation and post-processing.
152    ///
153    /// If you already have access to the necessary data, it is recommended to use
154    /// the equivalent method without the `_with_root` suffix.
155    pub async fn get_row_with_root(&self, height: u64, row: u16) -> Result<GetRowResponse> {
156        let header = self.inner.get_header_validated(height).await?;
157        Ok(self
158            .inner
159            .rpc
160            .share_get_row(header.height(), header.square_width(), row)
161            .await?)
162    }
163
164    /// Retrieves all shares that belong to the specified namespace within the
165    /// [`ExtendedDataSquare`] at the given height.
166    ///
167    /// The shares are returned in a row-by-row order, maintaining the original
168    /// layout if the namespace spans multiple rows.
169    pub async fn get_namespace_data(
170        &self,
171        height: u64,
172        namespace: Namespace,
173    ) -> Result<NamespaceData> {
174        Ok(self
175            .inner
176            .rpc
177            .share_get_namespace_data(height, namespace)
178            .await?)
179    }
180
181    /// Retrieves all shares that belong to the specified namespace within the
182    /// [`ExtendedDataSquare`] at the given height.
183    ///
184    /// The shares are returned in a row-by-row order, maintaining the original
185    /// layout if the namespace spans multiple rows.
186    ///
187    /// # NOTE
188    ///
189    /// This method will first fetch and validate the header at a given height and
190    /// then use it to provide necessary data for validation and post-processing.
191    ///
192    /// If you already have access to the necessary data, it is recommended to use
193    /// the equivalent method without the `_with_root` suffix.
194    pub async fn get_namespace_data_with_root(
195        &self,
196        height: u64,
197        namespace: Namespace,
198    ) -> Result<NamespaceData> {
199        let header = self.inner.get_header_validated(height).await?;
200
201        Ok(self
202            .inner
203            .rpc
204            .share_get_namespace_data(header.height(), namespace)
205            .await?)
206    }
207
208    /// Retrieves a list of shares and their corresponding proof.
209    ///
210    /// The start and end index ignores parity shares and corresponds to ODS.
211    pub async fn get_range(&self, height: u64, start: u64, end: u64) -> Result<GetRangeResponse> {
212        Ok(self.inner.rpc.share_get_range(height, start, end).await?)
213    }
214
215    /// Retrieves a list of shares and their corresponding proof.
216    ///
217    /// The start and end index ignores parity shares and corresponds to ODS.
218    ///
219    /// # NOTE
220    ///
221    /// This method will first fetch and validate the header at a given height and
222    /// then use it to provide necessary data for validation and post-processing.
223    ///
224    /// If you already have access to the necessary data, it is recommended to use
225    /// the equivalent method without the `_with_root` suffix.
226    pub async fn get_range_with_root(
227        &self,
228        height: u64,
229        start: u64,
230        end: u64,
231    ) -> Result<GetRangeResponse> {
232        let header = self.inner.get_header_validated(height).await?;
233        Ok(self
234            .inner
235            .rpc
236            .share_get_range(header.height(), start, end)
237            .await?)
238    }
239}
240
241#[cfg(test)]
242mod tests {
243    use super::*;
244
245    use crate::test_utils::{ensure_serializable, ensure_serializable_deserializable};
246
247    #[allow(dead_code)]
248    #[allow(unused_variables)]
249    #[allow(unreachable_code)]
250    #[allow(clippy::diverging_sub_expression)]
251    async fn enforce_serde_bounds() {
252        // intentionally no-run, compile only test
253        let api = ShareApi::new(unimplemented!());
254
255        let _: () = api.shares_available(0).await.unwrap();
256
257        let coordinates: Vec<SampleCoordinates> =
258            ensure_serializable_deserializable(unimplemented!());
259        ensure_serializable(api.get_samples(0, coordinates).await.unwrap());
260
261        ensure_serializable(api.get_eds(0).await.unwrap());
262
263        ensure_serializable_deserializable(api.get(0, 0, 0, 0).await.unwrap());
264
265        ensure_serializable_deserializable(api.get_row(0, 0, 0).await.unwrap());
266
267        let namespace = ensure_serializable_deserializable(unimplemented!());
268        ensure_serializable_deserializable(api.get_namespace_data(0, namespace).await.unwrap());
269
270        ensure_serializable_deserializable(api.get_range(0, 0, 0).await.unwrap());
271    }
272}