Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pindexer: add a nicer error when a checkpoint genesis is used #4891

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading