subspace_gateway_rpc/
lib.rs

1//! RPC API for the Subspace Gateway.
2
3use jsonrpsee::core::async_trait;
4use jsonrpsee::proc_macros::rpc;
5use jsonrpsee::types::{ErrorObject, ErrorObjectOwned};
6use std::fmt;
7use std::ops::{Deref, DerefMut};
8use subspace_core_primitives::objects::GlobalObjectMapping;
9use subspace_data_retrieval::object_fetcher::{self, ObjectFetcher};
10use subspace_data_retrieval::piece_getter::PieceGetter;
11use tracing::{debug, error};
12
13const SUBSPACE_ERROR: i32 = 9000;
14
15/// The maximum number of objects that can be requested in a single RPC call.
16///
17/// If the returned objects are large, they could overflow the RPC server (or client) buffers,
18/// despite this limit.
19// TODO: turn this into a CLI option
20const MAX_OBJECTS_PER_REQUEST: usize = 100;
21
22/// Top-level error type for the RPC handler.
23#[derive(Debug, thiserror::Error)]
24pub enum Error {
25    /// Too many mappings were supplied.
26    #[error("Mapping count {count} exceeded request limit {MAX_OBJECTS_PER_REQUEST}")]
27    TooManyMappings {
28        /// The number of supplied mappings.
29        count: usize,
30    },
31
32    /// The object fetcher failed.
33    #[error(transparent)]
34    ObjectFetcherError(#[from] object_fetcher::Error),
35}
36
37impl From<Error> for ErrorObjectOwned {
38    fn from(error: Error) -> Self {
39        ErrorObject::owned(SUBSPACE_ERROR + 1, format!("{error:?}"), None::<()>)
40    }
41}
42
43/// Binary data, encoded as hex.
44#[derive(Clone, Eq, PartialEq, serde::Serialize, serde::Deserialize)]
45#[serde(transparent)]
46pub struct HexData {
47    #[serde(with = "hex")]
48    pub data: Vec<u8>,
49}
50
51impl fmt::Debug for HexData {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(f, "HexData({})", hex::encode(&self.data))
54    }
55}
56
57impl fmt::Display for HexData {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        write!(f, "{}", hex::encode(&self.data))
60    }
61}
62
63impl From<Vec<u8>> for HexData {
64    fn from(data: Vec<u8>) -> Self {
65        Self { data }
66    }
67}
68
69impl Deref for HexData {
70    type Target = Vec<u8>;
71
72    fn deref(&self) -> &Self::Target {
73        &self.data
74    }
75}
76
77impl DerefMut for HexData {
78    fn deref_mut(&mut self) -> &mut Self::Target {
79        &mut self.data
80    }
81}
82
83/// Provides rpc methods for interacting with a Subspace DSN Gateway.
84#[rpc(client, server)]
85pub trait SubspaceGatewayRpcApi {
86    /// Get object data from a DSN object mapping batch.
87    /// Returns an error if any object fetch was unsuccessful.
88    ///
89    /// For efficiency, objects in a batch should be sorted by increasing piece index. Objects with
90    /// the same piece index should be sorted by increasing offset. This allows the last piece to
91    /// be re-used for the next object in the batch.
92    ///
93    /// Batches should be split if the gap between object piece indexes is 6 or more. Those objects
94    /// can't share any pieces, because a maximum-sized object only uses 6 pieces.
95    #[method(name = "subspace_fetchObject")]
96    async fn fetch_object(&self, mappings: GlobalObjectMapping) -> Result<Vec<HexData>, Error>;
97}
98
99/// Subspace Gateway RPC configuration
100pub struct SubspaceGatewayRpcConfig<PG>
101where
102    PG: PieceGetter + Send + Sync + 'static,
103{
104    /// DSN object fetcher instance.
105    pub object_fetcher: ObjectFetcher<PG>,
106}
107
108/// Implements the [`SubspaceGatewayRpcApiServer`] trait for interacting with the Subspace Gateway.
109pub struct SubspaceGatewayRpc<PG>
110where
111    PG: PieceGetter + Send + Sync + 'static,
112{
113    /// DSN object fetcher instance.
114    object_fetcher: ObjectFetcher<PG>,
115}
116
117/// [`SubspaceGatewayRpc`] is used to fetch objects from the DSN.
118impl<PG> SubspaceGatewayRpc<PG>
119where
120    PG: PieceGetter + Send + Sync + 'static,
121{
122    /// Creates a new instance of the `SubspaceGatewayRpc` handler.
123    pub fn new(config: SubspaceGatewayRpcConfig<PG>) -> Self {
124        Self {
125            object_fetcher: config.object_fetcher,
126        }
127    }
128}
129
130#[async_trait]
131impl<PG> SubspaceGatewayRpcApiServer for SubspaceGatewayRpc<PG>
132where
133    PG: PieceGetter + Send + Sync + 'static,
134{
135    async fn fetch_object(&self, mappings: GlobalObjectMapping) -> Result<Vec<HexData>, Error> {
136        let count = mappings.objects().len();
137        if count > MAX_OBJECTS_PER_REQUEST {
138            debug!(%count, %MAX_OBJECTS_PER_REQUEST, "Too many mappings in request");
139            return Err(Error::TooManyMappings { count });
140        }
141
142        let objects = self.object_fetcher.fetch_objects(mappings).await?;
143        let objects = objects.into_iter().map(HexData::from).collect();
144
145        Ok(objects)
146    }
147}