forest/state_migration/nv22/
miner.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4//! This module contains the migration logic for the `NV22` upgrade for the
5//! Miner actor. While the `NV22` upgrade does not change the state of the
6//! Miner actor, it does change the state of the Market actor, which requires
7//! metadata from the Miner actor.
8//!
9//! As per [FIP-0076](https://github.com/filecoin-project/FIPs/blob/master/FIPS/fip-0076.md#backwards-compatibility)
10//! > This proposal requires a state migration to the market actor to add the new `ProviderSectors` mapping,
11//! > and to add a sector number to and remove allocation ID from each `DealState`. Computing this mapping
12//! > requires reading all sector metadata from the miner actor.
13
14use crate::state_migration::common::{ActorMigration, ActorMigrationInput, ActorMigrationOutput};
15use crate::utils::db::CborStoreExt as _;
16use ahash::HashMap;
17use cid::Cid;
18use fil_actor_miner_state::v12::State as MinerStateOld;
19use fil_actors_shared::v12::Array as ArrayOld;
20use fvm_ipld_blockstore::Blockstore;
21use fvm_shared4::ActorID;
22use fvm_shared4::clock::ChainEpoch;
23use fvm_shared4::deal::DealID;
24use fvm_shared4::sector::{SectorID, SectorNumber};
25use parking_lot::RwLock;
26use std::sync::Arc;
27
28#[derive(Default)]
29pub struct ProviderSectors {
30    pub deal_to_sector: RwLock<HashMap<DealID, SectorID>>,
31    pub miner_to_sector_to_deals: RwLock<HashMap<ActorID, HashMap<SectorNumber, Vec<DealID>>>>,
32}
33
34pub struct MinerMigrator {
35    upgrade_epoch: ChainEpoch,
36    provider_sectors: Arc<ProviderSectors>,
37    out_cid: Cid,
38}
39
40pub(in crate::state_migration) fn miner_migrator<BS: Blockstore>(
41    upgrade_epoch: ChainEpoch,
42    provider_sectors: Arc<ProviderSectors>,
43    out_cid: Cid,
44) -> anyhow::Result<Arc<dyn ActorMigration<BS> + Send + Sync>> {
45    Ok(Arc::new(MinerMigrator {
46        upgrade_epoch,
47        provider_sectors,
48        out_cid,
49    }))
50}
51
52impl<BS: Blockstore> ActorMigration<BS> for MinerMigrator {
53    fn migrate_state(
54        &self,
55        store: &BS,
56        input: ActorMigrationInput,
57    ) -> anyhow::Result<Option<ActorMigrationOutput>> {
58        let miner_id = input.address.id()?;
59        let in_state: MinerStateOld = store.get_cbor_required(&input.head)?;
60
61        let in_sectors = ArrayOld::<fil_actor_miner_state::v12::SectorOnChainInfo, BS>::load(
62            &in_state.sectors,
63            store,
64        )?;
65
66        in_sectors.for_each(|i, sector| {
67            if sector.deal_ids.is_empty() || sector.expiration < self.upgrade_epoch {
68                return Ok(());
69            }
70
71            let mut sectors = self.provider_sectors.deal_to_sector.write();
72            for deal_id in sector.deal_ids.iter() {
73                sectors.insert(
74                    *deal_id,
75                    SectorID {
76                        miner: miner_id,
77                        number: i,
78                    },
79                );
80            }
81            drop(sectors);
82
83            let mut sector_deals = self.provider_sectors.miner_to_sector_to_deals.write();
84            sector_deals
85                .entry(miner_id)
86                .or_default()
87                .insert(i, sector.deal_ids.clone());
88
89            Ok(())
90        })?;
91
92        Ok(Some(ActorMigrationOutput {
93            new_code_cid: self.out_cid,
94            new_head: input.head,
95        }))
96    }
97}