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
use crate::v14::{Allocation, Claim};
use fil_actors_shared::v14::{
    parse_uint_key, ActorError, AsActorError, BatchReturn, BatchReturnGen, MapMap,
};
use fvm_ipld_blockstore::Blockstore;
use fvm_shared4::clock::ChainEpoch;
use fvm_shared4::error::ExitCode;
use fvm_shared4::ActorID;
use log::info;
use serde::de::DeserializeOwned;
use serde::Serialize;

// Something with an expiration epoch.
pub trait Expires {
    fn expiration(&self) -> ChainEpoch;
}

impl Expires for Allocation {
    fn expiration(&self) -> ChainEpoch {
        self.expiration
    }
}

impl Expires for Claim {
    fn expiration(&self) -> ChainEpoch {
        self.term_start + self.term_max
    }
}

// Finds all items in a collection for some owner that have expired.
// Returns those items' keys.
pub fn find_expired<T, BS>(
    collection: &mut MapMap<BS, T, ActorID, u64>,
    owner: ActorID,
    curr_epoch: ChainEpoch,
) -> Result<Vec<u64>, ActorError>
where
    T: Expires + Serialize + DeserializeOwned + Clone + PartialEq,
    BS: Blockstore,
{
    let mut found_ids = Vec::<u64>::new();
    collection
        .for_each_in(owner, |key, record| {
            if curr_epoch >= record.expiration() {
                let id = parse_uint_key(key)
                    .context_code(ExitCode::USR_ILLEGAL_STATE, "failed to parse uint key")?;
                found_ids.push(id);
            }
            Ok(())
        })
        .context_code(
            ExitCode::USR_ILLEGAL_STATE,
            "failed to iterate over allocations/claims",
        )?;
    Ok(found_ids)
}

// Checks each candidate item from the collection for expiration.
// Returns a batch return with OK for expired items, and FORBIDDEN for non-expired.
pub fn check_expired<T, BS>(
    collection: &mut MapMap<BS, T, ActorID, u64>,
    candidates: &Vec<u64>,
    owner: ActorID,
    curr_epoch: ChainEpoch,
) -> Result<BatchReturn, ActorError>
where
    T: Expires + Serialize + DeserializeOwned + Clone + PartialEq,
    BS: Blockstore,
{
    let mut ret_gen = BatchReturnGen::new(candidates.len());
    for id in candidates {
        // Check each specified claim is expired.
        let maybe_record = collection.get(owner, *id).context_code(
            ExitCode::USR_ILLEGAL_STATE,
            "HAMT lookup failure getting allocation/claim",
        )?;

        if let Some(record) = maybe_record {
            if curr_epoch >= record.expiration() {
                ret_gen.add_success();
            } else {
                ret_gen.add_fail(ExitCode::USR_FORBIDDEN);
                info!("cannot remove allocation/claim {} that has not expired", id);
            }
        } else {
            ret_gen.add_fail(ExitCode::USR_NOT_FOUND);
            info!(
                "allocation/claim references id {} that does not belong to {}",
                id, owner,
            );
        }
    }
    Ok(ret_gen.gen())
}