diff --git a/Cargo.lock b/Cargo.lock index 96ce835e31f..26c4f4c4b69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1634,7 +1634,7 @@ dependencies = [ "sp-runtime", "sp-std", "wasm-mutate", - "wasmparser 0.85.0", + "wasmparser 0.86.0", "wasmprinter", "wat", ] @@ -9105,9 +9105,9 @@ checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744" [[package]] name = "wasm-encoder" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b47b995b096a689358ca9de6c727b94351b95b390dbbf6b7021c22797d36caa" +checksum = "31f0c17267a5ffd6ae3d897589460e21db1673c84fb7016b909c9691369a75ea" dependencies = [ "leb128", ] @@ -9134,16 +9134,16 @@ dependencies = [ [[package]] name = "wasm-mutate" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afc948447bea01d54509cb4a6ccbeb7e572c42c4c84f778d286b7633a1c49e86" +checksum = "219c79f95ec42118a63e9df1f8b4858c2092875820d10b4d8e5b324759880212" dependencies = [ "egg", "log", "rand 0.8.5", "thiserror", "wasm-encoder", - "wasmparser 0.85.0", + "wasmparser 0.86.0", ] [[package]] @@ -9446,21 +9446,21 @@ checksum = "718ed7c55c2add6548cca3ddd6383d738cd73b892df400e96b9aa876f0141d7a" [[package]] name = "wasmparser" -version = "0.85.0" +version = "0.86.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570460c58b21e9150d2df0eaaedbb7816c34bcec009ae0dcc976e40ba81463e7" +checksum = "4bcbfe95447da2aa7ff171857fc8427513eb57c75a729bb190e974dc695e8f5c" dependencies = [ "indexmap", ] [[package]] name = "wasmprinter" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea454634a2a7888d053f7723a26a76024e4f705cf86f7b4d38d5f15b79369c31" +checksum = "aa4cca415278da771add7c9ab7f3391f04b8d98719d2cf28a185d38d5206697e" dependencies = [ "anyhow", - "wasmparser 0.85.0", + "wasmparser 0.86.0", ] [[package]] @@ -9633,20 +9633,21 @@ dependencies = [ [[package]] name = "wast" -version = "41.0.0" +version = "42.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f882898b8b817cc4edc16aa3692fdc087b356edc8cc0c2164f5b5181e31c3870" +checksum = "badcb03f976f983ff0daf294da9697be659442f61e6b0942bb37a2b6cbfe9dd4" dependencies = [ "leb128", "memchr", "unicode-width", + "wasm-encoder", ] [[package]] name = "wat" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b3b9b3e39e66c7fd3f8be785e74444d216260f491e93369e317ed6482ff80f" +checksum = "b92f20b742ac527066c8414bc0637352661b68cab07ef42586cefaba71c965cf" dependencies = [ "wast", ] diff --git a/core-backend/sandbox/src/funcs.rs b/core-backend/sandbox/src/funcs.rs index 3127292aa03..837aedba009 100644 --- a/core-backend/sandbox/src/funcs.rs +++ b/core-backend/sandbox/src/funcs.rs @@ -819,12 +819,16 @@ where let salt = funcs::get_vec(memory, salt_ptr, salt_len)?; let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; let value = funcs::get_u128(memory, value_ptr)?; - let new_actor_id = ext + let error_len = ext .create_program(InitPacket::new(code_hash.into(), salt, payload, value)) - .map_err(FuncError::Core)?; - wto(memory, program_id_ptr, new_actor_id.as_ref()) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + wto(memory, program_id_ptr, new_actor_id.as_ref()) + })?; + Ok(error_len) }) - .map(|()| ReturnValue::Unit) + .map(|code| Value::I32(code as i32).into()) .map_err(|err| { ctx.trap = Some(err); HostError @@ -850,7 +854,7 @@ where let salt = funcs::get_vec(memory, salt_ptr, salt_len)?; let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; let value = funcs::get_u128(memory, value_ptr)?; - let new_actor_id = ext + let error_len = ext .create_program(InitPacket::new_with_gas( code_hash.into(), salt, @@ -858,10 +862,14 @@ where gas_limit, value, )) - .map_err(FuncError::Core)?; - wto(memory, program_id_ptr, new_actor_id.as_ref()) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + wto(memory, program_id_ptr, new_actor_id.as_ref()) + })?; + Ok(error_len) }) - .map(|()| ReturnValue::Unit) + .map(|code| Value::I32(code as i32).into()) .map_err(|err| { ctx.trap = Some(err); HostError diff --git a/core-backend/wasmtime/src/funcs.rs b/core-backend/wasmtime/src/funcs.rs index f808b676efa..6556f234dde 100644 --- a/core-backend/wasmtime/src/funcs.rs +++ b/core-backend/wasmtime/src/funcs.rs @@ -641,21 +641,25 @@ where value_ptr: i32, program_id_ptr: i32| { let ext = caller.data().ext.clone(); - ext.with_fallible(|ext: &mut E| -> Result<(), FuncError> { + ext.with_fallible(|ext: &mut E| -> Result> { let mem_wrap = get_caller_memory(&mut caller, &mem); let code_hash = get_bytes32(&mem_wrap, code_hash_ptr as usize)?; let salt = get_vec(&mem_wrap, salt_ptr as usize, salt_len as usize)?; let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; let value = get_u128(&mem_wrap, value_ptr as usize)?; - let new_actor_id = ext + let error_len = ext .create_program(InitPacket::new(code_hash.into(), salt, payload, value)) - .map_err(FuncError::Core)?; - write_to_caller_memory( - &mut caller, - &mem, - program_id_ptr as isize as _, - new_actor_id.as_ref(), - ) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + write_to_caller_memory( + &mut caller, + &mem, + program_id_ptr as isize as _, + new_actor_id.as_ref(), + ) + })?; + Ok(error_len) }) .map_err(Trap::new) }; @@ -673,13 +677,13 @@ where value_ptr: i32, program_id_ptr: i32| { let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<(), FuncError> { + ext.with_fallible(|ext| -> Result> { let mem_wrap = get_caller_memory(&mut caller, &mem); let code_hash = get_bytes32(&mem_wrap, code_hash_ptr as usize)?; let salt = get_vec(&mem_wrap, salt_ptr as usize, salt_len as usize)?; let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; let value = get_u128(&mem_wrap, value_ptr as usize)?; - let new_actor_id = ext + let error_len = ext .create_program(InitPacket::new_with_gas( code_hash.into(), salt, @@ -687,13 +691,17 @@ where gas_limit as _, value, )) - .map_err(FuncError::Core)?; - write_to_caller_memory( - &mut caller, - &mem, - program_id_ptr as isize as _, - new_actor_id.as_ref(), - ) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + write_to_caller_memory( + &mut caller, + &mem, + program_id_ptr as isize as _, + new_actor_id.as_ref(), + ) + })?; + Ok(error_len) }) .map_err(Trap::new) }; diff --git a/core/src/message/common.rs b/core/src/message/common.rs index 484c1b884c4..26912be3a8e 100644 --- a/core/src/message/common.rs +++ b/core/src/message/common.rs @@ -24,7 +24,8 @@ use codec::{Decode, Encode}; use core::ops::Deref; use scale_info::TypeInfo; -/// Message. +/// An entity that is used for interaction between actors. +/// Can transfer value and executes by programs in corresponding function: init, handle or handle_reply. #[derive(Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] pub struct Message { /// Message id. diff --git a/core/src/message/context.rs b/core/src/message/context.rs index 7a308cecdfb..8a775d7f97b 100644 --- a/core/src/message/context.rs +++ b/core/src/message/context.rs @@ -36,7 +36,9 @@ pub const OUTGOING_LIMIT: u32 = 1024; /// Context settings. #[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] pub struct ContextSettings { + /// Fee for sending message. sending_fee: u64, + /// Limit of outgoing messages that program can send during execution of current message. outgoing_limit: u32, } @@ -65,7 +67,7 @@ pub struct ContextOutcome { handle: Vec, reply: Option, awakening: Vec, - // Additional information section + // Additional information section. program_id: ProgramId, source: ProgramId, origin_msg_id: MessageId, @@ -293,3 +295,229 @@ impl MessageContext { (outcome, store) } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::ids; + use alloc::vec; + + #[test] + fn default_message_context() { + let message_context = + MessageContext::new(Default::default(), Default::default(), Default::default()); + + // Check that created in new ContextSettings have valid outgoing_limit. + assert_eq!(message_context.settings.outgoing_limit, OUTGOING_LIMIT); + } + + #[test] + fn duplicated_init() { + let mut message_context = + MessageContext::new(Default::default(), Default::default(), Default::default()); + // first init to default ProgramId. + let result = message_context.init_program(Default::default()); + + assert!(result.is_ok()); + // second init to same default ProgramId should get error. + let duplicated_init = message_context.init_program(Default::default()); + + assert_eq!(duplicated_init, Err(Error::DuplicateInit)); + } + + #[test] + fn outgoing_limit_exceeded() { + // Check that we can always send exactly outgoing_limit messages. + let max_n = 5; + + for n in 0..=max_n { + // for outgoing_limit n checking that LimitExceeded will be after n's message. + let settings = ContextSettings::new(0, n); + + let mut message_context = MessageContext::new_with_settings( + Default::default(), + Default::default(), + Default::default(), + settings, + ); + // send n messages + for _ in 0..n { + let handle = message_context.send_init().expect("unreachable"); + message_context + .send_push(handle, b"payload") + .expect("unreachable"); + message_context + .send_commit(handle, HandlePacket::default()) + .expect("unreachable"); + } + // n + 1 should get first error. + let limit_exceeded = message_context.send_init(); + assert_eq!(limit_exceeded, Err(Error::LimitExceeded)); + + // we can't send messages in this MessageContext. + let limit_exceeded = message_context.init_program(Default::default()); + assert_eq!(limit_exceeded, Err(Error::LimitExceeded)); + } + } + + #[test] + fn invalid_out_of_bounds() { + let mut message_context = + MessageContext::new(Default::default(), Default::default(), Default::default()); + + // Use invalid handle 0. + let out_of_bounds = message_context.send_commit(0, Default::default()); + assert_eq!(out_of_bounds, Err(Error::OutOfBounds)); + + // make 0 valid. + let valid_handle = message_context.send_init().expect("unreachable"); + assert_eq!(valid_handle, 0); + + // Use valid handle 0. + let result = message_context.send_commit(0, Default::default()); + assert!(result.is_ok()); + + // Use invalid handle 42. + let out_of_bounds = message_context.send_commit(42, Default::default()); + assert_eq!(out_of_bounds, Err(Error::OutOfBounds)); + } + + #[test] + fn double_reply() { + let mut message_context = + MessageContext::new(Default::default(), Default::default(), Default::default()); + + // First reply. + let result = message_context.reply_commit(Default::default()); + assert!(result.is_ok()); + + // Reply twice in one message is forbidden. + let result = message_context.reply_commit(Default::default()); + assert!(matches!(result, Err(Error::DuplicateReply))); + } + + // Set of constants for clarity of a part of the test + const INCOMING_MESSAGE_ID: u64 = 3; + const INCOMING_MESSAGE_SOURCE: u64 = 4; + + #[test] + /// Test that covers full api of `MessageContext` + fn message_context_api() { + // Creating an incoming message around which the runner builds the `MessageContext` + let incoming_message = IncomingMessage::new( + MessageId::from(INCOMING_MESSAGE_ID), + ProgramId::from(INCOMING_MESSAGE_SOURCE), + vec![1, 2], + 0, + 0, + None, + ); + + // Creating a message context + let mut context = MessageContext::new( + incoming_message, + ids::ProgramId::from(INCOMING_MESSAGE_ID), + None, + ); + + // Checking that the initial parameters of the context match the passed constants + assert_eq!(context.current().id(), MessageId::from(INCOMING_MESSAGE_ID)); + assert!(context.store.reply.is_none()); + assert!(context.outcome.reply.is_none()); + + // Creating a reply packet + let reply_packet = ReplyPacket::new(vec![0, 0], 0); + + // Checking that we are able to initialize reply + assert!(context.reply_push(&[1, 2, 3]).is_ok()); + + // Setting reply message and making sure the operation was successful + assert!(context.reply_commit(reply_packet.clone()).is_ok()); + + // Checking that the `ReplyMessage` matches the passed one + assert_eq!( + context.outcome.reply.as_ref().unwrap().payload().to_vec(), + vec![1, 2, 3, 0, 0], + ); + + // Checking that repeated call `reply_push(...)` returns error and does not do anything + assert!(context.reply_push(&[1]).is_err()); + assert_eq!( + context.outcome.reply.as_ref().unwrap().payload().to_vec(), + vec![1, 2, 3, 0, 0], + ); + + // Checking that repeated call `reply_commit(...)` returns error and does not + assert!(context.reply_commit(reply_packet).is_err()); + + // Checking that at this point vector of outgoing messages is empty + assert!(context.outcome.handle.is_empty()); + + // Creating an expected handle for a future initialized message + let expected_handle = 0; + + // Initializing message and compare its handle with expected one + assert_eq!( + context.send_init().expect("Error initializing new message"), + expected_handle + ); + + // And checking that it is not formed + assert!(context + .store + .outgoing + .get(&expected_handle) + .expect("This key should be") + .is_some()); + + // Checking that we are able to push payload for the + // message that we have not committed yet + assert!(context.send_push(expected_handle, &[5, 7]).is_ok()); + assert!(context.send_push(expected_handle, &[9]).is_ok()); + + // Creating an outgoing packet to commit sending by parts + let commit_packet = HandlePacket::default(); + + // Checking if commit is successful + assert!(context.send_commit(expected_handle, commit_packet).is_ok()); + + // Checking that we are **NOT** able to push payload for the message or + // commit it if we already committed it or directly pushed before + assert!(context.send_push(expected_handle, &[5, 7]).is_err()); + assert!(context + .send_commit(expected_handle, HandlePacket::default()) + .is_err()); + + // Creating a handle to push and do commit non-existent message + let expected_handle = 15; + + // Checking that we also get an error when trying + // to commit or send a non-existent message + assert!(context.send_push(expected_handle, &[0]).is_err()); + assert!(context + .send_commit(expected_handle, HandlePacket::default()) + .is_err()); + + // Creating a handle to init and do not commit later + // to show that the message will not be sent + let expected_handle = 1; + + assert_eq!( + context.send_init().expect("Error initializing new message"), + expected_handle + ); + assert!(context.send_push(expected_handle, &[2, 2]).is_ok()); + + // Checking that reply message not lost and matches our initial + assert!(context.outcome.reply.is_some()); + assert_eq!( + context.outcome.reply.as_ref().unwrap().payload(), + vec![1, 2, 3, 0, 0] + ); + + // Checking that on drain we get only messages that were fully formed (directly sent or committed) + let (expected_result, _) = context.drain(); + assert_eq!(expected_result.handle.len(), 1); + assert_eq!(expected_result.handle[0].payload(), vec![5, 7, 9]); + } +} diff --git a/core/src/message/handle.rs b/core/src/message/handle.rs index 14257915e08..0e4a2a58b83 100644 --- a/core/src/message/handle.rs +++ b/core/src/message/handle.rs @@ -26,7 +26,8 @@ use crate::{ use codec::{Decode, Encode}; use scale_info::TypeInfo; -/// Handle message. +/// Message for Handle entry point. +/// Represents a standard message that sends between actors. #[derive(Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] pub struct HandleMessage { /// Message id. diff --git a/core/src/message/init.rs b/core/src/message/init.rs index 4ff75d662d1..35a0696148e 100644 --- a/core/src/message/init.rs +++ b/core/src/message/init.rs @@ -26,7 +26,8 @@ use crate::{ use codec::{Decode, Encode}; use scale_info::TypeInfo; -/// Init message. +/// Message for Init entry point. +/// Used to initiate a newly created program. #[derive(Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] pub struct InitMessage { /// Message id. diff --git a/core/src/message/reply.rs b/core/src/message/reply.rs index e99eacd3ad4..7d5b8af2d88 100644 --- a/core/src/message/reply.rs +++ b/core/src/message/reply.rs @@ -26,7 +26,8 @@ use crate::{ use codec::{Decode, Encode}; use scale_info::TypeInfo; -/// Reply message. +/// Message for Reply entry point. +/// [`ReplyMessage`] is unique because of storing [`MessageId`] from message on what it replies, and can be the only one per some message execution. #[derive(Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] pub struct ReplyMessage { /// Message id. diff --git a/examples/binaries/init-with-value/src/lib.rs b/examples/binaries/init-with-value/src/lib.rs index 0b736a93ee9..14ffb7e8ccc 100644 --- a/examples/binaries/init-with-value/src/lib.rs +++ b/examples/binaries/init-with-value/src/lib.rs @@ -38,16 +38,14 @@ pub use code::WASM_BINARY_OPT as WASM_BINARY; #[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)] pub enum SendMessage { - Init(u128), - // Create program without gas - InitWithoutGas(u128), - // First value is custom destination id - Handle(u64, u128), + Init { value: u128 }, + InitWithoutGas { value: u128 }, + Handle { destination: u64, value: u128 }, } #[cfg(not(feature = "std"))] mod wasm { - use gstd::{msg, prog, Vec}; + use gstd::{errors::ContractError, msg, prog}; use super::SendMessage; @@ -63,7 +61,7 @@ mod wasm { let init = |wgas: bool, value: u128| { let submitted_code = CHILD_CODE_HASH.into(); - let _ = if wgas { + let res = if wgas { prog::create_program_with_gas( submitted_code, COUNTER.to_le_bytes(), @@ -75,15 +73,21 @@ mod wasm { prog::create_program(submitted_code, COUNTER.to_le_bytes(), [], value) }; - COUNTER += 1; + match res { + Ok(_) => { + COUNTER += 1; + } + Err(ContractError::Ext(err)) => panic!("{}", err), + Err(err) => panic!("{}", err), + } }; for msg_data in data { match msg_data { - SendMessage::Init(value) => init(true, value), - SendMessage::InitWithoutGas(value) => init(false, value), - SendMessage::Handle(receiver, value) => { - let _ = msg::send(receiver.into(), b"", value); + SendMessage::Init { value } => init(true, value), + SendMessage::InitWithoutGas { value } => init(false, value), + SendMessage::Handle { destination, value } => { + let _ = msg::send(destination.into(), b"", value); } } } diff --git a/examples/binaries/program-factory/src/lib.rs b/examples/binaries/program-factory/src/lib.rs index ad4243179df..997ebbf51af 100644 --- a/examples/binaries/program-factory/src/lib.rs +++ b/examples/binaries/program-factory/src/lib.rs @@ -71,7 +71,8 @@ mod wasm { [], 1_000_000_000, 0, - ); + ) + .unwrap(); msg::send_bytes(new_program_id, [], 0).unwrap(); COUNTER += 1; @@ -80,7 +81,8 @@ mod wasm { for (code_hash, salt, gas_limit) in custom_child_data { let submitted_code = code_hash.into(); let new_program_id = - prog::create_program_with_gas(submitted_code, &salt, [], gas_limit, 0); + prog::create_program_with_gas(submitted_code, &salt, [], gas_limit, 0) + .unwrap(); let msg_id = msg::send_bytes(new_program_id, [], 0).unwrap(); } } diff --git a/examples/create-program/src/lib.rs b/examples/create-program/src/lib.rs index 03fd05a3c10..0dbfff2b7db 100644 --- a/examples/create-program/src/lib.rs +++ b/examples/create-program/src/lib.rs @@ -32,7 +32,8 @@ pub unsafe extern "C" fn handle() { b"unique", 10_000_000_000, 0, - ); + ) + .unwrap(); debug!("A new program is created {:?}", new_program_id); let msg_id = msg::send(new_program_id, b"", 0).unwrap(); @@ -47,7 +48,8 @@ pub unsafe extern "C" fn handle() { b"not_unique", 10_000_000_000, 0, - ); + ) + .unwrap(); debug!("A new program is created {:?}", new_program_id); let msg_id = msg::send(new_program_id, b"", 0).unwrap(); diff --git a/gcore/src/error.rs b/gcore/src/error.rs index f83ba1d3c32..f9d15390a8f 100644 --- a/gcore/src/error.rs +++ b/gcore/src/error.rs @@ -18,6 +18,8 @@ pub use gear_core_errors::{ExtError, MemoryError, MessageError}; +pub type Result = core::result::Result; + #[cfg(feature = "codec")] mod sys { extern "C" { @@ -33,7 +35,7 @@ pub struct SyscallError { } impl SyscallError { - pub fn into_result(self) -> Result<(), ExtError> { + pub fn into_result(self) -> Result<()> { if self.len == 0 { Ok(()) } else { diff --git a/gcore/src/msg.rs b/gcore/src/msg.rs index 84670aa6be2..20201a7ed19 100644 --- a/gcore/src/msg.rs +++ b/gcore/src/msg.rs @@ -25,9 +25,8 @@ //! processing a program can send messages to other programs and users including //! reply to the initial message. -use crate::{ActorId, MessageHandle, MessageId}; +use crate::{error::Result, ActorId, MessageHandle, MessageId}; use core::mem::MaybeUninit; -use gear_core_errors::ExtError; mod sys { use crate::error::SyscallError; @@ -188,7 +187,7 @@ pub fn load(buffer: &mut [u8]) { /// # See also /// /// [`reply_push`] function allows to form a reply message in parts. -pub fn reply(payload: &[u8], value: u128) -> Result { +pub fn reply(payload: &[u8], value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_reply( @@ -235,7 +234,7 @@ pub fn reply(payload: &[u8], value: u128) -> Result { /// # See also /// /// [`reply_push`] function allows to form a reply message in parts. -pub fn reply_with_gas(payload: &[u8], gas_limit: u64, value: u128) -> Result { +pub fn reply_with_gas(payload: &[u8], gas_limit: u64, value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_reply_wgas( @@ -280,7 +279,7 @@ pub fn reply_with_gas(payload: &[u8], gas_limit: u64, value: u128) -> Result Result { +pub fn reply_commit(value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_reply_commit( @@ -322,7 +321,7 @@ pub fn reply_commit(value: u128) -> Result { /// # See also /// /// [`reply_push`] function allows to form a reply message with in parts. -pub fn reply_commit_with_gas(gas_limit: u64, value: u128) -> Result { +pub fn reply_commit_with_gas(gas_limit: u64, value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_reply_commit_wgas( @@ -357,7 +356,7 @@ pub fn reply_commit_with_gas(gas_limit: u64, value: u128) -> Result Result<(), ExtError> { +pub fn reply_push(payload: &[u8]) -> Result<()> { unsafe { sys::gr_reply_push(payload.as_ptr(), payload.len() as _).into_result() } } @@ -420,7 +419,7 @@ pub fn reply_to() -> MessageId { /// /// [`send_init`],[`send_push`], [`send_commit`] functions allows to form a /// message to send in parts. -pub fn send(program: ActorId, payload: &[u8], value: u128) -> Result { +pub fn send(program: ActorId, payload: &[u8], value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_send( @@ -472,7 +471,7 @@ pub fn send_with_gas( payload: &[u8], gas_limit: u64, value: u128, -) -> Result { +) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_send_wgas( @@ -522,11 +521,7 @@ pub fn send_with_gas( /// /// [`send_push`], [`send_init`] functions allows to form a message to send in /// parts. -pub fn send_commit( - handle: MessageHandle, - program: ActorId, - value: u128, -) -> Result { +pub fn send_commit(handle: MessageHandle, program: ActorId, value: u128) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_send_commit( @@ -577,7 +572,7 @@ pub fn send_commit_with_gas( program: ActorId, gas_limit: u64, value: u128, -) -> Result { +) -> Result { unsafe { let mut message_id = MessageId::default(); sys::gr_send_commit_wgas( @@ -616,7 +611,7 @@ pub fn send_commit_with_gas( /// /// [`send_push`], [`send_commit`] functions allows to form a message to send in /// parts. -pub fn send_init() -> Result { +pub fn send_init() -> Result { unsafe { let mut handle = MaybeUninit::uninit(); sys::gr_send_init(handle.as_mut_ptr()).into_result()?; @@ -649,7 +644,7 @@ pub fn send_init() -> Result { /// /// [`send_init`], [`send_commit`] functions allows to form and send a message /// to send in parts. -pub fn send_push(handle: &MessageHandle, payload: &[u8]) -> Result<(), ExtError> { +pub fn send_push(handle: &MessageHandle, payload: &[u8]) -> Result<()> { unsafe { sys::gr_send_push(handle.0, payload.as_ptr(), payload.len() as _).into_result() } } diff --git a/gcore/src/prog.rs b/gcore/src/prog.rs index 2a2c64f1a13..20495a858af 100644 --- a/gcore/src/prog.rs +++ b/gcore/src/prog.rs @@ -18,9 +18,11 @@ //! Program creation API for Gear programs. -use crate::{ActorId, CodeHash}; +use crate::{error::Result, ActorId, CodeHash}; mod sys { + use crate::error::SyscallError; + extern "C" { pub fn gr_create_program( code_hash: *const u8, @@ -30,7 +32,7 @@ mod sys { data_len: u32, value_ptr: *const u8, program_id_ptr: *mut u8, - ); + ) -> SyscallError; pub fn gr_create_program_wgas( code_hash: *const u8, @@ -41,12 +43,17 @@ mod sys { gas_limit: u64, value_ptr: *const u8, program_id_ptr: *mut u8, - ); + ) -> SyscallError; } } /// Same as [`create_program_with_gas`], but without explicit gas limit. -pub fn create_program(code_hash: CodeHash, salt: &[u8], payload: &[u8], value: u128) -> ActorId { +pub fn create_program( + code_hash: CodeHash, + salt: &[u8], + payload: &[u8], + value: u128, +) -> Result { unsafe { let mut program_id = ActorId::default(); sys::gr_create_program( @@ -57,8 +64,9 @@ pub fn create_program(code_hash: CodeHash, salt: &[u8], payload: &[u8], value: u payload.len() as _, value.to_le_bytes().as_ptr(), program_id.as_mut_slice().as_mut_ptr(), - ); - program_id + ) + .into_result()?; + Ok(program_id) } } @@ -99,7 +107,8 @@ pub fn create_program(code_hash: CodeHash, salt: &[u8], payload: &[u8], value: u /// hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a") /// .into(); /// let new_program_id = -/// prog::create_program_with_gas(submitted_code, &get().to_le_bytes(), b"", 10_000, 0); +/// prog::create_program_with_gas(submitted_code, &get().to_le_bytes(), b"", 10_000, 0) +/// .unwrap(); /// } /// ``` /// Another case for salt is to receive it as an input: @@ -111,7 +120,7 @@ pub fn create_program(code_hash: CodeHash, salt: &[u8], payload: &[u8], value: u /// # let submitted_code: CodeHash = hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a").into(); /// let mut salt = vec![0u8; msg::size()]; /// msg::load(&mut salt[..]); -/// let new_program_id = prog::create_program_with_gas(submitted_code, &salt, b"", 10_000, 0); +/// let new_program_id = prog::create_program_with_gas(submitted_code, &salt, b"", 10_000, 0).unwrap(); /// } /// ``` /// @@ -124,7 +133,7 @@ pub fn create_program(code_hash: CodeHash, salt: &[u8], payload: &[u8], value: u /// # let submitted_code: CodeHash = hex_literal::hex!("abf3746e72a6e8740bd9e12b879fbdd59e052cb390f116454e9116c22021ae4a").into(); /// # let mut salt = vec![0u8; msg::size()]; /// # msg::load(&mut salt[..]); -/// let new_program_id = prog::create_program_with_gas(submitted_code, &salt, b"", 10_000, 0); +/// let new_program_id = prog::create_program_with_gas(submitted_code, &salt, b"", 10_000, 0).unwrap(); /// msg::send_with_gas(new_program_id, b"payload for a new program", 10_000, 0).unwrap(); /// } /// ``` @@ -134,7 +143,7 @@ pub fn create_program_with_gas( payload: &[u8], gas_limit: u64, value: u128, -) -> ActorId { +) -> Result { unsafe { let mut program_id = ActorId::default(); sys::gr_create_program_wgas( @@ -146,7 +155,8 @@ pub fn create_program_with_gas( gas_limit, value.to_le_bytes().as_ptr(), program_id.as_mut_slice().as_mut_ptr(), - ); - program_id + ) + .into_result()?; + Ok(program_id) } } diff --git a/gstd/src/prog/mod.rs b/gstd/src/prog/mod.rs index bf48823cc18..087a3a6b0d1 100644 --- a/gstd/src/prog/mod.rs +++ b/gstd/src/prog/mod.rs @@ -18,15 +18,16 @@ //! Program creation module -use crate::{prelude::convert::AsRef, ActorId, CodeHash}; +use crate::{common::errors::Result, prelude::convert::AsRef, ActorId, CodeHash}; pub fn create_program, T2: AsRef<[u8]>>( code_hash: CodeHash, salt: T1, payload: T2, value: u128, -) -> ActorId { - gcore::prog::create_program(code_hash.into(), salt.as_ref(), payload.as_ref(), value).into() +) -> Result { + let id = gcore::prog::create_program(code_hash.into(), salt.as_ref(), payload.as_ref(), value)?; + Ok(id.into()) } pub fn create_program_with_gas, T2: AsRef<[u8]>>( @@ -35,13 +36,13 @@ pub fn create_program_with_gas, T2: AsRef<[u8]>>( payload: T2, gas_limit: u64, value: u128, -) -> ActorId { - gcore::prog::create_program_with_gas( +) -> Result { + let id = gcore::prog::create_program_with_gas( code_hash.into(), salt.as_ref(), payload.as_ref(), gas_limit, value, - ) - .into() + )?; + Ok(id.into()) } diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index d063014ec12..c0f16ed0bdd 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -3049,7 +3049,6 @@ fn test_two_contracts_composition_works() { // // Note: on manager level message will not be included to the [queue](https://github.com/gear-tech/gear/blob/master/pallets/gear/src/manager.rs#L351-L364) // But it's is not preferable to enter that `if` clause. -// todo # 929 After create_program sys-call becomes fallible, tests must be changed #[test] fn test_create_program_with_value_lt_ed() { use demo_init_with_value::{SendMessage, WASM_BINARY}; @@ -3089,9 +3088,15 @@ fn test_create_program_with_value_lt_ed() { // Must be stated, that "handle" messages send value to some non-existing address // so messages will go to mailbox vec![ - SendMessage::Handle(msg_receiver_1, 500), - SendMessage::Handle(msg_receiver_2, 500), - SendMessage::Init(0), + SendMessage::Handle { + destination: msg_receiver_1, + value: 500 + }, + SendMessage::Handle { + destination: msg_receiver_2, + value: 500 + }, + SendMessage::Init { value: 0 }, ] .encode(), 10_000_000_000, @@ -3122,9 +3127,15 @@ fn test_create_program_with_value_lt_ed() { // First two messages won't fail, because provided values are in a valid range // The last message value (which is the value of init message) will end execution with trap vec![ - SendMessage::Handle(msg_receiver_1, 0), - SendMessage::Handle(msg_receiver_2, 0), - SendMessage::Init(ed - 1), + SendMessage::Handle { + destination: msg_receiver_1, + value: 500 + }, + SendMessage::Handle { + destination: msg_receiver_2, + value: 500 + }, + SendMessage::Init { value: ed - 1 }, ] .encode(), 10_000_000_000, @@ -3165,7 +3176,6 @@ fn test_create_program_with_value_lt_ed() { // // Again init message won't be added to the queue, because of the check here (https://github.com/gear-tech/gear/blob/master/pallets/gear/src/manager.rs#L351-L364). // But it's is not preferable to enter that `if` clause. -// todo # 929 After create_program sys-call becomes fallible, tests must be changed #[test] fn test_create_program_with_exceeding_value() { use demo_init_with_value::{SendMessage, WASM_BINARY}; @@ -3186,9 +3196,17 @@ fn test_create_program_with_exceeding_value() { WASM_BINARY.to_vec(), b"test1".to_vec(), vec![ - SendMessage::Handle(random_receiver, sending_to_program / 3), - SendMessage::Handle(random_receiver, sending_to_program / 3), - SendMessage::Init(sending_to_program + 1), + SendMessage::Handle { + destination: random_receiver, + value: sending_to_program / 3 + }, + SendMessage::Handle { + destination: random_receiver, + value: sending_to_program / 3 + }, + SendMessage::Init { + value: sending_to_program + 1, + }, ] .encode(), 10_000_000_000, @@ -3248,7 +3266,7 @@ fn test_create_program_without_gas_works() { Origin::signed(USER_1), WASM_BINARY.to_vec(), b"test1".to_vec(), - vec![SendMessage::InitWithoutGas(0)].encode(), + vec![SendMessage::InitWithoutGas { value: 0 }].encode(), 10_000_000_000, 0, )); diff --git a/utils/economic-checks/Cargo.toml b/utils/economic-checks/Cargo.toml index 1b8856c85df..66384bd925c 100644 --- a/utils/economic-checks/Cargo.toml +++ b/utils/economic-checks/Cargo.toml @@ -16,8 +16,8 @@ serde = "1" env_logger = "0.9" hex = "0.4.3" arbitrary = { version = "1" } -wasm-mutate = "0.2.3" -wasmparser = "0.85.0" +wasm-mutate = "0.2.4" +wasmparser = "0.86.0" # Internal deps common = { package = "gear-common", path = "../../common", default-features = false } @@ -44,7 +44,7 @@ sp-runtime = { version = "6.0.0", git = "https://github.com/gear-tech/substrate. [dev-dependencies] wat = "1.0" -wasmprinter = "0.2.35" +wasmprinter = "0.2.36" demo-compose = { path = "../../examples/binaries/compose" } [features]