pindexer/stake/
missed_blocks.rs

1use anyhow::Result;
2use cometindex::{
3    async_trait,
4    index::{EventBatch, EventBatchContext},
5    sqlx, AppView, PgTransaction,
6};
7
8use penumbra_sdk_proto::{core::component::stake::v1 as pb, event::ProtoEvent};
9use penumbra_sdk_stake::IdentityKey;
10
11#[derive(Debug)]
12pub struct MissedBlocks {}
13
14#[async_trait]
15impl AppView for MissedBlocks {
16    async fn init_chain(
17        &self,
18        dbtx: &mut PgTransaction,
19        _app_state: &serde_json::Value,
20    ) -> Result<(), anyhow::Error> {
21        // Create the table
22        sqlx::query(
23            "CREATE TABLE stake_missed_blocks (
24                id SERIAL PRIMARY KEY,
25                height BIGINT NOT NULL,
26                ik TEXT NOT NULL
27            );",
28        )
29        .execute(dbtx.as_mut())
30        .await?;
31
32        // Create descending index on height
33        sqlx::query(
34            "CREATE INDEX idx_stake_missed_blocks_height ON stake_missed_blocks(height DESC);",
35        )
36        .execute(dbtx.as_mut())
37        .await?;
38
39        // Create index on ik
40        sqlx::query("CREATE INDEX idx_stake_missed_blocks_ik ON stake_missed_blocks(ik);")
41            .execute(dbtx.as_mut())
42            .await?;
43
44        // Create composite index on height (descending) and ik
45        sqlx::query("CREATE INDEX idx_stake_missed_blocks_height_ik ON stake_missed_blocks(height DESC, ik);")
46            .execute(dbtx.as_mut())
47            .await?;
48
49        Ok(())
50    }
51
52    fn name(&self) -> String {
53        "stake/missed_blocks".to_string()
54    }
55
56    async fn index_batch(
57        &self,
58        dbtx: &mut PgTransaction,
59        batch: EventBatch,
60        _ctx: EventBatchContext,
61    ) -> Result<(), anyhow::Error> {
62        for event in batch.events() {
63            let pe = match pb::EventValidatorMissedBlock::from_event(event.as_ref()) {
64                Ok(pe) => pe,
65                Err(_) => continue,
66            };
67            let ik: IdentityKey = pe
68                .identity_key
69                .ok_or_else(|| anyhow::anyhow!("missing ik in event"))?
70                .try_into()?;
71
72            let height = event.block_height;
73
74            sqlx::query("INSERT INTO stake_missed_blocks (height, ik) VALUES ($1, $2)")
75                .bind(height as i64)
76                .bind(ik.to_string())
77                .execute(dbtx.as_mut())
78                .await?;
79        }
80
81        Ok(())
82    }
83}