use std::num::NonZeroUsize;
use crate::{
prelude::*,
shim::{address::Address, clock::ChainEpoch, econ::TokenAmount, state_tree::StateTree},
utils::cache::SizeTrackingCache,
};
mod macros;
mod migration_job;
pub(in crate::state_migration) mod migrators;
mod state_migration;
pub(in crate::state_migration) mod verifier;
pub(in crate::state_migration) use state_migration::StateMigration;
pub(in crate::state_migration) type Migrator<BS> = Arc<dyn ActorMigration<BS> + Send + Sync>;
#[derive(derive_more::Deref)]
pub(in crate::state_migration) struct MigrationCache(SizeTrackingCache<String, CidWrapper>);
impl MigrationCache {
pub fn new(size: NonZeroUsize) -> Self {
Self(SizeTrackingCache::new_with_metrics("migration", size))
}
pub fn get(&self, key: &str) -> Option<Cid> {
self.deref().get(key).map(From::from)
}
pub fn get_or_insert_with<F>(&self, key: &str, f: F) -> anyhow::Result<Cid>
where
F: FnOnce() -> anyhow::Result<Cid>,
{
self.deref()
.get_or_insert_with(key, || {
let cid = f()?;
Ok(cid.into())
})
.map(From::from)
}
pub fn push(&self, key: String, value: Cid) {
self.deref().insert(key, value.into());
}
}
impl ShallowClone for MigrationCache {
fn shallow_clone(&self) -> Self {
Self(self.deref().shallow_clone())
}
}
#[allow(dead_code)] pub(in crate::state_migration) struct ActorMigrationInput {
pub address: Address,
pub balance: TokenAmount,
pub head: Cid,
pub prior_epoch: ChainEpoch,
pub cache: MigrationCache,
}
pub(in crate::state_migration) struct ActorMigrationOutput {
pub new_code_cid: Cid,
pub new_head: Cid,
}
pub(in crate::state_migration) trait ActorMigration<BS: Blockstore> {
fn migrate_state(
&self,
store: &BS,
input: ActorMigrationInput,
) -> anyhow::Result<Option<ActorMigrationOutput>>;
fn is_deferred(&self) -> bool {
false
}
}
pub(in crate::state_migration) trait PostMigrator<BS: Blockstore>:
Send + Sync
{
fn post_migrate_state(&self, store: &BS, actors_out: &mut StateTree<BS>) -> anyhow::Result<()>;
}
pub(in crate::state_migration) trait PostMigrationCheck<BS: Blockstore>:
Send + Sync
{
fn post_migrate_check(&self, store: &BS, actors_out: &StateTree<BS>) -> anyhow::Result<()>;
}
pub(in crate::state_migration) type PostMigratorArc<BS> = Arc<dyn PostMigrator<BS>>;
pub(in crate::state_migration) type PostMigrationCheckArc<BS> = Arc<dyn PostMigrationCheck<BS>>;
pub(in crate::state_migration) trait TypeMigration<From, To> {
fn migrate_type(from: From, store: &impl Blockstore) -> anyhow::Result<To>;
}
pub(in crate::state_migration) struct TypeMigrator;
#[cfg(test)]
mod tests {
use super::MigrationCache;
use crate::utils::cid::CidCborExt;
use cid::Cid;
use nonzero_ext::nonzero;
#[test]
fn test_migration_cache() {
let cache = MigrationCache::new(nonzero!(10usize));
let cid = Cid::from_cbor_blake2b256(&42).unwrap();
cache.push("Cthulhu".to_owned(), cid);
assert_eq!(cache.get("Cthulhu"), Some(cid));
assert_eq!(cache.get("Ao"), None);
let cid = Cid::from_cbor_blake2b256(&666).unwrap();
assert_eq!(cache.get("Azathoth"), None);
let value = cache.get_or_insert_with("Azathoth", || Ok(cid)).unwrap();
assert_eq!(value, cid);
assert_eq!(cache.get("Azathoth"), Some(cid));
let value = cache
.get_or_insert_with("Dagon", || Ok(cache.get("Azathoth").unwrap()))
.unwrap();
assert_eq!(value, cid);
assert_eq!(cache.get("Dagon"), Some(cid));
}
}