diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md index 76436ebaac..345b75ab60 100644 --- a/COMPATIBILITY.md +++ b/COMPATIBILITY.md @@ -9,3 +9,4 @@ | 7 (Testnet 78) | v0.78.x | v0.37.5 | v1 | | 7 (Mainnet) | v0.79.x | v0.37.x | v1 | | 8 (Mainnet) | v0.80.x | v0.37.x | v1 | +| 9 (Mainnet) | v0.81.x | v0.37.x | v1 | diff --git a/crates/bin/pd/src/main.rs b/crates/bin/pd/src/main.rs index 7f2ead78ca..a622895d36 100644 --- a/crates/bin/pd/src/main.rs +++ b/crates/bin/pd/src/main.rs @@ -12,7 +12,7 @@ use cnidarium::Storage; use metrics_exporter_prometheus::PrometheusBuilder; use pd::{ cli::{NetworkCommand, Opt, RootCommand}, - migrate::Migration::{Mainnet1, ReadyToStart}, + migrate::Migration::{Mainnet2, ReadyToStart}, network::{ config::{get_network_dir, parse_tm_address, url_has_necessary_parts}, generate::NetworkConfig, @@ -472,7 +472,7 @@ async fn main() -> anyhow::Result<()> { let genesis_start = pd::migrate::last_block_timestamp(pd_home.clone()).await?; tracing::info!(?genesis_start, "last block timestamp"); - Mainnet1 + Mainnet2 .migrate(pd_home.clone(), comet_home, Some(genesis_start), force) .instrument(pd_migrate_span) .await diff --git a/crates/bin/pd/src/migrate.rs b/crates/bin/pd/src/migrate.rs index ad6e612a9e..2beaaed7e3 100644 --- a/crates/bin/pd/src/migrate.rs +++ b/crates/bin/pd/src/migrate.rs @@ -5,6 +5,7 @@ //! This module declares how local `pd` state should be altered, if at all, //! in order to be compatible with the network post-chain-upgrade. mod mainnet1; +mod mainnet2; mod reset_halt_bit; mod simple; mod testnet72; @@ -56,6 +57,9 @@ pub enum Migration { /// Mainnet-1 migration: /// - Restore IBC packet commitments for improperly handled withdrawal attempts Mainnet1, + /// Mainnet-2 migration: + /// - no-op + Mainnet2, } impl Migration { @@ -94,6 +98,9 @@ impl Migration { Migration::Mainnet1 => { mainnet1::migrate(storage, pd_home.clone(), genesis_start).await?; } + Migration::Mainnet2 => { + mainnet2::migrate(storage, pd_home.clone(), genesis_start).await?; + } // We keep historical migrations around for now, this will help inform an abstracted // design. Feel free to remove it if it's causing you trouble. _ => unimplemented!("the specified migration is unimplemented"), diff --git a/crates/bin/pd/src/migrate/mainnet2.rs b/crates/bin/pd/src/migrate/mainnet2.rs new file mode 100644 index 0000000000..32a4c4d364 --- /dev/null +++ b/crates/bin/pd/src/migrate/mainnet2.rs @@ -0,0 +1,103 @@ +//! Migration for shipping consensus-breaking IBC changes, fixing +//! how withdrawals from Penumbra to Noble are handled, and ensures that IBC +//! error messages from counterparty chains are processed. +use cnidarium::{StateDelta, Storage}; +use jmt::RootHash; +use penumbra_app::app::StateReadExt as _; +use penumbra_app::app_version::migrate_app_version; +use penumbra_governance::StateWriteExt; +use penumbra_sct::component::clock::EpochManager; +use penumbra_sct::component::clock::EpochRead; +use std::path::PathBuf; +use tracing::instrument; + +use crate::network::generate::NetworkConfig; + +/// Run the full migration, emitting a new genesis event, representing historical state. +/// +/// This will have the effect of reinserting packets which had acknowledgements containing +/// errors, and erroneously removed from state, as if the acknowledgements had contained successes. +#[instrument] +pub async fn migrate( + storage: Storage, + pd_home: PathBuf, + genesis_start: Option, +) -> anyhow::Result<()> { + // Setup: + let initial_state = storage.latest_snapshot(); + let chain_id = initial_state.get_chain_id().await?; + let root_hash = initial_state + .root_hash() + .await + .expect("chain state has a root hash"); + // We obtain the pre-upgrade hash solely to log it as a result. + let pre_upgrade_root_hash: RootHash = root_hash.into(); + let pre_upgrade_height = initial_state + .get_block_height() + .await + .expect("chain state has a block height"); + let post_upgrade_height = pre_upgrade_height.wrapping_add(1); + + let mut delta = StateDelta::new(initial_state); + let (migration_duration, post_upgrade_root_hash) = { + let start_time = std::time::SystemTime::now(); + + migrate_app_version(&mut delta, 9).await?; + + // Reset the application height and halt flag. + delta.ready_to_start(); + delta.put_block_height(0u64); + + // Finally, commit the changes to the chain state. + let post_upgrade_root_hash = storage.commit_in_place(delta).await?; + tracing::info!(?post_upgrade_root_hash, "post-migration root hash"); + + ( + start_time.elapsed().expect("start is set"), + post_upgrade_root_hash, + ) + }; + storage.release().await; + + // The migration is complete, now we need to generate a genesis file. To do this, we need + // to lookup a validator view from the chain, and specify the post-upgrade app hash and + // initial height. + let app_state = penumbra_app::genesis::Content { + chain_id, + ..Default::default() + }; + let mut genesis = NetworkConfig::make_genesis(app_state.clone()).expect("can make genesis"); + genesis.app_hash = post_upgrade_root_hash + .0 + .to_vec() + .try_into() + .expect("infallible conversion"); + + genesis.initial_height = post_upgrade_height as i64; + genesis.genesis_time = genesis_start.unwrap_or_else(|| { + let now = tendermint::time::Time::now(); + tracing::info!(%now, "no genesis time provided, detecting a testing setup"); + now + }); + let checkpoint = post_upgrade_root_hash.0.to_vec(); + let genesis = NetworkConfig::make_checkpoint(genesis, Some(checkpoint)); + let genesis_json = serde_json::to_string(&genesis).expect("can serialize genesis"); + tracing::info!("genesis: {}", genesis_json); + let genesis_path = pd_home.join("genesis.json"); + std::fs::write(genesis_path, genesis_json).expect("can write genesis"); + + let validator_state_path = pd_home.join("priv_validator_state.json"); + let fresh_validator_state = crate::network::generate::NetworkValidator::initial_state(); + std::fs::write(validator_state_path, fresh_validator_state).expect("can write validator state"); + + tracing::info!( + pre_upgrade_height, + post_upgrade_height, + ?pre_upgrade_root_hash, + ?post_upgrade_root_hash, + duration = migration_duration.as_secs(), + "successful migration!" + ); + + Ok(()) +} diff --git a/crates/core/app/src/app_version.rs b/crates/core/app/src/app_version.rs index 93ad9afc47..ec1e09e13a 100644 --- a/crates/core/app/src/app_version.rs +++ b/crates/core/app/src/app_version.rs @@ -1,6 +1,6 @@ /// Representation of the Penumbra application version. Notably, this is distinct /// from the crate version(s). This number should only ever be incremented. -pub const APP_VERSION: u64 = 8; +pub const APP_VERSION: u64 = 9; cfg_if::cfg_if! { if #[cfg(feature="component")] { diff --git a/crates/core/app/src/app_version/component.rs b/crates/core/app/src/app_version/component.rs index 2ca749dd03..281dc07d6d 100644 --- a/crates/core/app/src/app_version/component.rs +++ b/crates/core/app/src/app_version/component.rs @@ -16,6 +16,7 @@ fn version_to_software_version(version: u64) -> &'static str { 6 => "v0.77.x", 7 => "v0.79.x", 8 => "v0.80.x", + 9 => "v0.81.x", _ => "unknown", } }