1use 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#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug)]
51pub struct State {
52 pub info: Cid,
54
55 pub pre_commit_deposits: TokenAmount,
57
58 pub locked_funds: TokenAmount,
60
61 pub vesting_funds: Cid,
63
64 pub fee_debt: TokenAmount,
66
67 pub initial_pledge: TokenAmount,
69
70 pub pre_committed_sectors: Cid,
73
74 pub pre_committed_sectors_cleanup: Cid, pub allocated_sectors: Cid, pub sectors: Cid, pub proving_period_start: ChainEpoch,
94
95 pub current_deadline: u64,
99
100 pub deadlines: Cid,
104
105 pub early_terminations: BitField,
107
108 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 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 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 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 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 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 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 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 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 §or_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 §or_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 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 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 §ors,
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 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 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 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 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 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, bool)> {
575 if self.early_terminations.is_empty() {
577 return Ok((Default::default(), false));
578 }
579
580 let mut deadlines = self.load_deadlines(store)?;
582
583 let mut result = TerminationResult::new();
584 let mut to_unset = Vec::new();
585
586 for i in self.early_terminations.iter() {
588 let deadline_idx = i;
589
590 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 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 self.save_deadlines(store, deadlines)?;
626
627 let no_early_terminations = self.early_terminations.is_empty();
629
630 Ok((result, !no_early_terminations))
631 }
632
633 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 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 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 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 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 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 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 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 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 vesting_funds.add_locked_funds(current_epoch, vesting_sum, self.proving_period_start, spec);
845 self.locked_funds += vesting_sum;
846
847 self.save_vesting_funds(store, &vesting_funds)?;
849
850 Ok(amount_unlocked)
851 }
852
853 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, TokenAmount, ),
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 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 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 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 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 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 pub fn get_available_balance(
983 &self,
984 actor_balance: &TokenAmount,
985 ) -> anyhow::Result<TokenAmount> {
986 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 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 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 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 None => continue,
1064 };
1065
1066 precommits_to_delete.push(sector_number);
1068
1069 deposit_to_burn += sector.pre_commit_deposit;
1071 }
1072
1073 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 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 let total_faulty_power = deadline.faulty_power.clone();
1141
1142 let expired = deadline.pop_expired_sectors(store, dl_info.last(), quant)?;
1144
1145 pledge_delta -= &expired.on_time_pledge;
1149 self.add_initial_pledge(&expired.on_time_pledge.neg())?;
1150
1151 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 pub previously_faulty_power: PowerPair,
1202 pub detected_faulty_power: PowerPair,
1204 pub total_faulty_power: PowerPair,
1208}
1209
1210#[derive(Debug, PartialEq, Serialize_tuple, Deserialize_tuple)]
1212pub struct MinerInfo {
1213 pub owner: Address,
1217
1218 pub worker: Address,
1223
1224 pub control_addresses: Vec<Address>, pub pending_worker_key: Option<WorkerKeyChange>,
1229
1230 #[serde(with = "serde_bytes")]
1232 pub peer_id: Vec<u8>,
1233
1234 pub multi_address: Vec<BytesDe>,
1236
1237 pub window_post_proof_type: RegisteredPoStProof,
1239
1240 pub sector_size: SectorSize,
1242
1243 pub window_post_partition_sectors: u64,
1246
1247 pub consensus_fault_elapsed: ChainEpoch,
1250
1251 pub pending_owner_address: Option<Address>,
1254
1255 pub beneficiary: Address,
1258
1259 pub beneficiary_term: BeneficiaryTerm,
1262
1263 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}