use std::{num::NonZeroUsize, sync::Arc};
use crate::shim::{address::Address, clock::ChainEpoch, econ::TokenAmount, state_tree::StateTree};
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
mod macros;
mod migration_job;
pub(in crate::state_migration) mod migrators;
mod state_migration;
pub(in crate::state_migration) mod verifier;
use lru::LruCache;
use parking_lot::RwLock;
pub(in crate::state_migration) use state_migration::StateMigration;
pub(in crate::state_migration) type Migrator<BS> = Arc<dyn ActorMigration<BS> + Send + Sync>;
#[derive(Clone)]
pub(in crate::state_migration) struct MigrationCache {
cache: Arc<RwLock<LruCache<String, Cid>>>,
}
impl MigrationCache {
pub fn new(size: NonZeroUsize) -> Self {
Self {
cache: Arc::new(RwLock::new(LruCache::new(size))),
}
}
pub fn get(&self, key: &str) -> Option<Cid> {
self.cache.write().get(key).cloned()
}
pub fn get_or_insert_with<F>(&self, key: String, f: F) -> anyhow::Result<Cid>
where
F: FnOnce() -> anyhow::Result<Cid>,
{
if self.cache.read().contains(&key) {
Ok(self.cache.write().get(&key).cloned().unwrap())
} else {
let v = f()?;
self.cache.write().put(key, v);
Ok(v)
}
}
pub fn insert(&self, key: String, value: Cid) {
self.cache.write().put(key, value);
}
}
#[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 std::num::NonZeroUsize;
use super::MigrationCache;
use crate::utils::cid::CidCborExt;
use cid::Cid;
#[test]
fn test_migration_cache() {
let cache = MigrationCache::new(NonZeroUsize::new(10).unwrap());
let cid = Cid::from_cbor_blake2b256(&42).unwrap();
cache.insert("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".to_owned(), || Ok(cid))
.unwrap();
assert_eq!(value, cid);
assert_eq!(cache.get("Azathoth"), Some(cid));
let value = cache
.get_or_insert_with("Dagon".to_owned(), || Ok(cache.get("Azathoth").unwrap()))
.unwrap();
assert_eq!(value, cid);
assert_eq!(cache.get("Dagon"), Some(cid));
}
}