nitro_da_indexer_api/
lib.rs1#![doc = include_str!("../README.md")]
2
3use std::collections::HashSet;
4
5use anchor_lang::{AnchorDeserialize, Discriminator};
6use jsonrpsee::{
7 core::{RpcResult, SubscriptionResult},
8 proc_macros::rpc,
9};
10use nitro_da_proofs::compound::{
11 completeness::CompoundCompletenessProof, inclusion::CompoundInclusionProof,
12};
13use serde::{Deserialize, Serialize};
14use solana_sdk::{
15 clock::Slot, instruction::CompiledInstruction, pubkey::Pubkey,
16 transaction::VersionedTransaction,
17};
18
19#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
22pub enum CompoundProof {
23 Inclusion(CompoundInclusionProof),
25 Completeness(CompoundCompletenessProof),
27}
28
29#[rpc(server, client)]
31pub trait IndexerRpc {
32 #[method(name = "get_blobs")]
36 async fn get_blobs(&self, blober: Pubkey, slot: u64) -> RpcResult<Option<Vec<Vec<u8>>>>;
37
38 #[method(name = "get_proof")]
41 async fn get_proof(&self, blober: Pubkey, slot: u64) -> RpcResult<Option<CompoundProof>>;
42
43 #[method(name = "add_blobers")]
45 async fn add_blobers(&self, blobers: HashSet<Pubkey>) -> RpcResult<()>;
46
47 #[method(name = "remove_blobers")]
49 async fn remove_blobers(&self, blobers: HashSet<Pubkey>) -> RpcResult<()>;
50
51 #[subscription(name = "subscribe_blob_finalization" => "listen_subscribe_blob_finalization", unsubscribe = "unsubscribe_blob_finalization", item = (Pubkey, Slot))]
55 async fn subscribe_blob_finalization(&self, blobers: HashSet<Pubkey>) -> SubscriptionResult;
56}
57
58pub enum RelevantInstruction {
60 DeclareBlob(nitro_da_blober::instruction::DeclareBlob),
61 InsertChunk(nitro_da_blober::instruction::InsertChunk),
62 FinalizeBlob(nitro_da_blober::instruction::FinalizeBlob),
63}
64
65impl RelevantInstruction {
66 pub fn try_from_slice(compiled_instruction: &CompiledInstruction) -> Option<Self> {
67 use nitro_da_blober::instruction::*;
68 let discriminator = compiled_instruction.data.get(..8)?;
69
70 match discriminator {
71 DeclareBlob::DISCRIMINATOR => {
72 let data = compiled_instruction.data.get(8..).unwrap_or_default();
73 DeclareBlob::try_from_slice(data)
74 .map(RelevantInstruction::DeclareBlob)
75 .ok()
76 }
77 InsertChunk::DISCRIMINATOR => {
78 let data = compiled_instruction.data.get(8..).unwrap_or_default();
79 InsertChunk::try_from_slice(data)
80 .map(RelevantInstruction::InsertChunk)
81 .ok()
82 }
83 FinalizeBlob::DISCRIMINATOR => {
84 let data = compiled_instruction.data.get(8..).unwrap_or_default();
85 FinalizeBlob::try_from_slice(data)
86 .map(RelevantInstruction::FinalizeBlob)
87 .ok()
88 }
89 _ => None,
93 }
94 }
95}
96
97pub struct RelevantInstructionWithAccounts {
99 pub blob: Pubkey,
100 pub blober: Pubkey,
101 pub instruction: RelevantInstruction,
102}
103
104pub fn deserialize_relevant_instructions(
107 tx: &VersionedTransaction,
108 blob_pubkey_index: usize,
109 blober_pubkey_index: usize,
110) -> Vec<RelevantInstructionWithAccounts> {
111 tx.message
112 .instructions()
113 .iter()
114 .filter_map(|compiled_instruction| {
115 Some(RelevantInstructionWithAccounts {
116 blob: get_account_at_index(tx, compiled_instruction, blob_pubkey_index)?,
117 blober: get_account_at_index(tx, compiled_instruction, blober_pubkey_index)?,
118 instruction: RelevantInstruction::try_from_slice(compiled_instruction)?,
119 })
120 })
121 .collect()
122}
123
124pub fn extract_relevant_instructions(
126 transactions: &[VersionedTransaction],
127) -> Vec<RelevantInstructionWithAccounts> {
128 transactions
129 .iter()
130 .flat_map(|tx| deserialize_relevant_instructions(tx, 0, 1))
131 .collect()
132}
133
134pub fn get_account_at_index(
138 tx: &VersionedTransaction,
139 instruction: &CompiledInstruction,
140 index: usize,
141) -> Option<Pubkey> {
142 let actual_index = *instruction.accounts.get(index)? as usize;
143 tx.message.static_account_keys().get(actual_index).copied()
144}