Skip to content

Commit

Permalink
pindexer: add a nicer error when a checkpoint genesis is used (#4891)
Browse files Browse the repository at this point in the history
This adds an informative error explaining that we need the *original*
genesis file, before any upgrades, and why.

Closes #4880

- [x] If this code contains consensus-breaking changes, I have added the
"consensus-breaking" label. Otherwise, I declare my belief that there
are not consensus-breaking changes, for the following reason:

  > indexing only
  • Loading branch information
cronokirby authored Oct 8, 2024
1 parent ea7e231 commit 3cc3234
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 23 deletions.
1 change: 1 addition & 0 deletions crates/bin/pindexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub use indexer_ext::IndexerExt;
pub mod block;
pub mod dex;
pub mod ibc;
mod parsing;
pub mod shielded_pool;
mod sql;
pub mod stake;
Expand Down
27 changes: 27 additions & 0 deletions crates/bin/pindexer/src/parsing.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
use anyhow::{anyhow, Context as _};
use penumbra_app::genesis::{AppState, Content};
use serde_json::Value;

const GENESIS_NO_CONTENT_ERROR: &'static str = r#"
Error: using an upgrade genesis file instead of an initial genesis file.
This genesis file only contains a checkpoint hash of the state,
rather than information about how the initial state of the chain was initialized,
at the very first genesis.
Make sure that you're using the very first genesis file, before any upgrades.
"#;

/// Attempt to parse content from a value.
///
/// This is useful to get the initial chain state for app views.
///
/// This has a nice error message, so you should use this.
pub fn parse_content(data: Value) -> anyhow::Result<Content> {
let app_state: AppState = serde_json::from_value(data)
.context("error decoding app_state json: make sure that this is a penumbra genesis file")?;
let content = app_state
.content()
.ok_or(anyhow!(GENESIS_NO_CONTENT_ERROR))?;
Ok(content.clone())
}
20 changes: 6 additions & 14 deletions crates/bin/pindexer/src/stake/validator_set.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::collections::BTreeMap;

use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, Result};
use cometindex::{async_trait, sqlx, AppView, ContextualizedEvent, PgPool, PgTransaction};

use penumbra_app::genesis::AppState;
use penumbra_app::genesis::Content;
use penumbra_asset::asset;
use penumbra_num::Amount;
use penumbra_proto::{core::component::stake::v1 as pb, event::ProtoEvent};
Expand All @@ -12,6 +12,8 @@ use penumbra_stake::{
IdentityKey,
};

use crate::parsing::parse_content;

#[derive(Debug)]
pub struct ValidatorSet {}

Expand Down Expand Up @@ -45,10 +47,7 @@ impl AppView for ValidatorSet {
.execute(dbtx.as_mut())
.await?;

let app_state: penumbra_app::genesis::AppState =
serde_json::from_value(app_state.clone()).context("error decoding app_state json")?;

add_genesis_validators(dbtx, &app_state).await?;
add_genesis_validators(dbtx, &parse_content(app_state.clone())?).await?;
Ok(())
}

Expand Down Expand Up @@ -147,14 +146,7 @@ impl AppView for ValidatorSet {
}
}

async fn add_genesis_validators<'a>(
dbtx: &mut PgTransaction<'a>,
app_state: &AppState,
) -> Result<()> {
let content = app_state
.content()
.ok_or_else(|| anyhow::anyhow!("cannot initialize indexer from checkpoint genesis"))?;

async fn add_genesis_validators<'a>(dbtx: &mut PgTransaction<'a>, content: &Content) -> Result<()> {
// Given a genesis validator, we need to figure out its delegations at
// genesis by getting its delegation token then summing up all the allocations.
// Build up a table of the total allocations first.
Expand Down
16 changes: 7 additions & 9 deletions crates/bin/pindexer/src/supply.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use std::collections::{BTreeMap, HashSet};

use anyhow::{anyhow, Context, Result};
use anyhow::{anyhow, Result};
use cometindex::{async_trait, sqlx, AppView, ContextualizedEvent, PgTransaction};
use penumbra_app::genesis::{AppState, Content};
use penumbra_app::genesis::Content;
use penumbra_asset::{asset, STAKING_TOKEN_ASSET_ID};
use penumbra_num::Amount;
use penumbra_proto::{
Expand All @@ -16,6 +16,8 @@ use penumbra_stake::{rate::RateData, validator::Validator, IdentityKey};
use sqlx::{PgPool, Postgres, Transaction};
use std::iter;

use crate::parsing::parse_content;

mod unstaked_supply {
//! This module handles updates around the unstaked supply.
use anyhow::Result;
Expand Down Expand Up @@ -820,7 +822,7 @@ impl<'a> TryFrom<&'a ContextualizedEvent> for Event {
/// Add the initial native token supply.
async fn add_genesis_native_token_allocation_supply<'a>(
dbtx: &mut PgTransaction<'a>,
app_state: &AppState,
content: &Content,
) -> Result<()> {
fn content_mints(content: &Content) -> BTreeMap<asset::Id, Amount> {
let community_pool_mint = iter::once((
Expand All @@ -843,9 +845,6 @@ async fn add_genesis_native_token_allocation_supply<'a>(
out
}

let content = app_state
.content()
.ok_or_else(|| anyhow::anyhow!("cannot initialized indexer from checkpoint genesis"))?;
let mints = content_mints(content);

let unstaked_mint = u64::try_from(
Expand Down Expand Up @@ -911,9 +910,8 @@ impl AppView for Component {
// decode the initial supply from the genesis
// initial app state is not recomputed from events, because events are not emitted in init_chain.
// instead, the indexer directly parses the genesis.
let app_state: penumbra_app::genesis::AppState =
serde_json::from_value(app_state.clone()).context("error decoding app_state json")?;
add_genesis_native_token_allocation_supply(dbtx, &app_state).await?;
add_genesis_native_token_allocation_supply(dbtx, &parse_content(app_state.clone())?)
.await?;

Ok(())
}
Expand Down

0 comments on commit 3cc3234

Please sign in to comment.