pindexer/stake/
missed_blocks.rs1use 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 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 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 sqlx::query("CREATE INDEX idx_stake_missed_blocks_ik ON stake_missed_blocks(ik);")
41 .execute(dbtx.as_mut())
42 .await?;
43
44 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}