use std::ops::RangeInclusive;
use anyhow::{anyhow, Context as _};
use cid::Cid;
use fvm_ipld_blockstore::{Block, Blockstore, Buffered};
use fvm_ipld_encoding::{CborStore, DAG_CBOR};
use fvm_shared::version::NetworkVersion;
use log::debug;
use multihash::Code::Blake2b256;
use super::{Machine, MachineContext};
use crate::blockstore::BufferedBlockstore;
use crate::externs::Externs;
use crate::kernel::{ClassifyResult, Result};
use crate::machine::limiter::DefaultMemoryLimiter;
use crate::machine::Manifest;
use crate::state_tree::StateTree;
use crate::system_actor::State as SystemActorState;
pub struct DefaultMachine<B, E> {
context: MachineContext,
externs: E,
state_tree: StateTree<BufferedBlockstore<B>>,
builtin_actors: Manifest,
id: String,
}
impl<B, E> DefaultMachine<B, E>
where
B: Blockstore + 'static,
E: Externs + 'static,
{
pub fn new(context: &MachineContext, blockstore: B, externs: E) -> anyhow::Result<Self> {
#[cfg(not(feature = "nv24-dev"))]
const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
NetworkVersion::V21..=NetworkVersion::V23;
#[cfg(feature = "nv24-dev")]
const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
NetworkVersion::V21..=NetworkVersion::V24;
debug!(
"initializing a new machine, epoch={}, base_fee={}, nv={:?}, root={}",
context.epoch, &context.base_fee, context.network_version, context.initial_state_root
);
if !SUPPORTED_VERSIONS.contains(&context.network_version) {
return Err(anyhow!(
"unsupported network version: {}",
context.network_version
));
}
if !blockstore
.has(&context.initial_state_root)
.context("failed to load initial state-root")?
{
return Err(anyhow!(
"blockstore doesn't have the initial state-root {}",
&context.initial_state_root
));
}
put_empty_blocks(&blockstore)?;
let state_tree = {
let bstore = BufferedBlockstore::new(blockstore);
StateTree::new_from_root(bstore, &context.initial_state_root)?
};
let (builtin_actors_cid, manifest_version) = match context.builtin_actors_override {
Some(manifest_cid) => {
let (version, cid): (u32, Cid) = state_tree
.store()
.get_cbor(&manifest_cid)?
.context("failed to load actor manifest")?;
(cid, version)
}
None => {
let (state, _) = SystemActorState::load(&state_tree)?;
(state.builtin_actors, 1)
}
};
let builtin_actors =
Manifest::load(state_tree.store(), &builtin_actors_cid, manifest_version)?;
let randomness: [u8; 16] = rand::random();
Ok(DefaultMachine {
context: context.clone(),
externs,
state_tree,
builtin_actors,
id: format!(
"{}-{}",
context.epoch,
cid::multibase::encode(cid::multibase::Base::Base32Lower, randomness)
),
})
}
}
impl<B, E> Machine for DefaultMachine<B, E>
where
B: Blockstore + 'static,
E: Externs + 'static,
{
type Blockstore = BufferedBlockstore<B>;
type Externs = E;
type Limiter = DefaultMemoryLimiter;
fn blockstore(&self) -> &Self::Blockstore {
self.state_tree.store()
}
fn context(&self) -> &MachineContext {
&self.context
}
fn externs(&self) -> &Self::Externs {
&self.externs
}
fn builtin_actors(&self) -> &Manifest {
&self.builtin_actors
}
fn state_tree(&self) -> &StateTree<Self::Blockstore> {
&self.state_tree
}
fn state_tree_mut(&mut self) -> &mut StateTree<Self::Blockstore> {
&mut self.state_tree
}
fn flush(&mut self) -> Result<Cid> {
let root = self.state_tree_mut().flush()?;
self.blockstore().flush(&root).or_fatal()?;
Ok(root)
}
fn into_store(self) -> Self::Blockstore {
self.state_tree.into_store()
}
fn machine_id(&self) -> &str {
&self.id
}
fn new_limiter(&self) -> Self::Limiter {
DefaultMemoryLimiter::for_network(&self.context().network)
}
}
const EMPTY_ARRAY_BLOCK: Block<&'static [u8]> = Block::new(DAG_CBOR, &[128]);
#[test]
fn test_empty_array_block() {
let expected = Block::new(DAG_CBOR, fvm_ipld_encoding::to_vec::<[(); 0]>(&[]).unwrap());
assert_eq!(expected, EMPTY_ARRAY_BLOCK);
}
fn put_empty_blocks(blockstore: impl Blockstore) -> anyhow::Result<()> {
use fvm_shared::EMPTY_ARR_CID;
let empty_arr_cid = blockstore.put(Blake2b256, &EMPTY_ARRAY_BLOCK)?;
debug_assert!(
empty_arr_cid == EMPTY_ARR_CID,
"empty CID sanity check failed",
);
Ok(())
}