use std::ops::RangeInclusive;
use anyhow::{Context as _, anyhow};
use cid::Cid;
use fvm_ipld_blockstore::{Blockstore, Buffered};
use fvm_ipld_encoding::CborStore;
use fvm_shared::ActorID;
use fvm_shared::address::Address;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ErrorNumber;
use fvm_shared::version::NetworkVersion;
use log::debug;
use super::{Engine, Machine, MachineContext};
use crate::blockstore::BufferedBlockstore;
use crate::externs::Externs;
use crate::kernel::{ClassifyResult, Context as _, Result};
use crate::machine::Manifest;
use crate::state_tree::{ActorState, StateTree};
use crate::syscall_error;
use crate::system_actor::State as SystemActorState;
pub struct DefaultMachine<B, E> {
context: MachineContext,
engine: Engine,
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(
engine: &Engine,
context: &MachineContext,
blockstore: B,
externs: E,
) -> anyhow::Result<Self> {
const SUPPORTED_VERSIONS: RangeInclusive<NetworkVersion> =
NetworkVersion::V15..=NetworkVersion::V17;
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
));
}
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)?;
#[cfg(not(any(test, feature = "testing")))]
engine.preload(state_tree.store(), builtin_actors.builtin_actor_codes())?;
let randomness: [u8; 16] = rand::random();
Ok(DefaultMachine {
context: context.clone(),
engine: engine.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;
fn engine(&self) -> &Engine {
&self.engine
}
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 create_actor(&mut self, addr: &Address, act: ActorState) -> Result<ActorID> {
let state_tree = self.state_tree_mut();
let addr_id = state_tree
.register_new_address(addr)
.context("failed to register new address")
.or_fatal()?;
state_tree
.set_actor(&Address::new_id(addr_id), act)
.context("failed to set actor")
.or_fatal()?;
Ok(addr_id)
}
fn transfer(&mut self, from: ActorID, to: ActorID, value: &TokenAmount) -> Result<()> {
if value.is_negative() {
return Err(syscall_error!(IllegalArgument;
"attempted to transfer negative transfer value {}", value)
.into());
}
let mut from_actor = self
.state_tree
.get_actor_id(from)?
.context("cannot transfer from non-existent sender")
.or_error(ErrorNumber::InsufficientFunds)?;
if &from_actor.balance < value {
return Err(syscall_error!(InsufficientFunds; "sender does not have funds to transfer (balance {}, transfer {})", &from_actor.balance, value).into());
}
if from == to {
debug!("attempting to self-transfer: noop (from/to: {})", from);
return Ok(());
}
let mut to_actor = self
.state_tree
.get_actor_id(to)?
.context("cannot transfer to non-existent receiver")
.or_error(ErrorNumber::NotFound)?;
from_actor.deduct_funds(value)?;
to_actor.deposit_funds(value);
self.state_tree.set_actor_id(from, from_actor)?;
self.state_tree.set_actor_id(to, to_actor)?;
log::trace!("transferred {} from {} to {}", value, from, to);
Ok(())
}
fn into_store(self) -> Self::Blockstore {
self.state_tree.into_store()
}
fn machine_id(&self) -> &str {
&self.id
}
}