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";
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>,
}
#[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;
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); 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()
}
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)
}
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();
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,
})
}
pub fn code_by_id(&self, id: u32) -> Option<&Cid> {
self.by_id.get(&id)
}
pub fn id_by_code(&self, code: &Cid) -> u32 {
self.by_code.get(code).copied().unwrap_or(0)
}
pub fn is_account_actor(&self, cid: &Cid) -> bool {
&self.account_code == cid
}
pub fn is_placeholder_actor(&self, cid: &Cid) -> bool {
&self.placeholder_code == cid
}
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()
}
pub fn get_account_code(&self) -> &Cid {
&self.account_code
}
pub fn get_init_code(&self) -> &Cid {
&self.init_code
}
pub fn get_system_code(&self) -> &Cid {
&self.system_code
}
pub fn get_eam_code(&self) -> &Cid {
&self.eam_code
}
pub fn get_placeholder_code(&self) -> &Cid {
&self.placeholder_code
}
pub fn get_ethaccount_code(&self) -> &Cid {
&self.ethaccount_code
}
}