|
| 1 | +// Copyright 2019-2025 ChainSafe Systems |
| 2 | +// SPDX-License-Identifier: Apache-2.0, MIT |
| 3 | + |
| 4 | +use std::path::{Path, PathBuf}; |
| 5 | +use std::sync::Arc; |
| 6 | +use std::time::Instant; |
| 7 | + |
| 8 | +use cid::Cid; |
| 9 | +use clap::Args; |
| 10 | +use itertools::Itertools; |
| 11 | + |
| 12 | +use crate::utils::db::CborStoreExt; |
| 13 | +use crate::{ |
| 14 | + blocks::CachingBlockHeader, |
| 15 | + cli_shared::{chain_path, read_config}, |
| 16 | + daemon::db_util::load_all_forest_cars, |
| 17 | + db::{ |
| 18 | + car::ManyCar, |
| 19 | + db_engine::{db_root, open_db}, |
| 20 | + parity_db::ParityDb, |
| 21 | + CAR_DB_DIR_NAME, |
| 22 | + }, |
| 23 | + networks::{ChainConfig, NetworkChain}, |
| 24 | + shim::version::NetworkVersion, |
| 25 | +}; |
| 26 | + |
| 27 | +#[derive(Debug, Args)] |
| 28 | +pub struct MigrateStateCommand { |
| 29 | + /// Target network version |
| 30 | + network_version: NetworkVersion, |
| 31 | + /// Block to look back from |
| 32 | + block_to_look_back: Cid, |
| 33 | + /// Path to the Forest database folder |
| 34 | + #[arg(long)] |
| 35 | + db: Option<PathBuf>, |
| 36 | + /// Filecoin network chain |
| 37 | + #[arg(long, required = true)] |
| 38 | + chain: NetworkChain, |
| 39 | +} |
| 40 | + |
| 41 | +impl MigrateStateCommand { |
| 42 | + pub async fn run(self, _: crate::rpc::Client) -> anyhow::Result<()> { |
| 43 | + let Self { |
| 44 | + network_version, |
| 45 | + block_to_look_back, |
| 46 | + db, |
| 47 | + chain, |
| 48 | + } = self; |
| 49 | + let db = { |
| 50 | + let db = if let Some(db) = db { |
| 51 | + db |
| 52 | + } else { |
| 53 | + let (_, config) = read_config(None, Some(chain.clone()))?; |
| 54 | + db_root(&chain_path(&config))? |
| 55 | + }; |
| 56 | + load_db(&db)? |
| 57 | + }; |
| 58 | + let block: CachingBlockHeader = db.get_cbor_required(&block_to_look_back)?; |
| 59 | + let chain_config = Arc::new(ChainConfig::from_chain(&chain)); |
| 60 | + let mut state_root = block.state_root; |
| 61 | + let epoch = block.epoch - 1; |
| 62 | + let migrations = crate::state_migration::get_migrations(&chain) |
| 63 | + .into_iter() |
| 64 | + .filter(|(h, _)| { |
| 65 | + let nv: NetworkVersion = (*h).into(); |
| 66 | + network_version == nv |
| 67 | + }) |
| 68 | + .collect_vec(); |
| 69 | + anyhow::ensure!( |
| 70 | + !migrations.is_empty(), |
| 71 | + "No migration found for network version {network_version} on {chain}" |
| 72 | + ); |
| 73 | + for (_, migrate) in migrations { |
| 74 | + println!("Migrating... state_root: {state_root}, epoch: {epoch}"); |
| 75 | + let start = Instant::now(); |
| 76 | + let new_state = migrate(&chain_config, &db, &state_root, epoch)?; |
| 77 | + println!( |
| 78 | + "Done. old_state: {state_root}, new_state: {new_state}, took: {}", |
| 79 | + humantime::format_duration(start.elapsed()) |
| 80 | + ); |
| 81 | + state_root = new_state; |
| 82 | + } |
| 83 | + Ok(()) |
| 84 | + } |
| 85 | +} |
| 86 | + |
| 87 | +pub(super) fn load_db(db_root: &Path) -> anyhow::Result<Arc<ManyCar<ParityDb>>> { |
| 88 | + let db_writer = open_db(db_root.into(), Default::default())?; |
| 89 | + let db = ManyCar::new(db_writer); |
| 90 | + let forest_car_db_dir = db_root.join(CAR_DB_DIR_NAME); |
| 91 | + load_all_forest_cars(&db, &forest_car_db_dir)?; |
| 92 | + Ok(Arc::new(db)) |
| 93 | +} |
0 commit comments