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 type_migrations;
32
33type RunMigration<DB> = fn(&ChainConfig, &Arc<DB>, &Cid, ChainEpoch) -> anyhow::Result<Cid>;
34
35pub fn get_migrations<DB>(chain: &NetworkChain) -> Vec<(Height, RunMigration<DB>)>
36where
37 DB: Blockstore + Send + Sync,
38{
39 match chain {
40 NetworkChain::Mainnet => {
41 vec![
42 (Height::Shark, nv17::run_migration::<DB>),
43 (Height::Hygge, nv18::run_migration::<DB>),
44 (Height::Lightning, nv19::run_migration::<DB>),
45 (Height::Watermelon, nv21::run_migration::<DB>),
46 (Height::Dragon, nv22::run_migration::<DB>),
47 (Height::Waffle, nv23::run_migration::<DB>),
48 (Height::TukTuk, nv24::run_migration::<DB>),
49 (Height::Teep, nv25::run_migration::<DB>),
50 (Height::GoldenWeek, nv27::run_migration::<DB>),
51 ]
52 }
53 NetworkChain::Calibnet => {
54 vec![
55 (Height::Shark, nv17::run_migration::<DB>),
56 (Height::Hygge, nv18::run_migration::<DB>),
57 (Height::Lightning, nv19::run_migration::<DB>),
58 (Height::Watermelon, nv21::run_migration::<DB>),
59 (Height::WatermelonFix, nv21fix::run_migration::<DB>),
60 (Height::WatermelonFix2, nv21fix2::run_migration::<DB>),
61 (Height::Dragon, nv22::run_migration::<DB>),
62 (Height::DragonFix, nv22fix::run_migration::<DB>),
63 (Height::Waffle, nv23::run_migration::<DB>),
64 (Height::TukTuk, nv24::run_migration::<DB>),
65 (Height::Teep, nv25::run_migration::<DB>),
66 (Height::TockFix, nv26fix::run_migration::<DB>),
67 (Height::GoldenWeek, nv27::run_migration::<DB>),
68 ]
69 }
70 NetworkChain::Butterflynet => {
71 vec![
72 (Height::Teep, nv25::run_migration::<DB>),
73 (Height::GoldenWeek, nv27::run_migration::<DB>),
74 ]
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 ]
88 }
89 }
90}
91
92pub fn run_state_migrations<DB>(
94 epoch: ChainEpoch,
95 chain_config: &ChainConfig,
96 db: &Arc<DB>,
97 parent_state: &Cid,
98) -> anyhow::Result<Option<Cid>>
99where
100 DB: Blockstore + Send + Sync,
101{
102 let db_write_buffer = match std::env::var("FOREST_STATE_MIGRATION_DB_WRITE_BUFFER") {
104 Ok(v) => v.parse().ok(),
105 _ => None,
106 }
107 .unwrap_or(10000);
108 let mappings = get_migrations(&chain_config.network);
109
110 static BUNDLE_CHECKED: AtomicBool = AtomicBool::new(false);
112 if !BUNDLE_CHECKED.load(atomic::Ordering::Relaxed) {
113 BUNDLE_CHECKED.store(true, atomic::Ordering::Relaxed);
114 for (info_height, info) in chain_config.height_infos.iter() {
115 for (height, _) in &mappings {
116 if height == info_height {
117 assert!(
118 info.bundle.is_some(),
119 "Actor bundle info for height {height} needs to be defined in `src/networks/mod.rs` to run state migration"
120 );
121 break;
122 }
123 }
124 }
125 }
126
127 for (height, migrate) in mappings {
128 if epoch == chain_config.epoch(height) {
129 tracing::info!("Running {height} migration at epoch {epoch}");
130 let start_time = std::time::Instant::now();
131 let db = Arc::new(BlockstoreWithWriteBuffer::new_with_capacity(
132 db.clone(),
133 db_write_buffer,
134 ));
135 let new_state = migrate(chain_config, &db, parent_state, epoch)?;
136 let elapsed = start_time.elapsed();
137 let new_state_actors = db
139 .get_cbor::<StateRoot>(&new_state)
140 .ok()
141 .flatten()
142 .map(|sr| format!("{}", sr.actors))
143 .unwrap_or_default();
144 if new_state != *parent_state {
145 crate::utils::misc::reveal_upgrade_logo(height.into());
146 tracing::info!(
147 "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}.",
148 elapsed = humantime::format_duration(elapsed)
149 );
150 } else {
151 anyhow::bail!(
152 "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}.",
153 elapsed = humantime::format_duration(elapsed)
154 );
155 }
156
157 return Ok(Some(new_state));
158 }
159 }
160
161 Ok(None)
162}
163
164#[cfg(test)]
165mod tests;