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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
// Copyright 2021-2023 Protocol Labs
// SPDX-License-Identifier: Apache-2.0, MIT
use std::collections::HashMap;

use anyhow::{anyhow, Context};
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::CborStore;

const ACCOUNT_ACTOR_NAME: &str = "account";
const INIT_ACTOR_NAME: &str = "init";
const SYSTEM_ACTOR_NAME: &str = "system";
const PLACEHOLDER_ACTOR_NAME: &str = "placeholder";
const EAM_ACTOR_NAME: &str = "eam";
const ETHACCOUNT_ACTOR_NAME: &str = "ethaccount";

/// A mapping of builtin actor CIDs to their respective types.
pub struct Manifest {
    account_code: Cid,
    placeholder_code: Cid,
    system_code: Cid,
    init_code: Cid,
    eam_code: Cid,
    ethaccount_code: Cid,

    by_id: HashMap<u32, Cid>,
    by_code: HashMap<Cid, u32>,
}

/// Create an "id CID" (for testing).
#[cfg(any(feature = "testing", test))]
const fn id_cid(name: &[u8]) -> Cid {
    use std::mem;

    use fvm_shared::{IDENTITY_HASH, IPLD_RAW};
    use multihash::Multihash;

    // This code is ugly because const fns are a bit ugly right now:
    //
    // 1. Compiler can't drop the result, we need to forget it manually.
    //    https://doc.rust-lang.org/error-index.html#E0493
    // 2. We can't unwrap, we need to explicitly panic.
    // 3. Rust can't figure out that "panic" will prevent a drop of the error case.
    let result = Multihash::wrap(IDENTITY_HASH, name);
    let k = if let Ok(mh) = &result {
        Cid::new_v1(IPLD_RAW, *mh)
    } else {
        panic!();
    };
    mem::forget(result); // This is const, so we don't really care about "leaks".

    k
}

impl Manifest {
    #[cfg(any(feature = "testing", test))]
    pub const DUMMY_CODES: &'static [(&'static str, Cid)] = &[
        ("system", id_cid(b"fil/test/system")),
        ("init", id_cid(b"fil/test/init")),
        ("eam", id_cid(b"fil/test/eam")),
        ("ethaccount", id_cid(b"fil/test/ethaccount")),
        ("cron", id_cid(b"fil/test/cron")),
        ("account", id_cid(b"fil/test/account")),
        ("placeholder", id_cid(b"fil/test/placeholder")),
    ];

    #[cfg(any(feature = "testing", test))]
    pub fn dummy() -> Self {
        Self::new(Self::DUMMY_CODES.iter().copied()).unwrap()
    }

    /// Load a manifest from the blockstore.
    pub fn load<B: Blockstore>(bs: &B, root_cid: &Cid, ver: u32) -> anyhow::Result<Manifest> {
        if ver != 1 {
            return Err(anyhow!("unsupported manifest version {}", ver));
        }

        let vec: Vec<(String, Cid)> = match bs.get_cbor(root_cid)? {
            Some(vec) => vec,
            None => {
                return Err(anyhow!("cannot find manifest root cid {}", root_cid));
            }
        };

        Manifest::new(vec)
    }

    /// Construct a new manifest from actor name/cid tuples.
    pub fn new(iter: impl IntoIterator<Item = (impl Into<String>, Cid)>) -> anyhow::Result<Self> {
        let mut by_name = HashMap::new();
        let mut by_id = HashMap::new();
        let mut by_code = HashMap::new();

        // Actors are indexed sequentially, starting at 1, in the order in which they appear in the
        // manifest. 0 is reserved for "everything else" (i.e., not a builtin actor).
        for ((name, code_cid), id) in iter.into_iter().zip(1u32..) {
            let name = name.into();
            by_id.insert(id, code_cid);
            by_code.insert(code_cid, id);
            by_name.insert(name, code_cid);
        }

        let account_code = *by_name
            .get(ACCOUNT_ACTOR_NAME)
            .context("manifest missing account actor")?;

        let system_code = *by_name
            .get(SYSTEM_ACTOR_NAME)
            .context("manifest missing system actor")?;

        let init_code = *by_name
            .get(INIT_ACTOR_NAME)
            .context("manifest missing init actor")?;

        let placeholder_code = *by_name
            .get(PLACEHOLDER_ACTOR_NAME)
            .context("manifest missing placeholder actor")?;

        let eam_code = *by_name
            .get(EAM_ACTOR_NAME)
            .context("manifest missing eam actor")?;

        let ethaccount_code = *by_name
            .get(ETHACCOUNT_ACTOR_NAME)
            .context("manifest missing ethaccount actor")?;

        Ok(Self {
            account_code,
            system_code,
            init_code,
            placeholder_code,
            eam_code,
            ethaccount_code,
            by_id,
            by_code,
        })
    }

    /// Returns the code CID for a builtin actor, given the actor's ID.
    pub fn code_by_id(&self, id: u32) -> Option<&Cid> {
        self.by_id.get(&id)
    }

    /// Returns the the actor code's "id" if it's a builtin actor. Otherwise, returns 0.
    pub fn id_by_code(&self, code: &Cid) -> u32 {
        self.by_code.get(code).copied().unwrap_or(0)
    }

    /// Returns true id the passed code CID is the account actor.
    pub fn is_account_actor(&self, cid: &Cid) -> bool {
        &self.account_code == cid
    }

    /// Returns true id the passed code CID is the placeholder actor.
    pub fn is_placeholder_actor(&self, cid: &Cid) -> bool {
        &self.placeholder_code == cid
    }

    /// Returns true id the passed code CID is the EthAccount actor.
    pub fn is_ethaccount_actor(&self, cid: &Cid) -> bool {
        &self.ethaccount_code == cid
    }

    pub fn builtin_actor_codes(&self) -> impl Iterator<Item = &Cid> {
        self.by_id.values()
    }

    /// Returns the code CID for the account actor.
    pub fn get_account_code(&self) -> &Cid {
        &self.account_code
    }

    /// Returns the code CID for the init actor.
    pub fn get_init_code(&self) -> &Cid {
        &self.init_code
    }

    /// Returns the code CID for the system actor.
    pub fn get_system_code(&self) -> &Cid {
        &self.system_code
    }

    /// Returns the code CID for the eam actor.
    pub fn get_eam_code(&self) -> &Cid {
        &self.eam_code
    }

    /// Returns the code CID for the system actor.
    pub fn get_placeholder_code(&self) -> &Cid {
        &self.placeholder_code
    }

    /// Returns the code CID for the Ethereum Account actor.
    pub fn get_ethaccount_code(&self) -> &Cid {
        &self.ethaccount_code
    }
}