1use cnidarium::Snapshot;
5use cnidarium::{StateDelta, Storage};
6use ibc_types::core::channel::{Packet, PortId};
7use ibc_types::transfer::acknowledgement::TokenTransferAcknowledgement;
8use jmt::RootHash;
9use penumbra_sdk_app::app::StateReadExt as _;
10use penumbra_sdk_app::app_version::migrate_app_version;
11use penumbra_sdk_governance::StateWriteExt;
12use penumbra_sdk_ibc::{component::ChannelStateWriteExt as _, IbcRelay};
13use penumbra_sdk_sct::component::clock::EpochManager;
14use penumbra_sdk_sct::component::clock::EpochRead;
15use penumbra_sdk_transaction::{Action, Transaction};
16use std::path::PathBuf;
17use tracing::instrument;
18
19use crate::network::generate::NetworkConfig;
20
21const ICS20_TRANSFER_START_HEIGHT: u64 = 411616;
23
24fn tx_lost_transfers(transaction: Transaction) -> impl Iterator<Item = Packet> {
30 transaction
31 .transaction_body()
32 .actions
33 .into_iter()
34 .filter_map(move |action| match action {
35 Action::IbcRelay(IbcRelay::Acknowledgement(m)) => {
36 if m.packet.port_on_b != PortId::transfer() {
38 return None;
39 }
40 let transfer: TokenTransferAcknowledgement =
43 match serde_json::from_slice(m.acknowledgement.as_slice()) {
44 Err(_) => return None,
45 Ok(x) => x,
46 };
47 match transfer {
50 TokenTransferAcknowledgement::Success(_) => None,
51 TokenTransferAcknowledgement::Error(_) => Some(m.packet),
52 }
53 }
54 _ => None,
55 })
56}
57
58async fn lost_transfers(state: &StateDelta<Snapshot>) -> anyhow::Result<Vec<Packet>> {
62 let mut out = Vec::new();
63 let end_height = state.get_block_height().await?;
64 for height in ICS20_TRANSFER_START_HEIGHT..=end_height {
66 let transactions = state.transactions_by_height(height).await?.transactions;
67 for tx in transactions.into_iter() {
68 for lost in tx_lost_transfers(tx.try_into()?) {
69 out.push(lost);
70 }
71 }
72 }
73 Ok(out)
74}
75
76async fn replace_lost_packets(delta: &mut StateDelta<Snapshot>) -> anyhow::Result<()> {
78 let lost_packets = lost_transfers(delta).await?;
79 for packet in lost_packets {
80 delta.put_packet_commitment(&packet);
82 }
83 Ok(())
84}
85
86#[instrument]
91pub async fn migrate(
92 storage: Storage,
93 pd_home: PathBuf,
94 genesis_start: Option<tendermint::time::Time>,
95) -> anyhow::Result<()> {
96 let initial_state = storage.latest_snapshot();
98 let chain_id = initial_state.get_chain_id().await?;
99 let root_hash = initial_state
100 .root_hash()
101 .await
102 .expect("chain state has a root hash");
103 let pre_upgrade_root_hash: RootHash = root_hash.into();
105 let pre_upgrade_height = initial_state
106 .get_block_height()
107 .await
108 .expect("chain state has a block height");
109 let post_upgrade_height = pre_upgrade_height.wrapping_add(1);
110
111 let mut delta = StateDelta::new(initial_state);
112 let (migration_duration, post_upgrade_root_hash) = {
113 let start_time = std::time::SystemTime::now();
114
115 migrate_app_version(&mut delta, 8).await?;
124
125 replace_lost_packets(&mut delta).await?;
127
128 delta.ready_to_start();
130 delta.put_block_height(0u64);
131
132 let post_upgrade_root_hash = storage.commit_in_place(delta).await?;
134 tracing::info!(?post_upgrade_root_hash, "post-migration root hash");
135
136 (
137 start_time.elapsed().expect("start is set"),
138 post_upgrade_root_hash,
139 )
140 };
141 storage.release().await;
142
143 let app_state = penumbra_sdk_app::genesis::Content {
147 chain_id,
148 ..Default::default()
149 };
150 let mut genesis = NetworkConfig::make_genesis(app_state.clone()).expect("can make genesis");
151 genesis.app_hash = post_upgrade_root_hash
152 .0
153 .to_vec()
154 .try_into()
155 .expect("infallible conversion");
156
157 genesis.initial_height = post_upgrade_height as i64;
158 genesis.genesis_time = genesis_start.unwrap_or_else(|| {
159 let now = tendermint::time::Time::now();
160 tracing::info!(%now, "no genesis time provided, detecting a testing setup");
161 now
162 });
163 let checkpoint = post_upgrade_root_hash.0.to_vec();
164 let genesis = NetworkConfig::make_checkpoint(genesis, Some(checkpoint));
165 let genesis_json = serde_json::to_string(&genesis).expect("can serialize genesis");
166 tracing::info!("genesis: {}", genesis_json);
167 let genesis_path = pd_home.join("genesis.json");
168 std::fs::write(genesis_path, genesis_json).expect("can write genesis");
169
170 let validator_state_path = pd_home.join("priv_validator_state.json");
171 let fresh_validator_state = crate::network::generate::NetworkValidator::initial_state();
172 std::fs::write(validator_state_path, fresh_validator_state).expect("can write validator state");
173
174 tracing::info!(
175 pre_upgrade_height,
176 post_upgrade_height,
177 ?pre_upgrade_root_hash,
178 ?post_upgrade_root_hash,
179 duration = migration_duration.as_secs(),
180 "successful migration!"
181 );
182
183 Ok(())
184}