sov_rollup_interface/node/rpc/
mod.rs

1//! The rpc module defines types and traits for querying chain history
2//! via an RPC interface.
3#[cfg(feature = "native")]
4use serde::de::DeserializeOwned;
5use serde::{Deserialize, Serialize};
6#[cfg(feature = "native")]
7use tokio::sync::broadcast::Receiver;
8
9#[cfg(feature = "native")]
10use crate::stf::Event;
11use crate::stf::EventKey;
12
13/// A struct containing enough information to uniquely specify single batch.
14#[derive(Debug, PartialEq, Serialize, Deserialize)]
15pub struct SlotIdAndOffset {
16    /// The [`SlotIdentifier`] of the slot containing this batch.
17    pub slot_id: SlotIdentifier,
18    /// The offset into the slot at which this tx is located.
19    /// Index 0 is the first batch in the slot.
20    pub offset: u64,
21}
22
23/// A struct containing enough information to uniquely specify single transaction.
24#[derive(Debug, PartialEq, Serialize, Deserialize)]
25pub struct BatchIdAndOffset {
26    /// The [`BatchIdentifier`] of the batch containing this transaction.
27    pub batch_id: BatchIdentifier,
28    /// The offset into the batch at which this tx is located.
29    /// Index 0 is the first transaction in the batch.
30    pub offset: u64,
31}
32
33/// A struct containing enough information to uniquely specify single event.
34#[derive(Debug, PartialEq, Serialize, Deserialize)]
35pub struct TxIdAndOffset {
36    /// The [`TxIdentifier`] of the transaction containing this event.
37    pub tx_id: TxIdentifier,
38    /// The offset into the tx's events at which this event is located.
39    /// Index 0 is the first event from this tx.
40    pub offset: u64,
41}
42
43/// A struct containing enough information to uniquely specify single event.
44#[derive(Debug, PartialEq, Serialize, Deserialize)]
45pub struct TxIdAndKey {
46    /// The [`TxIdentifier`] of the transaction containing this event.
47    pub tx_id: TxIdentifier,
48    /// The key of the event.
49    pub key: EventKey,
50}
51
52/// An identifier that specifies a single batch
53#[derive(Debug, PartialEq, Serialize, Deserialize)]
54#[serde(untagged)]
55pub enum BatchIdentifier {
56    /// The hex-encoded hash of the batch, as computed by the DA layer.
57    Hash(#[serde(with = "utils::rpc_hex")] [u8; 32]),
58    /// An offset into a particular slot (i.e. the 3rd batch in slot 5).
59    SlotIdAndOffset(SlotIdAndOffset),
60    /// The monotonically increasing number of the batch, ordered by the DA layer For example, if the genesis slot
61    /// contains 0 batches, slot 1 contains 2 txs, and slot 3 contains 3 txs,
62    /// the last batch in block 3 would have number 5. The counter never resets.
63    Number(u64),
64}
65
66/// An identifier that specifies a single transaction.
67#[derive(Debug, PartialEq, Serialize, Deserialize)]
68#[serde(untagged)]
69pub enum TxIdentifier {
70    /// The hex encoded hash of the transaction.
71    Hash(#[serde(with = "utils::rpc_hex")] [u8; 32]),
72    /// An offset into a particular batch (i.e. the 3rd transaction in batch 5).
73    BatchIdAndOffset(BatchIdAndOffset),
74    /// The monotonically increasing number of the tx, ordered by the DA layer For example, if genesis
75    /// contains 0 txs, batch 1 contains 8 txs, and batch 3 contains 7 txs,
76    /// the last tx in batch 3 would have number 15. The counter never resets.
77    Number(u64),
78}
79
80/// An identifier that specifies a single event.
81#[derive(Debug, PartialEq, Serialize, Deserialize)]
82#[serde(untagged)]
83pub enum EventIdentifier {
84    /// An offset into a particular transaction (i.e. the 3rd event in transaction number 5).
85    TxIdAndOffset(TxIdAndOffset),
86    /// A particular event key from a particular transaction.
87    TxIdAndKey(TxIdAndKey),
88    /// The monotonically increasing number of the event, ordered by the DA layer For example, if the first tx
89    /// contains 7 events, tx 2 contains 11 events, and tx 3 contains 7 txs,
90    /// the last event in tx 3 would have number 25. The counter never resets.
91    Number(u64),
92}
93
94/// An identifier for a group of related events
95#[derive(Debug, PartialEq, Serialize, Deserialize)]
96#[serde(untagged)]
97pub enum EventGroupIdentifier {
98    /// Fetch all events from a particular transaction.
99    TxId(TxIdentifier),
100    /// Fetch all events (i.e. from all transactions) with a particular key.
101    Key(Vec<u8>),
102}
103
104/// An identifier that specifies a single slot.
105#[derive(Debug, PartialEq, Serialize, Deserialize)]
106#[serde(untagged)]
107pub enum SlotIdentifier {
108    /// The hex encoded hash of the slot (i.e. the da layer's block hash).
109    Hash(#[serde(with = "utils::rpc_hex")] [u8; 32]),
110    /// The monotonically increasing number of the slot, ordered by the DA layer but starting from 0
111    /// at the *rollup's* genesis.
112    Number(u64),
113}
114
115/// A QueryMode specifies how much information to return in response to an RPC query
116#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
117pub enum QueryMode {
118    /// Returns the parent struct but no details about its children.
119    /// For example, a `Compact` "get_slots" response would simply state the range of batch
120    /// numbers which occurred in the slot, but not the hashes of the batches themselves.
121    Compact,
122    /// Returns the parent struct and the hashes of all its children.
123    Standard,
124    /// Returns the parent struct and all its children, recursively fetching its children
125    /// in `Full` mode. For example, a `Full` "get_batch" response would include the `Full`
126    /// details of all the transactions in the batch, and those would in turn return the event bodies
127    /// which had occurred in those transactions.
128    Full,
129}
130
131impl Default for QueryMode {
132    fn default() -> Self {
133        Self::Standard
134    }
135}
136
137/// The body of a response to a JSON-RPC request for a particular slot.
138#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
139pub struct SlotResponse<B, Tx> {
140    /// The slot number.
141    pub number: u64,
142    /// The hex encoded slot hash.
143    #[serde(with = "utils::rpc_hex")]
144    pub hash: [u8; 32],
145    /// The range of batches in this slot.
146    pub batch_range: std::ops::Range<u64>,
147    /// The batches in this slot, if the [`QueryMode`] of the request is not `Compact`
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub batches: Option<Vec<ItemOrHash<BatchResponse<B, Tx>>>>,
150}
151
152/// The response to a JSON-RPC request for a particular batch.
153#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
154pub struct BatchResponse<B, Tx> {
155    /// The hex encoded batch hash.
156    #[serde(with = "utils::rpc_hex")]
157    pub hash: [u8; 32],
158    /// The range of transactions in this batch.
159    pub tx_range: std::ops::Range<u64>,
160    /// The transactions in this batch, if the [`QueryMode`] of the request is not `Compact`.
161    #[serde(skip_serializing_if = "Option::is_none")]
162    pub txs: Option<Vec<ItemOrHash<TxResponse<Tx>>>>,
163    /// The custom receipt specified by the rollup. This typically contains
164    /// information about the outcome of the batch.
165    pub custom_receipt: B,
166}
167
168/// The response to a JSON-RPC request for a particular transaction.
169#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
170pub struct TxResponse<Tx> {
171    /// The hex encoded transaction hash.
172    #[serde(with = "utils::rpc_hex")]
173    pub hash: [u8; 32],
174    /// The range of events occurring in this transaction.
175    pub event_range: std::ops::Range<u64>,
176    /// The transaction body, if stored by the rollup.
177    #[serde(skip_serializing_if = "Option::is_none")]
178    pub body: Option<Vec<u8>>,
179    /// The custom receipt specified by the rollup. This typically contains
180    /// information about the outcome of the transaction.
181    pub custom_receipt: Tx,
182}
183
184/// An RPC response which might contain a full item or just its hash.
185#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
186#[serde(untagged)]
187pub enum ItemOrHash<T> {
188    /// The hex encoded hash of the requested item.
189    Hash(#[serde(with = "utils::rpc_hex")] [u8; 32]),
190    /// The full item body.
191    Full(T),
192}
193
194/// A LedgerRpcProvider provides a way to query the ledger for information about slots, batches, transactions, and events.
195#[cfg(feature = "native")]
196pub trait LedgerRpcProvider {
197    /// Get the latest slot in the ledger.
198    fn get_head<B: DeserializeOwned + Clone, T: DeserializeOwned>(
199        &self,
200        query_mode: QueryMode,
201    ) -> Result<Option<SlotResponse<B, T>>, anyhow::Error>;
202
203    /// Get a list of slots by id. The IDs need not be ordered.
204    fn get_slots<B: DeserializeOwned, T: DeserializeOwned>(
205        &self,
206        slot_ids: &[SlotIdentifier],
207        query_mode: QueryMode,
208    ) -> Result<Vec<Option<SlotResponse<B, T>>>, anyhow::Error>;
209
210    /// Get a list of batches by id. The IDs need not be ordered.
211    fn get_batches<B: DeserializeOwned, T: DeserializeOwned>(
212        &self,
213        batch_ids: &[BatchIdentifier],
214        query_mode: QueryMode,
215    ) -> Result<Vec<Option<BatchResponse<B, T>>>, anyhow::Error>;
216
217    /// Get a list of transactions by id. The IDs need not be ordered.
218    fn get_transactions<T: DeserializeOwned>(
219        &self,
220        tx_ids: &[TxIdentifier],
221        query_mode: QueryMode,
222    ) -> Result<Vec<Option<TxResponse<T>>>, anyhow::Error>;
223
224    /// Get events by id. The IDs need not be ordered.
225    fn get_events(
226        &self,
227        event_ids: &[EventIdentifier],
228    ) -> Result<Vec<Option<Event>>, anyhow::Error>;
229
230    /// Get a single slot by hash.
231    fn get_slot_by_hash<B: DeserializeOwned, T: DeserializeOwned>(
232        &self,
233        hash: &[u8; 32],
234        query_mode: QueryMode,
235    ) -> Result<Option<SlotResponse<B, T>>, anyhow::Error>;
236
237    /// Get a single batch by hash.
238    fn get_batch_by_hash<B: DeserializeOwned, T: DeserializeOwned>(
239        &self,
240        hash: &[u8; 32],
241        query_mode: QueryMode,
242    ) -> Result<Option<BatchResponse<B, T>>, anyhow::Error>;
243
244    /// Get a single transaction by hash.
245    fn get_tx_by_hash<T: DeserializeOwned>(
246        &self,
247        hash: &[u8; 32],
248        query_mode: QueryMode,
249    ) -> Result<Option<TxResponse<T>>, anyhow::Error>;
250
251    /// Get a single slot by number.
252    fn get_slot_by_number<B: DeserializeOwned, T: DeserializeOwned>(
253        &self,
254        number: u64,
255        query_mode: QueryMode,
256    ) -> Result<Option<SlotResponse<B, T>>, anyhow::Error>;
257
258    /// Get a single batch by number.
259    fn get_batch_by_number<B: DeserializeOwned, T: DeserializeOwned>(
260        &self,
261        number: u64,
262        query_mode: QueryMode,
263    ) -> Result<Option<BatchResponse<B, T>>, anyhow::Error>;
264
265    /// Get a single event by number.
266    fn get_event_by_number(&self, number: u64) -> Result<Option<Event>, anyhow::Error>;
267
268    /// Get a single tx by number.
269    fn get_tx_by_number<T: DeserializeOwned>(
270        &self,
271        number: u64,
272        query_mode: QueryMode,
273    ) -> Result<Option<TxResponse<T>>, anyhow::Error>;
274
275    /// Get a range of slots. This query is the most efficient way to
276    /// fetch large numbers of slots, since it allows for easy batching of
277    /// db queries for adjacent items.
278    fn get_slots_range<B: DeserializeOwned, T: DeserializeOwned>(
279        &self,
280        start: u64,
281        end: u64,
282        query_mode: QueryMode,
283    ) -> Result<Vec<Option<SlotResponse<B, T>>>, anyhow::Error>;
284
285    /// Get a range of batches. This query is the most efficient way to
286    /// fetch large numbers of batches, since it allows for easy batching of
287    /// db queries for adjacent items.
288    fn get_batches_range<B: DeserializeOwned, T: DeserializeOwned>(
289        &self,
290        start: u64,
291        end: u64,
292        query_mode: QueryMode,
293    ) -> Result<Vec<Option<BatchResponse<B, T>>>, anyhow::Error>;
294
295    /// Get a range of batches. This query is the most efficient way to
296    /// fetch large numbers of transactions, since it allows for easy batching of
297    /// db queries for adjacent items.
298    fn get_transactions_range<T: DeserializeOwned>(
299        &self,
300        start: u64,
301        end: u64,
302        query_mode: QueryMode,
303    ) -> Result<Vec<Option<TxResponse<T>>>, anyhow::Error>;
304
305    /// Get a notification each time a slot is processed
306    fn subscribe_slots(&self) -> Result<Receiver<u64>, anyhow::Error>;
307}
308
309/// JSON-RPC -related utilities. Occasionally useful but unimportant for most
310/// use cases.
311pub mod utils {
312    /// Serialization and deserialization logic for `0x`-prefixed hex strings.
313    pub mod rpc_hex {
314        use core::fmt;
315        use std::marker::PhantomData;
316
317        use hex::{FromHex, ToHex};
318        use serde::de::{Error, Visitor};
319        use serde::{Deserializer, Serializer};
320
321        /// Serializes `data` as hex string using lowercase characters and prefixing with '0x'.
322        ///
323        /// Lowercase characters are used (e.g. `f9b4ca`). The resulting string's length
324        /// is always even, each byte in data is always encoded using two hex digits.
325        /// Thus, the resulting string contains exactly twice as many bytes as the input
326        /// data.
327        pub fn serialize<S, T>(data: T, serializer: S) -> Result<S::Ok, S::Error>
328        where
329            S: Serializer,
330            T: ToHex,
331        {
332            let formatted_string = format!("0x{}", data.encode_hex::<String>());
333            serializer.serialize_str(&formatted_string)
334        }
335
336        /// Deserializes a hex string into raw bytes.
337        ///
338        /// Both, upper and lower case characters are valid in the input string and can
339        /// even be mixed (e.g. `f9b4ca`, `F9B4CA` and `f9B4Ca` are all valid strings).
340        pub fn deserialize<'de, D, T>(deserializer: D) -> Result<T, D::Error>
341        where
342            D: Deserializer<'de>,
343            T: FromHex,
344            <T as FromHex>::Error: fmt::Display,
345        {
346            struct HexStrVisitor<T>(PhantomData<T>);
347
348            impl<'de, T> Visitor<'de> for HexStrVisitor<T>
349            where
350                T: FromHex,
351                <T as FromHex>::Error: fmt::Display,
352            {
353                type Value = T;
354
355                fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
356                    write!(f, "a hex encoded string")
357                }
358
359                fn visit_str<E>(self, data: &str) -> Result<Self::Value, E>
360                where
361                    E: Error,
362                {
363                    let data = data.trim_start_matches("0x");
364                    FromHex::from_hex(data).map_err(Error::custom)
365                }
366
367                fn visit_borrowed_str<E>(self, data: &'de str) -> Result<Self::Value, E>
368                where
369                    E: Error,
370                {
371                    let data = data.trim_start_matches("0x");
372                    FromHex::from_hex(data).map_err(Error::custom)
373                }
374            }
375
376            deserializer.deserialize_str(HexStrVisitor(PhantomData))
377        }
378    }
379}
380
381#[cfg(test)]
382mod rpc_hex_tests {
383    use serde::{Deserialize, Serialize};
384
385    #[derive(Serialize, Deserialize, PartialEq, Debug)]
386    struct TestStruct {
387        #[serde(with = "super::utils::rpc_hex")]
388        data: Vec<u8>,
389    }
390
391    #[test]
392    fn test_roundtrip() {
393        let test_data = TestStruct {
394            data: vec![0x01, 0x02, 0x03, 0x04],
395        };
396
397        let serialized = serde_json::to_string(&test_data).unwrap();
398        assert!(serialized.contains("0x01020304"));
399        let deserialized: TestStruct = serde_json::from_str(&serialized).unwrap();
400        assert_eq!(deserialized, test_data)
401    }
402
403    #[test]
404    fn test_accepts_hex_without_0x_prefix() {
405        let test_data = TestStruct {
406            data: vec![0x01, 0x02, 0x03, 0x04],
407        };
408
409        let deserialized: TestStruct = serde_json::from_str(r#"{"data": "01020304"}"#).unwrap();
410        assert_eq!(deserialized, test_data)
411    }
412}