diff --git a/core-backend/src/env.rs b/core-backend/src/env.rs index 099a4fe50cf..f2778806288 100644 --- a/core-backend/src/env.rs +++ b/core-backend/src/env.rs @@ -48,10 +48,9 @@ use gear_sandbox::{ default_executor::{ EnvironmentDefinitionBuilder, Instance, Memory as DefaultExecutorMemory, Store, }, - AsContextExt, HostError, HostFuncType, IntoValue, ReturnValue, SandboxEnvironmentBuilder, - SandboxInstance, SandboxMemory, SandboxStore, Value, + AsContextExt, HostError, HostFuncType, ReturnValue, SandboxEnvironmentBuilder, SandboxInstance, + SandboxMemory, SandboxStore, Value, }; -use gear_sandbox_env::WasmReturnValue; use gear_wasm_instrument::{ syscalls::SysCallName::{self, *}, GLOBAL_NAME_GAS, STACK_END_EXPORT_NAME, @@ -102,56 +101,13 @@ impl TryFrom for u64 { } } -macro_rules! wrap_common_func_internal_ret{ - ($func:path, $($arg_no:expr),*) => { - |ctx, args: &[Value]| -> Result { - let mut ctx = CallerWrap::prepare(ctx).map_err(|_| HostError)?; - $func(&mut ctx, $(SandboxValue(args[$arg_no]).try_into()?,)*) - .map(|(gas, r)| WasmReturnValue { - gas: gas as i64, - inner: r.into_value().into(), - }) - } - } -} - -macro_rules! wrap_common_func_internal_no_ret{ - ($func:path, $($arg_no:expr),*) => { - |ctx, _args: &[Value]| -> Result { - let mut ctx = CallerWrap::prepare(ctx).map_err(|_| HostError)?; - $func(&mut ctx, $(SandboxValue(_args[$arg_no]).try_into()?,)*) - .map(|(gas, _)| WasmReturnValue { - gas: gas as i64, - inner: ReturnValue::Unit, - }) +macro_rules! wrap_syscall { + ($func:ident) => { + |caller, args| { + let mut caller = CallerWrap::prepare(caller)?; + FuncsHandler::execute(&mut caller, args, FuncsHandler::$func) } - } -} - -#[rustfmt::skip] -macro_rules! wrap_common_func { - ($func:path, () -> ()) => { wrap_common_func_internal_no_ret!($func,) }; - ($func:path, (1) -> ()) => { wrap_common_func_internal_no_ret!($func, 0) }; - ($func:path, (2) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1) }; - ($func:path, (3) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2) }; - ($func:path, (4) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3) }; - ($func:path, (5) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4) }; - ($func:path, (6) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4, 5) }; - ($func:path, (7) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4, 5, 6) }; - ($func:path, (8) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4, 5, 6, 7) }; - ($func:path, (9) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4, 5, 6, 7, 8) }; - ($func:path, (10) -> ()) => { wrap_common_func_internal_no_ret!($func, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9) }; - - ($func:path, () -> (1)) => { wrap_common_func_internal_ret!($func,) }; - ($func:path, (1) -> (1)) => { wrap_common_func_internal_ret!($func, 0) }; - ($func:path, (2) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1) }; - ($func:path, (3) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2) }; - ($func:path, (4) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3) }; - ($func:path, (5) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3, 4) }; - ($func:path, (6) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3, 4, 5) }; - ($func:path, (7) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3, 4, 5, 6) }; - ($func:path, (8) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3, 4, 5, 6, 7) }; - ($func:path, (9) -> (1)) => { wrap_common_func_internal_ret!($func, 0, 1, 2, 3, 4, 5, 6, 7, 8) }; + }; } fn store_host_state_mut( @@ -239,11 +195,8 @@ where f: HostFuncType>, ) { if self.forbidden_funcs.contains(&name) { - self.env_def_builder.add_host_func( - "env", - name.to_str(), - wrap_common_func!(FuncsHandler::forbidden, (1) -> ()), - ); + self.env_def_builder + .add_host_func("env", name.to_str(), wrap_syscall!(forbidden)); } else { self.env_def_builder.add_host_func("env", name.to_str(), f); } @@ -274,67 +227,62 @@ where { #[rustfmt::skip] fn bind_funcs(builder: &mut EnvBuilder) { - builder.add_func(BlockHeight, wrap_common_func!(FuncsHandler::block_height, (2) -> ())); - builder.add_func(BlockTimestamp,wrap_common_func!(FuncsHandler::block_timestamp, (2) -> ())); - builder.add_func(CreateProgram, wrap_common_func!(FuncsHandler::create_program, (8) -> ())); - builder.add_func(CreateProgramWGas, wrap_common_func!(FuncsHandler::create_program_wgas, (9) -> ())); - builder.add_func(Debug, wrap_common_func!(FuncsHandler::debug, (3) -> ())); - builder.add_func(Panic, wrap_common_func!(FuncsHandler::panic, (3) -> ())); - builder.add_func(OomPanic, wrap_common_func!(FuncsHandler::oom_panic, (1) -> ())); - builder.add_func(Exit, wrap_common_func!(FuncsHandler::exit, (2) -> ())); - builder.add_func(ReplyCode, wrap_common_func!(FuncsHandler::reply_code, (2) -> ())); - builder.add_func(SignalCode, wrap_common_func!(FuncsHandler::signal_code, (2) -> ())); - builder.add_func(ReserveGas, wrap_common_func!(FuncsHandler::reserve_gas, (4) -> ())); - builder.add_func(ReplyDeposit, wrap_common_func!(FuncsHandler::reply_deposit, (4) -> ())); - builder.add_func(UnreserveGas, wrap_common_func!(FuncsHandler::unreserve_gas, (3) -> ())); - builder.add_func(GasAvailable, wrap_common_func!(FuncsHandler::gas_available, (2) -> ())); - builder.add_func(Leave, wrap_common_func!(FuncsHandler::leave, (1) -> ())); - builder.add_func(MessageId, wrap_common_func!(FuncsHandler::message_id, (2) -> ())); - builder.add_func(PayProgramRent, wrap_common_func!(FuncsHandler::pay_program_rent, (3) -> ())); - builder.add_func(ProgramId, wrap_common_func!(FuncsHandler::program_id, (2) -> ())); - builder.add_func(Random, wrap_common_func!(FuncsHandler::random, (3) -> ())); - builder.add_func(Read, wrap_common_func!(FuncsHandler::read, (5) -> ())); - builder.add_func(Reply, wrap_common_func!(FuncsHandler::reply, (5) -> ())); - builder.add_func(ReplyCommit, wrap_common_func!(FuncsHandler::reply_commit, (3) -> ())); - builder.add_func(ReplyCommitWGas, wrap_common_func!(FuncsHandler::reply_commit_wgas, (4) -> ())); - builder.add_func(ReplyPush, wrap_common_func!(FuncsHandler::reply_push, (4) -> ())); - builder.add_func(ReplyTo, wrap_common_func!(FuncsHandler::reply_to, (2) -> ())); - builder.add_func(SignalFrom, wrap_common_func!(FuncsHandler::signal_from, (2) -> ())); - builder.add_func(ReplyWGas, wrap_common_func!(FuncsHandler::reply_wgas, (6) -> ())); - builder.add_func(ReplyInput, wrap_common_func!(FuncsHandler::reply_input, (5) -> ())); - builder.add_func(ReplyPushInput, wrap_common_func!(FuncsHandler::reply_push_input, (4) -> ())); - builder.add_func(ReplyInputWGas, wrap_common_func!(FuncsHandler::reply_input_wgas, (6) -> ())); - builder.add_func(Send, wrap_common_func!(FuncsHandler::send, (6) -> ())); - builder.add_func(SendCommit, wrap_common_func!(FuncsHandler::send_commit, (5) -> ())); - builder.add_func(SendCommitWGas, wrap_common_func!(FuncsHandler::send_commit_wgas, (6) -> ())); - builder.add_func(SendInit, wrap_common_func!(FuncsHandler::send_init, (2) -> ())); - builder.add_func(SendPush, wrap_common_func!(FuncsHandler::send_push, (5) -> ())); - builder.add_func(SendWGas, wrap_common_func!(FuncsHandler::send_wgas, (7) -> ())); - builder.add_func(SendInput, wrap_common_func!(FuncsHandler::send_input, (6) -> ())); - builder.add_func(SendPushInput, wrap_common_func!(FuncsHandler::send_push_input, (5) -> ())); - builder.add_func(SendInputWGas, wrap_common_func!(FuncsHandler::send_input_wgas, (7) -> ())); - builder.add_func(Size, wrap_common_func!(FuncsHandler::size, (2) -> ())); - builder.add_func(Source, wrap_common_func!(FuncsHandler::source, (2) -> ())); - builder.add_func(Value, wrap_common_func!(FuncsHandler::value, (2) -> ())); - builder.add_func(ValueAvailable, wrap_common_func!(FuncsHandler::value_available, (2) -> ())); - builder.add_func(Wait, wrap_common_func!(FuncsHandler::wait, (1) -> ())); - builder.add_func(WaitFor, wrap_common_func!(FuncsHandler::wait_for, (2) -> ())); - builder.add_func(WaitUpTo, wrap_common_func!(FuncsHandler::wait_up_to, (2) -> ())); - builder.add_func(Wake, wrap_common_func!(FuncsHandler::wake, (4) -> ())); - builder.add_func(SystemReserveGas, wrap_common_func!(FuncsHandler::system_reserve_gas, (3) -> ())); - builder.add_func(ReservationReply, wrap_common_func!(FuncsHandler::reservation_reply, (5) -> ())); - builder.add_func(ReservationReplyCommit, wrap_common_func!(FuncsHandler::reservation_reply_commit, (3) -> ())); - builder.add_func(ReservationSend, wrap_common_func!(FuncsHandler::reservation_send, (6) -> ())); - builder.add_func(ReservationSendCommit, wrap_common_func!(FuncsHandler::reservation_send_commit, (5) -> ())); - builder.add_func(OutOfGas, wrap_common_func!(FuncsHandler::out_of_gas, (1) -> ())); - - builder.add_func(Alloc, wrap_common_func!(FuncsHandler::alloc, (2) -> (1))); - builder.add_func(Free, wrap_common_func!(FuncsHandler::free, (2) -> (1))); - - builder.add_func(CreateProgram, |caller, args| { - let mut caller = CallerWrap::prepare(caller)?; - FuncsHandler::execute(&mut caller, args, FuncsHandler::create_program2) - }); + builder.add_func(BlockHeight, wrap_syscall!(block_height)); + builder.add_func(BlockTimestamp,wrap_syscall!(block_timestamp)); + builder.add_func(CreateProgram, wrap_syscall!(create_program)); + builder.add_func(CreateProgramWGas, wrap_syscall!(create_program_wgas)); + builder.add_func(Debug, wrap_syscall!(debug)); + builder.add_func(Panic, wrap_syscall!(panic)); + builder.add_func(OomPanic, wrap_syscall!(oom_panic)); + builder.add_func(Exit, wrap_syscall!(exit)); + builder.add_func(ReplyCode, wrap_syscall!(reply_code)); + builder.add_func(SignalCode, wrap_syscall!(signal_code)); + builder.add_func(ReserveGas, wrap_syscall!(reserve_gas)); + builder.add_func(ReplyDeposit, wrap_syscall!(reply_deposit)); + builder.add_func(UnreserveGas, wrap_syscall!(unreserve_gas)); + builder.add_func(GasAvailable, wrap_syscall!(gas_available)); + builder.add_func(Leave, wrap_syscall!(leave)); + builder.add_func(MessageId, wrap_syscall!(message_id)); + builder.add_func(PayProgramRent, wrap_syscall!(pay_program_rent)); + builder.add_func(ProgramId, wrap_syscall!(program_id)); + builder.add_func(Random, wrap_syscall!(random)); + builder.add_func(Read, wrap_syscall!(read)); + builder.add_func(Reply, wrap_syscall!(reply)); + builder.add_func(ReplyCommit, wrap_syscall!(reply_commit)); + builder.add_func(ReplyCommitWGas, wrap_syscall!(reply_commit_wgas)); + builder.add_func(ReplyPush, wrap_syscall!(reply_push)); + builder.add_func(ReplyTo, wrap_syscall!(reply_to)); + builder.add_func(SignalFrom, wrap_syscall!(signal_from)); + builder.add_func(ReplyWGas, wrap_syscall!(reply_wgas)); + builder.add_func(ReplyInput, wrap_syscall!(reply_input)); + builder.add_func(ReplyPushInput, wrap_syscall!(reply_push_input)); + builder.add_func(ReplyInputWGas, wrap_syscall!(reply_input_wgas)); + builder.add_func(Send, wrap_syscall!(send)); + builder.add_func(SendCommit, wrap_syscall!(send_commit)); + builder.add_func(SendCommitWGas, wrap_syscall!(send_commit_wgas)); + builder.add_func(SendInit, wrap_syscall!(send_init)); + builder.add_func(SendPush, wrap_syscall!(send_push)); + builder.add_func(SendWGas, wrap_syscall!(send_wgas)); + builder.add_func(SendInput, wrap_syscall!(send_input)); + builder.add_func(SendPushInput, wrap_syscall!(send_push_input)); + builder.add_func(SendInputWGas, wrap_syscall!(send_input_wgas)); + builder.add_func(Size, wrap_syscall!(size)); + builder.add_func(Source, wrap_syscall!(source)); + builder.add_func(Value, wrap_syscall!(value)); + builder.add_func(ValueAvailable, wrap_syscall!(value_available)); + builder.add_func(Wait, wrap_syscall!(wait)); + builder.add_func(WaitFor, wrap_syscall!(wait_for)); + builder.add_func(WaitUpTo, wrap_syscall!(wait_up_to)); + builder.add_func(Wake, wrap_syscall!(wake)); + builder.add_func(SystemReserveGas, wrap_syscall!(system_reserve_gas)); + builder.add_func(ReservationReply, wrap_syscall!(reservation_reply)); + builder.add_func(ReservationReplyCommit, wrap_syscall!(reservation_reply_commit)); + builder.add_func(ReservationSend, wrap_syscall!(reservation_send)); + builder.add_func(ReservationSendCommit, wrap_syscall!(reservation_send_commit)); + builder.add_func(OutOfGas, wrap_syscall!(out_of_gas)); + + builder.add_func(Alloc, wrap_syscall!(alloc)); + builder.add_func(Free, wrap_syscall!(free)); } } diff --git a/core-backend/src/funcs.rs b/core-backend/src/funcs.rs index 62383a1dc19..d6c658778fc 100644 --- a/core-backend/src/funcs.rs +++ b/core-backend/src/funcs.rs @@ -34,12 +34,12 @@ use gear_core::{ costs::RuntimeCosts, env::{DropPayloadLockBound, Externalities}, gas::CounterType, + ids::{MessageId, ProgramId}, message::{ HandlePacket, InitPacket, MessageWaitedType, Payload, PayloadSizeError, ReplyPacket, }, pages::{PageNumber, PageU32Size, WasmPage}, }; -use gear_core_backend_codegen::host; use gear_core_errors::{MessageError, ReplyCode, SignalCode}; use gear_sandbox::{ReturnValue, Value}; use gear_sandbox_env::{HostError, WasmReturnValue}; @@ -100,6 +100,12 @@ impl From<()> for SysCallReturnValue { } } +impl From for SysCallReturnValue { + fn from(value: i32) -> Self { + Self(ReturnValue::Value(Value::I32(value))) + } +} + impl From for SysCallReturnValue { fn from(value: u32) -> Self { Self(ReturnValue::Value(Value::I32(value as i32))) @@ -120,7 +126,7 @@ pub trait SysCall { ) -> Result<(u64, T), HostError>; } -pub trait SysCallFabric { +pub trait SysCallFabric { fn create(self, args: &[Value]) -> Result; } @@ -135,32 +141,66 @@ where } } -impl SysCallFabric for H +impl SysCallFabric for H where - H: Fn(u32) -> S, + H: for<'a> Fn(&'a [Value]) -> S, S: SysCall, { fn create(self, args: &[Value]) -> Result { - let [a]: [Value; 1] = args.try_into().map_err(|_| HostError)?; - let a = SandboxValue(a).try_into()?; - Ok((self)(a)) + Ok((self)(args)) } } -impl SysCallFabric for H +macro_rules! impl_syscall_fabric { + ($($generic:ident),+) => { + #[allow(non_snake_case)] + impl SysCallFabric + for Handler + where + Handler: Fn($($generic),+) -> Call, + Call: SysCall, + $( $generic: TryFrom,)+ + { + fn create(self, args: &[Value]) -> Result { + const ARGS_AMOUNT: usize = impl_syscall_fabric!(@count $($generic),+); + + let [$($generic),+]: [Value; ARGS_AMOUNT] = args.try_into().map_err(|_| HostError)?; + $( + let $generic = SandboxValue($generic).try_into()?; + )+ + Ok((self)($($generic),+)) + } + } + }; + (@count $generic:ident) => { 1 }; + (@count $generic:ident, $($generics:ident),+) => { 1 + impl_syscall_fabric!(@count $($generics),+) }; +} + +impl_syscall_fabric!(A); +impl_syscall_fabric!(A, B); +impl_syscall_fabric!(A, B, C); +impl_syscall_fabric!(A, B, C, D); +impl_syscall_fabric!(A, B, C, D, E); +impl_syscall_fabric!(A, B, C, D, E, F); +impl_syscall_fabric!(A, B, C, D, E, F, G); + +type SimpleSysCall = F; + +impl SysCall for SimpleSysCall where - H: Fn(u32, u32, u32, u32, u32, u32) -> S, - S: SysCall, + F: Fn(&mut CallerWrap) -> Result, + Ext: BackendExternalities + 'static, { - fn create(self, args: &[Value]) -> Result { - let [a, b, c, d, e, f]: [Value; 6] = args.try_into().map_err(|_| HostError)?; - let a = SandboxValue(a).try_into()?; - let b = SandboxValue(b).try_into()?; - let c = SandboxValue(c).try_into()?; - let d = SandboxValue(d).try_into()?; - let e = SandboxValue(e).try_into()?; - let f = SandboxValue(f).try_into()?; - Ok((self)(a, b, c, d, e, f)) + type Context = InfallibleSysCallContext; + + fn execute( + &self, + caller: &mut CallerWrap, + ctx: Self::Context, + ) -> Result<(u64, T), HostError> { + let res = (self)(caller)?; + let InfallibleSysCallContext { gas } = ctx; + Ok((gas, res)) } } @@ -253,6 +293,7 @@ where ) -> Result where H: SysCallFabric, + Args: ?Sized, S: SysCall, R: Into, { @@ -291,708 +332,860 @@ where .map_err(RunFallibleError::FallibleExt) } - pub fn forbidden2() -> impl SysCall { - (RuntimeCosts::Null, |_: &mut CallerWrap| { - Err(ActorTerminationReason::Trap(TrapExplanation::ForbiddenFunction).into()) - }) + fn send_inner( + ctx: &mut CallerWrap, + pid_value_ptr: u32, + payload_ptr: u32, + len: u32, + gas_limit: Option, + delay: u32, + ) -> Result { + let read_hash_val = ctx.manager.register_read_as(pid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); + let HashWithValue { + hash: destination, + value, + } = ctx.read_as(read_hash_val)?; + let payload = Self::read_message_payload(ctx, read_payload)?; + + let packet = if let Some(gas_limit) = gas_limit { + HandlePacket::new_with_gas(destination.into(), payload, gas_limit, value) + } else { + HandlePacket::new(destination.into(), payload, value) + }; + + ctx.ext_mut().send(packet, delay).map_err(Into::into) } - pub fn alloc2(pages: u32) -> impl SysCall { + pub fn send(pid_value_ptr: u32, payload_ptr: u32, len: u32, delay: u32) -> impl SysCall { ( - RuntimeCosts::Alloc(pages), + RuntimeCosts::Send(len), + FallibleSysCallError::::default(), move |ctx: &mut CallerWrap| { - let res = ctx.alloc(pages); - let res = ctx.process_alloc_func_result(res)?; - - let page = match res { - Ok(page) => { - log::trace!("Alloc {pages:?} pages at {page:?}"); - page.raw() - } - Err(err) => { - log::trace!("Alloc failed: {err}"); - u32::MAX - } - }; - Ok(page) + Self::send_inner(ctx, pid_value_ptr, payload_ptr, len, None, delay) }, ) } - pub fn create_program2( - cid_value_ptr: u32, - salt_ptr: u32, - salt_len: u32, + pub fn send_wgas( + pid_value_ptr: u32, payload_ptr: u32, - payload_len: u32, + len: u32, + gas_limit: u64, delay: u32, ) -> impl SysCall { ( - RuntimeCosts::CreateProgram(payload_len, salt_len), - FallibleSysCallError::::default(), - move |ctx: &mut CallerWrap| -> Result<_, RunFallibleError> { - let read_cid_value = ctx.manager.register_read_as(cid_value_ptr); - let read_salt = ctx.manager.register_read(salt_ptr, salt_len); - let read_payload = ctx.manager.register_read(payload_ptr, payload_len); - let HashWithValue { - hash: code_id, - value, - } = ctx.read_as(read_cid_value)?; - let salt = Self::read_message_payload(ctx, read_salt)?; - let payload = Self::read_message_payload(ctx, read_payload)?; - - ctx.ext_mut() - .create_program(InitPacket::new(code_id.into(), salt, payload, value), delay) - .map_err(Into::into) + RuntimeCosts::Send(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::send_inner(ctx, pid_value_ptr, payload_ptr, len, Some(gas_limit), delay) }, ) } - pub fn size2(size_ptr: u32) -> impl SysCall { - (RuntimeCosts::Size, move |ctx: &mut CallerWrap| { - let size = ctx.ext_mut().size()? as u32; - - let write_size = ctx.manager.register_write_as(size_ptr); - ctx.write_as(write_size, size.to_le_bytes()) - .map_err(Into::into) - }) - } - - // TODO #3037 - #[allow(clippy::too_many_arguments)] - #[host(fallible, wgas, cost = RuntimeCosts::Send(len))] - pub fn send( - ctx: &mut CallerWrap<'_, '_, Ext>, + fn send_commit_inner( + ctx: &mut CallerWrap, + handle: u32, pid_value_ptr: u32, - payload_ptr: u32, - len: u32, + gas_limit: Option, delay: u32, - ) -> Result<(), HostError> { - let read_hash_val = ctx.manager.register_read_as(pid_value_ptr); - let read_payload = ctx.manager.register_read(payload_ptr, len); + ) -> Result { + let read_pid_value = ctx.manager.register_read_as(pid_value_ptr); let HashWithValue { hash: destination, value, - } = ctx.read_as(read_hash_val)?; - let payload = Self::read_message_payload(ctx, read_payload)?; + } = ctx.read_as(read_pid_value)?; + + let packet = if let Some(gas_limit) = gas_limit { + HandlePacket::new_with_gas(destination.into(), Default::default(), gas_limit, value) + } else { + HandlePacket::new(destination.into(), Default::default(), value) + }; ctx.ext_mut() - .send(HandlePacket::new(destination.into(), payload, value), delay) + .send_commit(handle, packet, delay) .map_err(Into::into) } - #[host(fallible, wgas, cost = RuntimeCosts::SendCommit)] - pub fn send_commit( - ctx: &mut CallerWrap<'_, '_, Ext>, + pub fn send_commit(handle: u32, pid_value_ptr: u32, delay: u32) -> impl SysCall { + ( + RuntimeCosts::SendCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::send_commit_inner(ctx, handle, pid_value_ptr, None, delay) + }, + ) + } + + pub fn send_commit_wgas( handle: u32, pid_value_ptr: u32, + gas_limit: u64, delay: u32, - ) -> Result<(), HostError> { - let read_pid_value = ctx.manager.register_read_as(pid_value_ptr); - let HashWithValue { - hash: destination, - value, - } = ctx.read_as(read_pid_value)?; - - ctx.ext_mut() - .send_commit( - handle, - HandlePacket::new(destination.into(), Default::default(), value), - delay, - ) - .map_err(Into::into) + ) -> impl SysCall { + ( + RuntimeCosts::SendCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::send_commit_inner(ctx, handle, pid_value_ptr, Some(gas_limit), delay) + }, + ) } - #[host(fallible, cost = RuntimeCosts::SendInit, err = ErrorWithHandle)] - pub fn send_init(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut().send_init().map_err(Into::into) + pub fn send_init() -> impl SysCall { + ( + RuntimeCosts::SendInit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| ctx.ext_mut().send_init().map_err(Into::into), + ) } - #[host(fallible, cost = RuntimeCosts::SendPush(len), err = ErrorBytes)] - pub fn send_push( - ctx: &mut CallerWrap<'_, '_, Ext>, - handle: u32, - payload_ptr: u32, - len: u32, - ) -> Result<(), HostError> { - let read_payload = ctx.manager.register_read(payload_ptr, len); - let payload = ctx.read(read_payload)?; + pub fn send_push(handle: u32, payload_ptr: u32, len: u32) -> impl SysCall { + ( + RuntimeCosts::SendPush(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_payload = ctx.manager.register_read(payload_ptr, len); + let payload = ctx.read(read_payload)?; - ctx.ext_mut() - .send_push(handle, &payload) - .map_err(Into::into) + ctx.ext_mut() + .send_push(handle, &payload) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::ReservationSend(len))] pub fn reservation_send( - ctx: &mut CallerWrap<'_, '_, Ext>, rid_pid_value_ptr: u32, payload_ptr: u32, len: u32, delay: u32, - ) -> Result<(), HostError> { - let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); - let read_payload = ctx.manager.register_read(payload_ptr, len); - let TwoHashesWithValue { - hash1: reservation_id, - hash2: destination, - value, - } = ctx.read_as(read_rid_pid_value)?; - let payload = Self::read_message_payload(ctx, read_payload)?; + ) -> impl SysCall { + ( + RuntimeCosts::ReservationSend(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); + let TwoHashesWithValue { + hash1: reservation_id, + hash2: destination, + value, + } = ctx.read_as(read_rid_pid_value)?; + let payload = Self::read_message_payload(ctx, read_payload)?; - ctx.ext_mut() - .reservation_send( - reservation_id.into(), - HandlePacket::new(destination.into(), payload, value), - delay, - ) - .map_err(Into::into) + ctx.ext_mut() + .reservation_send( + reservation_id.into(), + HandlePacket::new(destination.into(), payload, value), + delay, + ) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::ReservationSendCommit)] pub fn reservation_send_commit( - ctx: &mut CallerWrap<'_, '_, Ext>, handle: u32, rid_pid_value_ptr: u32, delay: u32, - ) -> Result<(), HostError> { - let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); - let TwoHashesWithValue { - hash1: reservation_id, - hash2: destination, - value, - } = ctx.read_as(read_rid_pid_value)?; + ) -> impl SysCall { + ( + RuntimeCosts::ReservationSendCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); + let TwoHashesWithValue { + hash1: reservation_id, + hash2: destination, + value, + } = ctx.read_as(read_rid_pid_value)?; - ctx.ext_mut() - .reservation_send_commit( - reservation_id.into(), - handle, - HandlePacket::new(destination.into(), Default::default(), value), - delay, - ) - .map_err(Into::into) + ctx.ext_mut() + .reservation_send_commit( + reservation_id.into(), + handle, + HandlePacket::new(destination.into(), Default::default(), value), + delay, + ) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::Read, err = ErrorBytes)] - pub fn read( - ctx: &mut CallerWrap<'_, '_, Ext>, - at: u32, - len: u32, - buffer_ptr: u32, - ) -> Result<(), HostError> { - let payload_lock = ctx.ext_mut().lock_payload(at, len)?; - payload_lock - .drop_with::(|payload_access| { - let write_buffer = ctx.manager.register_write(buffer_ptr, len); - let write_res = ctx.write(write_buffer, payload_access.as_slice()); - let unlock_bound = ctx.ext_mut().unlock_payload(payload_access.into_lock()); - - DropPayloadLockBound::from((unlock_bound, write_res)) - }) - .into_inner() - .map_err(Into::into) + pub fn read(at: u32, len: u32, buffer_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::Read, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let payload_lock = ctx.ext_mut().lock_payload(at, len)?; + payload_lock + .drop_with::(|payload_access| { + let write_buffer = ctx.manager.register_write(buffer_ptr, len); + let write_res = ctx.write(write_buffer, payload_access.as_slice()); + let unlock_bound = ctx.ext_mut().unlock_payload(payload_access.into_lock()); + + DropPayloadLockBound::from((unlock_bound, write_res)) + }) + .into_inner() + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::Size)] - pub fn size(ctx: &mut CallerWrap<'_, '_, Ext>, size_ptr: u32) -> Result<(), HostError> { - let size = ctx.ext_mut().size()? as u32; + pub fn size(size_ptr: u32) -> impl SysCall { + (RuntimeCosts::Size, move |ctx: &mut CallerWrap| { + let size = ctx.ext_mut().size()? as u32; - let write_size = ctx.manager.register_write_as(size_ptr); - ctx.write_as(write_size, size.to_le_bytes()) - .map_err(Into::into) + let write_size = ctx.manager.register_write_as(size_ptr); + ctx.write_as(write_size, size.to_le_bytes()) + .map_err(Into::into) + }) } - #[host(cost = RuntimeCosts::Exit)] - pub fn exit(ctx: &mut CallerWrap<'_, '_, Ext>, inheritor_id_ptr: u32) -> Result<(), HostError> { - let read_inheritor_id = ctx.manager.register_read_decoded(inheritor_id_ptr); - let inheritor_id = ctx.read_decoded(read_inheritor_id)?; - Err(ActorTerminationReason::Exit(inheritor_id).into()) + pub fn exit(inheritor_id_ptr: u32) -> impl SysCall { + (RuntimeCosts::Exit, move |ctx: &mut CallerWrap| { + let read_inheritor_id = ctx.manager.register_read_decoded(inheritor_id_ptr); + let inheritor_id = ctx.read_decoded(read_inheritor_id)?; + Err(ActorTerminationReason::Exit(inheritor_id).into()) + }) } - #[host(fallible, cost = RuntimeCosts::ReplyCode, err = ErrorWithReplyCode)] - pub fn reply_code(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut() - .reply_code() - .map(ReplyCode::to_bytes) - .map_err(Into::into) + pub fn reply_code() -> impl SysCall { + ( + RuntimeCosts::ReplyCode, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .reply_code() + .map(ReplyCode::to_bytes) + .map_err(Into::into) + }, + ) } // TODO: write proper benchmark #2825 - #[host(fallible, cost = RuntimeCosts::ReplyCode, err = ErrorWithSignalCode)] - pub fn signal_code(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut() - .signal_code() - .map(SignalCode::to_u32) - .map_err(Into::into) + pub fn signal_code() -> impl SysCall { + ( + RuntimeCosts::ReplyCode, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .signal_code() + .map(SignalCode::to_u32) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::Alloc(pages))] - pub fn alloc(ctx: &mut CallerWrap<'_, '_, Ext>, pages: u32) -> Result { - let res = ctx.alloc(pages); - let res = ctx.process_alloc_func_result(res)?; + pub fn alloc(pages: u32) -> impl SysCall { + ( + RuntimeCosts::Alloc(pages), + move |ctx: &mut CallerWrap| { + let res = ctx.alloc(pages); + let res = ctx.process_alloc_func_result(res)?; - let page = match res { - Ok(page) => { - log::trace!("Alloc {pages:?} pages at {page:?}"); - page.raw() - } - Err(err) => { - log::trace!("Alloc failed: {err}"); - u32::MAX - } - }; - Ok(page) + let page = match res { + Ok(page) => { + log::trace!("Alloc {pages:?} pages at {page:?}"); + page.raw() + } + Err(err) => { + log::trace!("Alloc failed: {err}"); + u32::MAX + } + }; + Ok(page) + }, + ) } - #[host(cost = RuntimeCosts::Free)] - pub fn free(ctx: &mut CallerWrap<'_, '_, Ext>, page_no: u32) -> Result { - let page = WasmPage::new(page_no).map_err(|_| { - UndefinedTerminationReason::Actor(ActorTerminationReason::Trap( - TrapExplanation::Unknown, - )) - })?; + pub fn free(page_no: u32) -> impl SysCall { + (RuntimeCosts::Free, move |ctx: &mut CallerWrap| { + let page = WasmPage::new(page_no).map_err(|_| { + UndefinedTerminationReason::Actor(ActorTerminationReason::Trap( + TrapExplanation::Unknown, + )) + })?; - let res = ctx.ext_mut().free(page); - let res = ctx.process_alloc_func_result(res)?; + let res = ctx.ext_mut().free(page); + let res = ctx.process_alloc_func_result(res)?; - match &res { - Ok(()) => { - log::trace!("Free {page:?}"); - } - Err(err) => { - log::trace!("Free failed: {err}"); - } - }; + match &res { + Ok(()) => { + log::trace!("Free {page:?}"); + } + Err(err) => { + log::trace!("Free failed: {err}"); + } + }; - Ok(res.is_err() as i32) + Ok(res.is_err() as i32) + }) } - #[host(cost = RuntimeCosts::BlockHeight)] - pub fn block_height( - ctx: &mut CallerWrap<'_, '_, Ext>, - height_ptr: u32, - ) -> Result<(), HostError> { - let height = ctx.ext_mut().block_height()?; + pub fn block_height(height_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::BlockHeight, + move |ctx: &mut CallerWrap| { + let height = ctx.ext_mut().block_height()?; - let write_height = ctx.manager.register_write_as(height_ptr); - ctx.write_as(write_height, height.to_le_bytes()) - .map_err(Into::into) + let write_height = ctx.manager.register_write_as(height_ptr); + ctx.write_as(write_height, height.to_le_bytes()) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::BlockTimestamp)] - pub fn block_timestamp( - ctx: &mut CallerWrap<'_, '_, Ext>, - timestamp_ptr: u32, - ) -> Result<(), HostError> { - let timestamp = ctx.ext_mut().block_timestamp()?; + pub fn block_timestamp(timestamp_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::BlockTimestamp, + move |ctx: &mut CallerWrap| { + let timestamp = ctx.ext_mut().block_timestamp()?; - let write_timestamp = ctx.manager.register_write_as(timestamp_ptr); - ctx.write_as(write_timestamp, timestamp.to_le_bytes()) - .map_err(Into::into) + let write_timestamp = ctx.manager.register_write_as(timestamp_ptr); + ctx.write_as(write_timestamp, timestamp.to_le_bytes()) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::Random)] - pub fn random( - ctx: &mut CallerWrap<'_, '_, Ext>, - subject_ptr: u32, - bn_random_ptr: u32, - ) -> Result<(), HostError> { - let read_subject = ctx.manager.register_read_decoded(subject_ptr); - let write_bn_random = ctx.manager.register_write_as(bn_random_ptr); + pub fn random(subject_ptr: u32, bn_random_ptr: u32) -> impl SysCall { + (RuntimeCosts::Random, move |ctx: &mut CallerWrap| { + let read_subject = ctx.manager.register_read_decoded(subject_ptr); + let write_bn_random = ctx.manager.register_write_as(bn_random_ptr); - let raw_subject: Hash = ctx.read_decoded(read_subject)?; + let raw_subject: Hash = ctx.read_decoded(read_subject)?; - let (random, bn) = ctx.ext_mut().random()?; - let subject = [&raw_subject, random].concat(); + let (random, bn) = ctx.ext_mut().random()?; + let subject = [&raw_subject, random].concat(); - let mut hash = [0; 32]; - hash.copy_from_slice(blake2b(32, &[], &subject).as_bytes()); + let mut hash = [0; 32]; + hash.copy_from_slice(blake2b(32, &[], &subject).as_bytes()); - ctx.write_as(write_bn_random, BlockNumberWithHash { bn, hash }) - .map_err(Into::into) + ctx.write_as(write_bn_random, BlockNumberWithHash { bn, hash }) + .map_err(Into::into) + }) } - #[host(fallible, wgas, cost = RuntimeCosts::Reply(len))] - pub fn reply( - ctx: &mut CallerWrap<'_, '_, Ext>, + /* + gas: u64, + payload_ptr: u32, + len: u32, + gas_limit: u64, + value_ptr: u32, + err_mid_ptr: u32 + */ + + fn reply_inner( + ctx: &mut CallerWrap, payload_ptr: u32, len: u32, + gas_limit: Option, value_ptr: u32, - ) -> Result<(), HostError> { + ) -> Result { let read_payload = ctx.manager.register_read(payload_ptr, len); let value = Self::register_and_read_value(ctx, value_ptr)?; let payload = Self::read_message_payload(ctx, read_payload)?; - ctx.ext_mut() - .reply(ReplyPacket::new(payload, value)) - .map_err(Into::into) + let packet = if let Some(gas_limit) = gas_limit { + ReplyPacket::new_with_gas(payload, gas_limit, value) + } else { + ReplyPacket::new(payload, value) + }; + + ctx.ext_mut().reply(packet).map_err(Into::into) } - #[host(fallible, wgas, cost = RuntimeCosts::ReplyCommit)] - pub fn reply_commit( - ctx: &mut CallerWrap<'_, '_, Ext>, + pub fn reply(payload_ptr: u32, len: u32, value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::Reply(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::reply_inner(ctx, payload_ptr, len, None, value_ptr) + }, + ) + } + + pub fn reply_wgas( + payload_ptr: u32, + len: u32, + gas_limit: u64, + value_ptr: u32, + ) -> impl SysCall { + ( + RuntimeCosts::Reply(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::reply_inner(ctx, payload_ptr, len, Some(gas_limit), value_ptr) + }, + ) + } + + fn reply_commit_inner( + ctx: &mut CallerWrap, + gas_limit: Option, value_ptr: u32, - ) -> Result<(), HostError> { + ) -> Result { let value = Self::register_and_read_value(ctx, value_ptr)?; - ctx.ext_mut() - .reply_commit(ReplyPacket::new(Default::default(), value)) - .map_err(Into::into) + let packet = if let Some(gas_limit) = gas_limit { + ReplyPacket::new_with_gas(Default::default(), gas_limit, value) + } else { + ReplyPacket::new(Default::default(), value) + }; + + ctx.ext_mut().reply_commit(packet).map_err(Into::into) } - #[host(fallible, cost = RuntimeCosts::ReservationReply(len))] - pub fn reservation_reply( - ctx: &mut CallerWrap<'_, '_, Ext>, - rid_value_ptr: u32, - payload_ptr: u32, - len: u32, - ) -> Result<(), HostError> { - let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); - let read_payload = ctx.manager.register_read(payload_ptr, len); - let HashWithValue { - hash: reservation_id, - value, - } = ctx.read_as(read_rid_value)?; - let payload = Self::read_message_payload(ctx, read_payload)?; + pub fn reply_commit(value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::ReplyCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| Self::reply_commit_inner(ctx, None, value_ptr), + ) + } - ctx.ext_mut() - .reservation_reply(reservation_id.into(), ReplyPacket::new(payload, value)) - .map_err(Into::into) + pub fn reply_commit_wgas(gas_limit: u64, value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::ReplyCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::reply_commit_inner(ctx, Some(gas_limit), value_ptr) + }, + ) } - #[host(fallible, cost = RuntimeCosts::ReservationReplyCommit)] - pub fn reservation_reply_commit( - ctx: &mut CallerWrap<'_, '_, Ext>, - rid_value_ptr: u32, - ) -> Result<(), HostError> { - let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); - let HashWithValue { - hash: reservation_id, - value, - } = ctx.read_as(read_rid_value)?; + pub fn reservation_reply(rid_value_ptr: u32, payload_ptr: u32, len: u32) -> impl SysCall { + ( + RuntimeCosts::ReservationReply(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); + let HashWithValue { + hash: reservation_id, + value, + } = ctx.read_as(read_rid_value)?; + let payload = Self::read_message_payload(ctx, read_payload)?; - ctx.ext_mut() - .reservation_reply_commit( - reservation_id.into(), - ReplyPacket::new(Default::default(), value), - ) - .map_err(Into::into) + ctx.ext_mut() + .reservation_reply(reservation_id.into(), ReplyPacket::new(payload, value)) + .map_err(Into::into) + }, + ) + } + + pub fn reservation_reply_commit(rid_value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::ReservationReplyCommit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); + let HashWithValue { + hash: reservation_id, + value, + } = ctx.read_as(read_rid_value)?; + + ctx.ext_mut() + .reservation_reply_commit( + reservation_id.into(), + ReplyPacket::new(Default::default(), value), + ) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::ReplyTo)] - pub fn reply_to(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut().reply_to().map_err(Into::into) + pub fn reply_to() -> impl SysCall { + ( + RuntimeCosts::ReplyTo, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| ctx.ext_mut().reply_to().map_err(Into::into), + ) } // TODO: write proper benchmark #2825 - #[host(fallible, cost = RuntimeCosts::SignalFrom)] - pub fn signal_from(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut().signal_from().map_err(Into::into) + pub fn signal_from() -> impl SysCall { + ( + RuntimeCosts::SignalFrom, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| ctx.ext_mut().signal_from().map_err(Into::into), + ) } - #[host(fallible, cost = RuntimeCosts::ReplyPush(len), err = ErrorBytes)] - pub fn reply_push( - ctx: &mut CallerWrap<'_, '_, Ext>, - payload_ptr: u32, - len: u32, - ) -> Result<(), HostError> { - let read_payload = ctx.manager.register_read(payload_ptr, len); - let payload = ctx.read(read_payload)?; + pub fn reply_push(payload_ptr: u32, len: u32) -> impl SysCall { + ( + RuntimeCosts::ReplyPush(len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_payload = ctx.manager.register_read(payload_ptr, len); + let payload = ctx.read(read_payload)?; - ctx.ext_mut().reply_push(&payload).map_err(Into::into) + ctx.ext_mut().reply_push(&payload).map_err(Into::into) + }, + ) } - #[host(fallible, wgas, cost = RuntimeCosts::ReplyInput)] - pub fn reply_input( - ctx: &mut CallerWrap<'_, '_, Ext>, + fn reply_input_inner( + ctx: &mut CallerWrap, offset: u32, len: u32, + gas_limit: Option, value_ptr: u32, - ) -> Result<(), HostError> { - // Charge for `len` is inside `reply_push_input` + ) -> Result { let value = Self::register_and_read_value(ctx, value_ptr)?; - let mut f = || { - ctx.ext_mut().reply_push_input(offset, len)?; - ctx.ext_mut() - .reply_commit(ReplyPacket::new(Default::default(), value)) + // Charge for `len` is inside `reply_push_input` + ctx.ext_mut().reply_push_input(offset, len)?; + + let packet = if let Some(gas_limit) = gas_limit { + ReplyPacket::new_with_gas(Default::default(), gas_limit, value) + } else { + ReplyPacket::new(Default::default(), value) }; - f().map_err(Into::into) + ctx.ext_mut().reply_commit(packet).map_err(Into::into) } - #[host(fallible, cost = RuntimeCosts::ReplyPushInput, err = ErrorBytes)] - pub fn reply_push_input( - ctx: &mut CallerWrap<'_, '_, Ext>, + pub fn reply_input(offset: u32, len: u32, value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::ReplyInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::reply_input_inner(ctx, offset, len, None, value_ptr) + }, + ) + } + + pub fn reply_input_wgas( offset: u32, len: u32, - ) -> Result<(), HostError> { - ctx.ext_mut() - .reply_push_input(offset, len) - .map_err(Into::into) + gas_limit: u64, + value_ptr: u32, + ) -> impl SysCall { + ( + RuntimeCosts::ReplyInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::reply_input_inner(ctx, offset, len, Some(gas_limit), value_ptr) + }, + ) } - #[allow(clippy::too_many_arguments)] - #[host(fallible, wgas, cost = RuntimeCosts::SendInput)] - pub fn send_input( - ctx: &mut CallerWrap<'_, '_, Ext>, + pub fn reply_push_input(offset: u32, len: u32) -> impl SysCall { + ( + RuntimeCosts::ReplyPushInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .reply_push_input(offset, len) + .map_err(Into::into) + }, + ) + } + + fn send_input_inner( + ctx: &mut CallerWrap, pid_value_ptr: u32, offset: u32, len: u32, + gas_limit: Option, delay: u32, - ) -> Result<(), HostError> { - // Charge for `len` inside `send_push_input` + ) -> Result { let read_pid_value = ctx.manager.register_read_as(pid_value_ptr); let HashWithValue { hash: destination, value, } = ctx.read_as(read_pid_value)?; - let mut f = || { - let handle = ctx.ext_mut().send_init()?; - ctx.ext_mut().send_push_input(handle, offset, len)?; - ctx.ext_mut().send_commit( - handle, - HandlePacket::new(destination.into(), Default::default(), value), - delay, - ) + let handle = ctx.ext_mut().send_init()?; + // Charge for `len` inside `send_push_input` + ctx.ext_mut().send_push_input(handle, offset, len)?; + + let packet = if let Some(gas_limit) = gas_limit { + HandlePacket::new_with_gas(destination.into(), Default::default(), gas_limit, value) + } else { + HandlePacket::new(destination.into(), Default::default(), value) }; - f().map_err(Into::into) + ctx.ext_mut() + .send_commit(handle, packet, delay) + .map_err(Into::into) } - #[host(fallible, cost = RuntimeCosts::SendPushInput, err = ErrorBytes)] - pub fn send_push_input( - ctx: &mut CallerWrap<'_, '_, Ext>, - handle: u32, + pub fn send_input(pid_value_ptr: u32, offset: u32, len: u32, delay: u32) -> impl SysCall { + ( + RuntimeCosts::SendInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::send_input_inner(ctx, pid_value_ptr, offset, len, None, delay) + }, + ) + } + + pub fn send_input_wgas( + pid_value_ptr: u32, offset: u32, len: u32, - ) -> Result<(), HostError> { - ctx.ext_mut() - .send_push_input(handle, offset, len) - .map_err(Into::into) + gas_limit: u64, + delay: u32, + ) -> impl SysCall { + ( + RuntimeCosts::SendInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::send_input_inner(ctx, pid_value_ptr, offset, len, Some(gas_limit), delay) + }, + ) } - #[host(cost = RuntimeCosts::Debug(data_len))] - pub fn debug( - ctx: &mut CallerWrap<'_, '_, Ext>, - data_ptr: u32, - data_len: u32, - ) -> Result<(), HostError> { - let read_data = ctx.manager.register_read(data_ptr, data_len); - let data: RuntimeBuffer = ctx - .read(read_data)? - .try_into() - .map_err(|RuntimeBufferSizeError| { - UnrecoverableMemoryError::RuntimeAllocOutOfBounds.into() - }) - .map_err(TrapExplanation::UnrecoverableExt)?; - - let s = String::from_utf8(data.into_vec()) - .map_err(|_err| UnrecoverableExecutionError::InvalidDebugString.into()) - .map_err(TrapExplanation::UnrecoverableExt)?; - ctx.ext_mut().debug(&s)?; + pub fn send_push_input(handle: u32, offset: u32, len: u32) -> impl SysCall { + ( + RuntimeCosts::SendPushInput, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .send_push_input(handle, offset, len) + .map_err(Into::into) + }, + ) + } - Ok(()) + pub fn debug(data_ptr: u32, data_len: u32) -> impl SysCall { + ( + RuntimeCosts::Debug(data_len), + move |ctx: &mut CallerWrap| { + let read_data = ctx.manager.register_read(data_ptr, data_len); + let data: RuntimeBuffer = ctx + .read(read_data)? + .try_into() + .map_err(|RuntimeBufferSizeError| { + UnrecoverableMemoryError::RuntimeAllocOutOfBounds.into() + }) + .map_err(TrapExplanation::UnrecoverableExt)?; + + let s = String::from_utf8(data.into_vec()) + .map_err(|_err| UnrecoverableExecutionError::InvalidDebugString.into()) + .map_err(TrapExplanation::UnrecoverableExt)?; + ctx.ext_mut().debug(&s)?; + + Ok(()) + }, + ) } - #[host(cost = RuntimeCosts::Null)] - pub fn panic( - ctx: &mut CallerWrap<'_, '_, Ext>, - data_ptr: u32, - data_len: u32, - ) -> Result<(), HostError> { - let read_data = ctx.manager.register_read(data_ptr, data_len); - let data = ctx.read(read_data).unwrap_or_default(); + pub fn panic(data_ptr: u32, data_len: u32) -> impl SysCall { + (RuntimeCosts::Null, move |ctx: &mut CallerWrap| { + let read_data = ctx.manager.register_read(data_ptr, data_len); + let data = ctx.read(read_data).unwrap_or_default(); - let s = String::from_utf8_lossy(&data).to_string(); + let s = String::from_utf8_lossy(&data).to_string(); - Err(ActorTerminationReason::Trap(TrapExplanation::Panic(s.into())).into()) + Err(ActorTerminationReason::Trap(TrapExplanation::Panic(s.into())).into()) + }) } - #[host(cost = RuntimeCosts::Null)] - pub fn oom_panic(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - Err(ActorTerminationReason::Trap(TrapExplanation::ProgramAllocOutOfBounds).into()) + pub fn oom_panic() -> impl SysCall { + (RuntimeCosts::Null, |_ctx: &mut CallerWrap| { + Err(ActorTerminationReason::Trap(TrapExplanation::ProgramAllocOutOfBounds).into()) + }) } - #[host(fallible, cost = RuntimeCosts::ReserveGas)] - pub fn reserve_gas( - ctx: &mut CallerWrap<'_, '_, Ext>, - gas_value: u64, - duration: u32, - ) -> Result<(), HostError> { - ctx.ext_mut() - .reserve_gas(gas_value, duration) - .map_err(Into::into) + pub fn reserve_gas(gas_value: u64, duration: u32) -> impl SysCall { + ( + RuntimeCosts::ReserveGas, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .reserve_gas(gas_value, duration) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::ReplyDeposit, err = ErrorBytes)] - pub fn reply_deposit( - ctx: &mut CallerWrap<'_, '_, Ext>, - message_id_ptr: u32, - gas_value: u64, - ) -> Result<(), HostError> { - let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); - let message_id = ctx.read_decoded(read_message_id)?; + pub fn reply_deposit(message_id_ptr: u32, gas_value: u64) -> impl SysCall { + ( + RuntimeCosts::ReplyDeposit, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); + let message_id = ctx.read_decoded(read_message_id)?; - ctx.ext_mut() - .reply_deposit(message_id, gas_value) - .map_err(Into::into) + ctx.ext_mut() + .reply_deposit(message_id, gas_value) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::UnreserveGas, err = ErrorWithGas)] - pub fn unreserve_gas( - ctx: &mut CallerWrap<'_, '_, Ext>, - reservation_id_ptr: u32, - ) -> Result<(), HostError> { - let read_reservation_id = ctx.manager.register_read_decoded(reservation_id_ptr); - let reservation_id = ctx.read_decoded(read_reservation_id)?; + pub fn unreserve_gas(reservation_id_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::UnreserveGas, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_reservation_id = ctx.manager.register_read_decoded(reservation_id_ptr); + let reservation_id = ctx.read_decoded(read_reservation_id)?; - ctx.ext_mut() - .unreserve_gas(reservation_id) - .map_err(Into::into) + ctx.ext_mut() + .unreserve_gas(reservation_id) + .map_err(Into::into) + }, + ) } - #[host(fallible, cost = RuntimeCosts::SystemReserveGas, err = ErrorBytes)] - pub fn system_reserve_gas( - ctx: &mut CallerWrap<'_, '_, Ext>, - gas_value: u64, - ) -> Result<(), HostError> { - ctx.ext_mut() - .system_reserve_gas(gas_value) - .map_err(Into::into) + pub fn system_reserve_gas(gas_value: u64) -> impl SysCall { + ( + RuntimeCosts::SystemReserveGas, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + ctx.ext_mut() + .system_reserve_gas(gas_value) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::GasAvailable)] - pub fn gas_available(ctx: &mut CallerWrap<'_, '_, Ext>, gas_ptr: u32) -> Result<(), HostError> { - let gas_available = ctx.ext_mut().gas_available()?; + pub fn gas_available(gas_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::GasAvailable, + move |ctx: &mut CallerWrap| { + let gas_available = ctx.ext_mut().gas_available()?; - let write_gas = ctx.manager.register_write_as(gas_ptr); - ctx.write_as(write_gas, gas_available.to_le_bytes()) - .map_err(Into::into) + let write_gas = ctx.manager.register_write_as(gas_ptr); + ctx.write_as(write_gas, gas_available.to_le_bytes()) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::MessageId)] - pub fn message_id( - ctx: &mut CallerWrap<'_, '_, Ext>, - message_id_ptr: u32, - ) -> Result<(), HostError> { - let message_id = ctx.ext_mut().message_id()?; + pub fn message_id(message_id_ptr: u32) -> impl SysCall { + (RuntimeCosts::MsgId, move |ctx: &mut CallerWrap| { + let message_id = ctx.ext_mut().message_id()?; - let write_message_id = ctx.manager.register_write_as(message_id_ptr); - ctx.write_as(write_message_id, message_id.into_bytes()) - .map_err(Into::into) + let write_message_id = ctx.manager.register_write_as(message_id_ptr); + ctx.write_as(write_message_id, message_id.into_bytes()) + .map_err(Into::into) + }) } - #[host(cost = RuntimeCosts::ProgramId)] - pub fn program_id( - ctx: &mut CallerWrap<'_, '_, Ext>, - program_id_ptr: u32, - ) -> Result<(), HostError> { - let program_id = ctx.ext_mut().program_id()?; + pub fn program_id(program_id_ptr: u32) -> impl SysCall { + (RuntimeCosts::ProgramId, move |ctx: &mut CallerWrap| { + let program_id = ctx.ext_mut().program_id()?; - let write_program_id = ctx.manager.register_write_as(program_id_ptr); - ctx.write_as(write_program_id, program_id.into_bytes()) - .map_err(Into::into) + let write_program_id = ctx.manager.register_write_as(program_id_ptr); + ctx.write_as(write_program_id, program_id.into_bytes()) + .map_err(Into::into) + }) } - #[host(fallible, cost = RuntimeCosts::PayProgramRent, err = ErrorWithBlockNumberAndValue)] - pub fn pay_program_rent( - ctx: &mut CallerWrap<'_, '_, Ext>, - rent_pid_ptr: u32, - ) -> Result<(), HostError> { - let read_rent_pid = ctx.manager.register_read_as(rent_pid_ptr); + pub fn pay_program_rent(rent_pid_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::PayProgramRent, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_rent_pid = ctx.manager.register_read_as(rent_pid_ptr); - let HashWithValue { - hash: program_id, - value: rent, - } = ctx.read_as(read_rent_pid)?; + let HashWithValue { + hash: program_id, + value: rent, + } = ctx.read_as(read_rent_pid)?; - ctx.ext_mut() - .pay_program_rent(program_id.into(), rent) - .map_err(Into::into) + ctx.ext_mut() + .pay_program_rent(program_id.into(), rent) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::Source)] - pub fn source(ctx: &mut CallerWrap<'_, '_, Ext>, source_ptr: u32) -> Result<(), HostError> { - let source = ctx.ext_mut().source()?; + pub fn source(source_ptr: u32) -> impl SysCall { + (RuntimeCosts::Source, move |ctx: &mut CallerWrap| { + let source = ctx.ext_mut().source()?; - let write_source = ctx.manager.register_write_as(source_ptr); - ctx.write_as(write_source, source.into_bytes()) - .map_err(Into::into) + let write_source = ctx.manager.register_write_as(source_ptr); + ctx.write_as(write_source, source.into_bytes()) + .map_err(Into::into) + }) } - #[host(cost = RuntimeCosts::Value)] - pub fn value(ctx: &mut CallerWrap<'_, '_, Ext>, value_ptr: u32) -> Result<(), HostError> { - let value = ctx.ext_mut().value()?; + pub fn value(value_ptr: u32) -> impl SysCall { + (RuntimeCosts::Value, move |ctx: &mut CallerWrap| { + let value = ctx.ext_mut().value()?; - let write_value = ctx.manager.register_write_as(value_ptr); - ctx.write_as(write_value, value.to_le_bytes()) - .map_err(Into::into) + let write_value = ctx.manager.register_write_as(value_ptr); + ctx.write_as(write_value, value.to_le_bytes()) + .map_err(Into::into) + }) } - #[host(cost = RuntimeCosts::ValueAvailable)] - pub fn value_available( - ctx: &mut CallerWrap<'_, '_, Ext>, - value_ptr: u32, - ) -> Result<(), HostError> { - let value_available = ctx.ext_mut().value_available()?; + pub fn value_available(value_ptr: u32) -> impl SysCall { + ( + RuntimeCosts::ValueAvailable, + move |ctx: &mut CallerWrap| { + let value_available = ctx.ext_mut().value_available()?; - let write_value = ctx.manager.register_write_as(value_ptr); - ctx.write_as(write_value, value_available.to_le_bytes()) - .map_err(Into::into) + let write_value = ctx.manager.register_write_as(value_ptr); + ctx.write_as(write_value, value_available.to_le_bytes()) + .map_err(Into::into) + }, + ) } - #[host(cost = RuntimeCosts::Leave)] - pub fn leave(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - Err(ActorTerminationReason::Leave.into()) + pub fn leave() -> impl SysCall { + (RuntimeCosts::Leave, move |_ctx: &mut CallerWrap| { + Err(ActorTerminationReason::Leave.into()) + }) } - #[host(cost = RuntimeCosts::Wait)] - pub fn wait(ctx: &mut CallerWrap<'_, '_, Ext>) -> Result<(), HostError> { - ctx.ext_mut().wait()?; - Err(ActorTerminationReason::Wait(None, MessageWaitedType::Wait).into()) + pub fn wait() -> impl SysCall { + (RuntimeCosts::Wait, move |ctx: &mut CallerWrap| { + ctx.ext_mut().wait()?; + Err(ActorTerminationReason::Wait(None, MessageWaitedType::Wait).into()) + }) } - #[host(cost = RuntimeCosts::WaitFor)] - pub fn wait_for(ctx: &mut CallerWrap<'_, '_, Ext>, duration: u32) -> Result<(), HostError> { - ctx.ext_mut().wait_for(duration)?; - Err(ActorTerminationReason::Wait(Some(duration), MessageWaitedType::WaitFor).into()) + pub fn wait_for(duration: u32) -> impl SysCall { + (RuntimeCosts::WaitFor, move |ctx: &mut CallerWrap| { + ctx.ext_mut().wait_for(duration)?; + Err(ActorTerminationReason::Wait(Some(duration), MessageWaitedType::WaitFor).into()) + }) } - #[host(cost = RuntimeCosts::WaitUpTo)] - pub fn wait_up_to(ctx: &mut CallerWrap<'_, '_, Ext>, duration: u32) -> Result<(), HostError> { - let waited_type = if ctx.ext_mut().wait_up_to(duration)? { - MessageWaitedType::WaitUpToFull - } else { - MessageWaitedType::WaitUpTo - }; - Err(ActorTerminationReason::Wait(Some(duration), waited_type).into()) + pub fn wait_up_to(duration: u32) -> impl SysCall { + (RuntimeCosts::WaitUpTo, move |ctx: &mut CallerWrap| { + let waited_type = if ctx.ext_mut().wait_up_to(duration)? { + MessageWaitedType::WaitUpToFull + } else { + MessageWaitedType::WaitUpTo + }; + Err(ActorTerminationReason::Wait(Some(duration), waited_type).into()) + }) } - #[host(fallible, cost = RuntimeCosts::Wake, err = ErrorBytes)] - pub fn wake( - ctx: &mut CallerWrap<'_, '_, Ext>, - message_id_ptr: u32, - delay: u32, - ) -> Result<(), HostError> { - let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); - let message_id = ctx.read_decoded(read_message_id)?; + pub fn wake(message_id_ptr: u32, delay: u32) -> impl SysCall { + ( + RuntimeCosts::Wake, + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); + let message_id = ctx.read_decoded(read_message_id)?; - ctx.ext_mut().wake(message_id, delay).map_err(Into::into) + ctx.ext_mut().wake(message_id, delay).map_err(Into::into) + }, + ) } #[allow(clippy::too_many_arguments)] - #[host(fallible, wgas, cost = RuntimeCosts::CreateProgram(payload_len, salt_len), err = ErrorWithTwoHashes)] - pub fn create_program( - ctx: &mut CallerWrap<'_, '_, Ext>, + fn create_program_inner( + ctx: &mut CallerWrap, cid_value_ptr: u32, salt_ptr: u32, salt_len: u32, payload_ptr: u32, payload_len: u32, + gas_limit: Option, delay: u32, - ) -> Result<(), HostError> { + ) -> Result<(MessageId, ProgramId), RunFallibleError> { let read_cid_value = ctx.manager.register_read_as(cid_value_ptr); let read_salt = ctx.manager.register_read(salt_ptr, salt_len); let read_payload = ctx.manager.register_read(payload_ptr, payload_len); @@ -1003,38 +1196,92 @@ where let salt = Self::read_message_payload(ctx, read_salt)?; let payload = Self::read_message_payload(ctx, read_payload)?; + let packet = if let Some(gas_limit) = gas_limit { + InitPacket::new_with_gas(code_id.into(), salt, payload, gas_limit, value) + } else { + InitPacket::new(code_id.into(), salt, payload, value) + }; + ctx.ext_mut() - .create_program(InitPacket::new(code_id.into(), salt, payload, value), delay) + .create_program(packet, delay) .map_err(Into::into) } - pub fn forbidden(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { - syscall_trace!("forbidden"); + pub fn create_program( + cid_value_ptr: u32, + salt_ptr: u32, + salt_len: u32, + payload_ptr: u32, + payload_len: u32, + delay: u32, + ) -> impl SysCall { + ( + RuntimeCosts::CreateProgram(payload_len, salt_len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| -> Result<_, RunFallibleError> { + Self::create_program_inner( + ctx, + cid_value_ptr, + salt_ptr, + salt_len, + payload_ptr, + payload_len, + None, + delay, + ) + }, + ) + } + + pub fn create_program_wgas( + cid_value_ptr: u32, + salt_ptr: u32, + salt_len: u32, + payload_ptr: u32, + payload_len: u32, + gas_limit: u64, + delay: u32, + ) -> impl SysCall { + ( + RuntimeCosts::CreateProgramWGas(payload_len, salt_len), + FallibleSysCallError::::default(), + move |ctx: &mut CallerWrap| { + Self::create_program_inner( + ctx, + cid_value_ptr, + salt_ptr, + salt_len, + payload_ptr, + payload_len, + Some(gas_limit), + delay, + ) + }, + ) + } - ctx.run_any(gas, RuntimeCosts::Null, |_| { + pub fn forbidden(_args: &[Value]) -> impl SysCall { + (RuntimeCosts::Null, |_: &mut CallerWrap| { Err(ActorTerminationReason::Trap(TrapExplanation::ForbiddenFunction).into()) }) } - pub fn out_of_gas( - ctx: &mut CallerWrap<'_, '_, Ext>, - _gas: u64, - ) -> Result<(u64, ()), HostError> { - syscall_trace!("out_of_gas"); - - let ext = ctx.ext_mut(); - let current_counter = ext.current_counter_type(); - log::trace!(target: "syscalls", "[out_of_gas] Current counter in global represents {current_counter:?}"); + pub fn out_of_gas() -> impl SysCall { + |ctx: &mut CallerWrap| { + let ext = ctx.ext_mut(); + let current_counter = ext.current_counter_type(); + log::trace!(target: "syscalls", "[out_of_gas] Current counter in global represents {current_counter:?}"); - if current_counter == CounterType::GasAllowance { - // We manually decrease it to 0 because global won't be affected - // since it didn't pass comparison to argument of `gas_charge()` - ext.decrease_current_counter_to(0); - } + if current_counter == CounterType::GasAllowance { + // We manually decrease it to 0 because global won't be affected + // since it didn't pass comparison to argument of `gas_charge()` + ext.decrease_current_counter_to(0); + } - let termination_reason: ActorTerminationReason = current_counter.into(); + let termination_reason: ActorTerminationReason = current_counter.into(); - ctx.set_termination_reason(termination_reason.into()); - Err(HostError) + ctx.set_termination_reason(termination_reason.into()); + Err(HostError) + } } }