fil_actor_miner/
state.rs

1// Copyright 2019-2022 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4use std::cmp;
5use std::ops::Neg;
6
7use anyhow::{anyhow, Error};
8use cid::multihash::Code;
9use cid::Cid;
10use fil_actors_runtime::runtime::Policy;
11use fil_actors_runtime::{
12    actor_error, make_empty_map, make_map_with_root_and_bitwidth, u64_key, ActorDowncast,
13    ActorError, Array,
14};
15use fvm_ipld_amt::Error as AmtError;
16use fvm_ipld_bitfield::BitField;
17use fvm_ipld_blockstore::Blockstore;
18use fvm_ipld_encoding::tuple::*;
19use fvm_ipld_encoding::{serde_bytes, BytesDe, Cbor, CborStore};
20use fvm_ipld_hamt::Error as HamtError;
21use fvm_shared::address::Address;
22
23use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED};
24use fvm_shared::econ::TokenAmount;
25use fvm_shared::error::ExitCode;
26use fvm_shared::sector::{RegisteredPoStProof, SectorNumber, SectorSize, MAX_SECTOR_NUMBER};
27use fvm_shared::HAMT_BIT_WIDTH;
28use num_traits::Zero;
29
30use super::beneficiary::*;
31use super::deadlines::new_deadline_info;
32use super::policy::*;
33use super::types::*;
34use super::{
35    assign_deadlines, deadline_is_mutable, new_deadline_info_from_offset_and_epoch,
36    quant_spec_for_deadline, BitFieldQueue, Deadline, DeadlineInfo, DeadlineSectorMap, Deadlines,
37    PowerPair, Sectors, TerminationResult, VestingFunds,
38};
39
40const PRECOMMIT_EXPIRY_AMT_BITWIDTH: u32 = 6;
41pub const SECTORS_AMT_BITWIDTH: u32 = 5;
42
43/// Balance of Miner Actor should be greater than or equal to
44/// the sum of PreCommitDeposits and LockedFunds.
45/// It is possible for balance to fall below the sum of PCD, LF and
46/// InitialPledgeRequirements, and this is a bad state (IP Debt)
47/// that limits a miner actor's behavior (i.e. no balance withdrawals)
48/// Excess balance as computed by st.GetAvailableBalance will be
49/// withdrawable or usable for pre-commit deposit or pledge lock-up.
50#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug)]
51pub struct State {
52    /// Contains static info about this miner
53    pub info: Cid,
54
55    /// Total funds locked as pre_commit_deposit
56    pub pre_commit_deposits: TokenAmount,
57
58    /// Total rewards and added funds locked in vesting table
59    pub locked_funds: TokenAmount,
60
61    /// VestingFunds (Vesting Funds schedule for the miner).
62    pub vesting_funds: Cid,
63
64    /// Absolute value of debt this miner owes from unpaid fees.
65    pub fee_debt: TokenAmount,
66
67    /// Sum of initial pledge requirements of all active sectors.
68    pub initial_pledge: TokenAmount,
69
70    /// Sectors that have been pre-committed but not yet proven.
71    /// Map, HAMT<SectorNumber, SectorPreCommitOnChainInfo>
72    pub pre_committed_sectors: Cid,
73
74    // PreCommittedSectorsCleanUp maintains the state required to cleanup expired PreCommittedSectors.
75    pub pre_committed_sectors_cleanup: Cid, // BitFieldQueue (AMT[Epoch]*BitField)
76
77    /// Allocated sector IDs. Sector IDs can never be reused once allocated.
78    pub allocated_sectors: Cid, // BitField
79
80    /// Information for all proven and not-yet-garbage-collected sectors.
81    ///
82    /// Sectors are removed from this AMT when the partition to which the
83    /// sector belongs is compacted.
84    pub sectors: Cid, // Array, AMT[SectorNumber]SectorOnChainInfo (sparse)
85
86    /// The first epoch in this miner's current proving period. This is the first epoch in which a PoSt for a
87    /// partition at the miner's first deadline may arrive. Alternatively, it is after the last epoch at which
88    /// a PoSt for the previous window is valid.
89    /// Always greater than zero, this may be greater than the current epoch for genesis miners in the first
90    /// WPoStProvingPeriod epochs of the chain; the epochs before the first proving period starts are exempt from Window
91    /// PoSt requirements.
92    /// Updated at the end of every period by a cron callback.
93    pub proving_period_start: ChainEpoch,
94
95    /// Index of the deadline within the proving period beginning at ProvingPeriodStart that has not yet been
96    /// finalized.
97    /// Updated at the end of each deadline window by a cron callback.
98    pub current_deadline: u64,
99
100    /// The sector numbers due for PoSt at each deadline in the current proving period, frozen at period start.
101    /// New sectors are added and expired ones removed at proving period boundary.
102    /// Faults are not subtracted from this in state, but on the fly.
103    pub deadlines: Cid,
104
105    /// Deadlines with outstanding fees for early sector termination.
106    pub early_terminations: BitField,
107
108    // True when miner cron is active, false otherwise
109    pub deadline_cron_active: bool,
110}
111
112#[derive(PartialEq, Eq)]
113pub enum CollisionPolicy {
114    AllowCollisions,
115    DenyCollisions,
116}
117
118impl Cbor for State {}
119
120impl State {
121    #[allow(clippy::too_many_arguments)]
122    pub fn new<BS: Blockstore>(
123        policy: &Policy,
124        store: &BS,
125        info_cid: Cid,
126        period_start: ChainEpoch,
127        deadline_idx: u64,
128    ) -> anyhow::Result<Self> {
129        let empty_precommit_map =
130            make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH).flush().map_err(|e| {
131                e.downcast_default(
132                    ExitCode::USR_ILLEGAL_STATE,
133                    "failed to construct empty precommit map",
134                )
135            })?;
136        let empty_precommits_cleanup_array =
137            Array::<BitField, BS>::new_with_bit_width(store, PRECOMMIT_EXPIRY_AMT_BITWIDTH)
138                .flush()
139                .map_err(|e| {
140                    e.downcast_default(
141                        ExitCode::USR_ILLEGAL_STATE,
142                        "failed to construct empty precommits array",
143                    )
144                })?;
145        let empty_sectors_array =
146            Array::<SectorOnChainInfo, BS>::new_with_bit_width(store, SECTORS_AMT_BITWIDTH)
147                .flush()
148                .map_err(|e| {
149                    e.downcast_default(
150                        ExitCode::USR_ILLEGAL_STATE,
151                        "failed to construct sectors array",
152                    )
153                })?;
154        let empty_bitfield = store.put_cbor(&BitField::new(), Code::Blake2b256).map_err(|e| {
155            e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct empty bitfield")
156        })?;
157        let deadline = Deadline::new(store)?;
158        let empty_deadline = store.put_cbor(&deadline, Code::Blake2b256).map_err(|e| {
159            e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
160        })?;
161
162        let empty_deadlines = store
163            .put_cbor(&Deadlines::new(policy, empty_deadline), Code::Blake2b256)
164            .map_err(|e| {
165                e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
166            })?;
167
168        let empty_vesting_funds_cid =
169            store.put_cbor(&VestingFunds::new(), Code::Blake2b256).map_err(|e| {
170                e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state")
171            })?;
172
173        Ok(Self {
174            info: info_cid,
175
176            pre_commit_deposits: TokenAmount::default(),
177            locked_funds: TokenAmount::default(),
178
179            vesting_funds: empty_vesting_funds_cid,
180
181            initial_pledge: TokenAmount::default(),
182            fee_debt: TokenAmount::default(),
183
184            pre_committed_sectors: empty_precommit_map,
185            allocated_sectors: empty_bitfield,
186            sectors: empty_sectors_array,
187            proving_period_start: period_start,
188            current_deadline: deadline_idx,
189            deadlines: empty_deadlines,
190            early_terminations: BitField::new(),
191            deadline_cron_active: false,
192            pre_committed_sectors_cleanup: empty_precommits_cleanup_array,
193        })
194    }
195
196    pub fn get_info<BS: Blockstore>(&self, store: &BS) -> anyhow::Result<MinerInfo> {
197        match store.get_cbor(&self.info) {
198            Ok(Some(info)) => Ok(info),
199            Ok(None) => Err(actor_error!(not_found, "failed to get miner info").into()),
200            Err(e) => Err(e.downcast_wrap("failed to get miner info")),
201        }
202    }
203
204    pub fn save_info<BS: Blockstore>(
205        &mut self,
206        store: &BS,
207        info: &MinerInfo,
208    ) -> anyhow::Result<()> {
209        let cid = store.put_cbor(&info, Code::Blake2b256)?;
210        self.info = cid;
211        Ok(())
212    }
213
214    /// Returns deadline calculations for the current (according to state) proving period.
215    pub fn deadline_info(&self, policy: &Policy, current_epoch: ChainEpoch) -> DeadlineInfo {
216        new_deadline_info_from_offset_and_epoch(policy, self.proving_period_start, current_epoch)
217    }
218    // Returns deadline calculations for the state recorded proving period and deadline.
219    // This is out of date if the a miner does not have an active miner cron
220    pub fn recorded_deadline_info(
221        &self,
222        policy: &Policy,
223        current_epoch: ChainEpoch,
224    ) -> DeadlineInfo {
225        new_deadline_info(policy, self.proving_period_start, self.current_deadline, current_epoch)
226    }
227
228    // Returns current proving period start for the current epoch according to the current epoch and constant state offset
229    pub fn current_proving_period_start(
230        &self,
231        policy: &Policy,
232        current_epoch: ChainEpoch,
233    ) -> ChainEpoch {
234        let dl_info = self.deadline_info(policy, current_epoch);
235        dl_info.period_start
236    }
237
238    /// Returns deadline calculations for the current (according to state) proving period.
239    pub fn quant_spec_for_deadline(&self, policy: &Policy, deadline_idx: u64) -> QuantSpec {
240        new_deadline_info(policy, self.proving_period_start, deadline_idx, 0).quant_spec()
241    }
242
243    /// Marks a set of sector numbers as having been allocated.
244    /// If policy is `DenyCollisions`, fails if the set intersects with the sector numbers already allocated.
245    pub fn allocate_sector_numbers<BS: Blockstore>(
246        &mut self,
247        store: &BS,
248        sector_numbers: &BitField,
249        policy: CollisionPolicy,
250    ) -> Result<(), ActorError> {
251        let prior_allocation = store
252            .get_cbor(&self.allocated_sectors)
253            .map_err(|e| {
254                e.downcast_default(
255                    ExitCode::USR_ILLEGAL_STATE,
256                    "failed to load allocated sectors bitfield",
257                )
258            })?
259            .ok_or_else(|| actor_error!(illegal_state, "allocated sectors bitfield not found"))?;
260
261        if policy != CollisionPolicy::AllowCollisions {
262            // NOTE: A fancy merge algorithm could extract this intersection while merging, below, saving
263            // one iteration of the runs
264            let collisions = &prior_allocation & sector_numbers;
265            if !collisions.is_empty() {
266                return Err(actor_error!(
267                    illegal_argument,
268                    "sector numbers {:?} already allocated",
269                    collisions
270                ));
271            }
272        }
273        let new_allocation = &prior_allocation | sector_numbers;
274        self.allocated_sectors =
275            store.put_cbor(&new_allocation, Code::Blake2b256).map_err(|e| {
276                e.downcast_default(
277                    ExitCode::USR_ILLEGAL_ARGUMENT,
278                    format!(
279                        "failed to store allocated sectors bitfield after adding {:?}",
280                        sector_numbers,
281                    ),
282                )
283            })?;
284        Ok(())
285    }
286
287    /// Stores a pre-committed sector info, failing if the sector number is already present.
288    pub fn put_precommitted_sectors<BS: Blockstore>(
289        &mut self,
290        store: &BS,
291        precommits: Vec<SectorPreCommitOnChainInfo>,
292    ) -> anyhow::Result<()> {
293        let mut precommitted =
294            make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?;
295        for precommit in precommits.into_iter() {
296            let sector_no = precommit.info.sector_number;
297            let modified = precommitted
298                .set_if_absent(u64_key(precommit.info.sector_number), precommit)
299                .map_err(|e| {
300                    e.downcast_wrap(format!("failed to store precommitment for {:?}", sector_no,))
301                })?;
302            if !modified {
303                return Err(anyhow!("sector {} already pre-commited", sector_no));
304            }
305        }
306
307        self.pre_committed_sectors = precommitted.flush()?;
308        Ok(())
309    }
310
311    pub fn get_precommitted_sector<BS: Blockstore>(
312        &self,
313        store: &BS,
314        sector_num: SectorNumber,
315    ) -> Result<Option<SectorPreCommitOnChainInfo>, HamtError> {
316        let precommitted =
317            make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?;
318        Ok(precommitted.get(&u64_key(sector_num))?.cloned())
319    }
320
321    /// Gets and returns the requested pre-committed sectors, skipping missing sectors.
322    pub fn find_precommitted_sectors<BS: Blockstore>(
323        &self,
324        store: &BS,
325        sector_numbers: &[SectorNumber],
326    ) -> anyhow::Result<Vec<SectorPreCommitOnChainInfo>> {
327        let precommitted = make_map_with_root_and_bitwidth::<_, SectorPreCommitOnChainInfo>(
328            &self.pre_committed_sectors,
329            store,
330            HAMT_BIT_WIDTH,
331        )?;
332        let mut result = Vec::with_capacity(sector_numbers.len());
333
334        for &sector_number in sector_numbers {
335            let info = match precommitted.get(&u64_key(sector_number)).map_err(|e| {
336                e.downcast_wrap(format!("failed to load precommitment for {}", sector_number))
337            })? {
338                Some(info) => info.clone(),
339                None => continue,
340            };
341
342            result.push(info);
343        }
344
345        Ok(result)
346    }
347
348    pub fn delete_precommitted_sectors<BS: Blockstore>(
349        &mut self,
350        store: &BS,
351        sector_nums: &[SectorNumber],
352    ) -> Result<(), HamtError> {
353        let mut precommitted = make_map_with_root_and_bitwidth::<_, SectorPreCommitOnChainInfo>(
354            &self.pre_committed_sectors,
355            store,
356            HAMT_BIT_WIDTH,
357        )?;
358
359        for &sector_num in sector_nums {
360            let prev_entry = precommitted.delete(&u64_key(sector_num))?;
361            if prev_entry.is_none() {
362                return Err(format!("sector {} doesn't exist", sector_num).into());
363            }
364        }
365
366        self.pre_committed_sectors = precommitted.flush()?;
367        Ok(())
368    }
369
370    pub fn has_sector_number<BS: Blockstore>(
371        &self,
372        store: &BS,
373        sector_num: SectorNumber,
374    ) -> anyhow::Result<bool> {
375        let sectors = Sectors::load(store, &self.sectors)?;
376        Ok(sectors.get(sector_num)?.is_some())
377    }
378
379    pub fn put_sectors<BS: Blockstore>(
380        &mut self,
381        store: &BS,
382        new_sectors: Vec<SectorOnChainInfo>,
383    ) -> anyhow::Result<()> {
384        let mut sectors = Sectors::load(store, &self.sectors)
385            .map_err(|e| e.downcast_wrap("failed to load sectors"))?;
386
387        sectors.store(new_sectors)?;
388
389        self.sectors =
390            sectors.amt.flush().map_err(|e| e.downcast_wrap("failed to persist sectors"))?;
391
392        Ok(())
393    }
394
395    pub fn get_sector<BS: Blockstore>(
396        &self,
397        store: &BS,
398        sector_num: SectorNumber,
399    ) -> anyhow::Result<Option<SectorOnChainInfo>> {
400        let sectors = Sectors::load(store, &self.sectors)?;
401        sectors.get(sector_num)
402    }
403
404    pub fn delete_sectors<BS: Blockstore>(
405        &mut self,
406        store: &BS,
407        sector_nos: &BitField,
408    ) -> Result<(), AmtError> {
409        let mut sectors = Sectors::load(store, &self.sectors)?;
410
411        for sector_num in sector_nos.iter() {
412            let deleted_sector = sectors
413                .amt
414                .delete(sector_num)
415                .map_err(|e| e.downcast_wrap("could not delete sector number"))?;
416            if deleted_sector.is_none() {
417                return Err(AmtError::Dynamic(Error::msg(format!(
418                    "sector {} doesn't exist, failed to delete",
419                    sector_num
420                ))));
421            }
422        }
423
424        self.sectors = sectors.amt.flush()?;
425        Ok(())
426    }
427
428    pub fn for_each_sector<BS: Blockstore, F>(&self, store: &BS, mut f: F) -> anyhow::Result<()>
429    where
430        F: FnMut(&SectorOnChainInfo) -> anyhow::Result<()>,
431    {
432        let sectors = Sectors::load(store, &self.sectors)?;
433        sectors.amt.for_each(|_, v| f(v))?;
434        Ok(())
435    }
436
437    /// Returns the deadline and partition index for a sector number.
438    pub fn find_sector<BS: Blockstore>(
439        &self,
440        policy: &Policy,
441        store: &BS,
442        sector_number: SectorNumber,
443    ) -> anyhow::Result<(u64, u64)> {
444        let deadlines = self.load_deadlines(store)?;
445        deadlines.find_sector(policy, store, sector_number)
446    }
447
448    /// Schedules each sector to expire at its next deadline end. If it can't find
449    /// any given sector, it skips it.
450    ///
451    /// This method assumes that each sector's power has not changed, despite the rescheduling.
452    ///
453    /// Note: this method is used to "upgrade" sectors, rescheduling the now-replaced
454    /// sectors to expire at the end of the next deadline. Given the expense of
455    /// sealing a sector, this function skips missing/faulty/terminated "upgraded"
456    /// sectors instead of failing. That way, the new sectors can still be proved.
457    pub fn reschedule_sector_expirations<BS: Blockstore>(
458        &mut self,
459        policy: &Policy,
460        store: &BS,
461        current_epoch: ChainEpoch,
462        sector_size: SectorSize,
463        mut deadline_sectors: DeadlineSectorMap,
464    ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
465        let mut deadlines = self.load_deadlines(store)?;
466        let sectors = Sectors::load(store, &self.sectors)?;
467
468        let mut all_replaced = Vec::new();
469        for (deadline_idx, partition_sectors) in deadline_sectors.iter() {
470            let deadline_info = new_deadline_info(
471                policy,
472                self.current_proving_period_start(policy, current_epoch),
473                deadline_idx,
474                current_epoch,
475            )
476            .next_not_elapsed();
477            let new_expiration = deadline_info.last();
478            let mut deadline = deadlines.load_deadline(policy, store, deadline_idx)?;
479
480            let replaced = deadline.reschedule_sector_expirations(
481                store,
482                &sectors,
483                new_expiration,
484                partition_sectors,
485                sector_size,
486                deadline_info.quant_spec(),
487            )?;
488            all_replaced.extend(replaced);
489
490            deadlines.update_deadline(policy, store, deadline_idx, &deadline)?;
491        }
492
493        self.save_deadlines(store, deadlines)?;
494
495        Ok(all_replaced)
496    }
497
498    /// Assign new sectors to deadlines.
499    pub fn assign_sectors_to_deadlines<BS: Blockstore>(
500        &mut self,
501        policy: &Policy,
502        store: &BS,
503        current_epoch: ChainEpoch,
504        mut sectors: Vec<SectorOnChainInfo>,
505        partition_size: u64,
506        sector_size: SectorSize,
507    ) -> anyhow::Result<()> {
508        let mut deadlines = self.load_deadlines(store)?;
509
510        // Sort sectors by number to get better runs in partition bitfields.
511        sectors.sort_by_key(|info| info.sector_number);
512
513        let mut deadline_vec: Vec<Option<Deadline>> =
514            (0..policy.wpost_period_deadlines).map(|_| None).collect();
515
516        deadlines.for_each(policy, store, |deadline_idx, deadline| {
517            // Skip deadlines that aren't currently mutable.
518            if deadline_is_mutable(
519                policy,
520                self.current_proving_period_start(policy, current_epoch),
521                deadline_idx,
522                current_epoch,
523            ) {
524                deadline_vec[deadline_idx as usize] = Some(deadline);
525            }
526
527            Ok(())
528        })?;
529
530        let deadline_to_sectors = assign_deadlines(
531            policy,
532            policy.max_partitions_per_deadline,
533            partition_size,
534            &deadline_vec,
535            sectors,
536        )?;
537
538        for (deadline_idx, deadline_sectors) in deadline_to_sectors.into_iter().enumerate() {
539            if deadline_sectors.is_empty() {
540                continue;
541            }
542
543            let quant = self.quant_spec_for_deadline(policy, deadline_idx as u64);
544            let deadline = deadline_vec[deadline_idx].as_mut().unwrap();
545
546            // The power returned from AddSectors is ignored because it's not activated (proven) yet.
547            let proven = false;
548            deadline.add_sectors(
549                store,
550                partition_size,
551                proven,
552                &deadline_sectors,
553                sector_size,
554                quant,
555            )?;
556
557            deadlines.update_deadline(policy, store, deadline_idx as u64, deadline)?;
558        }
559
560        self.save_deadlines(store, deadlines)?;
561
562        Ok(())
563    }
564
565    /// Pops up to `max_sectors` early terminated sectors from all deadlines.
566    ///
567    /// Returns `true` if we still have more early terminations to process.
568    pub fn pop_early_terminations<BS: Blockstore>(
569        &mut self,
570        policy: &Policy,
571        store: &BS,
572        max_partitions: u64,
573        max_sectors: u64,
574    ) -> anyhow::Result<(TerminationResult, /* has more */ bool)> {
575        // Anything to do? This lets us avoid loading the deadlines if there's nothing to do.
576        if self.early_terminations.is_empty() {
577            return Ok((Default::default(), false));
578        }
579
580        // Load deadlines
581        let mut deadlines = self.load_deadlines(store)?;
582
583        let mut result = TerminationResult::new();
584        let mut to_unset = Vec::new();
585
586        // Process early terminations.
587        for i in self.early_terminations.iter() {
588            let deadline_idx = i;
589
590            // Load deadline + partitions.
591            let mut deadline = deadlines.load_deadline(policy, store, deadline_idx)?;
592
593            let (deadline_result, more) = deadline
594                .pop_early_terminations(
595                    store,
596                    max_partitions - result.partitions_processed,
597                    max_sectors - result.sectors_processed,
598                )
599                .map_err(|e| {
600                    e.downcast_wrap(format!(
601                        "failed to pop early terminations for deadline {}",
602                        deadline_idx
603                    ))
604                })?;
605
606            result += deadline_result;
607
608            if !more {
609                to_unset.push(i);
610            }
611
612            // Save the deadline
613            deadlines.update_deadline(policy, store, deadline_idx, &deadline)?;
614
615            if !result.below_limit(max_partitions, max_sectors) {
616                break;
617            }
618        }
619
620        for deadline_idx in to_unset {
621            self.early_terminations.unset(deadline_idx);
622        }
623
624        // Save back the deadlines.
625        self.save_deadlines(store, deadlines)?;
626
627        // Ok, check to see if we've handled all early terminations.
628        let no_early_terminations = self.early_terminations.is_empty();
629
630        Ok((result, !no_early_terminations))
631    }
632
633    /// Returns an error if the target sector cannot be found, or some other bad state is reached.
634    /// Returns Ok(false) if the target sector is faulty, terminated, or unproven
635    /// Returns Ok(true) otherwise
636    pub fn check_sector_active<BS: Blockstore>(
637        &self,
638        policy: &Policy,
639        store: &BS,
640        deadline_idx: u64,
641        partition_idx: u64,
642        sector_number: SectorNumber,
643        require_proven: bool,
644    ) -> anyhow::Result<bool> {
645        let dls = self.load_deadlines(store)?;
646        let dl = dls.load_deadline(policy, store, deadline_idx)?;
647        let partition = dl.load_partition(store, partition_idx)?;
648
649        let exists = partition.sectors.get(sector_number);
650        if !exists {
651            return Err(actor_error!(
652                not_found;
653                "sector {} not a member of partition {}, deadline {}",
654                sector_number, partition_idx, deadline_idx
655            )
656            .into());
657        }
658
659        let faulty = partition.faults.get(sector_number);
660        if faulty {
661            return Ok(false);
662        }
663
664        let terminated = partition.terminated.get(sector_number);
665        if terminated {
666            return Ok(false);
667        }
668
669        let unproven = partition.unproven.get(sector_number);
670        if unproven && require_proven {
671            return Ok(false);
672        }
673
674        Ok(true)
675    }
676
677    /// Returns an error if the target sector cannot be found and/or is faulty/terminated.
678    pub fn check_sector_health<BS: Blockstore>(
679        &self,
680        policy: &Policy,
681        store: &BS,
682        deadline_idx: u64,
683        partition_idx: u64,
684        sector_number: SectorNumber,
685    ) -> anyhow::Result<()> {
686        let deadlines = self.load_deadlines(store)?;
687        let deadline = deadlines.load_deadline(policy, store, deadline_idx)?;
688        let partition = deadline.load_partition(store, partition_idx)?;
689
690        if !partition.sectors.get(sector_number) {
691            return Err(actor_error!(
692                not_found;
693                "sector {} not a member of partition {}, deadline {}",
694                sector_number, partition_idx, deadline_idx
695            )
696            .into());
697        }
698
699        if partition.faults.get(sector_number) {
700            return Err(actor_error!(
701                forbidden;
702                "sector {} not a member of partition {}, deadline {}",
703                sector_number, partition_idx, deadline_idx
704            )
705            .into());
706        }
707
708        if partition.terminated.get(sector_number) {
709            return Err(actor_error!(
710                not_found;
711                "sector {} not of partition {}, deadline {} is terminated",
712                sector_number, partition_idx, deadline_idx
713            )
714            .into());
715        }
716
717        Ok(())
718    }
719
720    /// Loads sector info for a sequence of sectors.
721    pub fn load_sector_infos<BS: Blockstore>(
722        &self,
723        store: &BS,
724        sectors: &BitField,
725    ) -> anyhow::Result<Vec<SectorOnChainInfo>> {
726        Ok(Sectors::load(store, &self.sectors)?.load_sector(sectors)?)
727    }
728
729    pub fn load_deadlines<BS: Blockstore>(&self, store: &BS) -> Result<Deadlines, ActorError> {
730        store
731            .get_cbor::<Deadlines>(&self.deadlines)
732            .map_err(|e| {
733                e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deadlines")
734            })?
735            .ok_or_else(
736                || actor_error!(illegal_state; "failed to load deadlines {}", self.deadlines),
737            )
738    }
739
740    pub fn save_deadlines<BS: Blockstore>(
741        &mut self,
742        store: &BS,
743        deadlines: Deadlines,
744    ) -> anyhow::Result<()> {
745        self.deadlines = store.put_cbor(&deadlines, Code::Blake2b256)?;
746        Ok(())
747    }
748
749    /// Loads the vesting funds table from the store.
750    pub fn load_vesting_funds<BS: Blockstore>(&self, store: &BS) -> anyhow::Result<VestingFunds> {
751        Ok(store
752            .get_cbor(&self.vesting_funds)
753            .map_err(|e| {
754                e.downcast_wrap(format!("failed to load vesting funds {}", self.vesting_funds))
755            })?
756            .ok_or_else(
757                || actor_error!(not_found; "failed to load vesting funds {:?}", self.vesting_funds),
758            )?)
759    }
760
761    /// Saves the vesting table to the store.
762    pub fn save_vesting_funds<BS: Blockstore>(
763        &mut self,
764        store: &BS,
765        funds: &VestingFunds,
766    ) -> anyhow::Result<()> {
767        self.vesting_funds = store.put_cbor(funds, Code::Blake2b256)?;
768        Ok(())
769    }
770
771    // Return true when the miner actor needs to continue scheduling deadline crons
772    pub fn continue_deadline_cron(&self) -> bool {
773        !self.pre_commit_deposits.is_zero()
774            || !self.initial_pledge.is_zero()
775            || !self.locked_funds.is_zero()
776    }
777
778    //
779    // Funds and vesting
780    //
781
782    pub fn add_pre_commit_deposit(&mut self, amount: &TokenAmount) -> anyhow::Result<()> {
783        let new_total = &self.pre_commit_deposits + amount;
784        if new_total.is_negative() {
785            return Err(anyhow!(
786                "negative pre-commit deposit {} after adding {} to prior {}",
787                new_total,
788                amount,
789                self.pre_commit_deposits
790            ));
791        }
792        self.pre_commit_deposits = new_total;
793        Ok(())
794    }
795
796    pub fn add_initial_pledge(&mut self, amount: &TokenAmount) -> anyhow::Result<()> {
797        let new_total = &self.initial_pledge + amount;
798        if new_total.is_negative() {
799            return Err(anyhow!(
800                "negative initial pledge requirement {} after adding {} to prior {}",
801                new_total,
802                amount,
803                self.initial_pledge
804            ));
805        }
806        self.initial_pledge = new_total;
807        Ok(())
808    }
809
810    pub fn apply_penalty(&mut self, penalty: &TokenAmount) -> anyhow::Result<()> {
811        if penalty.is_negative() {
812            Err(anyhow!("applying negative penalty {} not allowed", penalty))
813        } else {
814            self.fee_debt += penalty;
815            Ok(())
816        }
817    }
818
819    /// First vests and unlocks the vested funds AND then locks the given funds in the vesting table.
820    pub fn add_locked_funds<BS: Blockstore>(
821        &mut self,
822        store: &BS,
823        current_epoch: ChainEpoch,
824        vesting_sum: &TokenAmount,
825        spec: &VestSpec,
826    ) -> anyhow::Result<TokenAmount> {
827        if vesting_sum.is_negative() {
828            return Err(anyhow!("negative vesting sum {}", vesting_sum));
829        }
830
831        let mut vesting_funds = self.load_vesting_funds(store)?;
832
833        // unlock vested funds first
834        let amount_unlocked = vesting_funds.unlock_vested_funds(current_epoch);
835        self.locked_funds -= &amount_unlocked;
836        if self.locked_funds.is_negative() {
837            return Err(anyhow!(
838                "negative locked funds {} after unlocking {}",
839                self.locked_funds,
840                amount_unlocked
841            ));
842        }
843        // add locked funds now
844        vesting_funds.add_locked_funds(current_epoch, vesting_sum, self.proving_period_start, spec);
845        self.locked_funds += vesting_sum;
846
847        // save the updated vesting table state
848        self.save_vesting_funds(store, &vesting_funds)?;
849
850        Ok(amount_unlocked)
851    }
852
853    /// Draws from vesting table and unlocked funds to repay up to the fee debt.
854    /// Returns the amount unlocked from the vesting table and the amount taken from
855    /// current balance. If the fee debt exceeds the total amount available for repayment
856    /// the fee debt field is updated to track the remaining debt.  Otherwise it is set to zero.
857    pub fn repay_partial_debt_in_priority_order<BS: Blockstore>(
858        &mut self,
859        store: &BS,
860        current_epoch: ChainEpoch,
861        curr_balance: &TokenAmount,
862    ) -> Result<
863        (
864            TokenAmount, // from vesting
865            TokenAmount, // from balance
866        ),
867        anyhow::Error,
868    > {
869        let unlocked_balance = self.get_unlocked_balance(curr_balance)?;
870
871        let fee_debt = self.fee_debt.clone();
872        let from_vesting = self.unlock_unvested_funds(store, current_epoch, &fee_debt)?;
873
874        if from_vesting > self.fee_debt {
875            return Err(anyhow!("should never unlock more than the debt we need to repay"));
876        }
877        self.fee_debt -= &from_vesting;
878
879        let from_balance = cmp::min(&unlocked_balance, &self.fee_debt).clone();
880        self.fee_debt -= &from_balance;
881
882        Ok((from_vesting, from_balance))
883    }
884
885    /// Repays the full miner actor fee debt.  Returns the amount that must be
886    /// burnt and an error if there are not sufficient funds to cover repayment.
887    /// Miner state repays from unlocked funds and fails if unlocked funds are insufficient to cover fee debt.
888    /// FeeDebt will be zero after a successful call.
889    pub fn repay_debts(&mut self, curr_balance: &TokenAmount) -> anyhow::Result<TokenAmount> {
890        let unlocked_balance = self.get_unlocked_balance(curr_balance)?;
891        if unlocked_balance < self.fee_debt {
892            return Err(actor_error!(
893                insufficient_funds,
894                "unlocked balance can not repay fee debt ({} < {})",
895                unlocked_balance,
896                self.fee_debt
897            )
898            .into());
899        }
900
901        Ok(std::mem::take(&mut self.fee_debt))
902    }
903    /// Unlocks an amount of funds that have *not yet vested*, if possible.
904    /// The soonest-vesting entries are unlocked first.
905    /// Returns the amount actually unlocked.
906    pub fn unlock_unvested_funds<BS: Blockstore>(
907        &mut self,
908        store: &BS,
909        current_epoch: ChainEpoch,
910        target: &TokenAmount,
911    ) -> anyhow::Result<TokenAmount> {
912        if target.is_zero() || self.locked_funds.is_zero() {
913            return Ok(TokenAmount::zero());
914        }
915
916        let mut vesting_funds = self.load_vesting_funds(store)?;
917        let amount_unlocked = vesting_funds.unlock_unvested_funds(current_epoch, target);
918        self.locked_funds -= &amount_unlocked;
919        if self.locked_funds.is_negative() {
920            return Err(anyhow!(
921                "negative locked funds {} after unlocking {}",
922                self.locked_funds,
923                amount_unlocked
924            ));
925        }
926
927        self.save_vesting_funds(store, &vesting_funds)?;
928        Ok(amount_unlocked)
929    }
930
931    /// Unlocks all vesting funds that have vested before the provided epoch.
932    /// Returns the amount unlocked.
933    pub fn unlock_vested_funds<BS: Blockstore>(
934        &mut self,
935        store: &BS,
936        current_epoch: ChainEpoch,
937    ) -> anyhow::Result<TokenAmount> {
938        if self.locked_funds.is_zero() {
939            return Ok(TokenAmount::zero());
940        }
941
942        let mut vesting_funds = self.load_vesting_funds(store)?;
943        let amount_unlocked = vesting_funds.unlock_vested_funds(current_epoch);
944        self.locked_funds -= &amount_unlocked;
945        if self.locked_funds.is_negative() {
946            return Err(anyhow!(
947                "vesting cause locked funds to become negative: {}",
948                self.locked_funds,
949            ));
950        }
951
952        self.save_vesting_funds(store, &vesting_funds)?;
953        Ok(amount_unlocked)
954    }
955
956    /// CheckVestedFunds returns the amount of vested funds that have vested before the provided epoch.
957    pub fn check_vested_funds<BS: Blockstore>(
958        &self,
959        store: &BS,
960        current_epoch: ChainEpoch,
961    ) -> anyhow::Result<TokenAmount> {
962        let vesting_funds = self.load_vesting_funds(store)?;
963        Ok(vesting_funds
964            .funds
965            .iter()
966            .take_while(|fund| fund.epoch < current_epoch)
967            .fold(TokenAmount::zero(), |acc, fund| acc + &fund.amount))
968    }
969
970    /// Unclaimed funds that are not locked -- includes funds used to cover initial pledge requirement.
971    pub fn get_unlocked_balance(&self, actor_balance: &TokenAmount) -> anyhow::Result<TokenAmount> {
972        let unlocked_balance =
973            actor_balance - &self.locked_funds - &self.pre_commit_deposits - &self.initial_pledge;
974        if unlocked_balance.is_negative() {
975            return Err(anyhow!("negative unlocked balance {}", unlocked_balance));
976        }
977        Ok(unlocked_balance)
978    }
979
980    /// Unclaimed funds. Actor balance - (locked funds, precommit deposit, ip requirement)
981    /// Can go negative if the miner is in IP debt.
982    pub fn get_available_balance(
983        &self,
984        actor_balance: &TokenAmount,
985    ) -> anyhow::Result<TokenAmount> {
986        // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit
987        Ok(self.get_unlocked_balance(actor_balance)? - &self.fee_debt)
988    }
989
990    pub fn check_balance_invariants(&self, balance: &TokenAmount) -> anyhow::Result<()> {
991        if self.pre_commit_deposits.is_negative() {
992            return Err(anyhow!("pre-commit deposit is negative: {}", self.pre_commit_deposits));
993        }
994        if self.locked_funds.is_negative() {
995            return Err(anyhow!("locked funds is negative: {}", self.locked_funds));
996        }
997        if self.initial_pledge.is_negative() {
998            return Err(anyhow!("initial pledge is negative: {}", self.initial_pledge));
999        }
1000        if self.fee_debt.is_negative() {
1001            return Err(anyhow!("fee debt is negative: {}", self.fee_debt));
1002        }
1003
1004        let min_balance = &self.pre_commit_deposits + &self.locked_funds + &self.initial_pledge;
1005        if balance < &min_balance {
1006            return Err(anyhow!("fee debt is negative: {}", self.fee_debt));
1007        }
1008
1009        Ok(())
1010    }
1011
1012    /// pre-commit expiry
1013    pub fn quant_spec_every_deadline(&self, policy: &Policy) -> QuantSpec {
1014        QuantSpec { unit: policy.wpost_challenge_window, offset: self.proving_period_start }
1015    }
1016
1017    pub fn add_pre_commit_clean_ups<BS: Blockstore>(
1018        &mut self,
1019        policy: &Policy,
1020        store: &BS,
1021        cleanup_events: Vec<(ChainEpoch, u64)>,
1022    ) -> anyhow::Result<()> {
1023        // Load BitField Queue for sector expiry
1024        let quant = self.quant_spec_every_deadline(policy);
1025        let mut queue =
1026            super::BitFieldQueue::new(store, &self.pre_committed_sectors_cleanup, quant)
1027                .map_err(|e| e.downcast_wrap("failed to load pre-commit clean up queue"))?;
1028
1029        queue.add_many_to_queue_values(cleanup_events.into_iter())?;
1030        self.pre_committed_sectors_cleanup = queue.amt.flush()?;
1031        Ok(())
1032    }
1033
1034    pub fn cleanup_expired_pre_commits<BS: Blockstore>(
1035        &mut self,
1036        policy: &Policy,
1037        store: &BS,
1038        current_epoch: ChainEpoch,
1039    ) -> anyhow::Result<TokenAmount> {
1040        let mut deposit_to_burn = TokenAmount::zero();
1041
1042        // cleanup expired pre-committed sectors
1043        let mut cleanup_queue = BitFieldQueue::new(
1044            store,
1045            &self.pre_committed_sectors_cleanup,
1046            self.quant_spec_every_deadline(policy),
1047        )?;
1048
1049        let (sectors, modified) = cleanup_queue.pop_until(current_epoch)?;
1050
1051        if modified {
1052            self.pre_committed_sectors_cleanup = cleanup_queue.amt.flush()?;
1053        }
1054
1055        let mut precommits_to_delete = Vec::new();
1056
1057        for i in sectors.iter() {
1058            let sector_number = i as SectorNumber;
1059
1060            let sector = match self.get_precommitted_sector(store, sector_number)? {
1061                Some(sector) => sector,
1062                // already committed/deleted
1063                None => continue,
1064            };
1065
1066            // mark it for deletion
1067            precommits_to_delete.push(sector_number);
1068
1069            // increment deposit to burn
1070            deposit_to_burn += sector.pre_commit_deposit;
1071        }
1072
1073        // Actually delete it.
1074        if !precommits_to_delete.is_empty() {
1075            self.delete_precommitted_sectors(store, &precommits_to_delete)?;
1076        }
1077
1078        self.pre_commit_deposits -= &deposit_to_burn;
1079        if self.pre_commit_deposits.is_negative() {
1080            return Err(anyhow!(
1081                "pre-commit clean up caused negative deposits: {}",
1082                self.pre_commit_deposits
1083            ));
1084        }
1085
1086        Ok(deposit_to_burn)
1087    }
1088
1089    pub fn advance_deadline<BS: Blockstore>(
1090        &mut self,
1091        policy: &Policy,
1092        store: &BS,
1093        current_epoch: ChainEpoch,
1094    ) -> anyhow::Result<AdvanceDeadlineResult> {
1095        let mut pledge_delta = TokenAmount::zero();
1096
1097        let dl_info = self.deadline_info(policy, current_epoch);
1098
1099        if !dl_info.period_started() {
1100            return Ok(AdvanceDeadlineResult {
1101                pledge_delta,
1102                power_delta: PowerPair::zero(),
1103                previously_faulty_power: PowerPair::zero(),
1104                detected_faulty_power: PowerPair::zero(),
1105                total_faulty_power: PowerPair::zero(),
1106            });
1107        }
1108
1109        self.current_deadline = (dl_info.index + 1) % policy.wpost_period_deadlines;
1110        if self.current_deadline == 0 {
1111            self.proving_period_start = dl_info.period_start + policy.wpost_proving_period;
1112        }
1113
1114        let mut deadlines = self.load_deadlines(store)?;
1115
1116        let mut deadline = deadlines.load_deadline(policy, store, dl_info.index)?;
1117
1118        let previously_faulty_power = deadline.faulty_power.clone();
1119
1120        if !deadline.is_live() {
1121            return Ok(AdvanceDeadlineResult {
1122                pledge_delta,
1123                power_delta: PowerPair::zero(),
1124                previously_faulty_power,
1125                detected_faulty_power: PowerPair::zero(),
1126                total_faulty_power: deadline.faulty_power,
1127            });
1128        }
1129
1130        let quant = quant_spec_for_deadline(policy, &dl_info);
1131
1132        // Detect and penalize missing proofs.
1133        let fault_expiration = dl_info.last() + policy.fault_max_age;
1134
1135        let (mut power_delta, detected_faulty_power) =
1136            deadline.process_deadline_end(store, quant, fault_expiration, self.sectors)?;
1137
1138        // Capture deadline's faulty power after new faults have been detected, but before it is
1139        // dropped along with faulty sectors expiring this round.
1140        let total_faulty_power = deadline.faulty_power.clone();
1141
1142        // Expire sectors that are due, either for on-time expiration or "early" faulty-for-too-long.
1143        let expired = deadline.pop_expired_sectors(store, dl_info.last(), quant)?;
1144
1145        // Release pledge requirements for the sectors expiring on-time.
1146        // Pledge for the sectors expiring early is retained to support the termination fee that
1147        // will be assessed when the early termination is processed.
1148        pledge_delta -= &expired.on_time_pledge;
1149        self.add_initial_pledge(&expired.on_time_pledge.neg())?;
1150
1151        // Record reduction in power of the amount of expiring active power.
1152        // Faulty power has already been lost, so the amount expiring can be excluded from the delta.
1153        power_delta -= &expired.active_power;
1154
1155        let no_early_terminations = expired.early_sectors.is_empty();
1156        if !no_early_terminations {
1157            self.early_terminations.set(dl_info.index);
1158        }
1159
1160        deadlines.update_deadline(policy, store, dl_info.index, &deadline)?;
1161
1162        self.save_deadlines(store, deadlines)?;
1163
1164        Ok(AdvanceDeadlineResult {
1165            pledge_delta,
1166            power_delta,
1167            previously_faulty_power,
1168            detected_faulty_power,
1169            total_faulty_power,
1170        })
1171    }
1172
1173    pub fn get_all_precommitted_sectors<BS: Blockstore>(
1174        &self,
1175        store: &BS,
1176        sector_nos: &BitField,
1177    ) -> anyhow::Result<Vec<SectorPreCommitOnChainInfo>> {
1178        let mut precommits = Vec::new();
1179        let precommitted =
1180            make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?;
1181        for sector_no in sector_nos.iter() {
1182            if sector_no as u64 > MAX_SECTOR_NUMBER {
1183                return Err(
1184                    actor_error!(illegal_argument; "sector number greater than maximum").into()
1185                );
1186            }
1187            let info: &SectorPreCommitOnChainInfo =
1188                precommitted
1189                    .get(&u64_key(sector_no as u64))?
1190                    .ok_or_else(|| actor_error!(not_found, "sector {} not found", sector_no))?;
1191            precommits.push(info.clone());
1192        }
1193        Ok(precommits)
1194    }
1195}
1196
1197pub struct AdvanceDeadlineResult {
1198    pub pledge_delta: TokenAmount,
1199    pub power_delta: PowerPair,
1200    /// Power that was faulty before this advance (including recovering)
1201    pub previously_faulty_power: PowerPair,
1202    /// Power of new faults and failed recoveries
1203    pub detected_faulty_power: PowerPair,
1204    /// Total faulty power after detecting faults (before expiring sectors)
1205    /// Note that failed recovery power is included in both PreviouslyFaultyPower and
1206    /// DetectedFaultyPower, so TotalFaultyPower is not simply their sum.
1207    pub total_faulty_power: PowerPair,
1208}
1209
1210/// Static information about miner
1211#[derive(Debug, PartialEq, Serialize_tuple, Deserialize_tuple)]
1212pub struct MinerInfo {
1213    /// Account that owns this miner
1214    /// - Income and returned collateral are paid to this address
1215    /// - This address is also allowed to change the worker address for the miner
1216    pub owner: Address,
1217
1218    /// Worker account for this miner
1219    /// This will be the key that is used to sign blocks created by this miner, and
1220    /// sign messages sent on behalf of this miner to commit sectors, submit PoSts, and
1221    /// other day to day miner activities
1222    pub worker: Address,
1223
1224    /// Additional addresses that are permitted to submit messages controlling this actor (optional).
1225    pub control_addresses: Vec<Address>, // Must all be ID addresses.
1226
1227    /// Optional worker key to update at an epoch
1228    pub pending_worker_key: Option<WorkerKeyChange>,
1229
1230    /// Libp2p identity that should be used when connecting to this miner
1231    #[serde(with = "serde_bytes")]
1232    pub peer_id: Vec<u8>,
1233
1234    /// Vector of byte arrays representing Libp2p multi-addresses used for establishing a connection with this miner.
1235    pub multi_address: Vec<BytesDe>,
1236
1237    /// The proof type used by this miner for sealing sectors.
1238    pub window_post_proof_type: RegisteredPoStProof,
1239
1240    /// Amount of space in each sector committed to the network by this miner
1241    pub sector_size: SectorSize,
1242
1243    /// The number of sectors in each Window PoSt partition (proof).
1244    /// This is computed from the proof type and represented here redundantly.
1245    pub window_post_partition_sectors: u64,
1246
1247    /// The next epoch this miner is eligible for certain permissioned actor methods
1248    /// and winning block elections as a result of being reported for a consensus fault.
1249    pub consensus_fault_elapsed: ChainEpoch,
1250
1251    /// A proposed new owner account for this miner.
1252    /// Must be confirmed by a message from the pending address itself.
1253    pub pending_owner_address: Option<Address>,
1254
1255    /// Account for receive miner benefits, withdraw on miner must send to this address,
1256    /// set owner address by default when create miner
1257    pub beneficiary: Address,
1258
1259    /// beneficiary's total quota, how much quota has been withdraw,
1260    /// and when this beneficiary expired
1261    pub beneficiary_term: BeneficiaryTerm,
1262
1263    /// A proposal new beneficiary message for this miner
1264    pub pending_beneficiary_term: Option<PendingBeneficiaryChange>,
1265}
1266
1267impl MinerInfo {
1268    pub fn new(
1269        owner: Address,
1270        worker: Address,
1271        control_addresses: Vec<Address>,
1272        peer_id: Vec<u8>,
1273        multi_address: Vec<BytesDe>,
1274        window_post_proof_type: RegisteredPoStProof,
1275    ) -> Result<Self, ActorError> {
1276        let sector_size = window_post_proof_type
1277            .sector_size()
1278            .map_err(|e| actor_error!(illegal_argument, "invalid sector size: {}", e))?;
1279
1280        let window_post_partition_sectors = window_post_proof_type
1281            .window_post_partitions_sector()
1282            .map_err(|e| actor_error!(illegal_argument, "invalid partition sectors: {}", e))?;
1283
1284        Ok(Self {
1285            owner,
1286            worker,
1287            control_addresses,
1288            pending_worker_key: None,
1289            beneficiary: owner,
1290            beneficiary_term: BeneficiaryTerm::default(),
1291            pending_beneficiary_term: None,
1292            peer_id,
1293            multi_address,
1294            window_post_proof_type,
1295            sector_size,
1296            window_post_partition_sectors,
1297            consensus_fault_elapsed: EPOCH_UNDEFINED,
1298            pending_owner_address: None,
1299        })
1300    }
1301}