1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Copyright 2019-2022 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::SectorOnChainInfo;
use crate::{actor_error, ActorDowncast, ActorError, ExitCode};
use ahash::AHashSet;
use bitfield::BitField;
use cid::Cid;
use fil_types::{SectorNumber, MAX_SECTOR_NUMBER};
use ipld_amt::{Amt, Error as AmtError};
use ipld_blockstore::BlockStore;
use std::collections::HashSet;
use std::error::Error as StdError;

pub struct Sectors<'db, BS> {
    pub amt: Amt<'db, SectorOnChainInfo, BS>,
}

impl<'db, BS: BlockStore> Sectors<'db, BS> {
    pub fn load(store: &'db BS, root: &Cid) -> Result<Self, AmtError> {
        Ok(Self {
            amt: Amt::load(root, store)?,
        })
    }

    pub fn load_sector<'a>(
        &self,
        sector_numbers: impl bitfield::Validate<'a>,
    ) -> Result<Vec<SectorOnChainInfo>, ActorError> {
        let sector_numbers = match sector_numbers.validate() {
            Ok(sector_numbers) => sector_numbers,
            Err(e) => {
                return Err(actor_error!(
                    ErrIllegalArgument,
                    "failed to load sectors: {}",
                    e
                ))
            }
        };

        let mut sector_infos: Vec<SectorOnChainInfo> = Vec::new();
        for sector_number in sector_numbers.iter() {
            let sector_on_chain = self
                .amt
                .get(sector_number)
                .map_err(|e| {
                    e.downcast_default(
                        ExitCode::ErrIllegalState,
                        format!("failed to load sector {}", sector_number),
                    )
                })?
                .cloned()
                .ok_or_else(|| actor_error!(ErrNotFound; "sector not found: {}", sector_number))?;
            sector_infos.push(sector_on_chain);
        }
        Ok(sector_infos)
    }

    pub fn get(
        &self,
        sector_number: SectorNumber,
    ) -> Result<Option<SectorOnChainInfo>, Box<dyn StdError>> {
        Ok(self
            .amt
            .get(sector_number as usize)
            .map_err(|e| e.downcast_wrap(format!("failed to get sector {}", sector_number)))?
            .cloned())
    }

    pub fn store(&mut self, infos: Vec<SectorOnChainInfo>) -> Result<(), Box<dyn StdError>> {
        for info in infos {
            let sector_number = info.sector_number;

            if sector_number > MAX_SECTOR_NUMBER {
                return Err(format!("sector number {} out of range", info.sector_number).into());
            }

            self.amt.set(sector_number as usize, info).map_err(|e| {
                e.downcast_wrap(format!("failed to store sector {}", sector_number))
            })?;
        }

        Ok(())
    }

    pub fn must_get(
        &self,
        sector_number: SectorNumber,
    ) -> Result<SectorOnChainInfo, Box<dyn StdError>> {
        self.get(sector_number)?
            .ok_or_else(|| format!("sector {} not found", sector_number).into())
    }

    /// Loads info for a set of sectors to be proven.
    /// If any of the sectors are declared faulty and not to be recovered, info for the first non-faulty sector is substituted instead.
    /// If any of the sectors are declared recovered, they are returned from this method.
    pub fn load_for_proof(
        &self,
        proven_sectors: &BitField,
        expected_faults: &BitField,
    ) -> Result<Vec<SectorOnChainInfo>, Box<dyn StdError>> {
        let non_faults = proven_sectors - expected_faults;

        if non_faults.is_empty() {
            return Ok(Vec::new());
        }

        let good_sector_number = non_faults.first().expect("faults are not empty");

        let sector_infos = self.load_with_fault_max(
            proven_sectors,
            expected_faults,
            good_sector_number as SectorNumber,
        )?;

        Ok(sector_infos)
    }
    /// Loads sector info for a sequence of sectors, substituting info for a stand-in sector for any that are faulty.
    pub fn load_with_fault_max(
        &self,
        sectors: &BitField,
        faults: &BitField,
        fault_stand_in: SectorNumber,
    ) -> Result<Vec<SectorOnChainInfo>, Box<dyn StdError>> {
        let stand_in_info = self.must_get(fault_stand_in)?;

        // Expand faults into a map for quick lookups.
        // The faults bitfield should already be a subset of the sectors bitfield.
        let sector_count = sectors.len();

        let fault_set: HashSet<usize> = faults.iter().collect();

        let mut sector_infos = Vec::with_capacity(sector_count);
        for i in sectors.iter() {
            let faulty = fault_set.contains(&i);
            let sector = if !faulty {
                self.must_get(i as u64)?
            } else {
                stand_in_info.clone()
            };
            sector_infos.push(sector);
        }

        Ok(sector_infos)
    }
}

pub(crate) fn select_sectors(
    sectors: &[SectorOnChainInfo],
    field: &BitField,
) -> Result<Vec<SectorOnChainInfo>, Box<dyn StdError>> {
    let mut to_include: AHashSet<_> = field.iter().collect();

    let mut included = Vec::with_capacity(to_include.len());
    for s in sectors {
        let sec = s.sector_number as usize;
        if !to_include.contains(&sec) {
            continue;
        }
        included.push(s.clone());
        to_include.remove(&sec);
    }

    if !to_include.is_empty() {
        return Err(format!("failed to find {} expected sectors", to_include.len()).into());
    }

    Ok(included)
}