1#![allow(dead_code)]
3
4use anyhow;
5use cnidarium::{EscapedByteSlice, Snapshot, StateDelta, StateRead, StateWrite, Storage};
6use futures::StreamExt as _;
7use jmt::RootHash;
8use penumbra_sdk_app::{app::StateReadExt as _, SUBSTORE_PREFIXES};
9use penumbra_sdk_auction::{params::AuctionParameters, StateWriteExt};
10use penumbra_sdk_dex::SwapExecution;
11use penumbra_sdk_num::Amount;
12use penumbra_sdk_proto::{penumbra::core::component as pb, StateReadProto, StateWriteProto};
13use penumbra_sdk_sct::component::clock::{EpochManager, EpochRead};
14use std::path::PathBuf;
15
16use crate::network::generate::NetworkConfig;
17
18async fn write_auction_parameters(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
20 let params = AuctionParameters {};
21 delta.put_auction_params(params);
22 Ok(())
23}
24
25async fn fix_arb_execution_outputs(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
28 let mut stream = delta.prefix_proto("dex/arb_execution/");
29 while let Some(r) = stream.next().await {
30 let (key, swap_ex_proto): (String, pb::dex::v1::SwapExecution) = r?;
31 let mut swap_ex: SwapExecution = swap_ex_proto.try_into()?;
32 swap_ex.output = swap_ex
33 .input
34 .asset_id
35 .value(swap_ex.output.amount + swap_ex.input.amount);
36 delta.put(key, swap_ex);
37 }
38 Ok(())
39}
40
41async fn rewrite_base_liquidity_indices(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
47 let prefix_key = "dex/ab/".as_bytes();
48 tracing::trace!(prefix_key = ?EscapedByteSlice(&prefix_key), "updating base liquidity index values");
49 let mut liquidity_stream = delta.nonverifiable_prefix_raw(&prefix_key).boxed();
50
51 while let Some(r) = liquidity_stream.next().await {
52 let (key, raw_amount): (Vec<u8>, Vec<u8>) = r?;
53 tracing::info!(?key, raw_amount = ?EscapedByteSlice(&raw_amount), "migrating base liquidity index entry");
54
55 let amount = Amount::from_be_bytes(raw_amount.as_slice().try_into()?);
56
57 delta.nonverifiable_put(key.clone(), amount);
59 tracing::info!(
60 key = ?EscapedByteSlice(&key),
61 raw_amount = ?EscapedByteSlice(&raw_amount),
62 ?amount,
63 "updated base liquidity index"
64 );
65 }
66
67 Ok(())
68}
69
70async fn update_lp_index_order(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
77 let prefix_key = "dex/ra/".as_bytes();
78 tracing::trace!(prefix_key = ?EscapedByteSlice(&prefix_key), "updating liquidity position indices");
79 let mut liquidity_stream = delta.nonverifiable_prefix_raw(&prefix_key).boxed();
80
81 while let Some(r) = liquidity_stream.next().await {
82 let (old_key, asset_id): (Vec<u8>, Vec<u8>) = r?;
83 tracing::info!(?old_key, asset_id = ?EscapedByteSlice(&asset_id), "migrating asset liquidity");
84
85 let mut new_key = [0u8; 55];
87 new_key[0..7].copy_from_slice(b"dex/ra/");
88 new_key[7..32 + 7].copy_from_slice(&old_key[7..32 + 7]);
90 let a_from_b = Amount::from_be_bytes(old_key[32 + 7..32 + 7 + 16].try_into()?);
92 new_key[32 + 7..32 + 7 + 16].copy_from_slice(&(!a_from_b).to_be_bytes());
93
94 delta.nonverifiable_delete(old_key.clone());
96
97 delta.nonverifiable_put_raw(new_key.to_vec(), asset_id);
99 tracing::info!(
100 new_key = ?EscapedByteSlice(&new_key),
101 ?old_key,
102 "updated liquidity index"
103 );
104 }
105
106 Ok(())
107}
108
109pub async fn migrate(
122 storage: Storage,
123 path_to_export: PathBuf,
124 genesis_start: Option<tendermint::time::Time>,
125) -> anyhow::Result<()> {
126 let export_state = storage.latest_snapshot();
128 let root_hash = export_state.root_hash().await.expect("can get root hash");
129 let pre_upgrade_root_hash: RootHash = root_hash.into();
130 let pre_upgrade_height = export_state
131 .get_block_height()
132 .await
133 .expect("can get block height");
134 let post_upgrade_height = pre_upgrade_height.wrapping_add(1);
135
136 let mut delta = StateDelta::new(export_state);
139 let (migration_duration, post_upgrade_root_hash) = {
140 let start_time = std::time::SystemTime::now();
141
142 update_lp_index_order(&mut delta).await?;
144
145 fix_arb_execution_outputs(&mut delta).await?;
147
148 write_auction_parameters(&mut delta).await?;
150
151 rewrite_base_liquidity_indices(&mut delta).await?;
153
154 delta.put_block_height(0u64);
155 let post_upgrade_root_hash = storage.commit_in_place(delta).await?;
156 tracing::info!(?post_upgrade_root_hash, "post-upgrade root hash");
157
158 (
159 start_time.elapsed().expect("start time not set"),
160 post_upgrade_root_hash,
161 )
162 };
163
164 tracing::info!(?post_upgrade_root_hash, "post-upgrade root hash");
165 storage.release().await;
166
167 let rocksdb_dir = path_to_export.join("rocksdb");
168 let storage = Storage::load(rocksdb_dir, SUBSTORE_PREFIXES.to_vec()).await?;
169 let migrated_state = storage.latest_snapshot();
170 storage.release().await;
171
172 let chain_id = migrated_state.get_chain_id().await?;
176 let app_state = penumbra_sdk_app::genesis::Content {
177 chain_id,
178 ..Default::default()
179 };
180 let mut genesis = NetworkConfig::make_genesis(app_state.clone()).expect("can make genesis");
181 genesis.app_hash = post_upgrade_root_hash
182 .0
183 .to_vec()
184 .try_into()
185 .expect("infaillible conversion");
186 genesis.initial_height = post_upgrade_height as i64;
187 genesis.genesis_time = genesis_start.unwrap_or_else(|| {
188 let now = tendermint::time::Time::now();
189 tracing::info!(%now, "no genesis time provided, detecting a testing setup");
190 now
191 });
192 let checkpoint = post_upgrade_root_hash.0.to_vec();
193 let genesis = NetworkConfig::make_checkpoint(genesis, Some(checkpoint));
194
195 let genesis_json = serde_json::to_string(&genesis).expect("can serialize genesis");
196 tracing::info!("genesis: {}", genesis_json);
197 let genesis_path = path_to_export.join("genesis.json");
198 std::fs::write(genesis_path, genesis_json).expect("can write genesis");
199
200 let validator_state_path = path_to_export.join("priv_validator_state.json");
201 let fresh_validator_state = crate::network::generate::NetworkValidator::initial_state();
202 std::fs::write(validator_state_path, fresh_validator_state).expect("can write validator state");
203
204 tracing::info!(
205 pre_upgrade_height,
206 post_upgrade_height,
207 ?pre_upgrade_root_hash,
208 ?post_upgrade_root_hash,
209 duration = migration_duration.as_secs(),
210 "successful migration!"
211 );
212
213 Ok(())
214}