Skip to content

Commit

Permalink
feat: move send/upgrade out of the kernel
Browse files Browse the repository at this point in the history
Make them higher-level ops like everything else. I considered combining
them into a single "call op" however, not all users may want to
implement actor upgrading.

I've also moved the generic from the methods to the trait itself. This
resolves anorth's comment on the `TestKernel` by allowing additional
bounds to be specified by the _implementer_, instead of having all the
bounds encoded on the trait itself:

> Note that KK, the type of the kernel to crate for the receiving actor,
> is ignored, and Self is passed as the type parameter for the nested
> call. If we could find the correct bound to specify KK for the call,
> we would. This restricts the ability for the TestKernel to itself be
> wrapped by another kernel type.
  • Loading branch information
Stebalien committed Dec 14, 2023
1 parent a627137 commit 6a3f1ed
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 104 deletions.
62 changes: 34 additions & 28 deletions fvm/src/kernel/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,34 @@ where
self.call_manager.machine()
}

fn send<K: Kernel<CallManager = C>>(
fn limiter_mut(&mut self) -> &mut Self::Limiter {
self.call_manager.limiter_mut()
}

fn gas_available(&self) -> Gas {
self.call_manager.gas_tracker().gas_available()
}

fn charge_gas(&self, name: &str, compute: Gas) -> Result<GasTimer> {
self.call_manager.gas_tracker().charge_gas(name, compute)
}
}

impl<C> DefaultKernel<C>
where
C: CallManager,
{
/// Returns `Some(actor_state)` or `None` if this actor has been deleted.
fn get_self(&self) -> Result<Option<ActorState>> {
self.call_manager.get_actor(self.actor_id)
}
}

impl<K> SendOps<K> for DefaultKernel<K::CallManager>
where
K: Kernel,
{
fn send(
&mut self,
recipient: &Address,
method: MethodNum,
Expand Down Expand Up @@ -171,12 +198,13 @@ where
},
})
}
}

fn upgrade_actor<K: Kernel<CallManager = C>>(
&mut self,
new_code_cid: Cid,
params_id: BlockId,
) -> Result<CallResult> {
impl<K> UpgradeOps<K> for DefaultKernel<K::CallManager>
where
K: Kernel,
{
fn upgrade_actor(&mut self, new_code_cid: Cid, params_id: BlockId) -> Result<CallResult> {
if self.read_only {
return Err(
syscall_error!(ReadOnly, "upgrade_actor cannot be called while read-only").into(),
Expand Down Expand Up @@ -265,28 +293,6 @@ where
Err(err) => Err(err),
}
}

fn limiter_mut(&mut self) -> &mut Self::Limiter {
self.call_manager.limiter_mut()
}

fn gas_available(&self) -> Gas {
self.call_manager.gas_tracker().gas_available()
}

fn charge_gas(&self, name: &str, compute: Gas) -> Result<GasTimer> {
self.call_manager.gas_tracker().charge_gas(name, compute)
}
}

impl<C> DefaultKernel<C>
where
C: CallManager,
{
/// Returns `Some(actor_state)` or `None` if this actor has been deleted.
fn get_self(&self) -> Result<Option<ActorState>> {
self.call_manager.get_actor(self.actor_id)
}
}

impl<C> SelfOps for DefaultKernel<C>
Expand Down
23 changes: 2 additions & 21 deletions fvm/src/kernel/filecoin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ pub trait FilecoinKernel: Kernel {
#[delegate(NetworkOps, where = "C: CallManager")]
#[delegate(RandomnessOps, where = "C: CallManager")]
#[delegate(SelfOps, where = "C: CallManager")]
#[delegate(SendOps<K>, generics = "K", where = "C: CallManager, K: FilecoinKernel")]
#[delegate(UpgradeOps<K>, generics = "K", where = "C: CallManager, K: FilecoinKernel")]
pub struct DefaultFilecoinKernel<C>(pub DefaultKernel<C>);

impl<C> FilecoinKernel for DefaultFilecoinKernel<C>
Expand Down Expand Up @@ -260,27 +262,6 @@ where
self.0.machine()
}

fn send<K: Kernel<CallManager = C>>(
&mut self,
recipient: &Address,
method: u64,
params: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<CallResult> {
self.0
.send::<Self>(recipient, method, params, value, gas_limit, flags)
}

fn upgrade_actor<K: Kernel<CallManager = Self::CallManager>>(
&mut self,
new_code_cid: Cid,
params_id: BlockId,
) -> Result<CallResult> {
self.0.upgrade_actor::<Self>(new_code_cid, params_id)
}

fn new(
mgr: C,
blocks: BlockRegistry,
Expand Down
57 changes: 32 additions & 25 deletions fvm/src/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,29 +66,6 @@ pub trait Kernel: SyscallHandler<Self> + 'static {
/// The kernel's underlying "machine".
fn machine(&self) -> &<Self::CallManager as CallManager>::Machine;

/// Sends a message to another actor.
/// The method type parameter K is the type of the kernel to instantiate for
/// the receiving actor. This is necessary to support wrapping a kernel, so the outer
/// kernel can specify its Self as the receiver's kernel type, rather than the wrapped
/// kernel specifying its Self.
/// This method is part of the Kernel trait so it can refer to the Self::CallManager
/// associated type necessary to constrain K.
fn send<K: Kernel<CallManager = Self::CallManager>>(
&mut self,
recipient: &Address,
method: u64,
params: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<CallResult>;

fn upgrade_actor<K: Kernel<CallManager = Self::CallManager>>(
&mut self,
new_code_cid: Cid,
params_id: BlockId,
) -> Result<CallResult>;

/// Give access to the limiter of the underlying call manager.
fn limiter_mut(&mut self) -> &mut Self::Limiter;

Expand Down Expand Up @@ -121,6 +98,34 @@ pub trait MessageOps {
fn msg_context(&self) -> Result<MessageContext>;
}

/// The actor calling operations.
#[delegatable_trait]
pub trait SendOps<K: Kernel = Self> {
/// Sends a message to another actor.
/// The method type parameter K is the type of the kernel to instantiate for
/// the receiving actor. This is necessary to support wrapping a kernel, so the outer
/// kernel can specify its Self as the receiver's kernel type, rather than the wrapped
/// kernel specifying its Self.
/// This method is part of the Kernel trait so it can refer to the Self::CallManager
/// associated type necessary to constrain K.
fn send(
&mut self,
recipient: &Address,
method: u64,
params: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<CallResult>;
}

/// The actor upgrade operations.
#[delegatable_trait]
pub trait UpgradeOps<K: Kernel = Self> {
/// Upgrades the running actor to the specified code CID.
fn upgrade_actor(&mut self, new_code_cid: Cid, params_id: BlockId) -> Result<CallResult>;
}

/// The IPLD subset of the kernel.
#[delegatable_trait]
pub trait IpldBlockOps {
Expand Down Expand Up @@ -291,15 +296,16 @@ pub trait EventOps {
pub use {
ambassador_impl_CryptoOps, ambassador_impl_DebugOps, ambassador_impl_EventOps,
ambassador_impl_IpldBlockOps, ambassador_impl_MessageOps, ambassador_impl_NetworkOps,
ambassador_impl_RandomnessOps, ambassador_impl_SelfOps,
ambassador_impl_RandomnessOps, ambassador_impl_SelfOps, ambassador_impl_SendOps,
ambassador_impl_UpgradeOps,
};

/// Import this module (with a glob) if you're implementing a kernel, _especially_ if you want to
/// use ambassador to delegate the implementation.
pub mod prelude {
pub use super::{
ActorOps, CryptoOps, DebugOps, EventOps, IpldBlockOps, MessageOps, NetworkOps,
RandomnessOps, SelfOps,
RandomnessOps, SelfOps, SendOps, UpgradeOps,
};
pub use super::{Block, BlockId, BlockRegistry, BlockStat, CallResult, Kernel, SyscallHandler};
pub use crate::gas::{Gas, GasTimer, PriceList};
Expand All @@ -323,6 +329,7 @@ pub mod prelude {
ambassador_impl_ActorOps, ambassador_impl_CryptoOps, ambassador_impl_DebugOps,
ambassador_impl_EventOps, ambassador_impl_IpldBlockOps, ambassador_impl_MessageOps,
ambassador_impl_NetworkOps, ambassador_impl_RandomnessOps, ambassador_impl_SelfOps,
ambassador_impl_SendOps, ambassador_impl_UpgradeOps,
};
}

Expand Down
7 changes: 4 additions & 3 deletions fvm/src/syscalls/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use fvm_shared::{sys, ActorID};
use super::error::Abort;
use super::Context;
use super::ControlFlow;
use crate::kernel::UpgradeOps;
use crate::kernel::{ActorOps, CallResult, ClassifyResult, Result};
use crate::{syscall_error, Kernel};

Expand Down Expand Up @@ -111,8 +112,8 @@ pub fn create_actor(
context.kernel.create_actor(typ, actor_id, addr)
}

pub fn upgrade_actor<K: Kernel>(
context: Context<'_, K>,
pub fn upgrade_actor(
context: Context<'_, impl UpgradeOps + Kernel>,
new_code_cid_off: u32,
params_id: u32,
) -> ControlFlow<sys::out::send::Send> {
Expand All @@ -121,7 +122,7 @@ pub fn upgrade_actor<K: Kernel>(
Err(err) => return err.into(),
};

match context.kernel.upgrade_actor::<K>(cid, params_id) {
match context.kernel.upgrade_actor(cid, params_id) {
Ok(CallResult {
block_id,
block_stat,
Expand Down
6 changes: 5 additions & 1 deletion fvm/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::gas::{Gas, GasInstant, GasTimer, WasmGasPrices};
use crate::kernel::filecoin::{DefaultFilecoinKernel, FilecoinKernel};
use crate::kernel::{
ActorOps, CryptoOps, DebugOps, EventOps, ExecutionError, IpldBlockOps, MessageOps, NetworkOps,
RandomnessOps, SelfOps, SyscallHandler,
RandomnessOps, SelfOps, SendOps, SyscallHandler, UpgradeOps,
};

use crate::machine::limiter::MemoryLimiter;
Expand Down Expand Up @@ -257,6 +257,8 @@ where
K: Kernel
+ ActorOps
+ IpldBlockOps
+ SendOps
+ UpgradeOps
+ CryptoOps
+ DebugOps
+ EventOps
Expand Down Expand Up @@ -345,6 +347,8 @@ impl<K> SyscallHandler<K> for DefaultFilecoinKernel<K::CallManager>
where
K: FilecoinKernel
+ ActorOps
+ SendOps
+ UpgradeOps
+ IpldBlockOps
+ CryptoOps
+ DebugOps
Expand Down
9 changes: 4 additions & 5 deletions fvm/src/syscalls/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ use fvm_shared::sys::{self, SendFlags};

use super::Context;
use crate::gas::Gas;
use crate::kernel::{CallResult, ClassifyResult, Result};
use crate::Kernel;
use crate::kernel::{CallResult, ClassifyResult, Kernel, Result, SendOps};

/// Send a message to another actor. The result is placed as a CBOR-encoded
/// receipt in the block registry, and can be retrieved by the returned BlockId.
#[allow(clippy::too_many_arguments)]
pub fn send<K: Kernel>(
context: Context<'_, K>,
pub fn send(
context: Context<'_, impl SendOps + Kernel>,
recipient_off: u32,
recipient_len: u32,
method: u64,
Expand Down Expand Up @@ -43,7 +42,7 @@ pub fn send<K: Kernel>(
exit_code,
} = context
.kernel
.send::<K>(&recipient, method, params_id, &value, gas_limit, flags)?;
.send(&recipient, method, params_id, &value, gas_limit, flags)?;

Ok(sys::out::send::Send {
exit_code: exit_code.value(),
Expand Down
23 changes: 2 additions & 21 deletions testing/conformance/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ type InnerTestKernel = DefaultFilecoinKernel<DefaultCallManager<TestMachine>>;
#[delegate(NetworkOps)]
#[delegate(RandomnessOps)]
#[delegate(SelfOps)]
#[delegate(SendOps<K>, generics = "K", where = "K: FilecoinKernel")]
#[delegate(UpgradeOps<K>, generics = "K", where = "K: FilecoinKernel")]
pub struct TestKernel(pub InnerTestKernel);

impl TestKernel {
Expand Down Expand Up @@ -215,27 +217,6 @@ impl Kernel for TestKernel {
self.0.machine()
}

fn send<KK>(
&mut self,
recipient: &Address,
method: u64,
params: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<CallResult> {
// Note that KK, the type of the kernel to crate for the receiving actor, is ignored,
// and Self is passed as the type parameter for the nested call.
// If we could find the correct bound to specify KK for the call, we would.
// This restricts the ability for the TestKernel to itself be wrapped by another kernel type.
self.0
.send::<Self>(recipient, method, params, value, gas_limit, flags)
}

fn upgrade_actor<KK>(&mut self, new_code_cid: Cid, params_id: BlockId) -> Result<CallResult> {
self.0.upgrade_actor::<Self>(new_code_cid, params_id)
}

fn limiter_mut(&mut self) -> &mut Self::Limiter {
self.0.limiter_mut()
}
Expand Down

0 comments on commit 6a3f1ed

Please sign in to comment.