Skip to content

Commit

Permalink
Add Kernel::send<K> type parameter specifying kernel type for receiver (
Browse files Browse the repository at this point in the history
  • Loading branch information
anorth authored Jun 21, 2023
1 parent 47de0e9 commit db8c0b1
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 296 deletions.
133 changes: 64 additions & 69 deletions fvm/src/kernel/default.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,70 @@ where
fn machine(&self) -> &<Self::CallManager as CallManager>::Machine {
self.call_manager.machine()
}

fn send<K: Kernel<CallManager = C>>(
&mut self,
recipient: &Address,
method: MethodNum,
params_id: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<SendResult> {
let from = self.actor_id;
let read_only = self.read_only || flags.read_only();

if read_only && !value.is_zero() {
return Err(syscall_error!(ReadOnly; "cannot transfer value when read-only").into());
}

// Load parameters.
let params = if params_id == NO_DATA_BLOCK_ID {
None
} else {
Some(self.blocks.get(params_id)?.clone())
};

// Make sure we can actually store the return block.
if self.blocks.is_full() {
return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
}

// Send.
let result = self.call_manager.with_transaction(|cm| {
cm.send::<K>(
from, *recipient, method, params, value, gas_limit, read_only,
)
})?;

// Store result and return.
Ok(match result {
InvocationResult {
exit_code,
value: Some(blk),
} => {
let block_stat = blk.stat();
let block_id = self
.blocks
.put(blk)
.or_fatal()
.context("failed to store a valid return value")?;
SendResult {
block_id,
block_stat,
exit_code,
}
}
InvocationResult {
exit_code,
value: None,
} => SendResult {
block_id: NO_DATA_BLOCK_ID,
block_stat: BlockStat { codec: 0, size: 0 },
exit_code,
},
})
}
}

impl<C> DefaultKernel<C>
Expand Down Expand Up @@ -358,75 +422,6 @@ where
}
}

impl<C> SendOps for DefaultKernel<C>
where
C: CallManager,
{
fn send(
&mut self,
recipient: &Address,
method: MethodNum,
params_id: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<SendResult> {
let from = self.actor_id;
let read_only = self.read_only || flags.read_only();

if read_only && !value.is_zero() {
return Err(syscall_error!(ReadOnly; "cannot transfer value when read-only").into());
}

// Load parameters.
let params = if params_id == NO_DATA_BLOCK_ID {
None
} else {
Some(self.blocks.get(params_id)?.clone())
};

// Make sure we can actually store the return block.
if self.blocks.is_full() {
return Err(syscall_error!(LimitExceeded; "cannot store return block").into());
}

// Send.
let result = self.call_manager.with_transaction(|cm| {
cm.send::<Self>(
from, *recipient, method, params, value, gas_limit, read_only,
)
})?;

// Store result and return.
Ok(match result {
InvocationResult {
exit_code,
value: Some(blk),
} => {
let block_stat = blk.stat();
let block_id = self
.blocks
.put(blk)
.or_fatal()
.context("failed to store a valid return value")?;
SendResult {
block_id,
block_stat,
exit_code,
}
}
InvocationResult {
exit_code,
value: None,
} => SendResult {
block_id: NO_DATA_BLOCK_ID,
block_stat: BlockStat { codec: 0, size: 0 },
exit_code,
},
})
}
}

impl<C> CircSupplyOps for DefaultKernel<C>
where
C: CallManager,
Expand Down
31 changes: 17 additions & 14 deletions fvm/src/kernel/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ pub trait Kernel:
+ NetworkOps
+ RandomnessOps
+ SelfOps
+ SendOps
+ LimiterOps
+ 'static
{
Expand Down Expand Up @@ -97,6 +96,23 @@ pub trait Kernel:

/// 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<SendResult>;
}

/// Network-related operations.
Expand Down Expand Up @@ -209,19 +225,6 @@ pub trait ActorOps {
fn balance_of(&self, actor_id: ActorID) -> Result<TokenAmount>;
}

/// Operations to send messages to other actors.
pub trait SendOps {
fn send(
&mut self,
recipient: &Address,
method: u64,
params: BlockId,
value: &TokenAmount,
gas_limit: Option<Gas>,
flags: SendFlags,
) -> Result<SendResult>;
}

/// Operations to query the circulating supply.
pub trait CircSupplyOps {
/// Returns the total token supply in circulation at the beginning of the current epoch.
Expand Down
6 changes: 3 additions & 3 deletions fvm/src/syscalls/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::Kernel;
/// 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(
context: Context<'_, impl Kernel>,
pub fn send<K: Kernel>(
context: Context<'_, K>,
recipient_off: u32,
recipient_len: u32,
method: u64,
Expand Down Expand Up @@ -43,7 +43,7 @@ pub fn send(
exit_code,
} = context
.kernel
.send(&recipient, method, params_id, &value, gas_limit, flags)?;
.send::<K>(&recipient, method, params_id, &value, gas_limit, flags)?;

Ok(sys::out::send::Send {
exit_code: exit_code.value(),
Expand Down
Loading

0 comments on commit db8c0b1

Please sign in to comment.