Skip to content

Commit

Permalink
feat: runtime: Add send_generalized
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek committed Jan 29, 2023
1 parent 7feeb4b commit 70bac1a
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 74 deletions.
56 changes: 12 additions & 44 deletions runtime/src/runtime/fvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use fvm_shared::sector::{
};
use fvm_shared::sys::SendFlags;
use fvm_shared::version::NetworkVersion;
use fvm_shared::{ActorID, MethodNum};
use fvm_shared::{ActorID, MethodNum, Response};
use num_traits::FromPrimitive;
use serde::de::DeserializeOwned;
use serde::Serialize;
Expand Down Expand Up @@ -292,56 +292,20 @@ where
&self.blockstore
}

fn send(
fn send_generalized(
&self,
to: &Address,
method: MethodNum,
params: Option<IpldBlock>,
value: TokenAmount,
) -> Result<Option<IpldBlock>, ActorError> {
gas_limit: Option<u64>,
flags: SendFlags,
) -> Result<Response, ErrorNumber> {
if self.in_transaction {
return Err(actor_error!(assertion_failed; "send is not allowed during transaction"));
}
match fvm::send::send(to, method, params, value, None, SendFlags::empty()) {
Ok(ret) => {
if ret.exit_code.is_success() {
Ok(ret.return_data)
} else {
Err(ActorError::checked(
ret.exit_code,
format!(
"send to {} method {} aborted with code {}",
to, method, ret.exit_code
),
ret.return_data,
))
}
}
Err(err) => Err(match err {
// Some of these errors are from operations in the Runtime or SDK layer
// before or after the underlying VM send syscall.
ErrorNumber::NotFound => {
// This means that the receiving actor doesn't exist.
// TODO: we can't reasonably determine the correct "exit code" here.
actor_error!(unspecified; "receiver not found")
}
ErrorNumber::InsufficientFunds => {
// This means that the send failed because we have insufficient funds. We will
// get a _syscall error_, not an exit code, because the target actor will not
// run (and therefore will not exit).
actor_error!(insufficient_funds; "not enough funds")
}
ErrorNumber::LimitExceeded => {
// This means we've exceeded the recursion limit.
// TODO: Define a better exit code.
actor_error!(assertion_failed; "recursion limit exceeded")
}
err => {
// We don't expect any other syscall exit codes.
actor_error!(assertion_failed; "unexpected error: {}", err)
}
}),
return Err(ErrorNumber::IllegalOperation);
}

fvm::send::send(to, method, params, value, gas_limit, flags)
}

fn new_actor_address(&mut self) -> Result<Address, ActorError> {
Expand Down Expand Up @@ -387,6 +351,10 @@ where
fn base_fee(&self) -> TokenAmount {
fvm::network::base_fee()
}

fn read_only(&self) -> bool {
fvm::vm::read_only()
}
}

impl<B> Primitives for FvmRuntime<B>
Expand Down
67 changes: 64 additions & 3 deletions runtime/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use fvm_shared::sector::{
WindowPoStVerifyInfo,
};
use fvm_shared::version::NetworkVersion;
use fvm_shared::{ActorID, MethodNum};
use fvm_shared::{ActorID, MethodNum, Response};
use serde::de::DeserializeOwned;
use serde::Serialize;

pub use self::actor_code::*;
pub use self::policy::*;
pub use self::randomness::DomainSeparationTag;
use crate::runtime::builtins::Type;
use crate::ActorError;
use crate::{actor_error, ActorError};

mod actor_code;
pub mod builtins;
Expand All @@ -41,6 +41,8 @@ pub(crate) mod empty;

pub use empty::EMPTY_ARR_CID;
use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared::error::{ErrorNumber, ExitCode};
use fvm_shared::sys::SendFlags;

/// Runtime is the VM's internal runtime object.
/// this is everything that is accessible to actors, beyond parameters.
Expand Down Expand Up @@ -142,7 +144,63 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy {
method: MethodNum,
params: Option<IpldBlock>,
value: TokenAmount,
) -> Result<Option<IpldBlock>, ActorError>;
) -> Result<Option<IpldBlock>, ActorError> {
match self.send_generalized(to, method, params, value, None, SendFlags::empty()) {
Ok(ret) => {
if ret.exit_code.is_success() {
Ok(ret.return_data)
} else {
Err(ActorError::checked(
ret.exit_code,
format!(
"send to {} method {} aborted with code {}",
to, method, ret.exit_code
),
ret.return_data,
))
}
}
Err(err) => Err(match err {
// Some of these errors are from operations in the Runtime or SDK layer
// before or after the underlying VM send syscall.
ErrorNumber::NotFound => {
// This means that the receiving actor doesn't exist.
// TODO: we can't reasonably determine the correct "exit code" here.
actor_error!(unspecified; "receiver not found")
}
ErrorNumber::InsufficientFunds => {
// This means that the send failed because we have insufficient funds. We will
// get a _syscall error_, not an exit code, because the target actor will not
// run (and therefore will not exit).
actor_error!(insufficient_funds; "not enough funds")
}
ErrorNumber::LimitExceeded => {
// This means we've exceeded the recursion limit.
// TODO: Define a better exit code.
actor_error!(assertion_failed; "recursion limit exceeded")
}
ErrorNumber::ReadOnly => ActorError::unchecked(
ExitCode::USR_READ_ONLY,
"attempted to mutate state while in readonly mode".into(),
),
err => {
// We don't expect any other syscall exit codes.
actor_error!(assertion_failed; "unexpected error: {}", err)
}
}),
}
}

/// Generalization of [`Runtime::send`] that allows the caller to specify a gas limit and send flags.
fn send_generalized(
&self,
to: &Address,
method: MethodNum,
params: Option<IpldBlock>,
value: TokenAmount,
gas_limit: Option<u64>,
flags: SendFlags,
) -> Result<Response, ErrorNumber>;

/// Computes an address for a new actor. The returned address is intended to uniquely refer to
/// the actor even in the event of a chain re-org (whereas an ID-address might refer to a
Expand Down Expand Up @@ -187,6 +245,9 @@ pub trait Runtime: Primitives + Verifier + RuntimePolicy {

/// Returns the gas base fee (cost per unit) for the current epoch.
fn base_fee(&self) -> TokenAmount;

/// Returns true if the system is in read-only mode.
fn read_only(&self) -> bool;
}

/// Message information available to the actor about executing message.
Expand Down
40 changes: 27 additions & 13 deletions runtime/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ use fvm_shared::commcid::{FIL_COMMITMENT_SEALED, FIL_COMMITMENT_UNSEALED};
use fvm_shared::consensus::ConsensusFault;
use fvm_shared::crypto::signature::Signature;
use fvm_shared::econ::TokenAmount;
use fvm_shared::error::ExitCode;
use fvm_shared::error::{ErrorNumber, ExitCode};
use fvm_shared::piece::PieceInfo;
use fvm_shared::randomness::RANDOMNESS_LENGTH;
use fvm_shared::sector::{
AggregateSealVerifyInfo, AggregateSealVerifyProofAndInfos, RegisteredSealProof,
ReplicaUpdateInfo, SealVerifyInfo, WindowPoStVerifyInfo,
};
use fvm_shared::version::NetworkVersion;
use fvm_shared::{ActorID, MethodNum};
use fvm_shared::{ActorID, MethodNum, Response};

use multihash::derive::Multihash;
use multihash::MultihashDigest;
Expand All @@ -43,6 +43,7 @@ use crate::runtime::{
use crate::{actor_error, ActorError};

use fvm_ipld_encoding::ipld_block::IpldBlock;
use fvm_shared::sys::SendFlags;

lazy_static::lazy_static! {
pub static ref SYSTEM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/system");
Expand Down Expand Up @@ -306,10 +307,13 @@ pub struct ExpectedMessage {
pub method: MethodNum,
pub params: Option<IpldBlock>,
pub value: TokenAmount,
pub gas_limit: Option<u64>,
pub send_flags: SendFlags,

// returns from applying expectedMessage
pub send_return: Option<IpldBlock>,
pub exit_code: ExitCode,
pub send_error: Option<ErrorNumber>,
}

#[derive(Debug)]
Expand Down Expand Up @@ -576,6 +580,9 @@ impl<BS: Blockstore> MockRuntime<BS> {
value,
send_return,
exit_code,
send_flags: SendFlags::default(),
gas_limit: None,
send_error: None,
})
}

Expand Down Expand Up @@ -938,16 +945,18 @@ impl<BS: Blockstore> Runtime for MockRuntime<BS> {
&self.store
}

fn send(
fn send_generalized(
&self,
to: &Address,
method: MethodNum,
params: Option<IpldBlock>,
value: TokenAmount,
) -> Result<Option<IpldBlock>, ActorError> {
gas_limit: Option<u64>,
send_flags: SendFlags,
) -> Result<Response, ErrorNumber> {
self.require_in_call();
if self.in_transaction {
return Err(actor_error!(assertion_failed; "side-effect within transaction"));
return Ok(Response { exit_code: ExitCode::USR_ASSERTION_FAILED, return_data: None });
}

assert!(
Expand All @@ -965,22 +974,22 @@ impl<BS: Blockstore> Runtime for MockRuntime<BS> {
assert_eq!(expected_msg.method, method);
assert_eq!(expected_msg.params, params);
assert_eq!(expected_msg.value, value);
assert_eq!(expected_msg.gas_limit, gas_limit, "gas limit did not match expectation");
assert_eq!(expected_msg.send_flags, send_flags, "send flags did not match expectation");

if let Some(e) = expected_msg.send_error {
return Err(e);
}

{
let mut balance = self.balance.borrow_mut();
if value > *balance {
return Err(ActorError::unchecked(
ExitCode::SYS_SENDER_STATE_INVALID,
format!("cannot send value: {:?} exceeds balance: {:?}", value, *balance),
));
return Err(ErrorNumber::InsufficientFunds);
}
*balance -= value;
}

match expected_msg.exit_code {
ExitCode::OK => Ok(expected_msg.send_return),
x => Err(ActorError::unchecked(x, "Expected message Fail".to_string())),
}
Ok(Response { exit_code: expected_msg.exit_code, return_data: expected_msg.send_return })
}

fn new_actor_address(&mut self) -> Result<Address, ActorError> {
Expand Down Expand Up @@ -1059,6 +1068,11 @@ impl<BS: Blockstore> Runtime for MockRuntime<BS> {
fn base_fee(&self) -> TokenAmount {
self.base_fee.clone()
}

fn read_only(&self) -> bool {
// Unsupported for unit tests
false
}
}

impl<BS> Primitives for MockRuntime<BS> {
Expand Down
Loading

0 comments on commit 70bac1a

Please sign in to comment.