data_anchor_api/
rpc.rs

1use std::collections::HashSet;
2
3use anchor_lang::prelude::Pubkey;
4use chrono::{DateTime, Utc};
5use clap::ValueEnum;
6use data_anchor_blober::GROTH16_PROOF_SIZE;
7use data_anchor_proofs::compound::CompoundInclusionProof;
8use jsonrpsee::{
9    core::{RpcResult, SubscriptionResult},
10    proc_macros::rpc,
11};
12use serde::{Deserialize, Serialize};
13
14/// A data structure representing a blober's information, including the blober's pubkey, the
15/// payer's pubkey, and the network of the blober.
16#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
17pub struct BloberData {
18    #[serde(with = "pubkey_with_str")]
19    pub blober: Pubkey,
20    pub payer: Pubkey,
21    pub network_id: u64,
22}
23
24/// A time range with optional start and end times, used for filtering time.
25#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
26pub struct TimeRange {
27    /// The start time of the range, inclusive.
28    pub start: Option<DateTime<Utc>>,
29    /// The end time of the range, inclusive.
30    pub end: Option<DateTime<Utc>>,
31}
32
33impl TimeRange {
34    /// Returns the start and end times as a tuple of `DateTime<Utc>`, with defaults for
35    /// missing values.
36    pub fn to_db_defaults(&self) -> (DateTime<Utc>, DateTime<Utc>) {
37        #[allow(clippy::unwrap_used, reason = "Hardcoding 0 will never panic")]
38        let default_start = DateTime::<Utc>::from_timestamp_micros(0).unwrap();
39
40        (
41            self.start.unwrap_or(default_start),
42            self.end.unwrap_or(Utc::now()),
43        )
44    }
45}
46
47/// A wrapper around a blober's pubkey, used to identify a blober in RPC calls.
48#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
49pub struct PubkeyFromStr(#[serde(with = "pubkey_with_str")] pub Pubkey);
50
51impl From<PubkeyFromStr> for Pubkey {
52    fn from(value: PubkeyFromStr) -> Self {
53        value.0
54    }
55}
56
57impl From<Pubkey> for PubkeyFromStr {
58    fn from(value: Pubkey) -> Self {
59        PubkeyFromStr(value)
60    }
61}
62
63/// Data structure to hold the proof data
64#[serde_with::serde_as]
65#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
66pub struct ProofData {
67    /// The Groth16 proof bytes
68    #[serde_as(as = "serde_with::Bytes")]
69    pub proof: [u8; GROTH16_PROOF_SIZE],
70    /// The public values from the proof
71    pub public_values: Vec<u8>,
72    /// The verification key bytes in hex encoding with a leading "0x"
73    pub verification_key: String,
74}
75
76/// The Indexer RPC interface.
77#[rpc(server, client)]
78pub trait IndexerRpc {
79    /// Check the health of the RPC server. Returns an error if the server is not healthy.
80    #[method(name = "health")]
81    async fn health(&self) -> RpcResult<()>;
82
83    /// Retrieve a list of blobs for a given slot and blober pubkey. Returns an error if there was a
84    /// database or RPC failure, and None if the slot has not been completed yet. If the slot is
85    /// completed, an empty list will be returned.
86    #[method(name = "get_blobs")]
87    async fn get_blobs(&self, blober: PubkeyFromStr, slot: u64) -> RpcResult<Option<Vec<Vec<u8>>>>;
88
89    /// Retrieve a list of blobs for a given blober pubkey and time range. Returns an error if there
90    /// was a database or RPC failure, and an empty list if no blobs were found.
91    #[method(name = "get_blobs_by_blober")]
92    async fn get_blobs_by_blober(
93        &self,
94        blober: PubkeyFromStr,
95        time_range: Option<TimeRange>,
96    ) -> RpcResult<Vec<Vec<u8>>>;
97
98    /// Retrieve a list of blobs for a given payer pubkey, network ID, and time range. Returns an
99    /// error if there was a database or RPC failure, and an empty list if no blobs were found.
100    #[method(name = "get_blobs_by_payer")]
101    async fn get_blobs_by_payer(
102        &self,
103        payer: PubkeyFromStr,
104        network_name: String,
105        time_range: Option<TimeRange>,
106    ) -> RpcResult<Vec<Vec<u8>>>;
107
108    /// Retrieve a list of blobs for a given network name and time range. Returns an error if there
109    /// was a database or RPC failure, and an empty list if no blobs were found.
110    #[method(name = "get_blobs_by_network")]
111    async fn get_blobs_by_network(
112        &self,
113        network_name: String,
114        time_range: Option<TimeRange>,
115    ) -> RpcResult<Vec<Vec<u8>>>;
116
117    /// Retrieve a list of blobs for a given namespace and time range. Returns an error if there
118    /// was a database or RPC failure, and an empty list if no blobs were found.
119    #[method(name = "get_blobs_by_namespace")]
120    async fn get_blobs_by_namespace_for_payer(
121        &self,
122        namespace: String,
123        payer: Option<PubkeyFromStr>,
124        time_range: Option<TimeRange>,
125    ) -> RpcResult<Vec<Vec<u8>>>;
126
127    /// Retrieve a list of payers for a given network name. Returns an error if there was a
128    /// database or RPC failure, and an empty list if no payers were found.
129    #[method(name = "get_payers_by_network")]
130    async fn get_payers_by_network(&self, network_name: String) -> RpcResult<Vec<PubkeyFromStr>>;
131
132    /// Retrieve a proof for a given slot and blober pubkey. Returns an error if there was a
133    /// database or RPC failure, and None if the slot has not been completed yet.
134    #[deprecated(since = "0.4.3", note = "please use `checkpoint_proof` instead")]
135    #[method(name = "get_proof")]
136    async fn get_proof(
137        &self,
138        blober: PubkeyFromStr,
139        slot: u64,
140    ) -> RpcResult<Option<CompoundInclusionProof>>;
141
142    /// Retrieve a compound proof that covers a particular blob. Returns an error if there was a
143    /// database or RPC failure, and None if the blob does not exist.
144    #[deprecated(since = "0.4.3", note = "please use `checkpoint_proof` instead")]
145    #[method(name = "get_proof_for_blob")]
146    async fn get_proof_for_blob(
147        &self,
148        blob_address: PubkeyFromStr,
149    ) -> RpcResult<Option<CompoundInclusionProof>>;
150
151    /// Listen to blob finalization events from specified blobers. This will return a stream of
152    /// slots and blober PDAs that have finalized blobs. The stream will be closed when the RPC server is
153    /// shut down.
154    #[subscription(
155        name = "subscribe_blob_finalization" => "listen_subscribe_blob_finalization",
156        unsubscribe = "unsubscribe_blob_finalization", 
157        item = (Pubkey, u64)
158    )]
159    async fn subscribe_blob_finalization(
160        &self,
161        blobers: HashSet<PubkeyFromStr>,
162    ) -> SubscriptionResult;
163}
164
165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ValueEnum)]
166#[serde(rename_all = "kebab-case")]
167pub enum CustomerElf {
168    /// Data correctness elf, commits to the data being correct.
169    DataCorrectness,
170    /// Pob SLA elf, which commits to the data being correct and to a SLA result.
171    PobSla,
172}
173
174impl std::fmt::Display for CustomerElf {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            CustomerElf::DataCorrectness => write!(f, "data-correctness"),
178            CustomerElf::PobSla => write!(f, "pob-sla"),
179        }
180    }
181}
182
183impl CustomerElf {
184    pub fn authority(&self) -> Pubkey {
185        match self {
186            CustomerElf::DataCorrectness => data_anchor_data_correctness_verifier::id(),
187            CustomerElf::PobSla => data_anchor_pob_sla_verifier::id(),
188        }
189    }
190}
191
192#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
193#[repr(i8)]
194pub enum RequestFailureReason {
195    #[default]
196    Unknown,
197    ProofGenerationFailed,
198    TransactionError,
199    RpcConnection,
200}
201
202impl From<RequestFailureReason> for i16 {
203    fn from(reason: RequestFailureReason) -> Self {
204        match reason {
205            RequestFailureReason::Unknown => -1,
206            RequestFailureReason::ProofGenerationFailed => -2,
207            RequestFailureReason::TransactionError => -3,
208            RequestFailureReason::RpcConnection => -4,
209        }
210    }
211}
212
213impl From<i16> for RequestFailureReason {
214    fn from(reason: i16) -> Self {
215        match reason {
216            -1 => RequestFailureReason::Unknown,
217            -2 => RequestFailureReason::ProofGenerationFailed,
218            -3 => RequestFailureReason::TransactionError,
219            -4 => RequestFailureReason::RpcConnection,
220            #[allow(
221                clippy::panic,
222                reason = "This should never happen as we only use this for reading from the database"
223            )]
224            _ => panic!("Invalid request failure reason: {reason}"),
225        }
226    }
227}
228
229#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
230#[repr(i8)]
231pub enum RequestStatus {
232    #[default]
233    Created,
234    Submitted,
235    Completed,
236    Posted,
237    Failed(RequestFailureReason),
238}
239
240impl From<RequestStatus> for i16 {
241    fn from(status: RequestStatus) -> Self {
242        match status {
243            RequestStatus::Created => 0,
244            RequestStatus::Submitted => 1,
245            RequestStatus::Completed => 2,
246            RequestStatus::Posted => 3,
247            RequestStatus::Failed(reason) => reason.into(),
248        }
249    }
250}
251
252impl From<i16> for RequestStatus {
253    fn from(status: i16) -> Self {
254        match status {
255            0 => RequestStatus::Created,
256            1 => RequestStatus::Submitted,
257            2 => RequestStatus::Completed,
258            3 => RequestStatus::Posted,
259            x if x < 0 => RequestStatus::Failed(x.into()),
260            #[allow(
261                clippy::panic,
262                reason = "This should never happen as we only use this for reading from the database"
263            )]
264            _ => panic!("Invalid request status: {status}"),
265        }
266    }
267}
268
269/// The Proof RPC interface.
270#[rpc(server, client)]
271pub trait ProofRpc {
272    /// Check the health of the RPC server. Returns an error if the server is not healthy.
273    #[method(name = "health")]
274    async fn health(&self) -> RpcResult<()>;
275
276    /// Request building a succinct ZK Groth16 proof for a given blober and slot. (Custom per
277    /// client)
278    #[method(name = "checkpoint_proof")]
279    async fn checkpoint_proof(
280        &self,
281        blober: PubkeyFromStr,
282        slot: u64,
283        customer_elf: CustomerElf,
284    ) -> RpcResult<String>;
285
286    /// Get a proof request status by its ID. Returns an error if the request does not exist or
287    /// if there was a database or RPC failure.
288    #[method(name = "get_proof_request_status")]
289    async fn get_proof_request_status(&self, request_id: String) -> RpcResult<RequestStatus>;
290}
291
292pub mod pubkey_with_str {
293    use std::str::FromStr;
294
295    use anchor_lang::prelude::Pubkey;
296    use serde::{Deserialize, Deserializer, de};
297
298    pub fn deserialize<'de, D>(deserializer: D) -> Result<Pubkey, D::Error>
299    where
300        D: Deserializer<'de>,
301    {
302        String::deserialize(deserializer)
303            .and_then(|key| Pubkey::from_str(&key).map_err(de::Error::custom))
304    }
305
306    pub fn serialize<S>(pubkey: &Pubkey, serializer: S) -> Result<S::Ok, S::Error>
307    where
308        S: serde::Serializer,
309    {
310        serializer.serialize_str(&pubkey.to_string())
311    }
312}