Skip to main content

celestia_rpc/
share.rs

1//! celestia-node rpc types and methods related to shares
2
3use std::future::Future;
4use std::marker::{Send, Sync};
5
6use celestia_types::namespace_data::NamespaceData;
7use celestia_types::nmt::Namespace;
8use celestia_types::sample::{RawSample, Sample, SampleId};
9use celestia_types::{ExtendedDataSquare, RawShare, Share, ShareProof};
10use jsonrpsee::core::client::{ClientT, Error};
11use jsonrpsee::proc_macros::rpc;
12use serde::{Deserialize, Serialize};
13
14use crate::custom_client_error;
15
16/// Response type for [`ShareClient::share_get_range`].
17#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
18#[serde(rename_all = "PascalCase")]
19pub struct GetRangeResponse {
20    /// Shares contained in given range.
21    pub shares: Vec<Share>,
22    /// Proof of inclusion of the shares.
23    pub proof: ShareProof,
24}
25
26/// Side of a row within the EDS.
27#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
28#[serde(rename_all = "UPPERCASE")]
29pub enum RowSide {
30    /// The row data is on the left of the EDS (i.e. in the ODS).
31    Left,
32    /// The row data is on the right of the EDS (i.e. it's parity data).
33    Right,
34    /// The row contains both the original data and the parity data.
35    Both,
36}
37
38/// Response type for [`ShareClient::share_get_row`].
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40pub struct GetRowResponse {
41    /// Shares contained in given range.
42    pub shares: Vec<Share>,
43    /// Side of the row within the EDS.
44    pub side: RowSide,
45}
46
47/// Position of a sample in a 2D grid.
48#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
49pub struct SampleCoordinates {
50    /// Row index of the sample.
51    pub row: u16,
52    /// Column index of the sample.
53    #[serde(rename = "col")]
54    pub column: u16,
55}
56
57impl SampleCoordinates {
58    /// Create new `SampleCoordinates` base on row and column.
59    pub fn new(row: u16, column: u16) -> Self {
60        SampleCoordinates { row, column }
61    }
62}
63
64impl From<(u16, u16)> for SampleCoordinates {
65    fn from(value: (u16, u16)) -> Self {
66        SampleCoordinates::new(value.0, value.1)
67    }
68}
69
70mod rpc {
71    use super::*;
72    use celestia_types::eds::RawExtendedDataSquare;
73    use jsonrpsee::core::RpcResult;
74
75    #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
76    pub struct RawGetRowResponse {
77        pub(crate) shares: Vec<RawShare>,
78        pub(crate) side: RowSide,
79    }
80
81    /// Share RPC methods.
82    #[rpc(client, server, namespace = "share", namespace_separator = ".")]
83    trait Share {
84        /// See [`crate::ShareClient::share_get_eds`].
85        #[method(name = "GetEDS")]
86        async fn share_get_eds(&self, height: u64) -> RpcResult<RawExtendedDataSquare>;
87
88        /// See [`crate::ShareClient::share_get_range`].
89        #[method(name = "GetRange")]
90        async fn share_get_range(
91            &self,
92            height: u64,
93            start: u64,
94            end: u64,
95        ) -> RpcResult<GetRangeResponse>; // was Result<_, Error>
96
97        /// See [`crate::ShareClient::share_get_samples`].
98        #[method(name = "GetSamples")]
99        async fn share_get_samples(
100            &self,
101            height: u64,
102            indices: Vec<SampleCoordinates>, // was &[SampleCoordinates]
103        ) -> RpcResult<Vec<RawSample>>; // was Result<_, Error>
104
105        /// See [`crate::ShareClient::share_get_row`].
106        #[method(name = "GetRow")]
107        async fn share_get_row(&self, height: u64, row: u16) -> RpcResult<RawGetRowResponse>;
108
109        /// See [`crate::ShareClient::share_get_share`].
110        #[method(name = "GetShare")]
111        async fn share_get_share(&self, height: u64, row: u16, col: u16) -> RpcResult<RawShare>;
112
113        /// See [`crate::ShareClient::share_get_namespace_data`].
114        #[method(name = "GetNamespaceData")]
115        async fn share_get_namespace_data(
116            &self,
117            height: u64,
118            namespace: Namespace,
119        ) -> RpcResult<NamespaceData>;
120
121        /// See [`crate::ShareClient::share_shares_available`].
122        #[method(name = "SharesAvailable")]
123        async fn share_shares_available(&self, height: u64) -> RpcResult<()>;
124    }
125}
126
127/// Client implementation for the `Share` RPC API.
128pub trait ShareClient: ClientT {
129    /// GetEDS gets the full EDS identified at the specified height.
130    fn share_get_eds<'a, 'fut>(
131        &'a self,
132        height: u64,
133    ) -> impl Future<Output = Result<ExtendedDataSquare, Error>> + Send + 'fut
134    where
135        'a: 'fut,
136        Self: Sized + Sync + 'fut,
137    {
138        async move {
139            let raw_eds = rpc::ShareClient::share_get_eds(self, height).await?;
140            // Correct `Share` construction and validation is done inside `ExtendedDataSquare::from_raw`
141            ExtendedDataSquare::from_raw(raw_eds).map_err(custom_client_error)
142        }
143    }
144
145    /// Retrieves a list of shares and their corresponding proof.
146    ///
147    /// The start and end index ignores parity shares and corresponds to ODS.
148    fn share_get_range<'a, 'fut>(
149        &'a self,
150        height: u64,
151        start: u64,
152        end: u64,
153    ) -> impl Future<Output = Result<GetRangeResponse, Error>> + Send + 'fut
154    where
155        'a: 'fut,
156        Self: Sized + Sync + 'fut,
157    {
158        rpc::ShareClient::share_get_range(self, height, start, end)
159    }
160    /// Retrieves multiple shares from the [`ExtendedDataSquare`] at the given height
161    /// at the given sample coordinates.
162    ///
163    /// `coordinates` is a list of `(row, column)`.
164    fn share_get_samples<'a, 'fut, I, C>(
165        &'a self,
166        height: u64,
167        coordinates: I,
168    ) -> impl Future<Output = Result<Vec<Sample>, Error>> + Send + 'fut
169    where
170        'a: 'fut,
171        Self: Sized + Sync + 'fut,
172        I: IntoIterator<Item = C>,
173        C: Into<SampleCoordinates>,
174    {
175        let coordinates = coordinates
176            .into_iter()
177            .map(|c| c.into())
178            .collect::<Vec<_>>();
179
180        async move {
181            let raw_samples =
182                rpc::ShareClient::share_get_samples(self, height, coordinates.clone()).await?;
183            let mut samples = Vec::with_capacity(raw_samples.len());
184
185            for (coords, raw_sample) in coordinates.iter().zip(raw_samples.into_iter()) {
186                let sample_id = SampleId::new(coords.row, coords.column, height)
187                    .map_err(custom_client_error)?;
188
189                // Correct `Share` construction is done inside `Sample::from_raw`
190                let sample =
191                    Sample::from_raw(sample_id, raw_sample).map_err(custom_client_error)?;
192
193                samples.push(sample);
194            }
195
196            Ok(samples)
197        }
198    }
199
200    /// GetShare gets the list of shares in a single row.
201    fn share_get_row<'a, 'fut>(
202        &'a self,
203        height: u64,
204        square_width: u16,
205        row: u16,
206    ) -> impl Future<Output = Result<GetRowResponse, Error>> + Send + 'fut
207    where
208        'a: 'fut,
209        Self: Sized + Sync + 'fut,
210    {
211        async move {
212            let resp = rpc::ShareClient::share_get_row(self, height, row).await?;
213
214            let expected_len = match resp.side {
215                RowSide::Left | RowSide::Right => square_width / 2,
216                RowSide::Both => square_width,
217            };
218
219            if resp.shares.len() != usize::from(expected_len) {
220                return Err(Error::Custom(format!(
221                    "GetRowResponse::shares should have length of {} but received {}",
222                    expected_len,
223                    resp.shares.len(),
224                )));
225            }
226
227            let shares = resp
228                .shares
229                .into_iter()
230                .enumerate()
231                .map(|(relative_col, raw_share)| {
232                    let relative_col = u16::try_from(relative_col)
233                        .expect("square_width and expected_len already validated this");
234
235                    let col = match resp.side {
236                        RowSide::Left | RowSide::Both => relative_col,
237                        RowSide::Right => (square_width / 2) + relative_col,
238                    };
239
240                    let share = if is_ods_square(row, col, square_width) {
241                        Share::from_raw(&raw_share.data)
242                    } else {
243                        Share::parity(&raw_share.data)
244                    }
245                    .map_err(custom_client_error)?;
246
247                    Ok(share)
248                })
249                .collect::<Result<Vec<_>, Error>>()?;
250
251            Ok(GetRowResponse {
252                shares,
253                side: resp.side,
254            })
255        }
256    }
257
258    /// GetShare gets a Share by coordinates in EDS.
259    fn share_get_share<'a, 'fut>(
260        &'a self,
261        height: u64,
262        square_width: u16,
263        row: u16,
264        col: u16,
265    ) -> impl Future<Output = Result<Share, Error>> + Send + 'fut
266    where
267        'a: 'fut,
268        Self: Sized + Sync + 'fut,
269    {
270        async move {
271            let share = rpc::ShareClient::share_get_share(self, height, row, col).await?;
272            let share = if is_ods_square(row, col, square_width) {
273                Share::from_raw(&share.data)
274            } else {
275                Share::parity(&share.data)
276            }
277            .map_err(custom_client_error)?;
278
279            Ok(share)
280        }
281    }
282
283    /// GetNamespaceData gets all shares from an EDS within the given namespace.
284    ///
285    /// Shares are returned in a row-by-row order if the namespace spans multiple rows.
286    ///
287    /// PARITY and TAIL PADDING namespaces are not allowed.
288    fn share_get_namespace_data<'a, 'fut>(
289        &'a self,
290        height: u64,
291        namespace: Namespace,
292    ) -> impl Future<Output = Result<NamespaceData, Error>> + Send + 'fut
293    where
294        'a: 'fut,
295        Self: Sized + Sync + 'fut,
296    {
297        rpc::ShareClient::share_get_namespace_data(self, height, namespace)
298    }
299
300    /// SharesAvailable subjectively validates if Shares committed to the given Root are available on the Network.
301    fn share_shares_available<'a, 'fut>(
302        &'a self,
303        height: u64,
304    ) -> impl Future<Output = Result<(), Error>> + Send + 'fut
305    where
306        'a: 'fut,
307        Self: Sized + Sync + 'fut,
308    {
309        rpc::ShareClient::share_shares_available(self, height)
310    }
311}
312
313impl<T> ShareClient for T where T: ClientT {}
314
315fn is_ods_square(row: u16, column: u16, square_width: u16) -> bool {
316    let ods_width = square_width / 2;
317    row < ods_width && column < ods_width
318}
319
320/// Server trait for Share RPC endpoints.
321pub trait ShareServer: rpc::ShareServer {}
322
323impl<T> ShareServer for T where T: rpc::ShareServer {}
324
325pub use rpc::ShareServer as ShareRpcServer;