forest/state_migration/nv17/
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 `NV17` upgrade for the miner
5//! actor.
6
7use std::sync::Arc;
8
9use crate::networks::NetworkChain;
10use crate::shim::{address::Address, piece::PieceInfo};
11use crate::utils::db::CborStoreExt;
12use crate::{make_calibnet_policy, make_mainnet_policy};
13use ahash::HashMap;
14use anyhow::Context as _;
15use cid::{Cid, multibase::Base};
16use fil_actor_miner_state::{
17    v8::State as MinerStateOld,
18    v9::{State as MinerStateNew, util::sector_key},
19};
20use fil_actors_shared::abi::commp::compute_unsealed_sector_cid_v2;
21use fil_actors_shared::fvm_ipld_amt;
22use fvm_ipld_blockstore::Blockstore;
23
24use super::super::common::{
25    ActorMigration, ActorMigrationInput, ActorMigrationOutput, TypeMigration, TypeMigrator,
26};
27
28pub struct MinerMigrator {
29    chain: NetworkChain,
30    out_code: Cid,
31    market_proposals: Cid,
32    empty_deadline_v8_cid: Cid,
33    empty_deadlines_v8_cid: Cid,
34    empty_deadline_v9_cid: Cid,
35    empty_deadlines_v9_cid: Cid,
36}
37
38pub(super) fn miner_migrator<BS>(
39    out_code: Cid,
40    store: &Arc<BS>,
41    market_proposals: Cid,
42    chain: NetworkChain,
43) -> anyhow::Result<Arc<dyn ActorMigration<BS> + Send + Sync>>
44where
45    BS: Blockstore + Send + Sync,
46{
47    let empty_deadline_v8: fil_actor_miner_state::v8::Deadline =
48        fil_actor_miner_state::v8::Deadline::new(store)?;
49    let empty_deadline_v8_cid = store.put_cbor_default(&empty_deadline_v8)?;
50
51    let policy = match chain {
52        NetworkChain::Mainnet => make_mainnet_policy!(v8),
53        NetworkChain::Calibnet => make_calibnet_policy!(v8),
54        NetworkChain::Devnet(_) => unimplemented!("Policy::devnet"),
55        NetworkChain::Butterflynet => unimplemented!("Policy::butterflynet"),
56    };
57    let empty_deadlines_v8 =
58        fil_actor_miner_state::v8::Deadlines::new(&policy, empty_deadline_v8_cid);
59    let empty_deadlines_v8_cid = store.put_cbor_default(&empty_deadlines_v8)?;
60
61    let empty_deadline_v9 = fil_actor_miner_state::v9::Deadline::new(store)?;
62    let empty_deadline_v9_cid = store.put_cbor_default(&empty_deadline_v9)?;
63
64    let policy = match chain {
65        NetworkChain::Mainnet => make_mainnet_policy!(v9),
66        NetworkChain::Calibnet => make_calibnet_policy!(v9),
67        NetworkChain::Devnet(_) => unimplemented!("Policy::devnet"),
68        NetworkChain::Butterflynet => unimplemented!("Policy::butterflynet"),
69    };
70    let empty_deadlines_v9 =
71        fil_actor_miner_state::v9::Deadlines::new(&policy, empty_deadline_v9_cid);
72    let empty_deadlines_v9_cid = store.put_cbor_default(&empty_deadlines_v9)?;
73
74    Ok(Arc::new(MinerMigrator {
75        chain,
76        out_code,
77        market_proposals,
78        empty_deadline_v8_cid,
79        empty_deadlines_v8_cid,
80        empty_deadline_v9_cid,
81        empty_deadlines_v9_cid,
82    }))
83}
84
85impl<BS> ActorMigration<BS> for MinerMigrator
86where
87    BS: Blockstore + Send + Sync,
88{
89    fn migrate_state(
90        &self,
91        store: &BS,
92        input: ActorMigrationInput,
93    ) -> anyhow::Result<Option<ActorMigrationOutput>> {
94        let mut cache: HashMap<String, Cid> = Default::default();
95
96        let in_state: MinerStateOld = store.get_cbor_required(&input.head)?;
97        let new_pre_committed_sectors =
98            self.migrate_pre_committed_sectors(&store, &in_state.pre_committed_sectors)?;
99        let new_sectors =
100            self.migrate_sectors_with_cache(&mut cache, &store, &input.address, &in_state.sectors)?;
101        let new_deadlines = self.migrate_deadlines(&mut cache, &store, &in_state.deadlines)?;
102
103        let mut out_state: MinerStateNew = TypeMigrator::migrate_type(in_state, &store)?;
104        out_state.pre_committed_sectors = new_pre_committed_sectors;
105        out_state.sectors = new_sectors;
106        out_state.deadlines = new_deadlines;
107
108        let new_head = store.put_cbor_default(&out_state)?;
109
110        Ok(Some(ActorMigrationOutput {
111            new_code_cid: self.out_code,
112            new_head,
113        }))
114    }
115}
116
117impl MinerMigrator {
118    fn migrate_pre_committed_sectors(
119        &self,
120        store: &impl Blockstore,
121        old_pre_committed_sectors: &Cid,
122    ) -> anyhow::Result<Cid> {
123        use fil_actors_shared::v9::builtin::HAMT_BIT_WIDTH;
124
125        // Because of lifetime limitation, this is not stored as a field of `MinerMigrator` like in Go code
126        let market_proposals = fil_actors_shared::v8::Array::<
127            fil_actor_market_state::v8::DealProposal,
128            _,
129        >::load(&self.market_proposals, store)?;
130
131        let old_precommit_on_chain_infos =
132            fil_actors_shared::v8::make_map_with_root_and_bitwidth::<
133                _,
134                fil_actor_miner_state::v8::SectorPreCommitOnChainInfo,
135            >(old_pre_committed_sectors, store, HAMT_BIT_WIDTH)?;
136
137        let mut new_precommit_on_chain_infos = fil_actors_shared::v9::make_empty_map::<
138            _,
139            fil_actor_miner_state::v9::SectorPreCommitOnChainInfo,
140        >(store, HAMT_BIT_WIDTH);
141
142        old_precommit_on_chain_infos.for_each(|_key, value| {
143            let mut pieces = vec![];
144            for &deal_id in &value.info.deal_ids {
145                let deal = market_proposals.get(deal_id)?;
146                // Continue on not found to match Go logic
147                //
148                // Possible for the proposal to be missing if it's expired (but the deal is still in a precommit that's yet to be cleaned up)
149                // Just continue in this case, the sector is unProveCommitable anyway, will just fail later
150                if let Some(deal) = deal {
151                    pieces.push(PieceInfo::new(deal.piece_cid,deal.piece_size.into()).into());
152                }
153            }
154
155            let unsealed_cid = if !pieces.is_empty() {
156                Some(compute_unsealed_sector_cid_v2(
157                    value.info.seal_proof,
158                    pieces.as_slice(),
159                )?)
160            } else{
161                None
162            };
163
164            let mut sector_precommit_onchain_info:fil_actor_miner_state::v9::SectorPreCommitOnChainInfo = TypeMigrator::migrate_type(value.clone(), store)?;
165            sector_precommit_onchain_info.info.unsealed_cid = fil_actor_miner_state::v9::CompactCommD(unsealed_cid);
166            new_precommit_on_chain_infos.set(sector_key(value.info.sector_number)?, sector_precommit_onchain_info)?;
167            Ok(())
168        })?;
169
170        Ok(new_precommit_on_chain_infos.flush()?)
171    }
172
173    fn migrate_sectors_with_cache(
174        &self,
175        cache: &mut HashMap<String, Cid>,
176        store: &impl Blockstore,
177        miner_address: &Address,
178        in_root: &Cid,
179    ) -> anyhow::Result<Cid> {
180        let key = sectors_amt_key(in_root)?;
181
182        if let Some(v) = cache.get(&key) {
183            Ok(*v)
184        } else {
185            let in_array = fil_actors_shared::v8::Array::<
186                fil_actor_miner_state::v8::SectorOnChainInfo,
187                _,
188            >::load(in_root, store)?;
189
190            let prev_in_root = cache.get(&miner_prev_sectors_in_key(miner_address));
191            let prev_out_root = cache.get(&miner_prev_sectors_out_key(miner_address));
192
193            let mut out_array = if let Some(prev_in_root) = prev_in_root {
194                if let Some(prev_out_root) = prev_out_root {
195                    // we have previous work, but the AMT has changed -- diff them
196                    let prev_in_sectors = fvm_ipld_amt::Amt::<
197                        fil_actor_miner_state::v8::SectorOnChainInfo,
198                        _,
199                    >::load(prev_in_root, store)?;
200                    let in_sectors = fvm_ipld_amt::Amt::<
201                        fil_actor_miner_state::v8::SectorOnChainInfo,
202                        _,
203                    >::load(in_root, store)?;
204                    let changes = fvm_ipld_amt::diff(&prev_in_sectors, &in_sectors)?;
205                    let mut prev_out_sectors = fil_actors_shared::v9::Array::<
206                        fil_actor_miner_state::v9::SectorOnChainInfo,
207                        _,
208                    >::load(prev_out_root, store)?;
209                    for change in changes {
210                        use fvm_ipld_amt::ChangeType;
211                        match &change.change_type() {
212                            ChangeType::Remove => {
213                                prev_out_sectors.delete(change.key)?;
214                            }
215                            ChangeType::Modify | ChangeType::Add => {
216                                let info_v8 = in_sectors
217                                    .get(change.key)?
218                                    .context("Failed to get info from in_sectors")?;
219                                prev_out_sectors.set(
220                                    change.key,
221                                    TypeMigrator::migrate_type(info_v8.clone(), store)?,
222                                )?;
223                            }
224                        };
225                    }
226                    prev_out_sectors
227                } else {
228                    migrate_from_scratch(store, &in_array)?
229                }
230            } else {
231                migrate_from_scratch(store, &in_array)?
232            };
233
234            let out_root = out_array.flush()?;
235            cache.insert(miner_prev_sectors_in_key(miner_address), *in_root);
236            cache.insert(miner_prev_sectors_out_key(miner_address), out_root);
237
238            cache.insert(key, out_root);
239
240            Ok(out_root)
241        }
242    }
243
244    fn migrate_deadlines(
245        &self,
246        cache: &mut HashMap<String, Cid>,
247        store: &impl Blockstore,
248        deadlines: &Cid,
249    ) -> anyhow::Result<Cid> {
250        if deadlines == &self.empty_deadlines_v8_cid {
251            Ok(self.empty_deadlines_v9_cid)
252        } else {
253            let in_deadlines: fil_actor_miner_state::v8::Deadlines =
254                store.get_cbor_required(deadlines)?;
255
256            let policy = match &self.chain {
257                NetworkChain::Mainnet => make_mainnet_policy!(v9),
258                NetworkChain::Calibnet => make_calibnet_policy!(v9),
259                NetworkChain::Devnet(_) => unimplemented!("Policy::devnet"),
260                NetworkChain::Butterflynet => unimplemented!("Policy::butterflynet"),
261            };
262            let mut out_deadlines =
263                fil_actor_miner_state::v9::Deadlines::new(&policy, self.empty_deadline_v9_cid);
264            for (i, c) in in_deadlines.due.iter().enumerate() {
265                if c == &self.empty_deadline_v8_cid {
266                    if let Some(due_i) = out_deadlines.due.get_mut(i) {
267                        *due_i = *c;
268                    } else {
269                        out_deadlines.due.push(*c);
270                    }
271                } else {
272                    let in_deadline: fil_actor_miner_state::v8::Deadline =
273                        store.get_cbor_required(c)?;
274
275                    let out_sectors_snapshot_cid_cache_key =
276                        sectors_amt_key(&in_deadline.sectors_snapshot)?;
277                    let out_sectors_snapshot_cid =
278                        match cache.get(&out_sectors_snapshot_cid_cache_key) {
279                            Some(v) => *v,
280                            None => {
281                                let in_sectors_snapshot = fil_actors_shared::v8::Array::load(
282                                    &in_deadline.sectors_snapshot,
283                                    store,
284                                )?;
285                                let mut out_sectors_snapshot =
286                                    migrate_from_scratch(store, &in_sectors_snapshot)?;
287                                let out_sectors_snapshot_cid = out_sectors_snapshot.flush()?;
288                                cache.insert(
289                                    out_sectors_snapshot_cid_cache_key,
290                                    out_sectors_snapshot_cid,
291                                );
292                                out_sectors_snapshot_cid
293                            }
294                        };
295
296                    let out_deadline = fil_actor_miner_state::v9::Deadline {
297                        partitions: in_deadline.partitions,
298                        expirations_epochs: in_deadline.expirations_epochs,
299                        partitions_posted: in_deadline.partitions_posted,
300                        early_terminations: in_deadline.early_terminations,
301                        live_sectors: in_deadline.live_sectors,
302                        total_sectors: in_deadline.total_sectors,
303                        faulty_power: TypeMigrator::migrate_type(in_deadline.faulty_power, store)?,
304                        optimistic_post_submissions: in_deadline.optimistic_post_submissions,
305                        sectors_snapshot: out_sectors_snapshot_cid,
306                        partitions_snapshot: in_deadline.partitions_snapshot,
307                        optimistic_post_submissions_snapshot: in_deadline
308                            .optimistic_post_submissions_snapshot,
309                    };
310
311                    let out_deadline_cid = store.put_cbor_default(&out_deadline)?;
312
313                    if let Some(due_i) = out_deadlines.due.get_mut(i) {
314                        *due_i = out_deadline_cid;
315                    } else {
316                        out_deadlines.due.push(out_deadline_cid);
317                    }
318                }
319            }
320
321            store.put_cbor_default(&out_deadlines)
322        }
323    }
324}
325
326fn migrate_from_scratch<'bs, BS: Blockstore>(
327    store: &'bs BS,
328    in_array: &fil_actors_shared::v8::Array<fil_actor_miner_state::v8::SectorOnChainInfo, BS>,
329) -> anyhow::Result<
330    fil_actors_shared::v9::Array<'bs, fil_actor_miner_state::v9::SectorOnChainInfo, BS>,
331> {
332    use fil_actor_miner_state::v9::SECTORS_AMT_BITWIDTH;
333
334    let mut out_array = fil_actors_shared::v9::Array::<
335        fil_actor_miner_state::v9::SectorOnChainInfo,
336        _,
337    >::new_with_bit_width(store, SECTORS_AMT_BITWIDTH);
338
339    in_array.for_each(|key, info_v8| {
340        out_array.set(key, TypeMigrator::migrate_type(info_v8.clone(), store)?)?;
341        Ok(())
342    })?;
343
344    Ok(out_array)
345}
346
347fn miner_prev_sectors_in_key(addr: &Address) -> String {
348    format!("prev_sectors_in_{addr}")
349}
350
351fn miner_prev_sectors_out_key(addr: &Address) -> String {
352    format!("prev_sectors_out_{addr}")
353}
354
355fn sectors_amt_key(cid: &Cid) -> anyhow::Result<String> {
356    Ok(format!(
357        "sectors_amt_{}",
358        cid.to_string_of_base(Base::Base32Lower)?,
359    ))
360}
361
362#[cfg(test)]
363mod tests {
364    use super::*;
365    use crate::networks::{ChainConfig, Height};
366    use crate::shim::actors::BURNT_FUNDS_ACTOR_ADDR;
367    use crate::shim::actors::*;
368    use crate::shim::bigint::BigInt;
369    use crate::shim::{
370        econ::TokenAmount,
371        machine::{BuiltinActor, BuiltinActorManifest},
372        state_tree::{ActorState, StateRoot, StateTree, StateTreeVersion},
373    };
374    use crate::utils::multihash::prelude::*;
375    use fil_actors_shared::fvm_ipld_hamt::BytesKey;
376    use fvm_ipld_encoding::IPLD_RAW;
377    use fvm_shared2::{
378        bigint::Zero,
379        commcid::{
380            FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED, POSEIDON_BLS12_381_A1_FC1,
381            SHA2_256_TRUNC254_PADDED,
382        },
383        piece::PaddedPieceSize,
384    };
385    use multihash_codetable::Multihash;
386
387    #[test]
388    fn test_nv17_miner_migration() {
389        let store = Arc::new(crate::db::MemoryDB::default());
390        let (mut state_tree_old, manifest_old) = make_input_tree(&store);
391        let system_actor_old = state_tree_old
392            .get_actor(&system::ADDRESS.into())
393            .unwrap()
394            .unwrap();
395        let system_state_old: fil_actor_system_state::v9::State =
396            store.get_cbor_required(&system_actor_old.state).unwrap();
397        let manifest_data_cid_old = system_state_old.builtin_actors;
398        assert_eq!(manifest_data_cid_old, manifest_old.source_cid());
399        assert_eq!(
400            manifest_data_cid_old.to_string(),
401            "bafy2bzaceb7wfqkjc5c3ccjyhaf7zuhkvbzpvhnb35feaettztoharc7zdndc"
402        );
403
404        let base_addr_id = 10000;
405        let base_addr = Address::new_id(base_addr_id);
406        let base_worker_addr = Address::new_id(base_addr_id + 100);
407
408        // create 3 deal proposals
409        let mut market_actor_old = state_tree_old
410            .get_actor(&market::ADDRESS.into())
411            .unwrap()
412            .unwrap();
413        let mut market_state_old: fil_actor_market_state::v8::State =
414            store.get_cbor_required(&market_actor_old.state).unwrap();
415        let mut proposals = fil_actors_shared::v8::Array::<
416            fil_actor_market_state::v8::DealProposal,
417            _,
418        >::load(&market_state_old.proposals, &store)
419        .unwrap();
420        let base_deal = fil_actor_market_state::v8::DealProposal {
421            piece_cid: Default::default(),
422            piece_size: PaddedPieceSize(512),
423            verified_deal: true,
424            client: base_addr.into(),
425            provider: base_addr.into(),
426            label: fil_actor_market_state::v8::Label::String("".into()),
427            start_epoch: 0,
428            end_epoch: 0,
429            storage_price_per_epoch: Zero::zero(),
430            provider_collateral: Zero::zero(),
431            client_collateral: Zero::zero(),
432        };
433        let deal0 = {
434            let mut deal = base_deal.clone();
435            deal.piece_cid = make_piece_cid("0".as_bytes());
436            assert_eq!(
437                deal.piece_cid.to_string(),
438                "baga6ea4seaqf73hlm374q3zy3fjhq3dnnfwhtqw3yi452turwrtstvz2e75vp2i"
439            );
440            deal
441        };
442        let deal1 = {
443            let mut deal = base_deal.clone();
444            deal.piece_cid = make_piece_cid("1".as_bytes());
445            assert_eq!(
446                deal.piece_cid.to_string(),
447                "baga6ea4seaqgxbvsop7tj7hbtvvyatx7li7vor5nutvkely5jhab4uw5w6dvwsy"
448            );
449            deal
450        };
451        let deal2 = {
452            let mut deal = base_deal;
453            deal.piece_cid = make_piece_cid("2".as_bytes());
454            assert_eq!(
455                deal.piece_cid.to_string(),
456                "baga6ea4seaqni426hitf4fxo4a7vs4mltnoqgam4a7mlnri7sdnduzto5qj2wni"
457            );
458            deal
459        };
460
461        let mut pending_proposals =
462            fil_actors_shared::v8::Set::from_root(&store, &market_state_old.pending_proposals)
463                .unwrap();
464
465        proposals.set(0, deal0).unwrap();
466        pending_proposals
467            .put(BytesKey(deal1.cid().unwrap().to_bytes()))
468            .unwrap();
469        proposals.set(1, deal1).unwrap();
470        pending_proposals
471            .put(BytesKey(deal2.cid().unwrap().to_bytes()))
472            .unwrap();
473        proposals.set(2, deal2).unwrap();
474
475        market_state_old.proposals = proposals.flush().unwrap();
476        assert_eq!(
477            market_state_old.proposals.to_string(),
478            "bafy2bzacecskt5brcfawiowjlv5lwnskkks2xzsmsnhkmjixndqlxuyb3abxs"
479        );
480        market_state_old.pending_proposals = pending_proposals.root().unwrap();
481
482        let market_state_cid_old = store.put_cbor_default(&market_state_old).unwrap();
483        market_actor_old.state = market_state_cid_old;
484        state_tree_old
485            .set_actor(&market::ADDRESS.into(), market_actor_old)
486            .unwrap();
487
488        // base stuff to create miners
489        let miner_cid_old = manifest_old.get(BuiltinActor::Miner).unwrap();
490        assert_eq!(
491            miner_cid_old.to_string(),
492            "bafkqaetgnfwc6obpon2g64tbm5sw22lomvza"
493        );
494        let base_miner_state = make_base_miner_state(&store, &base_addr, &base_worker_addr);
495
496        let base_precommit = fil_actor_miner_state::v8::SectorPreCommitOnChainInfo {
497            pre_commit_deposit: Zero::zero(),
498            pre_commit_epoch: 0,
499            deal_weight: Zero::zero(),
500            verified_deal_weight: Zero::zero(),
501            info: fil_actor_miner_state::v8::SectorPreCommitInfo {
502                seal_proof: fvm_shared2::sector::RegisteredSealProof::StackedDRG32GiBV1P1,
503                sealed_cid: make_sealed_cid("100".as_bytes()),
504                ..Default::default()
505            },
506        };
507        assert_eq!(
508            base_precommit.info.sealed_cid.to_string(),
509            "bagboea4b5abcblkxgzugketokvsj5szdvyourcdvislw57venjeowxmfu3xljuyg"
510        );
511
512        // make 3 miners
513        // miner1 has no precommits at all
514        // miner2 has 4 precommits, but with no deals
515        // miner3 has 3 precommits, with deals [0], [1,2], and []
516
517        // miner1 has no precommits at all
518        let miner1_state_cid = store.put_cbor_default(&base_miner_state).unwrap();
519        assert_eq!(
520            miner1_state_cid.to_string(),
521            "bafy2bzaceaqtktd7f5b2gutreh3b2czp2mkqu4ilyuu7mjcpwrk75g5nl6w6k"
522        );
523
524        let miner1 = ActorState::new(miner_cid_old, miner1_state_cid, Zero::zero(), 0, None);
525        let addr1 = Address::new_id(base_addr_id + 1);
526        state_tree_old.set_actor(&addr1, miner1).unwrap();
527
528        // miner2 has precommits, but they have no deals
529        let mut precommits2 = fil_actors_shared::v8::make_map_with_root::<
530            _,
531            fil_actor_miner_state::v8::SectorPreCommitOnChainInfo,
532        >(&base_miner_state.pre_committed_sectors, &store)
533        .unwrap();
534        precommits2
535            .set(sector_key(0).unwrap(), base_precommit.clone())
536            .unwrap();
537        precommits2
538            .set(sector_key(1).unwrap(), base_precommit.clone())
539            .unwrap();
540        precommits2
541            .set(sector_key(2).unwrap(), base_precommit.clone())
542            .unwrap();
543        precommits2
544            .set(sector_key(3).unwrap(), base_precommit.clone())
545            .unwrap();
546
547        let precommit2_cid = precommits2.flush().unwrap();
548        assert_eq!(
549            precommit2_cid.to_string(),
550            "bafy2bzacedogkdulyeaujgdsiqzo323s5dpz44efwihxsuekkkpo4znpl3g2s"
551        );
552
553        let mut miner2_state = base_miner_state.clone();
554        miner2_state.pre_committed_sectors = precommit2_cid;
555        let miner2_state_cid = store.put_cbor_default(&miner2_state).unwrap();
556        assert_eq!(
557            miner2_state_cid.to_string(),
558            "bafy2bzacedad6xkymehkuoij4rhg2inzqnfin3er52znw53lddn5364usp2bi"
559        );
560
561        let miner2 = ActorState::new(miner_cid_old, miner2_state_cid, Zero::zero(), 0, None);
562        let addr2 = Address::new_id(base_addr_id + 2);
563        state_tree_old.set_actor(&addr2, miner2).unwrap();
564
565        // miner 3 has precommits, some of which have deals
566        let mut precommits3 = fil_actors_shared::v8::make_map_with_root::<
567            _,
568            fil_actor_miner_state::v8::SectorPreCommitOnChainInfo,
569        >(&base_miner_state.pre_committed_sectors, &store)
570        .unwrap();
571        let mut precommits3dot0 = base_precommit.clone();
572        precommits3dot0.info.deal_ids = vec![0];
573        precommits3dot0.info.sector_number = 0;
574
575        let mut precommits3dot1 = base_precommit.clone();
576        precommits3dot1.info.deal_ids = vec![1, 2];
577        precommits3dot1.info.sector_number = 1;
578
579        let mut precommits3dot2 = base_precommit;
580        precommits3dot2.info.sector_number = 2;
581
582        precommits3
583            .set(sector_key(0).unwrap(), precommits3dot0)
584            .unwrap();
585        precommits3
586            .set(sector_key(1).unwrap(), precommits3dot1)
587            .unwrap();
588        precommits3
589            .set(sector_key(2).unwrap(), precommits3dot2)
590            .unwrap();
591
592        let precommits3_cid = precommits3.flush().unwrap();
593        assert_eq!(
594            precommits3_cid.to_string(),
595            "bafy2bzacecdpddgu5sxniq5iez3xapyxvi3dg7pc5oxthywuclvxyj4h2vweo"
596        );
597
598        let mut miner3_state = base_miner_state.clone();
599        miner3_state.pre_committed_sectors = precommits3_cid;
600        let miner3_state_cid = store.put_cbor_default(&miner3_state).unwrap();
601        assert_eq!(
602            miner3_state_cid.to_string(),
603            "bafy2bzaceb7ojujla7jb6iaxeuk4etg2kui4gtjujwqadqkc7lkp4ugoqrh2m"
604        );
605
606        let miner3 = ActorState::new(miner_cid_old, miner3_state_cid, Zero::zero(), 0, None);
607        let addr3 = Address::new_id(base_addr_id + 3);
608        state_tree_old.set_actor(&addr3, miner3).unwrap();
609
610        let tree_root = state_tree_old.flush().unwrap();
611
612        let (new_manifest_cid, _new_manifest) = make_test_manifest(&store, "fil/9/");
613
614        let mut chain_config = ChainConfig::calibnet();
615        if let Some(entry) = chain_config.height_infos.get_mut(&Height::Shark) {
616            entry.bundle = Some(new_manifest_cid);
617        }
618        let new_state_cid =
619            super::super::run_migration(&chain_config, &store, &tree_root, 200).unwrap();
620        let actors_out_state_root: StateRoot = store.get_cbor_required(&new_state_cid).unwrap();
621        assert_eq!(
622            actors_out_state_root.actors.to_string(),
623            "bafy2bzacedgtk3lnnyfxnzc32etqaj3zvi7ar7nxq2jtxd2qr36ftbsjoycqu"
624        );
625        let new_state_cid2 =
626            super::super::run_migration(&chain_config, &store, &tree_root, 200).unwrap();
627        assert_eq!(new_state_cid, new_state_cid2);
628    }
629
630    #[test]
631    fn test_fip0029_miner_migration() {
632        let store = Arc::new(crate::db::MemoryDB::default());
633        let (mut state_tree_old, manifest_old) = make_input_tree(&store);
634        let addr = Address::new_id(10000);
635        let worker_addr = Address::new_id(20000);
636        let miner_cid_old = manifest_old.get(BuiltinActor::Miner).unwrap();
637        let miner_state = make_base_miner_state(&store, &addr, &worker_addr);
638        let miner_state_cid = store.put_cbor_default(&miner_state).unwrap();
639        assert_eq!(
640            miner_state_cid.to_string(),
641            "bafy2bzaceacitm72b4zks7ivplygpmyqa6aas2pxkv4rkiknluxiko5mn4ng6"
642        );
643        let miner_actor = ActorState::new(miner_cid_old, miner_state_cid, Zero::zero(), 0, None);
644        state_tree_old.set_actor(&addr, miner_actor).unwrap();
645        let state_tree_old_root = state_tree_old.flush().unwrap();
646        let (new_manifest_cid, _new_manifest) = make_test_manifest(&store, "fil/9/");
647        let mut chain_config = ChainConfig::calibnet();
648        if let Some(entry) = chain_config.height_infos.get_mut(&Height::Shark) {
649            entry.bundle = Some(new_manifest_cid);
650        }
651        let new_state_cid =
652            super::super::run_migration(&chain_config, &store, &state_tree_old_root, 200).unwrap();
653        let actors_out_state_root: StateRoot = store.get_cbor_required(&new_state_cid).unwrap();
654        assert_eq!(
655            actors_out_state_root.actors.to_string(),
656            "bafy2bzacebdpnjjyspbyj7al7d6234kdhkmdygkfdkp6zyao5o3egsfmribty"
657        );
658    }
659
660    fn make_input_tree<BS: Blockstore>(store: &Arc<BS>) -> (StateTree<BS>, BuiltinActorManifest) {
661        let mut tree = StateTree::new(store.clone(), StateTreeVersion::V4).unwrap();
662
663        let (_manifest_cid, manifest) = make_test_manifest(&store, "fil/8/");
664        let account_cid = manifest.get(BuiltinActor::Account).unwrap();
665        // fmt.Printf("accountCid: %s\n", accountCid)
666        assert_eq!(account_cid.to_string(), "bafkqadlgnfwc6obpmfrwg33vnz2a");
667        let system_cid = manifest.get_system();
668        // fmt.Printf("systemCid: %s\n", systemCid)
669        assert_eq!(system_cid.to_string(), "bafkqaddgnfwc6obpon4xg5dfnu");
670        let system_state = fil_actor_system_state::v9::State {
671            builtin_actors: manifest.source_cid(),
672        };
673        let system_state_cid = store.put_cbor_default(&system_state).unwrap();
674        assert_eq!(
675            system_state_cid.to_string(),
676            "bafy2bzacebrujchvrqxwiml3aaud4ts7kgj74kkf7qewwmrsj5tvhneeamtlq"
677        );
678        init_actor(
679            &mut tree,
680            system_state_cid,
681            system_cid,
682            &system::ADDRESS.into(),
683            Zero::zero(),
684        );
685
686        let init_cid = manifest.get_init();
687        // fmt.Printf("initCid: %s\n", initCid)
688        assert_eq!(init_cid.to_string(), "bafkqactgnfwc6obpnfxgs5a");
689        let init_state =
690            fil_actor_init_state::v8::State::new(&store, "migrationtest".into()).unwrap();
691        let init_state_cid = store.put_cbor_default(&init_state).unwrap();
692        assert_eq!(
693            init_state_cid.to_string(),
694            "bafy2bzacednf3o3eyjwkm2isixe5lezt6klncgz5axriewegbkw34r6pqszbe"
695        );
696        init_actor(
697            &mut tree,
698            init_state_cid,
699            init_cid,
700            &init::ADDRESS.into(),
701            Zero::zero(),
702        );
703
704        let reward_cid = manifest.get(BuiltinActor::Reward).unwrap();
705        assert_eq!(reward_cid.to_string(), "bafkqaddgnfwc6obpojsxoylsmq");
706        let reward_state = fil_actor_reward_state::v8::State::new(Default::default());
707        let reward_state_cid = store.put_cbor_default(&reward_state).unwrap();
708        assert_eq!(
709            reward_state_cid.to_string(),
710            "bafy2bzaceaslbmsgmgmfi6pn2osvqcfuqinauuyt67zifnefurhpk4zxd2fos"
711        );
712        init_actor(
713            &mut tree,
714            reward_state_cid,
715            reward_cid,
716            &reward::ADDRESS.into(),
717            TokenAmount::from_whole(1_100_000_000),
718        );
719
720        let cron_cid = manifest.get(BuiltinActor::Cron).unwrap();
721        assert_eq!(cron_cid.to_string(), "bafkqactgnfwc6obpmnzg63q");
722        let cron_state = fil_actor_cron_state::v8::State {
723            entries: vec![
724                fil_actor_cron_state::v8::Entry {
725                    receiver: crate::shim::actors::power::ADDRESS,
726                    method_num: crate::shim::actors::power::Method::OnEpochTickEnd as u64,
727                },
728                fil_actor_cron_state::v8::Entry {
729                    receiver: crate::shim::actors::market::ADDRESS,
730                    method_num: crate::shim::actors::market::Method::CronTick as u64,
731                },
732            ],
733        };
734        let cron_state_cid = store.put_cbor_default(&cron_state).unwrap();
735        assert_eq!(
736            cron_state_cid.to_string(),
737            "bafy2bzacebs5dwwxmsjmzvoqcamx3qtl2x5qpqgpqxgnzl7scccmbvd37ulvs"
738        );
739        init_actor(
740            &mut tree,
741            cron_state_cid,
742            cron_cid,
743            &cron::ADDRESS.into(),
744            Zero::zero(),
745        );
746
747        let power_cid = manifest.get(BuiltinActor::Power).unwrap();
748        assert_eq!(
749            power_cid.to_string(),
750            "bafkqaetgnfwc6obpon2g64tbm5sxa33xmvza"
751        );
752        let power_state = fil_actor_power_state::v8::State::new(&store).unwrap();
753        let power_state_cid = store.put_cbor_default(&power_state).unwrap();
754        assert_eq!(
755            power_state_cid.to_string(),
756            "bafy2bzacebx3h3ib435qrzwb7zj7enrgepqeiyyeqpq6zwygasoag4m3mhy3w"
757        );
758        init_actor(
759            &mut tree,
760            power_state_cid,
761            power_cid,
762            &power::ADDRESS.into(),
763            Zero::zero(),
764        );
765
766        let market_cid = manifest.get(BuiltinActor::Market).unwrap();
767        assert_eq!(
768            market_cid.to_string(),
769            "bafkqae3gnfwc6obpon2g64tbm5sw2ylsnnsxi"
770        );
771        let market_state = fil_actor_market_state::v8::State::new(&store).unwrap();
772        let market_state_cid = store.put_cbor_default(&market_state).unwrap();
773        assert_eq!(
774            market_state_cid.to_string(),
775            "bafy2bzacea5udmevoj4io3yqy7ku7aitblugdvirbirg7wstzstb5xub5empc"
776        );
777        init_actor(
778            &mut tree,
779            market_state_cid,
780            market_cid,
781            &market::ADDRESS.into(),
782            Zero::zero(),
783        );
784
785        // this will need to be replaced with the address of a multisig actor for the verified registry to be tested accurately
786        let verifreg_root = Address::new_id(80);
787        let account_state = fil_actor_account_state::v8::State {
788            address: verifreg_root.into(),
789        };
790        let account_state_cid = store.put_cbor_default(&account_state).unwrap();
791        assert_eq!(
792            account_state_cid.to_string(),
793            "bafy2bzaceajm42pledpxusdh4owdrdfvv463dthqg24npeeaz4jlbgzdcgkve"
794        );
795        init_actor(
796            &mut tree,
797            account_state_cid,
798            account_cid,
799            &account_state.address.into(),
800            Zero::zero(),
801        );
802
803        let verifreg_cid = manifest.get(BuiltinActor::VerifiedRegistry).unwrap();
804        assert_eq!(
805            verifreg_cid.to_string(),
806            "bafkqaftgnfwc6obpozsxe2lgnfswi4tfm5uxg5dspe"
807        );
808        let mut verifreg_state =
809            fil_actor_verifreg_state::v8::State::new(&store, verifreg_root.into()).unwrap();
810        let mut verified_clients = fil_actors_shared::v8::make_empty_map::<BS, BigInt>(
811            store,
812            fil_actors_shared::v8::builtin::HAMT_BIT_WIDTH,
813        );
814        // verified_clients is not set in the original go tests
815        //
816        // ```go
817        // verifiedClients, _ := adt8.MakeEmptyMap(store, 5)
818        // tmpAddr, _ := address.NewIDAddress(1001)
819        // tmpAddrKey := abi.AddrKey(tmpAddr)
820        // one := big.NewInt(1)
821        // _ = verifiedClients.Put(tmpAddrKey, &one)
822        // tmpAddr, _ = address.NewIDAddress(1002)
823        // two := big.NewInt(2)
824        // _ = verifiedClients.Put(abi.AddrKey(tmpAddr), &two)
825        // verifiedClientsCID, _ := verifiedClients.Root()
826        // vrState.VerifiedClients = verifiedClientsCID
827        // ```
828        {
829            verified_clients
830                .set(
831                    BytesKey(Address::new_id(1001).to_bytes()),
832                    num_bigint::BigInt::from(1).into(),
833                )
834                .unwrap();
835            verified_clients
836                .set(
837                    BytesKey(Address::new_id(1002).to_bytes()),
838                    num_bigint::BigInt::from(2).into(),
839                )
840                .unwrap();
841            verifreg_state.verified_clients = verified_clients.flush().unwrap();
842        }
843        let verifreg_state_cid = store.put_cbor_default(&verifreg_state).unwrap();
844        init_actor(
845            &mut tree,
846            verifreg_state_cid,
847            verifreg_cid,
848            &fil_actors_shared::v8::builtin::VERIFIED_REGISTRY_ACTOR_ADDR.into(),
849            Zero::zero(),
850        );
851
852        // burnt funds
853        let account_state = fil_actor_account_state::v8::State {
854            address: BURNT_FUNDS_ACTOR_ADDR,
855        };
856        let account_state_cid = store.put_cbor_default(&account_state).unwrap();
857        assert_eq!(
858            account_state_cid.to_string(),
859            "bafy2bzacedpuk5ggwoq3s2wixsyjjnexpsjstdlyntio76vs2lt2jvy3o6mau"
860        );
861        init_actor(
862            &mut tree,
863            account_state_cid,
864            account_cid,
865            &account_state.address.into(),
866            Zero::zero(),
867        );
868
869        tree.flush().unwrap();
870
871        (tree, manifest)
872    }
873
874    fn init_actor<BS: Blockstore>(
875        tree: &mut StateTree<BS>,
876        state: Cid,
877        code: Cid,
878        addr: &Address,
879        balance: TokenAmount,
880    ) {
881        let actor = ActorState::new(code, state, balance, 0, None);
882        tree.set_actor(addr, actor).unwrap();
883    }
884
885    fn make_test_manifest<BS: Blockstore>(store: &BS, prefix: &str) -> (Cid, BuiltinActorManifest) {
886        let mut manifest_data = vec![];
887        for name in [
888            "account",
889            "cron",
890            "init",
891            "storagemarket",
892            "storageminer",
893            "multisig",
894            "paymentchannel",
895            "storagepower",
896            "reward",
897            "system",
898            "verifiedregistry",
899            "datacap",
900        ] {
901            let hash = MultihashCode::Identity.digest(format!("{prefix}{name}").as_bytes());
902            let code_cid = Cid::new_v1(IPLD_RAW, hash);
903            manifest_data.push((name, code_cid));
904        }
905
906        let manifest_cid = store
907            .put_cbor_default(&(1, store.put_cbor_default(&manifest_data).unwrap()))
908            .unwrap();
909        let manifest = BuiltinActorManifest::load_manifest(store, &manifest_cid).unwrap();
910
911        (manifest_cid, manifest)
912    }
913
914    fn make_base_miner_state<BS: Blockstore>(
915        store: &BS,
916        base_addr: &Address,
917        base_worker_addr: &Address,
918    ) -> fil_actor_miner_state::v8::State {
919        let empty_miner_info = fil_actor_miner_state::v8::MinerInfo {
920            owner: base_addr.into(),
921            worker: base_worker_addr.into(),
922            control_addresses: vec![],
923            pending_worker_key: None,
924            peer_id: vec![],
925            multi_address: vec![],
926            window_post_proof_type: fvm_shared2::sector::RegisteredPoStProof::Invalid(0),
927            sector_size: fvm_shared2::sector::SectorSize::_2KiB, // 0 not available in rust API, change Go code to 2 << 10 and all tests still pass
928            window_post_partition_sectors: 0,
929            consensus_fault_elapsed: 0,
930            pending_owner_address: None,
931        };
932
933        let empty_miner_info_cid = store.put_cbor_default(&empty_miner_info).unwrap();
934
935        fil_actor_miner_state::v8::State::new(
936            &make_calibnet_policy!(v8),
937            store,
938            empty_miner_info_cid,
939            0,
940            0,
941        )
942        .unwrap()
943    }
944
945    fn make_piece_cid(data: &[u8]) -> Cid {
946        let hash = MultihashCode::Sha2_256.digest(data);
947        let hash = Multihash::wrap(SHA2_256_TRUNC254_PADDED, hash.digest()).unwrap();
948        Cid::new_v1(FIL_COMMITMENT_UNSEALED, hash)
949    }
950
951    fn make_sealed_cid(data: &[u8]) -> Cid {
952        let hash = MultihashCode::Sha2_256.digest(data);
953        let hash = Multihash::wrap(POSEIDON_BLS12_381_A1_FC1, hash.digest()).unwrap();
954        Cid::new_v1(FIL_COMMITMENT_SEALED, hash)
955    }
956}