pindexer/stake/
slashings.rs1use anyhow::{anyhow, Result};
2use cometindex::{
3 async_trait,
4 index::{EventBatch, EventBatchContext},
5 sqlx, AppView, ContextualizedEvent, 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 Slashings {}
13
14impl Slashings {
15 async fn index_event(
16 &self,
17 dbtx: &mut PgTransaction<'_>,
18 event: ContextualizedEvent<'_>,
19 ) -> Result<(), anyhow::Error> {
20 let pe = match pb::EventSlashingPenaltyApplied::from_event(event.as_ref()) {
21 Ok(pe) => pe,
22 Err(_) => return Ok(()),
23 };
24 let ik = IdentityKey::try_from(
25 pe.identity_key
26 .ok_or_else(|| anyhow!("missing ik in event"))?,
27 )?;
28
29 let height = event.block_height;
30 let epoch_index = pe.epoch_index;
31
32 let penalty_json = serde_json::to_string(
33 &pe.new_penalty
34 .ok_or_else(|| anyhow!("missing new_penalty"))?,
35 )?;
36
37 sqlx::query(
38 "INSERT INTO stake_slashings (height, ik, epoch_index, penalty)
39 VALUES ($1, $2, $3, $4)",
40 )
41 .bind(height as i64)
42 .bind(ik.to_string())
43 .bind(epoch_index as i64)
44 .bind(penalty_json)
45 .execute(dbtx.as_mut())
46 .await?;
47
48 Ok(())
49 }
50}
51
52#[async_trait]
53impl AppView for Slashings {
54 async fn init_chain(
55 &self,
56 dbtx: &mut PgTransaction,
57 _app_state: &serde_json::Value,
58 ) -> Result<(), anyhow::Error> {
59 sqlx::query(
60 "CREATE TABLE stake_slashings (
61 id SERIAL PRIMARY KEY,
62 height BIGINT NOT NULL,
63 ik TEXT NOT NULL,
64 epoch_index BIGINT NOT NULL,
65 penalty TEXT NOT NULL
66 );",
67 )
68 .execute(dbtx.as_mut())
69 .await?;
70
71 sqlx::query("CREATE INDEX idx_stake_slashings_height ON stake_slashings(height);")
72 .execute(dbtx.as_mut())
73 .await?;
74
75 sqlx::query("CREATE INDEX idx_stake_slashings_ik ON stake_slashings(ik);")
76 .execute(dbtx.as_mut())
77 .await?;
78
79 sqlx::query("CREATE INDEX idx_stake_slashings_height_ik ON stake_slashings(height, ik);")
80 .execute(dbtx.as_mut())
81 .await?;
82
83 Ok(())
84 }
85
86 fn name(&self) -> String {
87 "stake/slashings".to_string()
88 }
89
90 async fn index_batch(
91 &self,
92 dbtx: &mut PgTransaction,
93 batch: EventBatch,
94 _ctx: EventBatchContext,
95 ) -> Result<(), anyhow::Error> {
96 for event in batch.events() {
97 self.index_event(dbtx, event).await?;
98 }
99 Ok(())
100 }
101}