Skip to main content

forest/state_migration/common/
mod.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4//! Common code that's shared across all migration code.
5//! Each network upgrade / state migration code lives in their own module.
6
7use std::num::NonZeroUsize;
8
9use crate::{
10    prelude::*,
11    shim::{address::Address, clock::ChainEpoch, econ::TokenAmount, state_tree::StateTree},
12    utils::cache::SizeTrackingCache,
13};
14
15mod macros;
16mod migration_job;
17pub(in crate::state_migration) mod migrators;
18mod state_migration;
19pub(in crate::state_migration) mod verifier;
20
21pub(in crate::state_migration) use state_migration::StateMigration;
22pub(in crate::state_migration) type Migrator<BS> = Arc<dyn ActorMigration<BS> + Send + Sync>;
23
24/// Cache of existing CID to CID migrations for an actor.
25#[derive(derive_more::Deref)]
26pub(in crate::state_migration) struct MigrationCache(SizeTrackingCache<String, CidWrapper>);
27
28impl MigrationCache {
29    pub fn new(size: NonZeroUsize) -> Self {
30        Self(SizeTrackingCache::new_with_metrics("migration", size))
31    }
32
33    pub fn get_or_insert_with<F>(&self, key: &str, f: F) -> anyhow::Result<Cid>
34    where
35        F: FnOnce() -> anyhow::Result<Cid>,
36    {
37        self.deref()
38            .get_or_insert_with(key, || {
39                let cid = f()?;
40                Ok(cid.into())
41            })
42            .map(From::from)
43    }
44}
45
46impl ShallowClone for MigrationCache {
47    fn shallow_clone(&self) -> Self {
48        Self(self.deref().shallow_clone())
49    }
50}
51
52#[allow(dead_code)] // future migrations might need the fields.
53pub(in crate::state_migration) struct ActorMigrationInput {
54    /// Actor's address
55    pub address: Address,
56    /// Actor's balance
57    pub balance: TokenAmount,
58    /// Actor's state head CID
59    pub head: Cid,
60    /// Epoch of last state transition prior to migration
61    pub prior_epoch: ChainEpoch,
62    /// Cache of existing CID to CID migrations for this actor
63    pub cache: MigrationCache,
64}
65
66/// Output of actor migration job.
67pub(in crate::state_migration) struct ActorMigrationOutput {
68    /// New CID for the actor
69    pub new_code_cid: Cid,
70    /// New state head CID
71    pub new_head: Cid,
72}
73
74/// Trait that defines the interface for actor migration job.
75pub(in crate::state_migration) trait ActorMigration<BS: Blockstore> {
76    fn migrate_state(
77        &self,
78        store: &BS,
79        input: ActorMigrationInput,
80    ) -> anyhow::Result<Option<ActorMigrationOutput>>;
81
82    /// Some migration jobs might need to be deferred to be executed after the regular state migration.
83    /// These may require some metadata collected during other migrations.
84    fn is_deferred(&self) -> bool {
85        false
86    }
87}
88
89/// Trait that defines the interface for actor migration job to be executed after the state migration.
90pub(in crate::state_migration) trait PostMigrator<BS: Blockstore>:
91    Send + Sync
92{
93    fn post_migrate_state(&self, store: &BS, actors_out: &mut StateTree<BS>) -> anyhow::Result<()>;
94}
95
96/// Trait defining the interface for actor migration verifier.
97pub(in crate::state_migration) trait PostMigrationCheck<BS: Blockstore>:
98    Send + Sync
99{
100    fn post_migrate_check(&self, store: &BS, actors_out: &StateTree<BS>) -> anyhow::Result<()>;
101}
102
103/// Sized wrapper of [`PostMigrator`].
104pub(in crate::state_migration) type PostMigratorArc<BS> = Arc<dyn PostMigrator<BS>>;
105
106/// Sized wrapper of [`PostMigrationCheck`].
107pub(in crate::state_migration) type PostMigrationCheckArc<BS> = Arc<dyn PostMigrationCheck<BS>>;
108
109/// Trait that migrates from one data structure to another, similar to
110/// [`std::convert::TryInto`] trait but taking an extra block store parameter
111pub(in crate::state_migration) trait TypeMigration<From, To> {
112    fn migrate_type(from: From, store: &impl Blockstore) -> anyhow::Result<To>;
113}
114
115/// Type that implements [`TypeMigration`] for different type pairs. Prefer
116/// using a single `struct` so that the compiler could catch duplicate
117/// implementations
118pub(in crate::state_migration) struct TypeMigrator;
119
120#[cfg(test)]
121mod tests {
122    use super::MigrationCache;
123    use crate::utils::cid::CidCborExt;
124    use cid::Cid;
125    use nonzero_ext::nonzero;
126
127    #[test]
128    fn test_migration_cache() {
129        let cache = MigrationCache::new(nonzero!(10usize));
130        let cid = Cid::from_cbor_blake2b256(&42).unwrap();
131
132        let value = cache.get_or_insert_with("Azathoth", || Ok(cid)).unwrap();
133        assert_eq!(value, cid);
134        // A second call returns the cached value without recomputing.
135        let value = cache
136            .get_or_insert_with("Azathoth", || panic!("value should be cached"))
137            .unwrap();
138        assert_eq!(value, cid);
139
140        // Tests that there is no deadlock when inserting a value while reading
141        // (a different key from) the cache.
142        let value = cache
143            .get_or_insert_with("Dagon", || cache.get_or_insert_with("Azathoth", || Ok(cid)))
144            .unwrap();
145        assert_eq!(value, cid);
146    }
147}