pd/migrate/
testnet76.rs

1//! Contains functions related to the migration script of Testnet74
2#![allow(dead_code)]
3
4use anyhow;
5use cnidarium::{Snapshot, StateDelta, Storage};
6use futures::TryStreamExt;
7use jmt::RootHash;
8use pbjson_types::Any;
9use penumbra_sdk_app::app::StateReadExt as _;
10use penumbra_sdk_asset::Balance;
11use penumbra_sdk_auction::auction::dutch::DutchAuction;
12use penumbra_sdk_governance::StateWriteExt;
13use penumbra_sdk_proto::{DomainType, StateReadProto, StateWriteProto};
14use penumbra_sdk_sct::component::clock::{EpochManager, EpochRead};
15use penumbra_sdk_shielded_pool::params::ShieldedPoolParameters;
16use penumbra_sdk_shielded_pool::{component::StateWriteExt as _, fmd::Parameters as FmdParameters};
17use std::path::PathBuf;
18use tracing::instrument;
19
20use crate::network::generate::NetworkConfig;
21
22#[instrument(skip_all)]
23/// Reconstruct a correct tally of the auction component's VCB balance.
24/// This is achieved by:
25/// 1. Iterating over all auctions in the chain state.
26/// 2. Summing the input and output reserves of each auction.
27///    NB: This is sufficient because auctions with deployed LPs have value that is
28/// //     *outside* of the auction component, and recorded in the DEX VCB instead.
29/// 3. Writing the aggregate VCB balance for each asset to the chain state.
30async fn heal_auction_vcb(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
31    let key_prefix_auctions = penumbra_sdk_auction::state_key::auction_store::prefix();
32    let all_auctions = delta
33        .prefix_proto::<Any>(&key_prefix_auctions)
34        .map_ok(|(_, v)| DutchAuction::decode(v.value).expect("only dutch auctions"))
35        .try_collect::<Vec<DutchAuction>>()
36        .await?;
37
38    let total_vcb = all_auctions
39        .into_iter()
40        .filter(|auction| auction.state.sequence <= 1)
41        .fold(Balance::zero(), |acc, auction| {
42            let input_reserves = penumbra_sdk_asset::Value {
43                asset_id: auction.description.input.asset_id,
44                amount: auction.state.input_reserves,
45            };
46
47            let output_reserves = penumbra_sdk_asset::Value {
48                asset_id: auction.description.output_id,
49                amount: auction.state.output_reserves,
50            };
51
52            tracing::debug!(id = ?auction.description.id(), ?input_reserves, ?output_reserves, "aggregating auction into the component's VCB balance");
53
54            acc + Balance::from(input_reserves) + Balance::from(output_reserves)
55        });
56
57    for value in total_vcb.provided() {
58        tracing::debug!(?value, "writing aggregate VCB balance for asset");
59        let key_vcb_balance =
60            penumbra_sdk_auction::state_key::value_balance::for_asset(&value.asset_id);
61        delta.put(key_vcb_balance, value.amount);
62    }
63
64    Ok(())
65}
66
67async fn write_shielded_pool_params(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
68    delta.put_shielded_pool_params(ShieldedPoolParameters::default());
69    Ok(())
70}
71
72async fn write_fmd_params(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
73    delta.put_previous_fmd_parameters(FmdParameters::default());
74    delta.put_current_fmd_parameters(FmdParameters::default());
75    Ok(())
76}
77
78/// Run the full migration, given an export path and a start time for genesis.
79///
80/// Menu:
81/// - Reconstruct a correct VCB balance for the auction component.
82#[instrument]
83pub async fn migrate(
84    storage: Storage,
85    pd_home: PathBuf,
86    genesis_start: Option<tendermint::time::Time>,
87) -> anyhow::Result<()> {
88    // Setup:
89    let initial_state = storage.latest_snapshot();
90    let chain_id = initial_state.get_chain_id().await?;
91    let root_hash = initial_state
92        .root_hash()
93        .await
94        .expect("chain state has a root hash");
95    let pre_upgrade_root_hash: RootHash = root_hash.into();
96    let pre_upgrade_height = initial_state
97        .get_block_height()
98        .await
99        .expect("chain state has a block height");
100    let post_upgrade_height = pre_upgrade_height.wrapping_add(1);
101
102    // We initialize a `StateDelta` and start by reaching into the JMT for all entries matching the
103    // swap execution prefix. Then, we write each entry to the nv-storage.
104    let mut delta = StateDelta::new(initial_state);
105    let (migration_duration, post_upgrade_root_hash) = {
106        let start_time = std::time::SystemTime::now();
107
108        // Set shield pool params to the new default
109        write_shielded_pool_params(&mut delta).await?;
110        // Initialize fmd params
111        write_fmd_params(&mut delta).await?;
112        // Reconstruct a VCB balance for the auction component.
113        heal_auction_vcb(&mut delta).await?;
114
115        delta.ready_to_start();
116        delta.put_block_height(0u64);
117        let post_upgrade_root_hash = storage.commit_in_place(delta).await?;
118        tracing::info!(?post_upgrade_root_hash, "post-migration root hash");
119
120        (
121            start_time.elapsed().expect("start time not set"),
122            post_upgrade_root_hash,
123        )
124    };
125    storage.release().await;
126
127    // The migration is complete, now we need to generate a genesis file. To do this, we need
128    // to lookup a validator view from the chain, and specify the post-upgrade app hash and
129    // initial height.
130    let app_state = penumbra_sdk_app::genesis::Content {
131        chain_id,
132        ..Default::default()
133    };
134    let mut genesis = NetworkConfig::make_genesis(app_state.clone()).expect("can make genesis");
135    genesis.app_hash = post_upgrade_root_hash
136        .0
137        .to_vec()
138        .try_into()
139        .expect("infaillible conversion");
140    genesis.initial_height = post_upgrade_height as i64;
141    genesis.genesis_time = genesis_start.unwrap_or_else(|| {
142        let now = tendermint::time::Time::now();
143        tracing::info!(%now, "no genesis time provided, detecting a testing setup");
144        now
145    });
146    let checkpoint = post_upgrade_root_hash.0.to_vec();
147    let genesis = NetworkConfig::make_checkpoint(genesis, Some(checkpoint));
148
149    let genesis_json = serde_json::to_string(&genesis).expect("can serialize genesis");
150    tracing::info!("genesis: {}", genesis_json);
151    let genesis_path = pd_home.join("genesis.json");
152    std::fs::write(genesis_path, genesis_json).expect("can write genesis");
153
154    let validator_state_path = pd_home.join("priv_validator_state.json");
155
156    let fresh_validator_state = crate::network::generate::NetworkValidator::initial_state();
157    std::fs::write(validator_state_path, fresh_validator_state).expect("can write validator state");
158
159    tracing::info!(
160        pre_upgrade_height,
161        post_upgrade_height,
162        ?pre_upgrade_root_hash,
163        ?post_upgrade_root_hash,
164        duration = migration_duration.as_secs(),
165        "successful migration!"
166    );
167
168    Ok(())
169}