From 5b4a41922a622a969e640a8a7edc2f55b055a4c4 Mon Sep 17 00:00:00 2001 From: Timofey Date: Sun, 3 Dec 2023 11:59:37 +0000 Subject: [PATCH 1/3] feat: Add 'free_range' syscall (#3467) Co-authored-by: Gregory Sobol --- core-backend/src/env.rs | 1 + core-backend/src/funcs.rs | 32 +++- core-backend/src/memory.rs | 10 +- core-backend/src/mock.rs | 3 + core-processor/src/ext.rs | 35 +++- core/src/costs.rs | 14 +- core/src/env.rs | 8 +- core/src/memory.rs | 51 +++++- gcli/src/meta/executor.rs | 1 + gsdk/src/metadata/generated.rs | 2 + pallets/gear/src/benchmarking/mod.rs | 22 +++ pallets/gear/src/benchmarking/syscalls.rs | 37 +++++ .../benchmarking/tests/syscalls_integrity.rs | 154 +++++++----------- pallets/gear/src/schedule.rs | 10 ++ pallets/gear/src/tests.rs | 154 ++++++++++++++++++ pallets/gear/src/weights.rs | 42 +++++ runtime/vara/src/weights/pallet_gear.rs | 46 +++++- utils/regression-analysis/src/main.rs | 2 + utils/wasm-gen/src/config/syscalls/param.rs | 1 + .../src/generator/syscalls/invocator.rs | 34 +++- utils/wasm-instrument/src/syscalls.rs | 15 +- 21 files changed, 542 insertions(+), 132 deletions(-) diff --git a/core-backend/src/env.rs b/core-backend/src/env.rs index f9d973d2b95..858cd8b395a 100644 --- a/core-backend/src/env.rs +++ b/core-backend/src/env.rs @@ -241,6 +241,7 @@ where builder.add_func(Alloc, wrap_syscall!(alloc)); builder.add_func(Free, wrap_syscall!(free)); + builder.add_func(FreeRange, wrap_syscall!(free_range)); } } diff --git a/core-backend/src/funcs.rs b/core-backend/src/funcs.rs index 265a1aae110..e83f219811f 100644 --- a/core-backend/src/funcs.rs +++ b/core-backend/src/funcs.rs @@ -70,12 +70,6 @@ impl From for SyscallValue { } } -impl From for SyscallValue { - fn from(value: i64) -> Self { - SyscallValue(Value::I64(value)) - } -} - impl TryFrom for u32 { type Error = HostError; @@ -675,6 +669,32 @@ where }) } + pub fn free_range(start: u32, end: u32) -> impl Syscall { + InfallibleSyscall::new(RuntimeCosts::FreeRange, move |ctx: &mut CallerWrap| { + let page_err = |_| { + UndefinedTerminationReason::Actor(ActorTerminationReason::Trap( + TrapExplanation::Unknown, + )) + }; + + let start = WasmPage::new(start).map_err(page_err)?; + let end = WasmPage::new(end).map_err(page_err)?; + + let result = ctx.ext_mut().free_range(start, end); + + match ctx.process_alloc_func_result(result)? { + Ok(()) => { + log::trace!("Free range {start:?}:{end:?} success"); + Ok(0) + } + Err(e) => { + log::trace!("Free range {start:?}:{end:?} failed: {e}"); + Ok(1) + } + } + }) + } + pub fn env_vars(vars_ver: u32, vars_ptr: u32) -> impl Syscall { InfallibleSyscall::new(RuntimeCosts::EnvVars, move |ctx: &mut CallerWrap| { let vars = ctx.ext_mut().env_vars(vars_ver)?; diff --git a/core-backend/src/memory.rs b/core-backend/src/memory.rs index 626757962a5..5db2ce99655 100644 --- a/core-backend/src/memory.rs +++ b/core-backend/src/memory.rs @@ -534,7 +534,7 @@ mod tests { Ok(137.into()) ); - // if we have 2 in a row we can allocate even 2 + // if we free 2 in a row we can allocate even 2 ctx.free(117.into()).unwrap(); ctx.free(118.into()).unwrap(); @@ -543,6 +543,14 @@ mod tests { Ok(117.into()) ); + // same as above, if we free_range 2 in a row we can allocate 2 + ctx.free_range(117.into()..=118.into()).unwrap(); + + assert_eq!( + ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), + Ok(117.into()) + ); + // but if 2 are not in a row, bad luck ctx.free(117.into()).unwrap(); ctx.free(158.into()).unwrap(); diff --git a/core-backend/src/mock.rs b/core-backend/src/mock.rs index 84f55e70774..7f577544136 100644 --- a/core-backend/src/mock.rs +++ b/core-backend/src/mock.rs @@ -115,6 +115,9 @@ impl Externalities for MockExt { fn free(&mut self, _page: WasmPage) -> Result<(), Self::AllocError> { Err(Error) } + fn free_range(&mut self, _start: WasmPage, _end: WasmPage) -> Result<(), Self::AllocError> { + Err(Error) + } fn env_vars(&self, version: u32) -> Result { match version { 1 => Ok(EnvVars::V1(EnvVarsV1 { diff --git a/core-processor/src/ext.rs b/core-processor/src/ext.rs index 9c56457f97b..54e6b20f5bf 100644 --- a/core-processor/src/ext.rs +++ b/core-processor/src/ext.rs @@ -40,7 +40,7 @@ use gear_core::{ ContextOutcomeDrain, ContextStore, Dispatch, GasLimit, HandlePacket, InitPacket, MessageContext, Packet, ReplyPacket, }, - pages::{GearPage, PageU32Size, WasmPage}, + pages::{GearPage, PageNumber, PageU32Size, WasmPage}, program::MemoryInfix, reservation::GasReserver, }; @@ -799,6 +799,30 @@ impl Externalities for Ext { .map_err(Into::into) } + fn free_range(&mut self, start: WasmPage, end: WasmPage) -> Result<(), Self::AllocError> { + let page_count: u32 = end + .checked_sub(start) + .ok_or(AllocExtError::Alloc(AllocError::InvalidFreeRange( + start.into(), + end.into(), + )))? + .into(); + + Ext::charge_gas_if_enough( + &mut self.context.gas_counter, + &mut self.context.gas_allowance_counter, + self.context + .host_fn_weights + .free_range_per_page + .saturating_mul(page_count as u64), + )?; + + self.context + .allocations_context + .free_range(start..=end) + .map_err(Into::into) + } + fn env_vars(&self, version: u32) -> Result { match version { 1 => Ok(EnvVars::V1(EnvVarsV1 { @@ -1243,10 +1267,7 @@ impl Externalities for Ext { mod tests { use super::*; use alloc::vec; - use gear_core::{ - message::{ContextSettings, IncomingDispatch, Payload, MAX_PAYLOAD_SIZE}, - pages::PageNumber, - }; + use gear_core::message::{ContextSettings, IncomingDispatch, Payload, MAX_PAYLOAD_SIZE}; struct MessageContextBuilder { incoming_dispatch: IncomingDispatch, @@ -1363,12 +1384,12 @@ mod tests { ); // Freeing existing page. - // Counters still shouldn't be changed. + // Counters shouldn't be changed. assert!(ext.free(existing_page).is_ok()); assert_eq!(ext.gas_left(), gas_left); // Freeing non existing page. - // Counters shouldn't be changed. + // Counters still shouldn't be changed. assert_eq!( ext.free(non_existing_page), Err(AllocExtError::Alloc(AllocError::InvalidFree( diff --git a/core/src/costs.rs b/core/src/costs.rs index 52ba67d015c..9cbe724bfb4 100644 --- a/core/src/costs.rs +++ b/core/src/costs.rs @@ -94,9 +94,15 @@ pub struct HostFnWeights { /// Weight per allocated page for `alloc`. pub alloc_per_page: u64, - /// Weight of calling `alloc`. + /// Weight of calling `free`. pub free: u64, + /// Weight of calling `free_range` + pub free_range: u64, + + /// Weight of calling `free_range` per page + pub free_range_per_page: u64, + /// Weight of calling `gr_reserve_gas`. pub gr_reserve_gas: u64, @@ -326,6 +332,10 @@ pub enum RuntimeCosts { Alloc(u32), /// Weight of calling `free`. Free, + /// Base weight of calling `free_range` + FreeRange, + /// Weight of calling `free_range` per amount of pages. + FreeRangePerPage(u32), /// Weight of calling `gr_reserve_gas`. ReserveGas, /// Weight of calling `gr_unreserve_gas`. @@ -467,6 +477,8 @@ impl RuntimeCosts { Null => 0, Alloc(pages) => cost_with_weight_per_page(s.alloc, s.alloc_per_page, pages), Free => s.free, + FreeRange => s.free_range, + FreeRangePerPage(pages) => cost_with_weight_per_page(0, s.free_range_per_page, pages), ReserveGas => s.gr_reserve_gas, UnreserveGas => s.gr_unreserve_gas, SystemReserveGas => s.gr_system_reserve_gas, diff --git a/core/src/env.rs b/core/src/env.rs index 7f89688cd7e..dde033306e3 100644 --- a/core/src/env.rs +++ b/core/src/env.rs @@ -192,12 +192,12 @@ pub trait Externalities { mem: &mut impl Memory, ) -> Result; - /// Free specific memory page. - /// - /// Unlike traditional allocator, if multiple pages allocated via `alloc`, all pages - /// should be `free`-d separately. + /// Free specific page. fn free(&mut self, page: WasmPage) -> Result<(), Self::AllocError>; + /// Free specific memory range. + fn free_range(&mut self, start: WasmPage, end: WasmPage) -> Result<(), Self::AllocError>; + /// Get environment variables currently set in the system and in the form /// corresponded to the requested version. fn env_vars(&self, version: u32) -> Result; diff --git a/core/src/memory.rs b/core/src/memory.rs index 10bb65bd74f..a86643128f9 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -29,7 +29,7 @@ use core::{ fmt, fmt::Debug, iter, - ops::{Deref, DerefMut}, + ops::{Deref, DerefMut, RangeInclusive}, }; use scale_info::{ scale::{self, Decode, Encode, EncodeLike, Input, Output}, @@ -270,6 +270,9 @@ pub enum AllocError { /// outside additionally allocated for this program. #[display(fmt = "Page {_0} cannot be freed by the current program")] InvalidFree(u32), + /// Invalid range for free_range + #[display(fmt = "Invalid range {_0}:{_1} for free_range")] + InvalidFreeRange(u32, u32), /// Gas charge error #[from] #[display(fmt = "{_0}")] @@ -376,15 +379,29 @@ impl AllocationsContext { Ok(start) } - /// Free specific page. - /// - /// Currently running program should own this page. + /// Free specific memory page. pub fn free(&mut self, page: WasmPage) -> Result<(), AllocError> { - if page < self.static_pages || page >= self.max_pages || !self.allocations.remove(&page) { - Err(AllocError::InvalidFree(page.0)) - } else { - Ok(()) + if page < self.static_pages || page >= self.max_pages { + return Err(AllocError::InvalidFree(page.0)); + } + + if !self.allocations.remove(&page) { + return Err(AllocError::InvalidFree(page.0)); + } + + Ok(()) + } + + /// Try to free pages in range. Will only return error if range is invalid. + /// + /// Currently running program should own this pages. + pub fn free_range(&mut self, range: RangeInclusive) -> Result<(), AllocError> { + if *range.start() < self.static_pages || *range.end() >= self.max_pages { + return Err(AllocError::InvalidFreeRange(range.start().0, range.end().0)); } + + self.allocations.retain(|p| !range.contains(p)); + Ok(()) } /// Decomposes this instance and returns allocations. @@ -458,6 +475,13 @@ mod tests { let mut ctx = AllocationsContext::new(BTreeSet::from([WasmPage(0)]), WasmPage(1), WasmPage(1)); assert_eq!(ctx.free(WasmPage(1)), Err(AllocError::InvalidFree(1))); + + let mut ctx = AllocationsContext::new( + BTreeSet::from([WasmPage(1), WasmPage(3)]), + WasmPage(1), + WasmPage(4), + ); + assert_eq!(ctx.free_range(WasmPage(1)..=WasmPage(3)), Ok(())); } #[test] @@ -540,13 +564,15 @@ mod tests { enum Action { Alloc { pages: WasmPage }, Free { page: WasmPage }, + FreeRange { page: WasmPage, size: u8 }, } fn actions() -> impl Strategy> { let action = wasm_page_number().prop_flat_map(|page| { prop_oneof![ Just(Action::Alloc { pages: page }), - Just(Action::Free { page }) + Just(Action::Free { page }), + any::().prop_map(move |size| Action::FreeRange { page, size }), ] }); proptest::collection::vec(action, 0..1024) @@ -579,6 +605,7 @@ mod tests { fn assert_free_error(err: AllocError) { match err { AllocError::InvalidFree(_) => {} + AllocError::InvalidFreeRange(_, _) => {} err => panic!("{err:?}"), } } @@ -610,6 +637,12 @@ mod tests { assert_free_error(err); } } + Action::FreeRange { page, size } => { + let end = WasmPage::from(page.0.saturating_add(size as u32) as u16); + if let Err(err) = ctx.free_range(page..=end) { + assert_free_error(err); + } + } } } } diff --git a/gcli/src/meta/executor.rs b/gcli/src/meta/executor.rs index 42381ae2328..b15069d8dfa 100644 --- a/gcli/src/meta/executor.rs +++ b/gcli/src/meta/executor.rs @@ -131,6 +131,7 @@ mod env { "gr_size" => gr_size(store, memory), // methods may be used by programs but not required by metadata. "free" => func!(@result store, i32), + "free_range" => func!(@result store, i32, i32), "gr_block_height" => func!(store, u32), "gr_block_timestamp" => func!(store, u32), "gr_create_program_wgas" => func!(store, i32, i32, u32, i32, u32, u64, u32, i32), diff --git a/gsdk/src/metadata/generated.rs b/gsdk/src/metadata/generated.rs index 2024928555f..b64cac4330e 100644 --- a/gsdk/src/metadata/generated.rs +++ b/gsdk/src/metadata/generated.rs @@ -2778,6 +2778,8 @@ pub mod runtime_types { pub alloc: runtime_types::sp_weights::weight_v2::Weight, pub alloc_per_page: runtime_types::sp_weights::weight_v2::Weight, pub free: runtime_types::sp_weights::weight_v2::Weight, + pub free_range: runtime_types::sp_weights::weight_v2::Weight, + pub free_range_per_page: runtime_types::sp_weights::weight_v2::Weight, pub gr_reserve_gas: runtime_types::sp_weights::weight_v2::Weight, pub gr_unreserve_gas: runtime_types::sp_weights::weight_v2::Weight, pub gr_system_reserve_gas: runtime_types::sp_weights::weight_v2::Weight, diff --git a/pallets/gear/src/benchmarking/mod.rs b/pallets/gear/src/benchmarking/mod.rs index f829491f962..b911e5bcc97 100644 --- a/pallets/gear/src/benchmarking/mod.rs +++ b/pallets/gear/src/benchmarking/mod.rs @@ -826,6 +826,28 @@ benchmarks! { verify_process(res.unwrap()); } + free_range { + let r in 0 .. API_BENCHMARK_BATCHES; + let mut res = None; + let exec = Benches::::free_range(r, 1)?; + }: { + res.replace(run_process(exec)); + } + verify { + verify_process(res.unwrap()); + } + + free_range_per_page { + let p in 1 .. API_BENCHMARK_BATCHES; + let mut res = None; + let exec = Benches::::free_range(1, p)?; + }: { + res.replace(run_process(exec)); + } + verify { + verify_process(res.unwrap()); + } + gr_reserve_gas { let r in 0 .. T::ReservationsLimit::get() as u32; let mut res = None; diff --git a/pallets/gear/src/benchmarking/syscalls.rs b/pallets/gear/src/benchmarking/syscalls.rs index 4e0b6484fa9..411b1a55476 100644 --- a/pallets/gear/src/benchmarking/syscalls.rs +++ b/pallets/gear/src/benchmarking/syscalls.rs @@ -262,6 +262,43 @@ where Self::prepare_handle(module, 0) } + pub fn free_range(repetitions: u32, pages_per_call: u32) -> Result, &'static str> { + use Instruction::*; + + let n_pages = repetitions.checked_mul(pages_per_call).unwrap(); + assert!(n_pages <= max_pages::() as u32); + + let mut instructions = vec![]; + for _ in 0..API_BENCHMARK_BATCH_SIZE { + instructions.extend([I32Const(n_pages as i32), Call(0), I32Const(-1)]); + unreachable_condition(&mut instructions, I32Eq); // if alloc returns -1 then it's error + + for i in 0..repetitions { + let start = i.checked_mul(pages_per_call).unwrap(); + let end = pages_per_call + .checked_sub(1) + .and_then(|x| start.checked_add(x)) + .unwrap(); + instructions.extend([ + I32Const(start as i32), + I32Const(end as i32), + Call(1), + I32Const(0), + ]); + unreachable_condition(&mut instructions, I32Ne); + } + } + + let module = ModuleDefinition { + memory: Some(ImportedMemory::new(0)), + imported_functions: vec![SyscallName::Alloc, SyscallName::FreeRange], + handle_body: Some(body::from_instructions(instructions)), + ..Default::default() + }; + + Self::prepare_handle(module, 0) + } + pub fn gr_reserve_gas(r: u32) -> Result, &'static str> { let repetitions = r; let res_offset = COMMON_OFFSET; diff --git a/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs b/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs index 3a6938a92bc..f5a8bca4b8e 100644 --- a/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs +++ b/pallets/gear/src/benchmarking/tests/syscalls_integrity.rs @@ -178,8 +178,11 @@ where | SyscallName::Debug | SyscallName::Panic | SyscallName::OomPanic => {/* tests here aren't required, read module docs for more info */}, - SyscallName::Alloc => check_mem::(false), - SyscallName::Free => check_mem::(true), + + SyscallName::Alloc + | SyscallName::Free + | SyscallName::FreeRange => check_mem::(), + SyscallName::OutOfGas => { /*no need for tests */} SyscallName::Random => check_gr_random::(), SyscallName::ReserveGas => check_gr_reserve_gas::(), @@ -380,7 +383,7 @@ where }); } -fn check_mem(check_free: bool) +fn check_mem() where T: Config, T::AccountId: Origin, @@ -412,23 +415,25 @@ where utils::run_to_next_block::(None); // no errors occurred + assert!(Gear::::is_initialized(pid)); + assert!(Gear::::is_active(pid)); assert!(MailboxOf::::is_empty(&default_account)); - if check_free { - Gear::::send_message( - RawOrigin::Signed(default_account.clone()).into(), - pid, - b"".to_vec(), - 50_000_000_000, - 0u128.unique_saturated_into(), - false, - ) - .expect("failed to send message to test program"); - utils::run_to_next_block::(None); + Gear::::send_message( + RawOrigin::Signed(default_account.clone()).into(), + pid, + b"".to_vec(), + 50_000_000_000, + 0u128.unique_saturated_into(), + false, + ) + .expect("failed to send message to test program"); + utils::run_to_next_block::(None); - // no errors occurred - assert!(MailboxOf::::is_empty(&default_account)); - } + // no errors occurred + assert!(Gear::::is_initialized(pid)); + assert!(Gear::::is_active(pid)); + assert!(MailboxOf::::is_empty(&default_account)); Gear::::reset(); } @@ -1209,70 +1214,6 @@ where .into() } -// (module -// (import "env" "memory" (memory 1)) -// (import "env" "alloc" (func $alloc (param i32) (result i32))) -// (import "env" "free" (func $free (param i32))) -// (export "init" (func $init)) -// (export "handle" (func $handle)) -// (func $init -// ;; allocate 2 more pages with expected starting index 1 -// (block -// i32.const 0x2 -// call $alloc -// i32.const 0x1 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; put to page with index 2 (the third) some value -// (block -// i32.const 0x20001 -// i32.const 0x63 -// i32.store -// ) -// ;; put to page with index 1 (the second) some value -// (block -// i32.const 0x10001 -// i32.const 0x64 -// i32.store -// ) -// ;; check it has the value -// (block -// i32.const 0x10001 -// i32.load -// i32.const 0x65 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; remove page with index 1 (the second page) -// (block -// i32.const 0x1 -// call $free -// ) -// ) -// (func $handle -// ;; check that the second page is empty -// (block -// i32.const 0x10001 -// i32.load -// i32.const 0x0 -// i32.eq -// br_if 0 -// unreachable -// ) -// ;; check that the third page has data -// (block -// i32.const 0x20001 -// i32.load -// i32.const 0x63 -// i32.eq -// br_if 0 -// unreachable -// ) -// ) -// ) fn alloc_free_test_wasm() -> WasmModule where T::AccountId: Origin, @@ -1281,53 +1222,67 @@ where ModuleDefinition { memory: Some(ImportedMemory::new(1)), - imported_functions: vec![SyscallName::Alloc, SyscallName::Free], + imported_functions: vec![ + SyscallName::Alloc, + SyscallName::Free, + SyscallName::FreeRange, + ], init_body: Some(FuncBody::new( vec![], Instructions::new(vec![ - // ;; allocate 2 more pages with expected starting index 1 + // allocate 5 pages Instruction::Block(BlockType::NoResult), - Instruction::I32Const(0x2), + Instruction::I32Const(0x5), Instruction::Call(0), Instruction::I32Const(0x1), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; put to page with index 2 (the third) some value + // put some values in pages 2-5 Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x10001), + Instruction::I32Const(0x61), + Instruction::I32Store(2, 0), Instruction::I32Const(0x20001), + Instruction::I32Const(0x62), + Instruction::I32Store(2, 0), + Instruction::I32Const(0x30001), Instruction::I32Const(0x63), Instruction::I32Store(2, 0), - Instruction::End, - // ;; put to page with index 1 (the second) some value - Instruction::Block(BlockType::NoResult), - Instruction::I32Const(0x10001), + Instruction::I32Const(0x40001), Instruction::I32Const(0x64), Instruction::I32Store(2, 0), Instruction::End, - // ;; check it has the value + // check it has the value Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x10001), Instruction::I32Load(2, 0), - Instruction::I32Const(0x64), + Instruction::I32Const(0x61), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; remove page with index 1 (the second page) + // free second page Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x1), Instruction::Call(1), Instruction::Drop, Instruction::End, + // free_range pages 2-4 + Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x1), + Instruction::I32Const(0x3), + Instruction::Call(2), + Instruction::Drop, + Instruction::End, Instruction::End, ]), )), handle_body: Some(FuncBody::new( vec![], Instructions::new(vec![ - // ;; check that the second page is empty + // check that the second page is empty Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x10001), Instruction::I32Load(2, 0), @@ -1336,11 +1291,20 @@ where Instruction::BrIf(0), Instruction::Unreachable, Instruction::End, - // ;; check that the third page has data + // check that the 3rd page is empty Instruction::Block(BlockType::NoResult), Instruction::I32Const(0x20001), Instruction::I32Load(2, 0), - Instruction::I32Const(0x63), + Instruction::I32Const(0x0), + Instruction::I32Eq, + Instruction::BrIf(0), + Instruction::Unreachable, + Instruction::End, + // check that the 5th page still has data + Instruction::Block(BlockType::NoResult), + Instruction::I32Const(0x40001), + Instruction::I32Load(2, 0), + Instruction::I32Const(0x64), Instruction::I32Eq, Instruction::BrIf(0), Instruction::Unreachable, diff --git a/pallets/gear/src/schedule.rs b/pallets/gear/src/schedule.rs index 5e86704f868..737251e29d2 100644 --- a/pallets/gear/src/schedule.rs +++ b/pallets/gear/src/schedule.rs @@ -344,6 +344,12 @@ pub struct HostFnWeights { /// Weight of calling `free`. pub free: Weight, + /// Weight of calling `free_range`. + pub free_range: Weight, + + /// Weight of calling `free_range` per page. + pub free_range_per_page: Weight, + /// Weight of calling `gr_reserve_gas`. pub gr_reserve_gas: Weight, @@ -857,6 +863,8 @@ impl HostFnWeights { alloc: self.alloc.ref_time(), alloc_per_page: self.alloc_per_page.ref_time(), free: self.free.ref_time(), + free_range: self.free_range.ref_time(), + free_range_per_page: self.free_range_per_page.ref_time(), gr_reserve_gas: self.gr_reserve_gas.ref_time(), gr_unreserve_gas: self.gr_unreserve_gas.ref_time(), gr_system_reserve_gas: self.gr_system_reserve_gas.ref_time(), @@ -979,6 +987,8 @@ impl Default for HostFnWeights { .saturating_sub(to_weight!(cost_batched!(mem_grow))), alloc_per_page: to_weight!(cost_batched!(alloc_per_page)), free: to_weight!(cost_batched!(free)), + free_range: to_weight!(cost_batched!(free_range)), + free_range_per_page: to_weight!(cost_batched!(free_range_per_page)), gr_reserve_gas: to_weight!(cost!(gr_reserve_gas)), gr_system_reserve_gas: to_weight!(cost_batched!(gr_system_reserve_gas)), gr_unreserve_gas: to_weight!(cost!(gr_unreserve_gas)), diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index 2bbb59d5f83..5b778587a69 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -13904,6 +13904,160 @@ fn free_usage_error() { }); } +#[test] +fn free_range_oob_error() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; free impossible and non-existing range + i32.const 0x0 + i32.const 0xffffff + call $free_range + i32.const 0x0 + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 10_000_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + + assert!(Gear::is_terminated(pid)); + assert_failed( + mid, + ActorExecutionErrorReplyReason::Trap(TrapExplanation::Unknown), + ); + }); +} + +#[test] +fn free_range_invalid_range_error() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; free invalid range (start > end) + i32.const 0x55 + i32.const 0x2 + call $free_range + i32.const 0x1 ;; we expect an error + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 500_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + assert!(!Gear::is_terminated(pid)); + assert_succeed(mid); + }); +} + +#[test] +fn free_range_success() { + const WAT: &str = r#" +(module + (import "env" "memory" (memory 1)) + (import "env" "alloc" (func $alloc (param i32) (result i32))) + (import "env" "free" (func $free (param i32) (result i32))) + (import "env" "free_range" (func $free_range (param i32) (param i32) (result i32))) + (export "init" (func $init)) + (func $init + ;; allocate 4 pages + i32.const 0x4 + call $alloc + + i32.const 1 + i32.ne + if + unreachable + end + + ;; free one page in range + i32.const 0x2 + call $free + + i32.const 0 + i32.ne + if + unreachable + end + + ;; free range with one missing page + i32.const 0x1 + i32.const 0x4 + call $free_range + i32.const 0x0 + i32.ne + if + unreachable + end + ) +) + "#; + + init_logger(); + new_test_ext().execute_with(|| { + let pid = Gear::upload_program( + RuntimeOrigin::signed(USER_1), + ProgramCodeKind::Custom(WAT).to_bytes(), + DEFAULT_SALT.to_vec(), + EMPTY_PAYLOAD.to_vec(), + 500_000_000_u64, + 0, + false, + ) + .map(|_| get_last_program_id()) + .unwrap(); + let mid = get_last_message_id(); + + run_to_next_block(None); + + assert_succeed(mid); + assert!(Gear::is_initialized(pid)); + assert!(Gear::is_active(pid)); + }); +} + #[test] fn reject_incorrect_stack_pointer() { let wat = format!( diff --git a/pallets/gear/src/weights.rs b/pallets/gear/src/weights.rs index a71ad0a9798..b0695a26165 100644 --- a/pallets/gear/src/weights.rs +++ b/pallets/gear/src/weights.rs @@ -69,6 +69,8 @@ pub trait WeightInfo { fn alloc(r: u32, ) -> Weight; fn alloc_per_page(p: u32, ) -> Weight; fn free(r: u32, ) -> Weight; + fn free_range(r: u32, ) -> Weight; + fn free_range_per_page(p: u32, ) -> Weight; fn gr_reserve_gas(r: u32, ) -> Weight; fn gr_unreserve_gas(r: u32, ) -> Weight; fn gr_system_reserve_gas(r: u32, ) -> Weight; @@ -594,6 +596,26 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: @@ -2565,6 +2587,26 @@ impl WeightInfo for () { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: diff --git a/runtime/vara/src/weights/pallet_gear.rs b/runtime/vara/src/weights/pallet_gear.rs index b0219137a90..76d4cbfb0af 100644 --- a/runtime/vara/src/weights/pallet_gear.rs +++ b/runtime/vara/src/weights/pallet_gear.rs @@ -19,13 +19,13 @@ //! Autogenerated weights for pallet_gear //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-10-20, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! CPU: `Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("vara-dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/gear benchmark pallet --chain=vara-dev --steps=50 --repeat=20 --pallet=pallet_gear --extrinsic=alloc,alloc_in_handle,alloc_per_page,claim_value,create_program,db_read_per_kb,db_write_per_kb,free,gr_block_height,gr_block_timestamp,gr_create_program,gr_create_program_per_kb,gr_create_program_wgas,gr_create_program_wgas_per_kb,gr_debug,gr_debug_per_kb,gr_gas_available,gr_message_id,gr_pay_program_rent,gr_program_id,gr_random,gr_read,gr_read_per_kb,gr_reply_code,gr_reply_deposit,gr_reply_per_kb,gr_reply_push,gr_reply_push_input,gr_reply_push_input_per_kb,gr_reply_push_per_kb,gr_reply_to,gr_reply_wgas_per_kb,gr_reservation_reply_commit_per_kb,gr_reservation_reply_per_kb,gr_reservation_send,gr_reservation_send_commit,gr_reservation_send_per_kb,gr_reserve_gas,gr_send,gr_send_commit,gr_send_commit_wgas,gr_send_init,gr_send_input,gr_send_input_wgas,gr_send_per_kb,gr_send_push,gr_send_push_input,gr_send_push_input_per_kb,gr_send_push_per_kb,gr_send_wgas,gr_send_wgas_per_kb,gr_signal_code,gr_signal_from,gr_size,gr_source,gr_system_reserve_gas,gr_unreserve_gas,gr_value,gr_value_available,gr_wake,initial_allocation,instantiate_module_per_kb,instr_br,instr_br_if,instr_br_table,instr_br_table_per_entry,instr_call,instr_call_const,instr_call_indirect,instr_call_indirect_per_param,instr_call_per_local,instr_global_get,instr_global_set,instr_i32add,instr_i32and,instr_i32clz,instr_i32ctz,instr_i32divs,instr_i32divu,instr_i32eq,instr_i32eqz,instr_i32extend16s,instr_i32extend8s,instr_i32ges,instr_i32geu,instr_i32gts,instr_i32gtu,instr_i32les,instr_i32leu,instr_i32load,instr_i32lts,instr_i32ltu,instr_i32mul,instr_i32ne,instr_i32or,instr_i32popcnt,instr_i32rems,instr_i32remu,instr_i32rotl,instr_i32rotr,instr_i32shl,instr_i32shrs,instr_i32shru,instr_i32store,instr_i32sub,instr_i32wrapi64,instr_i32xor,instr_i64add,instr_i64and,instr_i64clz,instr_i64ctz,instr_i64divs,instr_i64divu,instr_i64eq,instr_i64eqz,instr_i64extend16s,instr_i64extend32s,instr_i64extend8s,instr_i64extendsi32,instr_i64extendui32,instr_i64ges,instr_i64geu,instr_i64gts,instr_i64gtu,instr_i64les,instr_i64leu,instr_i64load,instr_i64lts,instr_i64ltu,instr_i64mul,instr_i64ne,instr_i64or,instr_i64popcnt,instr_i64rems,instr_i64remu,instr_i64rotl,instr_i64rotr,instr_i64shl,instr_i64shrs,instr_i64shru,instr_i64store,instr_i64sub,instr_i64xor,instr_if,instr_local_get,instr_local_set,instr_local_tee,instr_memory_current,instr_select,lazy_pages_host_func_read,lazy_pages_host_func_write,lazy_pages_host_func_write_after_read,lazy_pages_load_page_storage_data,lazy_pages_signal_read,lazy_pages_signal_write,lazy_pages_signal_write_after_read,mem_grow,pay_program_rent,reinstrument_per_kb,resume_session_commit,resume_session_init,resume_session_push,send_message,send_reply,tasks_pause_program,tasks_pause_program_uninited,tasks_remove_from_mailbox,tasks_remove_from_waitlist,tasks_remove_gas_reservation,tasks_remove_resume_session,tasks_send_dispatch,tasks_send_user_message,tasks_send_user_message_to_mailbox,tasks_wake_message,tasks_wake_message_no_wake,upload_code,upload_program,gr_env_vars --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./scripts/benchmarking/weights-output/pallet_gear.rs --template=.maintain/frame-weight-template.hbs +// ./target/production/gear benchmark pallet --chain=vara-dev --steps=50 --repeat=20 --pallet=pallet_gear --extrinsic=alloc,alloc_in_handle,alloc_per_page,claim_value,create_program,db_read_per_kb,db_write_per_kb,free,free_range,free_range_per_page,gr_block_height,gr_block_timestamp,gr_create_program,gr_create_program_per_kb,gr_create_program_wgas,gr_create_program_wgas_per_kb,gr_debug,gr_debug_per_kb,gr_env_vars,gr_gas_available,gr_message_id,gr_pay_program_rent,gr_program_id,gr_random,gr_read,gr_read_per_kb,gr_reply_code,gr_reply_deposit,gr_reply_per_kb,gr_reply_push,gr_reply_push_input,gr_reply_push_input_per_kb,gr_reply_push_per_kb,gr_reply_to,gr_reply_wgas_per_kb,gr_reservation_reply_commit_per_kb,gr_reservation_reply_per_kb,gr_reservation_send,gr_reservation_send_commit,gr_reservation_send_per_kb,gr_reserve_gas,gr_send,gr_send_commit,gr_send_commit_wgas,gr_send_init,gr_send_input,gr_send_input_wgas,gr_send_per_kb,gr_send_push,gr_send_push_input,gr_send_push_input_per_kb,gr_send_push_per_kb,gr_send_wgas,gr_send_wgas_per_kb,gr_signal_code,gr_signal_from,gr_size,gr_source,gr_system_reserve_gas,gr_unreserve_gas,gr_value,gr_value_available,gr_wake,initial_allocation,instantiate_module_per_kb,instr_br,instr_br_if,instr_br_table,instr_br_table_per_entry,instr_call,instr_call_const,instr_call_indirect,instr_call_indirect_per_param,instr_call_per_local,instr_global_get,instr_global_set,instr_i32add,instr_i32and,instr_i32clz,instr_i32ctz,instr_i32divs,instr_i32divu,instr_i32eq,instr_i32eqz,instr_i32extend16s,instr_i32extend8s,instr_i32ges,instr_i32geu,instr_i32gts,instr_i32gtu,instr_i32les,instr_i32leu,instr_i32load,instr_i32lts,instr_i32ltu,instr_i32mul,instr_i32ne,instr_i32or,instr_i32popcnt,instr_i32rems,instr_i32remu,instr_i32rotl,instr_i32rotr,instr_i32shl,instr_i32shrs,instr_i32shru,instr_i32store,instr_i32sub,instr_i32wrapi64,instr_i32xor,instr_i64add,instr_i64and,instr_i64clz,instr_i64ctz,instr_i64divs,instr_i64divu,instr_i64eq,instr_i64eqz,instr_i64extend16s,instr_i64extend32s,instr_i64extend8s,instr_i64extendsi32,instr_i64extendui32,instr_i64ges,instr_i64geu,instr_i64gts,instr_i64gtu,instr_i64les,instr_i64leu,instr_i64load,instr_i64lts,instr_i64ltu,instr_i64mul,instr_i64ne,instr_i64or,instr_i64popcnt,instr_i64rems,instr_i64remu,instr_i64rotl,instr_i64rotr,instr_i64shl,instr_i64shrs,instr_i64shru,instr_i64store,instr_i64sub,instr_i64xor,instr_if,instr_local_get,instr_local_set,instr_local_tee,instr_memory_current,instr_select,lazy_pages_host_func_read,lazy_pages_host_func_write,lazy_pages_host_func_write_after_read,lazy_pages_load_page_storage_data,lazy_pages_signal_read,lazy_pages_signal_write,lazy_pages_signal_write_after_read,mem_grow,pay_program_rent,reinstrument_per_kb,resume_session_commit,resume_session_init,resume_session_push,send_message,send_reply,tasks_pause_program,tasks_pause_program_uninited,tasks_remove_from_mailbox,tasks_remove_from_waitlist,tasks_remove_gas_reservation,tasks_remove_resume_session,tasks_send_dispatch,tasks_send_user_message,tasks_send_user_message_to_mailbox,tasks_wake_message,tasks_wake_message_no_wake,upload_code,upload_program --execution=wasm --wasm-execution=compiled --heap-pages=4096 --output=./scripts/benchmarking/weights-output/pallet_gear.rs --template=.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -69,6 +69,8 @@ pub trait WeightInfo { fn alloc(r: u32, ) -> Weight; fn alloc_per_page(p: u32, ) -> Weight; fn free(r: u32, ) -> Weight; + fn free_range(r: u32, ) -> Weight; + fn free_range_per_page(p: u32, ) -> Weight; fn gr_reserve_gas(r: u32, ) -> Weight; fn gr_unreserve_gas(r: u32, ) -> Weight; fn gr_system_reserve_gas(r: u32, ) -> Weight; @@ -594,6 +596,26 @@ impl pallet_gear::WeightInfo for SubstrateWeight { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: @@ -2565,6 +2587,26 @@ impl WeightInfo for () { // Standard Error: 269_511 .saturating_add(Weight::from_parts(63_993_415, 0).saturating_mul(r.into())) } + /// The range of component `r` is `[0, 20]`. + fn free_range(r: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 229_654_000 picoseconds. + Weight::from_parts(251_181_937, 0) + // Standard Error: 228_273 + .saturating_add(Weight::from_parts(76_272_378, 0).saturating_mul(r.into())) + } + /// The range of component `p` is `[1, 20]`. + fn free_range_per_page(p: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 305_902_000 picoseconds. + Weight::from_parts(302_932_691, 0) + // Standard Error: 60_684 + .saturating_add(Weight::from_parts(5_306_653, 0).saturating_mul(p.into())) + } /// The range of component `r` is `[0, 256]`. fn gr_reserve_gas(r: u32, ) -> Weight { // Proof Size summary in bytes: diff --git a/utils/regression-analysis/src/main.rs b/utils/regression-analysis/src/main.rs index 94a7903bacf..1d72cac6ece 100644 --- a/utils/regression-analysis/src/main.rs +++ b/utils/regression-analysis/src/main.rs @@ -309,6 +309,8 @@ fn weights(kind: WeightsKind, input_file: PathBuf, output_file: PathBuf) { alloc, alloc_per_page, free, + free_range, + free_range_per_page, gr_gas_available, gr_message_id, gr_pay_program_rent, diff --git a/utils/wasm-gen/src/config/syscalls/param.rs b/utils/wasm-gen/src/config/syscalls/param.rs index f1bf90f1955..6e2352e41ae 100644 --- a/utils/wasm-gen/src/config/syscalls/param.rs +++ b/utils/wasm-gen/src/config/syscalls/param.rs @@ -97,6 +97,7 @@ impl Default for SyscallsParamsConfig { (ParamType::DelayBlockNumber, (0..=4).into()), (ParamType::Handler, (0..=100).into()), (ParamType::Free, (free_start..=free_end).into()), + (ParamType::FreeUpperBound, (0..=10).into()), (ParamType::Version, (1..=1).into()), ] .into_iter() diff --git a/utils/wasm-gen/src/generator/syscalls/invocator.rs b/utils/wasm-gen/src/generator/syscalls/invocator.rs index 06d0cb1bc4f..52318d268dd 100644 --- a/utils/wasm-gen/src/generator/syscalls/invocator.rs +++ b/utils/wasm-gen/src/generator/syscalls/invocator.rs @@ -43,6 +43,9 @@ pub(crate) enum ProcessedSyscallParams { Alloc { allowed_values: Option, }, + FreeUpperBound { + allowed_values: Option, + }, Value { value_type: ValueType, allowed_values: Option, @@ -84,6 +87,10 @@ pub(crate) fn process_syscall_params( ty: PtrType::SizedBufferStart { .. }, .. }) => ProcessedSyscallParams::MemoryArrayPtr, + ParamType::FreeUpperBound => { + let allowed_values = params_config.get_rule(¶m); + ProcessedSyscallParams::FreeUpperBound { allowed_values } + } ParamType::Ptr(_) => ProcessedSyscallParams::MemoryPtrValue, _ => ProcessedSyscallParams::Value { value_type: param.into(), @@ -167,6 +174,10 @@ impl ParamSetter { } } + /// Get value of the instruction. + /// + /// # Panics + /// Panics if the instruction is not `I32Const` or `I64Const`. fn get_value(&self) -> i64 { match self.0 { Instruction::I32Const(value) => value as i64, @@ -561,6 +572,23 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { setters.push(setter); } + ProcessedSyscallParams::FreeUpperBound { allowed_values } => { + let previous_param = setters + .last() + .expect("expected Free parameter before FreeUpperBound") + .as_i32() + .expect("referenced param should evaluate to I32Const"); + + let size = allowed_values + .expect("allowed_values should be set for FreeUpperBound") + .get_i32(self.unstructured)?; + + let value = previous_param.saturating_add(size); + + log::trace!(" ---- Add value - {}", value); + + setters.push(ParamSetter::new_i32(value)) + } } } @@ -638,12 +666,12 @@ impl<'a, 'b> SyscallsInvocator<'a, 'b> { // Alloc syscall: returns u32::MAX (= -1i32) in case of error. -1 } - ParamType::Free => { - // Free syscall: returns 1 in case of error. + ParamType::Free | ParamType::FreeUpperBound => { + // free/free_range syscall: returns 1 in case of error. 1 } _ => { - unimplemented!("Only alloc and free are supported for now") + unimplemented!("Only alloc and free/free_range are supported for now") } }; diff --git a/utils/wasm-instrument/src/syscalls.rs b/utils/wasm-instrument/src/syscalls.rs index c00cace2543..3a3dd2a7cbe 100644 --- a/utils/wasm-instrument/src/syscalls.rs +++ b/utils/wasm-instrument/src/syscalls.rs @@ -93,6 +93,7 @@ pub enum SyscallName { // Hard under the hood calls, serving proper program execution Alloc, Free, + FreeRange, OutOfGas, // Miscellaneous @@ -120,6 +121,7 @@ impl SyscallName { SyscallName::OomPanic => "gr_oom_panic", SyscallName::Exit => "gr_exit", SyscallName::Free => "free", + SyscallName::FreeRange => "free_range", SyscallName::GasAvailable => "gr_gas_available", SyscallName::Leave => "gr_leave", SyscallName::MessageId => "gr_message_id", @@ -180,6 +182,7 @@ impl SyscallName { [ Self::Alloc, Self::Free, + Self::FreeRange, Self::Debug, Self::Panic, Self::OomPanic, @@ -242,6 +245,7 @@ impl SyscallName { match self { Self::Alloc => SyscallSignature::system([Alloc], [I32]), Self::Free => SyscallSignature::system([Free], [I32]), + Self::FreeRange => SyscallSignature::system([Free, FreeUpperBound], [I32]), Self::Debug => SyscallSignature::gr([ Ptr(PtrInfo::new_immutable(PtrType::SizedBufferStart { length_param_idx: 1, @@ -610,16 +614,19 @@ impl SyscallName { /// belongs to. See [`PtrInfo`] and [`PtrType`] for more details. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum ParamType { - Length, // i32 buffers length + Length, // i32 buffers size in memory Ptr(PtrInfo), // i32 pointer Gas, // i64 gas amount Offset, // i32 offset in the input buffer (message payload) + MessagePosition, // i32 message position DurationBlockNumber, // i32 duration in blocks DelayBlockNumber, // i32 delay in blocks Handler, // i32 handler number - Alloc, // i32 pages to alloc - Free, // i32 page number to free - Version, // i32 version number of exec settings + Alloc, // i32 alloc pages + Free, // i32 free page + // i32 free upper bound for use with free_range. Should be placed after Free in fn signature + FreeUpperBound, + Version, // i32 version number of exec settings } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] From efcb98589fb3ba16031152d95b2f249a3072951e Mon Sep 17 00:00:00 2001 From: clearloop <26088946+clearloop@users.noreply.github.com> Date: Mon, 4 Dec 2023 02:22:28 +0800 Subject: [PATCH 2/3] chore(dep): upgrade cargo-metadata (#3545) --- Cargo.lock | 20 +++++++++++++++++--- Cargo.toml | 24 +++++------------------- utils/wasm-builder/src/crate_info.rs | 4 ++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 65be8f601d5..fcd795dd766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1025,6 +1025,20 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.18", + "serde", + "serde_json", + "thiserror", +] + [[package]] name = "cargo_toml" version = "0.15.3" @@ -1615,7 +1629,7 @@ name = "crates-io-manager" version = "1.0.3" dependencies = [ "anyhow", - "cargo_metadata", + "cargo_metadata 0.18.1", "cargo_toml", "crates-io", "curl", @@ -4554,7 +4568,7 @@ name = "gear-wasm-builder" version = "0.1.2" dependencies = [ "anyhow", - "cargo_metadata", + "cargo_metadata 0.18.1", "chrono", "colored", "dirs", @@ -12671,7 +12685,7 @@ source = "git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0 dependencies = [ "ansi_term", "build-helper", - "cargo_metadata", + "cargo_metadata 0.15.3", "filetime", "sp-maybe-compressed-blob", "strum", diff --git a/Cargo.toml b/Cargo.toml index f6eced17bb9..90e81959fff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -102,9 +102,8 @@ base64 = "0.21.5" byteorder = { version = "1.5.0", default-features = false } blake2-rfc = { version = "0.2.18", default-features = false } bs58 = { version = "0.5.0", default-features = false } -# TODO: upgrade this package ( issue #2694 ) -cargo_metadata = "=0.15.3" -clap = { version = "4.4.10" } +cargo_metadata = "0.18.1" +clap = "4.4.8" codec = { package = "parity-scale-codec", version = "3.6.4", default-features = false } color-eyre = "0.6.2" colored = "2.0.0" @@ -148,22 +147,9 @@ serde_json = "^1" serde_yaml = "0.8.26" sha-1 = "0.10.1" static_assertions = "1" -# # NOTE -# -# 1. subxt v0.29.0 breaks the logging in gsdk, our fork is based on the -# unpublished v0.29.0 from the main repo with fixes. -# -# 2. subxt v0.29.0 upgrades the substrate dependencies which are not -# compatible with our current dependencies. -# -# 3. changing but patching the source here for making these work out of -# workspace. -# -# 4. subxt-metadata and subxt-codegen are just used by gsdk-codegen for now -# gathering them here for easy management. -subxt = { version = "0.32.1" } -subxt-metadata = { version = "0.32.1" } -subxt-codegen = { version = "0.32.1" } +subxt = "0.32.1" +subxt-metadata = "0.32.1" +subxt-codegen = "0.32.1" syn = "2.0.39" thiserror = "1.0.50" tokio = { version = "1.34.0" } diff --git a/utils/wasm-builder/src/crate_info.rs b/utils/wasm-builder/src/crate_info.rs index e94342744d3..6afa55a7287 100644 --- a/utils/wasm-builder/src/crate_info.rs +++ b/utils/wasm-builder/src/crate_info.rs @@ -18,7 +18,7 @@ use anyhow::{Context, Result}; use cargo_metadata::{Metadata, MetadataCommand, Package}; -use std::{collections::HashMap, path::Path}; +use std::{collections::BTreeMap, path::Path}; use crate::builder_error::BuilderError; @@ -32,7 +32,7 @@ pub struct CrateInfo { /// Crate version. pub version: String, /// Crate features. - pub features: HashMap>, + pub features: BTreeMap>, } impl CrateInfo { From 162cec16cb3de3d05c26c9e4e0c98d3d02be6661 Mon Sep 17 00:00:00 2001 From: mqxf Date: Sun, 3 Dec 2023 20:10:11 +0100 Subject: [PATCH 3/3] feat(demo-constructor): Support multiple calls in `if_else` (#3487) Co-authored-by: Dmitry Novikov --- examples/constructor/src/builder.rs | 17 +++----- examples/constructor/src/call.rs | 16 ++++--- pallets/gear/src/tests.rs | 66 +++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 19 deletions(-) diff --git a/examples/constructor/src/builder.rs b/examples/constructor/src/builder.rs index bf871191605..a11b94f0640 100644 --- a/examples/constructor/src/builder.rs +++ b/examples/constructor/src/builder.rs @@ -1,5 +1,5 @@ use crate::{Arg, Call}; -use alloc::{boxed::Box, string::ToString, vec, vec::Vec}; +use alloc::{string::ToString, vec, vec::Vec}; use core::{fmt::Debug, ops::Deref}; use parity_scale_codec::{WrapperTypeDecode, WrapperTypeEncode}; @@ -297,20 +297,13 @@ impl Calls { pub fn if_else( self, bool_arg: impl Into>, - mut true_call: Self, - mut false_call: Self, + true_calls: Self, + false_calls: Self, ) -> Self { - if true_call.len() != 1 || false_call.len() != 1 { - unimplemented!() - }; - - let true_call = true_call.0.remove(0); - let false_call = false_call.0.remove(0); - self.add_call(Call::IfElse( bool_arg.into(), - Box::new(true_call), - Box::new(false_call), + true_calls.calls(), + false_calls.calls(), )) } diff --git a/examples/constructor/src/call.rs b/examples/constructor/src/call.rs index 2f14a20030c..2a82d24c5ca 100644 --- a/examples/constructor/src/call.rs +++ b/examples/constructor/src/call.rs @@ -1,5 +1,5 @@ use crate::Arg; -use alloc::{boxed::Box, string::String, vec::Vec}; +use alloc::{string::String, vec::Vec}; use parity_scale_codec::{Decode, Encode}; #[derive(Clone, Debug, Decode, Encode)] @@ -34,7 +34,7 @@ pub enum Call { Exit(Arg<[u8; 32]>), BytesEq(Arg>, Arg>), Noop, - IfElse(Arg, Box, Box), + IfElse(Arg, Vec, Vec), Load, LoadBytes, Wait, @@ -246,18 +246,20 @@ mod wasm { Some((left == right).encode()) } - fn if_else(self, previous: Option) -> Option> { - let Self::IfElse(flag, true_call, false_call) = self else { + fn if_else(self, mut previous: Option) -> Option> { + let Self::IfElse(flag, true_calls, false_calls) = self else { unreachable!() }; let flag = flag.value(); - let call = if flag { true_call } else { false_call }; + let calls = if flag { true_calls } else { false_calls }; - let (_call, value) = call.process(previous); + for call in calls { + previous = Some(call.process(previous)); + } - value + previous.and_then(|res| res.1) } fn value(self) -> Option> { diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index 5b778587a69..63cfa56dfc9 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -15130,6 +15130,72 @@ fn test_handle_signal_wait() { }); } +#[test] +fn test_constructor_if_else() { + use demo_constructor::{Arg, Call, Calls, Scheme, WASM_BINARY}; + + init_logger(); + new_test_ext().execute_with(|| { + let init = Calls::builder().bool("switch", false); + let handle = Calls::builder() + .if_else( + Arg::get("switch"), + Calls::builder().add_call(Call::Bool(false)), + Calls::builder().add_call(Call::Bool(true)), + ) + .store("switch") + .if_else( + Arg::get("switch"), + Calls::builder().wait_for(1), + Calls::builder().exit(<[u8; 32]>::from(USER_1.into_origin())), + ); + + let scheme = Scheme::predefined(init, handle, Default::default(), Default::default()); + + assert_ok!(Gear::upload_program( + RuntimeOrigin::signed(USER_1), + WASM_BINARY.to_vec(), + DEFAULT_SALT.to_vec(), + scheme.encode(), + 100_000_000_000, + 0, + false, + )); + + let pid = get_last_program_id(); + + run_to_next_block(None); + + assert!(Gear::is_active(pid)); + assert!(Gear::is_initialized(pid)); + + assert_ok!(Gear::send_message( + RuntimeOrigin::signed(USER_1), + pid, + EMPTY_PAYLOAD.to_vec(), + 100_000_000_000, + 0, + false, + )); + + let mid = get_last_message_id(); + + run_to_next_block(None); + + let task = ScheduledTask::WakeMessage(pid, mid); + + assert!(WaitlistOf::::contains(&pid, &mid)); + assert!(TaskPoolOf::::contains( + &(Gear::block_number() + 1), + &task + )); + + run_to_next_block(None); + + assert!(!WaitlistOf::::contains(&pid, &mid)); + }); +} + mod utils { #![allow(unused)]