From 8213ac2f1b9c07fc0d5f0a23be88088cf8e32878 Mon Sep 17 00:00:00 2001 From: Alex North <445306+anorth@users.noreply.github.com> Date: Wed, 28 Jun 2023 11:00:55 +1200 Subject: [PATCH] Remove legacy error downcasting from miner actor --- actors/miner/src/bitfield_queue.rs | 11 +- actors/miner/src/deadline_state.rs | 468 +++---- actors/miner/src/deadlines.rs | 27 +- actors/miner/src/expiration_queue.rs | 28 +- actors/miner/src/lib.rs | 1211 ++++++------------- actors/miner/src/partition_state.rs | 227 ++-- actors/miner/src/sectors.rs | 58 +- actors/miner/src/state.rs | 392 +++--- actors/miner/tests/deadline_state_test.rs | 39 +- actors/miner/tests/record_skipped_faults.rs | 4 +- actors/miner/tests/state_harness.rs | 13 +- actors/miner/tests/util.rs | 9 +- runtime/src/util/errors.rs | 57 + runtime/src/util/mod.rs | 2 + 14 files changed, 1062 insertions(+), 1484 deletions(-) create mode 100644 runtime/src/util/errors.rs diff --git a/actors/miner/src/bitfield_queue.rs b/actors/miner/src/bitfield_queue.rs index 6d3c1c969..819258b32 100644 --- a/actors/miner/src/bitfield_queue.rs +++ b/actors/miner/src/bitfield_queue.rs @@ -1,10 +1,11 @@ // Copyright 2019-2022 ChainSafe Systems // SPDX-License-Identifier: Apache-2.0, MIT +use anyhow::Context; use std::convert::TryInto; use cid::Cid; -use fil_actors_runtime::{ActorDowncast, Array}; +use fil_actors_runtime::Array; use fvm_ipld_amt::Error as AmtError; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; @@ -35,13 +36,13 @@ impl<'db, BS: Blockstore> BitFieldQueue<'db, BS> { let bitfield = self .amt .get(epoch) - .map_err(|e| e.downcast_wrap(format!("failed to lookup queue epoch {}", epoch)))? + .with_context(|| format!("failed to lookup queue epoch {}", epoch))? .cloned() .unwrap_or_default(); self.amt .set(epoch, &bitfield | values) - .map_err(|e| e.downcast_wrap(format!("failed to set queue epoch {}", epoch)))?; + .with_context(|| format!("failed to set queue epoch {}", epoch))?; Ok(()) } @@ -73,11 +74,11 @@ impl<'db, BS: Blockstore> BitFieldQueue<'db, BS> { Ok(()) }) - .map_err(|e| e.downcast_wrap("failed to cut from bitfield queue"))?; + .context("failed to cut from bitfield queue")?; self.amt .batch_delete(epochs_to_remove, true) - .map_err(|e| e.downcast_wrap("failed to remove empty epochs from bitfield queue"))?; + .context("failed to remove empty epochs from bitfield queue")?; Ok(()) } diff --git a/actors/miner/src/deadline_state.rs b/actors/miner/src/deadline_state.rs index 2525af756..8fc122230 100644 --- a/actors/miner/src/deadline_state.rs +++ b/actors/miner/src/deadline_state.rs @@ -4,11 +4,11 @@ use std::cmp; use std::collections::BTreeSet; -use anyhow::anyhow; use cid::multihash::Code; use cid::Cid; use fil_actors_runtime::runtime::Policy; -use fil_actors_runtime::{actor_error, ActorDowncast, ActorError, Array}; +use fil_actors_runtime::util::AsActorErrors; +use fil_actors_runtime::{actor_error, ActorError, Array, AsActorError}; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -55,26 +55,25 @@ impl Deadlines { policy: &Policy, store: &BS, deadline_idx: u64, - ) -> anyhow::Result { + ) -> Result { if deadline_idx >= policy.wpost_period_deadlines { - return Err(anyhow!(actor_error!( - illegal_argument, - "invalid deadline {}", - deadline_idx - ))); + return Err(actor_error!(illegal_argument, "invalid deadline {}", deadline_idx)); } - store.get_cbor(&self.due[deadline_idx as usize])?.ok_or_else(|| { - anyhow!(actor_error!(illegal_state, "failed to lookup deadline {}", deadline_idx)) - }) + store + .get_cbor(&self.due[deadline_idx as usize]) + .or_illegal_state("failed to load deadline")? + .ok_or_else(|| { + actor_error!(illegal_state, "failed to lookup deadline {}", deadline_idx) + }) } pub fn for_each( &self, policy: &Policy, store: &BS, - mut f: impl FnMut(u64, Deadline) -> anyhow::Result<()>, - ) -> anyhow::Result<()> { + mut f: impl FnMut(u64, Deadline) -> Result<(), ActorError>, + ) -> Result<(), ActorError> { for i in 0..(self.due.len() as u64) { let index = i; let deadline = self.load_deadline(policy, store, index)?; @@ -89,14 +88,16 @@ impl Deadlines { store: &BS, deadline_idx: u64, deadline: &Deadline, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { if deadline_idx >= policy.wpost_period_deadlines { - return Err(anyhow!("invalid deadline {}", deadline_idx)); + return Err(actor_error!(illegal_argument, "invalid deadline {}", deadline_idx)); } deadline.validate_state()?; - self.due[deadline_idx as usize] = store.put_cbor(deadline, Code::Blake2b256)?; + self.due[deadline_idx as usize] = store + .put_cbor(deadline, Code::Blake2b256) + .or_with_illegal_state(||format!("failed to save deadline {}", deadline_idx))?; Ok(()) } } @@ -181,24 +182,24 @@ pub struct DisputeInfo { } impl Deadline { - pub fn new(store: &BS) -> anyhow::Result { + pub fn new(store: &BS) -> Result { let empty_partitions_array = Array::<(), BS>::new_with_bit_width(store, DEADLINE_PARTITIONS_AMT_BITWIDTH) .flush() - .map_err(|e| e.downcast_wrap("Failed to create empty states array"))?; + .or_illegal_state("failed to create empty states array")?; let empty_deadline_expiration_array = Array::<(), BS>::new_with_bit_width(store, DEADLINE_EXPIRATIONS_AMT_BITWIDTH) .flush() - .map_err(|e| e.downcast_wrap("Failed to create empty states array"))?; + .or_illegal_state("failed to create empty expiration array")?; let empty_post_submissions_array = Array::<(), BS>::new_with_bit_width( store, DEADLINE_OPTIMISTIC_POST_SUBMISSIONS_AMT_BITWIDTH, ) .flush() - .map_err(|e| e.downcast_wrap("Failed to create empty states array"))?; + .or_illegal_state("failed to create empty posts array")?; let empty_sectors_array = Array::<(), BS>::new_with_bit_width(store, SECTORS_AMT_BITWIDTH) .flush() - .map_err(|e| e.downcast_wrap("Failed to construct empty sectors snapshot array"))?; + .or_illegal_state("failed to create empty sectors snapshot array")?; Ok(Self { partitions: empty_partitions_array, expirations_epochs: empty_deadline_expiration_array, @@ -217,46 +218,45 @@ impl Deadline { pub fn partitions_amt<'db, BS: Blockstore>( &self, store: &'db BS, - ) -> anyhow::Result> { - Ok(Array::load(&self.partitions, store)?) + ) -> Result, ActorError> { + Array::load(&self.partitions, store).or_illegal_state("failed to load partitions") } pub fn optimistic_proofs_amt<'db, BS: Blockstore>( &self, store: &'db BS, - ) -> anyhow::Result> { - Ok(Array::load(&self.optimistic_post_submissions, store)?) + ) -> Result, ActorError> { + Array::load(&self.optimistic_post_submissions, store) + .or_illegal_state("failed to load proofs") } pub fn partitions_snapshot_amt<'db, BS: Blockstore>( &self, store: &'db BS, - ) -> anyhow::Result> { - Ok(Array::load(&self.partitions_snapshot, store)?) + ) -> Result, ActorError> { + Array::load(&self.partitions_snapshot, store) + .or_illegal_state("failed to load partitions snapshot") } pub fn optimistic_proofs_snapshot_amt<'db, BS: Blockstore>( &self, store: &'db BS, - ) -> anyhow::Result> { - Ok(Array::load(&self.optimistic_post_submissions_snapshot, store)?) + ) -> Result, ActorError> { + Array::load(&self.optimistic_post_submissions_snapshot, store) + .or_illegal_state("failed to load proofs snapshot") } pub fn load_partition( &self, store: &BS, partition_idx: u64, - ) -> anyhow::Result { - let partitions = Array::::load(&self.partitions, store)?; + ) -> Result { + let partitions = Array::::load(&self.partitions, store) + .or_illegal_state("failed to load partitions")?; let partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to lookup partition {}", partition_idx), - ) - })? + .or_with_illegal_state(|| format!("failed to lookup partition {}", partition_idx))? .ok_or_else(|| actor_error!(not_found, "no partition {}", partition_idx))?; Ok(partition.clone()) @@ -266,16 +266,14 @@ impl Deadline { &self, store: &BS, partition_idx: u64, - ) -> anyhow::Result { - let partitions = Array::::load(&self.partitions_snapshot, store)?; + ) -> Result { + let partitions = Array::::load(&self.partitions_snapshot, store) + .or_illegal_state("failed to load partition snapshot")?; let partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to lookup partition snapshot {}", partition_idx), - ) + .or_with_illegal_state(|| { + format!("failed to lookup partition snapshot {}", partition_idx) })? .ok_or_else(|| actor_error!(not_found, "no partition snapshot {}", partition_idx))?; @@ -289,19 +287,19 @@ impl Deadline { expiration_epoch: ChainEpoch, partitions: &[u64], quant: QuantSpec, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { // Avoid doing any work if there's nothing to reschedule. if partitions.is_empty() { return Ok(()); } let mut queue = BitFieldQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load expiration queue"))?; + .or_illegal_state("failed to load expiration queue")?; queue .add_to_queue_values(expiration_epoch, partitions.iter().copied()) - .map_err(|e| e.downcast_wrap("failed to mutate expiration queue"))?; + .or_illegal_state("failed to add partitions to expiration queue")?; self.expirations_epochs = - queue.amt.flush().map_err(|e| e.downcast_wrap("failed to save expiration queue"))?; + queue.amt.flush().or_illegal_state("failed to save expiration queue")?; Ok(()) } @@ -313,8 +311,10 @@ impl Deadline { store: &BS, until: ChainEpoch, quant: QuantSpec, - ) -> anyhow::Result { - let (expired_partitions, modified) = self.pop_expired_partitions(store, until, quant)?; + ) -> Result { + let (expired_partitions, modified) = self + .pop_expired_partitions(store, until, quant) + .or_illegal_state("failed to pop expired partitions")?; if !modified { // nothing to do. @@ -334,16 +334,16 @@ impl Deadline { for i in expired_partitions.iter() { let partition_idx = i; let mut partition = partitions - .get(partition_idx)? + .get(partition_idx) + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? .cloned() - .ok_or_else(|| anyhow!("missing expected partition {}", partition_idx))?; + .ok_or_else(|| { + actor_error!(illegal_state, "missing expected partition {}", partition_idx) + })?; let partition_expiration = - partition.pop_expired_sectors(store, until, quant).map_err(|e| { - e.downcast_wrap(format!( - "failed to pop expired sectors from partition {}", - partition_idx - )) + partition.pop_expired_sectors(store, until, quant).or_with_illegal_state(|| { + format!("failed to pop expired sectors from partition {}", partition_idx) })?; if !partition_expiration.early_sectors.is_empty() { @@ -356,10 +356,12 @@ impl Deadline { all_faulty_power += &partition_expiration.faulty_power; all_on_time_pledge += &partition_expiration.on_time_pledge; - partitions.set(partition_idx, partition)?; + partitions + .set(partition_idx, partition) + .or_illegal_state("failed to save partition")?; } - self.partitions = partitions.flush()?; + self.partitions = partitions.flush().or_illegal_state("failed to save partitions")?; // Update early expiration bitmap. let new_early_terminations = BitField::try_from_bits(partitions_with_early_terminations) @@ -397,7 +399,7 @@ impl Deadline { mut sectors: &[SectorOnChainInfo], sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { let mut total_power = PowerPair::zero(); if sectors.is_empty() { return Ok(total_power); @@ -413,7 +415,10 @@ impl Deadline { // try filling up the last partition first. for partition_idx in partitions.count().saturating_sub(1).. { // Get/create partition to update. - let mut partition = match partitions.get(partition_idx)? { + let mut partition = match partitions + .get(partition_idx) + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? + { Some(partition) => partition.clone(), None => { // This case will usually happen zero times. @@ -441,7 +446,9 @@ impl Deadline { total_power += &partition_power; // Save partition back. - partitions.set(partition_idx, partition)?; + partitions + .set(partition_idx, partition) + .or_with_illegal_state(|| format!("failed to save partition {}", partition_idx))?; // Record deadline -> partition mapping so we can later update the deadlines. partition_deadline_updates @@ -453,16 +460,16 @@ impl Deadline { } // Save partitions back. - self.partitions = partitions.flush()?; + self.partitions = partitions.flush().or_illegal_state("failed to save partitions")?; // Next, update the expiration queue. - let mut deadline_expirations = - BitFieldQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load expiration epochs"))?; + let mut deadline_expirations = BitFieldQueue::new(store, &self.expirations_epochs, quant) + .or_illegal_state("failed to load expiration epochs")?; deadline_expirations .add_many_to_queue_values(partition_deadline_updates.iter().copied()) - .map_err(|e| e.downcast_wrap("failed to add expirations for new deadlines"))?; - self.expirations_epochs = deadline_expirations.amt.flush()?; + .or_illegal_state("failed to add expirations for new deadlines")?; + self.expirations_epochs = + deadline_expirations.amt.flush().or_illegal_state("failed to save expiration queue")?; Ok(total_power) } @@ -472,7 +479,7 @@ impl Deadline { store: &BS, max_partitions: u64, max_sectors: u64, - ) -> anyhow::Result<(TerminationResult, /* has more */ bool)> { + ) -> Result<(TerminationResult, /* has more */ bool), ActorError> { let mut partitions = self.partitions_amt(store)?; let mut partitions_finished = Vec::::new(); @@ -481,9 +488,10 @@ impl Deadline { for i in self.early_terminations.iter() { let partition_idx = i; - let mut partition = match partitions.get(partition_idx).map_err(|e| { - e.downcast_wrap(format!("failed to load partition {}", partition_idx)) - })? { + let mut partition = match partitions + .get(partition_idx) + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? + { Some(partition) => partition.clone(), None => { partitions_finished.push(partition_idx); @@ -494,7 +502,7 @@ impl Deadline { // Pop early terminations. let (partition_result, more) = partition .pop_early_terminations(store, max_sectors - result.sectors_processed) - .map_err(|e| e.downcast_wrap("failed to pop terminations from partition"))?; + .or_illegal_state("failed to pop terminations from partition")?; result += partition_result; @@ -504,9 +512,9 @@ impl Deadline { } // Save partition - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_wrap(format!("failed to store partition {}", partition_idx)) - })?; + partitions + .set(partition_idx, partition) + .or_with_illegal_state(|| format!("failed to store partition {}", partition_idx))?; if !result.below_limit(max_partitions, max_sectors) { break; @@ -519,8 +527,7 @@ impl Deadline { } // Save deadline's partitions - self.partitions = - partitions.flush().map_err(|e| e.downcast_wrap("failed to update partitions"))?; + self.partitions = partitions.flush().or_illegal_state("failed to update partitions")?; // Update global early terminations bitfield. let no_early_terminations = self.early_terminations.is_empty(); @@ -532,14 +539,15 @@ impl Deadline { store: &BS, until: ChainEpoch, quant: QuantSpec, - ) -> anyhow::Result<(BitField, bool)> { - let mut expirations = BitFieldQueue::new(store, &self.expirations_epochs, quant)?; - let (popped, modified) = expirations - .pop_until(until) - .map_err(|e| e.downcast_wrap("failed to pop expiring partitions"))?; + ) -> Result<(BitField, bool), ActorError> { + let mut expirations = BitFieldQueue::new(store, &self.expirations_epochs, quant) + .or_illegal_state("failed to load expiration queue")?; + let (popped, modified) = + expirations.pop_until(until).or_illegal_state("failed to pop expiring partitions")?; if modified { - self.expirations_epochs = expirations.amt.flush()?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to save expiration queue")?; } Ok((popped, modified)) @@ -555,16 +563,14 @@ impl Deadline { partition_sectors: &mut PartitionSectorMap, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { let mut partitions = self.partitions_amt(store)?; let mut power_lost = PowerPair::zero(); for (partition_idx, sector_numbers) in partition_sectors.iter() { let mut partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_wrap(format!("failed to load partition {}", partition_idx)) - })? + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? .ok_or_else( || actor_error!(not_found; "failed to find partition {}", partition_idx), )? @@ -580,15 +586,12 @@ impl Deadline { sector_size, quant, ) - .map_err(|e| { - e.downcast_wrap(format!( - "failed to terminate sectors in partition {}", - partition_idx - )) + .or_with_illegal_state(|| { + format!("failed to terminate sectors in partition {}", partition_idx) })?; - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_wrap(format!("failed to store updated partition {}", partition_idx)) + partitions.set(partition_idx, partition).or_with_illegal_state(|| { + format!("failed to store updated partition {}", partition_idx) })?; if !removed.is_empty() { @@ -606,8 +609,7 @@ impl Deadline { } // save partitions back - self.partitions = - partitions.flush().map_err(|e| e.downcast_wrap("failed to persist partitions"))?; + self.partitions = partitions.flush().or_illegal_state("failed to persist partitions")?; Ok(power_lost) } @@ -628,10 +630,10 @@ impl Deadline { BitField, // dead PowerPair, // removed power ), - anyhow::Error, + ActorError, > { let old_partitions = - self.partitions_amt(store).map_err(|e| e.downcast_wrap("failed to load partitions"))?; + self.partitions_amt(store).or_illegal_state("failed to load partitions")?; let partition_count = old_partitions.count(); let to_remove_set: BTreeSet<_> = to_remove @@ -642,7 +644,7 @@ impl Deadline { if let Some(&max_partition) = to_remove_set.iter().max() { if max_partition >= partition_count { return Err( - actor_error!(illegal_argument; "partition index {} out of range [0, {})", max_partition, partition_count).into() + actor_error!(illegal_argument; "partition index {} out of range [0, {})", max_partition, partition_count) ); } } else { @@ -653,7 +655,7 @@ impl Deadline { // Should already be checked earlier, but we might as well check again. if !self.early_terminations.is_empty() { return Err( - actor_error!(illegal_argument; "cannot remove partitions from deadline with early terminations").into(), + actor_error!(illegal_argument; "cannot remove partitions from deadline with early terminations") ); } @@ -705,11 +707,10 @@ impl Deadline { Ok(()) }) - .map_err(|e| e.downcast_wrap("while removing partitions"))?; + .or_illegal_state("while removing partitions")?; - self.partitions = new_partitions - .flush() - .map_err(|e| e.downcast_wrap("failed to persist new partition table"))?; + self.partitions = + new_partitions.flush().or_illegal_state("failed to persist new partition table")?; let dead = BitField::union(&all_dead_sectors); let live = BitField::union(&all_live_sectors); @@ -723,16 +724,16 @@ impl Deadline { // Update expiration bitfields. let mut expiration_epochs = BitFieldQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load expiration queue"))?; + .or_illegal_state("failed to load expiration queue")?; - expiration_epochs.cut(to_remove).map_err(|e| { - e.downcast_wrap("failed cut removed partitions from deadline expiration queue") - })?; + expiration_epochs + .cut(to_remove) + .or_illegal_state("failed cut removed partitions from deadline expiration queue")?; self.expirations_epochs = expiration_epochs .amt .flush() - .map_err(|e| e.downcast_wrap("failed persist deadline expiration queue"))?; + .or_illegal_state("failed persist deadline expiration queue")?; Ok((live, dead, removed_power)) } @@ -745,7 +746,7 @@ impl Deadline { quant: QuantSpec, fault_expiration_epoch: ChainEpoch, partition_sectors: &mut PartitionSectorMap, - ) -> anyhow::Result { + ) -> Result { let mut partitions = self.partitions_amt(store)?; // Record partitions with some fault, for subsequently indexing in the deadline. @@ -756,12 +757,7 @@ impl Deadline { for (partition_idx, sector_numbers) in partition_sectors.iter() { let mut partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partition {}", partition_idx), - ) - })? + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? .ok_or_else(|| actor_error!(not_found; "no such partition {}", partition_idx))? .clone(); @@ -774,11 +770,8 @@ impl Deadline { sector_size, quant, ) - .map_err(|e| { - e.downcast_wrap(format!( - "failed to declare faults in partition {}", - partition_idx - )) + .or_with_illegal_state(|| { + format!("failed to declare faults in partition {}", partition_idx) })?; self.faulty_power += &partition_new_faulty_power; @@ -787,17 +780,12 @@ impl Deadline { partitions_with_fault.push(partition_idx); } - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to store partition {}", partition_idx), - ) - })?; + partitions + .set(partition_idx, partition) + .or_with_illegal_state(|| format!("failed to store partition {}", partition_idx))?; } - self.partitions = partitions.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to store partitions root") - })?; + self.partitions = partitions.flush().or_illegal_state("failed to store partitions root")?; self.add_expiration_partitions( store, @@ -805,12 +793,7 @@ impl Deadline { &partitions_with_fault, quant, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to update expirations for partitions with faults", - ) - })?; + .or_illegal_state("failed to update expirations for partitions with faults")?; Ok(power_delta) } @@ -821,39 +804,27 @@ impl Deadline { sectors: &Sectors<'_, BS>, sector_size: SectorSize, partition_sectors: &mut PartitionSectorMap, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { let mut partitions = self.partitions_amt(store)?; for (partition_idx, sector_numbers) in partition_sectors.iter() { let mut partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partition {}", partition_idx), - ) - })? + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? .ok_or_else(|| actor_error!(not_found; "no such partition {}", partition_idx))? .clone(); partition .declare_faults_recovered(sectors, sector_size, sector_numbers) - .map_err(|e| e.downcast_wrap("failed to add recoveries"))?; + .or_illegal_state("failed to add recoveries")?; - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update partition {}", partition_idx), - ) + partitions.set(partition_idx, partition).or_with_illegal_state(|| { + format!("failed to update partition {}", partition_idx) })?; } // Power is not regained until the deadline end, when the recovery is confirmed. - - self.partitions = partitions.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to store partitions root") - })?; - + self.partitions = partitions.flush().or_illegal_state("failed to store partitions root")?; Ok(()) } @@ -867,9 +838,8 @@ impl Deadline { fault_expiration_epoch: ChainEpoch, sectors: Cid, ) -> Result<(PowerPair, PowerPair), ActorError> { - let mut partitions = self.partitions_amt(store).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load partitions") - })?; + let mut partitions = + self.partitions_amt(store).or_illegal_state("failed to load partitions")?; let mut detected_any = false; let mut rescheduled_partitions = Vec::::new(); @@ -884,12 +854,7 @@ impl Deadline { let mut partition = partitions .get(partition_idx) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partition {}", partition_idx), - ) - })? + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? .ok_or_else(|| actor_error!(illegal_state; "no partition {}", partition_idx))? .clone(); @@ -906,11 +871,8 @@ impl Deadline { let (part_power_delta, part_penalized_power, part_new_faulty_power) = partition .record_missed_post(store, fault_expiration_epoch, quant) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to record missed PoSt for partition {}", partition_idx), - ) + .or_with_illegal_state(|| { + format!("failed to record missed PoSt for partition {}", partition_idx) })?; // We marked some sectors faulty, we need to record the new @@ -921,11 +883,8 @@ impl Deadline { } // Save new partition state. - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update partition {}", partition_idx), - ) + partitions.set(partition_idx, partition).or_with_illegal_state(|| { + format!("failed to update partition {}", partition_idx) })?; self.faulty_power += &part_new_faulty_power; @@ -936,9 +895,7 @@ impl Deadline { // Save modified deadline state. if detected_any { - self.partitions = partitions.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to store partitions") - })?; + self.partitions = partitions.flush().or_illegal_state("failed to store partitions")?; } self.add_expiration_partitions( @@ -947,12 +904,7 @@ impl Deadline { &rescheduled_partitions, quant, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to update deadline expiration queue", - ) - })?; + .or_illegal_state("failed to update deadline expiration queue")?; // Reset PoSt submissions. self.partitions_posted = BitField::new(); @@ -963,43 +915,31 @@ impl Deadline { DEADLINE_OPTIMISTIC_POST_SUBMISSIONS_AMT_BITWIDTH, ) .flush() - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to clear pending proofs array") - })?; + .or_illegal_state("failed to clear pending proofs array")?; - // only snapshot sectors if there's a proof that might be disputed (this is equivalent to asking if the OptimisticPoStSubmissionsSnapshot is empty) + // only snapshot sectors if there's a proof that might be disputed + // (this is equivalent to asking if the OptimisticPoStSubmissionsSnapshot is empty) if self.optimistic_post_submissions != self.optimistic_post_submissions_snapshot { self.sectors_snapshot = sectors; } else { self.sectors_snapshot = - Array::<(), BS>::new_with_bit_width(store, SECTORS_AMT_BITWIDTH).flush().map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to clear sectors snapshot array", - ) - }, - )?; + Array::<(), BS>::new_with_bit_width(store, SECTORS_AMT_BITWIDTH) + .flush() + .or_illegal_state("failed to clear sectors snapshot array")?; } Ok((power_delta, penalized_power)) } - pub fn for_each( - &self, - store: &BS, - f: impl FnMut(u64, &Partition) -> anyhow::Result<()>, - ) -> anyhow::Result<()> { - let parts = self.partitions_amt(store)?; - parts.for_each(f)?; - Ok(()) - } - pub fn validate_state(&self) -> anyhow::Result<()> { + pub fn validate_state(&self) -> Result<(), ActorError> { if self.live_sectors > self.total_sectors { - return Err(anyhow!("deadline left with more live sectors than total")); + return Err(actor_error!( + illegal_state, + "deadline left with more live sectors than total" + )); } if self.faulty_power.raw.is_negative() || self.faulty_power.qa.is_negative() { - return Err(anyhow!("deadline left with negative faulty power")); + return Err(actor_error!(illegal_state, "deadline left with negative faulty power")); } Ok(()) @@ -1009,10 +949,9 @@ impl Deadline { &self, store: &BS, partitions: BitField, - ) -> anyhow::Result { - let partitions_snapshot = self - .partitions_snapshot_amt(store) - .map_err(|e| e.downcast_wrap("failed to load partitions {}"))?; + ) -> Result { + let partitions_snapshot = + self.partitions_snapshot_amt(store).or_illegal_state("failed to load partitions {}")?; let mut all_sectors = Vec::new(); let mut all_ignored = Vec::new(); @@ -1020,8 +959,9 @@ impl Deadline { let mut disputed_power = PowerPair::zero(); for part_idx in partitions.iter() { let partition_snapshot = partitions_snapshot - .get(part_idx)? - .ok_or_else(|| anyhow!("failed to find partition {}", part_idx))?; + .get(part_idx) + .or_with_illegal_state(|| format!("failed to load partition {}", part_idx))? + .ok_or_else(|| actor_error!(not_found, "failed to find partition {}", part_idx))?; // Record sectors for proof verification all_sectors.push(partition_snapshot.sectors.clone()); @@ -1031,7 +971,9 @@ impl Deadline { // Record active sectors for marking faults. let active = partition_snapshot.active_sectors(); - disputed_sectors.add(part_idx, active)?; + disputed_sectors + .add(part_idx, active) + .or_illegal_state("failed to add disputed sector")?; // Record disputed power for penalties. // @@ -1114,24 +1056,27 @@ impl Deadline { quant: QuantSpec, fault_expiration: ChainEpoch, post_partitions: &mut [PoStPartition], - ) -> anyhow::Result { + ) -> Result { let partition_indexes = BitField::try_from_bits(post_partitions.iter().map(|p| p.index)) - .map_err(|_| actor_error!(illegal_argument; "partition index out of bitfield range"))?; + .context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + "partition index out of bitfield range", + )?; let num_partitions = partition_indexes.len(); if num_partitions != post_partitions.len() as u64 { - return Err(anyhow!(actor_error!(illegal_argument, "duplicate partitions proven"))); + return Err(actor_error!(illegal_argument, "duplicate partitions proven")); } // First check to see if we're proving any already proven partitions. // This is faster than checking one by one. let already_proven = &self.partitions_posted & &partition_indexes; if !already_proven.is_empty() { - return Err(anyhow!(actor_error!( + return Err(actor_error!( illegal_argument, "partition already proven: {:?}", already_proven - ))); + )); } let mut partitions = self.partitions_amt(store)?; @@ -1148,7 +1093,7 @@ impl Deadline { for post in post_partitions { let mut partition = partitions .get(post.index) - .map_err(|e| e.downcast_wrap(format!("failed to load partition {}", post.index)))? + .or_with_illegal_state(|| format!("failed to load partition {}", post.index))? .ok_or_else(|| actor_error!(not_found; "no such partition {}", post.index))? .clone(); @@ -1164,11 +1109,8 @@ impl Deadline { fault_expiration, &post.skipped, ) - .map_err(|e| { - e.downcast_wrap(format!( - "failed to add skipped faults to partition {}", - post.index - )) + .or_with_illegal_state(|| { + format!("failed to add skipped faults to partition {}", post.index) })?; // If we have new faulty power, we've added some faults. We need @@ -1177,12 +1119,10 @@ impl Deadline { rescheduled_partitions.push(post.index); } - let recovered_power = - partition.recover_faults(store, sectors, sector_size, quant).map_err(|e| { - e.downcast_wrap(format!( - "failed to recover faulty sectors for partition {}", - post.index - )) + let recovered_power = partition + .recover_faults(store, sectors, sector_size, quant) + .or_with_illegal_state(|| { + format!("failed to recover faulty sectors for partition {}", post.index) })?; new_power_delta += &partition.activate_unproven(); @@ -1195,12 +1135,9 @@ impl Deadline { all_ignored.push(partition.terminated.clone()); // This will be rolled back if the method aborts with a failed proof. - partitions.set(post.index, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update partition {}", post.index), - ) - })?; + partitions + .set(post.index, partition) + .or_with_illegal_state(|| format!("failed to update partition {}", post.index))?; new_faulty_power_total += &new_fault_power; retracted_recovery_power_total += &retracted_recovery_power; @@ -1213,20 +1150,13 @@ impl Deadline { } self.add_expiration_partitions(store, fault_expiration, &rescheduled_partitions, quant) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to update expirations for partitions with faults", - ) - })?; + .or_illegal_state("failed to update expirations for partitions with faults")?; // Save everything back. self.faulty_power -= &recovered_power_total; self.faulty_power += &new_faulty_power_total; - self.partitions = partitions.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to persist partitions") - })?; + self.partitions = partitions.flush().or_illegal_state("failed to persist partitions")?; // Collect all sectors, faults, and recoveries for proof verification. let all_sector_numbers = BitField::union(&all_sectors); @@ -1250,18 +1180,17 @@ impl Deadline { store: &BS, partitions: &BitField, proofs: &[PoStProof], - ) -> anyhow::Result<()> { - let mut proof_arr = self - .optimistic_proofs_amt(store) - .map_err(|e| e.downcast_wrap("failed to load post proofs"))?; + ) -> Result<(), ActorError> { + let mut proof_arr = + self.optimistic_proofs_amt(store).or_illegal_state("failed to load post proofs")?; proof_arr .set( proof_arr.count(), // TODO: Can we do this with out cloning? WindowedPoSt { partitions: partitions.clone(), proofs: proofs.to_vec() }, ) - .map_err(|e| e.downcast_wrap("failed to store proof"))?; - let root = proof_arr.flush().map_err(|e| e.downcast_wrap("failed to save proofs"))?; + .or_illegal_state("failed to store proof")?; + let root = proof_arr.flush().or_illegal_state("failed to save proofs")?; self.optimistic_post_submissions = root; Ok(()) } @@ -1273,18 +1202,18 @@ impl Deadline { &mut self, store: &BS, idx: u64, - ) -> anyhow::Result<(BitField, Vec)> { + ) -> Result<(BitField, Vec), ActorError> { let mut proof_arr = self .optimistic_proofs_snapshot_amt(store) - .map_err(|e| e.downcast_wrap("failed to load post proofs snapshot amt"))?; + .or_illegal_state("failed to load post proofs snapshot amt")?; // Extract and remove the proof from the proofs array, leaving a hole. // This will not affect concurrent attempts to refute other proofs. let post = proof_arr .delete(idx) - .map_err(|e| e.downcast_wrap(format!("failed to retrieve proof {}", idx)))? + .or_with_illegal_state(|| format!("failed to retrieve proof {}", idx))? .ok_or_else(|| actor_error!(illegal_argument, "proof {} not found", idx))?; - let root = proof_arr.flush().map_err(|e| e.downcast_wrap("failed to save proofs"))?; + let root = proof_arr.flush().or_illegal_state("failed to save proofs")?; self.optimistic_post_submissions_snapshot = root; Ok((post.partitions, post.proofs)) } @@ -1305,7 +1234,7 @@ impl Deadline { partition_sectors: &mut PartitionSectorMap, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let mut partitions = self.partitions_amt(store)?; // track partitions with moved expirations. @@ -1313,9 +1242,10 @@ impl Deadline { let mut all_replaced = Vec::new(); for (partition_idx, sector_numbers) in partition_sectors.iter() { - let mut partition = match partitions.get(partition_idx).map_err(|e| { - e.downcast_wrap(format!("failed to load partition {}", partition_idx)) - })? { + let mut partition = match partitions + .get(partition_idx) + .or_with_illegal_state(|| format!("failed to load partition {}", partition_idx))? + { Some(partition) => partition.clone(), None => { // We failed to find the partition, it could have moved @@ -1334,11 +1264,8 @@ impl Deadline { sector_size, quant, ) - .map_err(|e| { - e.downcast_wrap(format!( - "failed to reschedule expirations in partition {}", - partition_idx - )) + .or_with_illegal_state(|| { + format!("failed to reschedule expirations in partition {}", partition_idx) })?; if replaced.is_empty() { @@ -1348,17 +1275,16 @@ impl Deadline { all_replaced.extend(replaced); rescheduled_partitions.push(partition_idx); - partitions.set(partition_idx, partition).map_err(|e| { - e.downcast_wrap(format!("failed to store partition {}", partition_idx)) - })?; + partitions + .set(partition_idx, partition) + .or_with_illegal_state(|| format!("failed to store partition {}", partition_idx))?; } if !rescheduled_partitions.is_empty() { - self.partitions = - partitions.flush().map_err(|e| e.downcast_wrap("failed to save partitions"))?; + self.partitions = partitions.flush().or_illegal_state("failed to save partitions")?; self.add_expiration_partitions(store, expiration, &rescheduled_partitions, quant) - .map_err(|e| e.downcast_wrap("failed to reschedule partition expirations"))?; + .or_illegal_state("failed to reschedule partition expirations")?; } Ok(all_replaced) diff --git a/actors/miner/src/deadlines.rs b/actors/miner/src/deadlines.rs index 3e625f908..975a49d10 100644 --- a/actors/miner/src/deadlines.rs +++ b/actors/miner/src/deadlines.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0, MIT use fil_actors_runtime::runtime::Policy; -use fil_actors_runtime::Array; +use fil_actors_runtime::{actor_error, ActorError, Array, AsActorErrors}; use fvm_ipld_blockstore::Blockstore; use fvm_shared::clock::{ChainEpoch, QuantSpec}; @@ -36,29 +36,32 @@ impl Deadlines { policy: &Policy, store: &BS, sector_number: SectorNumber, - ) -> anyhow::Result<(u64, u64)> { + ) -> Result<(u64, u64), ActorError> { for i in 0..self.due.len() { let deadline_idx = i as u64; let deadline = self.load_deadline(policy, store, deadline_idx)?; - let partitions = Array::::load(&deadline.partitions, store)?; + let partitions = Array::::load(&deadline.partitions, store) + .or_illegal_state("failed to load deadlines")?; let mut partition_idx = None; - partitions.for_each_while(|i, partition| { - if partition.sectors.get(sector_number) { - partition_idx = Some(i); - Ok(false) - } else { - Ok(true) - } - })?; + partitions + .for_each_while(|i, partition| { + if partition.sectors.get(sector_number) { + partition_idx = Some(i); + Ok(false) + } else { + Ok(true) + } + }) + .or_illegal_state("failed to iterate partitions")?; if let Some(partition_idx) = partition_idx { return Ok((deadline_idx, partition_idx)); } } - Err(anyhow::anyhow!("sector {} not due at any deadline", sector_number)) + Err(actor_error!(not_found, "sector {} not due at any deadline", sector_number)) } } diff --git a/actors/miner/src/expiration_queue.rs b/actors/miner/src/expiration_queue.rs index ec868b766..e50827e16 100644 --- a/actors/miner/src/expiration_queue.rs +++ b/actors/miner/src/expiration_queue.rs @@ -7,7 +7,7 @@ use std::convert::TryInto; use anyhow::{anyhow, Context}; use cid::Cid; use fil_actors_runtime::runtime::Policy; -use fil_actors_runtime::{ActorDowncast, Array}; +use fil_actors_runtime::Array; use fvm_ipld_amt::{Error as AmtError, ValueMut}; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; @@ -188,7 +188,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { &PowerPair::zero(), &group.pledge, ) - .map_err(|e| e.downcast_wrap("failed to record new sector expirations"))?; + .context("failed to record new sector expirations")?; total_sectors.push(sector_numbers); total_power += &group.power; @@ -215,7 +215,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { let (sector_numbers, power, pledge) = self .remove_active_sectors(sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to remove sector expirations"))?; + .context("failed to remove sector expirations")?; self.add( new_expiration, @@ -225,7 +225,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { &PowerPair::zero(), &pledge, ) - .map_err(|e| e.downcast_wrap("failed to record new sector expirations"))?; + .context("failed to record new sector expirations")?; Ok(()) } @@ -444,11 +444,11 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { ) -> anyhow::Result<(BitField, BitField, PowerPair, TokenAmount)> { let (old_sector_numbers, old_power, old_pledge) = self .remove_active_sectors(old_sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to remove replaced sectors"))?; + .context("failed to remove replaced sectors")?; let (new_sector_numbers, new_power, new_pledge) = self .add_active_sectors(new_sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to add replacement sectors"))?; + .context("failed to add replacement sectors")?; Ok(( old_sector_numbers, @@ -511,7 +511,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { // Remove non-faulty sectors. let (removed_sector_numbers, removed_power, removed_pledge) = self .remove_active_sectors(&non_faulty_sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to remove on-time recoveries"))?; + .context("failed to remove on-time recoveries")?; removed.on_time_sectors = removed_sector_numbers; removed.active_power = removed_power; removed.on_time_pledge = removed_pledge; @@ -654,14 +654,12 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { let mut expiration_set = self .amt .get(epoch.try_into()?) - .map_err(|e| e.downcast_wrap(format!("failed to lookup queue epoch {}", epoch)))? + .with_context(|| format!("failed to lookup queue epoch {}", epoch))? .ok_or_else(|| anyhow!("missing expected expiration set at epoch {}", epoch))? .clone(); expiration_set .remove(on_time_sectors, early_sectors, pledge, active_power, faulty_power) - .map_err(|e| { - anyhow!("failed to remove expiration values for queue epoch {}: {}", epoch, e) - })?; + .with_context(|| format!("failed to remove expiration values for queue epoch {}", epoch))?; self.must_update_or_delete(epoch, expiration_set)?; Ok(()) @@ -732,7 +730,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { Ok(self .amt .get(key.try_into()?) - .map_err(|e| e.downcast_wrap(format!("failed to lookup queue epoch {}", key)))? + .with_context(|| format!("failed to lookup queue epoch {}", key))? .cloned() .unwrap_or_default()) } @@ -744,7 +742,7 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { ) -> anyhow::Result<()> { self.amt .set(epoch.try_into()?, expiration_set) - .map_err(|e| e.downcast_wrap(format!("failed to set queue epoch {}", epoch))) + .with_context(|| format!("failed to set queue epoch {}", epoch)) } /// Since this might delete the node, it's not safe for use inside an iteration. @@ -756,11 +754,11 @@ impl<'db, BS: Blockstore> ExpirationQueue<'db, BS> { if expiration_set.is_empty() { self.amt .delete(epoch.try_into()?) - .map_err(|e| e.downcast_wrap(format!("failed to delete queue epoch {}", epoch)))?; + .with_context(|| format!("failed to delete queue epoch {}", epoch))?; } else { self.amt .set(epoch.try_into()?, expiration_set) - .map_err(|e| e.downcast_wrap(format!("failed to set queue epoch {}", epoch)))?; + .with_context(|| format!("failed to set queue epoch {}", epoch))?; } Ok(()) diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 6411b722f..407d42ebc 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -7,11 +7,12 @@ use std::collections::BTreeMap; use std::iter; use std::ops::Neg; -use anyhow::{anyhow, Error}; +use anyhow::anyhow; use byteorder::{BigEndian, ByteOrder, WriteBytesExt}; use cid::Cid; use fvm_ipld_bitfield::{BitField, Validate}; use fvm_ipld_blockstore::Blockstore; +use fvm_ipld_encoding::ipld_block::IpldBlock; use fvm_ipld_encoding::{from_slice, BytesDe, CborStore}; use fvm_shared::address::{Address, Payload, Protocol}; use fvm_shared::bigint::{BigInt, Integer}; @@ -23,6 +24,7 @@ use fvm_shared::randomness::*; use fvm_shared::reward::ThisEpochRewardReturn; use fvm_shared::sector::*; use fvm_shared::smooth::FilterEstimate; +use fvm_shared::version::NetworkVersion; use fvm_shared::{ActorID, METHOD_CONSTRUCTOR, METHOD_SEND}; use itertools::Itertools; use log::{error, info, warn}; @@ -38,17 +40,16 @@ pub use deadline_info::*; pub use deadline_state::*; pub use deadlines::*; pub use expiration_queue::*; +use ext::market::ActivateDealsResult; use fil_actors_runtime::cbor::{serialize, serialize_vec}; use fil_actors_runtime::runtime::builtins::Type; use fil_actors_runtime::runtime::{ActorCode, DomainSeparationTag, Policy, Runtime}; use fil_actors_runtime::{ actor_dispatch, actor_error, deserialize_block, extract_send_result, ActorContext, - ActorDowncast, ActorError, AsActorError, BatchReturn, BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, + ActorError, AsActorError, AsActorErrors, BatchReturn, BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, SYSTEM_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; -use fvm_ipld_encoding::ipld_block::IpldBlock; -use fvm_shared::version::NetworkVersion; pub use monies::*; pub use partition_state::*; pub use policy::*; @@ -190,14 +191,13 @@ impl Actor { let policy = rt.policy(); let current_epoch = rt.curr_epoch(); let blake2b = |b: &[u8]| rt.hash_blake2b(b); - let offset = - assign_proving_period_offset(policy, rt.message().receiver(), current_epoch, blake2b) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_SERIALIZATION, - "failed to assign proving period offset", - ) - })?; + let offset = assign_proving_period_offset( + policy, + rt.message().receiver(), + current_epoch, + blake2b, + ) + .context_code(ExitCode::USR_SERIALIZATION, "failed to assign proving period offset")?; let period_start = current_proving_period_start(policy, current_epoch, offset); if period_start > current_epoch { @@ -226,14 +226,12 @@ impl Actor { params.multi_addresses, params.window_post_proof_type, )?; - let info_cid = rt.store().put_cbor(&info, Blake2b256).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state") - })?; + let info_cid = rt + .store() + .put_cbor(&info, Blake2b256) + .or_illegal_state("failed to construct illegal state")?; - let st = - State::new(policy, rt.store(), info_cid, period_start, deadline_idx).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct state") - })?; + let st = State::new(policy, rt.store(), info_cid, period_start, deadline_idx)?; rt.create(&st)?; Ok(()) @@ -243,7 +241,8 @@ impl Actor { fn control_addresses(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let info = get_miner_info(rt.store(), &state)?; + let store = rt.store(); + let info = state.get_info(store)?; Ok(GetControlAddressesReturn { owner: info.owner, worker: info.worker, @@ -255,7 +254,8 @@ impl Actor { fn get_owner(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let info = get_miner_info(rt.store(), &state)?; + let store = rt.store(); + let info = state.get_info(store)?; Ok(GetOwnerReturn { owner: info.owner, proposed: info.pending_owner_address }) } @@ -271,7 +271,8 @@ impl Actor { None => return Ok(IsControllingAddressReturn { is_controlling: false }), }; let state: State = rt.state()?; - let info = get_miner_info(rt.store(), &state)?; + let store = rt.store(); + let info = state.get_info(store)?; let is_controlling = info.control_addresses.iter().chain(&[info.worker, info.owner]).any(|a| *a == input); @@ -282,7 +283,8 @@ impl Actor { fn get_sector_size(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let sector_size = get_miner_info(rt.store(), &state)?.sector_size; + let store = rt.store(); + let sector_size = state.get_info(store)?.sector_size; Ok(GetSectorSizeReturn { sector_size }) } @@ -292,10 +294,9 @@ impl Actor { fn get_available_balance(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let available_balance = - state.get_available_balance(&rt.current_balance()).map_err(|e| { - actor_error!(illegal_state, "failed to calculate available balance: {}", e) - })?; + let available_balance = state + .get_available_balance(&rt.current_balance()) + ?; Ok(GetAvailableBalanceReturn { available_balance }) } @@ -305,7 +306,7 @@ impl Actor { let state: State = rt.state()?; let vesting_funds = state .load_vesting_funds(rt.store()) - .map_err(|e| actor_error!(illegal_state, "failed to load vesting funds: {}", e))?; + ?; let ret = vesting_funds.funds.into_iter().map(|v| (v.epoch, v.amount)).collect_vec(); Ok(GetVestingFundsReturn { vesting_funds: ret }) } @@ -332,7 +333,8 @@ impl Actor { .collect::>()?; rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; // Only the Owner is allowed to change the new_worker and control addresses. rt.validate_immediate_caller_is(std::iter::once(&info.owner))?; @@ -348,10 +350,7 @@ impl Actor { }) } - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "could not save miner info") - })?; - + state.save_info(rt.store(), &info)?; Ok(()) })?; @@ -361,7 +360,8 @@ impl Actor { /// Triggers a worker address change if a change has been requested and its effective epoch has arrived. fn confirm_change_worker_address(rt: &impl Runtime) -> Result<(), ActorError> { rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; rt.validate_immediate_caller_is(std::iter::once(&info.owner))?; @@ -389,7 +389,8 @@ impl Actor { } rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; if rt.message().caller() == info.owner || info.pending_owner_address.is_none() { rt.validate_immediate_caller_is(std::iter::once(&info.owner))?; @@ -424,10 +425,7 @@ impl Actor { } } - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save miner info") - })?; - + state.save_info(rt.store(), &info)?; Ok(()) }) } @@ -436,7 +434,8 @@ impl Actor { fn get_peer_id(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let peer_id = get_miner_info(rt.store(), &state)?.peer_id; + let store = rt.store(); + let peer_id = state.get_info(store)?.peer_id; Ok(GetPeerIDReturn { peer_id }) } @@ -445,17 +444,15 @@ impl Actor { check_peer_info(policy, ¶ms.new_id, &[])?; rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; info.peer_id = params.new_id; - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "could not save miner info") - })?; - + state.save_info(rt.store(), &info)?; Ok(()) })?; Ok(()) @@ -465,7 +462,8 @@ impl Actor { fn get_multiaddresses(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let state: State = rt.state()?; - let multi_addrs = get_miner_info(rt.store(), &state)?.multi_address; + let store = rt.store(); + let multi_addrs = state.get_info(store)?.multi_address; Ok(GetMultiaddrsReturn { multi_addrs }) } @@ -477,17 +475,15 @@ impl Actor { check_peer_info(policy, &[], ¶ms.new_multi_addrs)?; rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; info.multi_address = params.new_multi_addrs; - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "could not save miner info") - })?; - + state.save_info(rt.store(), &info)?; Ok(()) })?; Ok(()) @@ -538,11 +534,13 @@ impl Actor { } let post_result = rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; - let max_proof_size = info.window_post_proof_type.proof_size().map_err(|e| { - actor_error!(illegal_state, "failed to determine max window post proof size: {}", e) - })?; + let max_proof_size = info + .window_post_proof_type + .proof_size() + .or_illegal_state("failed to determine max window post proof size")?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -656,20 +654,10 @@ impl Actor { return Err(actor_error!(illegal_argument, "post commit randomness mismatched")); } - let sectors = Sectors::load(rt.store(), &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors") - })?; - - let mut deadlines = - state.load_deadlines(rt.store()).map_err(|e| e.wrap("failed to load deadlines"))?; + let sectors = Sectors::load(rt.store(), &state.sectors)?; + let mut deadlines = state.load_deadlines(rt.store())?; - let mut deadline = - deadlines.load_deadline(rt.policy(), rt.store(), params.deadline).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", params.deadline), - ) - })?; + let mut deadline = deadlines.load_deadline(rt.policy(), rt.store(), params.deadline)?; // Record proven sectors/partitions, returning updates to power and the final set of sectors // proven/skipped. @@ -682,24 +670,14 @@ impl Actor { // overloading cron. let policy = rt.policy(); let fault_expiration = current_deadline.last() + policy.fault_max_age; - let post_result = deadline - .record_proven_sectors( - rt.store(), - §ors, - info.sector_size, - current_deadline.quant_spec(), - fault_expiration, - &mut params.partitions, - ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!( - "failed to process post submission for deadline {}", - params.deadline - ), - ) - })?; + let post_result = deadline.record_proven_sectors( + rt.store(), + §ors, + info.sector_size, + current_deadline.quant_spec(), + fault_expiration, + &mut params.partitions, + )?; // Make sure we actually proved something. let proven_sectors = &post_result.sectors - &post_result.ignored_sectors; @@ -715,51 +693,27 @@ impl Actor { } // If we're not recovering power, record the proof for optimistic verification. if post_result.recovered_power.is_zero() { - deadline - .record_post_proofs(rt.store(), &post_result.partitions, ¶ms.proofs) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to record proof for optimistic verification", - ) - })? + deadline.record_post_proofs(rt.store(), &post_result.partitions, ¶ms.proofs)? } else { // Load sector infos for proof, substituting a known-good sector for known-faulty sectors. // Note: this is slightly sub-optimal, loading info for the recovering sectors again after they were already // loaded above. - let sector_infos = sectors - .load_for_proof(&post_result.sectors, &post_result.ignored_sectors) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load sectors for post verification", - ) - })?; + let sector_infos = + sectors.load_for_proof(&post_result.sectors, &post_result.ignored_sectors)?; if !verify_windowed_post( rt, current_deadline.challenge, §or_infos, params.proofs, ) - .map_err(|e| e.wrap("window post failed"))? + .context("window post failed")? { return Err(actor_error!(illegal_argument, "invalid post was submitted")); } } - let deadline_idx = params.deadline; - deadlines.update_deadline(policy, rt.store(), params.deadline, &deadline).map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update deadline {}", deadline_idx), - ) - }, - )?; - - state.save_deadlines(rt.store(), deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; + deadlines.update_deadline(policy, rt.store(), params.deadline, &deadline)?; + state.save_deadlines(rt.store(), deadlines)?; Ok(post_result) })?; @@ -771,7 +725,7 @@ impl Actor { request_update_power(rt, post_result.power_delta)?; let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -782,9 +736,10 @@ impl Actor { rt: &impl Runtime, params: ProveCommitAggregateParams, ) -> Result<(), ActorError> { - let sector_numbers = params.sector_numbers.validate().map_err(|e| { - actor_error!(illegal_state, "Failed to validate bitfield for aggregated sectors: {}", e) - })?; + let sector_numbers = params + .sector_numbers + .validate() + .or_illegal_state("failed to validate bitfield for aggregated sectors")?; let agg_sectors_count = sector_numbers.len(); { @@ -815,15 +770,13 @@ impl Actor { } } let state: State = rt.state()?; - let info = get_miner_info(rt.store(), &state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; let store = rt.store(); - let precommits = - state.get_all_precommitted_sectors(store, sector_numbers).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to get precommits") - })?; + let precommits = state.get_all_precommitted_sectors(store, sector_numbers)?; if precommits.is_empty() { return Err(actor_error!( @@ -921,9 +874,7 @@ impl Actor { proof: params.aggregate_proof, infos: svis, }) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "aggregate seal verify failed") - })?; + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "aggregate seal verify failed")?; let rew = request_current_epoch_block_reward(rt)?; let pwr = request_current_total_power(rt)?; @@ -940,9 +891,7 @@ impl Actor { let state: State = rt.state()?; let aggregate_fee = aggregate_prove_commit_network_fee(precommits_to_confirm.len() as i64, &rt.base_fee()); - let unlocked_balance = state - .get_unlocked_balance(&rt.current_balance()) - .map_err(|_e| actor_error!(illegal_state, "failed to determine unlocked balance"))?; + let unlocked_balance = state.get_unlocked_balance(&rt.current_balance())?; if unlocked_balance < aggregate_fee { return Err(actor_error!( insufficient_funds, @@ -952,7 +901,7 @@ impl Actor { )); } burn_funds(rt, aggregate_fee)?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -1033,16 +982,15 @@ impl Actor { } let state: State = rt.state()?; - let info = get_miner_info(rt.store(), &state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.owner, info.worker]), )?; let sector_store = rt.store().clone(); - let mut sectors = Sectors::load(§or_store, &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors array") - })?; + let mut sectors = Sectors::load(§or_store, &state.sectors)?; let mut power_delta = PowerPair::zero(); let mut pledge_delta = TokenAmount::zero(); @@ -1122,17 +1070,14 @@ impl Actor { continue; } - if !state - .check_sector_active( - rt.policy(), - rt.store(), - update.deadline, - update.partition, - update.sector_number, - true, - ) - .map_err(|_| actor_error!(illegal_argument, "error checking sector health"))? - { + if !state.check_sector_active( + rt.policy(), + rt.store(), + update.deadline, + update.partition, + update.sector_number, + true, + )? { info!("sector isn't healthy, skipping sector {}", update.sector_number); continue; } @@ -1239,28 +1184,20 @@ impl Actor { let mut new_sectors = Vec::with_capacity(validated_updates.len()); for &dl_idx in deadlines_to_load.iter() { - let mut deadline = - deadlines.load_deadline(rt.policy(), rt.store(), dl_idx).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", dl_idx), - ) - })?; + let mut deadline = deadlines.load_deadline(rt.policy(), rt.store(), dl_idx)?; - let mut partitions = deadline.partitions_amt(rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partitions for deadline {}", dl_idx), - ) + let mut partitions = deadline.partitions_amt(rt.store()).with_context(|| { + format!("failed to load partitions for deadline {}", dl_idx) })?; let quant = state.quant_spec_for_deadline(rt.policy(), dl_idx); for with_details in &decls_by_deadline[&dl_idx] { - let update_proof_type = - with_details.sector_info.seal_proof.registered_update_proof().map_err( - |_| actor_error!(illegal_state, "couldn't load update proof type"), - )?; + let update_proof_type = with_details + .sector_info + .seal_proof + .registered_update_proof() + .or_illegal_state("couldn't load update proof type")?; if with_details.update.update_proof_type != update_proof_type { return Err(actor_error!( illegal_argument, @@ -1278,15 +1215,15 @@ impl Actor { new_unsealed_cid: with_details.full_unsealed_cid, proof: with_details.update.replica_proof.clone(), }) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, + .with_context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + || { format!( "failed to verify replica proof for sector {}", with_details.sector_info.sector_number - ), - ) - })?; + ) + }, + )?; let mut new_sector_info = with_details.sector_info.clone(); @@ -1350,13 +1287,10 @@ impl Actor { let mut partition = partitions .get(with_details.update.partition) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!( - "failed to load deadline {} partition {}", - with_details.update.deadline, with_details.update.partition - ), + .or_with_illegal_state(|| { + format!( + "failed to load deadline {} partition {}", + with_details.update.deadline, with_details.update.partition ) })? .cloned() @@ -1377,48 +1311,36 @@ impl Actor { info.sector_size, quant, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!( - "failed to replace sector at deadline {} partition {}", - with_details.update.deadline, with_details.update.partition - ), + .or_with_illegal_state(|| { + format!( + "failed to replace sector at deadline {} partition {}", + with_details.update.deadline, with_details.update.partition ) })?; power_delta += &partition_power_delta; pledge_delta += &partition_pledge_delta; - partitions.set(with_details.update.partition, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, + partitions + .set(with_details.update.partition, partition) + .or_with_illegal_state(|| { format!( "failed to save deadline {} partition {}", with_details.update.deadline, with_details.update.partition - ), - ) - })?; + ) + })?; succeeded.push(new_sector_info.sector_number); new_sectors.push(new_sector_info); } - deadline.partitions = partitions.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save partitions for deadline {}", dl_idx), - ) + deadline.partitions = partitions.flush().or_with_illegal_state(|| { + format!("failed to save partitions for deadline {}", dl_idx) })?; - deadlines.update_deadline(rt.policy(), rt.store(), dl_idx, &deadline).map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save deadline {}", dl_idx), - ) - }, - )?; + deadlines + .update_deadline(rt.policy(), rt.store(), dl_idx, &deadline) + .or_with_illegal_state(|| format!("failed to save deadline {}", dl_idx))?; } let success_len = succeeded.len(); @@ -1440,24 +1362,15 @@ impl Actor { } // Overwrite sector infos. - sectors.store(new_sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to update sector infos") - })?; + sectors.store(new_sectors)?; - state.sectors = sectors.amt.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save sectors") - })?; - state.save_deadlines(rt.store(), deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; + state.sectors = sectors.amt.flush().or_illegal_state("failed to save sectors")?; + state.save_deadlines(rt.store(), deadlines)?; // Update pledge. let current_balance = rt.current_balance(); if pledge_delta.is_positive() { - let unlocked_balance = - state.get_unlocked_balance(¤t_balance).map_err(|e| { - actor_error!(illegal_state, "failed to calculate unlocked balance: {}", e) - })?; + let unlocked_balance = state.get_unlocked_balance(¤t_balance)?; if unlocked_balance < pledge_delta { return Err(actor_error!( insufficient_funds, @@ -1468,14 +1381,10 @@ impl Actor { } } - state - .add_initial_pledge(&pledge_delta) - .map_err(|e| actor_error!(illegal_state, "failed to add initial pledge: {}", e))?; - - state.check_balance_invariants(¤t_balance).map_err(balance_invariants_broken)?; - + state.add_initial_pledge(&pledge_delta)?; + state.check_balance_invariants(¤t_balance)?; BitField::try_from_bits(succeeded) - .map_err(|_| actor_error!(illegal_argument; "invalid sector number")) + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "invalid sector number") })?; notify_pledge_changed(rt, &pledge_delta)?; @@ -1531,7 +1440,8 @@ impl Actor { )); } - let info = get_miner_info(rt.store(), st)?; + let store = rt.store(); + let info = st.get_info(store)?; // --- check proof --- // Find the proving period start for the deadline in question. @@ -1542,58 +1452,32 @@ impl Actor { let target_deadline = new_deadline_info(policy, pp_start, params.deadline, current_epoch); // Load the target deadline - let mut deadlines_current = st - .load_deadlines(rt.store()) - .map_err(|e| e.wrap("failed to load deadlines"))?; - - let mut dl_current = deadlines_current - .load_deadline(policy, rt.store(), params.deadline) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deadline") - })?; + let mut deadlines_current = st.load_deadlines(rt.store())?; + + let mut dl_current = + deadlines_current.load_deadline(policy, rt.store(), params.deadline)?; // Take the post from the snapshot for dispute. // This operation REMOVES the PoSt from the snapshot so // it can't be disputed again. If this method fails, // this operation must be rolled back. let (partitions, proofs) = - dl_current.take_post_proofs(rt.store(), params.post_index).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load proof for dispute", - ) - })?; + dl_current.take_post_proofs(rt.store(), params.post_index)?; // Load the partition info we need for the dispute. - let mut dispute_info = dl_current - .load_partitions_for_dispute(rt.store(), partitions) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load partition for dispute", - ) - })?; + let mut dispute_info = + dl_current.load_partitions_for_dispute(rt.store(), partitions)?; // This includes power that is no longer active (e.g., due to sector terminations). // It must only be used for penalty calculations, not power adjustments. let penalised_power = dispute_info.disputed_power.clone(); // Load sectors for the dispute. - let sectors = - Sectors::load(rt.store(), &dl_current.sectors_snapshot).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load sectors array", - ) - })?; - let sector_infos = sectors - .load_for_proof(&dispute_info.all_sector_nos, &dispute_info.ignored_sector_nos) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load sectors to dispute window post", - ) - })?; + let sectors = Sectors::load(rt.store(), &dl_current.sectors_snapshot)?; + let sector_infos = sectors.load_for_proof( + &dispute_info.all_sector_nos, + &dispute_info.ignored_sector_nos, + )?; // Check proof, we fail if validation succeeds. if verify_windowed_post(rt, target_deadline.challenge, §or_infos, proofs)? { @@ -1609,31 +1493,23 @@ impl Actor { // However, some of these sectors may have been // terminated. That's fine, we'll skip them. let fault_expiration_epoch = target_deadline.last() + policy.fault_max_age; - let power_delta = dl_current - .record_faults( - rt.store(), - §ors, - info.sector_size, - quant_spec_for_deadline(policy, &target_deadline), - fault_expiration_epoch, - &mut dispute_info.disputed_sectors, - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to declare faults") - })?; + let power_delta = dl_current.record_faults( + rt.store(), + §ors, + info.sector_size, + quant_spec_for_deadline(policy, &target_deadline), + fault_expiration_epoch, + &mut dispute_info.disputed_sectors, + )?; - deadlines_current - .update_deadline(policy, rt.store(), params.deadline, &dl_current) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update deadline {}", params.deadline), - ) - })?; + deadlines_current.update_deadline( + policy, + rt.store(), + params.deadline, + &dl_current, + )?; - st.save_deadlines(rt.store(), deadlines_current).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; + st.save_deadlines(rt.store(), deadlines_current)?; // --- penalties --- @@ -1654,17 +1530,13 @@ impl Actor { // could end up receiving a substantial // portion of their fee back as a reward. let penalty_target = &penalty_base + &reward_target; - st.apply_penalty(&penalty_target) - .map_err(|e| actor_error!(illegal_state, "failed to apply penalty {}", e))?; + st.apply_penalty(&penalty_target)?; let (penalty_from_vesting, penalty_from_balance) = st .repay_partial_debt_in_priority_order( rt.store(), current_epoch, &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to pay debt") - })?; + )?; let to_burn = &penalty_from_vesting + &penalty_from_balance; @@ -1691,7 +1563,7 @@ impl Actor { notify_pledge_changed(rt, &pledge_delta)?; let st: State = rt.state()?; - st.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + st.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -1887,29 +1759,19 @@ impl Actor { let aggregate_fee = aggregate_pre_commit_network_fee(sectors.len() as i64, &rt.base_fee()); // AggregateFee applied to fee debt to consolidate burn with outstanding debts state.apply_penalty(&aggregate_fee) - .map_err(|e| { - actor_error!( - illegal_state, - "failed to apply penalty: {}", - e - ) - })?; + ?; } // available balance already accounts for fee debt so it is correct to call // this before RepayDebts. We would have to // subtract fee debt explicitly if we called this after. let available_balance = state .get_available_balance(&rt.current_balance()) - .map_err(|e| { - actor_error!( - illegal_state, - "failed to calculate available balance: {}", - e - ) - })?; + ?; fee_to_burn = repay_debts_or_abort(rt, state)?; - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state + .get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses @@ -1933,12 +1795,9 @@ impl Actor { // Sector must have the same Window PoSt proof type as the miner's recorded seal type. let sector_wpost_proof = precommit.seal_proof .registered_window_post_proof() - .map_err(|_e| - actor_error!( - illegal_argument, - "failed to lookup Window PoSt proof type for sector seal proof {}", - i64::from(precommit.seal_proof) - ))?; + .with_context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + || format!("failed to lookup Window PoSt proof type for sector seal proof {}", i64::from(precommit.seal_proof)))?; if sector_wpost_proof != info.window_post_proof_type { return Err(actor_error!(illegal_argument, "sector Window PoSt proof type %d must match miner Window PoSt proof type {} (seal proof type {})", i64::from(sector_wpost_proof), i64::from(info.window_post_proof_type))); } @@ -1995,25 +1854,10 @@ impl Actor { if available_balance < total_deposit_required { return Err(actor_error!(insufficient_funds, "insufficient funds {} for pre-commit deposit: {}", available_balance, total_deposit_required)); } - state.add_pre_commit_deposit(&total_deposit_required) - .map_err(|e| - actor_error!( - illegal_state, - "failed to add pre-commit deposit {}: {}", - total_deposit_required, e - ))?; - state.allocate_sector_numbers(store, §or_numbers, CollisionPolicy::DenyCollisions) - .map_err(|e| - e.wrap("failed to allocate sector numbers") - )?; - state.put_precommitted_sectors(store, chain_infos) - .map_err(|e| - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to write pre-committed sectors") - )?; - state.add_pre_commit_clean_ups(rt.policy(), store, clean_up_events) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to add pre-commit expiry to queue") - })?; + state.add_pre_commit_deposit(&total_deposit_required)?; + state.allocate_sector_numbers(store, §or_numbers, CollisionPolicy::DenyCollisions)?; + state.put_precommitted_sectors(store, chain_infos)?; + state.add_pre_commit_clean_ups(rt.policy(), store, clean_up_events)?; // Activate miner cron needs_cron = !state.deadline_cron_active; state.deadline_cron_active = true; @@ -2021,7 +1865,7 @@ impl Actor { })?; burn_funds(rt, fee_to_burn)?; let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; if needs_cron { let new_dl_info = state.deadline_info(rt.policy(), curr_epoch); enroll_cron_event( @@ -2050,23 +1894,13 @@ impl Actor { let st: State = rt.state()?; let precommit = st - .get_precommitted_sector(rt.store(), sector_number) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load pre-committed sector {}", sector_number), - ) - })? + .get_precommitted_sector(rt.store(), sector_number)? .ok_or_else(|| actor_error!(not_found, "no pre-commited sector {}", sector_number))?; - let max_proof_size = precommit.info.seal_proof.proof_size().map_err(|e| { - actor_error!( - illegal_state, - "failed to determine max proof size for sector {}: {}", - sector_number, - e - ) - })?; + let max_proof_size = + precommit.info.seal_proof.proof_size().or_with_illegal_state(|| { + format!("failed to determine max proof size for sector {}", sector_number,) + })?; if params.proof.len() > max_proof_size { return Err(actor_error!( illegal_argument, @@ -2138,13 +1972,7 @@ impl Actor { let st: State = rt.state()?; let store = rt.store(); // This skips missing pre-commits. - let precommited_sectors = - st.find_precommitted_sectors(store, ¶ms.sectors).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load pre-committed sectors", - ) - })?; + let precommited_sectors = st.find_precommitted_sectors(store, ¶ms.sectors)?; confirm_sector_proofs_valid_internal( rt, precommited_sectors, @@ -2221,13 +2049,13 @@ impl Actor { /* Loop over sectors and do extension */ let (power_delta, pledge_delta) = rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; - let mut deadlines = - state.load_deadlines(rt.store()).map_err(|e| e.wrap("failed to load deadlines"))?; + let mut deadlines = state.load_deadlines(rt.store())?; // Group declarations by deadline, and remember iteration order. // @@ -2244,30 +2072,15 @@ impl Actor { decls.push(decl); } - let mut sectors = Sectors::load(rt.store(), &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors array") - })?; + let mut sectors = Sectors::load(rt.store(), &state.sectors)?; let mut power_delta = PowerPair::zero(); let mut pledge_delta = TokenAmount::zero(); for deadline_idx in deadlines_to_load { let policy = rt.policy(); - let mut deadline = - deadlines.load_deadline(policy, rt.store(), deadline_idx).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", deadline_idx), - ) - })?; - - let mut partitions = deadline.partitions_amt(rt.store()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partitions for deadline {}", deadline_idx), - ) - })?; - + let mut deadline = deadlines.load_deadline(policy, rt.store(), deadline_idx)?; + let mut partitions = deadline.partitions_amt(rt.store())?; let quant = state.quant_spec_for_deadline(policy, deadline_idx); // Group modified partitions by epoch to which they are extended. Duplicates are ok. @@ -2279,18 +2092,11 @@ impl Actor { let mut partition = partitions .get(decl.partition) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load partition {:?}", key), - ) - })? + .or_with_illegal_state(|| format!("failed to load partition {:?}", key))? .cloned() .ok_or_else(|| actor_error!(not_found, "no such partition {:?}", key))?; - let old_sectors = sectors - .load_sector(&decl.sectors) - .map_err(|e| e.wrap("failed to load sectors"))?; + let old_sectors = sectors.load_sector(&decl.sectors)?; let new_sectors: Vec = old_sectors .iter() .map(|sector| match kind { @@ -2322,12 +2128,7 @@ impl Actor { .collect::>()?; // Overwrite sector infos. - sectors.store(new_sectors.clone()).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update sectors {:?}", decl.sectors), - ) - })?; + sectors.store(new_sectors.clone())?; // Remove old sectors from partition and assign new sectors. let (partition_power_delta, partition_pledge_delta) = partition @@ -2338,22 +2139,16 @@ impl Actor { info.sector_size, quant, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to replace sector expirations at {:?}", key), - ) + .with_context(|| { + format!("failed to replace sector expirations at {:?}", key) })?; power_delta += &partition_power_delta; pledge_delta += partition_pledge_delta; // expected to be zero, see note below. - partitions.set(decl.partition, partition).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save partition {:?}", key), - ) - })?; + partitions + .set(decl.partition, partition) + .or_with_illegal_state(|| format!("failed to save partition {:?}", key))?; // Record the new partition expiration epoch for setting outside this loop // over declarations. @@ -2368,46 +2163,33 @@ impl Actor { } } - deadline.partitions = partitions.flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save partitions for deadline {}", deadline_idx), - ) + deadline.partitions = partitions.flush().or_with_illegal_state(|| { + format!("failed to save partitions for deadline {}", deadline_idx) })?; // Record partitions in deadline expiration queue for epoch in epochs_to_reschedule { let p_idxs = partitions_by_new_epoch.get(&epoch).unwrap(); - deadline.add_expiration_partitions(rt.store(), epoch, p_idxs, quant).map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!( - "failed to add expiration partitions to \ + deadline + .add_expiration_partitions(rt.store(), epoch, p_idxs, quant) + .with_context(|| { + format!( + "failed to add expiration partitions to \ deadline {} epoch {}", - deadline_idx, epoch - ), + deadline_idx, epoch ) - }, - )?; + })?; } - deadlines.update_deadline(policy, rt.store(), deadline_idx, &deadline).map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save deadline {}", deadline_idx), - ) - }, - )?; + deadlines + .update_deadline(policy, rt.store(), deadline_idx, &deadline) + .with_context(|| format!("failed to save deadline {}", deadline_idx))?; } - state.sectors = sectors.amt.flush().map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save sectors") - })?; - state.save_deadlines(rt.store(), deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; + state.sectors = sectors.amt.flush().or_illegal_state("failed to save sectors")?; + state + .save_deadlines(rt.store(), deadlines) + ?; Ok((power_delta, pledge_delta)) })?; @@ -2462,30 +2244,28 @@ impl Actor { let deadline = term.deadline; let partition = term.partition; - to_process.add(rt.policy(), deadline, partition, term.sectors).map_err(|e| { - actor_error!( - illegal_argument, - "failed to process deadline {}, partition {}: {}", - deadline, - partition, - e - ) - })?; + to_process + .add(rt.policy(), deadline, partition, term.sectors) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("failed to process deadline {}, partition {}", deadline, partition) + })?; } { let policy = rt.policy(); to_process .check(policy.addressed_partitions_max, policy.addressed_sectors_max) - .map_err(|e| { - actor_error!(illegal_argument, "cannot process requested parameters: {}", e) - })?; + .context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + "cannot process requested parameters", + )?; } let (had_early_terminations, power_delta) = rt.transaction(|state: &mut State, rt| { let had_early_terminations = have_pending_early_terminations(state); - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -2495,14 +2275,11 @@ impl Actor { let curr_epoch = rt.curr_epoch(); let mut power_delta = PowerPair::zero(); - let mut deadlines = - state.load_deadlines(store).map_err(|e| e.wrap("failed to load deadlines"))?; + let mut deadlines = state.load_deadlines(store)?; // We're only reading the sectors, so there's no need to save this back. // However, we still want to avoid re-loading this array per-partition. - let sectors = Sectors::load(store, &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors") - })?; + let sectors = Sectors::load(store, &state.sectors)?; for (deadline_idx, partition_sectors) in to_process.iter() { // If the deadline is the current or next deadline to prove, don't allow terminating sectors. @@ -2521,13 +2298,7 @@ impl Actor { } let quant = state.quant_spec_for_deadline(rt.policy(), deadline_idx); - let mut deadline = - deadlines.load_deadline(rt.policy(), store, deadline_idx).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", deadline_idx), - ) - })?; + let mut deadline = deadlines.load_deadline(rt.policy(), store, deadline_idx)?; let removed_power = deadline .terminate_sectors( @@ -2539,30 +2310,17 @@ impl Actor { info.sector_size, quant, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to terminate sectors in deadline {}", deadline_idx), - ) + .with_context(|| { + format!("failed to terminate sectors in deadline {}", deadline_idx) })?; state.early_terminations.set(deadline_idx); power_delta -= &removed_power; - deadlines.update_deadline(rt.policy(), store, deadline_idx, &deadline).map_err( - |e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update deadline {}", deadline_idx), - ) - }, - )?; + deadlines.update_deadline(rt.policy(), store, deadline_idx, &deadline)?; } - state.save_deadlines(store, deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; - + state.save_deadlines(store, deadlines)?; Ok((had_early_terminations, power_delta)) })?; let epoch_reward = request_current_epoch_block_reward(rt)?; @@ -2584,7 +2342,7 @@ impl Actor { schedule_early_termination_work(rt)?; } let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; request_update_power(rt, power_delta)?; Ok(TerminateSectorsReturn { done: !more }) @@ -2609,28 +2367,23 @@ impl Actor { let deadline = term.deadline; let partition = term.partition; - to_process.add(rt.policy(), deadline, partition, term.sectors).map_err(|e| { - actor_error!( - illegal_argument, - "failed to process deadline {}, partition {}: {}", - deadline, - partition, - e - ) - })?; + to_process + .add(rt.policy(), deadline, partition, term.sectors) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("failed to process deadline {}, partition {}", deadline, partition,) + })?; } { let policy = rt.policy(); to_process .check(policy.addressed_partitions_max, policy.addressed_sectors_max) - .map_err(|e| { - actor_error!(illegal_argument, "cannot process requested parameters: {}", e) - })?; + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "cannot process requested parameters")?; } let power_delta = rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -2638,12 +2391,9 @@ impl Actor { let store = rt.store(); - let mut deadlines = - state.load_deadlines(store).map_err(|e| e.wrap("failed to load deadlines"))?; + let mut deadlines = state.load_deadlines(store)?; - let sectors = Sectors::load(store, &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors array") - })?; + let sectors = Sectors::load(store, &state.sectors)?; let mut new_fault_power_total = PowerPair::zero(); let curr_epoch = rt.curr_epoch(); @@ -2655,32 +2405,17 @@ impl Actor { deadline_idx, curr_epoch, ) - .map_err(|e| { - actor_error!( - illegal_argument, - "invalid fault declaration deadline {}: {}", - deadline_idx, - e - ) - })?; - - validate_fr_declaration_deadline(&target_deadline).map_err(|e| { - actor_error!( - illegal_argument, - "failed fault declaration at deadline {}: {}", - deadline_idx, - e - ) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("invalid fault declaration deadline {}", deadline_idx) })?; - let mut deadline = - deadlines.load_deadline(policy, store, deadline_idx).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", deadline_idx), - ) + validate_fr_declaration_deadline(&target_deadline) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("failed fault declaration at deadline {}", deadline_idx) })?; + let mut deadline = deadlines.load_deadline(policy, store, deadline_idx)?; + let fault_expiration_epoch = target_deadline.last() + policy.fault_max_age; let deadline_power_delta = deadline @@ -2692,27 +2427,16 @@ impl Actor { fault_expiration_epoch, partition_map, ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to declare faults for deadline {}", deadline_idx), - ) + .with_context(|| { + format!("failed to declare faults for deadline {}", deadline_idx) })?; - deadlines.update_deadline(policy, store, deadline_idx, &deadline).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to store deadline {} partitions", deadline_idx), - ) - })?; + deadlines.update_deadline(policy, store, deadline_idx, &deadline)?; new_fault_power_total += &deadline_power_delta; } - state.save_deadlines(store, deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; - + state.save_deadlines(store, deadlines)?; Ok(new_fault_power_total) })?; @@ -2748,24 +2472,21 @@ impl Actor { let deadline = term.deadline; let partition = term.partition; - to_process.add(rt.policy(), deadline, partition, term.sectors).map_err(|e| { - actor_error!( - illegal_argument, - "failed to process deadline {}, partition {}: {}", - deadline, - partition, - e - ) - })?; + to_process + .add(rt.policy(), deadline, partition, term.sectors) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("failed to process deadline {}, partition {}", deadline, partition,) + })?; } { let policy = rt.policy(); to_process .check(policy.addressed_partitions_max, policy.addressed_sectors_max) - .map_err(|e| { - actor_error!(illegal_argument, "cannot process requested parameters: {}", e) - })?; + .context_code( + ExitCode::USR_ILLEGAL_ARGUMENT, + "cannot process requested parameters", + )?; } let fee_to_burn = rt.transaction(|state: &mut State, rt| { @@ -2773,7 +2494,8 @@ impl Actor { // and repay fee debt now. let fee_to_burn = repay_debts_or_abort(rt, state)?; - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -2788,12 +2510,9 @@ impl Actor { let store = rt.store(); - let mut deadlines = - state.load_deadlines(store).map_err(|e| e.wrap("failed to load deadlines"))?; + let mut deadlines = state.load_deadlines(store)?; - let sectors = Sectors::load(store, &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors array") - })?; + let sectors = Sectors::load(store, &state.sectors)?; let curr_epoch = rt.curr_epoch(); for (deadline_idx, partition_map) in to_process.iter() { let policy = rt.policy(); @@ -2803,59 +2522,33 @@ impl Actor { deadline_idx, curr_epoch, ) - .map_err(|e| { - actor_error!( - illegal_argument, - "invalid recovery declaration deadline {}: {}", - deadline_idx, - e - ) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("invalid recovery declaration deadline {}", deadline_idx,) })?; - validate_fr_declaration_deadline(&target_deadline).map_err(|e| { - actor_error!( - illegal_argument, - "failed recovery declaration at deadline {}: {}", - deadline_idx, - e - ) - })?; - - let mut deadline = - deadlines.load_deadline(policy, store, deadline_idx).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", deadline_idx), - ) + validate_fr_declaration_deadline(&target_deadline) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!("failed recovery declaration at deadline {}", deadline_idx,) })?; + let mut deadline = deadlines.load_deadline(policy, store, deadline_idx)?; + deadline .declare_faults_recovered(store, §ors, info.sector_size, partition_map) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to declare recoveries for deadline {}", deadline_idx), - ) + .with_context(|| { + format!("failed to declare recoveries for deadline {}", deadline_idx) })?; - deadlines.update_deadline(policy, store, deadline_idx, &deadline).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to store deadline {}", deadline_idx), - ) - })?; + deadlines.update_deadline(policy, store, deadline_idx, &deadline)?; } - state.save_deadlines(store, deadlines).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save deadlines") - })?; - + state.save_deadlines(store, deadlines)?; Ok(fee_to_burn) })?; burn_funds(rt, fee_to_burn)?; let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; // Power is not restored yet, but when the recovered sectors are successfully PoSted. Ok(()) @@ -2878,15 +2571,17 @@ impl Actor { } } - let partitions = params.partitions.validate().map_err(|e| { - actor_error!(illegal_argument, "failed to parse partitions bitfield: {}", e) - })?; + let partitions = params + .partitions + .validate() + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "failed to parse partitions bitfield")?; let partition_count = partitions.len(); let params_deadline = params.deadline; rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -2923,48 +2618,27 @@ impl Actor { } let quant = state.quant_spec_for_deadline(policy, params_deadline); - let mut deadlines = - state.load_deadlines(store).map_err(|e| e.wrap("failed to load deadlines"))?; - - let mut deadline = - deadlines.load_deadline(policy, store, params_deadline).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load deadline {}", params_deadline), - ) - })?; + let mut deadlines = state.load_deadlines(store)?; + + let mut deadline = deadlines.load_deadline(policy, store, params_deadline)?; let (live, dead, removed_power) = - deadline.remove_partitions(store, partitions, quant).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to remove partitions from deadline {}", params_deadline), - ) + deadline.remove_partitions(store, partitions, quant).with_context(|| { + format!("failed to remove partitions from deadline {}", params_deadline) })?; - state.delete_sectors(store, &dead).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to delete dead sectors") - })?; + state.delete_sectors(store, &dead)?; - let sectors = state.load_sector_infos(store, &live).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load moved sectors") - })?; + let sectors = state.load_sector_infos(store, &live)?; let proven = true; - let added_power = deadline - .add_sectors( - store, - info.window_post_partition_sectors, - proven, - §ors, - info.sector_size, - quant, - ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to add back moved sectors", - ) - })?; + let added_power = deadline.add_sectors( + store, + info.window_post_partition_sectors, + proven, + §ors, + info.sector_size, + quant, + )?; if removed_power != added_power { return Err(actor_error!( @@ -2975,20 +2649,8 @@ impl Actor { )); } - deadlines.update_deadline(policy, store, params_deadline, &deadline).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to update deadline {}", params_deadline), - ) - })?; - - state.save_deadlines(store, deadlines).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to save deadline {}", params_deadline), - ) - })?; - + deadlines.update_deadline(policy, store, params_deadline, &deadline)?; + state.save_deadlines(store, deadlines)?; Ok(()) })?; @@ -3011,7 +2673,7 @@ impl Actor { let mask_sector_numbers = params .mask_sector_numbers .validate() - .map_err(|e| actor_error!(illegal_argument, "invalid mask bitfield: {}", e))?; + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "invalid mask bitfield")?; let last_sector_number = mask_sector_numbers .last() @@ -3027,7 +2689,8 @@ impl Actor { } rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), @@ -3068,9 +2731,9 @@ impl Actor { // This ensures the miner has sufficient funds to lock up amountToLock. // This should always be true if reward actor sends reward funds with the message. - let unlocked_balance = st.get_unlocked_balance(&rt.current_balance()).map_err(|e| { - actor_error!(illegal_state, "failed to calculate unlocked balance: {}", e) - })?; + let unlocked_balance = st + .get_unlocked_balance(&rt.current_balance()) + ?; if unlocked_balance < reward_to_lock { return Err(actor_error!( @@ -3081,21 +2744,16 @@ impl Actor { )); } - let newly_vested = st - .add_locked_funds( - rt.store(), - rt.curr_epoch(), - &reward_to_lock, - locked_reward_vesting_spec, - ) - .map_err(|e| { - actor_error!(illegal_state, "failed to lock funds in vesting table: {}", e) - })?; + let newly_vested = st.add_locked_funds( + rt.store(), + rt.curr_epoch(), + &reward_to_lock, + locked_reward_vesting_spec, + )?; pledge_delta_total -= &newly_vested; pledge_delta_total += &reward_to_lock; - st.apply_penalty(¶ms.penalty) - .map_err(|e| actor_error!(illegal_state, "failed to apply penalty: {}", e))?; + st.apply_penalty(¶ms.penalty)?; // Attempt to repay all fee debt in this call. In most cases the miner will have enough // funds in the *reward alone* to cover the penalty. In the rare case a miner incurs more @@ -3105,10 +2763,7 @@ impl Actor { rt.store(), rt.curr_epoch(), &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to repay penalty") - })?; + )?; pledge_delta_total -= &penalty_from_vesting; let to_burn = penalty_from_vesting + penalty_from_balance; Ok((pledge_delta_total, to_burn)) @@ -3117,7 +2772,7 @@ impl Actor { notify_pledge_changed(rt, &pledge_delta_total)?; burn_funds(rt, to_burn)?; let st: State = rt.state()?; - st.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + st.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -3133,7 +2788,7 @@ impl Actor { let fault = rt .verify_consensus_fault(¶ms.header1, ¶ms.header2, ¶ms.header_extra) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_ARGUMENT, "fault not verified"))? + .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "fault not verified")? .ok_or_else(|| actor_error!(illegal_argument, "No consensus fault found"))?; if fault.target != rt.message().receiver() { return Err(actor_error!( @@ -3168,7 +2823,8 @@ impl Actor { let mut pledge_delta = TokenAmount::zero(); let (burn_amount, reward_amount) = rt.transaction(|st: &mut State, rt| { - let mut info = get_miner_info(rt.store(), st)?; + let store = rt.store(); + let mut info = st.get_info(store)?; // Verify miner hasn't already been faulted if fault.epoch < info.consensus_fault_elapsed { @@ -3180,9 +2836,7 @@ impl Actor { )); } - st.apply_penalty(&fault_penalty).map_err(|e| { - actor_error!(illegal_state, format!("failed to apply penalty: {}", e)) - })?; + st.apply_penalty(&fault_penalty)?; // Pay penalty let (penalty_from_vesting, penalty_from_balance) = st @@ -3190,10 +2844,7 @@ impl Actor { rt.store(), rt.curr_epoch(), &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to pay fees") - })?; + )?; let mut burn_amount = &penalty_from_vesting + &penalty_from_balance; pledge_delta -= penalty_from_vesting; @@ -3205,10 +2856,7 @@ impl Actor { info.consensus_fault_elapsed = rt.curr_epoch() + rt.policy().consensus_fault_ineligibility_duration; - st.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_SERIALIZATION, "failed to save miner info") - })?; - + st.save_info(rt.store(), &info)?; Ok((burn_amount, reward_amount)) })?; @@ -3222,7 +2870,7 @@ impl Actor { notify_pledge_changed(rt, &pledge_delta)?; let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -3240,7 +2888,9 @@ impl Actor { let (info, amount_withdrawn, newly_vested, fee_to_burn, state) = rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state + .get_info(store)?; // Only the owner or the beneficiary is allowed to withdraw the balance. rt.validate_immediate_caller_is(&[info.owner, info.beneficiary])?; @@ -3257,20 +2907,13 @@ impl Actor { // Unlock vested funds so we can spend them. let newly_vested = - state.unlock_vested_funds(rt.store(), rt.curr_epoch()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "Failed to vest fund") - })?; + state.unlock_vested_funds(rt.store(), rt.curr_epoch())?; // available balance already accounts for fee debt so it is correct to call // this before RepayDebts. We would have to // subtract fee debt explicitly if we called this after. let available_balance = - state.get_available_balance(&rt.current_balance()).map_err(|e| { - actor_error!( - illegal_state, - format!("failed to calculate available balance: {}", e) - ) - })?; + state.get_available_balance(&rt.current_balance())?; // Verify unlocked funds cover both InitialPledgeRequirement and FeeDebt // and repay fee debt now. @@ -3299,12 +2942,7 @@ impl Actor { amount_withdrawn = std::cmp::min(amount_withdrawn, &remaining_quota); if amount_withdrawn.is_positive() { info.beneficiary_term.used_quota += amount_withdrawn; - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to save miner info", - ) - })?; + state.save_info(rt.store(), &info)?; } Ok((info, amount_withdrawn.clone(), newly_vested, fee_to_burn, state.clone())) } else { @@ -3324,7 +2962,7 @@ impl Actor { burn_funds(rt, fee_to_burn)?; notify_pledge_changed(rt, &newly_vested.neg())?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(WithdrawBalanceReturn { amount_withdrawn }) } @@ -3348,7 +2986,9 @@ impl Actor { })?); rt.transaction(|state: &mut State, rt| { - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state + .get_info(store)?; if caller == info.owner { // This is a ChangeBeneficiary proposal when the caller is Owner if new_beneficiary != info.owner { @@ -3452,9 +3092,7 @@ impl Actor { } } - state.save_info(rt.store(), &info).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save miner info") - })?; + state.save_info(rt.store(), &info)?; Ok(()) }) } @@ -3465,7 +3103,8 @@ impl Actor { fn get_beneficiary(rt: &impl Runtime) -> Result { rt.validate_immediate_caller_accept_any()?; let st: State = rt.state()?; - let info = get_miner_info(rt.store(), &st)?; + let store = rt.store(); + let info = st.get_info(store)?; Ok(GetBeneficiaryReturn { active: ActiveBeneficiary { @@ -3478,21 +3117,18 @@ impl Actor { fn repay_debt(rt: &impl Runtime) -> Result<(), ActorError> { let (from_vesting, from_balance, state) = rt.transaction(|state: &mut State, rt| { - let info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let info = state.get_info(store)?; rt.validate_immediate_caller_is( info.control_addresses.iter().chain(&[info.worker, info.owner]), )?; // Repay as much fee debt as possible. - let (from_vesting, from_balance) = state - .repay_partial_debt_in_priority_order( - rt.store(), - rt.curr_epoch(), - &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to unlock fee debt") - })?; + let (from_vesting, from_balance) = state.repay_partial_debt_in_priority_order( + rt.store(), + rt.curr_epoch(), + &rt.current_balance(), + )?; Ok((from_vesting, from_balance, state.clone())) })?; @@ -3501,7 +3137,7 @@ impl Actor { notify_pledge_changed(rt, &from_vesting.neg())?; burn_funds(rt, burn_amount)?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(()) } @@ -3511,12 +3147,8 @@ impl Actor { ) -> Result<(), ActorError> { rt.validate_immediate_caller_is(std::iter::once(&STORAGE_POWER_ACTOR_ADDR))?; - let payload: CronEventPayload = from_slice(¶ms.event_payload).map_err(|e| { - actor_error!( - illegal_state, - format!("failed to unmarshal miner cron payload into expected structure: {}", e) - ) - })?; + let payload: CronEventPayload = from_slice(¶ms.event_payload) + .or_illegal_state("failed to unmarshal miner cron payload into expected structure")?; match payload.event_type { CRON_EVENT_PROVING_DEADLINE => handle_proving_deadline( @@ -3538,7 +3170,7 @@ impl Actor { } }; let state: State = rt.state()?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok(()) } } @@ -3929,19 +3561,12 @@ fn process_early_terminations( let store = rt.store(); let policy = rt.policy(); - let (result, more) = state - .pop_early_terminations( - policy, - store, - policy.addressed_partitions_max, - policy.addressed_sectors_max, - ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to pop early terminations", - ) - })?; + let (result, more) = state.pop_early_terminations( + policy, + store, + policy.addressed_partitions_max, + policy.addressed_sectors_max, + )?; // Nothing to do, don't waste any time. // This can happen if we end up processing early terminations @@ -3951,10 +3576,9 @@ fn process_early_terminations( return Ok((result, more, Vec::new(), TokenAmount::zero(), TokenAmount::zero())); } - let info = get_miner_info(rt.store(), state)?; - let sectors = Sectors::load(store, &state.sectors).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load sectors array") - })?; + let store1 = rt.store(); + let info = state.get_info(store1)?; + let sectors = Sectors::load(store, &state.sectors)?; let mut total_initial_pledge = TokenAmount::zero(); let mut deals_to_terminate = @@ -3964,9 +3588,7 @@ fn process_early_terminations( let mut penalty = TokenAmount::zero(); for (epoch, sector_numbers) in result.iter() { - let sectors = sectors - .load_sector(sector_numbers) - .map_err(|e| e.wrap("failed to load sector infos"))?; + let sectors = sectors.load_sector(sector_numbers)?; penalty += termination_penalty( info.sector_size, @@ -3988,15 +3610,11 @@ fn process_early_terminations( } // Pay penalty - state - .apply_penalty(&penalty) - .map_err(|e| actor_error!(illegal_state, "failed to apply penalty: {}", e))?; + state.apply_penalty(&penalty)?; // Remove pledge requirement. let mut pledge_delta = -total_initial_pledge; - state.add_initial_pledge(&pledge_delta).map_err(|e| { - actor_error!(illegal_state, "failed to add initial pledge {}: {}", pledge_delta, e) - })?; + state.add_initial_pledge(&pledge_delta)?; // Use unlocked pledge to pay down outstanding fee debt let (penalty_from_vesting, penalty_from_balance) = state @@ -4004,10 +3622,7 @@ fn process_early_terminations( rt.store(), rt.curr_epoch(), &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to repay penalty") - })?; + )?; penalty = &penalty_from_vesting + penalty_from_balance; pledge_delta -= penalty_from_vesting; @@ -4061,27 +3676,18 @@ fn handle_proving_deadline( // Vest locked funds. // This happens first so that any subsequent penalties are taken // from locked vesting funds before funds free this epoch. - let newly_vested = state - .unlock_vested_funds(rt.store(), rt.curr_epoch()) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to vest funds"))?; + let newly_vested = state.unlock_vested_funds(rt.store(), rt.curr_epoch())?; pledge_delta_total -= newly_vested; // Process pending worker change if any - let mut info = get_miner_info(rt.store(), state)?; + let store = rt.store(); + let mut info = state.get_info(store)?; process_pending_worker(&mut info, rt, state)?; - let deposit_to_burn = state - .cleanup_expired_pre_commits(policy, rt.store(), rt.curr_epoch()) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to expire pre-committed sectors", - ) - })?; - state - .apply_penalty(&deposit_to_burn) - .map_err(|e| actor_error!(illegal_state, "failed to apply penalty: {}", e))?; + let deposit_to_burn = + state.cleanup_expired_pre_commits(policy, rt.store(), rt.curr_epoch())?; + state.apply_penalty(&deposit_to_burn)?; log::debug!( "storage provider {} penalized {} for expired pre commits", @@ -4093,9 +3699,7 @@ fn handle_proving_deadline( // That way, don't re-schedule a cron callback if one is already scheduled. had_early_terminations = have_pending_early_terminations(state); - let result = state.advance_deadline(policy, rt.store(), rt.curr_epoch()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to advance deadline") - })?; + let result = state.advance_deadline(policy, rt.store(), rt.curr_epoch())?; // Faults detected by this missed PoSt pay no penalty, but sectors that were already faulty // and remain faulty through this deadline pay the fault fee. @@ -4108,9 +3712,7 @@ fn handle_proving_deadline( power_delta_total += &result.power_delta; pledge_delta_total += &result.pledge_delta; - state - .apply_penalty(&penalty_target) - .map_err(|e| actor_error!(illegal_state, "failed to apply penalty: {}", e))?; + state.apply_penalty(&penalty_target)?; log::debug!( "storage provider {} penalized {} for continued fault", @@ -4123,10 +3725,7 @@ fn handle_proving_deadline( rt.store(), rt.curr_epoch(), &rt.current_balance(), - ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to unlock penalty") - })?; + )?; penalty_total = &penalty_from_vesting + penalty_from_balance; pledge_delta_total -= penalty_from_vesting; @@ -4268,7 +3867,7 @@ fn request_update_power(rt: &impl Runtime, delta: PowerPair) -> Result<(), Actor })?, TokenAmount::zero(), )) - .map_err(|e| e.wrap(format!("failed to update power with {:?}", delta_clone)))?; + .with_context(|| format!("failed to update power with {:?}", delta_clone))?; Ok(()) } @@ -4443,7 +4042,7 @@ fn request_current_epoch_block_reward( Default::default(), TokenAmount::zero(), )) - .map_err(|e| e.wrap("failed to check epoch baseline power"))?, + .context("failed to check epoch baseline power")?, ) } @@ -4458,7 +4057,7 @@ fn request_current_total_power( Default::default(), TokenAmount::zero(), )) - .map_err(|e| e.wrap("failed to check current power"))?, + .context("failed to check current power")?, ) } @@ -4678,15 +4277,6 @@ pub fn power_for_sectors(sector_size: SectorSize, sectors: &[SectorOnChainInfo]) PowerPair { raw: BigInt::from(sector_size as u64) * BigInt::from(sectors.len()), qa } } -fn get_miner_info(store: &BS, state: &State) -> Result -where - BS: Blockstore, -{ - state - .get_info(store) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "could not read miner info")) -} - fn process_pending_worker( info: &mut MinerInfo, rt: &impl Runtime, @@ -4705,9 +4295,7 @@ fn process_pending_worker( info.worker = pending_worker_key.new_worker; info.pending_worker_key = None; - state - .save_info(rt.store(), info) - .map_err(|e| e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to save miner info")) + state.save_info(rt.store(), info) } /// Repays all fee debt and then verifies that the miner has amount needed to cover @@ -4718,9 +4306,7 @@ fn process_pending_worker( /// almost always redundant since vesting is quantized to ~daily units. Vesting /// will be at most one proving period old if computed in the cron callback. fn repay_debts_or_abort(rt: &impl Runtime, state: &mut State) -> Result { - let res = state.repay_debts(&rt.current_balance()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "unlocked balance can not repay fee debt") - })?; + let res = state.repay_debts(&rt.current_balance())?; info!("RepayDebtsOrAbort was called and succeeded"); Ok(res) } @@ -4817,7 +4403,7 @@ fn confirm_sector_proofs_valid_internal( let (total_pledge, newly_vested) = rt.transaction(|state: &mut State, rt| { let policy = rt.policy(); let store = rt.store(); - let info = get_miner_info(store, state)?; + let info = state.get_info(store)?; let mut new_sector_numbers = Vec::::with_capacity(activated_sectors.len()); let mut deposit_to_unlock = TokenAmount::zero(); @@ -4898,40 +4484,27 @@ fn confirm_sector_proofs_valid_internal( new_sectors.push(new_sector_info); } - state.put_sectors(store, new_sectors.clone()).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to put new sectors") - })?; + state.put_sectors(store, new_sectors.clone())?; - state.delete_precommitted_sectors(store, &new_sector_numbers).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to delete precommited sectors") - })?; + state.delete_precommitted_sectors(store, &new_sector_numbers)?; - state - .assign_sectors_to_deadlines( - policy, - store, - rt.curr_epoch(), - new_sectors, - info.window_post_partition_sectors, - info.sector_size, - ) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to assign new sectors to deadlines", - ) - })?; + state.assign_sectors_to_deadlines( + policy, + store, + rt.curr_epoch(), + new_sectors, + info.window_post_partition_sectors, + info.sector_size, + )?; let newly_vested = TokenAmount::zero(); // Unlock deposit for successful proofs, make it available for lock-up as initial pledge. state .add_pre_commit_deposit(&(-deposit_to_unlock)) - .map_err(|e| actor_error!(illegal_state, "failed to add precommit deposit: {}", e))?; + ?; - let unlocked_balance = state.get_unlocked_balance(&rt.current_balance()).map_err(|e| { - actor_error!(illegal_state, "failed to calculate unlocked balance: {}", e) - })?; + let unlocked_balance = state.get_unlocked_balance(&rt.current_balance())?; if unlocked_balance < total_pledge { return Err(actor_error!( insufficient_funds, @@ -4943,9 +4516,9 @@ fn confirm_sector_proofs_valid_internal( state .add_initial_pledge(&total_pledge) - .map_err(|e| actor_error!(illegal_state, "failed to add initial pledge: {}", e))?; + ?; - state.check_balance_invariants(&rt.current_balance()).map_err(balance_invariants_broken)?; + state.check_balance_invariants(&rt.current_balance())?; Ok((total_pledge, newly_vested)) })?; @@ -5078,14 +4651,6 @@ fn batch_activate_deals_and_claim_allocations( Ok((batch_activation_res.activation_results, activation_and_claim_results)) } -// XXX: probably better to push this one level down into state -fn balance_invariants_broken(e: Error) -> ActorError { - ActorError::unchecked( - ERR_BALANCE_INVARIANTS_BROKEN, - format!("balance invariants broken: {}", e), - ) -} - impl ActorCode for Actor { type Methods = Method; diff --git a/actors/miner/src/partition_state.rs b/actors/miner/src/partition_state.rs index 9ff3c3e72..59905b5a7 100644 --- a/actors/miner/src/partition_state.rs +++ b/actors/miner/src/partition_state.rs @@ -4,10 +4,7 @@ use std::convert::TryInto; use std::ops::{self, Neg}; -use anyhow::{anyhow, Context}; use cid::Cid; -use fil_actors_runtime::runtime::Policy; -use fil_actors_runtime::{actor_error, ActorDowncast, Array}; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; @@ -18,6 +15,9 @@ use fvm_shared::error::ExitCode; use fvm_shared::sector::{SectorSize, StoragePower}; use num_traits::{Signed, Zero}; +use fil_actors_runtime::runtime::Policy; +use fil_actors_runtime::{actor_error, ActorError, Array, AsActorError, AsActorErrors}; + use super::{ power_for_sectors, select_sectors, validate_partition_contains_sectors, BitFieldQueue, ExpirationQueue, ExpirationSet, SectorOnChainInfo, Sectors, TerminationResult, @@ -65,15 +65,17 @@ pub struct Partition { } impl Partition { - pub fn new(store: &BS) -> anyhow::Result { + pub fn new(store: &BS) -> Result { let empty_expiration_array = Array::::new_with_bit_width(store, PARTITION_EXPIRATION_AMT_BITWIDTH) - .flush()?; + .flush() + .or_illegal_state("failed to create empty array")?; let empty_early_termination_array = Array::::new_with_bit_width( store, PARTITION_EARLY_TERMINATION_ARRAY_AMT_BITWIDTH, ) - .flush()?; + .flush() + .or_illegal_state("failed to create empty array")?; Ok(Self { sectors: BitField::new(), @@ -116,21 +118,19 @@ impl Partition { sectors: &[SectorOnChainInfo], sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { let mut expirations = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load sector expirations"))?; + .or_illegal_state("failed to load sector expirations")?; let (sector_numbers, power, _) = expirations .add_active_sectors(sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to record new sector expirations"))?; + .or_illegal_state("failed to record new sector expirations")?; - self.expirations_epochs = expirations - .amt - .flush() - .map_err(|e| e.downcast_wrap("failed to store sector expirations"))?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to store sector expirations")?; if self.sectors.contains_any(§or_numbers) { - return Err(anyhow!("not all added sectors are new")); + return Err(actor_error!(assertion_failed, "not all added sectors are new")); } // Update other metadata using the calculated totals. @@ -159,19 +159,19 @@ impl Partition { fault_expiration: ChainEpoch, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result<(PowerPair, PowerPair)> { + ) -> Result<(PowerPair, PowerPair), ActorError> { // Load expiration queue let mut queue = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load partition queue"))?; + .or_illegal_state("failed to load partition queue")?; // Reschedule faults - let new_faulty_power = - queue - .reschedule_as_faults(fault_expiration, sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to add faults to partition queue"))?; + let new_faulty_power = queue + .reschedule_as_faults(fault_expiration, sectors, sector_size) + .or_illegal_state("failed to add faults to partition queue")?; // Save expiration queue - self.expirations_epochs = queue.amt.flush()?; + self.expirations_epochs = + queue.amt.flush().or_illegal_state("failed to save expiration queue")?; // Update partition metadata self.faults |= sector_numbers; @@ -187,8 +187,7 @@ impl Partition { let mut power_delta = new_faulty_power.clone().neg(); - let unproven_infos = select_sectors(sectors, &unproven) - .map_err(|e| e.downcast_wrap("failed to select unproven sectors"))?; + let unproven_infos = select_sectors(sectors, &unproven)?; if !unproven_infos.is_empty() { let lost_unproven_power = power_for_sectors(sector_size, &unproven_infos); self.unproven_power -= &lost_unproven_power; @@ -217,7 +216,7 @@ impl Partition { fault_expiration_epoch: ChainEpoch, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result<(BitField, PowerPair, PowerPair)> { + ) -> Result<(BitField, PowerPair, PowerPair), ActorError> { validate_partition_contains_sectors(self, sector_numbers) .map_err(|e| actor_error!(illegal_argument; "failed fault declaration: {}", e))?; @@ -242,7 +241,7 @@ impl Partition { sector_size, quant, ) - .map_err(|e| e.downcast_wrap("failed to add faults"))? + .or_illegal_state("failed to add faults")? } else { Default::default() }; @@ -273,24 +272,25 @@ impl Partition { sectors: &Sectors<'_, BS>, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { // Process recoveries, assuming the proof will be successful. // This similarly updates state. let recovered_sectors = sectors .load_sector(&self.recoveries) - .map_err(|e| e.wrap("failed to load recovered sectors"))?; + .or_illegal_state("failed to load recovered sectors")?; // Load expiration queue let mut queue = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| anyhow!("failed to load partition queue: {:?}", e))?; + .or_illegal_state("failed to load partition queue")?; // Reschedule recovered let power = queue .reschedule_recovered(recovered_sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to reschedule faults in partition queue"))?; + .or_illegal_state("failed to reschedule faults in partition queue")?; // Save expiration queue - self.expirations_epochs = queue.amt.flush()?; + self.expirations_epochs = + queue.amt.flush().or_illegal_state("failed to save expirations")?; // Update partition metadata self.faults -= &self.recoveries; @@ -319,7 +319,7 @@ impl Partition { sectors: &Sectors<'_, BS>, sector_size: SectorSize, sector_numbers: &BitField, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { // Check that the declared sectors are actually assigned to the partition. validate_partition_contains_sectors(self, sector_numbers) .map_err(|e| actor_error!(illegal_argument; "failed fault declaration: {}", e))?; @@ -377,7 +377,7 @@ impl Partition { sector_numbers: &BitField, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result> { + ) -> Result, ActorError> { // Ensure these sectors actually belong to this partition. let present = sector_numbers & &self.sectors; @@ -389,9 +389,12 @@ impl Partition { let sector_infos = sectors.load_sector(&active)?; let mut expirations = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load sector expirations"))?; - expirations.reschedule_expirations(new_expiration, §or_infos, sector_size)?; - self.expirations_epochs = expirations.amt.flush()?; + .or_illegal_state("failed to load sector expirations")?; + expirations + .reschedule_expirations(new_expiration, §or_infos, sector_size) + .or_illegal_state("failed to reschedule expirations")?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to save expiration queue")?; // check invariants self.validate_state()?; @@ -411,25 +414,24 @@ impl Partition { new_sectors: &[SectorOnChainInfo], sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result<(PowerPair, TokenAmount)> { + ) -> Result<(PowerPair, TokenAmount), ActorError> { let mut expirations = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load sector expirations"))?; + .or_illegal_state("failed to load sector expirations")?; let (old_sector_numbers, new_sector_numbers, power_delta, pledge_delta) = expirations .replace_sectors(old_sectors, new_sectors, sector_size) - .map_err(|e| e.downcast_wrap("failed to replace sector expirations"))?; + .or_illegal_state("failed to replace sector expirations")?; - self.expirations_epochs = expirations - .amt - .flush() - .map_err(|e| e.downcast_wrap("failed to save sector expirations"))?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to save sector expirations")?; // Check the sectors being removed are active (alive, not faulty). let active = self.active_sectors(); let all_active = active.contains_all(&old_sector_numbers); if !all_active { - return Err(anyhow!( + return Err(actor_error!( + assertion_failed, "refusing to replace inactive sectors in {:?} (active: {:?})", old_sector_numbers, active @@ -455,19 +457,19 @@ impl Partition { store: &BS, epoch: ChainEpoch, sectors: &BitField, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { let mut early_termination_queue = BitFieldQueue::new(store, &self.early_terminated, NO_QUANTIZATION) - .map_err(|e| e.downcast_wrap("failed to load early termination queue"))?; + .or_illegal_state("failed to load early termination queue")?; early_termination_queue .add_to_queue(epoch, sectors) - .map_err(|e| e.downcast_wrap("failed to add to early termination queue"))?; + .or_illegal_state("failed to add to early termination queue")?; self.early_terminated = early_termination_queue .amt .flush() - .map_err(|e| e.downcast_wrap("failed to save early termination queue"))?; + .or_illegal_state("failed to save early termination queue")?; Ok(()) } @@ -485,30 +487,28 @@ impl Partition { sector_numbers: &BitField, sector_size: SectorSize, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { let live_sectors = self.live_sectors(); if !live_sectors.contains_all(sector_numbers) { - return Err(actor_error!(illegal_argument, "can only terminate live sectors").into()); + return Err(actor_error!(illegal_argument, "can only terminate live sectors")); } let sector_infos = sectors.load_sector(sector_numbers)?; let mut expirations = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load sector expirations"))?; + .or_illegal_state("failed to load sector expirations")?; let (mut removed, removed_recovering) = expirations .remove_sectors(policy, §or_infos, &self.faults, &self.recoveries, sector_size) - .map_err(|e| e.downcast_wrap("failed to remove sector expirations"))?; + .or_illegal_state("failed to remove sector expirations")?; - self.expirations_epochs = expirations - .amt - .flush() - .map_err(|e| e.downcast_wrap("failed to save sector expirations"))?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to save sector expirations")?; let removed_sectors = &removed.on_time_sectors | &removed.early_sectors; // Record early termination. self.record_early_termination(store, epoch, &removed_sectors) - .map_err(|e| e.downcast_wrap("failed to record early sector termination"))?; + .or_illegal_state("failed to record early sector termination")?; let unproven_nos = &removed_sectors & &self.unproven; @@ -541,21 +541,23 @@ impl Partition { store: &BS, until: ChainEpoch, quant: QuantSpec, - ) -> anyhow::Result { + ) -> Result { // This is a sanity check to make sure we handle proofs _before_ // handling sector expirations. if !self.unproven.is_empty() { - return Err(anyhow!( + return Err(actor_error!( + assertion_failed, "cannot pop expired sectors from a partition with unproven sectors" )); } let mut expirations = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load expiration queue"))?; - let popped = expirations.pop_until(until).map_err(|e| { - e.downcast_wrap(format!("failed to pop expiration queue until {}", until)) - })?; - self.expirations_epochs = expirations.amt.flush()?; + .or_illegal_state("failed to load expiration queue")?; + let popped = expirations + .pop_until(until) + .or_with_illegal_state(|| format!("failed to pop expiration queue until {}", until))?; + self.expirations_epochs = + expirations.amt.flush().or_illegal_state("failed to save expirations")?; let expired_sectors = &popped.on_time_sectors | &popped.early_sectors; @@ -564,15 +566,21 @@ impl Partition { // and all recoveries retracted. // No recoveries may be posted until the deadline is closed. if !self.recoveries.is_empty() { - return Err(anyhow!("unexpected recoveries while processing expirations")); + return Err(actor_error!( + illegal_state, + "unexpected recoveries while processing expirations" + )); } if !self.recovering_power.is_zero() { - return Err(anyhow!("unexpected recovering power while processing expirations")); + return Err(actor_error!( + illegal_state, + "unexpected recovering power while processing expirations" + )); } // Nothing expiring now should have already terminated. if self.terminated.contains_any(&expired_sectors) { - return Err(anyhow!("expiring sectors already terminated")); + return Err(actor_error!(illegal_state, "expiring sectors already terminated")); } // Mark the sectors as terminated and subtract sector power. @@ -583,7 +591,7 @@ impl Partition { // Record the epoch of any sectors expiring early, for termination fee calculation later. self.record_early_termination(store, until, &popped.early_sectors) - .map_err(|e| e.downcast_wrap("failed to record early terminations"))?; + .or_illegal_state("failed to record early terminations")?; // check invariants self.validate_state()?; @@ -599,18 +607,19 @@ impl Partition { store: &BS, fault_expiration: ChainEpoch, quant: QuantSpec, - ) -> anyhow::Result<(PowerPair, PowerPair, PowerPair)> { + ) -> Result<(PowerPair, PowerPair, PowerPair), ActorError> { // Collapse tail of queue into the last entry, and mark all power faulty. // Load expiration queue let mut queue = ExpirationQueue::new(store, &self.expirations_epochs, quant) - .map_err(|e| e.downcast_wrap("failed to load partition queue"))?; + .or_illegal_state("failed to load partition queue")?; queue .reschedule_all_as_faults(fault_expiration) - .map_err(|e| e.downcast_wrap("failed to reschedule all as faults"))?; + .or_illegal_state("failed to reschedule all as faults")?; // Save expiration queue - self.expirations_epochs = queue.amt.flush()?; + self.expirations_epochs = + queue.amt.flush().or_illegal_state("failed to save expiration queue")?; // Compute faulty power for penalization. New faulty power is the total power minus already faulty. let new_faulty_power = &self.live_power - &self.faulty_power; @@ -640,10 +649,11 @@ impl Partition { &mut self, store: &BS, max_sectors: u64, - ) -> anyhow::Result<(TerminationResult, /* has more */ bool)> { + ) -> Result<(TerminationResult, /* has more */ bool), ActorError> { // Load early terminations. let mut early_terminated_queue = - BitFieldQueue::new(store, &self.early_terminated, NO_QUANTIZATION)?; + BitFieldQueue::new(store, &self.early_terminated, NO_QUANTIZATION) + .or_illegal_state("failed to load early termination queue")?; let mut processed = Vec::::new(); let mut remaining: Option<(BitField, ChainEpoch)> = None; @@ -658,8 +668,10 @@ impl Partition { let limit = max_sectors - result.sectors_processed; let to_process = if limit < count { - let to_process = - sectors.slice(0, limit).context("expected more sectors in bitfield")?; + let to_process = sectors.slice(0, limit).context_code( + ExitCode::USR_ASSERTION_FAILED, + "expected more sectors in bitfield", + )?; let rest = sectors - &to_process; remaining = Some((rest, epoch)); result.sectors_processed += limit; @@ -675,24 +687,26 @@ impl Partition { let keep_going = result.sectors_processed < max_sectors; Ok(keep_going) }) - .map_err(|e| e.downcast_wrap("failed to walk early terminations queue"))?; + .or_illegal_state("failed to walk early terminations queue")?; // Update early terminations - early_terminated_queue.amt.batch_delete(processed, true).map_err(|e| { - e.downcast_wrap("failed to remove entries from early terminations queue") - })?; + early_terminated_queue + .amt + .batch_delete(processed, true) + .or_illegal_state("failed to remove entries from early terminations queue")?; if let Some((remaining_sectors, remaining_epoch)) = remaining.take() { - early_terminated_queue.amt.set(remaining_epoch as u64, remaining_sectors).map_err( - |e| e.downcast_wrap("failed to update remaining entry early terminations queue"), - )?; + early_terminated_queue + .amt + .set(remaining_epoch as u64, remaining_sectors) + .or_illegal_state("failed to update remaining entry early terminations queue")?; } // Save early terminations. self.early_terminated = early_terminated_queue .amt .flush() - .map_err(|e| e.downcast_wrap("failed to store early terminations queue"))?; + .or_illegal_state("failed to store early terminations queue")?; // check invariants self.validate_state()?; @@ -715,7 +729,7 @@ impl Partition { quant: QuantSpec, fault_expiration: ChainEpoch, skipped: &BitField, - ) -> anyhow::Result<(PowerPair, PowerPair, PowerPair, bool)> { + ) -> Result<(PowerPair, PowerPair, PowerPair, bool), ActorError> { if skipped.is_empty() { return Ok((PowerPair::zero(), PowerPair::zero(), PowerPair::zero(), false)); } @@ -726,7 +740,7 @@ impl Partition { illegal_argument, "skipped faults contains sectors outside partition" ) - .into()); + ); } // Find all skipped faults that have been labeled recovered @@ -751,9 +765,7 @@ impl Partition { sector_size, quant, ) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to add skipped faults") - })?; + .or_illegal_state("failed to add skipped faults")?; // Remove faulty recoveries self.remove_recoveries(&retracted_recoveries, &retracted_recovery_power); @@ -765,61 +777,70 @@ impl Partition { } /// Test invariants about the partition power are valid. - pub fn validate_power_state(&self) -> anyhow::Result<()> { + pub fn validate_power_state(&self) -> Result<(), ActorError> { if self.live_power.raw.is_negative() || self.live_power.qa.is_negative() { - return Err(anyhow!("Partition left with negative live power")); + return Err(actor_error!(illegal_state, "Partition left with negative live power")); } if self.unproven_power.raw.is_negative() || self.unproven_power.qa.is_negative() { - return Err(anyhow!("Partition left with negative unproven power")); + return Err(actor_error!(illegal_state, "Partition left with negative unproven power")); } if self.faulty_power.raw.is_negative() || self.faulty_power.qa.is_negative() { - return Err(anyhow!("Partition left with negative faulty power")); + return Err(actor_error!(illegal_state, "Partition left with negative faulty power")); } if self.recovering_power.raw.is_negative() || self.recovering_power.qa.is_negative() { - return Err(anyhow!("Partition left with negative recovering power")); + return Err(actor_error!( + illegal_state, + "Partition left with negative recovering power" + )); } if self.unproven_power.raw > self.live_power.raw { - return Err(anyhow!("Partition left with invalid unproven power")); + return Err(actor_error!(illegal_state, "Partition left with invalid unproven power")); } if self.faulty_power.raw > self.live_power.raw { - return Err(anyhow!("Partition left with invalid faulty power")); + return Err(actor_error!(illegal_state, "Partition left with invalid faulty power")); } // The first half of this conditional shouldn't matter, keeping for readability if self.recovering_power.raw > self.live_power.raw || self.recovering_power.raw > self.faulty_power.raw { - return Err(anyhow!("Partition left with invalid recovering power")); + return Err(actor_error!( + illegal_state, + "Partition left with invalid recovering power" + )); } Ok(()) } - pub fn validate_bf_state(&self) -> anyhow::Result<()> { + pub fn validate_bf_state(&self) -> Result<(), ActorError> { let mut merge = &self.unproven | &self.faults; // Unproven or faulty sectors should not be in terminated if self.terminated.contains_any(&merge) { - return Err(anyhow!("Partition left with terminated sectors in multiple states")); + return Err(actor_error!( + illegal_state, + "Partition left with terminated sectors in multiple states" + )); } merge |= &self.terminated; // All merged sectors should exist in partition sectors if !self.sectors.contains_all(&merge) { - return Err(anyhow!("Partition left with invalid sector state")); + return Err(actor_error!(illegal_state, "Partition left with invalid sector state")); } // All recoveries should exist in partition faults if !self.faults.contains_all(&self.recoveries) { - return Err(anyhow!("Partition left with invalid recovery state")); + return Err(actor_error!(illegal_state, "Partition left with invalid recovery state")); } Ok(()) } - pub fn validate_state(&self) -> anyhow::Result<()> { - self.validate_power_state()?; - self.validate_bf_state()?; + pub fn validate_state(&self) -> Result<(), ActorError> { + self.validate_power_state().or_illegal_state("invalid power state")?; + self.validate_bf_state().or_illegal_state("invalid bitfield state")?; Ok(()) } } diff --git a/actors/miner/src/sectors.rs b/actors/miner/src/sectors.rs index 40469b9de..3ef227a8f 100644 --- a/actors/miner/src/sectors.rs +++ b/actors/miner/src/sectors.rs @@ -3,14 +3,12 @@ use std::collections::BTreeSet; -use anyhow::anyhow; use cid::Cid; -use fil_actors_runtime::{actor_error, ActorDowncast, ActorError, Array}; -use fvm_ipld_amt::Error as AmtError; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; -use fvm_shared::error::ExitCode; -use fvm_shared::sector::{SectorNumber, MAX_SECTOR_NUMBER}; +use fvm_shared::sector::{MAX_SECTOR_NUMBER, SectorNumber}; + +use fil_actors_runtime::{actor_error, ActorError, Array, AsActorErrors}; use super::SectorOnChainInfo; @@ -19,8 +17,8 @@ pub struct Sectors<'db, BS> { } impl<'db, BS: Blockstore> Sectors<'db, BS> { - pub fn load(store: &'db BS, root: &Cid) -> Result { - Ok(Self { amt: Array::load(root, store)? }) + pub fn load(store: &'db BS, root: &Cid) -> Result { + Ok(Self { amt: Array::load(root, store).or_illegal_state("failed to load sectors")? }) } pub fn load_sector( @@ -32,12 +30,7 @@ impl<'db, BS: Blockstore> Sectors<'db, BS> { let sector_on_chain = self .amt .get(sector_number) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - format!("failed to load sector {}", sector_number), - ) - })? + .or_with_illegal_state(|| format!("failed to load sector {}", sector_number))? .cloned() .ok_or_else(|| actor_error!(not_found; "sector not found: {}", sector_number))?; sector_infos.push(sector_on_chain); @@ -45,32 +38,41 @@ impl<'db, BS: Blockstore> Sectors<'db, BS> { Ok(sector_infos) } - pub fn get(&self, sector_number: SectorNumber) -> anyhow::Result> { + pub fn get( + &self, + sector_number: SectorNumber, + ) -> Result, ActorError> { Ok(self .amt .get(sector_number) - .map_err(|e| e.downcast_wrap(format!("failed to get sector {}", sector_number)))? + .or_with_illegal_state(|| format!("failed to get sector {}", sector_number))? .cloned()) } - pub fn store(&mut self, infos: Vec) -> anyhow::Result<()> { + pub fn store(&mut self, infos: Vec) -> Result<(), ActorError> { for info in infos { let sector_number = info.sector_number; if sector_number > MAX_SECTOR_NUMBER { - return Err(anyhow!("sector number {} out of range", info.sector_number)); + return Err(actor_error!( + illegal_argument, + "sector number {} out of range", + info.sector_number + )); } - self.amt.set(sector_number, info).map_err(|e| { - e.downcast_wrap(format!("failed to store sector {}", sector_number)) - })?; + self.amt + .set(sector_number, info) + .or_with_illegal_state(|| format!("failed to store sector {}", sector_number))?; } Ok(()) } - pub fn must_get(&self, sector_number: SectorNumber) -> anyhow::Result { - self.get(sector_number)?.ok_or_else(|| anyhow!("sector {} not found", sector_number)) + pub fn must_get(&self, sector_number: SectorNumber) -> Result { + self.get(sector_number) + .or_with_illegal_state(|| format!("failed to load sector {}", sector_number))? + .ok_or_else(|| actor_error!(not_found, "sector {} not found", sector_number)) } /// Loads info for a set of sectors to be proven. @@ -80,7 +82,7 @@ impl<'db, BS: Blockstore> Sectors<'db, BS> { &self, proven_sectors: &BitField, expected_faults: &BitField, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let non_faults = proven_sectors - expected_faults; if non_faults.is_empty() { @@ -103,7 +105,7 @@ impl<'db, BS: Blockstore> Sectors<'db, BS> { sectors: &BitField, faults: &BitField, fault_stand_in: SectorNumber, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let stand_in_info = self.must_get(fault_stand_in)?; // Expand faults into a map for quick lookups. @@ -126,13 +128,17 @@ impl<'db, BS: Blockstore> Sectors<'db, BS> { pub fn select_sectors( sectors: &[SectorOnChainInfo], field: &BitField, -) -> anyhow::Result> { +) -> Result, ActorError> { let mut to_include: BTreeSet<_> = field.iter().collect(); let included = sectors.iter().filter(|si| to_include.remove(&si.sector_number)).cloned().collect(); if !to_include.is_empty() { - return Err(anyhow!("failed to find {} expected sectors", to_include.len())); + return Err(actor_error!( + not_found, + "failed to find {} expected sectors", + to_include.len() + )); } Ok(included) diff --git a/actors/miner/src/state.rs b/actors/miner/src/state.rs index 205de0c15..6ae87c669 100644 --- a/actors/miner/src/state.rs +++ b/actors/miner/src/state.rs @@ -4,22 +4,13 @@ use std::cmp; use std::ops::Neg; -use anyhow::{anyhow, Error}; use cid::multihash::Code; use cid::Cid; -use fil_actors_runtime::runtime::Policy; -use fil_actors_runtime::{ - actor_error, make_empty_map, make_map_with_root_and_bitwidth, u64_key, ActorDowncast, - ActorError, Array, -}; -use fvm_ipld_amt::Error as AmtError; use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::Blockstore; use fvm_ipld_encoding::tuple::*; use fvm_ipld_encoding::{strict_bytes, BytesDe, CborStore}; -use fvm_ipld_hamt::Error as HamtError; use fvm_shared::address::Address; - use fvm_shared::clock::{ChainEpoch, QuantSpec, EPOCH_UNDEFINED}; use fvm_shared::econ::TokenAmount; use fvm_shared::error::ExitCode; @@ -28,6 +19,14 @@ use fvm_shared::{ActorID, HAMT_BIT_WIDTH}; use itertools::Itertools; use num_traits::Zero; +use fil_actors_runtime::runtime::Policy; +use fil_actors_runtime::{ + actor_error, make_empty_map, make_map_with_root_and_bitwidth, u64_key, ActorContext, + ActorError, Array, AsActorError, AsActorErrors, +}; + +use crate::ERR_BALANCE_INVARIANTS_BROKEN; + use super::beneficiary::*; use super::deadlines::new_deadline_info; use super::policy::*; @@ -124,50 +123,33 @@ impl State { info_cid: Cid, period_start: ChainEpoch, deadline_idx: u64, - ) -> anyhow::Result { - let empty_precommit_map = - make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH).flush().map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to construct empty precommit map", - ) - })?; + ) -> Result { + let empty_precommit_map = make_empty_map::<_, ()>(store, HAMT_BIT_WIDTH) + .flush() + .or_illegal_state("failed to construct empty precommit map")?; let empty_precommits_cleanup_array = Array::::new_with_bit_width(store, PRECOMMIT_EXPIRY_AMT_BITWIDTH) .flush() - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to construct empty precommits array", - ) - })?; + .or_illegal_state("failed to construct empty precommits array")?; let empty_sectors_array = Array::::new_with_bit_width(store, SECTORS_AMT_BITWIDTH) .flush() - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to construct sectors array", - ) - })?; - let empty_bitfield = store.put_cbor(&BitField::new(), Code::Blake2b256).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct empty bitfield") - })?; + .or_illegal_state("failed to construct sectors array")?; + let empty_bitfield = store + .put_cbor(&BitField::new(), Code::Blake2b256) + .or_illegal_state("failed to construct empty bitfield")?; let deadline = Deadline::new(store)?; - let empty_deadline = store.put_cbor(&deadline, Code::Blake2b256).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state") - })?; + let empty_deadline = store + .put_cbor(&deadline, Code::Blake2b256) + .or_illegal_state("failed to construct illegal state")?; let empty_deadlines = store .put_cbor(&Deadlines::new(policy, empty_deadline), Code::Blake2b256) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state") - })?; + .or_illegal_state("failed to construct illegal state")?; - let empty_vesting_funds_cid = - store.put_cbor(&VestingFunds::new(), Code::Blake2b256).map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct illegal state") - })?; + let empty_vesting_funds_cid = store + .put_cbor(&VestingFunds::new(), Code::Blake2b256) + .or_illegal_state("failed to construct illegal state")?; Ok(Self { info: info_cid, @@ -192,11 +174,11 @@ impl State { }) } - pub fn get_info(&self, store: &BS) -> anyhow::Result { + pub fn get_info(&self, store: &BS) -> Result { match store.get_cbor(&self.info) { Ok(Some(info)) => Ok(info), - Ok(None) => Err(actor_error!(not_found, "failed to get miner info").into()), - Err(e) => Err(e.downcast_wrap("failed to get miner info")), + Ok(None) => Err(actor_error!(not_found, "failed to get miner info")), + Err(e) => Err(actor_error!(illegal_state, "failed to get miner info: {}", e)), } } @@ -204,8 +186,9 @@ impl State { &mut self, store: &BS, info: &MinerInfo, - ) -> anyhow::Result<()> { - let cid = store.put_cbor(&info, Code::Blake2b256)?; + ) -> Result<(), ActorError> { + let cid = + store.put_cbor(&info, Code::Blake2b256).or_illegal_state("failed to save info")?; self.info = cid; Ok(()) } @@ -249,12 +232,7 @@ impl State { ) -> Result<(), ActorError> { let prior_allocation = store .get_cbor(&self.allocated_sectors) - .map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_STATE, - "failed to load allocated sectors bitfield", - ) - })? + .or_illegal_state("failed to load allocated sectors bitfield")? .ok_or_else(|| actor_error!(illegal_state, "allocated sectors bitfield not found"))?; if policy != CollisionPolicy::AllowCollisions { @@ -270,14 +248,12 @@ impl State { } } let new_allocation = &prior_allocation | sector_numbers; - self.allocated_sectors = - store.put_cbor(&new_allocation, Code::Blake2b256).map_err(|e| { - e.downcast_default( - ExitCode::USR_ILLEGAL_ARGUMENT, - format!( - "failed to store allocated sectors bitfield after adding {:?}", - sector_numbers, - ), + self.allocated_sectors = store + .put_cbor(&new_allocation, Code::Blake2b256) + .with_context_code(ExitCode::USR_ILLEGAL_ARGUMENT, || { + format!( + "failed to store allocated sectors bitfield after adding {:?}", + sector_numbers, ) })?; Ok(()) @@ -288,22 +264,28 @@ impl State { &mut self, store: &BS, precommits: Vec, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { let mut precommitted = - make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?; + make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH) + .or_illegal_state("failed to load precommits")?; for precommit in precommits.into_iter() { let sector_no = precommit.info.sector_number; let modified = precommitted .set_if_absent(u64_key(precommit.info.sector_number), precommit) - .map_err(|e| { - e.downcast_wrap(format!("failed to store precommitment for {:?}", sector_no,)) + .or_with_illegal_state(|| { + format!("failed to store precommitment for {:?}", sector_no) })?; if !modified { - return Err(anyhow!("sector {} already pre-commited", sector_no)); + return Err(actor_error!( + illegal_state, + "sector {} already pre-commited", + sector_no + )); } } - self.pre_committed_sectors = precommitted.flush()?; + self.pre_committed_sectors = + precommitted.flush().or_illegal_state("failed to save precommits")?; Ok(()) } @@ -311,10 +293,14 @@ impl State { &self, store: &BS, sector_num: SectorNumber, - ) -> Result, HamtError> { + ) -> Result, ActorError> { let precommitted = - make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?; - Ok(precommitted.get(&u64_key(sector_num))?.cloned()) + make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH) + .or_illegal_state("failed to load precommits")?; + Ok(precommitted + .get(&u64_key(sector_num)) + .or_with_illegal_state(|| format!("failed to load precommit {}", sector_num))? + .cloned()) } /// Gets and returns the requested pre-committed sectors, skipping missing sectors. @@ -322,21 +308,23 @@ impl State { &self, store: &BS, sector_numbers: &[SectorNumber], - ) -> anyhow::Result> { + ) -> Result, ActorError> { let precommitted = make_map_with_root_and_bitwidth::<_, SectorPreCommitOnChainInfo>( &self.pre_committed_sectors, store, HAMT_BIT_WIDTH, - )?; + ) + .or_illegal_state("failed to load precommits")?; let mut result = Vec::with_capacity(sector_numbers.len()); for §or_number in sector_numbers { - let info = match precommitted.get(&u64_key(sector_number)).map_err(|e| { - e.downcast_wrap(format!("failed to load precommitment for {}", sector_number)) - })? { - Some(info) => info.clone(), - None => continue, - }; + let info = + match precommitted.get(&u64_key(sector_number)).or_with_illegal_state(|| { + format!("failed to load precommitment for {}", sector_number) + })? { + Some(info) => info.clone(), + None => continue, + }; result.push(info); } @@ -348,21 +336,25 @@ impl State { &mut self, store: &BS, sector_nums: &[SectorNumber], - ) -> Result<(), HamtError> { + ) -> Result<(), ActorError> { let mut precommitted = make_map_with_root_and_bitwidth::<_, SectorPreCommitOnChainInfo>( &self.pre_committed_sectors, store, HAMT_BIT_WIDTH, - )?; + ) + .or_illegal_state("failed to load precommits")?; for §or_num in sector_nums { - let prev_entry = precommitted.delete(&u64_key(sector_num))?; + let prev_entry = precommitted + .delete(&u64_key(sector_num)) + .or_with_illegal_state(|| format!("failed to delete sector {}", sector_num))?; if prev_entry.is_none() { - return Err(format!("sector {} doesn't exist", sector_num).into()); + return Err(actor_error!(illegal_state, "sector {} doesn't exist", sector_num)); } } - self.pre_committed_sectors = precommitted.flush()?; + self.pre_committed_sectors = + precommitted.flush().or_illegal_state("failed to save precommits")?; Ok(()) } @@ -370,7 +362,7 @@ impl State { &self, store: &BS, sector_num: SectorNumber, - ) -> anyhow::Result { + ) -> Result { let sectors = Sectors::load(store, &self.sectors)?; Ok(sectors.get(sector_num)?.is_some()) } @@ -379,15 +371,10 @@ impl State { &mut self, store: &BS, new_sectors: Vec, - ) -> anyhow::Result<()> { - let mut sectors = Sectors::load(store, &self.sectors) - .map_err(|e| e.downcast_wrap("failed to load sectors"))?; - + ) -> Result<(), ActorError> { + let mut sectors = Sectors::load(store, &self.sectors)?; sectors.store(new_sectors)?; - - self.sectors = - sectors.amt.flush().map_err(|e| e.downcast_wrap("failed to persist sectors"))?; - + self.sectors = sectors.amt.flush().or_illegal_state("failed to persist sectors")?; Ok(()) } @@ -395,7 +382,7 @@ impl State { &self, store: &BS, sector_num: SectorNumber, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let sectors = Sectors::load(store, &self.sectors)?; sectors.get(sector_num) } @@ -404,32 +391,36 @@ impl State { &mut self, store: &BS, sector_nos: &BitField, - ) -> Result<(), AmtError> { + ) -> Result<(), ActorError> { let mut sectors = Sectors::load(store, &self.sectors)?; for sector_num in sector_nos.iter() { let deleted_sector = sectors .amt .delete(sector_num) - .map_err(|e| e.downcast_wrap("could not delete sector number"))?; + .with_context_code(ExitCode::USR_ILLEGAL_STATE, || { + format!("could not delete sector number {:?}", sector_num) + })?; if deleted_sector.is_none() { - return Err(AmtError::Dynamic(Error::msg(format!( + return Err(actor_error!( + not_found, "sector {} doesn't exist, failed to delete", sector_num - )))); + )); } } - self.sectors = sectors.amt.flush()?; + self.sectors = sectors.amt.flush().or_illegal_state("failed to save sectors")?; Ok(()) } - pub fn for_each_sector(&self, store: &BS, mut f: F) -> anyhow::Result<()> + // For testing + pub fn for_each_sector(&self, store: &BS, mut f: F) -> Result<(), ActorError> where F: FnMut(&SectorOnChainInfo) -> anyhow::Result<()>, { let sectors = Sectors::load(store, &self.sectors)?; - sectors.amt.for_each(|_, v| f(v))?; + sectors.amt.for_each(|_, v| f(v)).or_illegal_state("failed to iterate sectors")?; Ok(()) } @@ -439,7 +430,7 @@ impl State { policy: &Policy, store: &BS, sector_number: SectorNumber, - ) -> anyhow::Result<(u64, u64)> { + ) -> Result<(u64, u64), ActorError> { let deadlines = self.load_deadlines(store)?; deadlines.find_sector(policy, store, sector_number) } @@ -460,7 +451,7 @@ impl State { current_epoch: ChainEpoch, sector_size: SectorSize, mut deadline_sectors: DeadlineSectorMap, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let mut deadlines = self.load_deadlines(store)?; let sectors = Sectors::load(store, &self.sectors)?; @@ -503,7 +494,7 @@ impl State { mut sectors: Vec, partition_size: u64, sector_size: SectorSize, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { let mut deadlines = self.load_deadlines(store)?; // Sort sectors by number to get better runs in partition bitfields. @@ -532,7 +523,8 @@ impl State { partition_size, &deadline_vec, sectors, - )?; + ) + .or_illegal_state("failed to assign sectors to deadlines")?; for (deadline_idx, deadline_sectors) in deadline_to_sectors.into_iter().enumerate() { if deadline_sectors.is_empty() { @@ -570,7 +562,7 @@ impl State { store: &BS, max_partitions: u64, max_sectors: u64, - ) -> anyhow::Result<(TerminationResult, /* has more */ bool)> { + ) -> Result<(TerminationResult, /* has more */ bool), ActorError> { // Anything to do? This lets us avoid loading the deadlines if there's nothing to do. if self.early_terminations.is_empty() { return Ok((Default::default(), false)); @@ -595,11 +587,8 @@ impl State { max_partitions - result.partitions_processed, max_sectors - result.sectors_processed, ) - .map_err(|e| { - e.downcast_wrap(format!( - "failed to pop early terminations for deadline {}", - deadline_idx - )) + .with_context(|| { + format!("failed to pop early terminations for deadline {}", deadline_idx) })?; result += deadline_result; @@ -640,7 +629,7 @@ impl State { partition_idx: u64, sector_number: SectorNumber, require_proven: bool, - ) -> anyhow::Result { + ) -> Result { let dls = self.load_deadlines(store)?; let dl = dls.load_deadline(policy, store, deadline_idx)?; let partition = dl.load_partition(store, partition_idx)?; @@ -651,8 +640,7 @@ impl State { not_found; "sector {} not a member of partition {}, deadline {}", sector_number, partition_idx, deadline_idx - ) - .into()); + )); } let faulty = partition.faults.get(sector_number); @@ -681,7 +669,7 @@ impl State { deadline_idx: u64, partition_idx: u64, sector_number: SectorNumber, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { let deadlines = self.load_deadlines(store)?; let deadline = deadlines.load_deadline(policy, store, deadline_idx)?; let partition = deadline.load_partition(store, partition_idx)?; @@ -691,8 +679,7 @@ impl State { not_found; "sector {} not a member of partition {}, deadline {}", sector_number, partition_idx, deadline_idx - ) - .into()); + )); } if partition.faults.get(sector_number) { @@ -700,8 +687,7 @@ impl State { forbidden; "sector {} not a member of partition {}, deadline {}", sector_number, partition_idx, deadline_idx - ) - .into()); + )); } if partition.terminated.get(sector_number) { @@ -709,8 +695,7 @@ impl State { not_found; "sector {} not of partition {}, deadline {} is terminated", sector_number, partition_idx, deadline_idx - ) - .into()); + )); } Ok(()) @@ -721,16 +706,14 @@ impl State { &self, store: &BS, sectors: &BitField, - ) -> anyhow::Result> { - Ok(Sectors::load(store, &self.sectors)?.load_sector(sectors)?) + ) -> Result, ActorError> { + Sectors::load(store, &self.sectors)?.load_sector(sectors) } pub fn load_deadlines(&self, store: &BS) -> Result { store .get_cbor::(&self.deadlines) - .map_err(|e| { - e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to load deadlines") - })? + .or_illegal_state("failed to load deadlines")? .ok_or_else( || actor_error!(illegal_state; "failed to load deadlines {}", self.deadlines), ) @@ -740,21 +723,26 @@ impl State { &mut self, store: &BS, deadlines: Deadlines, - ) -> anyhow::Result<()> { - self.deadlines = store.put_cbor(&deadlines, Code::Blake2b256)?; + ) -> Result<(), ActorError> { + self.deadlines = store + .put_cbor(&deadlines, Code::Blake2b256) + .or_illegal_state("failed to save deadlines")?; Ok(()) } /// Loads the vesting funds table from the store. - pub fn load_vesting_funds(&self, store: &BS) -> anyhow::Result { - Ok(store + pub fn load_vesting_funds( + &self, + store: &BS, + ) -> Result { + store .get_cbor(&self.vesting_funds) - .map_err(|e| { - e.downcast_wrap(format!("failed to load vesting funds {}", self.vesting_funds)) + .or_with_illegal_state(|| { + format!("failed to load vesting funds {:?}", self.vesting_funds) })? .ok_or_else( || actor_error!(not_found; "failed to load vesting funds {:?}", self.vesting_funds), - )?) + ) } /// Saves the vesting table to the store. @@ -762,8 +750,10 @@ impl State { &mut self, store: &BS, funds: &VestingFunds, - ) -> anyhow::Result<()> { - self.vesting_funds = store.put_cbor(funds, Code::Blake2b256)?; + ) -> Result<(), ActorError> { + self.vesting_funds = store + .put_cbor(funds, Code::Blake2b256) + .or_illegal_state("failed to save vesting funds")?; Ok(()) } @@ -778,10 +768,11 @@ impl State { // Funds and vesting // - pub fn add_pre_commit_deposit(&mut self, amount: &TokenAmount) -> anyhow::Result<()> { + pub fn add_pre_commit_deposit(&mut self, amount: &TokenAmount) -> Result<(), ActorError> { let new_total = &self.pre_commit_deposits + amount; if new_total.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "negative pre-commit deposit {} after adding {} to prior {}", new_total, amount, @@ -792,10 +783,11 @@ impl State { Ok(()) } - pub fn add_initial_pledge(&mut self, amount: &TokenAmount) -> anyhow::Result<()> { + pub fn add_initial_pledge(&mut self, amount: &TokenAmount) -> Result<(), ActorError> { let new_total = &self.initial_pledge + amount; if new_total.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "negative initial pledge requirement {} after adding {} to prior {}", new_total, amount, @@ -806,9 +798,9 @@ impl State { Ok(()) } - pub fn apply_penalty(&mut self, penalty: &TokenAmount) -> anyhow::Result<()> { + pub fn apply_penalty(&mut self, penalty: &TokenAmount) -> Result<(), ActorError> { if penalty.is_negative() { - Err(anyhow!("applying negative penalty {} not allowed", penalty)) + Err(actor_error!(illegal_state, "applying negative penalty {} not allowed", penalty)) } else { self.fee_debt += penalty; Ok(()) @@ -822,9 +814,9 @@ impl State { current_epoch: ChainEpoch, vesting_sum: &TokenAmount, spec: &VestSpec, - ) -> anyhow::Result { + ) -> Result { if vesting_sum.is_negative() { - return Err(anyhow!("negative vesting sum {}", vesting_sum)); + return Err(actor_error!(illegal_state, "negative vesting sum {}", vesting_sum)); } let mut vesting_funds = self.load_vesting_funds(store)?; @@ -833,7 +825,8 @@ impl State { let amount_unlocked = vesting_funds.unlock_vested_funds(current_epoch); self.locked_funds -= &amount_unlocked; if self.locked_funds.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "negative locked funds {} after unlocking {}", self.locked_funds, amount_unlocked @@ -863,7 +856,7 @@ impl State { TokenAmount, // from vesting TokenAmount, // from balance ), - anyhow::Error, + ActorError, > { let unlocked_balance = self.get_unlocked_balance(curr_balance)?; @@ -871,7 +864,10 @@ impl State { let from_vesting = self.unlock_unvested_funds(store, current_epoch, &fee_debt)?; if from_vesting > self.fee_debt { - return Err(anyhow!("should never unlock more than the debt we need to repay")); + return Err(actor_error!( + illegal_state, + "should never unlock more than the debt we need to repay" + )); } self.fee_debt -= &from_vesting; @@ -885,7 +881,7 @@ impl State { /// burnt and an error if there are not sufficient funds to cover repayment. /// Miner state repays from unlocked funds and fails if unlocked funds are insufficient to cover fee debt. /// FeeDebt will be zero after a successful call. - pub fn repay_debts(&mut self, curr_balance: &TokenAmount) -> anyhow::Result { + pub fn repay_debts(&mut self, curr_balance: &TokenAmount) -> Result { let unlocked_balance = self.get_unlocked_balance(curr_balance)?; if unlocked_balance < self.fee_debt { return Err(actor_error!( @@ -893,8 +889,7 @@ impl State { "unlocked balance can not repay fee debt ({} < {})", unlocked_balance, self.fee_debt - ) - .into()); + )); } Ok(std::mem::take(&mut self.fee_debt)) @@ -907,7 +902,7 @@ impl State { store: &BS, current_epoch: ChainEpoch, target: &TokenAmount, - ) -> anyhow::Result { + ) -> Result { if target.is_zero() || self.locked_funds.is_zero() { return Ok(TokenAmount::zero()); } @@ -916,7 +911,8 @@ impl State { let amount_unlocked = vesting_funds.unlock_unvested_funds(current_epoch, target); self.locked_funds -= &amount_unlocked; if self.locked_funds.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "negative locked funds {} after unlocking {}", self.locked_funds, amount_unlocked @@ -933,7 +929,7 @@ impl State { &mut self, store: &BS, current_epoch: ChainEpoch, - ) -> anyhow::Result { + ) -> Result { if self.locked_funds.is_zero() { return Ok(TokenAmount::zero()); } @@ -942,9 +938,10 @@ impl State { let amount_unlocked = vesting_funds.unlock_vested_funds(current_epoch); self.locked_funds -= &amount_unlocked; if self.locked_funds.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "vesting cause locked funds to become negative: {}", - self.locked_funds, + self.locked_funds )); } @@ -957,7 +954,7 @@ impl State { &self, store: &BS, current_epoch: ChainEpoch, - ) -> anyhow::Result { + ) -> Result { let vesting_funds = self.load_vesting_funds(store)?; Ok(vesting_funds .funds @@ -967,11 +964,18 @@ impl State { } /// Unclaimed funds that are not locked -- includes funds used to cover initial pledge requirement. - pub fn get_unlocked_balance(&self, actor_balance: &TokenAmount) -> anyhow::Result { + pub fn get_unlocked_balance( + &self, + actor_balance: &TokenAmount, + ) -> Result { let unlocked_balance = actor_balance - &self.locked_funds - &self.pre_commit_deposits - &self.initial_pledge; if unlocked_balance.is_negative() { - return Err(anyhow!("negative unlocked balance {}", unlocked_balance)); + return Err(actor_error!( + illegal_state, + "negative unlocked balance {}", + unlocked_balance + )); } Ok(unlocked_balance) } @@ -981,28 +985,42 @@ impl State { pub fn get_available_balance( &self, actor_balance: &TokenAmount, - ) -> anyhow::Result { + ) -> Result { // (actor_balance - &self.locked_funds) - &self.pre_commit_deposit - &self.initial_pledge Ok(self.get_unlocked_balance(actor_balance)? - &self.fee_debt) } - pub fn check_balance_invariants(&self, balance: &TokenAmount) -> anyhow::Result<()> { + pub fn check_balance_invariants(&self, balance: &TokenAmount) -> Result<(), ActorError> { if self.pre_commit_deposits.is_negative() { - return Err(anyhow!("pre-commit deposit is negative: {}", self.pre_commit_deposits)); + return Err(ActorError::unchecked( + ERR_BALANCE_INVARIANTS_BROKEN, + format!("pre-commit deposit is negative: {}", self.pre_commit_deposits), + )); } if self.locked_funds.is_negative() { - return Err(anyhow!("locked funds is negative: {}", self.locked_funds)); + return Err(ActorError::unchecked( + ERR_BALANCE_INVARIANTS_BROKEN, + format!("locked funds is negative: {}", self.locked_funds), + )); } if self.initial_pledge.is_negative() { - return Err(anyhow!("initial pledge is negative: {}", self.initial_pledge)); + return Err(ActorError::unchecked( + ERR_BALANCE_INVARIANTS_BROKEN, + format!("initial pledge is negative: {}", self.initial_pledge), + )); } if self.fee_debt.is_negative() { - return Err(anyhow!("fee debt is negative: {}", self.fee_debt)); + return Err(ActorError::unchecked( + ERR_BALANCE_INVARIANTS_BROKEN, + format!("fee debt is negative: {}", self.fee_debt), + )); } - let min_balance = &self.pre_commit_deposits + &self.locked_funds + &self.initial_pledge; if balance < &min_balance { - return Err(anyhow!("fee debt is negative: {}", self.fee_debt)); + return Err(ActorError::unchecked( + ERR_BALANCE_INVARIANTS_BROKEN, + format!("fee debt is negative: {}", self.fee_debt), + )); } Ok(()) @@ -1018,15 +1036,17 @@ impl State { policy: &Policy, store: &BS, cleanup_events: Vec<(ChainEpoch, u64)>, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { // Load BitField Queue for sector expiry let quant = self.quant_spec_every_deadline(policy); - let mut queue = - super::BitFieldQueue::new(store, &self.pre_committed_sectors_cleanup, quant) - .map_err(|e| e.downcast_wrap("failed to load pre-commit clean up queue"))?; - - queue.add_many_to_queue_values(cleanup_events.into_iter())?; - self.pre_committed_sectors_cleanup = queue.amt.flush()?; + let mut queue = BitFieldQueue::new(store, &self.pre_committed_sectors_cleanup, quant) + .or_illegal_state("failed to load pre-commit clean up queue")?; + + queue + .add_many_to_queue_values(cleanup_events.into_iter()) + .or_illegal_state("failed to add precommit expirations")?; + self.pre_committed_sectors_cleanup = + queue.amt.flush().or_illegal_state("failed to save precommits")?; Ok(()) } @@ -1035,7 +1055,7 @@ impl State { policy: &Policy, store: &BS, current_epoch: ChainEpoch, - ) -> anyhow::Result { + ) -> Result { let mut deposit_to_burn = TokenAmount::zero(); // cleanup expired pre-committed sectors @@ -1043,12 +1063,17 @@ impl State { store, &self.pre_committed_sectors_cleanup, self.quant_spec_every_deadline(policy), - )?; + ) + .or_illegal_state("failed to load precommit expirations")?; - let (sectors, modified) = cleanup_queue.pop_until(current_epoch)?; + let (sectors, modified) = + cleanup_queue.pop_until(current_epoch).or_illegal_state("failed to pop precommits")?; if modified { - self.pre_committed_sectors_cleanup = cleanup_queue.amt.flush()?; + self.pre_committed_sectors_cleanup = cleanup_queue + .amt + .flush() + .or_illegal_state("failed to save precommit expirations")?; } let mut precommits_to_delete = Vec::new(); @@ -1076,7 +1101,8 @@ impl State { self.pre_commit_deposits -= &deposit_to_burn; if self.pre_commit_deposits.is_negative() { - return Err(anyhow!( + return Err(actor_error!( + illegal_state, "pre-commit clean up caused negative deposits: {}", self.pre_commit_deposits )); @@ -1090,7 +1116,7 @@ impl State { policy: &Policy, store: &BS, current_epoch: ChainEpoch, - ) -> anyhow::Result { + ) -> Result { let mut pledge_delta = TokenAmount::zero(); let dl_info = self.deadline_info(policy, current_epoch); @@ -1173,18 +1199,18 @@ impl State { &self, store: &BS, sector_nos: &BitField, - ) -> anyhow::Result> { + ) -> Result, ActorError> { let mut precommits = Vec::new(); let precommitted = - make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH)?; + make_map_with_root_and_bitwidth(&self.pre_committed_sectors, store, HAMT_BIT_WIDTH) + .or_illegal_state("failed to load precommits")?; for sector_no in sector_nos.iter() { if sector_no > MAX_SECTOR_NUMBER { - return Err( - actor_error!(illegal_argument; "sector number greater than maximum").into() - ); + return Err(actor_error!(illegal_argument; "sector number greater than maximum")); } let info: &SectorPreCommitOnChainInfo = precommitted - .get(&u64_key(sector_no))? + .get(&u64_key(sector_no)) + .or_with_illegal_state(|| format!("failed to load precommit {}", sector_no))? .ok_or_else(|| actor_error!(not_found, "sector {} not found", sector_no))?; precommits.push(info.clone()); } diff --git a/actors/miner/tests/deadline_state_test.rs b/actors/miner/tests/deadline_state_test.rs index 7ca1520c4..d9201f0f1 100644 --- a/actors/miner/tests/deadline_state_test.rs +++ b/actors/miner/tests/deadline_state_test.rs @@ -467,7 +467,7 @@ fn terminate_sectors( epoch: ChainEpoch, sectors: Vec, partition_sectors: HashMap, -) -> anyhow::Result { +) -> Result { let store = rt.store(); let sectors_array = sectors_arr(&store, sectors); @@ -533,10 +533,7 @@ fn fails_to_terminate_missing_sector() { ); assert!(ret.is_err()); - let err = ret - .expect_err("can only terminate live sectors") - .downcast::() - .expect("Invalid error"); + let err = ret.expect_err("can only terminate live sectors"); assert_eq!(err.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); } @@ -555,10 +552,7 @@ fn fails_to_terminate_missing_partition() { ); assert!(ret.is_err()); - let err = ret - .expect_err("can only terminate existing partitions") - .downcast::() - .expect("Invalid error"); + let err = ret.expect_err("can only terminate existing partitions"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); } @@ -577,10 +571,7 @@ fn fails_to_terminate_already_terminated_sector() { ); assert!(ret.is_err()); - let err = ret - .expect_err("cannot terminate already terminated sector") - .downcast::() - .expect("Invalid error"); + let err = ret.expect_err("cannot terminate already terminated sector"); assert_eq!(err.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); } @@ -961,11 +952,7 @@ fn post_missing_partition() { &mut post_partitions, ); - let err = post_result - .err() - .expect("missing partition, should have failed") - .downcast::() - .expect("Invalid error"); + let err = post_result.err().expect("missing partition, should have failed"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); } @@ -998,11 +985,7 @@ fn post_partition_twice() { &mut post_partitions, ); - let err = post_result - .err() - .expect("duplicate partition, should have failed") - .downcast::() - .expect("Invalid error"); + let err = post_result.err().expect("duplicate partition, should have failed"); assert_eq!(err.exit_code(), ExitCode::USR_ILLEGAL_ARGUMENT); } @@ -1137,10 +1120,7 @@ fn cannot_declare_faults_in_missing_partitions() { &mut partition_sector_map, ); - let err = result - .expect_err("missing partition, should have failed") - .downcast::() - .expect("Invalid error"); + let err = result.expect_err("missing partition, should have failed"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); } @@ -1164,10 +1144,7 @@ fn cannot_declare_faults_recovered_in_missing_partitions() { &mut partition_sector_map, ); - let err = result - .expect_err("missing partition, should have failed") - .downcast::() - .expect("Invalid error"); + let err = result.expect_err("missing partition, should have failed"); assert_eq!(err.exit_code(), ExitCode::USR_NOT_FOUND); } diff --git a/actors/miner/tests/record_skipped_faults.rs b/actors/miner/tests/record_skipped_faults.rs index a47f98f4e..0f3802e64 100644 --- a/actors/miner/tests/record_skipped_faults.rs +++ b/actors/miner/tests/record_skipped_faults.rs @@ -52,9 +52,7 @@ fn fail_if_all_declared_sectors_are_not_in_the_partition() { let err: ActorError = partition .record_skipped_faults(&store, §or_arr, SECTOR_SIZE, QUANT_SPEC, EXP, &skipped) - .unwrap_err() - .downcast() - .unwrap(); + .unwrap_err(); assert_eq!(ExitCode::USR_ILLEGAL_ARGUMENT, err.exit_code()); } diff --git a/actors/miner/tests/state_harness.rs b/actors/miner/tests/state_harness.rs index 7c982ec91..6dcc5a9f8 100644 --- a/actors/miner/tests/state_harness.rs +++ b/actors/miner/tests/state_harness.rs @@ -9,7 +9,6 @@ use fvm_ipld_bitfield::BitField; use fvm_ipld_blockstore::MemoryBlockstore; use fvm_ipld_encoding::BytesDe; use fvm_ipld_encoding::CborStore; -use fvm_ipld_hamt::Error as HamtError; use fvm_shared::econ::TokenAmount; use fvm_shared::sector::{SectorNumber, SectorSize}; use fvm_shared::{clock::ChainEpoch, clock::QuantSpec, sector::RegisteredPoStProof}; @@ -61,7 +60,7 @@ impl StateHarness { pub fn put_precommitted_sectors( &mut self, precommits: Vec, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { self.st.put_precommitted_sectors(&self.store, precommits) } @@ -69,7 +68,7 @@ impl StateHarness { pub fn delete_precommitted_sectors( &mut self, sector_nums: &[SectorNumber], - ) -> Result<(), HamtError> { + ) -> Result<(), ActorError> { self.st.delete_precommitted_sectors(&self.store, sector_nums) } @@ -99,7 +98,7 @@ impl StateHarness { &mut self, policy: &Policy, cleanup_events: Vec<(ChainEpoch, u64)>, - ) -> anyhow::Result<()> { + ) -> Result<(), ActorError> { self.st.add_pre_commit_clean_ups(policy, &self.store, cleanup_events) } @@ -109,7 +108,7 @@ impl StateHarness { current_epoch: ChainEpoch, vesting_sum: &TokenAmount, spec: &VestSpec, - ) -> anyhow::Result { + ) -> Result { self.st.add_locked_funds(&self.store, current_epoch, vesting_sum, spec) } @@ -117,7 +116,7 @@ impl StateHarness { pub fn unlock_vested_funds( &mut self, current_epoch: ChainEpoch, - ) -> anyhow::Result { + ) -> Result { self.st.unlock_vested_funds(&self.store, current_epoch) } @@ -126,7 +125,7 @@ impl StateHarness { &mut self, current_epoch: ChainEpoch, target: &TokenAmount, - ) -> anyhow::Result { + ) -> Result { self.st.unlock_unvested_funds(&self.store, current_epoch, target) } diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index cc44bf1db..7809bbd44 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -46,8 +46,8 @@ use fil_actor_miner::ext::verifreg::{ use fil_actors_runtime::runtime::{DomainSeparationTag, Policy, Runtime, RuntimePolicy}; use fil_actors_runtime::{test_utils::*, BatchReturn, BatchReturnGen}; use fil_actors_runtime::{ - ActorDowncast, ActorError, Array, DealWeight, MessageAccumulator, BURNT_FUNDS_ACTOR_ADDR, - INIT_ACTOR_ADDR, REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, + ActorError, Array, DealWeight, MessageAccumulator, BURNT_FUNDS_ACTOR_ADDR, INIT_ACTOR_ADDR, + REWARD_ACTOR_ADDR, STORAGE_MARKET_ACTOR_ADDR, STORAGE_POWER_ACTOR_ADDR, VERIFIED_REGISTRY_ACTOR_ADDR, }; use fvm_ipld_amt::Amt; @@ -1897,9 +1897,8 @@ impl ActorHarness { st: &State, ) -> HashMap> { let quant = st.quant_spec_every_deadline(&rt.policy); - let queue = BitFieldQueue::new(&rt.store, &st.pre_committed_sectors_cleanup, quant) - .map_err(|e| e.downcast_wrap("failed to load pre-commit clean up queue")) - .unwrap(); + let queue = + BitFieldQueue::new(&rt.store, &st.pre_committed_sectors_cleanup, quant).unwrap(); let mut expirations: HashMap> = HashMap::new(); queue .amt diff --git a/runtime/src/util/errors.rs b/runtime/src/util/errors.rs new file mode 100644 index 000000000..8e27b038a --- /dev/null +++ b/runtime/src/util/errors.rs @@ -0,0 +1,57 @@ +use crate::{ActorError, AsActorError}; +use fvm_shared::error::ExitCode; +use std::fmt::Display; + +// Convenient wrappers for error conversion. +pub trait AsActorErrors: Sized { + fn or_illegal_state(self, context: C) -> Result + where + C: Display + 'static; + + fn or_with_illegal_state(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; + + fn or_assertion_failed(self, context: C) -> Result + where + C: Display + 'static; + + fn or_with_assertion_failed(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C; +} + +// Note: E should be std::error::Error, revert to this after anyhow:Error is no longer used. +impl AsActorErrors for Result { + fn or_illegal_state(self, context: C) -> Result + where + C: Display + 'static, + { + self.context_code(ExitCode::USR_ILLEGAL_STATE, context) + } + + fn or_with_illegal_state(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.with_context_code(ExitCode::USR_ILLEGAL_STATE, f) + } + + fn or_assertion_failed(self, context: C) -> Result + where + C: Display + 'static, + { + self.context_code(ExitCode::USR_ASSERTION_FAILED, context) + } + + fn or_with_assertion_failed(self, f: F) -> Result + where + C: Display + 'static, + F: FnOnce() -> C, + { + self.with_context_code(ExitCode::USR_ASSERTION_FAILED, f) + } +} diff --git a/runtime/src/util/mod.rs b/runtime/src/util/mod.rs index d2dbd22fe..b0bc0a07c 100644 --- a/runtime/src/util/mod.rs +++ b/runtime/src/util/mod.rs @@ -10,6 +10,7 @@ pub use self::message_accumulator::MessageAccumulator; pub use self::multimap::*; pub use self::set::Set; pub use self::set_multimap::SetMultimap; +pub use self::errors::*; mod batch_return; pub mod cbor; @@ -19,3 +20,4 @@ mod message_accumulator; mod multimap; mod set; mod set_multimap; +mod errors;