1#![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)]
23async 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#[instrument]
83pub async fn migrate(
84 storage: Storage,
85 pd_home: PathBuf,
86 genesis_start: Option<tendermint::time::Time>,
87) -> anyhow::Result<()> {
88 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 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 write_shielded_pool_params(&mut delta).await?;
110 write_fmd_params(&mut delta).await?;
112 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 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}