forest/state_migration/common/
mod.rs1use std::{num::NonZeroUsize, sync::Arc};
8
9use crate::{
10 shim::{address::Address, clock::ChainEpoch, econ::TokenAmount, state_tree::StateTree},
11 utils::{cache::SizeTrackingLruCache, get_size::CidWrapper},
12};
13use cid::Cid;
14use fvm_ipld_blockstore::Blockstore;
15
16mod macros;
17mod migration_job;
18pub(in crate::state_migration) mod migrators;
19mod state_migration;
20pub(in crate::state_migration) mod verifier;
21
22pub(in crate::state_migration) use state_migration::StateMigration;
23pub(in crate::state_migration) type Migrator<BS> = Arc<dyn ActorMigration<BS> + Send + Sync>;
24
25#[derive(Clone)]
27pub(in crate::state_migration) struct MigrationCache {
28 cache: Arc<SizeTrackingLruCache<String, CidWrapper>>,
29}
30
31impl MigrationCache {
32 pub fn new(size: NonZeroUsize) -> Self {
33 Self {
34 cache: Arc::new(SizeTrackingLruCache::new_with_metrics(
35 "migration".into(),
36 size,
37 )),
38 }
39 }
40
41 pub fn get(&self, key: &str) -> Option<Cid> {
42 self.cache.get_cloned(key).map(From::from)
43 }
44
45 pub fn get_or_insert_with<F>(&self, key: String, f: F) -> anyhow::Result<Cid>
46 where
47 F: FnOnce() -> anyhow::Result<Cid>,
48 {
49 if let Some(v) = self.cache.get_cloned(&key) {
50 Ok(v.into())
51 } else {
52 let v = f()?;
53 self.push(key, v);
54 Ok(v)
55 }
56 }
57
58 pub fn push(&self, key: String, value: Cid) {
59 self.cache.push(key, value.into());
60 }
61}
62
63#[allow(dead_code)] pub(in crate::state_migration) struct ActorMigrationInput {
65 pub address: Address,
67 pub balance: TokenAmount,
69 pub head: Cid,
71 pub prior_epoch: ChainEpoch,
73 pub cache: MigrationCache,
75}
76
77pub(in crate::state_migration) struct ActorMigrationOutput {
79 pub new_code_cid: Cid,
81 pub new_head: Cid,
83}
84
85pub(in crate::state_migration) trait ActorMigration<BS: Blockstore> {
87 fn migrate_state(
88 &self,
89 store: &BS,
90 input: ActorMigrationInput,
91 ) -> anyhow::Result<Option<ActorMigrationOutput>>;
92
93 fn is_deferred(&self) -> bool {
96 false
97 }
98}
99
100pub(in crate::state_migration) trait PostMigrator<BS: Blockstore>:
102 Send + Sync
103{
104 fn post_migrate_state(&self, store: &BS, actors_out: &mut StateTree<BS>) -> anyhow::Result<()>;
105}
106
107pub(in crate::state_migration) trait PostMigrationCheck<BS: Blockstore>:
109 Send + Sync
110{
111 fn post_migrate_check(&self, store: &BS, actors_out: &StateTree<BS>) -> anyhow::Result<()>;
112}
113
114pub(in crate::state_migration) type PostMigratorArc<BS> = Arc<dyn PostMigrator<BS>>;
116
117pub(in crate::state_migration) type PostMigrationCheckArc<BS> = Arc<dyn PostMigrationCheck<BS>>;
119
120pub(in crate::state_migration) trait TypeMigration<From, To> {
123 fn migrate_type(from: From, store: &impl Blockstore) -> anyhow::Result<To>;
124}
125
126pub(in crate::state_migration) struct TypeMigrator;
130
131#[cfg(test)]
132mod tests {
133 use std::num::NonZeroUsize;
134
135 use super::MigrationCache;
136 use crate::utils::cid::CidCborExt;
137 use cid::Cid;
138
139 #[test]
140 fn test_migration_cache() {
141 let cache = MigrationCache::new(NonZeroUsize::new(10).unwrap());
142 let cid = Cid::from_cbor_blake2b256(&42).unwrap();
143 cache.push("Cthulhu".to_owned(), cid);
144 assert_eq!(cache.get("Cthulhu"), Some(cid));
145 assert_eq!(cache.get("Ao"), None);
146
147 let cid = Cid::from_cbor_blake2b256(&666).unwrap();
148 assert_eq!(cache.get("Azathoth"), None);
149
150 let value = cache
151 .get_or_insert_with("Azathoth".to_owned(), || Ok(cid))
152 .unwrap();
153 assert_eq!(value, cid);
154 assert_eq!(cache.get("Azathoth"), Some(cid));
155
156 let value = cache
158 .get_or_insert_with("Dagon".to_owned(), || Ok(cache.get("Azathoth").unwrap()))
159 .unwrap();
160 assert_eq!(value, cid);
161 assert_eq!(cache.get("Dagon"), Some(cid));
162 }
163}