use std::collections::HashSet;
use anchor_lang::prelude::Pubkey;
use chrono::{DateTime, Utc};
use clap::ValueEnum;
use data_anchor_blober::GROTH16_PROOF_SIZE;
use data_anchor_proofs::compound::CompoundInclusionProof;
use jsonrpsee::{
core::{RpcResult, SubscriptionResult},
proc_macros::rpc,
};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct BloberData {
#[serde(with = "pubkey_with_str")]
pub blober: Pubkey,
pub payer: Pubkey,
pub network_id: u64,
}
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TimeRange {
pub start: Option<DateTime<Utc>>,
pub end: Option<DateTime<Utc>>,
}
impl TimeRange {
pub fn to_db_defaults(&self) -> (DateTime<Utc>, DateTime<Utc>) {
#[allow(clippy::unwrap_used, reason = "Hardcoding 0 will never panic")]
let default_start = DateTime::<Utc>::from_timestamp_micros(0).unwrap();
(
self.start.unwrap_or(default_start),
self.end.unwrap_or(Utc::now()),
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PubkeyFromStr(#[serde(with = "pubkey_with_str")] pub Pubkey);
impl From<PubkeyFromStr> for Pubkey {
fn from(value: PubkeyFromStr) -> Self {
value.0
}
}
impl From<Pubkey> for PubkeyFromStr {
fn from(value: Pubkey) -> Self {
PubkeyFromStr(value)
}
}
#[serde_with::serde_as]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProofData {
#[serde_as(as = "serde_with::Bytes")]
pub proof: [u8; GROTH16_PROOF_SIZE],
pub public_values: Vec<u8>,
pub verification_key: String,
}
#[rpc(server, client)]
pub trait IndexerRpc {
#[method(name = "health")]
async fn health(&self) -> RpcResult<()>;
#[method(name = "get_blobs")]
async fn get_blobs(&self, blober: PubkeyFromStr, slot: u64) -> RpcResult<Option<Vec<Vec<u8>>>>;
#[method(name = "get_blobs_by_blober")]
async fn get_blobs_by_blober(
&self,
blober: PubkeyFromStr,
time_range: Option<TimeRange>,
) -> RpcResult<Vec<Vec<u8>>>;
#[method(name = "get_blobs_by_payer")]
async fn get_blobs_by_payer(
&self,
payer: PubkeyFromStr,
network_name: String,
time_range: Option<TimeRange>,
) -> RpcResult<Vec<Vec<u8>>>;
#[method(name = "get_blobs_by_network")]
async fn get_blobs_by_network(
&self,
network_name: String,
time_range: Option<TimeRange>,
) -> RpcResult<Vec<Vec<u8>>>;
#[method(name = "get_blobs_by_namespace")]
async fn get_blobs_by_namespace_for_payer(
&self,
namespace: String,
payer: Option<PubkeyFromStr>,
time_range: Option<TimeRange>,
) -> RpcResult<Vec<Vec<u8>>>;
#[method(name = "get_payers_by_network")]
async fn get_payers_by_network(&self, network_name: String) -> RpcResult<Vec<PubkeyFromStr>>;
#[deprecated(since = "0.4.3", note = "please use `checkpoint_proof` instead")]
#[method(name = "get_proof")]
async fn get_proof(
&self,
blober: PubkeyFromStr,
slot: u64,
) -> RpcResult<Option<CompoundInclusionProof>>;
#[deprecated(since = "0.4.3", note = "please use `checkpoint_proof` instead")]
#[method(name = "get_proof_for_blob")]
async fn get_proof_for_blob(
&self,
blob_address: PubkeyFromStr,
) -> RpcResult<Option<CompoundInclusionProof>>;
#[subscription(
name = "subscribe_blob_finalization" => "listen_subscribe_blob_finalization",
unsubscribe = "unsubscribe_blob_finalization",
item = (Pubkey, u64)
)]
async fn subscribe_blob_finalization(
&self,
blobers: HashSet<PubkeyFromStr>,
) -> SubscriptionResult;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ValueEnum)]
#[serde(rename_all = "kebab-case")]
pub enum CustomerElf {
DataCorrectness,
PobSla,
}
impl std::fmt::Display for CustomerElf {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CustomerElf::DataCorrectness => write!(f, "data-correctness"),
CustomerElf::PobSla => write!(f, "pob-sla"),
}
}
}
impl CustomerElf {
pub fn authority(&self) -> Pubkey {
match self {
CustomerElf::DataCorrectness => data_anchor_data_correctness_verifier::id(),
CustomerElf::PobSla => data_anchor_pob_sla_verifier::id(),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(i8)]
pub enum RequestFailureReason {
#[default]
Unknown,
ProofGenerationFailed,
TransactionError,
RpcConnection,
}
impl From<RequestFailureReason> for i16 {
fn from(reason: RequestFailureReason) -> Self {
match reason {
RequestFailureReason::Unknown => -1,
RequestFailureReason::ProofGenerationFailed => -2,
RequestFailureReason::TransactionError => -3,
RequestFailureReason::RpcConnection => -4,
}
}
}
impl From<i16> for RequestFailureReason {
fn from(reason: i16) -> Self {
match reason {
-1 => RequestFailureReason::Unknown,
-2 => RequestFailureReason::ProofGenerationFailed,
-3 => RequestFailureReason::TransactionError,
-4 => RequestFailureReason::RpcConnection,
#[allow(
clippy::panic,
reason = "This should never happen as we only use this for reading from the database"
)]
_ => panic!("Invalid request failure reason: {reason}"),
}
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(i8)]
pub enum RequestStatus {
#[default]
Created,
Submitted,
Completed,
Posted,
Failed(RequestFailureReason),
}
impl From<RequestStatus> for i16 {
fn from(status: RequestStatus) -> Self {
match status {
RequestStatus::Created => 0,
RequestStatus::Submitted => 1,
RequestStatus::Completed => 2,
RequestStatus::Posted => 3,
RequestStatus::Failed(reason) => reason.into(),
}
}
}
impl From<i16> for RequestStatus {
fn from(status: i16) -> Self {
match status {
0 => RequestStatus::Created,
1 => RequestStatus::Submitted,
2 => RequestStatus::Completed,
3 => RequestStatus::Posted,
x if x < 0 => RequestStatus::Failed(x.into()),
#[allow(
clippy::panic,
reason = "This should never happen as we only use this for reading from the database"
)]
_ => panic!("Invalid request status: {status}"),
}
}
}
#[rpc(server, client)]
pub trait ProofRpc {
#[method(name = "health")]
async fn health(&self) -> RpcResult<()>;
#[method(name = "checkpoint_proof")]
async fn checkpoint_proof(
&self,
blober: PubkeyFromStr,
slot: u64,
customer_elf: CustomerElf,
) -> RpcResult<String>;
#[method(name = "get_proof_request_status")]
async fn get_proof_request_status(&self, request_id: String) -> RpcResult<RequestStatus>;
}
pub mod pubkey_with_str {
use std::str::FromStr;
use anchor_lang::prelude::Pubkey;
use serde::{Deserialize, Deserializer, de};
pub fn deserialize<'de, D>(deserializer: D) -> Result<Pubkey, D::Error>
where
D: Deserializer<'de>,
{
String::deserialize(deserializer)
.and_then(|key| Pubkey::from_str(&key).map_err(de::Error::custom))
}
pub fn serialize<S>(pubkey: &Pubkey, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&pubkey.to_string())
}
}