use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time::Instant;
use cid::Cid;
use clap::Args;
use itertools::Itertools;
use crate::db::{BlockstoreWithWriteBuffer, Either};
use crate::utils::db::CborStoreExt;
use crate::{
blocks::CachingBlockHeader,
cli_shared::{chain_path, read_config},
daemon::db_util::load_all_forest_cars,
db::{
CAR_DB_DIR_NAME,
car::ManyCar,
db_engine::{db_root, open_db},
parity_db::ParityDb,
},
networks::{ChainConfig, NetworkChain},
shim::version::NetworkVersion,
};
#[derive(Debug, Args)]
pub struct MigrateStateCommand {
network_version: NetworkVersion,
block_to_look_back: Cid,
#[arg(long)]
db: Option<PathBuf>,
#[arg(long, required = true)]
chain: NetworkChain,
#[arg(long, default_value_t = 10000)]
db_write_buffer: usize,
}
impl MigrateStateCommand {
pub async fn run(self, _: crate::rpc::Client) -> anyhow::Result<()> {
let Self {
network_version,
block_to_look_back,
db,
chain,
db_write_buffer,
} = self;
let db = {
let db = if let Some(db) = db {
db
} else {
let (_, config) = read_config(None, Some(chain.clone()))?;
db_root(&chain_path(&config))?
};
let db = load_db(&db)?;
Arc::new(if db_write_buffer > 0 {
Either::Left(BlockstoreWithWriteBuffer::new_with_capacity(
db,
db_write_buffer,
))
} else {
Either::Right(db)
})
};
let block: CachingBlockHeader = db.get_cbor_required(&block_to_look_back)?;
let chain_config = Arc::new(ChainConfig::from_chain(&chain));
let mut state_root = block.state_root;
let epoch = block.epoch - 1;
let migrations = crate::state_migration::get_migrations(&chain)
.into_iter()
.filter(|(h, _)| {
let nv: NetworkVersion = (*h).into();
network_version == nv
})
.collect_vec();
anyhow::ensure!(
!migrations.is_empty(),
"No migration found for network version {network_version} on {chain}"
);
for (_, migrate) in migrations {
println!("Migrating... state_root: {state_root}, epoch: {epoch}");
let start = Instant::now();
let new_state = migrate(&chain_config, &db, &state_root, epoch)?;
println!(
"Done. old_state: {state_root}, new_state: {new_state}, took: {}",
humantime::format_duration(start.elapsed())
);
state_root = new_state;
}
Ok(())
}
}
pub(super) fn load_db(db_root: &Path) -> anyhow::Result<Arc<ManyCar<ParityDb>>> {
let db_writer = open_db(db_root.into(), &Default::default())?;
let db = ManyCar::new(db_writer);
let forest_car_db_dir = db_root.join(CAR_DB_DIR_NAME);
load_all_forest_cars(&db, &forest_car_db_dir)?;
Ok(Arc::new(db))
}