1use std::sync::{
5 Arc,
6 atomic::{self, AtomicBool},
7};
8
9use crate::db::BlockstoreWithWriteBuffer;
10use crate::networks::{ChainConfig, Height, NetworkChain};
11use crate::shim::clock::ChainEpoch;
12use crate::shim::state_tree::StateRoot;
13use cid::Cid;
14use fvm_ipld_blockstore::Blockstore;
15use fvm_ipld_encoding::CborStore;
16
17pub(in crate::state_migration) mod common;
18mod nv17;
19mod nv18;
20mod nv19;
21mod nv21;
22mod nv21fix;
23mod nv21fix2;
24mod nv22;
25mod nv22fix;
26mod nv23;
27mod nv24;
28mod nv25;
29mod nv26fix;
30mod nv27;
31mod nv28;
32mod type_migrations;
33
34type RunMigration<DB> = fn(&ChainConfig, &Arc<DB>, &Cid, ChainEpoch) -> anyhow::Result<Cid>;
35
36pub fn get_migrations<DB>(chain: &NetworkChain) -> Vec<(Height, RunMigration<DB>)>
37where
38 DB: Blockstore + Send + Sync,
39{
40 match chain {
41 NetworkChain::Mainnet => {
42 vec![
43 (Height::Shark, nv17::run_migration::<DB>),
44 (Height::Hygge, nv18::run_migration::<DB>),
45 (Height::Lightning, nv19::run_migration::<DB>),
46 (Height::Watermelon, nv21::run_migration::<DB>),
47 (Height::Dragon, nv22::run_migration::<DB>),
48 (Height::Waffle, nv23::run_migration::<DB>),
49 (Height::TukTuk, nv24::run_migration::<DB>),
50 (Height::Teep, nv25::run_migration::<DB>),
51 (Height::GoldenWeek, nv27::run_migration::<DB>),
52 (Height::FireHorse, nv28::run_migration::<DB>),
53 ]
54 }
55 NetworkChain::Calibnet => {
56 vec![
57 (Height::Shark, nv17::run_migration::<DB>),
58 (Height::Hygge, nv18::run_migration::<DB>),
59 (Height::Lightning, nv19::run_migration::<DB>),
60 (Height::Watermelon, nv21::run_migration::<DB>),
61 (Height::WatermelonFix, nv21fix::run_migration::<DB>),
62 (Height::WatermelonFix2, nv21fix2::run_migration::<DB>),
63 (Height::Dragon, nv22::run_migration::<DB>),
64 (Height::DragonFix, nv22fix::run_migration::<DB>),
65 (Height::Waffle, nv23::run_migration::<DB>),
66 (Height::TukTuk, nv24::run_migration::<DB>),
67 (Height::Teep, nv25::run_migration::<DB>),
68 (Height::TockFix, nv26fix::run_migration::<DB>),
69 (Height::GoldenWeek, nv27::run_migration::<DB>),
70 (Height::FireHorse, nv28::run_migration::<DB>),
71 ]
72 }
73 NetworkChain::Butterflynet => {
74 vec![(Height::FireHorse, nv28::run_migration::<DB>)]
75 }
76 NetworkChain::Devnet(_) => {
77 vec![
78 (Height::Shark, nv17::run_migration::<DB>),
79 (Height::Hygge, nv18::run_migration::<DB>),
80 (Height::Lightning, nv19::run_migration::<DB>),
81 (Height::Watermelon, nv21::run_migration::<DB>),
82 (Height::Dragon, nv22::run_migration::<DB>),
83 (Height::Waffle, nv23::run_migration::<DB>),
84 (Height::TukTuk, nv24::run_migration::<DB>),
85 (Height::Teep, nv25::run_migration::<DB>),
86 (Height::GoldenWeek, nv27::run_migration::<DB>),
87 (Height::FireHorse, nv28::run_migration::<DB>),
88 ]
89 }
90 }
91}
92
93pub fn run_state_migrations<DB>(
95 epoch: ChainEpoch,
96 chain_config: &ChainConfig,
97 db: &Arc<DB>,
98 parent_state: &Cid,
99) -> anyhow::Result<Option<Cid>>
100where
101 DB: Blockstore + Send + Sync,
102{
103 let db_write_buffer = match std::env::var("FOREST_STATE_MIGRATION_DB_WRITE_BUFFER") {
105 Ok(v) => v.parse().ok(),
106 _ => None,
107 }
108 .unwrap_or(10000);
109 let mappings = get_migrations(&chain_config.network);
110
111 static BUNDLE_CHECKED: AtomicBool = AtomicBool::new(false);
113 if !BUNDLE_CHECKED.load(atomic::Ordering::Relaxed) {
114 BUNDLE_CHECKED.store(true, atomic::Ordering::Relaxed);
115 for (info_height, info) in chain_config.height_infos.iter() {
116 for (height, _) in &mappings {
117 if height == info_height {
118 assert!(
119 info.bundle.is_some(),
120 "Actor bundle info for height {height} needs to be defined in `src/networks/mod.rs` to run state migration"
121 );
122 break;
123 }
124 }
125 }
126 }
127
128 for (height, migrate) in mappings {
129 if epoch == chain_config.epoch(height) {
130 tracing::info!("Running {height} migration at epoch {epoch}");
131 let start_time = std::time::Instant::now();
132 let db = Arc::new(BlockstoreWithWriteBuffer::new_with_capacity(
133 db.clone(),
134 db_write_buffer,
135 ));
136 let new_state = migrate(chain_config, &db, parent_state, epoch)?;
137 let elapsed = start_time.elapsed();
138 let new_state_actors = db
140 .get_cbor::<StateRoot>(&new_state)
141 .ok()
142 .flatten()
143 .map(|sr| format!("{}", sr.actors))
144 .unwrap_or_default();
145 if new_state != *parent_state {
146 crate::utils::misc::reveal_upgrade_logo(height.into());
147 tracing::info!(
148 "State migration at height {height}(epoch {epoch}) was successful, Previous state: {parent_state}, new state: {new_state}, new state actors: {new_state_actors}. Took: {elapsed}.",
149 elapsed = humantime::format_duration(elapsed)
150 );
151 } else {
152 anyhow::bail!(
153 "State post migration at height {height} must not match. Previous state: {parent_state}, new state: {new_state}, new state actors: {new_state_actors}. Took {elapsed}.",
154 elapsed = humantime::format_duration(elapsed)
155 );
156 }
157
158 return Ok(Some(new_state));
159 }
160 }
161
162 Ok(None)
163}
164
165#[cfg(test)]
166mod tests;