use std::collections::{BTreeMap, BTreeSet};
use anyhow::Context;
use cid::multihash::MultihashDigest;
use cid::Cid;
use fvm_ipld_blockstore::Blockstore;
use fvm_ipld_encoding::tuple::*;
use fvm_ipld_hamt::Hamt;
use fvm_shared::{address::Address, ActorID, HAMT_BIT_WIDTH};
use recall_fendermint_vm_genesis::{Actor, ActorMeta};
use crate::{eam::EthAddress, system};
pub const FIRST_NON_SINGLETON_ADDR: ActorID = 100;
define_singleton!(INIT { id: 1, code_id: 2 });
pub type AddressMap = BTreeMap<Address, ActorID>;
pub fn builtin_actor_eth_addr(id: ActorID) -> EthAddress {
let eth_addr = EthAddress::from_id(id);
let eth_addr = cid::multihash::Code::Keccak256.digest(ð_addr.0);
let eth_addr: [u8; 20] = eth_addr.digest()[12..32].try_into().unwrap();
EthAddress(eth_addr)
}
#[derive(Serialize_tuple, Deserialize_tuple, Clone, Debug)]
pub struct State {
pub address_map: Cid,
pub next_id: ActorID,
pub network_name: String,
#[cfg(feature = "m2-native")]
pub installed_actors: Cid,
}
impl State {
pub fn new<BS: Blockstore>(
store: &BS,
network_name: String,
accounts: &[Actor],
eth_builtin_ids: &BTreeSet<ActorID>,
eth_library_count: u64,
) -> anyhow::Result<(Self, AddressMap)> {
let mut allocated_ids = AddressMap::new();
let mut address_map = Hamt::<&BS, ActorID>::new_with_bit_width(store, HAMT_BIT_WIDTH);
let mut set_address = |addr: Address, id: ActorID| {
tracing::debug!(
addr = addr.to_string(),
actor_id = id,
"setting init address"
);
address_map.set(addr.to_bytes().into(), id)
};
let addresses = accounts.iter().flat_map(|a| match &a.meta {
ActorMeta::Account(acc) => {
vec![acc.owner.0]
}
ActorMeta::Multisig(ms) => ms.signers.iter().map(|a| a.0).collect(),
});
let mut next_id = FIRST_NON_SINGLETON_ADDR;
for addr in addresses {
if allocated_ids.contains_key(&addr) {
continue;
}
allocated_ids.insert(addr, next_id);
set_address(addr, next_id).context("cannot set ID of account address")?;
next_id += 1;
}
for a in accounts.iter() {
if let ActorMeta::Multisig { .. } = a.meta {
next_id += 1;
}
}
for id in eth_builtin_ids {
let addr = Address::from(builtin_actor_eth_addr(*id));
set_address(addr, *id).context("cannot set ID of eth contract address")?;
}
for _ in 0..eth_library_count {
let addr = Address::from(builtin_actor_eth_addr(next_id));
set_address(addr, next_id).context("cannot set ID of eth library address")?;
next_id += 1;
}
set_address(*system::SYSTEM_ACTOR_ETH_ADDR, system::SYSTEM_ACTOR_ID)
.context("cannot set ID of system eth address")?;
#[cfg(feature = "m2-native")]
let installed_actors = store.put_cbor(&Vec::<Cid>::new(), Code::Blake2b256)?;
let state = Self {
address_map: address_map.flush()?,
next_id,
network_name,
#[cfg(feature = "m2-native")]
installed_actors,
};
tracing::debug!(?state, "init actor state");
Ok((state, allocated_ids))
}
}