From 50a4ec10b145d793ba112d29092e64287f8400c4 Mon Sep 17 00:00:00 2001 From: Georgy Shepelev Date: Sun, 17 Sep 2023 20:30:16 +0400 Subject: [PATCH 1/6] refactor(node): Benchmark tasks and reduce GasAllowance accordingly (#2797) --- Cargo.lock | 2 + Cargo.toml | 8 +- common/src/lib.rs | 15 +- common/src/scheduler/task.rs | 28 +- examples/constructor/Cargo.toml | 4 +- examples/constructor/build.rs | 6 +- examples/constructor/src/arg.rs | 2 +- examples/constructor/src/call.rs | 2 +- examples/constructor/src/lib.rs | 10 +- .../src/scheme/demo_proxy_with_gas.rs | 2 +- examples/delayed-sender/Cargo.toml | 3 +- examples/delayed-sender/build.rs | 6 +- examples/delayed-sender/src/code.rs | 18 + examples/delayed-sender/src/lib.rs | 7 +- examples/init-wait/Cargo.toml | 9 +- examples/init-wait/build.rs | 6 +- examples/init-wait/src/code.rs | 18 + examples/init-wait/src/lib.rs | 24 +- examples/reserve-gas/Cargo.toml | 3 +- examples/reserve-gas/build.rs | 6 +- examples/reserve-gas/src/lib.rs | 6 +- examples/wait_wake/build.rs | 6 +- examples/waiter/Cargo.toml | 8 +- examples/waiter/build.rs | 6 +- examples/waiter/src/code.rs | 28 +- examples/waiter/src/lib.rs | 30 +- gclient/Cargo.toml | 4 +- gsdk/Cargo.toml | 2 +- gsdk/tests/rpc.rs | 2 +- pallets/gear-scheduler/src/tests.rs | 15 +- pallets/gear/Cargo.toml | 15 + pallets/gear/src/benchmarking/mod.rs | 122 +++++- pallets/gear/src/benchmarking/tasks.rs | 371 ++++++++++++++++++ pallets/gear/src/lib.rs | 58 ++- pallets/gear/src/manager/task.rs | 164 ++++++-- pallets/gear/src/mock.rs | 2 +- pallets/gear/src/tests.rs | 37 +- pallets/gear/src/weights.rs | 231 +++++++++++ runtime/gear/src/weights/pallet_gear.rs | 231 +++++++++++ runtime/vara/src/weights/pallet_gear.rs | 231 +++++++++++ 40 files changed, 1595 insertions(+), 153 deletions(-) create mode 100644 pallets/gear/src/benchmarking/tasks.rs diff --git a/Cargo.lock b/Cargo.lock index 108999619f0..76d3ff7a82e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1903,6 +1903,7 @@ dependencies = [ name = "demo-constructor" version = "0.1.0" dependencies = [ + "gcore", "gear-wasm-builder", "gstd", "hex", @@ -2314,6 +2315,7 @@ version = "0.1.0" dependencies = [ "demo-waiter", "futures", + "gcore", "gear-core", "gear-wasm-builder", "gstd", diff --git a/Cargo.toml b/Cargo.toml index 108d5011b92..1b581dac2b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -371,7 +371,7 @@ demo-calc-hash-in-one-block = { path = "examples/calc-hash/in-one-block" } demo-calc-hash-over-blocks = { path = "examples/calc-hash/over-blocks" } demo-custom = { path = "examples/custom" } demo-compose = { path = "examples/compose" } -demo-constructor = { path = "examples/constructor" } +demo-constructor = { path = "examples/constructor", default-features = false } demo-delayed-sender = { path = "examples/delayed-sender" } demo-distributor = { path = "examples/distributor" } demo-futures-unordered = { path = "examples/futures-unordered", features = ["debug"] } @@ -379,7 +379,7 @@ demo-gas-burned = { path = "examples/gas-burned" } demo-fungible-token = { path = "examples/fungible-token" } demo-incomplete-async-payloads = { path = "examples/incomplete-async-payloads" } demo-init-fail-sender = { path = "examples/init-fail-sender" } -demo-init-wait = { path = "examples/init-wait" } +demo-init-wait = { path = "examples/init-wait", default-features = false } demo-init-wait-reply-exit = { path = "examples/init-wait-reply-exit" } demo-messager = { path = "examples/messager" } demo-meta-io = { path = "examples/new-meta/io" } @@ -396,7 +396,7 @@ demo-proxy-relay = { path = "examples/proxy-relay" } demo-proxy-reservation-with-gas = { path = "examples/proxy-reservation-with-gas" } demo-read-big-state = { path = "examples/read-big-state", default-features = false } demo-reservation-manager = { path = "examples/reservation-manager" } -demo-reserve-gas = { path = "examples/reserve-gas" } +demo-reserve-gas = { path = "examples/reserve-gas", default-features = false } demo-rwlock = { path = "examples/rwlock" } demo-send-from-reservation = { path = "examples/send-from-reservation" } demo-signal-entry = { path = "examples/signal-entry" } @@ -404,7 +404,7 @@ demo-state-rollback = { path = "examples/state-rollback" } demo-sync-duplicate = { path = "examples/sync-duplicate" } demo-vec = { path = "examples/vec" } demo-wait = { path = "examples/wait" } -demo-waiter = { path = "examples/waiter" } +demo-waiter = { path = "examples/waiter", default-features = false } demo-wait-timeout = { path = "examples/wait-timeout" } demo-wait-wake = { path = "examples/wait_wake" } demo-waiting-proxy = { path = "examples/waiting-proxy" } diff --git a/common/src/lib.rs b/common/src/lib.rs index 3d4873bb6a2..64f47fd4aa4 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -73,6 +73,9 @@ pub use gas_provider::{ LockId, LockableTree, Provider as GasProvider, ReservableTree, Tree as GasTree, }; +/// Type alias for gas entity. +pub type Gas = u64; + pub trait Origin: Sized { fn into_origin(self) -> H256; fn from_origin(val: H256) -> Self; @@ -228,18 +231,6 @@ impl Program { }) ) } - - pub fn is_uninitialized(&self) -> Option { - if let Program::Active(ActiveProgram { - state: ProgramState::Uninitialized { message_id }, - .. - }) = self - { - Some(*message_id) - } else { - None - } - } } #[derive(Clone, Debug, derive_more::Display)] diff --git a/common/src/scheduler/task.rs b/common/src/scheduler/task.rs index ebeb0ff7e78..cbfd0575943 100644 --- a/common/src/scheduler/task.rs +++ b/common/src/scheduler/task.rs @@ -16,7 +16,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::paused_program_storage::SessionId; +use crate::{paused_program_storage::SessionId, Gas}; use frame_support::{ codec::{self, Decode, Encode, MaxEncodedLen}, scale_info::{self, TypeInfo}, @@ -85,7 +85,7 @@ pub enum ScheduledTask { } impl ScheduledTask { - pub fn process_with(self, handler: &mut impl TaskHandler) { + pub fn process_with(self, handler: &mut impl TaskHandler) -> Gas { use ScheduledTask::*; match self { @@ -117,32 +117,36 @@ pub trait TaskHandler { // Rent charging section. // ----- /// Pause program action. - fn pause_program(&mut self, program_id: ProgramId); + fn pause_program(&mut self, program_id: ProgramId) -> Gas; /// Remove code action. - fn remove_code(&mut self, code_id: CodeId); + fn remove_code(&mut self, code_id: CodeId) -> Gas; /// Remove from mailbox action. - fn remove_from_mailbox(&mut self, user_id: AccountId, message_id: MessageId); + fn remove_from_mailbox(&mut self, user_id: AccountId, message_id: MessageId) -> Gas; /// Remove from waitlist action. - fn remove_from_waitlist(&mut self, program_id: ProgramId, message_id: MessageId); + fn remove_from_waitlist(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas; /// Remove paused program action. - fn remove_paused_program(&mut self, program_id: ProgramId); + fn remove_paused_program(&mut self, program_id: ProgramId) -> Gas; // Time chained section. // ----- /// Wake message action. - fn wake_message(&mut self, program_id: ProgramId, message_id: MessageId); + fn wake_message(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas; // Send delayed message to program action. - fn send_dispatch(&mut self, stashed_message_id: MessageId); + fn send_dispatch(&mut self, stashed_message_id: MessageId) -> Gas; // Send delayed message to user action. - fn send_user_message(&mut self, stashed_message_id: MessageId, to_mailbox: bool); + fn send_user_message(&mut self, stashed_message_id: MessageId, to_mailbox: bool) -> Gas; /// Remove gas reservation action. - fn remove_gas_reservation(&mut self, program_id: ProgramId, reservation_id: ReservationId); + fn remove_gas_reservation( + &mut self, + program_id: ProgramId, + reservation_id: ReservationId, + ) -> Gas; /// Remove data created by resume program session. - fn remove_resume_session(&mut self, session_id: SessionId); + fn remove_resume_session(&mut self, session_id: SessionId) -> Gas; } #[test] diff --git a/examples/constructor/Cargo.toml b/examples/constructor/Cargo.toml index 10782f0558e..383ed6e29f7 100644 --- a/examples/constructor/Cargo.toml +++ b/examples/constructor/Cargo.toml @@ -7,6 +7,7 @@ license.workspace = true workspace = "../../" [dependencies] +gcore.workspace = true gstd.workspace = true parity-scale-codec = { workspace = true, features = ["derive"] } hex.workspace = true @@ -16,5 +17,6 @@ gear-wasm-builder.workspace = true [features] debug = ["gstd/debug"] -std = [] +wasm-wrapper = [] +std = ["wasm-wrapper"] default = ["std"] diff --git a/examples/constructor/build.rs b/examples/constructor/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/constructor/build.rs +++ b/examples/constructor/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/constructor/src/arg.rs b/examples/constructor/src/arg.rs index 2d78edf7576..b749bbc03b7 100644 --- a/examples/constructor/src/arg.rs +++ b/examples/constructor/src/arg.rs @@ -90,7 +90,7 @@ impl From<&'static str> for Arg { } } -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { use super::*; diff --git a/examples/constructor/src/call.rs b/examples/constructor/src/call.rs index 0287abd0db5..06c0fde0edb 100644 --- a/examples/constructor/src/call.rs +++ b/examples/constructor/src/call.rs @@ -44,7 +44,7 @@ pub enum Call { Loop, } -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { use super::*; use crate::DATA; diff --git a/examples/constructor/src/lib.rs b/examples/constructor/src/lib.rs index d112fb3d26b..0ebc224368d 100644 --- a/examples/constructor/src/lib.rs +++ b/examples/constructor/src/lib.rs @@ -16,22 +16,22 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] pub(crate) use wasm::DATA; -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); } -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] pub use code::WASM_BINARY_OPT as WASM_BINARY; mod arg; diff --git a/examples/constructor/src/scheme/demo_proxy_with_gas.rs b/examples/constructor/src/scheme/demo_proxy_with_gas.rs index 35c33e3b6c1..70b7e2a1ba5 100644 --- a/examples/constructor/src/scheme/demo_proxy_with_gas.rs +++ b/examples/constructor/src/scheme/demo_proxy_with_gas.rs @@ -1,5 +1,5 @@ use crate::{Arg, Call, Calls, Scheme}; -use gstd::errors::{ReplyCode, SuccessReplyReason}; +use gcore::errors::{ReplyCode, SuccessReplyReason}; use parity_scale_codec::Encode; pub const PROXIED_MESSAGE: &[u8] = b"proxied message"; diff --git a/examples/delayed-sender/Cargo.toml b/examples/delayed-sender/Cargo.toml index e23c5e2f310..7c386c1b22a 100644 --- a/examples/delayed-sender/Cargo.toml +++ b/examples/delayed-sender/Cargo.toml @@ -14,5 +14,6 @@ gear-wasm-builder.workspace = true [features] debug = ["gstd/debug"] -std = [ ] +wasm-wrapper = [] +std = ["wasm-wrapper"] default = ["std"] diff --git a/examples/delayed-sender/build.rs b/examples/delayed-sender/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/delayed-sender/build.rs +++ b/examples/delayed-sender/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/delayed-sender/src/code.rs b/examples/delayed-sender/src/code.rs index 3107b539988..c1c1c2d61f8 100644 --- a/examples/delayed-sender/src/code.rs +++ b/examples/delayed-sender/src/code.rs @@ -1,3 +1,21 @@ +// This file is part of Gear. + +// Copyright (C) Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use gstd::{msg, MessageId, exec}; static mut MID: Option = None; diff --git a/examples/delayed-sender/src/lib.rs b/examples/delayed-sender/src/lib.rs index 9348dfb7a52..1175ebd0d91 100644 --- a/examples/delayed-sender/src/lib.rs +++ b/examples/delayed-sender/src/lib.rs @@ -15,17 +15,18 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . + #![no_std] -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); } -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] pub use code::WASM_BINARY_OPT as WASM_BINARY; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { include! {"./code.rs"} } diff --git a/examples/init-wait/Cargo.toml b/examples/init-wait/Cargo.toml index 8b0d9b0b238..5a7c643798f 100644 --- a/examples/init-wait/Cargo.toml +++ b/examples/init-wait/Cargo.toml @@ -2,8 +2,8 @@ name = "demo-init-wait" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" +edition.workspace = true +license.workspace = true workspace = "../../" [dependencies] @@ -12,9 +12,8 @@ gstd.workspace = true [build-dependencies] gear-wasm-builder.workspace = true -[lib] - [features] debug = ["gstd/debug"] -std = [] +wasm-wrapper = [] +std = ["wasm-wrapper"] default = ["std"] diff --git a/examples/init-wait/build.rs b/examples/init-wait/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/init-wait/build.rs +++ b/examples/init-wait/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/init-wait/src/code.rs b/examples/init-wait/src/code.rs index 432a5fe94f1..c6ef0bc9edc 100644 --- a/examples/init-wait/src/code.rs +++ b/examples/init-wait/src/code.rs @@ -1,3 +1,21 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use gstd::{exec, msg, collections::BTreeMap, MessageId}; #[derive(PartialEq, Debug)] diff --git a/examples/init-wait/src/lib.rs b/examples/init-wait/src/lib.rs index e5e4ef17484..a4fc7ea8b42 100644 --- a/examples/init-wait/src/lib.rs +++ b/examples/init-wait/src/lib.rs @@ -1,14 +1,32 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + #![cfg_attr(not(feature = "std"), no_std)] -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); } -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] pub use code::WASM_BINARY_OPT as WASM_BINARY; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { include! {"./code.rs"} } diff --git a/examples/reserve-gas/Cargo.toml b/examples/reserve-gas/Cargo.toml index aef27afb79c..132b43911fa 100644 --- a/examples/reserve-gas/Cargo.toml +++ b/examples/reserve-gas/Cargo.toml @@ -18,5 +18,6 @@ gtest.workspace = true [features] debug = ["gstd/debug"] -std = [] +wasm-wrapper = [] +std = ["wasm-wrapper", "parity-scale-codec/std"] default = ["std"] diff --git a/examples/reserve-gas/build.rs b/examples/reserve-gas/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/reserve-gas/build.rs +++ b/examples/reserve-gas/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/reserve-gas/src/lib.rs b/examples/reserve-gas/src/lib.rs index da47e95a438..7644c983bb7 100644 --- a/examples/reserve-gas/src/lib.rs +++ b/examples/reserve-gas/src/lib.rs @@ -23,12 +23,12 @@ extern crate alloc; use alloc::vec::Vec; use parity_scale_codec::{Decode, Encode}; -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); } -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] pub use code::WASM_BINARY_OPT as WASM_BINARY; pub const RESERVATION_AMOUNT: u64 = 50_000_000; @@ -62,7 +62,7 @@ pub enum ReplyAction { pub type GasAmount = u64; pub type BlockCount = u32; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { use super::*; use gstd::{ diff --git a/examples/wait_wake/build.rs b/examples/wait_wake/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/wait_wake/build.rs +++ b/examples/wait_wake/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/waiter/Cargo.toml b/examples/waiter/Cargo.toml index a72ef94b3a5..3090fc3f7a0 100644 --- a/examples/waiter/Cargo.toml +++ b/examples/waiter/Cargo.toml @@ -2,14 +2,15 @@ name = "demo-waiter" version = "0.1.0" authors.workspace = true -edition = "2021" -license = "GPL-3.0" +edition.workspace = true +license.workspace = true workspace = "../../" [dependencies] parity-scale-codec = { workspace = true, features = ["derive"] } futures.workspace = true gstd.workspace = true +gcore.workspace = true [build-dependencies] gear-wasm-builder.workspace = true @@ -23,5 +24,6 @@ demo-waiter = { path = ".", features = ["debug"] } [features] debug = ["gstd/debug"] -std = ["parity-scale-codec/std"] +wasm-wrapper = [] +std = ["parity-scale-codec/std", "wasm-wrapper"] default = ["std"] diff --git a/examples/waiter/build.rs b/examples/waiter/build.rs index 4c502a3ddee..b911ce127bb 100644 --- a/examples/waiter/build.rs +++ b/examples/waiter/build.rs @@ -16,6 +16,10 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use gear_wasm_builder::WasmBuilder; + fn main() { - gear_wasm_builder::build(); + WasmBuilder::new() + .exclude_features(vec!["std", "wasm-wrapper"]) + .build(); } diff --git a/examples/waiter/src/code.rs b/examples/waiter/src/code.rs index 9bc91cb726d..7a68dd08957 100644 --- a/examples/waiter/src/code.rs +++ b/examples/waiter/src/code.rs @@ -1,3 +1,21 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + use crate::{ Command, LockContinuation, LockStaticAccessSubcommand, MxLockContinuation, RwLockContinuation, RwLockType, SleepForWaitType, WaitSubcommand, @@ -22,33 +40,33 @@ async fn main() { match cmd { Command::Wait(subcommand) => process_wait_subcommand(subcommand), Command::SendFor(to, duration) => { - msg::send_bytes_for_reply(to, [], 0, 0) + msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .exactly(Some(duration)) .expect("Invalid wait duration.") .await; } Command::SendUpTo(to, duration) => { - msg::send_bytes_for_reply(to, [], 0, 0) + msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .up_to(Some(duration)) .expect("Invalid wait duration.") .await; } Command::SendUpToWait(to, duration) => { - msg::send_bytes_for_reply(to, [], 0, 0) + msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .up_to(Some(duration)) .expect("Invalid wait duration.") .await; // after waking, wait again. - msg::send_bytes_for_reply(to, [], 0, 0) + msg::send_bytes_for_reply(to.into(), [], 0, 0) .expect("send message failed") .await; } Command::SendAndWaitFor(duration, to) => { - msg::send(to, b"ping", 0); + msg::send(to.into(), b"ping", 0); exec::wait_for(duration); } Command::ReplyAndWait(subcommand) => { diff --git a/examples/waiter/src/lib.rs b/examples/waiter/src/lib.rs index 1e744113065..b7d4619f789 100644 --- a/examples/waiter/src/lib.rs +++ b/examples/waiter/src/lib.rs @@ -15,29 +15,37 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![no_std] -use gstd::{ActorId, Vec}; +#![cfg_attr(not(feature = "std"), no_std)] + +extern crate alloc; + +use alloc::vec::Vec; +use gcore::BlockCount; use parity_scale_codec::{Decode, Encode}; -#[cfg(feature = "std")] +type ActorId = [u8; 32]; + +#[cfg(feature = "wasm-wrapper")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); } -#[cfg(feature = "std")] +#[cfg(feature = "wasm-wrapper")] pub use code::WASM_BINARY_OPT as WASM_BINARY; -#[cfg(not(feature = "std"))] +#[cfg(not(feature = "wasm-wrapper"))] mod wasm { include! {"./code.rs"} } +#[cfg(feature = "std")] pub fn system_reserve() -> u64 { gstd::Config::system_reserve() } // Re-exports for testing +#[cfg(feature = "std")] pub fn default_wait_up_to_duration() -> u32 { gstd::Config::wait_up_to() } @@ -94,14 +102,14 @@ pub enum RwLockContinuation { #[derive(Debug, Encode, Decode)] pub enum Command { Wait(WaitSubcommand), - SendFor(ActorId, gstd::BlockCount), - SendUpTo(ActorId, gstd::BlockCount), - SendUpToWait(ActorId, gstd::BlockCount), - SendAndWaitFor(gstd::BlockCount, ActorId), + SendFor(ActorId, BlockCount), + SendUpTo(ActorId, BlockCount), + SendUpToWait(ActorId, BlockCount), + SendAndWaitFor(BlockCount, ActorId), ReplyAndWait(WaitSubcommand), - SleepFor(Vec, SleepForWaitType), + SleepFor(Vec, SleepForWaitType), WakeUp([u8; 32]), - MxLock(gstd::BlockCount, MxLockContinuation), + MxLock(BlockCount, MxLockContinuation), MxLockStaticAccess(LockStaticAccessSubcommand), RwLock(RwLockType, RwLockContinuation), RwLockStaticAccess(RwLockType, LockStaticAccessSubcommand), diff --git a/gclient/Cargo.toml b/gclient/Cargo.toml index 3ee7de0f6ec..26f9d8b973a 100644 --- a/gclient/Cargo.toml +++ b/gclient/Cargo.toml @@ -38,7 +38,7 @@ demo-async-tester.workspace = true demo-calc-hash.workspace = true demo-calc-hash-in-one-block.workspace = true demo-custom.workspace = true -demo-constructor.workspace = true +demo-constructor = { workspace = true, features = ["std"] } demo-distributor.workspace = true demo-meta-io.workspace = true demo-new-meta.workspace = true @@ -47,7 +47,7 @@ demo-node.workspace = true demo-program-factory.workspace = true demo-proxy = { workspace = true, features = ["std"] } demo-proxy-relay.workspace = true -demo-reserve-gas.workspace = true +demo-reserve-gas = { workspace = true, features = ["std"] } gmeta = { workspace = true } gstd = { workspace = true, features = ["debug"] } demo-wat.workspace = true diff --git a/gsdk/Cargo.toml b/gsdk/Cargo.toml index d823fcd42d9..3985dbc5494 100644 --- a/gsdk/Cargo.toml +++ b/gsdk/Cargo.toml @@ -42,7 +42,7 @@ gsdk = { path = ".", features = ["testing"] } tokio = { workspace = true, features = [ "full" ] } demo-messager.workspace = true demo-new-meta.workspace = true -demo-waiter.workspace = true +demo-waiter = { workspace = true, features = ["std"] } [features] testing = [ "rand" ] diff --git a/gsdk/tests/rpc.rs b/gsdk/tests/rpc.rs index c458cee94e8..376af7741de 100644 --- a/gsdk/tests/rpc.rs +++ b/gsdk/tests/rpc.rs @@ -151,7 +151,7 @@ async fn test_calculate_reply_gas() -> Result<()> { let salt = vec![]; let pid = ProgramId::generate(CodeId::generate(demo_waiter::WASM_BINARY), &salt); - let payload = demo_waiter::Command::SendUpTo(alice.into(), 10); + let payload = demo_waiter::Command::SendUpTo(alice, 10); // 1. upload program. let signer = Api::new(Some(&node_uri(&node))) diff --git a/pallets/gear-scheduler/src/tests.rs b/pallets/gear-scheduler/src/tests.rs index d2ba06af84f..58a0285d026 100644 --- a/pallets/gear-scheduler/src/tests.rs +++ b/pallets/gear-scheduler/src/tests.rs @@ -89,6 +89,7 @@ fn populate_wl_from( (mid, pid) } +#[track_caller] fn task_and_wl_message_exist( mid: impl Into, pid: impl Into, @@ -100,9 +101,7 @@ fn task_and_wl_message_exist( let ts = TaskPoolOf::::contains(&bn, &ScheduledTask::RemoveFromWaitlist(pid, mid)); let wl = WaitlistOf::::contains(&pid, &mid); - if ts != wl { - panic!("Logic invalidated"); - } + assert_eq!(ts, wl, "Logic invalidated"); ts } @@ -196,12 +195,14 @@ fn gear_handles_tasks() { ); assert_eq!(GearBank::account_total(&USER_1), gas_price(DEFAULT_GAS)); + let task = ScheduledTask::RemoveFromWaitlist(Default::default(), Default::default()); + let task_gas = pallet_gear::manager::get_maximum_task_gas::(&task); // Check if task and message got processed in block `bn`. run_to_block(bn, Some(u64::MAX)); // Read of the first block of incomplete tasks and write for removal of task. assert_eq!( GasAllowanceOf::::get(), - u64::MAX - db_r_w(1, 1).ref_time() + u64::MAX - db_r_w(1, 1).ref_time() - task_gas ); // Storages checking. @@ -298,7 +299,9 @@ fn gear_handles_outdated_tasks() { // Check if task and message got processed before start of block `bn`. // But due to the low gas allowance, we may process the only first task. - run_to_block(bn, Some(db_r_w(1, 2).ref_time() + 1)); + let task = ScheduledTask::RemoveFromWaitlist(Default::default(), Default::default()); + let task_gas = pallet_gear::manager::get_maximum_task_gas::(&task); + run_to_block(bn, Some(db_r_w(2, 2).ref_time() + task_gas + 1)); // Read of the first block of incomplete tasks, write to it afterwards + single task processing. assert_eq!(GasAllowanceOf::::get(), 1); @@ -329,7 +332,7 @@ fn gear_handles_outdated_tasks() { // Delete of the first block of incomplete tasks + single task processing. assert_eq!( GasAllowanceOf::::get(), - u64::MAX - db_r_w(0, 2).ref_time() + u64::MAX - db_r_w(0, 2).ref_time() - task_gas ); let cost2 = wl_cost_for(bn + 1 - initial_block); diff --git a/pallets/gear/Cargo.toml b/pallets/gear/Cargo.toml index 8dc26661bee..ac6f96b0a64 100644 --- a/pallets/gear/Cargo.toml +++ b/pallets/gear/Cargo.toml @@ -60,6 +60,11 @@ sp-consensus-slots = { workspace = true, optional = true } test-syscalls = { workspace = true, optional = true } demo-read-big-state = { workspace = true, optional = true } demo-proxy = { workspace = true, optional = true } +demo-reserve-gas = { workspace = true, optional = true } +demo-delayed-sender = { workspace = true, optional = true } +demo-constructor = { workspace = true, optional = true } +demo-waiter = { workspace = true, optional = true } +demo-init-wait = { workspace = true, optional = true } [dev-dependencies] wabt.workspace = true @@ -154,6 +159,11 @@ std = [ "test-syscalls?/std", "demo-read-big-state?/std", "demo-proxy?/std", + "demo-reserve-gas?/std", + "demo-delayed-sender?/std", + "demo-constructor?/std", + "demo-waiter?/std", + "demo-init-wait?/std", "gear-runtime-interface/std", ] runtime-benchmarks = [ @@ -172,6 +182,11 @@ runtime-benchmarks = [ "demo-read-big-state/wasm-wrapper", "demo-proxy/wasm-wrapper", "gsys", + "demo-reserve-gas/wasm-wrapper", + "demo-delayed-sender/wasm-wrapper", + "demo-constructor/wasm-wrapper", + "demo-waiter/wasm-wrapper", + "demo-init-wait/wasm-wrapper", ] runtime-benchmarks-checkers = [] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/gear/src/benchmarking/mod.rs b/pallets/gear/src/benchmarking/mod.rs index 089b6ba2799..354379bfe16 100644 --- a/pallets/gear/src/benchmarking/mod.rs +++ b/pallets/gear/src/benchmarking/mod.rs @@ -39,6 +39,7 @@ mod code; mod sandbox; mod syscalls; +mod tasks; mod utils; use syscalls::Benches; @@ -59,7 +60,7 @@ use crate::{ schedule::{API_BENCHMARK_BATCH_SIZE, INSTR_BENCHMARK_BATCH_SIZE}, BalanceOf, BenchmarkStorage, Call, Config, CurrencyOf, Event, ExecutionEnvironment, Ext as Externalities, GasHandlerOf, GearBank, MailboxOf, Pallet as Gear, Pallet, - ProgramStorageOf, QueueOf, RentFreePeriodOf, ResumeMinimalPeriodOf, Schedule, + ProgramStorageOf, QueueOf, RentFreePeriodOf, ResumeMinimalPeriodOf, Schedule, TaskPoolOf, }; use ::alloc::{ collections::{BTreeMap, BTreeSet}, @@ -68,6 +69,7 @@ use ::alloc::{ use common::{ self, benchmarking, paused_program_storage::SessionId, + scheduler::{ScheduledTask, TaskHandler}, storage::{Counter, *}, ActiveProgram, CodeMetadata, CodeStorage, GasTree, Origin, PausedProgramStorage, ProgramStorage, ReservableTree, @@ -241,6 +243,31 @@ where .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)) } +fn get_last_session_id() -> Option { + find_latest_event::(|event| match event { + Event::ProgramResumeSessionStarted { session_id, .. } => Some(session_id), + _ => None, + }) +} + +pub fn find_latest_event(mapping_filter: F) -> Option +where + T: Config, + F: Fn(Event) -> Option, +{ + SystemPallet::::events() + .into_iter() + .rev() + .filter_map(|event_record| { + let event = <::RuntimeEvent as From<_>>::from(event_record.event); + let event: Result, _> = event.try_into(); + + event.ok() + }) + .find_map(mapping_filter) +} + +#[track_caller] fn resume_session_prepare( c: u32, program_id: ProgramId, @@ -261,14 +288,6 @@ where ) .expect("failed to start resume session"); - let event_record = SystemPallet::::events().pop().unwrap(); - let event = <::RuntimeEvent as From<_>>::from(event_record.event); - let event: Result, _> = event.try_into(); - let session_id = match event { - Ok(Event::ProgramResumeSessionStarted { session_id, .. }) => session_id, - _ => unreachable!(), - }; - let memory_pages = { let mut pages = Vec::with_capacity(c as usize); for i in 0..c { @@ -278,7 +297,7 @@ where pages }; - (session_id, memory_pages) + (get_last_session_id::().unwrap(), memory_pages) } /// An instantiated and deployed program. @@ -2741,6 +2760,89 @@ benchmarks! { sbox.invoke(); } + tasks_remove_resume_session { + let session_id = tasks::remove_resume_session::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.remove_resume_session(session_id); + } + + tasks_remove_gas_reservation { + let (program_id, reservation_id) = tasks::remove_gas_reservation::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.remove_gas_reservation(program_id, reservation_id); + } + + tasks_send_user_message_to_mailbox { + let message_id = tasks::send_user_message::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.send_user_message(message_id, true); + } + + tasks_send_user_message { + let message_id = tasks::send_user_message::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.send_user_message(message_id, false); + } + + tasks_send_dispatch { + let message_id = tasks::send_dispatch::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.send_dispatch(message_id); + } + + tasks_wake_message { + let (program_id, message_id) = tasks::wake_message::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.wake_message(program_id, message_id); + } + + tasks_wake_message_no_wake { + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.wake_message(Default::default(), Default::default()); + } + + tasks_remove_from_waitlist { + let (program_id, message_id) = tasks::remove_from_waitlist::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.remove_from_waitlist(program_id, message_id); + } + + tasks_remove_from_mailbox { + let (user, message_id) = tasks::remove_from_mailbox::(); + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.remove_from_mailbox(T::AccountId::from_origin(user.into_origin()), message_id); + } + + tasks_pause_program { + let c in 0 .. (MAX_PAGES - 1) * (WASM_PAGE_SIZE / GEAR_PAGE_SIZE) as u32; + + let code = benchmarking::generate_wasm2(0.into()).unwrap(); + let program_id = tasks::pause_program_prepare::(c, code); + + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.pause_program(program_id); + } + + tasks_pause_program_uninited { + let c in 0 .. (MAX_PAGES - 1) * (WASM_PAGE_SIZE / GEAR_PAGE_SIZE) as u32; + + let program_id = tasks::pause_program_prepare::(c, demo_init_wait::WASM_BINARY.to_vec()); + + let mut ext_manager = ExtManager::::default(); + }: { + ext_manager.pause_program(program_id); + } + // This is no benchmark. It merely exist to have an easy way to pretty print the currently // configured `Schedule` during benchmark development. // It can be outputted using the following command: diff --git a/pallets/gear/src/benchmarking/tasks.rs b/pallets/gear/src/benchmarking/tasks.rs new file mode 100644 index 00000000000..6447b509f01 --- /dev/null +++ b/pallets/gear/src/benchmarking/tasks.rs @@ -0,0 +1,371 @@ +// This file is part of Gear. + +// Copyright (C) Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use super::*; +use gear_core::ids::ReservationId; + +#[track_caller] +fn send_user_message_prepare(delay: u32) +where + T: Config, + T::AccountId: Origin, +{ + use demo_delayed_sender::WASM_BINARY; + + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + Gear::::upload_program( + RawOrigin::Signed(caller).into(), + WASM_BINARY.to_vec(), + salt, + delay.encode(), + 100_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + Gear::::process_queue(Default::default()); +} + +#[track_caller] +pub(super) fn pause_program_prepare(c: u32, code: Vec) -> ProgramId +where + T: Config, + T::AccountId: Origin, +{ + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 400_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(&code), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller).into(), + code, + salt, + b"init_payload".to_vec(), + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + Gear::::process_queue(Default::default()); + + let memory_page = { + let mut page = PageBuf::new_zeroed(); + page[0] = 1; + + page + }; + + for i in 0..c { + ProgramStorageOf::::set_program_page_data( + program_id, + GearPage::from(i as u16), + memory_page.clone(), + ); + } + + ProgramStorageOf::::update_active_program(program_id, |program| { + program.pages_with_data = BTreeSet::from_iter((0..c).map(|i| GearPage::from(i as u16))); + + let wasm_pages = (c as usize * GEAR_PAGE_SIZE) / WASM_PAGE_SIZE; + program.allocations = + BTreeSet::from_iter((0..wasm_pages).map(|i| WasmPage::from(i as u16))); + }) + .expect("program should exist"); + + program_id +} + +#[track_caller] +pub(super) fn remove_resume_session() -> SessionId +where + T: Config, + T::AccountId: Origin, +{ + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + let code = benchmarking::generate_wasm2(16.into()).unwrap(); + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(&code), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller.clone()).into(), + code, + salt, + b"init_payload".to_vec(), + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + init_block::(None); + + ProgramStorageOf::::pause_program(program_id, 100u32.into()).unwrap(); + + Gear::::resume_session_init( + RawOrigin::Signed(caller).into(), + program_id, + Default::default(), + CodeId::default(), + ) + .expect("failed to start resume session"); + + get_last_session_id::().unwrap() +} + +#[track_caller] +pub(super) fn remove_gas_reservation() -> (ProgramId, ReservationId) +where + T: Config, + T::AccountId: Origin, +{ + use demo_reserve_gas::{InitAction, WASM_BINARY}; + + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(WASM_BINARY), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller).into(), + WASM_BINARY.to_vec(), + salt, + InitAction::Normal(vec![(50_000, 100)]).encode(), + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + Gear::::process_queue(Default::default()); + + let program: ActiveProgram<_> = ProgramStorageOf::::get_program(program_id) + .expect("program should exist") + .try_into() + .expect("program should be active"); + + ( + program_id, + program + .gas_reservation_map + .first_key_value() + .map(|(k, _v)| *k) + .unwrap(), + ) +} + +#[track_caller] +pub(super) fn send_user_message() -> MessageId +where + T: Config, + T::AccountId: Origin, +{ + let delay = 1u32; + send_user_message_prepare::(delay); + + let task = TaskPoolOf::::iter_prefix_keys(Gear::::block_number() + delay.into()) + .next() + .expect("task should be scheduled"); + let (message_id, to_mailbox) = match task { + ScheduledTask::SendUserMessage { + message_id, + to_mailbox, + } => (message_id, to_mailbox), + _ => unreachable!("task should be SendUserMessage"), + }; + assert!(to_mailbox); + + message_id +} + +#[track_caller] +pub(super) fn send_dispatch() -> MessageId +where + T: Config, + T::AccountId: Origin, +{ + use demo_constructor::{Call, Calls, Scheme, WASM_BINARY}; + + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(WASM_BINARY), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller.clone()).into(), + WASM_BINARY.to_vec(), + salt, + Scheme::empty().encode(), + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + let delay = 1u32; + let calls = Calls::builder().add_call(Call::Send( + <[u8; 32]>::from(program_id.into_origin()).into(), + [].into(), + Some(0u64.into()), + 0u128.into(), + delay.into(), + )); + Gear::::send_message( + RawOrigin::Signed(caller).into(), + program_id, + calls.encode(), + 10_000_000_000, + 0u32.into(), + false, + ) + .expect("failed to send message"); + + Gear::::process_queue(Default::default()); + + let task = TaskPoolOf::::iter_prefix_keys(Gear::::block_number() + delay.into()) + .next() + .unwrap(); + + match task { + ScheduledTask::SendDispatch(message_id) => message_id, + _ => unreachable!(), + } +} + +#[track_caller] +pub(super) fn wake_message() -> (ProgramId, MessageId) +where + T: Config, + T::AccountId: Origin, +{ + use demo_waiter::{Command, WaitSubcommand, WASM_BINARY}; + + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(WASM_BINARY), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller.clone()).into(), + WASM_BINARY.to_vec(), + salt, + vec![], + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + let delay = 10u32; + Gear::::send_message( + RawOrigin::Signed(caller).into(), + program_id, + Command::Wait(WaitSubcommand::WaitFor(delay)).encode(), + 10_000_000_000, + 0u32.into(), + false, + ) + .expect("failed to send message"); + + Gear::::process_queue(Default::default()); + + let task = TaskPoolOf::::iter_prefix_keys(Gear::::block_number() + delay.into()) + .next() + .unwrap(); + let (_program_id, message_id) = match task { + ScheduledTask::WakeMessage(program_id, message_id) => (program_id, message_id), + _ => unreachable!(), + }; + + (program_id, message_id) +} + +#[track_caller] +pub(super) fn remove_from_waitlist() -> (ProgramId, MessageId) +where + T: Config, + T::AccountId: Origin, +{ + use demo_waiter::{Command, WaitSubcommand, WASM_BINARY}; + + let caller = benchmarking::account("caller", 0, 0); + CurrencyOf::::deposit_creating(&caller, 200_000_000_000_000u128.unique_saturated_into()); + + init_block::(None); + + let salt = vec![]; + let program_id = ProgramId::generate(CodeId::generate(WASM_BINARY), &salt); + Gear::::upload_program( + RawOrigin::Signed(caller.clone()).into(), + WASM_BINARY.to_vec(), + salt, + vec![], + 10_000_000_000, + 0u32.into(), + ) + .expect("submit program failed"); + + Gear::::send_message( + RawOrigin::Signed(caller).into(), + program_id, + Command::Wait(WaitSubcommand::Wait).encode(), + 10_000_000_000, + 0u32.into(), + false, + ) + .expect("failed to send message"); + + Gear::::process_queue(Default::default()); + + let expiration = find_latest_event::(|event| match event { + Event::MessageWaited { expiration, .. } => Some(expiration), + _ => None, + }) + .expect("message should be waited"); + + let task = TaskPoolOf::::iter_prefix_keys(expiration) + .next() + .unwrap(); + let (_program_id, message_id) = match task { + ScheduledTask::RemoveFromWaitlist(program_id, message_id) => (program_id, message_id), + _ => unreachable!(), + }; + + (program_id, message_id) +} + +#[track_caller] +pub(super) fn remove_from_mailbox() -> (ProgramId, MessageId) +where + T: Config, + T::AccountId: Origin, +{ + send_user_message_prepare::(0u32); + + find_latest_event::(|event| match event { + Event::UserMessageSent { message, .. } => Some((message.destination(), message.id())), + _ => None, + }) + .expect("message should be sent") +} diff --git a/pallets/gear/src/lib.rs b/pallets/gear/src/lib.rs index e236c02de33..0db25c86d60 100644 --- a/pallets/gear/src/lib.rs +++ b/pallets/gear/src/lib.rs @@ -934,7 +934,6 @@ pub mod pallet { ..=current_bn.saturated_into()) .map(|block| block.saturated_into::>()); for bn in missing_blocks { - // Tasks drain iterator. let tasks = TaskPoolOf::::drain_prefix_keys(bn); // Checking gas allowance. @@ -948,30 +947,63 @@ pub mod pallet { } // Iterating over tasks, scheduled on `bn`. + let mut last_task = None; for task in tasks { - log::debug!("Processing task: {:?}", task); - // Decreasing gas allowance due to DB deletion. GasAllowanceOf::::decrease(DbWeightOf::::get().writes(1).ref_time()); - // Processing task. - // - // NOTE: Gas allowance decrease should be implemented - // inside `TaskHandler` trait and/or inside other - // generic types, which interact with storage. - task.process_with(ext_manager); + // gas required to process task. + let max_task_gas = manager::get_maximum_task_gas::(&task); + log::debug!("Processing task: {task:?}, max gas = {max_task_gas}"); // Checking gas allowance. // - // Making sure we have gas to remove next task - // or update the first block of incomplete tasks. - if GasAllowanceOf::::get() <= DbWeightOf::::get().writes(2).ref_time() { + // Making sure we have gas to process the current task + // and update the first block of incomplete tasks. + if GasAllowanceOf::::get().saturating_sub(max_task_gas) + <= DbWeightOf::::get().writes(1).ref_time() + { + // Since the task is not processed write DB cost should be refunded. + // In the same time gas allowance should be charged for read DB cost. + GasAllowanceOf::::put( + GasAllowanceOf::::get() + .saturating_add(DbWeightOf::::get().writes(1).ref_time()) + .saturating_sub(DbWeightOf::::get().reads(1).ref_time()), + ); + + last_task = Some(task); + log::debug!("Not enough gas to process task at: {bn:?}"); + + break; + } + + // Processing task and update allowance of gas. + let task_gas = task.process_with(ext_manager); + GasAllowanceOf::::decrease(task_gas); + + // Check that there is enough gas allowance to query next task and update the first block of incomplete tasks. + if GasAllowanceOf::::get() + <= DbWeightOf::::get().reads_writes(1, 1).ref_time() + { stopped_at = Some(bn); - log::debug!("Stopping processing tasks at: {stopped_at:?}"); + log::debug!("Stopping processing tasks at (read next): {stopped_at:?}"); break; } } + if let Some(task) = last_task { + stopped_at = Some(bn); + + // since there is the overlay mechanism we don't need to subtract write cost + // from gas allowance on task insertion. + GasAllowanceOf::::put( + GasAllowanceOf::::get() + .saturating_add(DbWeightOf::::get().writes(1).ref_time()), + ); + TaskPoolOf::::add(bn, task) + .unwrap_or_else(|e| unreachable!("Scheduling logic invalidated! {:?}", e)); + } + // Stopping iteration over blocks if no resources left. if stopped_at.is_some() { break; diff --git a/pallets/gear/src/manager/task.rs b/pallets/gear/src/manager/task.rs index 41a399265f2..54f6eb85f2b 100644 --- a/pallets/gear/src/manager/task.rs +++ b/pallets/gear/src/manager/task.rs @@ -17,8 +17,8 @@ // along with this program. If not, see . use crate::{ - manager::ExtManager, Config, DispatchStashOf, Event, Pallet, ProgramStorageOf, QueueOf, - TaskPoolOf, WaitlistOf, + manager::ExtManager, weights::WeightInfo, Config, DbWeightOf, DispatchStashOf, Event, Pallet, + ProgramStorageOf, QueueOf, TaskPoolOf, WaitlistOf, }; use alloc::string::ToString; use common::{ @@ -29,21 +29,71 @@ use common::{ paused_program_storage::SessionId, scheduler::*, storage::*, - Origin, PausedProgramStorage, Program, ProgramStorage, + ActiveProgram, Gas, Origin, PausedProgramStorage, Program, ProgramState, ProgramStorage, }; +use core::cmp; use gear_core::{ + code::MAX_WASM_PAGE_COUNT, ids::{CodeId, MessageId, ProgramId, ReservationId}, message::{DispatchKind, ReplyMessage}, + pages::{GEAR_PAGE_SIZE, WASM_PAGE_SIZE}, }; use gear_core_errors::{ErrorReplyReason, SignalCode}; use sp_core::Get; use sp_runtime::Saturating; +pub fn get_maximum_task_gas(task: &ScheduledTask) -> Gas { + use ScheduledTask::*; + + match task { + PauseProgram(_) => { + // TODO: #3079 + if ::ProgramRentEnabled::get() { + let count = + u32::from(MAX_WASM_PAGE_COUNT * (WASM_PAGE_SIZE / GEAR_PAGE_SIZE) as u16 / 2); + cmp::max( + ::WeightInfo::tasks_pause_program(count).ref_time(), + ::WeightInfo::tasks_pause_program_uninited(count).ref_time(), + ) + } else { + DbWeightOf::::get().writes(2).ref_time() + } + } + RemoveCode(_) => todo!("#646"), + RemoveFromMailbox(_, _) => { + ::WeightInfo::tasks_remove_from_mailbox().ref_time() + } + RemoveFromWaitlist(_, _) => { + ::WeightInfo::tasks_remove_from_waitlist().ref_time() + } + RemovePausedProgram(_) => todo!("#646"), + WakeMessage(_, _) => cmp::max( + ::WeightInfo::tasks_wake_message().ref_time(), + ::WeightInfo::tasks_wake_message_no_wake().ref_time(), + ), + SendDispatch(_) => ::WeightInfo::tasks_send_dispatch().ref_time(), + SendUserMessage { .. } => cmp::max( + ::WeightInfo::tasks_send_user_message_to_mailbox().ref_time(), + ::WeightInfo::tasks_send_user_message().ref_time(), + ), + RemoveGasReservation(_, _) => { + ::WeightInfo::tasks_remove_gas_reservation().ref_time() + } + RemoveResumeSession(_) => { + ::WeightInfo::tasks_remove_resume_session().ref_time() + } + } +} + impl TaskHandler for ExtManager where T::AccountId: Origin, { - fn pause_program(&mut self, program_id: ProgramId) { + fn pause_program(&mut self, program_id: ProgramId) -> Gas { + // + // TODO: #3079 + // + if !::ProgramRentEnabled::get() { log::debug!("Program rent logic is disabled."); @@ -63,13 +113,17 @@ where TaskPoolOf::::add(expiration_block, task) .unwrap_or_else(|e| unreachable!("Scheduling logic invalidated! {:?}", e)); - return; + return DbWeightOf::::get().writes(1).ref_time(); } - let program = ProgramStorageOf::::get_program(program_id) - .unwrap_or_else(|| unreachable!("Program to pause not found.")); + let program: ActiveProgram<_> = ProgramStorageOf::::get_program(program_id) + .unwrap_or_else(|| unreachable!("Program to pause not found.")) + .try_into() + .unwrap_or_else(|e| unreachable!("Pause program task logic corrupted: {e:?}")); - let Some(init_message_id) = program.is_uninitialized() else { + let pages_with_data = program.pages_with_data.len() as u32; + + let ProgramState::Uninitialized{ message_id: init_message_id } = program.state else { // pause initialized program let gas_reservation_map = ProgramStorageOf::::pause_program(program_id, Pallet::::block_number()) @@ -90,7 +144,10 @@ where change: ProgramChangeKind::Paused, }); - return; + let gas = ::WeightInfo::tasks_pause_program(pages_with_data).ref_time(); + log::trace!("Task gas: tasks_pause_program = {gas}"); + + return gas; }; // terminate uninitialized program @@ -141,13 +198,19 @@ where id: program_id, change: ProgramChangeKind::Terminated, }); + + let gas = + ::WeightInfo::tasks_pause_program_uninited(pages_with_data).ref_time(); + log::trace!("Task gas: tasks_pause_program_uninited = {gas}"); + + gas } - fn remove_code(&mut self, _code_id: CodeId) { - todo!("#646"); + fn remove_code(&mut self, _code_id: CodeId) -> Gas { + todo!("#646") } - fn remove_from_mailbox(&mut self, user_id: T::AccountId, message_id: MessageId) { + fn remove_from_mailbox(&mut self, user_id: T::AccountId, message_id: MessageId) -> Gas { // Read reason. let reason = UserMessageReadSystemReason::OutOfRent.into_reason(); @@ -169,9 +232,14 @@ where // Queueing dispatch. QueueOf::::queue(dispatch) .unwrap_or_else(|e| unreachable!("Message queue corrupted! {:?}", e)); + + let gas = ::WeightInfo::tasks_remove_from_mailbox().ref_time(); + log::trace!("Task gas: tasks_remove_from_mailbox = {gas}"); + + gas } - fn remove_from_waitlist(&mut self, program_id: ProgramId, message_id: MessageId) { + fn remove_from_waitlist(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas { // Wake reason. let reason = MessageWokenSystemReason::OutOfRent.into_reason(); @@ -240,24 +308,42 @@ where let origin = waitlisted.source(); Self::process_failed_init(program_id, origin, true); } + + let gas = ::WeightInfo::tasks_remove_from_waitlist().ref_time(); + log::trace!("Task gas: tasks_remove_from_waitlist = {gas}"); + + gas } - fn remove_paused_program(&mut self, _program_id: ProgramId) { - todo!("#646"); + fn remove_paused_program(&mut self, _program_id: ProgramId) -> Gas { + todo!("#646") } - fn wake_message(&mut self, program_id: ProgramId, message_id: MessageId) { - if let Some(dispatch) = Pallet::::wake_dispatch( + fn wake_message(&mut self, program_id: ProgramId, message_id: MessageId) -> Gas { + match Pallet::::wake_dispatch( program_id, message_id, MessageWokenRuntimeReason::WakeCalled.into_reason(), ) { - QueueOf::::queue(dispatch) - .unwrap_or_else(|e| unreachable!("Message queue corrupted! {:?}", e)); + Some(dispatch) => { + QueueOf::::queue(dispatch) + .unwrap_or_else(|e| unreachable!("Message queue corrupted! {:?}", e)); + + let gas = ::WeightInfo::tasks_wake_message().ref_time(); + log::trace!("Task gas: tasks_wake_message = {gas}"); + + gas + } + None => { + let gas = ::WeightInfo::tasks_wake_message_no_wake().ref_time(); + log::trace!("Task gas: tasks_wake_message_no_wake = {gas}"); + + gas + } } } - fn send_dispatch(&mut self, stashed_message_id: MessageId) { + fn send_dispatch(&mut self, stashed_message_id: MessageId) -> Gas { // No validation required. If program doesn't exist, then NotExecuted appears. let (dispatch, hold_interval) = DispatchStashOf::::take(stashed_message_id) @@ -268,9 +354,14 @@ where QueueOf::::queue(dispatch) .unwrap_or_else(|e| unreachable!("Message queue corrupted! {:?}", e)); + + let gas = ::WeightInfo::tasks_send_dispatch().ref_time(); + log::trace!("Task gas: tasks_send_dispatch = {gas}"); + + gas } - fn send_user_message(&mut self, stashed_message_id: MessageId, to_mailbox: bool) { + fn send_user_message(&mut self, stashed_message_id: MessageId, to_mailbox: bool) -> Gas { // TODO: validate here destination and send error reply, if required. // Atm despite the fact that program may exist, message goes into mailbox / event. let (message, hold_interval) = DispatchStashOf::::take(stashed_message_id) @@ -285,15 +376,40 @@ where .try_into() .unwrap_or_else(|_| unreachable!("Signal message sent to user")); Pallet::::send_user_message_after_delay(message, to_mailbox); + + if to_mailbox { + let gas = ::WeightInfo::tasks_send_user_message_to_mailbox().ref_time(); + log::trace!("Task gas: tasks_send_user_message_to_mailbox = {gas}"); + + gas + } else { + let gas = ::WeightInfo::tasks_send_user_message().ref_time(); + log::trace!("Task gas: tasks_send_user_message = {gas}"); + + gas + } } - fn remove_gas_reservation(&mut self, program_id: ProgramId, reservation_id: ReservationId) { + fn remove_gas_reservation( + &mut self, + program_id: ProgramId, + reservation_id: ReservationId, + ) -> Gas { let _slot = Self::remove_gas_reservation_impl(program_id, reservation_id); + + let gas = ::WeightInfo::tasks_remove_gas_reservation().ref_time(); + log::trace!("Task gas: tasks_remove_gas_reservation = {gas}"); + + gas } - fn remove_resume_session(&mut self, session_id: SessionId) { - log::debug!("Execute task to remove resume session with session_id = {session_id}"); + fn remove_resume_session(&mut self, session_id: SessionId) -> Gas { ProgramStorageOf::::remove_resume_session(session_id) .unwrap_or_else(|e| unreachable!("ProgramStorage corrupted! {:?}", e)); + + let gas = ::WeightInfo::tasks_remove_resume_session().ref_time(); + log::trace!("Task gas: tasks_remove_resume_session = {gas}"); + + gas } } diff --git a/pallets/gear/src/mock.rs b/pallets/gear/src/mock.rs index e4f29b149e2..6ddddaa70ca 100644 --- a/pallets/gear/src/mock.rs +++ b/pallets/gear/src/mock.rs @@ -302,7 +302,7 @@ pub fn new_test_ext() -> sp_io::TestExternalities { pallet_balances::GenesisConfig:: { balances: vec![ (USER_1, 5_000_000_000_000_000_u128), - (USER_2, 200_000_000_000_000_u128), + (USER_2, 350_000_000_000_000_u128), (USER_3, 500_000_000_000_000_u128), (LOW_BALANCE_USER, 1_000_000_u128), (BLOCK_AUTHOR, 500_000_u128), diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index ae861d0d365..019d2ac293b 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -5118,7 +5118,7 @@ fn test_requeue_after_wait_for_timeout() { run_to_next_block(None); let duration = 10; - let payload = Command::SendAndWaitFor(duration, USER_1.into()).encode(); + let payload = Command::SendAndWaitFor(duration, USER_1.into_origin().into()).encode(); assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), program_id, @@ -5189,7 +5189,7 @@ fn test_sending_waits() { // // Send message and then wait_for. let duration = 5; - let payload = Command::SendFor(USER_1.into(), duration).encode(); + let payload = Command::SendFor(USER_1.into_origin().into(), duration).encode(); assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), @@ -5209,7 +5209,7 @@ fn test_sending_waits() { // // Send message and then wait_up_to. let duration = 10; - let payload = Command::SendUpTo(USER_1.into(), duration).encode(); + let payload = Command::SendUpTo(USER_1.into_origin().into(), duration).encode(); assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), program_id, @@ -5228,7 +5228,7 @@ fn test_sending_waits() { // // Send message and then wait no_more, wake, wait no_more again. let duration = 10; - let payload = Command::SendUpToWait(USER_2.into(), duration).encode(); + let payload = Command::SendUpToWait(USER_2.into_origin().into(), duration).encode(); assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), program_id, @@ -8809,6 +8809,8 @@ fn free_storage_hold_on_scheduler_overwhelm() { #[test] fn execution_over_blocks() { + const MAX_BLOCK: u64 = 10_000_000_000; + init_logger(); let assert_last_message = |src: [u8; 32], count: u128| { @@ -8849,7 +8851,7 @@ fn execution_over_blocks() { )); let in_one_block = get_last_program_id(); - run_to_next_block(None); + run_to_next_block(Some(MAX_BLOCK)); // estimate start cost let pkg = Package::new(times, src); @@ -8870,7 +8872,7 @@ fn execution_over_blocks() { use demo_calc_hash_in_one_block::{Package, WASM_BINARY}; // We suppose that gas limit is less than gas allowance - let block_gas_limit = BlockGasLimitOf::::get() - 10000; + let block_gas_limit = MAX_BLOCK - 10_000; // Deploy demo-calc-hash-in-one-block. assert_ok!(Gear::upload_program( @@ -8887,30 +8889,31 @@ fn execution_over_blocks() { let src = [0; 32]; + let expected = 64; assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), in_one_block, - Package::new(128, src).encode(), + Package::new(expected, src).encode(), block_gas_limit, 0, false, )); - run_to_next_block(None); + run_to_next_block(Some(MAX_BLOCK)); - assert_last_message([0; 32], 128); + assert_last_message([0; 32], expected); assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), in_one_block, - Package::new(17_384, src).encode(), + Package::new(1_024, src).encode(), block_gas_limit, 0, false, )); let message_id = get_last_message_id(); - run_to_next_block(None); + run_to_next_block(Some(MAX_BLOCK)); assert_failed( message_id, @@ -8921,7 +8924,7 @@ fn execution_over_blocks() { new_test_ext().execute_with(|| { use demo_calc_hash::sha2_512_256; use demo_calc_hash_over_blocks::{Method, WASM_BINARY}; - let block_gas_limit = BlockGasLimitOf::::get(); + let block_gas_limit = MAX_BLOCK; let (_, calc_threshold) = estimate_gas_per_calc(); @@ -8931,26 +8934,26 @@ fn execution_over_blocks() { WASM_BINARY.to_vec(), DEFAULT_SALT.to_vec(), calc_threshold.encode(), - 10_000_000_000, + 9_000_000_000, 0, )); let over_blocks = get_last_program_id(); assert!(ProgramStorageOf::::program_exists(over_blocks)); - let (src, id, expected) = ([0; 32], sha2_512_256(b"42"), 16_384); + let (src, id, expected) = ([0; 32], sha2_512_256(b"42"), 512); // trigger calculation assert_ok!(Gear::send_message( RuntimeOrigin::signed(USER_1), over_blocks, Method::Start { src, id, expected }.encode(), - 10_000_000_000, + 9_000_000_000, 0, false, )); - run_to_next_block(None); + run_to_next_block(Some(MAX_BLOCK)); let mut count = 0; loop { @@ -8970,7 +8973,7 @@ fn execution_over_blocks() { )); count += 1; - run_to_next_block(None); + run_to_next_block(Some(MAX_BLOCK)); } assert!(count > 1); diff --git a/pallets/gear/src/weights.rs b/pallets/gear/src/weights.rs index c52d1f58fac..3ad8efedec0 100644 --- a/pallets/gear/src/weights.rs +++ b/pallets/gear/src/weights.rs @@ -215,6 +215,17 @@ pub trait WeightInfo { fn instr_i32rotl(r: u32, ) -> Weight; fn instr_i64rotr(r: u32, ) -> Weight; fn instr_i32rotr(r: u32, ) -> Weight; + fn tasks_remove_resume_session() -> Weight; + fn tasks_remove_gas_reservation() -> Weight; + fn tasks_send_user_message_to_mailbox() -> Weight; + fn tasks_send_user_message() -> Weight; + fn tasks_send_dispatch() -> Weight; + fn tasks_wake_message() -> Weight; + fn tasks_wake_message_no_wake() -> Weight; + fn tasks_remove_from_waitlist() -> Weight; + fn tasks_remove_from_mailbox() -> Weight; + fn tasks_pause_program(c: u32, ) -> Weight; + fn tasks_pause_program_uninited(c: u32, ) -> Weight; fn allocation_cost() -> Weight; fn grow_cost() -> Weight; fn initial_cost() -> Weight; @@ -2062,6 +2073,116 @@ impl WeightInfo for SubstrateWeight { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 5_698_000 picoseconds. + Weight::from_parts(5_953_000, 4169) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1003` + // Estimated: `23637` + // Minimum execution time: 61_615_000 picoseconds. + Weight::from_parts(64_309_000, 23637) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `784` + // Estimated: `21534` + // Minimum execution time: 43_449_000 picoseconds. + Weight::from_parts(44_990_000, 21534) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `33891` + // Minimum execution time: 75_764_000 picoseconds. + Weight::from_parts(77_875_000, 33891) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `591` + // Estimated: `19885` + // Minimum execution time: 31_362_000 picoseconds. + Weight::from_parts(32_425_000, 19885) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `25908` + // Minimum execution time: 45_023_000 picoseconds. + Weight::from_parts(46_479_000, 25908) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_365_000 picoseconds. + Weight::from_parts(3_639_000, 3545) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1522` + // Estimated: `57192` + // Minimum execution time: 114_023_000 picoseconds. + Weight::from_parts(117_157_000, 57192) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1228` + // Estimated: `46026` + // Minimum execution time: 90_320_000 picoseconds. + Weight::from_parts(93_096_000, 46026) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2200 + c * (16400 ±0)` + // Estimated: `19363 + c * (84480 ±0)` + // Minimum execution time: 28_326_000 picoseconds. + Weight::from_parts(28_612_000, 19363) + // Standard Error: 73_191 + .saturating_add(Weight::from_parts(39_403_570, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3025 + c * (42 ±0)` + // Estimated: `59431 + c * (2947 ±0)` + // Minimum execution time: 86_517_000 picoseconds. + Weight::from_parts(78_047_954, 59431) + // Standard Error: 2_660 + .saturating_add(Weight::from_parts(1_060_607, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(9_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } // For backwards compatibility and tests @@ -3903,4 +4024,114 @@ impl WeightInfo for () { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 5_698_000 picoseconds. + Weight::from_parts(5_953_000, 4169) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1003` + // Estimated: `23637` + // Minimum execution time: 61_615_000 picoseconds. + Weight::from_parts(64_309_000, 23637) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `784` + // Estimated: `21534` + // Minimum execution time: 43_449_000 picoseconds. + Weight::from_parts(44_990_000, 21534) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `33891` + // Minimum execution time: 75_764_000 picoseconds. + Weight::from_parts(77_875_000, 33891) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `591` + // Estimated: `19885` + // Minimum execution time: 31_362_000 picoseconds. + Weight::from_parts(32_425_000, 19885) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `25908` + // Minimum execution time: 45_023_000 picoseconds. + Weight::from_parts(46_479_000, 25908) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_365_000 picoseconds. + Weight::from_parts(3_639_000, 3545) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1522` + // Estimated: `57192` + // Minimum execution time: 114_023_000 picoseconds. + Weight::from_parts(117_157_000, 57192) + .saturating_add(RocksDbWeight::get().reads(16_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1228` + // Estimated: `46026` + // Minimum execution time: 90_320_000 picoseconds. + Weight::from_parts(93_096_000, 46026) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2200 + c * (16400 ±0)` + // Estimated: `19363 + c * (84480 ±0)` + // Minimum execution time: 28_326_000 picoseconds. + Weight::from_parts(28_612_000, 19363) + // Standard Error: 73_191 + .saturating_add(Weight::from_parts(39_403_570, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3025 + c * (42 ±0)` + // Estimated: `59431 + c * (2947 ±0)` + // Minimum execution time: 86_517_000 picoseconds. + Weight::from_parts(78_047_954, 59431) + // Standard Error: 2_660 + .saturating_add(Weight::from_parts(1_060_607, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } diff --git a/runtime/gear/src/weights/pallet_gear.rs b/runtime/gear/src/weights/pallet_gear.rs index 707b287d5c5..8b14d71ab58 100644 --- a/runtime/gear/src/weights/pallet_gear.rs +++ b/runtime/gear/src/weights/pallet_gear.rs @@ -215,6 +215,17 @@ pub trait WeightInfo { fn instr_i32rotl(r: u32, ) -> Weight; fn instr_i64rotr(r: u32, ) -> Weight; fn instr_i32rotr(r: u32, ) -> Weight; + fn tasks_remove_resume_session() -> Weight; + fn tasks_remove_gas_reservation() -> Weight; + fn tasks_send_user_message_to_mailbox() -> Weight; + fn tasks_send_user_message() -> Weight; + fn tasks_send_dispatch() -> Weight; + fn tasks_wake_message() -> Weight; + fn tasks_wake_message_no_wake() -> Weight; + fn tasks_remove_from_waitlist() -> Weight; + fn tasks_remove_from_mailbox() -> Weight; + fn tasks_pause_program(c: u32, ) -> Weight; + fn tasks_pause_program_uninited(c: u32, ) -> Weight; fn allocation_cost() -> Weight; fn grow_cost() -> Weight; fn initial_cost() -> Weight; @@ -2062,6 +2073,116 @@ impl pallet_gear::WeightInfo for SubstrateWeight { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 5_698_000 picoseconds. + Weight::from_parts(5_953_000, 4169) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1003` + // Estimated: `23637` + // Minimum execution time: 61_615_000 picoseconds. + Weight::from_parts(64_309_000, 23637) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `784` + // Estimated: `21534` + // Minimum execution time: 43_449_000 picoseconds. + Weight::from_parts(44_990_000, 21534) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `33891` + // Minimum execution time: 75_764_000 picoseconds. + Weight::from_parts(77_875_000, 33891) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `591` + // Estimated: `19885` + // Minimum execution time: 31_362_000 picoseconds. + Weight::from_parts(32_425_000, 19885) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `25908` + // Minimum execution time: 45_023_000 picoseconds. + Weight::from_parts(46_479_000, 25908) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_365_000 picoseconds. + Weight::from_parts(3_639_000, 3545) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1522` + // Estimated: `57192` + // Minimum execution time: 114_023_000 picoseconds. + Weight::from_parts(117_157_000, 57192) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1228` + // Estimated: `46026` + // Minimum execution time: 90_320_000 picoseconds. + Weight::from_parts(93_096_000, 46026) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2200 + c * (16400 ±0)` + // Estimated: `19363 + c * (84480 ±0)` + // Minimum execution time: 28_326_000 picoseconds. + Weight::from_parts(28_612_000, 19363) + // Standard Error: 73_191 + .saturating_add(Weight::from_parts(39_403_570, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3025 + c * (42 ±0)` + // Estimated: `59431 + c * (2947 ±0)` + // Minimum execution time: 86_517_000 picoseconds. + Weight::from_parts(78_047_954, 59431) + // Standard Error: 2_660 + .saturating_add(Weight::from_parts(1_060_607, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(9_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } // For backwards compatibility and tests @@ -3903,4 +4024,114 @@ impl WeightInfo for () { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 5_698_000 picoseconds. + Weight::from_parts(5_953_000, 4169) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1003` + // Estimated: `23637` + // Minimum execution time: 61_615_000 picoseconds. + Weight::from_parts(64_309_000, 23637) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `784` + // Estimated: `21534` + // Minimum execution time: 43_449_000 picoseconds. + Weight::from_parts(44_990_000, 21534) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `906` + // Estimated: `33891` + // Minimum execution time: 75_764_000 picoseconds. + Weight::from_parts(77_875_000, 33891) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `591` + // Estimated: `19885` + // Minimum execution time: 31_362_000 picoseconds. + Weight::from_parts(32_425_000, 19885) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `872` + // Estimated: `25908` + // Minimum execution time: 45_023_000 picoseconds. + Weight::from_parts(46_479_000, 25908) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_365_000 picoseconds. + Weight::from_parts(3_639_000, 3545) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1522` + // Estimated: `57192` + // Minimum execution time: 114_023_000 picoseconds. + Weight::from_parts(117_157_000, 57192) + .saturating_add(RocksDbWeight::get().reads(16_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1228` + // Estimated: `46026` + // Minimum execution time: 90_320_000 picoseconds. + Weight::from_parts(93_096_000, 46026) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2200 + c * (16400 ±0)` + // Estimated: `19363 + c * (84480 ±0)` + // Minimum execution time: 28_326_000 picoseconds. + Weight::from_parts(28_612_000, 19363) + // Standard Error: 73_191 + .saturating_add(Weight::from_parts(39_403_570, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3025 + c * (42 ±0)` + // Estimated: `59431 + c * (2947 ±0)` + // Minimum execution time: 86_517_000 picoseconds. + Weight::from_parts(78_047_954, 59431) + // Standard Error: 2_660 + .saturating_add(Weight::from_parts(1_060_607, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } diff --git a/runtime/vara/src/weights/pallet_gear.rs b/runtime/vara/src/weights/pallet_gear.rs index 707b287d5c5..c5702325ce6 100644 --- a/runtime/vara/src/weights/pallet_gear.rs +++ b/runtime/vara/src/weights/pallet_gear.rs @@ -215,6 +215,17 @@ pub trait WeightInfo { fn instr_i32rotl(r: u32, ) -> Weight; fn instr_i64rotr(r: u32, ) -> Weight; fn instr_i32rotr(r: u32, ) -> Weight; + fn tasks_remove_resume_session() -> Weight; + fn tasks_remove_gas_reservation() -> Weight; + fn tasks_send_user_message_to_mailbox() -> Weight; + fn tasks_send_user_message() -> Weight; + fn tasks_send_dispatch() -> Weight; + fn tasks_wake_message() -> Weight; + fn tasks_wake_message_no_wake() -> Weight; + fn tasks_remove_from_waitlist() -> Weight; + fn tasks_remove_from_mailbox() -> Weight; + fn tasks_pause_program(c: u32, ) -> Weight; + fn tasks_pause_program_uninited(c: u32, ) -> Weight; fn allocation_cost() -> Weight; fn grow_cost() -> Weight; fn initial_cost() -> Weight; @@ -2062,6 +2073,116 @@ impl pallet_gear::WeightInfo for SubstrateWeight { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 6_081_000 picoseconds. + Weight::from_parts(6_314_000, 4169) + .saturating_add(T::DbWeight::get().reads(1_u64)) + .saturating_add(T::DbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1107` + // Estimated: `24053` + // Minimum execution time: 61_245_000 picoseconds. + Weight::from_parts(64_310_000, 24053) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `22158` + // Minimum execution time: 47_184_000 picoseconds. + Weight::from_parts(48_335_000, 22158) + .saturating_add(T::DbWeight::get().reads(6_u64)) + .saturating_add(T::DbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `1010` + // Estimated: `34619` + // Minimum execution time: 75_767_000 picoseconds. + Weight::from_parts(77_229_000, 34619) + .saturating_add(T::DbWeight::get().reads(11_u64)) + .saturating_add(T::DbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `695` + // Estimated: `20509` + // Minimum execution time: 31_513_000 picoseconds. + Weight::from_parts(33_232_000, 20509) + .saturating_add(T::DbWeight::get().reads(7_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `976` + // Estimated: `26636` + // Minimum execution time: 48_739_000 picoseconds. + Weight::from_parts(49_963_000, 26636) + .saturating_add(T::DbWeight::get().reads(8_u64)) + .saturating_add(T::DbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_513_000 picoseconds. + Weight::from_parts(3_670_000, 3545) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1626` + // Estimated: `58232` + // Minimum execution time: 109_582_000 picoseconds. + Weight::from_parts(112_343_000, 58232) + .saturating_add(T::DbWeight::get().reads(16_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1332` + // Estimated: `46962` + // Minimum execution time: 90_789_000 picoseconds. + Weight::from_parts(93_329_000, 46962) + .saturating_add(T::DbWeight::get().reads(14_u64)) + .saturating_add(T::DbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2303 + c * (16400 ±0)` + // Estimated: `19878 + c * (84480 ±0)` + // Minimum execution time: 31_262_000 picoseconds. + Weight::from_parts(31_610_000, 19878) + // Standard Error: 69_131 + .saturating_add(Weight::from_parts(39_928_419, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(4_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3129 + c * (42 ±0)` + // Estimated: `60575 + c * (2947 ±0)` + // Minimum execution time: 91_223_000 picoseconds. + Weight::from_parts(98_002_861, 60575) + // Standard Error: 2_086 + .saturating_add(Weight::from_parts(1_092_801, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(13_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(9_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } // For backwards compatibility and tests @@ -3903,4 +4024,114 @@ impl WeightInfo for () { // Standard Error: 5_851 .saturating_add(Weight::from_parts(639_333, 0).saturating_mul(r.into())) } + fn tasks_remove_resume_session() -> Weight { + // Proof Size summary in bytes: + // Measured: `352` + // Estimated: `4169` + // Minimum execution time: 6_081_000 picoseconds. + Weight::from_parts(6_314_000, 4169) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + } + fn tasks_remove_gas_reservation() -> Weight { + // Proof Size summary in bytes: + // Measured: `1107` + // Estimated: `24053` + // Minimum execution time: 61_245_000 picoseconds. + Weight::from_parts(64_310_000, 24053) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_send_user_message_to_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `888` + // Estimated: `22158` + // Minimum execution time: 47_184_000 picoseconds. + Weight::from_parts(48_335_000, 22158) + .saturating_add(RocksDbWeight::get().reads(6_u64)) + .saturating_add(RocksDbWeight::get().writes(5_u64)) + } + fn tasks_send_user_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `1010` + // Estimated: `34619` + // Minimum execution time: 75_767_000 picoseconds. + Weight::from_parts(77_229_000, 34619) + .saturating_add(RocksDbWeight::get().reads(11_u64)) + .saturating_add(RocksDbWeight::get().writes(10_u64)) + } + fn tasks_send_dispatch() -> Weight { + // Proof Size summary in bytes: + // Measured: `695` + // Estimated: `20509` + // Minimum execution time: 31_513_000 picoseconds. + Weight::from_parts(33_232_000, 20509) + .saturating_add(RocksDbWeight::get().reads(7_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message() -> Weight { + // Proof Size summary in bytes: + // Measured: `976` + // Estimated: `26636` + // Minimum execution time: 48_739_000 picoseconds. + Weight::from_parts(49_963_000, 26636) + .saturating_add(RocksDbWeight::get().reads(8_u64)) + .saturating_add(RocksDbWeight::get().writes(6_u64)) + } + fn tasks_wake_message_no_wake() -> Weight { + // Proof Size summary in bytes: + // Measured: `80` + // Estimated: `3545` + // Minimum execution time: 3_513_000 picoseconds. + Weight::from_parts(3_670_000, 3545) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + fn tasks_remove_from_waitlist() -> Weight { + // Proof Size summary in bytes: + // Measured: `1626` + // Estimated: `58232` + // Minimum execution time: 109_582_000 picoseconds. + Weight::from_parts(112_343_000, 58232) + .saturating_add(RocksDbWeight::get().reads(16_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + fn tasks_remove_from_mailbox() -> Weight { + // Proof Size summary in bytes: + // Measured: `1332` + // Estimated: `46962` + // Minimum execution time: 90_789_000 picoseconds. + Weight::from_parts(93_329_000, 46962) + .saturating_add(RocksDbWeight::get().reads(14_u64)) + .saturating_add(RocksDbWeight::get().writes(13_u64)) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `2303 + c * (16400 ±0)` + // Estimated: `19878 + c * (84480 ±0)` + // Minimum execution time: 31_262_000 picoseconds. + Weight::from_parts(31_610_000, 19878) + // Standard Error: 69_131 + .saturating_add(Weight::from_parts(39_928_419, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(4_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 84480).saturating_mul(c.into())) + } + /// The range of component `c` is `[0, 2044]`. + fn tasks_pause_program_uninited(c: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `3129 + c * (42 ±0)` + // Estimated: `60575 + c * (2947 ±0)` + // Minimum execution time: 91_223_000 picoseconds. + Weight::from_parts(98_002_861, 60575) + // Standard Error: 2_086 + .saturating_add(Weight::from_parts(1_092_801, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(13_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(9_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 2947).saturating_mul(c.into())) + } } From 766c8bc3ade9f330e14b68e1381d52985139c0ec Mon Sep 17 00:00:00 2001 From: Sabaun Taraki Date: Mon, 18 Sep 2023 15:04:52 +0300 Subject: [PATCH 2/6] feat(runtime-fuzzer): Aggregational PR for different fuzzer features and adjustments (#3268) --- docker/runtime-fuzzer/Dockerfile | 2 +- utils/node-loader/src/utils.rs | 28 +++- utils/runtime-fuzzer/Cargo.toml | 4 + utils/runtime-fuzzer/README.md | 2 +- utils/runtime-fuzzer/bin/run_corpus.rs | 58 ++++++++ utils/runtime-fuzzer/src/arbitrary_call.rs | 31 +++- utils/wasm-gen/src/config.rs | 20 +++ utils/wasm-gen/src/config/generator.rs | 6 +- utils/wasm-gen/src/config/syscalls.rs | 50 +++---- utils/wasm-gen/src/config/syscalls/amount.rs | 34 +++-- utils/wasm-gen/src/config/syscalls/param.rs | 12 +- utils/wasm-gen/src/generator/syscalls.rs | 38 +++++ .../src/generator/syscalls/additional_data.rs | 4 +- .../src/generator/syscalls/imports.rs | 132 ++++++++---------- .../src/generator/syscalls/invocator.rs | 114 ++++++++------- utils/wasm-gen/src/tests.rs | 30 ++-- 16 files changed, 381 insertions(+), 184 deletions(-) create mode 100644 utils/runtime-fuzzer/bin/run_corpus.rs diff --git a/docker/runtime-fuzzer/Dockerfile b/docker/runtime-fuzzer/Dockerfile index 339055cb3b4..9cf7f8550f4 100644 --- a/docker/runtime-fuzzer/Dockerfile +++ b/docker/runtime-fuzzer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:22.10 +FROM ubuntu:22.04 MAINTAINER GEAR diff --git a/utils/node-loader/src/utils.rs b/utils/node-loader/src/utils.rs index 9beff5e7cda..d63d12ea75a 100644 --- a/utils/node-loader/src/utils.rs +++ b/utils/node-loader/src/utils.rs @@ -6,7 +6,10 @@ use gear_call_gen::Seed; use gear_core::ids::{MessageId, ProgramId}; use gear_core_errors::ReplyCode; use gear_utils::NonEmpty; -use gear_wasm_gen::{EntryPointsSet, StandardGearWasmConfigsBundle}; +use gear_wasm_gen::{ + EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName, + SysCallsInjectionAmounts, SysCallsParamsConfig, +}; use gsdk::metadata::runtime_types::{ gear_common::event::DispatchStatus as GenDispatchStatus, gear_core::{ @@ -209,10 +212,33 @@ pub fn get_wasm_gen_config( seed: Seed, existing_programs: impl Iterator, ) -> StandardGearWasmConfigsBundle { + let initial_pages = 2; + let mut injection_amounts = SysCallsInjectionAmounts::all_once(); + injection_amounts.set_multiple( + [ + (SysCallName::Leave, 0..=0), + (SysCallName::Panic, 0..=0), + (SysCallName::OomPanic, 0..=0), + (SysCallName::Send, 20..=30), + (SysCallName::Exit, 0..=1), + (SysCallName::Alloc, 5..=10), + (SysCallName::Free, 5..=10), + ] + .map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range)) + .into_iter(), + ); + + let mut params_config = SysCallsParamsConfig::default(); + params_config.add_rule(ParamType::Alloc, (1..=10).into()); + params_config.add_rule(ParamType::Free, (initial_pages..=initial_pages + 25).into()); + StandardGearWasmConfigsBundle { log_info: Some(format!("Gear program seed = '{seed}'")), existing_addresses: NonEmpty::collect(existing_programs), entry_points_set: EntryPointsSet::InitHandleHandleReply, + injection_amounts, + params_config, + initial_pages: initial_pages as u32, ..Default::default() } } diff --git a/utils/runtime-fuzzer/Cargo.toml b/utils/runtime-fuzzer/Cargo.toml index 0c145193e63..410ab54b0f9 100644 --- a/utils/runtime-fuzzer/Cargo.toml +++ b/utils/runtime-fuzzer/Cargo.toml @@ -4,6 +4,10 @@ version = "0.1.0" authors.workspace = true edition.workspace = true +[[bin]] +name = "run_corpus" +path = "bin/run_corpus.rs" + [dependencies] anyhow.workspace = true arbitrary.workspace = true diff --git a/utils/runtime-fuzzer/README.md b/utils/runtime-fuzzer/README.md index a7600d80bf9..8c141ea4f23 100644 --- a/utils/runtime-fuzzer/README.md +++ b/utils/runtime-fuzzer/README.md @@ -70,7 +70,7 @@ There are two ways to view coverage: ```bash # generate `lcov.info` file with coverage HOST_TARGET=$(rustc -Vv | grep "host: " | sed "s/^host: \(.*\)$/\1/") - cargo cov -- export target/x86_64-unknown-linux-gnu/coverage/x86_64-unknown-linux-gnu/release/main \ + cargo cov -- export target/$HOST_TARGET/coverage/$HOST_TARGET/release/main \ --format=lcov \ --instr-profile=fuzz/coverage/main/coverage.profdata \ --ignore-filename-regex=/rustc/ \ diff --git a/utils/runtime-fuzzer/bin/run_corpus.rs b/utils/runtime-fuzzer/bin/run_corpus.rs new file mode 100644 index 00000000000..fd4e2aaa66b --- /dev/null +++ b/utils/runtime-fuzzer/bin/run_corpus.rs @@ -0,0 +1,58 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! Runs provided from the cli corpus +//! +//! Alternatively, `cargo fuzz run` can be used to reproduce some corpus, +//! but it won't give logs of [`GearCalls`] generation, which sheds some +//! light on how `gear-wasm-gen` worked. +//! +//! Also that script can be used to run any bytes input, not only fuzzer's +//! corpus. +//! +//! Just simply run `cargo run --release -- -p `. + +use anyhow::Result; +use arbitrary::{Arbitrary, Unstructured}; +use clap::Parser; +use runtime_fuzzer::{self, GearCalls}; +use std::{fs, path::PathBuf}; + +/// A simple tool to run corpus. +#[derive(Debug, Parser)] +#[command(author, version, about, long_about = None)] +struct Params { + /// Path to the file, which contains corpus. + #[arg(short, long)] + path: PathBuf, +} + +fn main() -> Result<()> { + let params = Params::parse(); + + let corpus_bytes = fs::read(params.path)?; + + gear_utils::init_default_logger(); + + let mut unstructured = Unstructured::new(&corpus_bytes); + let gear_calls = GearCalls::arbitrary(&mut unstructured)?; + + runtime_fuzzer::run(gear_calls); + + Ok(()) +} diff --git a/utils/runtime-fuzzer/src/arbitrary_call.rs b/utils/runtime-fuzzer/src/arbitrary_call.rs index e8eb10c5c70..5f4d9f5836f 100644 --- a/utils/runtime-fuzzer/src/arbitrary_call.rs +++ b/utils/runtime-fuzzer/src/arbitrary_call.rs @@ -23,7 +23,8 @@ use arbitrary::{Arbitrary, Result, Unstructured}; use gear_core::ids::{CodeId, ProgramId}; use gear_utils::NonEmpty; use gear_wasm_gen::{ - EntryPointsSet, StandardGearWasmConfigsBundle, SysCallName, SysCallsInjectionAmounts, + EntryPointsSet, InvocableSysCall, ParamType, StandardGearWasmConfigsBundle, SysCallName, + SysCallsInjectionAmounts, SysCallsParamsConfig, }; use sha1::*; use std::{ @@ -190,12 +191,28 @@ fn config( programs: [ProgramId; GearCalls::INIT_MSGS], log_info: Option, ) -> StandardGearWasmConfigsBundle { + let initial_pages = 2; let mut injection_amounts = SysCallsInjectionAmounts::all_once(); - injection_amounts.set(SysCallName::Leave, 0, 0); - injection_amounts.set(SysCallName::Panic, 0, 0); - injection_amounts.set(SysCallName::OomPanic, 0, 0); - injection_amounts.set(SysCallName::Send, 20, 30); - injection_amounts.set(SysCallName::Exit, 0, 1); + injection_amounts.set_multiple( + [ + (SysCallName::Leave, 0..=0), + (SysCallName::Panic, 0..=0), + (SysCallName::OomPanic, 0..=0), + (SysCallName::Send, 20..=30), + (SysCallName::Exit, 0..=1), + (SysCallName::Alloc, 20..=30), + (SysCallName::Free, 20..=30), + ] + .map(|(sys_call, range)| (InvocableSysCall::Loose(sys_call), range)) + .into_iter(), + ); + + let mut params_config = SysCallsParamsConfig::default(); + params_config.add_rule(ParamType::Alloc, (10..=20).into()); + params_config.add_rule( + ParamType::Free, + (initial_pages..=initial_pages + 250).into(), + ); let existing_addresses = NonEmpty::collect( programs @@ -214,6 +231,8 @@ fn config( injection_amounts, existing_addresses, log_info, + params_config, + initial_pages: initial_pages as u32, ..Default::default() } } diff --git a/utils/wasm-gen/src/config.rs b/utils/wasm-gen/src/config.rs index e07b62de0e7..5993ab0283e 100644 --- a/utils/wasm-gen/src/config.rs +++ b/utils/wasm-gen/src/config.rs @@ -147,6 +147,12 @@ pub struct StandardGearWasmConfigsBundle { pub injection_amounts: SysCallsInjectionAmounts, /// Config of gear wasm call entry-points (exports). pub entry_points_set: EntryPointsSet, + /// Initial wasm memory pages. + pub initial_pages: u32, + /// Optional stack end pages. + pub stack_end_page: Option, + /// Sys-calls params config + pub params_config: SysCallsParamsConfig, } impl Default for StandardGearWasmConfigsBundle { @@ -158,6 +164,9 @@ impl Default for StandardGearWasmConfigsBundle { call_indirect_enabled: true, injection_amounts: SysCallsInjectionAmounts::all_once(), entry_points_set: Default::default(), + initial_pages: DEFAULT_INITIAL_SIZE, + stack_end_page: None, + params_config: SysCallsParamsConfig::default(), } } } @@ -171,6 +180,9 @@ impl> ConfigsBundle for StandardGearWasmConfigsBundle { call_indirect_enabled, injection_amounts, entry_points_set, + initial_pages, + stack_end_page, + params_config, } = self; let selectable_params = SelectableParams { @@ -188,10 +200,18 @@ impl> ConfigsBundle for StandardGearWasmConfigsBundle { } else { sys_calls_config_builder = sys_calls_config_builder.with_source_msg_dest(); } + sys_calls_config_builder = sys_calls_config_builder.with_params_config(params_config); + + let memory_pages_config = MemoryPagesConfig { + initial_size: initial_pages, + stack_end_page, + upper_limit: None, + }; let gear_wasm_generator_config = GearWasmGeneratorConfigBuilder::new() .with_recursions_removed(remove_recursion) .with_sys_calls_config(sys_calls_config_builder.build()) .with_entry_points_config(entry_points_set) + .with_memory_config(memory_pages_config) .build(); (gear_wasm_generator_config, selectable_params) diff --git a/utils/wasm-gen/src/config/generator.rs b/utils/wasm-gen/src/config/generator.rs index 5ee9341d8bd..7d831396143 100644 --- a/utils/wasm-gen/src/config/generator.rs +++ b/utils/wasm-gen/src/config/generator.rs @@ -20,6 +20,8 @@ use crate::SysCallsConfig; +pub(crate) const DEFAULT_INITIAL_SIZE: u32 = 16; + /// Builder for [`GearWasmGeneratorConfig`]. pub struct GearWasmGeneratorConfigBuilder(GearWasmGeneratorConfig); @@ -95,9 +97,9 @@ pub struct MemoryPagesConfig { impl Default for MemoryPagesConfig { fn default() -> Self { Self { - initial_size: Self::MAX_VALUE / 2 + 5, + initial_size: DEFAULT_INITIAL_SIZE, upper_limit: None, - stack_end_page: Some(Self::MAX_VALUE / 2), + stack_end_page: None, } } } diff --git a/utils/wasm-gen/src/config/syscalls.rs b/utils/wasm-gen/src/config/syscalls.rs index b9a37a4ed17..baa69462473 100644 --- a/utils/wasm-gen/src/config/syscalls.rs +++ b/utils/wasm-gen/src/config/syscalls.rs @@ -41,7 +41,7 @@ impl SysCallsConfigBuilder { Self(SysCallsConfig { injection_amounts, params_config: SysCallsParamsConfig::default(), - sending_message_destination: MessageDestination::default(), + sys_call_destination: SysCallDestination::default(), error_processing_config: ErrorProcessingConfig::None, log_info: None, }) @@ -54,23 +54,23 @@ impl SysCallsConfigBuilder { self } - /// Set whether `gr_send*` sys-calls must use `gr_source` result for message destination. + /// Set whether `gr_send*` and `gr_exit` sys-calls must use `gr_source` result for sys-call destination. pub fn with_source_msg_dest(mut self) -> Self { - self.0.sending_message_destination = MessageDestination::Source; - self.enable_sys_call(SysCallName::Source); + self.0.sys_call_destination = SysCallDestination::Source; + self.enable_sys_call(InvocableSysCall::Loose(SysCallName::Source)); self } - /// Set whether `gr_send*` sys-calls must use some address from `addresses` collection - /// as a message destination. + /// Set whether `gr_send*` and `gr_exit` sys-calls must use some address from `addresses` collection + /// as a sys-call destination. pub fn with_data_offset_msg_dest>(mut self, addresses: NonEmpty) -> Self { let addresses = NonEmpty::collect(addresses.into_iter().map(|pid| HashWithValue { hash: pid.into(), value: 0, })) .expect("collected from non empty"); - self.0.sending_message_destination = MessageDestination::ExistingAddresses(addresses); + self.0.sys_call_destination = SysCallDestination::ExistingAddresses(addresses); self } @@ -81,7 +81,7 @@ impl SysCallsConfigBuilder { /// Choosing gear export to log data is done from best `init` to worse `handle`. pub fn with_log_info(mut self, log: String) -> Self { self.0.log_info = Some(log); - self.enable_sys_call(SysCallName::Debug); + self.enable_sys_call(InvocableSysCall::Loose(SysCallName::Debug)); self } @@ -93,7 +93,7 @@ impl SysCallsConfigBuilder { self } - fn enable_sys_call(&mut self, name: SysCallName) { + fn enable_sys_call(&mut self, name: InvocableSysCall) { let range = self.0.injection_amounts.get(name); let range_start = *range.start(); @@ -138,22 +138,22 @@ impl ErrorProcessingConfig { pub struct SysCallsConfig { injection_amounts: SysCallsInjectionAmounts, params_config: SysCallsParamsConfig, - sending_message_destination: MessageDestination, + sys_call_destination: SysCallDestination, error_processing_config: ErrorProcessingConfig, log_info: Option, } impl SysCallsConfig { /// Get possible number of times (range) the sys-call can be injected in the wasm. - pub fn injection_amounts(&self, name: SysCallName) -> RangeInclusive { + pub fn injection_amounts(&self, name: InvocableSysCall) -> RangeInclusive { self.injection_amounts.get(name) } - /// Get defined message destination for `gr_send*` sys-calls. + /// Get defined sys-call destination for `gr_send*` and `gr_exit` sys-calls. /// - /// For more info, read [`MessageDestination`]. - pub fn sending_message_destination(&self) -> &MessageDestination { - &self.sending_message_destination + /// For more info, read [`SysCallDestination`]. + pub fn sys_call_destination(&self) -> &SysCallDestination { + &self.sys_call_destination } /// Get defined log info. @@ -174,33 +174,33 @@ impl SysCallsConfig { } } -/// Message destination choice. +/// Sys-call destination choice. /// -/// `gr_send*` sys-calls generated from this crate can send messages +/// `gr_send*` and `gr_exit` sys-calls generated from this crate can be sent /// to different destination in accordance to the config. /// It's either to the message source, to some existing known address, /// or to some random, most probably non-existing, address. #[derive(Debug, Clone, Default)] -pub enum MessageDestination { +pub enum SysCallDestination { Source, ExistingAddresses(NonEmpty), #[default] Random, } -impl MessageDestination { - /// Check whether message destination is a result of `gr_source`. +impl SysCallDestination { + /// Check whether sys-call destination is a result of `gr_source`. pub fn is_source(&self) -> bool { - matches!(&self, MessageDestination::Source) + matches!(&self, SysCallDestination::Source) } - /// Check whether message destination is defined randomly. + /// Check whether sys-call destination is defined randomly. pub fn is_random(&self) -> bool { - matches!(&self, MessageDestination::Random) + matches!(&self, SysCallDestination::Random) } - /// Check whether message destination is defined from a collection of existing addresses. + /// Check whether sys-call destination is defined from a collection of existing addresses. pub fn is_existing_addresses(&self) -> bool { - matches!(&self, MessageDestination::ExistingAddresses(_)) + matches!(&self, SysCallDestination::ExistingAddresses(_)) } } diff --git a/utils/wasm-gen/src/config/syscalls/amount.rs b/utils/wasm-gen/src/config/syscalls/amount.rs index 10653a8ab74..58350dea518 100644 --- a/utils/wasm-gen/src/config/syscalls/amount.rs +++ b/utils/wasm-gen/src/config/syscalls/amount.rs @@ -20,36 +20,44 @@ //! //! Types here are used to create [`crate::SysCallsConfig`]. +use crate::InvocableSysCall; + use gear_wasm_instrument::syscalls::SysCallName; use std::{collections::HashMap, ops::RangeInclusive}; /// Possible injection amount ranges for each sys-call. #[derive(Debug, Clone)] -pub struct SysCallsInjectionAmounts(HashMap>); +pub struct SysCallsInjectionAmounts(HashMap>); impl SysCallsInjectionAmounts { /// Instantiate a sys-calls amounts ranges map, where each gear sys-call is injected into wasm-module only once. pub fn all_once() -> Self { - Self( - SysCallName::instrumentable() - .into_iter() - .map(|name| (name, (1..=1))) - .collect(), - ) + Self::new_with_range(1..=1) } /// Instantiate a sys-calls amounts ranges map, where no gear sys-call is ever injected into wasm-module. pub fn all_never() -> Self { + Self::new_with_range(0..=0) + } + + /// Instantiate a sys-calls amounts ranges map with given range. + fn new_with_range(range: RangeInclusive) -> Self { + let sys_calls = SysCallName::instrumentable(); Self( - SysCallName::instrumentable() - .into_iter() - .map(|name| (name, (0..=0))) + sys_calls + .iter() + .cloned() + .map(|name| (InvocableSysCall::Loose(name), range.clone())) + .chain(sys_calls.iter().cloned().filter_map(|name| { + InvocableSysCall::has_precise_variant(name) + .then_some((InvocableSysCall::Precise(name), range.clone())) + })) .collect(), ) } /// Get amount possible sys-call amount range. - pub fn get(&self, name: SysCallName) -> RangeInclusive { + pub fn get(&self, name: InvocableSysCall) -> RangeInclusive { self.0 .get(&name) .cloned() @@ -57,14 +65,14 @@ impl SysCallsInjectionAmounts { } /// Sets possible amount range for the the sys-call. - pub fn set(&mut self, name: SysCallName, min: u32, max: u32) { + pub fn set(&mut self, name: InvocableSysCall, min: u32, max: u32) { self.0.insert(name, min..=max); } /// Same as [`SysCallsAmountRanges::set`], but sets amount ranges for multiple sys-calls. pub fn set_multiple( &mut self, - sys_calls_freqs: impl Iterator)>, + sys_calls_freqs: impl Iterator)>, ) { self.0.extend(sys_calls_freqs) } diff --git a/utils/wasm-gen/src/config/syscalls/param.rs b/utils/wasm-gen/src/config/syscalls/param.rs index 81b94c3e90b..705bdd580ed 100644 --- a/utils/wasm-gen/src/config/syscalls/param.rs +++ b/utils/wasm-gen/src/config/syscalls/param.rs @@ -20,10 +20,12 @@ //! //! Types here are used to create [`crate::SysCallsConfig`]. +use crate::DEFAULT_INITIAL_SIZE; use arbitrary::{Result, Unstructured}; -use gear_wasm_instrument::syscalls::ParamType; use std::{collections::HashMap, ops::RangeInclusive}; +pub use gear_wasm_instrument::syscalls::ParamType; + /// Sys-calls params config. /// /// This is basically a map, which creates a relationship between each kind of @@ -42,6 +44,10 @@ use std::{collections::HashMap, ops::RangeInclusive}; pub struct SysCallsParamsConfig(HashMap); impl SysCallsParamsConfig { + pub fn empty() -> Self { + Self(HashMap::new()) + } + /// New [`SysCallsParamsConfig`] with all rules set to produce one constant value. pub fn all_constant_value(value: i64) -> Self { let allowed_values: SysCallParamAllowedValues = (value..=value).into(); @@ -77,6 +83,8 @@ impl SysCallsParamsConfig { impl Default for SysCallsParamsConfig { fn default() -> Self { + let free_start = DEFAULT_INITIAL_SIZE as i64; + let free_end = free_start + 5; Self( [ (ParamType::Size, (0..=0x10000).into()), @@ -87,7 +95,7 @@ impl Default for SysCallsParamsConfig { (ParamType::Duration, (1..=8).into()), (ParamType::Delay, (0..=4).into()), (ParamType::Handler, (0..=100).into()), - (ParamType::Free, (0..=512).into()), + (ParamType::Free, (free_start..=free_end).into()), ] .into_iter() .collect(), diff --git a/utils/wasm-gen/src/generator/syscalls.rs b/utils/wasm-gen/src/generator/syscalls.rs index 5b667c216fe..20997838ed6 100644 --- a/utils/wasm-gen/src/generator/syscalls.rs +++ b/utils/wasm-gen/src/generator/syscalls.rs @@ -113,6 +113,44 @@ impl InvocableSysCall { } } + /// Checks whether given sys-call has the precise variant. + pub(crate) fn has_precise_variant(sys_call: SysCallName) -> bool { + Self::required_imports_for_sys_call(sys_call).is_some() + } + + /// Returns the required imports to build precise sys-call. + fn required_imports_for_sys_call(sys_call: SysCallName) -> Option<&'static [SysCallName]> { + // NOTE: the last sys-call must be pattern itself + Some(match sys_call { + SysCallName::ReservationSend => { + &[SysCallName::ReserveGas, SysCallName::ReservationSend] + } + SysCallName::ReservationReply => { + &[SysCallName::ReserveGas, SysCallName::ReservationReply] + } + SysCallName::SendCommit => &[ + SysCallName::SendInit, + SysCallName::SendPush, + SysCallName::SendCommit, + ], + SysCallName::SendCommitWGas => &[ + SysCallName::Size, + SysCallName::SendInit, + SysCallName::SendPushInput, + SysCallName::SendCommitWGas, + ], + _ => return None, + }) + } + + /// Returns the required imports to build precise sys-call, but of a fixed size. + fn required_imports(sys_call: SysCallName) -> &'static [SysCallName; N] { + Self::required_imports_for_sys_call(sys_call) + .expect("failed to find required imports for sys-call") + .try_into() + .expect("failed to convert slice") + } + // If syscall changes from fallible into infallible or vice versa in future, // we'll see it by analyzing code coverage stats produced by fuzzer. pub(crate) fn is_fallible(&self) -> bool { diff --git a/utils/wasm-gen/src/generator/syscalls/additional_data.rs b/utils/wasm-gen/src/generator/syscalls/additional_data.rs index 4a2cdbb3751..ed31137c446 100644 --- a/utils/wasm-gen/src/generator/syscalls/additional_data.rs +++ b/utils/wasm-gen/src/generator/syscalls/additional_data.rs @@ -23,7 +23,7 @@ use crate::{ CallIndexes, CallIndexesHandle, DisabledSysCallsImportsGenerator, ModuleWithCallIndexes, SysCallsImportsGenerationProof, }, - utils, EntryPointName, InvocableSysCall, MessageDestination, SysCallsConfig, WasmModule, + utils, EntryPointName, InvocableSysCall, SysCallDestination, SysCallsConfig, WasmModule, }; use arbitrary::Unstructured; use gear_core::ids::ProgramId; @@ -140,7 +140,7 @@ impl<'a, 'b> AdditionalDataInjector<'a, 'b> { )); } - let MessageDestination::ExistingAddresses(existing_addresses) = self.config.sending_message_destination() else { + let SysCallDestination::ExistingAddresses(existing_addresses) = self.config.sys_call_destination() else { return None; }; diff --git a/utils/wasm-gen/src/generator/syscalls/imports.rs b/utils/wasm-gen/src/generator/syscalls/imports.rs index 8389f943f08..91887407814 100644 --- a/utils/wasm-gen/src/generator/syscalls/imports.rs +++ b/utils/wasm-gen/src/generator/syscalls/imports.rs @@ -57,16 +57,9 @@ pub struct SysCallsImportsGeneratorInstantiator<'a, 'b>( ), ); -/// The set of sys-calls that need to be imported to create precise sys-call. -#[derive(thiserror::Error, Debug)] -#[error("The following sys-calls must be imported: {0:?}")] -pub struct RequiredSysCalls(&'static [SysCallName]); - /// An error that occurs when generating precise sys-call. #[derive(thiserror::Error, Debug)] pub enum PreciseSysCallError { - #[error("{0}")] - RequiredImports(#[from] RequiredSysCalls), #[error("{0}")] Arbitrary(#[from] ArbitraryError), } @@ -197,16 +190,32 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { /// Generates precise sys-calls and handles errors if any occurred during generation. fn generate_precise_sys_calls(&mut self) -> Result<()> { - for result in [ - self.generate_send_from_reservation(), - self.generate_reply_from_reservation(), - self.generate_send_commit(), - self.generate_send_commit_with_gas(), - ] { - if let Err(err) = result { - match err { - PreciseSysCallError::RequiredImports(err) => log::trace!("{err}"), - PreciseSysCallError::Arbitrary(err) => return Err(err), + use SysCallName::*; + + #[allow(clippy::type_complexity)] + let sys_calls: [( + SysCallName, + fn(&mut Self, SysCallName) -> Result<(), PreciseSysCallError>, + ); 4] = [ + (ReservationSend, Self::generate_send_from_reservation), + (ReservationReply, Self::generate_reply_from_reservation), + (SendCommit, Self::generate_send_commit), + (SendCommitWGas, Self::generate_send_commit_with_gas), + ]; + + for (sys_call, generate_method) in sys_calls { + let sys_call_amount_range = self + .config + .injection_amounts(InvocableSysCall::Precise(sys_call)); + let sys_call_amount = self.unstructured.int_in_range(sys_call_amount_range)?; + for _ in 0..sys_call_amount { + log::trace!( + "Constructing {name} sys-call...", + name = InvocableSysCall::Precise(sys_call).to_str() + ); + + if let Err(PreciseSysCallError::Arbitrary(err)) = generate_method(self, sys_call) { + return Err(err); } } } @@ -223,7 +232,9 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { &mut self, sys_call: SysCallName, ) -> Result> { - let sys_call_amount_range = self.config.injection_amounts(sys_call); + let sys_call_amount_range = self + .config + .injection_amounts(InvocableSysCall::Loose(sys_call)); let sys_call_amount = self.unstructured.int_in_range(sys_call_amount_range)?; Ok((sys_call_amount != 0).then(|| { let call_indexes_handle = self.insert_sys_call_import(sys_call); @@ -280,9 +291,9 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { /// Returns the indexes of invocable sys-calls. fn invocable_sys_calls_indexes( - &self, + &mut self, sys_calls: &'static [SysCallName; N], - ) -> Result<[usize; N], RequiredSysCalls> { + ) -> [usize; N] { let mut indexes = [0; N]; for (index, &sys_call) in indexes.iter_mut().zip(sys_calls.iter()) { @@ -290,10 +301,16 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { .sys_calls_imports .get(&InvocableSysCall::Loose(sys_call)) .map(|&(_, call_indexes_handle)| call_indexes_handle) - .ok_or_else(|| RequiredSysCalls(&sys_calls[..]))?; + .unwrap_or_else(|| { + // insert required import when we can't find it + let call_indexes_handle = self.insert_sys_call_import(sys_call); + self.sys_calls_imports + .insert(InvocableSysCall::Loose(sys_call), (0, call_indexes_handle)); + call_indexes_handle + }) } - Ok(indexes) + indexes } /// Generates a function which calls "properly" the given sys-call. @@ -327,13 +344,12 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { log::trace!( "Built proper call to {precise_sys_call_name}", - precise_sys_call_name = InvocableSysCall::Precise(sys_call).to_str() + precise_sys_call_name = invocable_sys_call.to_str() ); let call_indexes_handle = self.call_indexes.len(); self.call_indexes.add_func(func_idx.signature as usize); - // TODO: make separate config for precise sys-calls (#3122) self.sys_calls_imports .insert(invocable_sys_call, (1, call_indexes_handle)); } @@ -355,15 +371,12 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { } /// Generates a function which calls "properly" the `gr_reservation_send`. - fn generate_send_from_reservation(&mut self) -> Result<(), PreciseSysCallError> { - const SYS_CALL: SysCallName = SysCallName::ReservationSend; - log::trace!( - "Constructing {name} sys-call...", - name = InvocableSysCall::Precise(SYS_CALL).to_str() - ); - + fn generate_send_from_reservation( + &mut self, + sys_call: SysCallName, + ) -> Result<(), PreciseSysCallError> { let [reserve_gas_idx, reservation_send_idx] = - self.invocable_sys_calls_indexes(&[SysCallName::ReserveGas, SYS_CALL])?; + self.invocable_sys_calls_indexes(InvocableSysCall::required_imports(sys_call)); // subtract to be sure we are in memory boundaries. let rid_pid_value_ptr = self.reserve_memory(); @@ -443,21 +456,18 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { Instruction::End, ]); - self.generate_proper_sys_call_invocation(SYS_CALL, func_instructions); + self.generate_proper_sys_call_invocation(sys_call, func_instructions); Ok(()) } /// Generates a function which calls "properly" the `gr_reservation_reply`. - fn generate_reply_from_reservation(&mut self) -> Result<(), PreciseSysCallError> { - const SYS_CALL: SysCallName = SysCallName::ReservationReply; - log::trace!( - "Constructing {name} sys-call...", - name = InvocableSysCall::Precise(SYS_CALL).to_str() - ); - + fn generate_reply_from_reservation( + &mut self, + sys_call: SysCallName, + ) -> Result<(), PreciseSysCallError> { let [reserve_gas_idx, reservation_reply_idx] = - self.invocable_sys_calls_indexes(&[SysCallName::ReserveGas, SYS_CALL])?; + self.invocable_sys_calls_indexes(InvocableSysCall::required_imports(sys_call)); // subtract to be sure we are in memory boundaries. let rid_value_ptr = self.reserve_memory(); @@ -518,25 +528,15 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { Instruction::End, ]); - self.generate_proper_sys_call_invocation(SYS_CALL, func_instructions); + self.generate_proper_sys_call_invocation(sys_call, func_instructions); Ok(()) } /// Generates a function which calls "properly" the `gr_send_commit`. - fn generate_send_commit(&mut self) -> Result<(), PreciseSysCallError> { - const SYS_CALL: SysCallName = SysCallName::SendCommit; - log::trace!( - "Constructing {name} sys-call...", - name = InvocableSysCall::Precise(SYS_CALL).to_str() - ); - + fn generate_send_commit(&mut self, sys_call: SysCallName) -> Result<(), PreciseSysCallError> { let [send_init_idx, send_push_idx, send_commit_idx] = - self.invocable_sys_calls_indexes(&[ - SysCallName::SendInit, - SysCallName::SendPush, - SYS_CALL, - ])?; + self.invocable_sys_calls_indexes(InvocableSysCall::required_imports(sys_call)); // subtract to be sure we are in memory boundaries. let handle_ptr = self.reserve_memory(); @@ -620,26 +620,18 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { let func_instructions = Instructions::new(elements); - self.generate_proper_sys_call_invocation(SYS_CALL, func_instructions); + self.generate_proper_sys_call_invocation(sys_call, func_instructions); Ok(()) } /// Generates a function which calls "properly" the `gr_send_commit_wgas`. - fn generate_send_commit_with_gas(&mut self) -> Result<(), PreciseSysCallError> { - const SYS_CALL: SysCallName = SysCallName::SendCommitWGas; - log::trace!( - "Constructing {name} sys-call...", - name = InvocableSysCall::Precise(SYS_CALL).to_str() - ); - - let [size_idx, send_init_idx, send_push_input_idx, send_commit_wgas_idx] = self - .invocable_sys_calls_indexes(&[ - SysCallName::Size, - SysCallName::SendInit, - SysCallName::SendPushInput, - SYS_CALL, - ])?; + fn generate_send_commit_with_gas( + &mut self, + sys_call: SysCallName, + ) -> Result<(), PreciseSysCallError> { + let [size_idx, send_init_idx, send_push_input_idx, send_commit_wgas_idx] = + self.invocable_sys_calls_indexes(InvocableSysCall::required_imports(sys_call)); // subtract to be sure we are in memory boundaries. let handle_ptr = self.reserve_memory(); @@ -730,7 +722,7 @@ impl<'a, 'b> SysCallsImportsGenerator<'a, 'b> { let func_instructions = Instructions::new(elements); - self.generate_proper_sys_call_invocation(SYS_CALL, func_instructions); + self.generate_proper_sys_call_invocation(sys_call, func_instructions); Ok(()) } diff --git a/utils/wasm-gen/src/generator/syscalls/invocator.rs b/utils/wasm-gen/src/generator/syscalls/invocator.rs index 479913e10cf..df5d3fca451 100644 --- a/utils/wasm-gen/src/generator/syscalls/invocator.rs +++ b/utils/wasm-gen/src/generator/syscalls/invocator.rs @@ -35,7 +35,9 @@ use std::{collections::BTreeMap, iter}; #[derive(Debug)] pub(crate) enum ProcessedSysCallParams { - Alloc, + Alloc { + allowed_values: Option, + }, Value { value_type: ValueType, allowed_values: Option, @@ -56,7 +58,9 @@ pub(crate) fn process_sys_call_params( continue; } let processed_param = match param { - ParamType::Alloc => ProcessedSysCallParams::Alloc, + ParamType::Alloc => ProcessedSysCallParams::Alloc { + allowed_values: params_config.get_rule(¶m), + }, ParamType::Ptr(maybe_idx) => maybe_idx .map(|_| { // skipping next as we don't need the following `Size` param, @@ -87,7 +91,7 @@ pub(crate) fn process_sys_call_params( /// data injection outcome ([`AddressesInjectionOutcome`]). The latter was introduced /// to give additional guarantees for config and generators consistency. Otherwise, /// if there wasn't any addresses injection outcome, which signals that there was a try to -/// inject addresses, sys-calls invocator could falsely set `gr_send*` call's destination param +/// inject addresses, sys-calls invocator could falsely set `gr_send*` and `gr_exit` call's destination param /// to random value. For example, existing addresses could have been defined in the config, but /// additional data injector was disabled, before injecting addresses from the config. As a result, /// invocator would set un-intended by config values as messages destination. To avoid such @@ -170,8 +174,11 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { ); for (invocable, (amount, call_indexes_handle)) in self.sys_call_imports.clone() { - let instructions = - self.build_sys_call_invoke_instructions(invocable, call_indexes_handle)?; + let instructions = self.build_sys_call_invoke_instructions( + invocable, + invocable.into_signature(), + call_indexes_handle, + )?; log::trace!( "Inserting the {} sys_call {} times", @@ -200,6 +207,7 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { fn build_sys_call_invoke_instructions( &mut self, invocable: InvocableSysCall, + signature: SysCallSignature, call_indexes_handle: CallIndexesHandle, ) -> Result> { log::trace!( @@ -208,42 +216,37 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { self.unstructured.len() ); - let insert_error_processing = self - .config - .error_processing_config() - .error_should_be_processed(&invocable); - let (fallible, mut signature) = (invocable.is_fallible(), invocable.into_signature()); - - if self.is_not_send_sys_call(invocable) { + if self.is_sys_call_with_destination(invocable) { log::trace!( - " -- Generating build call for non-send sys-call {}", + " -- Generating build call for {} sys-call with destination", invocable.to_str() ); - return self.build_call( - signature, - fallible, - insert_error_processing, - call_indexes_handle, + + self.build_call_with_destination(invocable, signature, call_indexes_handle) + } else { + log::trace!( + " -- Generating build call for common sys-call {}", + invocable.to_str() ); - } - log::trace!( - " -- Generating build call for send sys-call {}", - invocable.to_str() - ); + self.build_call(invocable, signature, call_indexes_handle) + } + } + fn build_call_with_destination( + &mut self, + invocable: InvocableSysCall, + mut signature: SysCallSignature, + call_indexes_handle: CallIndexesHandle, + ) -> Result> { // The value for the first param is chosen from config. // It's either the result of `gr_source`, some existing address (set in the data section) or a completely random value. signature.params.remove(0); - let mut call_without_destination_instrs = self.build_call( - signature, - fallible, - insert_error_processing, - call_indexes_handle, - )?; + let mut call_without_destination_instrs = + self.build_call(invocable, signature, call_indexes_handle)?; - let res = if self.config.sending_message_destination().is_source() { - log::trace!(" -- Message destination is result of `gr_source`"); + let res = if self.config.sys_call_destination().is_source() { + log::trace!(" -- Sys-call destination is result of `gr_source`"); let gr_source_call_indexes_handle = self .sys_call_imports @@ -278,17 +281,14 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { let address_offset = match self.offsets.as_mut() { Some(offsets) => { - assert!(self - .config - .sending_message_destination() - .is_existing_addresses()); - log::trace!(" -- Message destination is an existing program address"); + assert!(self.config.sys_call_destination().is_existing_addresses()); + log::trace!(" -- Sys-call destination is an existing program address"); offsets.next_offset() } None => { - assert!(self.config.sending_message_destination().is_random()); - log::trace!(" -- Message destination is a random address"); + assert!(self.config.sys_call_destination().is_random()); + log::trace!(" -- Sys-call destination is a random address"); self.unstructured.arbitrary()? } @@ -303,23 +303,32 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { Ok(res) } - fn is_not_send_sys_call(&self, sys_call: InvocableSysCall) -> bool { + fn is_sys_call_with_destination(&self, sys_call: InvocableSysCall) -> bool { + self.is_send_sys_call(sys_call) || self.is_exit_sys_call(sys_call) + } + + fn is_send_sys_call(&self, sys_call: InvocableSysCall) -> bool { use InvocableSysCall::*; - ![ + [ Loose(SysCallName::Send), Loose(SysCallName::SendWGas), Loose(SysCallName::SendInput), Loose(SysCallName::SendInputWGas), Precise(SysCallName::ReservationSend), + Precise(SysCallName::SendCommit), + Precise(SysCallName::SendCommitWGas), ] .contains(&sys_call) } + fn is_exit_sys_call(&self, sys_call: InvocableSysCall) -> bool { + matches!(sys_call, InvocableSysCall::Loose(SysCallName::Exit)) + } + fn build_call( &mut self, + invocable: InvocableSysCall, signature: SysCallSignature, - fallible: bool, - insert_error_processing: bool, call_indexes_handle: CallIndexesHandle, ) -> Result> { let param_setters = self.build_param_setters(&signature.params)?; @@ -331,9 +340,14 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { instructions.push(Instruction::Call(call_indexes_handle as u32)); + let insert_error_processing = self + .config + .error_processing_config() + .error_should_be_processed(&invocable); + let mut result_processing = if !insert_error_processing { Self::build_result_processing_ignored(signature) - } else if fallible { + } else if invocable.is_fallible() { Self::build_result_processing_fallible(signature, ¶m_setters) } else { Self::build_result_processing_infallible(signature) @@ -360,15 +374,17 @@ impl<'a, 'b> SysCallsInvocator<'a, 'b> { let mut setters = Vec::with_capacity(params.len()); for processed_param in process_sys_call_params(params, self.config.params_config()) { match processed_param { - ProcessedSysCallParams::Alloc => { - let pages_to_alloc = self - .unstructured - .int_in_range(0..=mem_size_pages.saturating_sub(1))?; - let setter = ParamSetter::new_i32(pages_to_alloc as i32); + ProcessedSysCallParams::Alloc { allowed_values } => { + let pages_to_alloc = if let Some(allowed_values) = allowed_values { + allowed_values.get_i32(self.unstructured)? + } else { + let mem_size_pages = (mem_size_pages / 3).max(1); + self.unstructured.int_in_range(0..=mem_size_pages)? as i32 + }; log::trace!(" ---- Allocate memory - {pages_to_alloc}"); - setters.push(setter); + setters.push(ParamSetter::new_i32(pages_to_alloc)); } ProcessedSysCallParams::Value { value_type, diff --git a/utils/wasm-gen/src/tests.rs b/utils/wasm-gen/src/tests.rs index 1f45db0b6fd..57019510d32 100644 --- a/utils/wasm-gen/src/tests.rs +++ b/utils/wasm-gen/src/tests.rs @@ -18,17 +18,25 @@ use super::*; use arbitrary::Unstructured; -use gear_backend_common::{TerminationReason, TrapExplanation}; +use gear_backend_common::{BackendReport, Environment, TerminationReason, TrapExplanation}; +use gear_backend_sandbox::SandboxEnvironment; use gear_core::{ code::Code, memory::Memory, - message::{IncomingMessage, ReplyPacket}, + message::{ + ContextSettings, DispatchKind, IncomingDispatch, IncomingMessage, MessageContext, + ReplyPacket, + }, pages::WASM_PAGE_SIZE, }; +use gear_core_processor::{ProcessorContext, ProcessorExternalities}; use gear_utils::NonEmpty; -use gear_wasm_instrument::parity_wasm::{ - self, - elements::{Instruction, Module}, +use gear_wasm_instrument::{ + parity_wasm::{ + self, + elements::{Instruction, Module}, + }, + rules::CustomConstantCostRules, }; use proptest::prelude::*; use rand::{rngs::SmallRng, RngCore, SeedableRng}; @@ -231,12 +239,6 @@ fn execute_wasm_with_syscall_injected( params_config: SysCallsParamsConfig, initial_memory_write: Option, ) -> TerminationReason { - use gear_backend_common::{BackendReport, Environment}; - use gear_backend_sandbox::SandboxEnvironment; - use gear_core::message::{ContextSettings, DispatchKind, IncomingDispatch, MessageContext}; - use gear_core_processor::{ProcessorContext, ProcessorExternalities}; - use gear_wasm_instrument::rules::CustomConstantCostRules; - const INITIAL_PAGES: u16 = 1; const INJECTED_SYSCALLS: u32 = 8; @@ -245,7 +247,11 @@ fn execute_wasm_with_syscall_injected( let mut unstructured = Unstructured::new(&buf); let mut injection_amounts = SysCallsInjectionAmounts::all_never(); - injection_amounts.set(syscall, INJECTED_SYSCALLS, INJECTED_SYSCALLS); + injection_amounts.set( + InvocableSysCall::Loose(syscall), + INJECTED_SYSCALLS, + INJECTED_SYSCALLS, + ); let error_processing_config = if ignore_fallible_errors { ErrorProcessingConfig::None From 7ccc91c4281b9db64eebb9caa66367f97f7f3266 Mon Sep 17 00:00:00 2001 From: ekovalev Date: Mon, 18 Sep 2023 16:19:19 +0200 Subject: [PATCH 3/6] feat(rpc): Add RPC call to query inflation and ROI (#3290) --- Cargo.lock | 24 +++++ Cargo.toml | 2 + node/service/Cargo.toml | 2 + node/service/src/client.rs | 4 +- node/service/src/rpc/mod.rs | 6 +- pallets/staking-rewards/rpc/Cargo.toml | 19 ++++ .../rpc/runtime-api/Cargo.toml | 19 ++++ .../rpc/runtime-api/src/lib.rs | 28 ++++++ pallets/staking-rewards/rpc/src/lib.rs | 96 +++++++++++++++++++ pallets/staking-rewards/src/lib.rs | 40 ++++++++ runtime/gear/Cargo.toml | 4 + runtime/gear/src/lib.rs | 6 ++ runtime/vara/Cargo.toml | 2 + runtime/vara/src/lib.rs | 6 ++ 14 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 pallets/staking-rewards/rpc/Cargo.toml create mode 100644 pallets/staking-rewards/rpc/runtime-api/Cargo.toml create mode 100644 pallets/staking-rewards/rpc/runtime-api/src/lib.rs create mode 100644 pallets/staking-rewards/rpc/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 76d3ff7a82e..00d28e6f667 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4136,6 +4136,8 @@ dependencies = [ "pallet-gear-program", "pallet-gear-rpc-runtime-api", "pallet-gear-scheduler", + "pallet-gear-staking-rewards", + "pallet-gear-staking-rewards-rpc-runtime-api", "pallet-gear-voucher", "pallet-grandpa", "pallet-multisig", @@ -4288,6 +4290,8 @@ dependencies = [ "log", "pallet-gear-rpc", "pallet-gear-rpc-runtime-api", + "pallet-gear-staking-rewards-rpc", + "pallet-gear-staking-rewards-rpc-runtime-api", "pallet-im-online", "pallet-staking", "pallet-transaction-payment", @@ -7666,6 +7670,25 @@ dependencies = [ "sp-std 5.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", ] +[[package]] +name = "pallet-gear-staking-rewards-rpc" +version = "1.0.0" +dependencies = [ + "jsonrpsee", + "pallet-gear-staking-rewards-rpc-runtime-api", + "sp-api", + "sp-blockchain", + "sp-runtime 7.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", +] + +[[package]] +name = "pallet-gear-staking-rewards-rpc-runtime-api" +version = "1.0.0" +dependencies = [ + "pallet-gear-staking-rewards", + "sp-api", +] + [[package]] name = "pallet-gear-voucher" version = "1.0.0" @@ -13503,6 +13526,7 @@ dependencies = [ "pallet-gear-rpc-runtime-api", "pallet-gear-scheduler", "pallet-gear-staking-rewards", + "pallet-gear-staking-rewards-rpc-runtime-api", "pallet-gear-voucher", "pallet-grandpa", "pallet-identity", diff --git a/Cargo.toml b/Cargo.toml index 1b581dac2b3..eecdbf0432f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -231,6 +231,8 @@ pallet-gear-rpc = { path = "pallets/gear/rpc" } pallet-gear-rpc-runtime-api = { path = "pallets/gear/rpc/runtime-api", default-features = false } pallet-gear-scheduler = { path = "pallets/gear-scheduler", default-features = false } pallet-gear-staking-rewards = { path = "pallets/staking-rewards", default-features = false } +pallet-gear-staking-rewards-rpc = { path = "pallets/staking-rewards/rpc" } +pallet-gear-staking-rewards-rpc-runtime-api = { path = "pallets/staking-rewards/rpc/runtime-api", default-features = false } pallet-gear-voucher = { path = "pallets/gear-voucher", default-features = false } pallet-gear-bank = { path = "pallets/gear-bank", default-features = false } runtime-common = { package = "gear-runtime-common", path = "runtime/common", default-features = false } diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index 79ff6da306f..707bd61836a 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -22,6 +22,8 @@ serde = { workspace = true, features = ["derive"] } # Gear pallet-gear-rpc.workspace = true pallet-gear-rpc-runtime-api = { workspace = true, features = ["std"] } +pallet-gear-staking-rewards-rpc.workspace = true +pallet-gear-staking-rewards-rpc-runtime-api = { workspace = true, features = ["std"] } runtime-primitives = { workspace = true, features = ["std"] } gear-runtime-interface = { workspace = true, features = ["std"] } authorship.workspace = true diff --git a/node/service/src/client.rs b/node/service/src/client.rs index 284686c4cb3..b118fe36453 100644 --- a/node/service/src/client.rs +++ b/node/service/src/client.rs @@ -119,6 +119,7 @@ pub trait RuntimeApiCollection: + sp_offchain::OffchainWorkerApi + sp_session::SessionKeys + pallet_gear_rpc_runtime_api::GearApi + + pallet_gear_staking_rewards_rpc_runtime_api::GearStakingRewardsApi where >::StateBackend: sp_api::StateBackend, { @@ -136,7 +137,8 @@ where + sp_api::Metadata + sp_offchain::OffchainWorkerApi + sp_session::SessionKeys - + pallet_gear_rpc_runtime_api::GearApi, + + pallet_gear_rpc_runtime_api::GearApi + + pallet_gear_staking_rewards_rpc_runtime_api::GearStakingRewardsApi, >::StateBackend: sp_api::StateBackend, { } diff --git a/node/service/src/rpc/mod.rs b/node/service/src/rpc/mod.rs index 527495e6f18..8c1242bb07b 100644 --- a/node/service/src/rpc/mod.rs +++ b/node/service/src/rpc/mod.rs @@ -101,6 +101,7 @@ where + 'static, C::Api: substrate_frame_rpc_system::AccountNonceApi, C::Api: pallet_gear_rpc::GearRuntimeApi, + C::Api: pallet_gear_staking_rewards_rpc::GearStakingRewardsRuntimeApi, C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi, C::Api: BabeApi, C::Api: BlockBuilder, @@ -110,6 +111,7 @@ where B::State: sc_client_api::backend::StateBackend>, { use pallet_gear_rpc::{Gear, GearApiServer}; + use pallet_gear_staking_rewards_rpc::{GearStakingRewards, GearStakingRewardsApiServer}; use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApiServer}; use runtime_info::{RuntimeInfoApi, RuntimeInfoServer}; use sc_consensus_babe_rpc::{Babe, BabeApiServer}; @@ -192,7 +194,9 @@ where io.merge(Gear::new(client.clone()).into_rpc())?; - io.merge(RuntimeInfoApi::::new(client).into_rpc())?; + io.merge(RuntimeInfoApi::::new(client.clone()).into_rpc())?; + + io.merge(GearStakingRewards::new(client).into_rpc())?; Ok(io) } diff --git a/pallets/staking-rewards/rpc/Cargo.toml b/pallets/staking-rewards/rpc/Cargo.toml new file mode 100644 index 00000000000..9d1240ed403 --- /dev/null +++ b/pallets/staking-rewards/rpc/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pallet-gear-staking-rewards-rpc" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage = "https://gear-tech.io" +repository = "https://github.com/gear-tech/gear" + +[dependencies] +jsonrpsee = { workspace = true, features = ["server", "macros"] } + +# Substrate packages +sp-api.workspace = true +sp-blockchain.workspace = true +sp-runtime.workspace = true + +# Local packages +pallet-gear-staking-rewards-rpc-runtime-api.workspace = true diff --git a/pallets/staking-rewards/rpc/runtime-api/Cargo.toml b/pallets/staking-rewards/rpc/runtime-api/Cargo.toml new file mode 100644 index 00000000000..5c9d8cb65e9 --- /dev/null +++ b/pallets/staking-rewards/rpc/runtime-api/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "pallet-gear-staking-rewards-rpc-runtime-api" +version = "1.0.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage = "https://gear-tech.io" +repository = "https://github.com/gear-tech/gear" + +[dependencies] +sp-api.workspace = true +pallet-gear-staking-rewards.workspace = true + +[features] +default = ["std"] +std = [ + "sp-api/std", + "pallet-gear-staking-rewards/std", +] diff --git a/pallets/staking-rewards/rpc/runtime-api/src/lib.rs b/pallets/staking-rewards/rpc/runtime-api/src/lib.rs new file mode 100644 index 00000000000..04aebbf65da --- /dev/null +++ b/pallets/staking-rewards/rpc/runtime-api/src/lib.rs @@ -0,0 +1,28 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet_gear_staking_rewards::InflationInfo; + +sp_api::decl_runtime_apis! { + pub trait GearStakingRewardsApi { + /// Calculate token economics related data. + fn inflation_info() -> InflationInfo; + } +} diff --git a/pallets/staking-rewards/rpc/src/lib.rs b/pallets/staking-rewards/rpc/src/lib.rs new file mode 100644 index 00000000000..45267e0bfd9 --- /dev/null +++ b/pallets/staking-rewards/rpc/src/lib.rs @@ -0,0 +1,96 @@ +// This file is part of Gear. + +// Copyright (C) 2021-2023 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +//! RPC interface for the gear module. + +use jsonrpsee::{ + core::{Error as JsonRpseeError, RpcResult}, + proc_macros::rpc, + types::error::{CallError, ErrorObject}, +}; +pub use pallet_gear_staking_rewards_rpc_runtime_api::GearStakingRewardsApi as GearStakingRewardsRuntimeApi; +use pallet_gear_staking_rewards_rpc_runtime_api::InflationInfo; +use sp_api::ProvideRuntimeApi; +use sp_blockchain::HeaderBackend; +use sp_runtime::traits::Block as BlockT; +use std::sync::Arc; + +#[rpc(server)] +pub trait GearStakingRewardsApi { + #[method(name = "stakingRewards_inflationInfo")] + fn query_inflation_info(&self, at: Option) -> RpcResult; +} + +/// Provides RPC methods to query token economics related data. +pub struct GearStakingRewards { + /// Shared reference to the client. + client: Arc, + _marker: std::marker::PhantomData

, +} + +impl GearStakingRewards { + /// Creates a new instance of the GearStakingRewards Rpc helper. + pub fn new(client: Arc) -> Self { + Self { + client, + _marker: Default::default(), + } + } +} + +/// Error type of this RPC api. +pub enum Error { + /// The transaction was not decodable. + DecodeError, + /// The call to runtime failed. + RuntimeError, +} + +impl From for i32 { + fn from(e: Error) -> i32 { + match e { + Error::RuntimeError => 1, + Error::DecodeError => 2, + } + } +} + +impl GearStakingRewardsApiServer<::Hash, InflationInfo> + for GearStakingRewards +where + Block: BlockT, + C: 'static + ProvideRuntimeApi + HeaderBackend, + C::Api: GearStakingRewardsRuntimeApi, +{ + fn query_inflation_info(&self, at: Option) -> RpcResult { + let api = self.client.runtime_api(); + let at_hash = at.unwrap_or_else(|| self.client.info().best_hash); + + fn map_err(err: impl std::fmt::Debug, desc: &'static str) -> JsonRpseeError { + CallError::Custom(ErrorObject::owned( + Error::RuntimeError.into(), + desc, + Some(format!("{err:?}")), + )) + .into() + } + + api.inflation_info(at_hash) + .map_err(|e| map_err(e, "Unable to query inflation info")) + } +} diff --git a/pallets/staking-rewards/src/lib.rs b/pallets/staking-rewards/src/lib.rs index a379e014e8b..039a0ee60df 100644 --- a/pallets/staking-rewards/src/lib.rs +++ b/pallets/staking-rewards/src/lib.rs @@ -59,6 +59,8 @@ use frame_support::{ PalletId, }; use pallet_staking::EraPayout; +use parity_scale_codec::{Decode, Encode}; +pub use scale_info::TypeInfo; use sp_runtime::{ traits::{AccountIdConversion, Saturating, StaticLookup}, PerThing, Perquintill, @@ -79,6 +81,16 @@ pub type NegativeImbalanceOf = <::Currency as Cu >>::NegativeImbalance; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +/// Token economics related details. +#[derive(Clone, Decode, Encode, Eq, PartialEq, TypeInfo)] +#[cfg_attr(feature = "std", derive(Debug, serde::Deserialize, serde::Serialize))] +pub struct InflationInfo { + /// Inflation + pub inflation: Perquintill, + /// ROI + pub roi: Perquintill, +} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -333,11 +345,39 @@ pub mod pallet { .saturating_sub(T::Currency::minimum_balance()) } + /// Return the current total stakeable tokens amount. + /// + /// This value is not calculated but rather updated manually in line with tokenomics model. pub fn total_stakeable_tokens() -> BalanceOf { // Should never be 0 but in theory could (Self::non_stakeable_share().left_from_one() * T::Currency::total_issuance()) .saturating_sub(Self::pool()) } + + /// Calculate actual infaltion and ROI parameters. + pub fn inflation_info() -> InflationInfo { + let total_staked = pallet_staking::Pallet::::eras_total_stake( + pallet_staking::Pallet::::current_era().unwrap_or(0), + ); + let total_issuance = T::Currency::total_issuance(); + + let (payout, _) = inflation::compute_total_payout( + total_staked, + Self::total_stakeable_tokens(), + total_issuance, + Self::ideal_staking_ratio(), + T::MinInflation::get(), + Self::target_inflation(), + T::Falloff::get(), + T::MaxROI::get(), + Perquintill::one(), + ); + + let inflation = Perquintill::from_rational(payout, total_issuance); + let roi = Perquintill::from_rational(payout, total_staked); + + InflationInfo { inflation, roi } + } } } diff --git a/runtime/gear/Cargo.toml b/runtime/gear/Cargo.toml index fdd6f2deb3a..c68acce6ee8 100644 --- a/runtime/gear/Cargo.toml +++ b/runtime/gear/Cargo.toml @@ -70,6 +70,8 @@ pallet-gear-payment.workspace = true pallet-gear-voucher.workspace = true pallet-gear-rpc-runtime-api.workspace = true runtime-primitives.workspace = true +pallet-gear-staking-rewards.workspace = true +pallet-gear-staking-rewards-rpc-runtime-api.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true @@ -126,6 +128,8 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "pallet-gear-staking-rewards-rpc-runtime-api/std", + "pallet-gear-staking-rewards/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", diff --git a/runtime/gear/src/lib.rs b/runtime/gear/src/lib.rs index 6094556ebd9..da93e6fce77 100644 --- a/runtime/gear/src/lib.rs +++ b/runtime/gear/src/lib.rs @@ -806,4 +806,10 @@ impl_runtime_apis_plus_common! { Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() } } + + impl pallet_gear_staking_rewards_rpc_runtime_api::GearStakingRewardsApi for Runtime { + fn inflation_info() -> pallet_gear_staking_rewards::InflationInfo { + unimplemented!(); + } + } } diff --git a/runtime/vara/Cargo.toml b/runtime/vara/Cargo.toml index f179c2bd346..40e450428e2 100644 --- a/runtime/vara/Cargo.toml +++ b/runtime/vara/Cargo.toml @@ -97,6 +97,7 @@ pallet-gear-payment.workspace = true pallet-gear-staking-rewards.workspace = true pallet-gear-voucher.workspace = true pallet-gear-rpc-runtime-api.workspace = true +pallet-gear-staking-rewards-rpc-runtime-api.workspace = true runtime-primitives.workspace = true [dev-dependencies] @@ -143,6 +144,7 @@ std = [ "pallet-gear-program/std", "pallet-gear-staking-rewards/std", "pallet-gear-rpc-runtime-api/std", + "pallet-gear-staking-rewards-rpc-runtime-api/std", "pallet-grandpa/std", "pallet-identity/std", "pallet-im-online/std", diff --git a/runtime/vara/src/lib.rs b/runtime/vara/src/lib.rs index fee12da3048..f0446824539 100644 --- a/runtime/vara/src/lib.rs +++ b/runtime/vara/src/lib.rs @@ -1382,4 +1382,10 @@ impl_runtime_apis_plus_common! { Executive::try_execute_block(block, state_root_check, signature_check, select).unwrap() } } + + impl pallet_gear_staking_rewards_rpc_runtime_api::GearStakingRewardsApi for Runtime { + fn inflation_info() -> pallet_gear_staking_rewards::InflationInfo { + StakingRewards::inflation_info() + } + } } From 65a7177a83556af48a0b50baeed16d4171f859de Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Mon, 18 Sep 2023 21:09:13 +0400 Subject: [PATCH 4/6] fix(builds): Pass `dev` feature (#3298) --- .github/workflows/build.yml | 2 +- Makefile | 4 ++-- scripts/calc-gas-spent.sh | 2 +- scripts/ci_build.sh | 4 ++-- scripts/read-state.sh | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7dc8cfc4c84..7c913d062b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -158,7 +158,7 @@ jobs: - name: "Build: Production binaries" if: github.event_name == 'push' - run: cargo build -p gear-cli -F cli --profile production + run: cargo build -p gear-cli -F dev,cli --profile production - name: Prepare artifacts if: github.event_name == 'push' diff --git a/Makefile b/Makefile index 23d3f966ed4..223516d7f46 100644 --- a/Makefile +++ b/Makefile @@ -175,11 +175,11 @@ purge-chain-release: .PHONY: purge-dev-chain purge-dev-chain: - @ ./scripts/gear.sh run purge-dev-chain + @ ./scripts/gear.sh run purge-dev-chain -F dev .PHONY: purge-dev-chain-release purge-dev-chain-release: - @ ./scripts/gear.sh run purge-dev-chain --release + @ ./scripts/gear.sh run purge-dev-chain --release -F dev # Test section .PHONY: test # \ diff --git a/scripts/calc-gas-spent.sh b/scripts/calc-gas-spent.sh index 7f2a67a5adb..342cc89791f 100755 --- a/scripts/calc-gas-spent.sh +++ b/scripts/calc-gas-spent.sh @@ -2,7 +2,7 @@ # # Prerequisites: # -# RUST_LOG=gwasm=debug,pallet_gear=debug cargo run -p gear-cli --release -- --dev --tmp -l0 +# RUST_LOG=gwasm=debug,pallet_gear=debug cargo run -p gear-cli --release -F dev -- --dev --tmp -l0 # # Then upload the PING program and copy it's ID. # URL: https://github.com/gear-tech/apps/releases/download/build/demo_ping.opt.wasm diff --git a/scripts/ci_build.sh b/scripts/ci_build.sh index 2ddaff402ab..628a9490a6e 100755 --- a/scripts/ci_build.sh +++ b/scripts/ci_build.sh @@ -42,7 +42,7 @@ echo "Test: gsdk tests" ./scripts/gear.sh test gsdk --release echo "Test: Runtime benchmarks and benchmark tests work" - cargo build -p gear-cli --release --features=runtime-benchmarks,runtime-benchmarks-checkers + cargo build -p gear-cli --release --features=dev,runtime-benchmarks,runtime-benchmarks-checkers ./target/release/gear benchmark pallet --chain=dev --pallet=pallet_gear --steps=20 --extrinsic="*" --execution=wasm --wasm-execution=compiled --heap-pages=4096 ./target/release/gear benchmark pallet --chain=dev --pallet=pallet_gear --extrinsic="check_all" --execution=wasm --wasm-execution=compiled --heap-pages=4096 --extra ./target/release/gear benchmark pallet --chain=dev --pallet=pallet_gear --extrinsic="check_lazy_pages_all" --execution=native --heap-pages=4096 --extra @@ -54,5 +54,5 @@ echo "Test: `try-runtime` feature tests" cargo test -p "pallet-*" --features try-runtime --release --locked echo "Test: Try runtime migrations" - cargo build -p gear-cli --features try-runtime --release --locked + cargo build -p gear-cli --features dev,try-runtime --release --locked ./target/release/gear try-runtime --runtime ./target/release/wbuild/gear-runtime/gear_runtime.wasm on-runtime-upgrade --checks live --uri wss://rpc-private-testnet.gear-tech.io:443 diff --git a/scripts/read-state.sh b/scripts/read-state.sh index f94795e116a..af24d82d4ef 100755 --- a/scripts/read-state.sh +++ b/scripts/read-state.sh @@ -2,7 +2,7 @@ # # Prerequisites: # -# RUST_LOG=gwasm=debug,pallet_gear=debug cargo run -p gear-cli --release -- --dev --tmp -l0 +# RUST_LOG=gwasm=debug,pallet_gear=debug cargo run -p gear-cli --release -F dev -- --dev --tmp -l0 # # Usage: # From 012c1eb8bbfd7f2f30b8129bd153305d374fb0ad Mon Sep 17 00:00:00 2001 From: Arsenii Lyashenko Date: Mon, 18 Sep 2023 21:16:34 +0300 Subject: [PATCH 5/6] chore!: Stabilize `lazy-pages` feature (#3123) --- .github/workflows/CI.yaml | 1 - .github/workflows/build-macos.yml | 5 +- .github/workflows/build.yml | 6 +- .github/workflows/test-measurements.yaml | 4 +- Cargo.lock | 8 +- Makefile | 8 +- core-processor/Cargo.toml | 6 +- core-processor/src/common.rs | 2 - core-processor/src/executor.rs | 283 +------------- core-processor/src/ext.rs | 364 ++++++----------- core-processor/src/processing.rs | 6 +- core/Cargo.toml | 1 + core/src/memory.rs | 118 ++++++ docker/Vara-Dockerfile | 2 +- examples/new-meta/tests/read_state.rs | 6 +- gcli/Cargo.toml | 4 +- gcli/src/meta/mod.rs | 35 +- gtest/Cargo.toml | 5 +- gtest/src/manager.rs | 68 ++-- gtest/src/program.rs | 10 +- gtest/src/system.rs | 5 + node/cli/Cargo.toml | 7 +- node/service/Cargo.toml | 4 - pallets/gear-debug/Cargo.toml | 1 - pallets/gear-debug/src/tests/mod.rs | 12 +- pallets/gear-scheduler/Cargo.toml | 2 +- pallets/gear/Cargo.toml | 6 +- pallets/gear/src/benchmarking/mod.rs | 24 +- .../gear/src/benchmarking/tests/lazy_pages.rs | 5 - pallets/gear/src/benchmarking/tests/mod.rs | 2 - pallets/gear/src/benchmarking/utils.rs | 13 +- pallets/gear/src/ext.rs | 367 ------------------ pallets/gear/src/lib.rs | 57 +-- pallets/gear/src/manager/journal.rs | 2 + pallets/gear/src/queue.rs | 18 +- pallets/gear/src/runtime_api.rs | 48 +-- pallets/gear/src/tests.rs | 18 +- pallets/payment/Cargo.toml | 1 - runtime/common/Cargo.toml | 1 + runtime/gear/Cargo.toml | 5 - runtime/vara/Cargo.toml | 5 - scripts/ci_build.sh | 7 +- utils/runtime-fuzzer/Cargo.toml | 2 +- utils/wasm-gen/Cargo.toml | 3 +- utils/wasm-gen/src/tests.rs | 21 +- 45 files changed, 428 insertions(+), 1150 deletions(-) delete mode 100644 pallets/gear/src/ext.rs diff --git a/.github/workflows/CI.yaml b/.github/workflows/CI.yaml index 9256c5ee13e..01a50f5f55a 100644 --- a/.github/workflows/CI.yaml +++ b/.github/workflows/CI.yaml @@ -139,7 +139,6 @@ jobs: -p "pallet-*" -p gear-lazy-pages -p gear-runtime-interface - --features=lazy-pages --release upload: diff --git a/.github/workflows/build-macos.yml b/.github/workflows/build-macos.yml index a4a3a9a2cb9..1337b6fc61c 100644 --- a/.github/workflows/build-macos.yml +++ b/.github/workflows/build-macos.yml @@ -43,9 +43,7 @@ jobs: run: curl -LsSf https://get.nexte.st/latest/mac | tar zxf - -C ${CARGO_HOME:-~/.cargo}/bin - name: "Build: Node" - run: >- - cargo build - -p gear-cli --features=lazy-pages + run: cargo build -p gear-cli - name: "Test: Lazy pages" run: >- @@ -53,4 +51,3 @@ jobs: -p "pallet-*" -p gear-lazy-pages -p gear-runtime-interface - --features=lazy-pages diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7c913d062b7..fc674d05f4b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -77,11 +77,8 @@ jobs: - name: "Check: Vara runtime imports" run: ./target/release/wasm-proc --check-runtime-imports target/release/wbuild/vara-runtime/vara_runtime.compact.wasm - - name: "Test: Gear pallet tests with lazy pages" - run: ./scripts/gear.sh test pallet --features lazy-pages --release --locked - - name: "Test: Gear workspace" - run: ./scripts/gear.sh test gear --exclude gclient --exclude gcli --exclude gsdk --features pallet-gear-debug/lazy-pages --release --locked + run: ./scripts/gear.sh test gear --exclude gclient --exclude gcli --exclude gsdk --release --locked - name: "Test: gsdk tests" run: ./scripts/gear.sh test gsdk --release @@ -252,7 +249,6 @@ jobs: -p "pallet-*" -p gear-lazy-pages -p gear-runtime-interface - --features=lazy-pages --release env: CARGO_BUILD_TARGET: x86_64-pc-windows-msvc diff --git a/.github/workflows/test-measurements.yaml b/.github/workflows/test-measurements.yaml index b9c3a16a657..fca0c365b9e 100644 --- a/.github/workflows/test-measurements.yaml +++ b/.github/workflows/test-measurements.yaml @@ -42,13 +42,13 @@ jobs: tar -xf /cache/check_cargo_registry_${{ github.ref_name }}.tar -C / - name: "Build: Gear" - run: ./scripts/gear.sh build gear --release --locked --features=dev,runtime-benchmarks,lazy-pages + run: ./scripts/gear.sh build gear --release --locked --features=dev,runtime-benchmarks - name: "Collect: Gear workspace tests" run: | mkdir -p ./target/analysis/tests/ mkdir -p ./target/analysis/output/ - for i in `seq 1 $COUNT`; do echo $i; ./scripts/gear.sh test gear --exclude gclient --exclude gcli --features pallet-gear-debug/lazy-pages --release -j1 > ./target/analysis/output/$i 2>&1 ; mv ./target/nextest/ci/junit.xml ./target/analysis/tests/$i; done + for i in `seq 1 $COUNT`; do echo $i; ./scripts/gear.sh test gear --exclude gclient --exclude gcli --release -j1 > ./target/analysis/output/$i 2>&1 ; mv ./target/nextest/ci/junit.xml ./target/analysis/tests/$i; done ./target/release/regression-analysis collect-data --data-folder-path ./target/analysis/tests/ --output-path ./target/pallet-tests.json - name: "Generate report: Gear workspace tests" diff --git a/Cargo.lock b/Cargo.lock index 00d28e6f667..3041ea8580e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3656,6 +3656,7 @@ dependencies = [ "gear-core", "gear-core-errors", "gear-core-processor", + "gear-lazy-pages-common", "gmeta", "gsdk", "hex", @@ -3671,6 +3672,7 @@ dependencies = [ "schnorrkel", "serde", "serde_json", + "sp-io 7.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", "thiserror", "tokio", "which", @@ -3931,6 +3933,7 @@ dependencies = [ "log", "parity-scale-codec", "paste", + "proptest", "scale-info", "static_assertions", "wabt", @@ -3957,9 +3960,9 @@ dependencies = [ "gear-backend-common", "gear-core", "gear-core-errors", + "gear-lazy-pages-common", "gear-wasm-instrument", "log", - "proptest", "scale-info", "static_assertions", ] @@ -4427,6 +4430,7 @@ dependencies = [ "gear-backend-sandbox", "gear-core", "gear-core-processor", + "gear-lazy-pages-common", "gear-utils", "gear-wasm-instrument", "gsys", @@ -4761,6 +4765,7 @@ dependencies = [ "gear-core", "gear-core-errors", "gear-core-processor", + "gear-lazy-pages-common", "gear-utils", "gear-wasm-builder", "gear-wasm-instrument", @@ -4769,6 +4774,7 @@ dependencies = [ "parity-scale-codec", "path-clean", "rand 0.8.5", + "sp-io 7.0.0 (git+https://github.com/gear-tech/substrate.git?branch=gear-polkadot-v0.9.41-canary-no-sandbox)", ] [[package]] diff --git a/Makefile b/Makefile index 223516d7f46..0e6ee45d5ed 100644 --- a/Makefile +++ b/Makefile @@ -54,11 +54,11 @@ node-release: .PHONY: vara vara: - @ ./scripts/gear.sh build node --no-default-features --features=vara-native,lazy-pages + @ ./scripts/gear.sh build node --no-default-features --features=vara-native .PHONY: vara-release vara-release: - @ ./scripts/gear.sh build node --release --no-default-features --features=vara-native,lazy-pages + @ ./scripts/gear.sh build node --release --no-default-features --features=vara-native .PHONY: gear-replay gear-replay: @@ -197,13 +197,13 @@ test-doc: test-gear: #\ We use lazy-pages feature for pallet-gear-debug due to cargo building issue \ and fact that pallet-gear default is lazy-pages. - @ ./scripts/gear.sh test gear --exclude gclient --exclude gcli --exclude gsdk --features pallet-gear-debug/lazy-pages + @ ./scripts/gear.sh test gear --exclude gclient --exclude gcli --exclude gsdk .PHONY: test-gear-release test-gear-release: # \ We use lazy-pages feature for pallet-gear-debug due to cargo building issue \ and fact that pallet-gear default is lazy-pages. - @ ./scripts/gear.sh test gear --release --exclude gclient --exclude gcli --exclude gsdk --features pallet-gear-debug/lazy-pages + @ ./scripts/gear.sh test gear --release --exclude gclient --exclude gcli --exclude gsdk .PHONY: test-gsdk test-gsdk: node-release diff --git a/core-processor/Cargo.toml b/core-processor/Cargo.toml index 7230e40a335..cc07d9088ee 100644 --- a/core-processor/Cargo.toml +++ b/core-processor/Cargo.toml @@ -15,6 +15,7 @@ gear-core.workspace = true gear-core-errors = { workspace = true, features = ["codec"] } gear-backend-common.workspace = true gear-wasm-instrument.workspace = true +gear-lazy-pages-common.workspace = true scale-info = { workspace = true, features = ["derive"] } log.workspace = true @@ -23,10 +24,11 @@ static_assertions.workspace = true actor-system-error.workspace = true [dev-dependencies] -proptest.workspace = true env_logger.workspace = true enum-iterator.workspace = true [features] +default = ["std"] +std = ["gear-lazy-pages-common/std"] strict = [] -mock = [] \ No newline at end of file +mock = [] diff --git a/core-processor/src/common.rs b/core-processor/src/common.rs index 65d7df3f45f..16d0f842f07 100644 --- a/core-processor/src/common.rs +++ b/core-processor/src/common.rs @@ -545,8 +545,6 @@ pub struct WasmExecutionContext { pub gas_reserver: GasReserver, /// Program to be executed. pub program: Program, - /// Memory pages with initial data. - pub pages_initial_data: BTreeMap, /// Size of the memory block. pub memory_size: WasmPage, } diff --git a/core-processor/src/executor.rs b/core-processor/src/executor.rs index c98778ff592..c1fa182a76e 100644 --- a/core-processor/src/executor.rs +++ b/core-processor/src/executor.rs @@ -26,7 +26,7 @@ use crate::{ }; use actor_system_error::actor_system_error; use alloc::{ - collections::{BTreeMap, BTreeSet}, + collections::BTreeSet, format, string::{String, ToString}, vec::Vec, @@ -41,12 +41,12 @@ use gear_core::{ env::Externalities, gas::{CountersOwner, GasAllowanceCounter, GasCounter, ValueCounter}, ids::ProgramId, - memory::{AllocationsContext, Memory, MemoryError, PageBuf}, + memory::{AllocationsContext, Memory}, message::{ ContextSettings, DispatchKind, IncomingDispatch, IncomingMessage, MessageContext, WasmEntryPoint, }, - pages::{GearPage, PageU32Size, WasmPage}, + pages::{PageU32Size, WasmPage}, program::Program, reservation::GasReserver, }; @@ -67,9 +67,6 @@ pub enum ActorPrepareMemoryError { /// Stack end page, which value is specified in WASM code, cannot be bigger than static memory size. #[display(fmt = "Stack end page {_0:?} is bigger then WASM static memory size {_1:?}")] StackEndPageBiggerWasmMemSize(WasmPage, WasmPage), - /// It's not allowed to set initial data for stack memory pages, if they are specified in WASM code. - #[display(fmt = "Set initial data for stack pages is restricted")] - StackPagesHaveInitialData, /// Stack is not aligned to WASM page size #[display(fmt = "Stack end addr {_0:#x} must be aligned to WASM page size")] StackIsNotAligned(u32), @@ -80,24 +77,10 @@ pub enum SystemPrepareMemoryError { /// Mem size less then static pages num #[display(fmt = "Mem size less then static pages num")] InsufficientMemorySize, - /// Page with data is not allocated for program - #[display(fmt = "{_0:?} is not allocated for program")] - PageIsNotAllocated(GearPage), - /// Cannot read initial memory data from wasm memory. - #[display(fmt = "Cannot read data for {_0:?}: {_1}")] - InitialMemoryReadFailed(GearPage, MemoryError), - /// Cannot write initial data to wasm memory. - #[display(fmt = "Cannot write initial data for {_0:?}: {_1}")] - InitialDataWriteFailed(GearPage, MemoryError), - /// Initial pages data must be empty in lazy pages mode - #[display(fmt = "Initial pages data must be empty when execute with lazy pages")] - InitialPagesContainsDataInLazyPagesMode, } /// Make checks that everything with memory goes well. -fn check_memory<'a>( - allocations: &BTreeSet, - pages_with_data: impl Iterator, +fn check_memory( static_pages: WasmPage, memory_size: WasmPage, ) -> Result<(), SystemPrepareMemoryError> { @@ -110,31 +93,13 @@ fn check_memory<'a>( return Err(SystemPrepareMemoryError::InsufficientMemorySize); } - // Checks that all pages with data are in allocations set. - for page in pages_with_data { - let wasm_page = page.to_page(); - if wasm_page >= static_pages && !allocations.contains(&wasm_page) { - return Err(SystemPrepareMemoryError::PageIsNotAllocated(*page)); - } - } - Ok(()) } -fn lazy_pages_check_initial_data( - initial_pages_data: &BTreeMap, -) -> Result<(), SystemPrepareMemoryError> { - initial_pages_data - .is_empty() - .then_some(()) - .ok_or(SystemPrepareMemoryError::InitialPagesContainsDataInLazyPagesMode) -} - /// Writes initial pages data to memory and prepare memory for execution. fn prepare_memory( mem: &mut EnvMem, program_id: ProgramId, - pages_data: &mut BTreeMap, static_pages: WasmPage, stack_end: Option, globals_config: GlobalsAccessConfig, @@ -158,94 +123,17 @@ fn prepare_memory( None }; - // Set initial data for pages - for (page, data) in pages_data.iter_mut() { - mem.write(page.offset(), data) - .map_err(|err| SystemPrepareMemoryError::InitialDataWriteFailed(*page, err))?; - } - - if ProcessorExt::LAZY_PAGES_ENABLED { - lazy_pages_check_initial_data(pages_data)?; - - ProcessorExt::lazy_pages_init_for_program( - mem, - program_id, - stack_end, - globals_config, - lazy_pages_weights, - ); - } else { - // If we executes without lazy pages, then we have to save all initial data for static pages, - // in order to be able to identify pages, which has been changed during execution. - // Skip stack page if they are specified. - let begin = stack_end.unwrap_or_default(); - - if pages_data.keys().any(|&p| p < begin.to_page()) { - return Err(ActorPrepareMemoryError::StackPagesHaveInitialData.into()); - } + ProcessorExt::lazy_pages_init_for_program( + mem, + program_id, + stack_end, + globals_config, + lazy_pages_weights, + ); - let non_stack_pages = begin.iter_end(static_pages).unwrap_or_else(|err| { - unreachable!( - "We have already checked that `stack_end` is <= `static_pages`, but get: {}", - err - ) - }); - for page in non_stack_pages.flat_map(|p| p.to_pages_iter()) { - if pages_data.contains_key(&page) { - // This page already has initial data - continue; - } - let mut data = PageBuf::new_zeroed(); - mem.read(page.offset(), &mut data) - .map_err(|err| SystemPrepareMemoryError::InitialMemoryReadFailed(page, err))?; - pages_data.insert(page, data); - } - } Ok(()) } -/// Returns pages and their new data, which must be updated or uploaded to storage. -fn get_pages_to_be_updated( - old_pages_data: BTreeMap, - new_pages_data: BTreeMap, - static_pages: WasmPage, -) -> BTreeMap { - if ProcessorExt::LAZY_PAGES_ENABLED { - // In lazy pages mode we update some page data in storage, - // when it has been write accessed, so no need to compare old and new page data. - new_pages_data.keys().for_each(|page| { - log::trace!("{:?} has been write accessed, update it in storage", page) - }); - return new_pages_data; - } - - let mut page_update = BTreeMap::new(); - let mut old_pages_data = old_pages_data; - let static_gear_pages = static_pages.to_page(); - for (page, new_data) in new_pages_data { - let initial_data = if let Some(initial_data) = old_pages_data.remove(&page) { - initial_data - } else { - // If it's static page without initial data, - // then it's stack page and we skip this page update. - if page < static_gear_pages { - continue; - } - - // If page has no data in `pages_initial_data` then data is zeros. - // Because it's default data for wasm pages which is not static, - // and for all static pages we save data in `pages_initial_data` in E::new. - PageBuf::new_zeroed() - }; - - if new_data != initial_data { - page_update.insert(page, new_data); - log::trace!("{page:?} has been changed - will be updated in storage"); - } - } - page_update -} - /// Execute wasm with dispatch and return dispatch result. pub fn execute_wasm( balance: u128, @@ -264,7 +152,6 @@ where gas_allowance_counter, gas_reserver, program, - mut pages_initial_data, memory_size, } = context; @@ -277,13 +164,7 @@ where let static_pages = program.static_pages(); let allocations = program.allocations(); - check_memory( - allocations, - pages_initial_data.keys(), - static_pages, - memory_size, - ) - .map_err(SystemExecutionError::PrepareMemory)?; + check_memory(static_pages, memory_size).map_err(SystemExecutionError::PrepareMemory)?; // Creating allocations context. let allocations_context = @@ -353,7 +234,6 @@ where prepare_memory::( memory, program_id, - &mut pages_initial_data, static_pages, stack_end, globals_config, @@ -377,12 +257,10 @@ where }; // released pages initial data will be added to `pages_initial_data` after execution. - if E::Ext::LAZY_PAGES_ENABLED { - E::Ext::lazy_pages_post_execution_actions(&mut memory); + E::Ext::lazy_pages_post_execution_actions(&mut memory); - if !E::Ext::lazy_pages_status().is_normal() { - termination = ext.current_counter_type().into() - } + if !E::Ext::lazy_pages_status().is_normal() { + termination = ext.current_counter_type().into() } (termination, memory, ext) @@ -416,11 +294,6 @@ where .into_ext_info(&memory) .map_err(SystemExecutionError::IntoExtInfo)?; - if E::Ext::LAZY_PAGES_ENABLED { - lazy_pages_check_initial_data(&pages_initial_data) - .map_err(SystemExecutionError::PrepareMemory)?; - } - // Parsing outcome. let kind = match termination { ActorTerminationReason::Exit(value_dest) => DispatchResultKind::Exit(value_dest), @@ -437,8 +310,9 @@ where ActorTerminationReason::GasAllowanceExceeded => DispatchResultKind::GasAllowanceExceed, }; - let page_update = - get_pages_to_be_updated::(pages_initial_data, info.pages_data, static_pages); + // With lazy-pages we update some page data in storage, + // when it has been write accessed, so no need to compare old and new page data. + let page_update = info.pages_data; // Getting new programs that are scheduled to be initialized (respected messages are in `generated_dispatches` collection) let program_candidates = info.program_candidates_data; @@ -467,7 +341,6 @@ where pub fn execute_for_reply( function: EP, instrumented_code: InstrumentedCode, - pages_initial_data: Option>, allocations: Option>, program_id: Option, payload: Vec, @@ -481,8 +354,6 @@ where EP: WasmEntryPoint, { let program = Program::new(program_id.unwrap_or_default(), instrumented_code); - let mut pages_initial_data: BTreeMap = - pages_initial_data.unwrap_or_default(); let static_pages = program.static_pages(); let allocations = allocations.unwrap_or_else(|| program.allocations().clone()); @@ -558,7 +429,6 @@ where prepare_memory::( memory, program.id(), - &mut pages_initial_data, static_pages, stack_end, globals_config, @@ -623,18 +493,13 @@ where #[cfg(test)] mod tests { use super::*; - use alloc::vec::Vec; use gear_backend_common::lazy_pages::Status; - use gear_core::{ - memory::PageBufInner, - pages::{PageNumber, WasmPage}, - }; + use gear_core::pages::WasmPage; struct TestExt; struct LazyTestExt; impl ProcessorExternalities for TestExt { - const LAZY_PAGES_ENABLED: bool = false; fn new(_context: ProcessorContext) -> Self { Self } @@ -655,8 +520,6 @@ mod tests { } impl ProcessorExternalities for LazyTestExt { - const LAZY_PAGES_ENABLED: bool = true; - fn new(_context: ProcessorContext) -> Self { Self } @@ -676,118 +539,14 @@ mod tests { } } - fn prepare_pages_and_allocs() -> (Vec, BTreeSet) { - let data = [0u16, 1, 2, 8, 18, 25, 27, 28, 93, 146, 240, 518]; - let pages = data.map(Into::into); - (pages.to_vec(), pages.map(|p| p.to_page()).into()) - } - - fn prepare_pages() -> BTreeMap { - let mut pages = BTreeMap::new(); - for i in 0..=255 { - let buffer = PageBufInner::filled_with(i); - pages.insert((i as u16).into(), PageBuf::from_inner(buffer)); - } - pages - } - #[test] fn check_memory_insufficient() { - let res = check_memory(&[].into(), [].iter(), 8.into(), 4.into()); + let res = check_memory(8.into(), 4.into()); assert_eq!(res, Err(SystemPrepareMemoryError::InsufficientMemorySize)); } - #[test] - fn check_memory_not_allocated() { - let (pages, mut allocs) = prepare_pages_and_allocs(); - let last = *allocs.iter().last().unwrap(); - allocs.remove(&last); - let res = check_memory(&allocs, pages.iter(), 2.into(), 4.into()); - assert_eq!( - res, - Err(SystemPrepareMemoryError::PageIsNotAllocated( - *pages.last().unwrap() - )) - ); - } - #[test] fn check_memory_ok() { - let (pages, allocs) = prepare_pages_and_allocs(); - check_memory(&allocs, pages.iter(), 4.into(), 8.into()).unwrap(); - } - - #[test] - fn lazy_pages_to_update() { - let new_pages = prepare_pages(); - let res = - get_pages_to_be_updated::(Default::default(), new_pages.clone(), 0.into()); - // All touched pages are to be updated in lazy mode - assert_eq!(res, new_pages); - } - - #[test] - fn no_pages_to_update() { - let old_pages = prepare_pages(); - let mut new_pages = old_pages.clone(); - let static_pages = 4; - let res = - get_pages_to_be_updated::(old_pages, new_pages.clone(), static_pages.into()); - assert_eq!(res, Default::default()); - - // Change static pages - for i in 0..static_pages { - let buffer = PageBufInner::filled_with(42); - new_pages.insert(i.into(), PageBuf::from_inner(buffer)); - } - // Do not include non-static pages - let new_pages = new_pages - .into_iter() - .take(WasmPage::from(static_pages).to_page::().raw() as _) - .collect(); - let res = - get_pages_to_be_updated::(Default::default(), new_pages, static_pages.into()); - assert_eq!(res, Default::default()); - } - - #[test] - fn pages_to_update() { - let old_pages = prepare_pages(); - let mut new_pages = old_pages.clone(); - - let page_with_zero_data = WasmPage::from(30).to_page(); - let changes: BTreeMap = [ - ( - WasmPage::from(1).to_page(), - PageBuf::from_inner(PageBufInner::filled_with(42u8)), - ), - ( - WasmPage::from(5).to_page(), - PageBuf::from_inner(PageBufInner::filled_with(84u8)), - ), - (page_with_zero_data, PageBuf::new_zeroed()), - ] - .into_iter() - .collect(); - new_pages.extend(changes.clone().into_iter()); - - // Change pages - let static_pages = 4.into(); - let res = get_pages_to_be_updated::(old_pages, new_pages.clone(), static_pages); - assert_eq!(res, changes); - - // There was no any old page - let res = - get_pages_to_be_updated::(Default::default(), new_pages.clone(), static_pages); - - // The result is all pages except the static ones - for page in static_pages.to_page::().iter_from_zero() { - new_pages.remove(&page); - } - - // Remove page with zero data, because it must not be updated. - new_pages.remove(&page_with_zero_data); - - assert_eq!(res, new_pages); + check_memory(4.into(), 8.into()).unwrap(); } } diff --git a/core-processor/src/ext.rs b/core-processor/src/ext.rs index aa872cd3d9b..7d8098dd92e 100644 --- a/core-processor/src/ext.rs +++ b/core-processor/src/ext.rs @@ -39,20 +39,20 @@ use gear_core::{ }, ids::{CodeId, MessageId, ProgramId, ReservationId}, memory::{ - AllocError, AllocationsContext, GrowHandler, Memory, MemoryError, MemoryInterval, - NoopGrowHandler, PageBuf, + AllocError, AllocationsContext, GrowHandler, Memory, MemoryError, MemoryInterval, PageBuf, }, message::{ ContextOutcomeDrain, GasLimit, HandlePacket, InitPacket, MessageContext, Packet, ReplyPacket, }, - pages::{GearPage, PageU32Size, WasmPage}, + pages::{PageU32Size, WasmPage}, reservation::GasReserver, }; use gear_core_errors::{ ExecutionError as FallibleExecutionError, ExtError as FallibleExtErrorCore, MessageError, ProgramRentError, ReplyCode, ReservationError, SignalCode, }; +use gear_lazy_pages_common as lazy_pages; use gear_wasm_instrument::syscalls::SysCallName; /// Processor context. @@ -155,9 +155,6 @@ impl ProcessorContext { /// Trait to which ext must have to work in processor wasm executor. /// Currently used only for lazy-pages support. pub trait ProcessorExternalities { - /// Whether this extension works with lazy pages. - const LAZY_PAGES_ENABLED: bool; - /// Create new fn new(context: ProcessorContext) -> Self; @@ -286,6 +283,39 @@ impl BackendAllocSyscallError for AllocExtError { } } +struct LazyGrowHandler { + old_mem_addr: Option, + old_mem_size: WasmPage, +} + +impl GrowHandler for LazyGrowHandler { + fn before_grow_action(mem: &mut impl Memory) -> Self { + // New pages allocation may change wasm memory buffer location. + // So we remove protections from lazy-pages + // and then in `after_grow_action` we set protection back for new wasm memory buffer. + let old_mem_addr = mem.get_buffer_host_addr(); + lazy_pages::remove_lazy_pages_prot(mem); + Self { + old_mem_addr, + old_mem_size: mem.size(), + } + } + + fn after_grow_action(self, mem: &mut impl Memory) { + // Add new allocations to lazy pages. + // Protect all lazy pages including new allocations. + let new_mem_addr = mem.get_buffer_host_addr().unwrap_or_else(|| { + unreachable!("Memory size cannot be zero after grow is applied for memory") + }); + lazy_pages::update_lazy_pages_and_protect_again( + mem, + self.old_mem_addr, + self.old_mem_size, + new_mem_addr, + ); + } +} + /// Structure providing externalities for running host functions. pub struct Ext { /// Processor context. @@ -300,8 +330,6 @@ pub struct Ext { /// Empty implementation for non-substrate (and non-lazy-pages) using impl ProcessorExternalities for Ext { - const LAZY_PAGES_ENABLED: bool = false; - fn new(context: ProcessorContext) -> Self { let current_counter = if context.gas_counter.left() <= context.gas_allowance_counter.left() { @@ -318,36 +346,87 @@ impl ProcessorExternalities for Ext { } fn lazy_pages_init_for_program( - _mem: &mut impl Memory, - _prog_id: ProgramId, - _stack_end: Option, - _globals_config: GlobalsAccessConfig, - _lazy_pages_weights: LazyPagesWeights, + mem: &mut impl Memory, + prog_id: ProgramId, + stack_end: Option, + globals_config: GlobalsAccessConfig, + lazy_pages_weights: LazyPagesWeights, ) { - unreachable!("Must not be called: lazy-pages is unsupported by this ext") + lazy_pages::init_for_program(mem, prog_id, stack_end, globals_config, lazy_pages_weights); } - fn lazy_pages_post_execution_actions(_mem: &mut impl Memory) { - unreachable!("Must not be called: lazy-pages is unsupported by this ext") + fn lazy_pages_post_execution_actions(mem: &mut impl Memory) { + lazy_pages::remove_lazy_pages_prot(mem); } fn lazy_pages_status() -> Status { - unreachable!("Must not be called: lazy-pages is unsupported by this ext") + lazy_pages::get_status() } } impl BackendExternalities for Ext { fn into_ext_info(self, memory: &impl Memory) -> Result { - let pages_for_data = - |static_pages: WasmPage, allocations: &BTreeSet| -> Vec { - static_pages - .iter_from_zero() - .chain(allocations.iter().copied()) - .flat_map(|p| p.to_pages_iter()) - .collect() - }; + let ProcessorContext { + allocations_context, + message_context, + gas_counter, + gas_reserver, + system_reservation, + program_candidates_data, + program_rents, + .. + } = self.context; + + let (static_pages, initial_allocations, allocations) = allocations_context.into_parts(); + + // Accessed pages are all pages, that had been released and are in allocations set or static. + let mut accessed_pages = lazy_pages::get_write_accessed_pages(); + accessed_pages.retain(|p| { + let wasm_page = p.to_page(); + wasm_page < static_pages || allocations.contains(&wasm_page) + }); + log::trace!("accessed pages numbers = {:?}", accessed_pages); + + let mut pages_data = BTreeMap::new(); + for page in accessed_pages { + let mut buf = PageBuf::new_zeroed(); + memory.read(page.offset(), &mut buf)?; + pages_data.insert(page, buf); + } + + let (outcome, mut context_store) = message_context.drain(); + let ContextOutcomeDrain { + outgoing_dispatches: generated_dispatches, + awakening, + reply_deposits, + } = outcome.drain(); + + let system_reservation_context = SystemReservationContext { + current_reservation: system_reservation, + previous_reservation: context_store.system_reservation(), + }; - self.into_ext_info_inner(memory, pages_for_data) + context_store.set_reservation_nonce(&gas_reserver); + if let Some(reservation) = system_reservation { + context_store.add_system_reservation(reservation); + } + + let info = ExtInfo { + gas_amount: gas_counter.to_amount(), + gas_reserver, + system_reservation_context, + allocations: (allocations != initial_allocations) + .then_some(allocations) + .unwrap_or_default(), + pages_data, + generated_dispatches, + awakening, + reply_deposits, + context_store, + program_candidates_data, + program_rents, + }; + Ok(info) } fn gas_amount(&self) -> GasAmount { @@ -355,11 +434,11 @@ impl BackendExternalities for Ext { } fn pre_process_memory_accesses( - _reads: &[MemoryInterval], - _writes: &[MemoryInterval], - _gas_counter: &mut u64, + reads: &[MemoryInterval], + writes: &[MemoryInterval], + gas_counter: &mut u64, ) -> Result<(), ProcessAccessError> { - Ok(()) + lazy_pages::pre_process_memory_accesses(reads, writes, gas_counter) } } @@ -591,7 +670,18 @@ impl Externalities for Ext { pages_num: u32, mem: &mut impl Memory, ) -> Result { - self.alloc_inner::(pages_num, mem) + let pages = WasmPage::new(pages_num).map_err(|_| AllocError::ProgramAllocOutOfBounds)?; + + self.context + .allocations_context + .alloc::(pages, mem, |pages| { + Ext::charge_gas_if_enough( + &mut self.context.gas_counter, + &mut self.context.gas_allowance_counter, + self.context.page_costs.mem_grow.calc(pages), + ) + }) + .map_err(Into::into) } fn free(&mut self, page: WasmPage) -> Result<(), Self::AllocError> { @@ -1031,89 +1121,6 @@ impl Externalities for Ext { } } -impl Ext { - /// Inner alloc realization. - pub fn alloc_inner( - &mut self, - pages_num: u32, - mem: &mut impl Memory, - ) -> Result { - let pages = WasmPage::new(pages_num).map_err(|_| AllocError::ProgramAllocOutOfBounds)?; - - self.context - .allocations_context - .alloc::(pages, mem, |pages| { - Ext::charge_gas_if_enough( - &mut self.context.gas_counter, - &mut self.context.gas_allowance_counter, - self.context.page_costs.mem_grow.calc(pages), - ) - }) - .map_err(Into::into) - } - - /// Into ext info inner impl. - /// `pages_for_data` returns vector of pages which data will be stored in info. - pub fn into_ext_info_inner( - self, - memory: &impl Memory, - pages_for_data: impl FnOnce(WasmPage, &BTreeSet) -> Vec, - ) -> Result { - let ProcessorContext { - allocations_context, - message_context, - gas_counter, - gas_reserver, - system_reservation, - program_candidates_data, - program_rents, - .. - } = self.context; - - let (static_pages, initial_allocations, allocations) = allocations_context.into_parts(); - let mut pages_data = BTreeMap::new(); - for page in pages_for_data(static_pages, &allocations) { - let mut buf = PageBuf::new_zeroed(); - memory.read(page.offset(), &mut buf)?; - pages_data.insert(page, buf); - } - - let (outcome, mut context_store) = message_context.drain(); - let ContextOutcomeDrain { - outgoing_dispatches: generated_dispatches, - awakening, - reply_deposits, - } = outcome.drain(); - - let system_reservation_context = SystemReservationContext { - current_reservation: system_reservation, - previous_reservation: context_store.system_reservation(), - }; - - context_store.set_reservation_nonce(&gas_reserver); - if let Some(reservation) = system_reservation { - context_store.add_system_reservation(reservation); - } - - let info = ExtInfo { - gas_amount: gas_counter.to_amount(), - gas_reserver, - system_reservation_context, - allocations: (allocations != initial_allocations) - .then_some(allocations) - .unwrap_or_default(), - pages_data, - generated_dispatches, - awakening, - reply_deposits, - context_store, - program_candidates_data, - program_rents, - }; - Ok(info) - } -} - #[cfg(test)] mod tests { use super::*; @@ -1636,133 +1643,4 @@ mod tests { assert_eq!(dispatch.message().payload_bytes(), &[3, 4, 5]); } - - mod property_tests { - use super::*; - use gear_core::{ - memory::HostPointer, - pages::{PageError, PageNumber}, - }; - use proptest::{ - arbitrary::any, - collection::size_range, - prop_oneof, proptest, - strategy::{Just, Strategy}, - test_runner::Config as ProptestConfig, - }; - - struct TestMemory(WasmPage); - - impl Memory for TestMemory { - type GrowError = PageError; - - fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { - self.0 = self.0.add(pages)?; - Ok(()) - } - - fn size(&self) -> WasmPage { - self.0 - } - - fn write(&mut self, _offset: u32, _buffer: &[u8]) -> Result<(), MemoryError> { - unimplemented!() - } - - fn read(&self, _offset: u32, _buffer: &mut [u8]) -> Result<(), MemoryError> { - unimplemented!() - } - - unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { - unimplemented!() - } - } - - #[derive(Debug, Clone)] - enum Action { - Alloc { pages: WasmPage }, - Free { page: WasmPage }, - } - - fn actions() -> impl Strategy> { - let action = wasm_page_number().prop_flat_map(|page| { - prop_oneof![ - Just(Action::Alloc { pages: page }), - Just(Action::Free { page }) - ] - }); - proptest::collection::vec(action, 0..1024) - } - - fn allocations() -> impl Strategy> { - proptest::collection::btree_set(wasm_page_number(), size_range(0..1024)) - } - - fn wasm_page_number() -> impl Strategy { - any::().prop_map(WasmPage::from) - } - - fn proptest_config() -> ProptestConfig { - ProptestConfig { - cases: 1024, - ..Default::default() - } - } - - #[track_caller] - fn assert_alloc_error(err: ::AllocError) { - match err { - AllocExtError::Alloc( - AllocError::IncorrectAllocationData(_) | AllocError::ProgramAllocOutOfBounds, - ) => {} - err => Err(err).unwrap(), - } - } - - #[track_caller] - fn assert_free_error(err: ::AllocError) { - match err { - AllocExtError::Alloc(AllocError::InvalidFree(_)) => {} - err => Err(err).unwrap(), - } - } - - proptest! { - #![proptest_config(proptest_config())] - #[test] - fn alloc( - static_pages in wasm_page_number(), - allocations in allocations(), - max_pages in wasm_page_number(), - mem_size in wasm_page_number(), - actions in actions(), - ) { - let _ = env_logger::try_init(); - - let ctx = AllocationsContext::new(allocations, static_pages, max_pages); - let ctx = ProcessorContextBuilder::new() - .with_gas(GasCounter::new(u64::MAX)) - .with_allowance(GasAllowanceCounter::new(u64::MAX)) - .with_allocation_context(ctx) - .build(); - let mut ext = Ext::new(ctx); - let mut mem = TestMemory(mem_size); - - for action in actions { - match action { - Action::Alloc { pages } => { - if let Err(err) = ext.alloc(pages.raw(), &mut mem) { - assert_alloc_error(err); - } - } - Action::Free { page } => { - if let Err(err) = ext.free(page) { - assert_free_error(err); - } - }, - } - } - } - } - } } diff --git a/core-processor/src/processing.rs b/core-processor/src/processing.rs index 69c36f22cb4..094dc7117be 100644 --- a/core-processor/src/processing.rs +++ b/core-processor/src/processing.rs @@ -27,16 +27,14 @@ use crate::{ ext::ProcessorExternalities, precharge::SuccessfulDispatchResultKind, }; -use alloc::{collections::BTreeMap, string::ToString, vec::Vec}; +use alloc::{string::ToString, vec::Vec}; use gear_backend_common::{ BackendExternalities, BackendSyscallError, Environment, SystemReservationContext, }; use gear_core::{ env::Externalities, ids::{MessageId, ProgramId}, - memory::PageBuf, message::{ContextSettings, DispatchKind, IncomingDispatch, ReplyMessage, StoredDispatch}, - pages::GearPage, reservation::GasReservationState, }; use gear_core_errors::{ErrorReplyReason, SignalCode}; @@ -46,7 +44,6 @@ pub fn process( block_config: &BlockConfig, execution_context: ProcessExecutionContext, random_data: (Vec, u32), - memory_pages: BTreeMap, ) -> Result, SystemExecutionError> where E: Environment, @@ -97,7 +94,6 @@ where gas_allowance_counter: execution_context.gas_allowance_counter, gas_reserver: execution_context.gas_reserver, program: execution_context.program, - pages_initial_data: memory_pages, memory_size: execution_context.memory_size, }; diff --git a/core/Cargo.toml b/core/Cargo.toml index 2b5cb2e556e..666e400433f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -29,6 +29,7 @@ byteorder.workspace = true [dev-dependencies] wabt.workspace = true env_logger.workspace = true +proptest.workspace = true [features] strict = [] diff --git a/core/src/memory.rs b/core/src/memory.rs index 3d676e09fee..6b429b78dd7 100644 --- a/core/src/memory.rs +++ b/core/src/memory.rs @@ -497,4 +497,122 @@ mod tests { test(1238, 3498); test(0, 64444); } + + mod property_tests { + use super::*; + use crate::{memory::HostPointer, pages::PageError}; + use proptest::{ + arbitrary::any, + collection::size_range, + prop_oneof, proptest, + strategy::{Just, Strategy}, + test_runner::Config as ProptestConfig, + }; + + struct TestMemory(WasmPage); + + impl Memory for TestMemory { + type GrowError = PageError; + + fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { + self.0 = self.0.add(pages)?; + Ok(()) + } + + fn size(&self) -> WasmPage { + self.0 + } + + fn write(&mut self, _offset: u32, _buffer: &[u8]) -> Result<(), MemoryError> { + unimplemented!() + } + + fn read(&self, _offset: u32, _buffer: &mut [u8]) -> Result<(), MemoryError> { + unimplemented!() + } + + unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { + unimplemented!() + } + } + + #[derive(Debug, Clone)] + enum Action { + Alloc { pages: WasmPage }, + Free { page: WasmPage }, + } + + fn actions() -> impl Strategy> { + let action = wasm_page_number().prop_flat_map(|page| { + prop_oneof![ + Just(Action::Alloc { pages: page }), + Just(Action::Free { page }) + ] + }); + proptest::collection::vec(action, 0..1024) + } + + fn allocations() -> impl Strategy> { + proptest::collection::btree_set(wasm_page_number(), size_range(0..1024)) + } + + fn wasm_page_number() -> impl Strategy { + any::().prop_map(WasmPage::from) + } + + fn proptest_config() -> ProptestConfig { + ProptestConfig { + cases: 1024, + ..Default::default() + } + } + + #[track_caller] + fn assert_alloc_error(err: AllocError) { + match err { + AllocError::IncorrectAllocationData(_) | AllocError::ProgramAllocOutOfBounds => {} + err => Err(err).unwrap(), + } + } + + #[track_caller] + fn assert_free_error(err: AllocError) { + match err { + AllocError::InvalidFree(_) => {} + err => Err(err).unwrap(), + } + } + + proptest! { + #![proptest_config(proptest_config())] + #[test] + fn alloc( + static_pages in wasm_page_number(), + allocations in allocations(), + max_pages in wasm_page_number(), + mem_size in wasm_page_number(), + actions in actions(), + ) { + let _ = env_logger::try_init(); + + let mut ctx = AllocationsContext::new(allocations, static_pages, max_pages); + let mut mem = TestMemory(mem_size); + + for action in actions { + match action { + Action::Alloc { pages } => { + if let Err(err) = ctx.alloc::(pages, &mut mem, |_| Ok(())) { + assert_alloc_error(err); + } + } + Action::Free { page } => { + if let Err(err) = ctx.free(page) { + assert_free_error(err); + } + } + } + } + } + } + } } diff --git a/docker/Vara-Dockerfile b/docker/Vara-Dockerfile index 0744a6a0cb2..4abe70f29fc 100644 --- a/docker/Vara-Dockerfile +++ b/docker/Vara-Dockerfile @@ -33,7 +33,7 @@ RUN rustc --version RUN rustup update nightly && rustup target add wasm32-unknown-unknown --toolchain nightly # Build -RUN cargo build -p gear-cli --no-default-features --features=vara-native,lazy-pages --profile $PROFILE +RUN cargo build -p gear-cli --no-default-features --features=vara-native --profile $PROFILE # ===== SECOND STAGE ====== diff --git a/examples/new-meta/tests/read_state.rs b/examples/new-meta/tests/read_state.rs index d285ac014f8..7ee30cc09bb 100644 --- a/examples/new-meta/tests/read_state.rs +++ b/examples/new-meta/tests/read_state.rs @@ -106,7 +106,7 @@ fn read_state_with_wasm_func_returns_transformed_state() { const FUNC_NAME: &str = "first_wallet"; assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); - let actual_state = program + let actual_state: Option = program .read_state_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args!()) .expect("Unable to read program state"); @@ -126,7 +126,7 @@ fn read_state_with_parameterized_wasm_func_returns_transformed_state() { name: "OtherName".into(), }; - let actual_state = program + let actual_state: Option = program .read_state_using_wasm( FUNC_NAME, META_WASM_V2.to_vec(), @@ -151,7 +151,7 @@ fn read_state_with_two_args_wasm_func_returns_transformed_state() { let name = "OtherName".to_string(); let surname = "OtherSurname".to_string(); - let actual_state = program + let actual_state: Option = program .read_state_using_wasm( FUNC_NAME, META_WASM_V2.to_vec(), diff --git a/gcli/Cargo.toml b/gcli/Cargo.toml index f676cbac66d..9d9821b9bf3 100644 --- a/gcli/Cargo.toml +++ b/gcli/Cargo.toml @@ -40,10 +40,12 @@ clap = { workspace = true, features = ["derive"] } thiserror.workspace = true tokio = { workspace = true, features = [ "full" ] } whoami.workspace = true -core-processor.workspace = true +core-processor = { workspace = true, features = [ "std" ] } gear-backend-sandbox = { workspace = true, features = [ "std" ] } +gear-lazy-pages-common = { workspace = true, features = [ "std" ] } reqwest = { workspace = true, default-features = false, features = [ "json", "rustls-tls" ] } etc.workspace = true +sp-io = { workspace = true, features = [ "std" ] } [dev-dependencies] rand.workspace = true diff --git a/gcli/src/meta/mod.rs b/gcli/src/meta/mod.rs index fe8b3772c83..059d0fac8bc 100644 --- a/gcli/src/meta/mod.rs +++ b/gcli/src/meta/mod.rs @@ -73,6 +73,8 @@ pub enum Meta { } impl Meta { + const PAGE_STORAGE_PREFIX: [u8; 32] = *b"gcligcligcligcligcligcligcligcli"; + fn format_metadata(meta: &MetadataRepr, fmt: &mut fmt::Formatter) -> fmt::Result { let registry = PortableRegistry::decode(&mut meta.registry.as_ref()).map_err(|_| fmt::Error)?; @@ -107,20 +109,25 @@ impl Meta { /// Execute meta method. fn execute(wasm: InstrumentedCode, method: &str) -> Result> { - core_processor::informational::execute_for_reply::< - gear_backend_sandbox::SandboxEnvironment, - String, - >( - method.into(), - wasm, - None, - None, - None, - Default::default(), - u64::MAX, - BlockInfo::default(), - ) - .map_err(Error::WasmExecution) + assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + Self::PAGE_STORAGE_PREFIX + )); + + sp_io::TestExternalities::default().execute_with(|| { + core_processor::informational::execute_for_reply::< + gear_backend_sandbox::SandboxEnvironment, + String, + >( + method.into(), + wasm, + None, + None, + Default::default(), + u64::MAX, + BlockInfo::default(), + ) + .map_err(Error::WasmExecution) + }) } /// Decode metawasm from wasm binary. diff --git a/gtest/Cargo.toml b/gtest/Cargo.toml index 55886642194..fc1ab748a54 100644 --- a/gtest/Cargo.toml +++ b/gtest/Cargo.toml @@ -10,10 +10,13 @@ gear-core.workspace = true gear-core-errors.workspace = true gear-backend-common.workspace = true gear-backend-sandbox = { workspace = true, features = ["std"] } -core-processor.workspace = true +core-processor = { workspace = true, features = ["std"] } +gear-lazy-pages-common = { workspace = true, features = ["std"] } gear-wasm-builder.workspace = true gear-utils.workspace = true +sp-io = { workspace = true, features = ["std"] } + anyhow.workspace = true codec = { workspace = true, features = ["derive"] } hex.workspace = true diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 23a5d0cb964..54a41e9bd16 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -19,7 +19,7 @@ use crate::{ log::{CoreLog, RunResult}, program::{Gas, WasmProgram}, - Result, TestError, DISPATCH_HOLD_COST, EPOCH_DURATION_IN_BLOCKS, EXISTENTIAL_DEPOSIT, + Result, System, TestError, DISPATCH_HOLD_COST, EPOCH_DURATION_IN_BLOCKS, EXISTENTIAL_DEPOSIT, INITIAL_RANDOM_SEED, MAILBOX_THRESHOLD, MAX_RESERVATIONS, MODULE_INSTANTIATION_BYTE_COST, MODULE_INSTRUMENTATION_BYTE_COST, MODULE_INSTRUMENTATION_COST, READ_COST, READ_PER_BYTE_COST, RENT_COST, RESERVATION_COST, RESERVE_FOR, WAITLIST_COST, WRITE_COST, WRITE_PER_BYTE_COST, @@ -45,9 +45,12 @@ use gear_core::{ use gear_core_errors::{ErrorReplyReason, SignalCode, SimpleExecutionError}; use gear_wasm_instrument::wasm_instrument::gas_metering::ConstantCostRules; use rand::{rngs::StdRng, RngCore, SeedableRng}; +use sp_io::TestExternalities; use std::{ + cell::RefCell, collections::{BTreeMap, BTreeSet, HashMap, VecDeque}, convert::TryInto, + rc::Rc, time::{SystemTime, UNIX_EPOCH}, }; @@ -117,13 +120,7 @@ impl TestActor { } // Gets a new executable actor derived from the inner program. - fn get_executable_actor_data( - &self, - ) -> Option<( - ExecutableActorData, - CoreProgram, - BTreeMap, - )> { + fn get_executable_actor_data(&self) -> Option<(ExecutableActorData, CoreProgram)> { let (program, pages_data, code_id, gas_reservation_map) = match self { TestActor::Initialized(Program::Genuine { program, @@ -161,7 +158,6 @@ impl TestActor { gas_reservation_map, }, program, - pages_data, )) } } @@ -219,6 +215,8 @@ pub(crate) struct ExtManager { pub(crate) gas_limits: BTreeMap, pub(crate) delayed_dispatches: HashMap>, + pub(crate) externalities: Rc>, + // Last run info pub(crate) origin: ProgramId, pub(crate) msg_id: MessageId, @@ -325,6 +323,29 @@ impl ExtManager { } } + pub(crate) fn with_externalities(&mut self, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + let externalities = self.externalities.clone(); + let mut externalities = externalities.borrow_mut(); + externalities.execute_with(|| f(self)) + } + + fn update_storage_pages(program_id: ProgramId, memory_pages: &BTreeMap) { + // write pages into storage so lazy-pages can access them + for (page, buf) in memory_pages { + let page_no: u32 = (*page).into(); + let prefix = [ + System::PAGE_STORAGE_PREFIX.as_slice(), + program_id.into_bytes().as_slice(), + page_no.to_le_bytes().as_slice(), + ] + .concat(); + sp_io::storage::set(&prefix, buf); + } + } + #[track_caller] fn validate_dispatch(&mut self, dispatch: &Dispatch) { if 0 < dispatch.value() && dispatch.value() < crate::EXISTENTIAL_DEPOSIT { @@ -362,7 +383,7 @@ impl ExtManager { pub(crate) fn validate_and_run_dispatch(&mut self, dispatch: Dispatch) -> RunResult { self.validate_dispatch(&dispatch); - self.run_dispatch(dispatch) + self.with_externalities(|this| this.run_dispatch(dispatch)) } #[track_caller] @@ -409,14 +430,8 @@ impl ExtManager { if actor.is_dormant() { self.process_dormant(balance, dispatch); - } else if let Some((data, program, memory_pages)) = actor.get_executable_actor_data() { - self.process_normal( - balance, - data, - program.code().clone(), - memory_pages, - dispatch, - ); + } else if let Some((data, program)) = actor.get_executable_actor_data() { + self.process_normal(balance, data, program.code().clone(), dispatch); } else if let Some(mock) = actor.take_mock() { self.process_mock(mock, dispatch); } else { @@ -447,11 +462,10 @@ impl ExtManager { .get_mut(program_id) .ok_or_else(|| TestError::ActorNotFound(*program_id))?; - if let Some((_, program, memory_pages)) = actor.get_executable_actor_data() { + if let Some((_, program)) = actor.get_executable_actor_data() { core_processor::informational::execute_for_reply::, _>( String::from("state"), program.code().clone(), - Some(memory_pages), Some(program.allocations().clone()), Some(*program_id), Default::default(), @@ -494,7 +508,6 @@ impl ExtManager { mapping_code, None, None, - None, mapping_code_payload, u64::MAX, self.block_info, @@ -592,9 +605,11 @@ impl ExtManager { }; match program { - Program::Genuine { pages_data, .. } => *pages_data = memory_pages, + Program::Genuine { pages_data, .. } => *pages_data = memory_pages.clone(), Program::Mock(_) => panic!("Can't read memory of mock program"), } + + self.with_externalities(|_this| Self::update_storage_pages(*program_id, &memory_pages)) } #[track_caller] @@ -775,14 +790,13 @@ impl ExtManager { balance: u128, data: ExecutableActorData, code: InstrumentedCode, - memory_pages: BTreeMap, dispatch: StoredDispatch, ) { - self.process_dispatch(balance, Some((data, code)), memory_pages, dispatch); + self.process_dispatch(balance, Some((data, code)), dispatch); } fn process_dormant(&mut self, balance: u128, dispatch: StoredDispatch) { - self.process_dispatch(balance, None, Default::default(), dispatch); + self.process_dispatch(balance, None, dispatch); } #[track_caller] @@ -790,7 +804,6 @@ impl ExtManager { &mut self, balance: u128, data: Option<(ExecutableActorData, InstrumentedCode)>, - memory_pages: BTreeMap, dispatch: StoredDispatch, ) { let dest = dispatch.destination(); @@ -868,7 +881,6 @@ impl ExtManager { &block_config, (context, code, balance).into(), self.random_data.clone(), - memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic violated: {}", e)); @@ -1001,6 +1013,8 @@ impl JournalHandler for ExtManager { .expect("Can't find existing program"); if let Some(actor_pages_data) = actor.get_pages_data_mut() { + Self::update_storage_pages(program_id, &pages_data); + actor_pages_data.append(&mut pages_data); } else { unreachable!("No pages data found for program") diff --git a/gtest/src/program.rs b/gtest/src/program.rs index cc0c4e5b426..dca23cfb66a 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -447,7 +447,9 @@ impl<'a> Program<'a> { /// Reads the program’s state as a byte vector. pub fn read_state_bytes(&self) -> Result> { - self.manager.borrow_mut().read_state_bytes(&self.id) + self.manager + .borrow_mut() + .with_externalities(|this| this.read_state_bytes(&self.id)) } /// Reads the program’s transformed state as a byte vector. The transformed @@ -502,9 +504,9 @@ impl<'a> Program<'a> { wasm: Vec, args: Option>, ) -> Result> { - self.manager - .borrow_mut() - .read_state_bytes_using_wasm(&self.id, fn_name, wasm, args) + self.manager.borrow_mut().with_externalities(|this| { + this.read_state_bytes_using_wasm(&self.id, fn_name, wasm, args) + }) } /// Reads and decodes the program's state . diff --git a/gtest/src/system.rs b/gtest/src/system.rs index 81462312b43..b4e3c3a70b1 100644 --- a/gtest/src/system.rs +++ b/gtest/src/system.rs @@ -37,8 +37,13 @@ impl Default for System { } impl System { + pub(crate) const PAGE_STORAGE_PREFIX: [u8; 32] = *b"gtestgtestgtestgtestgtestgtest00"; + /// Create a new system. pub fn new() -> Self { + assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + Self::PAGE_STORAGE_PREFIX + )); Default::default() } diff --git a/node/cli/Cargo.toml b/node/cli/Cargo.toml index 02fe1c8c9d2..5a52ca5e042 100644 --- a/node/cli/Cargo.toml +++ b/node/cli/Cargo.toml @@ -63,7 +63,7 @@ gcli = { workspace = true, optional = true } substrate-build-script-utils.workspace = true [features] -default = ["gear-native", "vara-native", "lazy-pages"] +default = ["gear-native", "vara-native"] gear-native = [ "service/gear-native", "gear-runtime", @@ -73,11 +73,6 @@ vara-native = [ "vara-runtime", "pallet-gear-staking-rewards", ] -lazy-pages = [ - "service/lazy-pages", - "vara-runtime?/lazy-pages", - "gear-runtime?/lazy-pages", -] runtime-benchmarks = [ "service/runtime-benchmarks", "frame-benchmarking", diff --git a/node/service/Cargo.toml b/node/service/Cargo.toml index 707bd61836a..c78f087ddbf 100644 --- a/node/service/Cargo.toml +++ b/node/service/Cargo.toml @@ -111,10 +111,6 @@ vara-native = [ "pallet-im-online", "sp-authority-discovery" ] -lazy-pages = [ - "gear-runtime?/lazy-pages", - "vara-runtime?/lazy-pages", -] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-benchmarking-cli/runtime-benchmarks", diff --git a/pallets/gear-debug/Cargo.toml b/pallets/gear-debug/Cargo.toml index af5b894aecf..f139f859414 100644 --- a/pallets/gear-debug/Cargo.toml +++ b/pallets/gear-debug/Cargo.toml @@ -75,5 +75,4 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", ] -lazy-pages = ["pallet-gear/lazy-pages"] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/gear-debug/src/tests/mod.rs b/pallets/gear-debug/src/tests/mod.rs index 2008ae84895..683853ff36c 100644 --- a/pallets/gear-debug/src/tests/mod.rs +++ b/pallets/gear-debug/src/tests/mod.rs @@ -25,13 +25,11 @@ use common::{ ActiveProgram, CodeStorage, Origin as _, PausedProgramStorage, ProgramStorage, }; use frame_support::{assert_err, assert_ok}; -#[cfg(feature = "lazy-pages")] -use gear_core::pages::GearPage; use gear_core::{ ids::{CodeId, MessageId, ProgramId}, memory::PageBuf, message::{DispatchKind, StoredDispatch, StoredMessage, UserMessage}, - pages::{PageNumber, PageU32Size, WasmPage}, + pages::{GearPage, PageNumber, PageU32Size, WasmPage}, }; use gear_wasm_instrument::STACK_END_EXPORT_NAME; use pallet_gear::{ @@ -393,7 +391,6 @@ fn get_last_snapshot() -> DebugData { } } -#[cfg(feature = "lazy-pages")] #[test] fn check_not_allocated_pages() { // Currently we has no mechanism to restrict not allocated pages access during wasm execution @@ -595,7 +592,6 @@ fn check_not_allocated_pages() { }) } -#[cfg(feature = "lazy-pages")] #[test] fn check_changed_pages_in_storage() { // This test checks that only pages, which has been write accessed, @@ -903,12 +899,6 @@ fn check_gear_stack_end() { persistent_pages.insert(gear_page2, page_data.clone()); persistent_pages.insert(gear_page3, page_data); - #[cfg(feature = "lazy-pages")] - log::debug!("LAZY-PAGES IS ON"); - - #[cfg(not(feature = "lazy-pages"))] - log::debug!("LAZY-PAGES IS OFF"); - System::assert_last_event( crate::Event::DebugDataSnapshot(DebugData { dispatch_queue: vec![], diff --git a/pallets/gear-scheduler/Cargo.toml b/pallets/gear-scheduler/Cargo.toml index b8d9a29ccb6..d85e5827e3b 100644 --- a/pallets/gear-scheduler/Cargo.toml +++ b/pallets/gear-scheduler/Cargo.toml @@ -31,7 +31,7 @@ sp-std.workspace = true sp-io.workspace = true [dev-dependencies] -core-processor.workspace = true +core-processor = { workspace = true, features = ["std"] } pallet-gear-bank = { workspace = true, features = ["std"] } pallet-gear = { workspace = true, features = ["std"] } pallet-gear-messenger = { workspace = true, features = ["std"] } diff --git a/pallets/gear/Cargo.toml b/pallets/gear/Cargo.toml index ac6f96b0a64..cfb8ed21892 100644 --- a/pallets/gear/Cargo.toml +++ b/pallets/gear/Cargo.toml @@ -26,7 +26,7 @@ static_assertions.workspace = true # Internal deps common.workspace = true gear-runtime-interface = { workspace = true } -gear-lazy-pages-common = { workspace = true, optional = true } +gear-lazy-pages-common.workspace = true core-processor.workspace = true gear-core.workspace = true gear-core-errors.workspace = true @@ -136,7 +136,9 @@ std = [ "frame-system/std", "gear-wasm-instrument/std", "scopeguard/use_std", + "core-processor/std", "gear-backend-sandbox/std", + "gear-lazy-pages-common/std", "scale-info/std", "sp-io/std", "sp-std/std", @@ -154,7 +156,6 @@ std = [ "pallet-gear-proc-macro/full", "primitive-types/std", "serde/std", - "gear-lazy-pages-common?/std", "sp-consensus-babe/std", "test-syscalls?/std", "demo-read-big-state?/std", @@ -190,5 +191,4 @@ runtime-benchmarks = [ ] runtime-benchmarks-checkers = [] try-runtime = ["frame-support/try-runtime"] -lazy-pages = ["gear-lazy-pages-common"] fuzz = ["pallet-gear-gas/fuzz", "common/fuzz"] diff --git a/pallets/gear/src/benchmarking/mod.rs b/pallets/gear/src/benchmarking/mod.rs index 354379bfe16..d41682ab355 100644 --- a/pallets/gear/src/benchmarking/mod.rs +++ b/pallets/gear/src/benchmarking/mod.rs @@ -238,7 +238,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)) } @@ -369,7 +368,6 @@ pub struct Exec { block_config: BlockConfig, context: ProcessExecutionContext, random_data: (Vec, u32), - memory_pages: BTreeMap, } benchmarks! { @@ -388,12 +386,10 @@ benchmarks! { check_all { syscalls_integrity::main_test::(); tests::check_stack_overflow::(); - #[cfg(feature = "lazy-pages")] - { - tests::lazy_pages::lazy_pages_charging::(); - tests::lazy_pages::lazy_pages_charging_special::(); - tests::lazy_pages::lazy_pages_gas_exceed::(); - } + + tests::lazy_pages::lazy_pages_charging::(); + tests::lazy_pages::lazy_pages_charging_special::(); + tests::lazy_pages::lazy_pages_gas_exceed::(); } : {} #[extra] @@ -403,12 +399,9 @@ benchmarks! { #[extra] check_lazy_pages_all { - #[cfg(feature = "lazy-pages")] - { - tests::lazy_pages::lazy_pages_charging::(); - tests::lazy_pages::lazy_pages_charging_special::(); - tests::lazy_pages::lazy_pages_gas_exceed::(); - } + tests::lazy_pages::lazy_pages_charging::(); + tests::lazy_pages::lazy_pages_charging_special::(); + tests::lazy_pages::lazy_pages_gas_exceed::(); } : {} #[extra] @@ -418,19 +411,16 @@ benchmarks! { #[extra] check_lazy_pages_charging { - #[cfg(feature = "lazy-pages")] tests::lazy_pages::lazy_pages_charging::(); }: {} #[extra] check_lazy_pages_charging_special { - #[cfg(feature = "lazy-pages")] tests::lazy_pages::lazy_pages_charging_special::(); }: {} #[extra] check_lazy_pages_gas_exceed { - #[cfg(feature = "lazy-pages")] tests::lazy_pages::lazy_pages_gas_exceed::(); }: {} diff --git a/pallets/gear/src/benchmarking/tests/lazy_pages.rs b/pallets/gear/src/benchmarking/tests/lazy_pages.rs index 10e3513d270..94a92d7e6df 100644 --- a/pallets/gear/src/benchmarking/tests/lazy_pages.rs +++ b/pallets/gear/src/benchmarking/tests/lazy_pages.rs @@ -307,7 +307,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); @@ -384,7 +383,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); @@ -544,7 +542,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); @@ -588,7 +585,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); @@ -630,7 +626,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); diff --git a/pallets/gear/src/benchmarking/tests/mod.rs b/pallets/gear/src/benchmarking/tests/mod.rs index d163bcf2baf..e07981baff9 100644 --- a/pallets/gear/src/benchmarking/tests/mod.rs +++ b/pallets/gear/src/benchmarking/tests/mod.rs @@ -23,7 +23,6 @@ use super::*; -#[cfg(feature = "lazy-pages")] pub mod lazy_pages; pub mod syscalls_integrity; mod utils; @@ -76,7 +75,6 @@ where &exec.block_config, exec.context, exec.random_data, - exec.memory_pages, ) .unwrap() .into_iter() diff --git a/pallets/gear/src/benchmarking/utils.rs b/pallets/gear/src/benchmarking/utils.rs index 60992c73675..d04a116c50a 100644 --- a/pallets/gear/src/benchmarking/utils.rs +++ b/pallets/gear/src/benchmarking/utils.rs @@ -21,10 +21,10 @@ use super::Exec; use crate::{ manager::{CodeInfo, ExtManager, HandleKind}, - Config, CostsPerBlockOf, CurrencyOf, DbWeightOf, MailboxOf, Pallet as Gear, QueueOf, - RentCostPerBlockOf, + Config, CostsPerBlockOf, CurrencyOf, DbWeightOf, MailboxOf, Pallet as Gear, ProgramStorageOf, + QueueOf, RentCostPerBlockOf, }; -use common::{scheduler::SchedulingCostsPerBlock, storage::*, CodeStorage, Origin}; +use common::{scheduler::SchedulingCostsPerBlock, storage::*, CodeStorage, Origin, ProgramStorage}; use core_processor::{ configs::{BlockConfig, BlockInfo}, ContextChargedForCode, ContextChargedForInstrumentation, @@ -40,11 +40,6 @@ use sp_core::H256; use sp_runtime::traits::UniqueSaturatedInto; use sp_std::{convert::TryInto, prelude::*}; -#[cfg(feature = "lazy-pages")] -use crate::ProgramStorageOf; -#[cfg(feature = "lazy-pages")] -use common::ProgramStorage; - const DEFAULT_BLOCK_NUMBER: u32 = 0; const DEFAULT_INTERVAL: u32 = 1_000; @@ -119,7 +114,6 @@ where T: Config, T::AccountId: Origin, { - #[cfg(feature = "lazy-pages")] assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( ProgramStorageOf::::pages_final_prefix() )); @@ -295,6 +289,5 @@ where block_config, context: (context, code, balance).into(), random_data: (vec![0u8; 32], 0), - memory_pages: Default::default(), }) } diff --git a/pallets/gear/src/ext.rs b/pallets/gear/src/ext.rs deleted file mode 100644 index a278c23b06e..00000000000 --- a/pallets/gear/src/ext.rs +++ /dev/null @@ -1,367 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 2022-2023 Gear Technologies Inc. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use alloc::{collections::BTreeSet, vec::Vec}; -use core_processor::{ - AllocExtError, Ext, FallibleExtError, ProcessorContext, ProcessorExternalities, - UnrecoverableExtError, -}; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, LazyPagesWeights, Status}, - memory::ProcessAccessError, - BackendExternalities, ExtInfo, -}; -use gear_core::{ - costs::RuntimeCosts, - env::{Externalities, PayloadSliceLock, UnlockPayloadBound}, - gas::{ChargeError, CounterType, CountersOwner, GasAmount, GasLeft}, - ids::{MessageId, ProgramId, ReservationId}, - memory::{GrowHandler, Memory, MemoryError, MemoryInterval}, - message::{HandlePacket, InitPacket, ReplyPacket}, - pages::{GearPage, PageU32Size, WasmPage}, -}; -use gear_core_errors::{ReplyCode, SignalCode}; -use gear_lazy_pages_common as lazy_pages; -use gear_wasm_instrument::syscalls::SysCallName; - -/// Ext with lazy pages support. -pub struct LazyPagesExt { - inner: Ext, -} - -impl BackendExternalities for LazyPagesExt { - fn into_ext_info(self, memory: &impl Memory) -> Result { - let pages_for_data = - |static_pages: WasmPage, allocations: &BTreeSet| -> Vec { - // Accessed pages are all pages, that had been released and are in allocations set or static. - let mut accessed_pages = lazy_pages::get_write_accessed_pages(); - accessed_pages.retain(|p| { - let wasm_page = p.to_page(); - wasm_page < static_pages || allocations.contains(&wasm_page) - }); - log::trace!("accessed pages numbers = {:?}", accessed_pages); - accessed_pages - }; - self.inner.into_ext_info_inner(memory, pages_for_data) - } - - fn gas_amount(&self) -> GasAmount { - self.inner.context.gas_counter.to_amount() - } - - fn pre_process_memory_accesses( - reads: &[MemoryInterval], - writes: &[MemoryInterval], - gas_counter: &mut u64, - ) -> Result<(), ProcessAccessError> { - lazy_pages::pre_process_memory_accesses(reads, writes, gas_counter) - } -} - -impl ProcessorExternalities for LazyPagesExt { - const LAZY_PAGES_ENABLED: bool = true; - - fn new(context: ProcessorContext) -> Self { - Self { - inner: Ext::new(context), - } - } - - fn lazy_pages_init_for_program( - mem: &mut impl Memory, - prog_id: ProgramId, - stack_end: Option, - globals_config: GlobalsAccessConfig, - lazy_pages_weights: LazyPagesWeights, - ) { - lazy_pages::init_for_program(mem, prog_id, stack_end, globals_config, lazy_pages_weights); - } - - fn lazy_pages_post_execution_actions(mem: &mut impl Memory) { - lazy_pages::remove_lazy_pages_prot(mem); - } - - fn lazy_pages_status() -> Status { - lazy_pages::get_status() - } -} - -struct LazyGrowHandler { - old_mem_addr: Option, - old_mem_size: WasmPage, -} - -impl GrowHandler for LazyGrowHandler { - fn before_grow_action(mem: &mut impl Memory) -> Self { - // New pages allocation may change wasm memory buffer location. - // So we remove protections from lazy-pages - // and then in `after_grow_action` we set protection back for new wasm memory buffer. - let old_mem_addr = mem.get_buffer_host_addr(); - lazy_pages::remove_lazy_pages_prot(mem); - Self { - old_mem_addr, - old_mem_size: mem.size(), - } - } - - fn after_grow_action(self, mem: &mut impl Memory) { - // Add new allocations to lazy pages. - // Protect all lazy pages including new allocations. - let new_mem_addr = mem.get_buffer_host_addr().unwrap_or_else(|| { - unreachable!("Memory size cannot be zero after grow is applied for memory") - }); - lazy_pages::update_lazy_pages_and_protect_again( - mem, - self.old_mem_addr, - self.old_mem_size, - new_mem_addr, - ); - } -} - -impl CountersOwner for LazyPagesExt { - fn charge_gas_runtime(&mut self, cost: RuntimeCosts) -> Result<(), ChargeError> { - self.inner.charge_gas_runtime(cost) - } - - fn charge_gas_runtime_if_enough(&mut self, cost: RuntimeCosts) -> Result<(), ChargeError> { - self.inner.charge_gas_runtime_if_enough(cost) - } - - fn charge_gas_if_enough(&mut self, amount: u64) -> Result<(), ChargeError> { - self.inner.charge_gas_if_enough(amount) - } - - fn gas_left(&self) -> GasLeft { - self.inner.gas_left() - } - - fn current_counter_type(&self) -> CounterType { - self.inner.current_counter_type() - } - - fn decrease_current_counter_to(&mut self, amount: u64) { - self.inner.decrease_current_counter_to(amount) - } - - fn define_current_counter(&mut self) -> u64 { - self.inner.define_current_counter() - } -} - -impl Externalities for LazyPagesExt { - type UnrecoverableError = UnrecoverableExtError; - type FallibleError = FallibleExtError; - type AllocError = AllocExtError; - - fn alloc( - &mut self, - pages_num: u32, - mem: &mut impl Memory, - ) -> Result { - self.inner.alloc_inner::(pages_num, mem) - } - - fn free(&mut self, page: WasmPage) -> Result<(), Self::AllocError> { - self.inner.free(page) - } - - fn block_height(&self) -> Result { - self.inner.block_height() - } - - fn block_timestamp(&self) -> Result { - self.inner.block_timestamp() - } - - fn send_init(&mut self) -> Result { - self.inner.send_init() - } - - fn send_push(&mut self, handle: u32, buffer: &[u8]) -> Result<(), Self::FallibleError> { - self.inner.send_push(handle, buffer) - } - - fn send_push_input( - &mut self, - handle: u32, - offset: u32, - len: u32, - ) -> Result<(), Self::FallibleError> { - self.inner.send_push_input(handle, offset, len) - } - - fn reply_push(&mut self, buffer: &[u8]) -> Result<(), Self::FallibleError> { - self.inner.reply_push(buffer) - } - - fn send_commit( - &mut self, - handle: u32, - msg: HandlePacket, - delay: u32, - ) -> Result { - self.inner.send_commit(handle, msg, delay) - } - - fn reservation_send_commit( - &mut self, - id: ReservationId, - handle: u32, - msg: HandlePacket, - delay: u32, - ) -> Result { - self.inner.reservation_send_commit(id, handle, msg, delay) - } - - fn reply_commit(&mut self, msg: ReplyPacket) -> Result { - self.inner.reply_commit(msg) - } - - fn reservation_reply_commit( - &mut self, - id: ReservationId, - msg: ReplyPacket, - ) -> Result { - self.inner.reservation_reply_commit(id, msg) - } - - fn reply_to(&self) -> Result { - self.inner.reply_to() - } - - fn signal_from(&self) -> Result { - self.inner.signal_from() - } - - fn reply_push_input(&mut self, offset: u32, len: u32) -> Result<(), Self::FallibleError> { - self.inner.reply_push_input(offset, len) - } - - fn source(&self) -> Result { - self.inner.source() - } - - fn reply_code(&self) -> Result { - self.inner.reply_code() - } - - fn signal_code(&self) -> Result { - self.inner.signal_code() - } - - fn message_id(&self) -> Result { - self.inner.message_id() - } - - fn pay_program_rent( - &mut self, - program_id: ProgramId, - rent: u128, - ) -> Result<(u128, u32), Self::FallibleError> { - self.inner.pay_program_rent(program_id, rent) - } - - fn program_id(&self) -> Result { - self.inner.program_id() - } - - fn debug(&self, data: &str) -> Result<(), Self::UnrecoverableError> { - self.inner.debug(data) - } - - fn size(&self) -> Result { - self.inner.size() - } - - fn random(&self) -> Result<(&[u8], u32), Self::UnrecoverableError> { - self.inner.random() - } - - fn reserve_gas( - &mut self, - amount: u64, - blocks: u32, - ) -> Result { - self.inner.reserve_gas(amount, blocks) - } - - fn unreserve_gas(&mut self, id: ReservationId) -> Result { - self.inner.unreserve_gas(id) - } - - fn system_reserve_gas(&mut self, amount: u64) -> Result<(), Self::FallibleError> { - self.inner.system_reserve_gas(amount) - } - - fn gas_available(&self) -> Result { - self.inner.gas_available() - } - - fn value(&self) -> Result { - self.inner.value() - } - - fn wait(&mut self) -> Result<(), Self::UnrecoverableError> { - self.inner.wait() - } - - fn wait_for(&mut self, duration: u32) -> Result<(), Self::UnrecoverableError> { - self.inner.wait_for(duration) - } - - fn wait_up_to(&mut self, duration: u32) -> Result { - self.inner.wait_up_to(duration) - } - - fn wake(&mut self, waker_id: MessageId, delay: u32) -> Result<(), Self::FallibleError> { - self.inner.wake(waker_id, delay) - } - - fn value_available(&self) -> Result { - self.inner.value_available() - } - - fn create_program( - &mut self, - packet: InitPacket, - delay: u32, - ) -> Result<(MessageId, ProgramId), Self::FallibleError> { - self.inner.create_program(packet, delay) - } - - fn reply_deposit( - &mut self, - message_id: MessageId, - amount: u64, - ) -> Result<(), Self::FallibleError> { - self.inner.reply_deposit(message_id, amount) - } - - fn forbidden_funcs(&self) -> &BTreeSet { - &self.inner.context.forbidden_funcs - } - - fn lock_payload(&mut self, at: u32, len: u32) -> Result { - self.inner.lock_payload(at, len) - } - - fn unlock_payload(&mut self, payload_holder: &mut PayloadSliceLock) -> UnlockPayloadBound { - self.inner.unlock_payload(payload_holder) - } -} diff --git a/pallets/gear/src/lib.rs b/pallets/gear/src/lib.rs index 0db25c86d60..1363b7166a2 100644 --- a/pallets/gear/src/lib.rs +++ b/pallets/gear/src/lib.rs @@ -24,9 +24,6 @@ extern crate alloc; #[cfg(feature = "runtime-benchmarks")] mod benchmarking; -#[cfg(feature = "lazy-pages")] -mod ext; - mod internal; mod queue; mod runtime_api; @@ -58,6 +55,7 @@ use core::marker::PhantomData; use core_processor::{ common::{DispatchOutcome as CoreDispatchOutcome, ExecutableActorData, JournalNote}, configs::{BlockConfig, BlockInfo}, + Ext, }; use frame_support::{ dispatch::{DispatchError, DispatchResultWithPostInfo, PostDispatchInfo}, @@ -74,6 +72,7 @@ use gear_core::{ message::*, pages::{GearPage, WasmPage}, }; +use gear_lazy_pages_common as lazy_pages; use manager::{CodeInfo, QueuePostProcessingData}; use primitive_types::H256; use sp_runtime::{ @@ -86,15 +85,6 @@ use sp_std::{ prelude::*, }; -#[cfg(feature = "lazy-pages")] -use gear_lazy_pages_common as lazy_pages; - -#[cfg(feature = "lazy-pages")] -use ext::LazyPagesExt as Ext; - -#[cfg(not(feature = "lazy-pages"))] -use core_processor::Ext; - type ExecutionEnvironment = gear_backend_sandbox::SandboxEnvironment; pub(crate) type AccountIdOf = ::AccountId; @@ -1023,48 +1013,13 @@ pub mod pallet { } } - pub(crate) fn enable_lazy_pages() -> bool { - #[cfg(feature = "lazy-pages")] - { - let prefix = ProgramStorageOf::::pages_final_prefix(); - if !lazy_pages::try_to_enable_lazy_pages(prefix) { - unreachable!("By some reasons we cannot run lazy-pages on this machine"); - } - true - } - - #[cfg(not(feature = "lazy-pages"))] - { - false + pub(crate) fn enable_lazy_pages() { + let prefix = ProgramStorageOf::::pages_final_prefix(); + if !lazy_pages::try_to_enable_lazy_pages(prefix) { + unreachable!("By some reasons we cannot run lazy-pages on this machine"); } } - pub(crate) fn get_and_track_memory_pages( - manager: &mut ExtManager, - program_id: ProgramId, - pages_with_data: &BTreeSet, - lazy_pages_enabled: bool, - ) -> Option> { - let pages = if lazy_pages_enabled { - Default::default() - } else { - match ProgramStorageOf::::get_program_data_for_pages( - program_id, - pages_with_data.iter(), - ) { - Ok(data) => data, - Err(err) => { - log::error!("Cannot get data for program pages: {err:?}"); - return None; - } - } - }; - - manager.insert_program_id_loaded_pages(program_id); - - Some(pages) - } - pub(crate) fn block_config() -> BlockConfig { let block_info = BlockInfo { height: Self::block_number().unique_saturated_into(), diff --git a/pallets/gear/src/manager/journal.rs b/pallets/gear/src/manager/journal.rs index b60e65d147f..f5c4deed77d 100644 --- a/pallets/gear/src/manager/journal.rs +++ b/pallets/gear/src/manager/journal.rs @@ -333,6 +333,8 @@ where ProgramStorageOf::::update_active_program(program_id, |p| { for (page, data) in pages_data { + log::trace!("{:?} has been write accessed, update it in storage", page); + ProgramStorageOf::::set_program_page_data(program_id, page, data); p.pages_with_data.insert(page); } diff --git a/pallets/gear/src/queue.rs b/pallets/gear/src/queue.rs index c4381b91543..7bc4280f9a3 100644 --- a/pallets/gear/src/queue.rs +++ b/pallets/gear/src/queue.rs @@ -21,7 +21,6 @@ use core_processor::{common::PrechargedDispatch, ContextChargedForInstrumentatio pub(crate) struct QueueStep<'a, T: Config, F> { pub block_config: &'a BlockConfig, - pub lazy_pages_enabled: bool, pub ext_manager: &'a mut ExtManager, pub gas_limit: GasBalanceOf, pub dispatch: StoredDispatch, @@ -31,7 +30,6 @@ pub(crate) struct QueueStep<'a, T: Config, F> { #[derive(Debug)] pub(crate) enum QueueStepError { - NoMemoryPages, ActorData(PrechargedDispatch), } @@ -46,7 +44,6 @@ where pub(crate) fn execute(self) -> Result, QueueStepError> { let Self { block_config, - lazy_pages_enabled, ext_manager, gas_limit, dispatch, @@ -136,13 +133,7 @@ where }; // Load program memory pages. - let memory_pages = Pallet::::get_and_track_memory_pages( - ext_manager, - program_id, - &context.actor_data().pages_with_data, - lazy_pages_enabled, - ) - .ok_or(QueueStepError::NoMemoryPages)?; + ext_manager.insert_program_id_loaded_pages(program_id); let (random, bn) = T::Randomness::random(dispatch_id.as_ref()); @@ -150,7 +141,6 @@ where block_config, (context, code, balance).into(), (random.encode(), bn.unique_saturated_into()), - memory_pages, ) .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); @@ -169,14 +159,14 @@ where { /// Message Queue processing. pub(crate) fn process_queue(mut ext_manager: ExtManager) { + Self::enable_lazy_pages(); + let block_config = Self::block_config(); if T::DebugInfo::is_remap_id_enabled() { T::DebugInfo::remap_id(); } - let lazy_pages_enabled = Self::enable_lazy_pages(); - while QueueProcessingOf::::allowed() { let dispatch = match QueueOf::::dequeue() .unwrap_or_else(|e| unreachable!("Message queue corrupted! {:?}", e)) @@ -226,7 +216,6 @@ where let step = QueueStep { block_config: &block_config, - lazy_pages_enabled, ext_manager: &mut ext_manager, gas_limit, dispatch, @@ -257,7 +246,6 @@ where MessageWaitedSystemReason::ProgramIsNotInitialized.into_reason(), ); } - Err(QueueStepError::NoMemoryPages) => continue, } } diff --git a/pallets/gear/src/runtime_api.rs b/pallets/gear/src/runtime_api.rs index a37fea57381..d3dc1d94a9a 100644 --- a/pallets/gear/src/runtime_api.rs +++ b/pallets/gear/src/runtime_api.rs @@ -30,7 +30,6 @@ pub(crate) const RUNTIME_API_BLOCK_LIMITS_COUNT: u64 = 6; pub(crate) struct CodeWithMemoryData { pub instrumented_code: InstrumentedCode, pub allocations: BTreeSet, - pub program_pages: Option>, } impl Pallet @@ -46,6 +45,8 @@ where allow_other_panics: bool, allow_skip_zero_replies: bool, ) -> Result> { + Self::enable_lazy_pages(); + let account = ::from_origin(source); let balance = CurrencyOf::::free_balance(&account); @@ -102,8 +103,6 @@ where let mut block_config = Self::block_config(); block_config.forbidden_funcs = [SysCallName::GasAvailable].into(); - let lazy_pages_enabled = Self::enable_lazy_pages(); - let mut min_limit = 0; let mut reserved = 0; let mut burned = 0; @@ -144,7 +143,6 @@ where let step = QueueStep { block_config: &block_config, - lazy_pages_enabled, ext_manager: &mut ext_manager, gas_limit, dispatch: queued_dispatch, @@ -250,24 +248,11 @@ where let instrumented_code = T::CodeStorage::get_code(code_id) .ok_or_else(|| String::from("Failed to get code for given program id"))?; - #[cfg(not(feature = "lazy-pages"))] - let program_pages = Some( - ProgramStorageOf::::get_program_data_for_pages( - program_id, - program.pages_with_data.iter(), - ) - .map_err(|e| format!("Get program pages data error: {e:?}"))?, - ); - - #[cfg(feature = "lazy-pages")] - let program_pages = None; - let allocations = program.allocations; Ok(CodeWithMemoryData { instrumented_code, allocations, - program_pages, }) } @@ -278,13 +263,7 @@ where wasm: Vec, argument: Option>, ) -> Result, String> { - #[cfg(feature = "lazy-pages")] - { - let prefix = ProgramStorageOf::::pages_final_prefix(); - if !lazy_pages::try_to_enable_lazy_pages(prefix) { - unreachable!("By some reasons we cannot run lazy-pages on this machine"); - } - } + Self::enable_lazy_pages(); let schedule = T::Schedule::get(); @@ -322,7 +301,6 @@ where instrumented_code, None, None, - None, payload, BlockGasLimitOf::::get() * RUNTIME_API_BLOCK_LIMITS_COUNT, block_info, @@ -333,20 +311,13 @@ where program_id: ProgramId, payload: Vec, ) -> Result, String> { - #[cfg(feature = "lazy-pages")] - { - let prefix = ProgramStorageOf::::pages_final_prefix(); - if !lazy_pages::try_to_enable_lazy_pages(prefix) { - unreachable!("By some reasons we cannot run lazy-pages on this machine"); - } - } + Self::enable_lazy_pages(); log::debug!("Reading state of {program_id:?}"); let CodeWithMemoryData { instrumented_code, allocations, - program_pages, } = Self::code_with_memory(program_id)?; let block_info = BlockInfo { @@ -357,7 +328,6 @@ where core_processor::informational::execute_for_reply::, String>( String::from("state"), instrumented_code, - program_pages, Some(allocations), Some(program_id), payload, @@ -367,20 +337,13 @@ where } pub(crate) fn read_metahash_impl(program_id: ProgramId) -> Result { - #[cfg(feature = "lazy-pages")] - { - let prefix = ProgramStorageOf::::pages_final_prefix(); - if !lazy_pages::try_to_enable_lazy_pages(prefix) { - unreachable!("By some reasons we cannot run lazy-pages on this machine"); - } - } + Self::enable_lazy_pages(); log::debug!("Reading metahash of {program_id:?}"); let CodeWithMemoryData { instrumented_code, allocations, - program_pages, } = Self::code_with_memory(program_id)?; let block_info = BlockInfo { @@ -391,7 +354,6 @@ where core_processor::informational::execute_for_reply::, String>( String::from("metahash"), instrumented_code, - program_pages, Some(allocations), Some(program_id), Default::default(), diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index 019d2ac293b..fef0874afa1 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -2989,7 +2989,6 @@ fn restrict_start_section() { }); } -#[cfg(feature = "lazy-pages")] #[test] fn memory_access_cases() { // This test access different pages in wasm linear memory. @@ -3231,7 +3230,6 @@ fn memory_access_cases() { }); } -#[cfg(feature = "lazy-pages")] #[test] fn lazy_pages() { use gear_core::pages::{GearPage, PageU32Size}; @@ -4170,16 +4168,12 @@ fn claim_value_works() { // In `calculate_gas_info` program start to work with page data in storage, // so need to take in account gas, which spent for data loading. - let charged_for_page_load = if cfg!(feature = "lazy-pages") { - gas_price( - ::Schedule::get() - .memory_weights - .load_page_data - .ref_time(), - ) - } else { - 0 - }; + let charged_for_page_load = gas_price( + ::Schedule::get() + .memory_weights + .load_page_data + .ref_time(), + ); // Gas left returns to sender from consuming of value tree while claiming. let expected_sender_balance = diff --git a/pallets/payment/Cargo.toml b/pallets/payment/Cargo.toml index 93c809367f0..84d09e5244c 100644 --- a/pallets/payment/Cargo.toml +++ b/pallets/payment/Cargo.toml @@ -75,5 +75,4 @@ runtime-benchmarks = [ "frame-support/runtime-benchmarks", "parity-wasm", ] -lazy-pages = ["pallet-gear/lazy-pages"] try-runtime = ["frame-support/try-runtime"] diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index b673f851b3d..a35574095b4 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -53,6 +53,7 @@ std = [ "pallet-gear-bank/std", "pallet-gear-messenger/std", "runtime-primitives/std", + "gear-core-processor/std", "sp-runtime/std", "sp-std/std", "validator-set/std", diff --git a/runtime/gear/Cargo.toml b/runtime/gear/Cargo.toml index c68acce6ee8..77751e6991e 100644 --- a/runtime/gear/Cargo.toml +++ b/runtime/gear/Cargo.toml @@ -181,8 +181,3 @@ try-runtime = [ "runtime-common/try-runtime", ] dev = ["pallet-gear-debug", "pallet-gear-program/dev"] -lazy-pages = [ - "pallet-gear/lazy-pages", - "pallet-gear-payment/lazy-pages", - "pallet-gear-debug?/lazy-pages", -] diff --git a/runtime/vara/Cargo.toml b/runtime/vara/Cargo.toml index 40e450428e2..1640bc001fb 100644 --- a/runtime/vara/Cargo.toml +++ b/runtime/vara/Cargo.toml @@ -262,8 +262,3 @@ dev = [ "pallet-gear-program/dev", "pallet-sudo", ] -lazy-pages = [ - "pallet-gear/lazy-pages", - "pallet-gear-payment/lazy-pages", - "pallet-gear-debug?/lazy-pages", -] diff --git a/scripts/ci_build.sh b/scripts/ci_build.sh index 628a9490a6e..e7c24ae2bc1 100755 --- a/scripts/ci_build.sh +++ b/scripts/ci_build.sh @@ -24,13 +24,8 @@ echo "Check: Gear runtime imports" echo "Check: Vara runtime imports" ./target/release/wasm-proc --check-runtime-imports target/release/wbuild/vara-runtime/vara_runtime.compact.wasm -echo "Test: Gear pallet tests with lazy pages" - cargo test -p pallet-gear --features=lazy-pages --release --locked - cargo test -p pallet-gear-debug --features=lazy-pages --release --locked - cargo test -p pallet-gear-payment --features=lazy-pages --release --locked - echo "Test: Gear workspace" - ./scripts/gear.sh test gear --exclude gclient --exclude gcli --features pallet-gear-debug/lazy-pages --release --locked + ./scripts/gear.sh test gear --exclude gclient --exclude gcli --release --locked echo "Test: `gcli`" ./scripts/gear.sh test gcli --release --locked --retries 3 diff --git a/utils/runtime-fuzzer/Cargo.toml b/utils/runtime-fuzzer/Cargo.toml index 410ab54b0f9..e78ce560ea4 100644 --- a/utils/runtime-fuzzer/Cargo.toml +++ b/utils/runtime-fuzzer/Cargo.toml @@ -26,7 +26,7 @@ runtime-primitives.workspace = true gear-common.workspace = true gear-core.workspace = true gear-utils.workspace = true -gear-runtime = { workspace = true, features = ["std", "fuzz", "lazy-pages"] } +gear-runtime = { workspace = true, features = ["std", "fuzz"] } pallet-gear.workspace = true pallet-gear-bank.workspace = true diff --git a/utils/wasm-gen/Cargo.toml b/utils/wasm-gen/Cargo.toml index 9b9c6464ea9..ea4e5c01fbf 100644 --- a/utils/wasm-gen/Cargo.toml +++ b/utils/wasm-gen/Cargo.toml @@ -26,4 +26,5 @@ proptest.workspace = true gear-backend-sandbox = { workspace = true, features = ["std"] } gear-backend-common = { workspace = true, features = ["mock"] } -gear-core-processor = { workspace = true, features = ["mock"] } +gear-core-processor = { workspace = true, features = ["std", "mock"] } +gear-lazy-pages-common = { workspace = true, features = ["std"] } diff --git a/utils/wasm-gen/src/tests.rs b/utils/wasm-gen/src/tests.rs index 57019510d32..ab09d5081e8 100644 --- a/utils/wasm-gen/src/tests.rs +++ b/utils/wasm-gen/src/tests.rs @@ -22,6 +22,7 @@ use gear_backend_common::{BackendReport, Environment, TerminationReason, TrapExp use gear_backend_sandbox::SandboxEnvironment; use gear_core::{ code::Code, + ids::{CodeId, ProgramId}, memory::Memory, message::{ ContextSettings, DispatchKind, IncomingDispatch, IncomingMessage, MessageContext, @@ -242,6 +243,12 @@ fn execute_wasm_with_syscall_injected( const INITIAL_PAGES: u16 = 1; const INJECTED_SYSCALLS: u32 = 8; + const PROGRAM_STORAGE_PREFIX: [u8; 32] = *b"execute_wasm_with_syscall_inject"; + + assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + PROGRAM_STORAGE_PREFIX + )); + // We create Unstructured from zeroes here as we just need any let buf = vec![0; UNSTRUCTURED_SIZE]; let mut unstructured = Unstructured::new(&buf); @@ -295,10 +302,14 @@ fn execute_wasm_with_syscall_injected( // Imitate that reply was already sent. let _ = message_context.reply_commit(ReplyPacket::auto(), None); + let code_id = CodeId::generate(code.original_code()); + let program_id = ProgramId::generate(code_id, b""); + let processor_context = ProcessorContext { message_context, max_pages: INITIAL_PAGES.into(), rent_cost: 10, + program_id, ..ProcessorContext::new_mock() }; @@ -313,7 +324,15 @@ fn execute_wasm_with_syscall_injected( .expect("Failed to create environment"); let report = env - .execute(|mem, _, _| -> Result<(), u32> { + .execute(|mem, _stack_end, globals_config| -> Result<(), u32> { + gear_core_processor::Ext::lazy_pages_init_for_program( + mem, + program_id, + Some(mem.size()), + globals_config, + Default::default(), + ); + if let Some(mem_write) = initial_memory_write { return mem .write(mem_write.offset, &mem_write.content) From 51669483c6a7db2559f640b9e9e49b706c9fb5d0 Mon Sep 17 00:00:00 2001 From: mqxf Date: Mon, 18 Sep 2023 22:57:46 +0200 Subject: [PATCH 6/6] refactor(pallet-gear): Allow `calculate_gas_info` on terminated program. (#3278) --- pallets/gear/src/runtime_api.rs | 23 ++++++++++++++----- pallets/gear/src/tests.rs | 39 +++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/pallets/gear/src/runtime_api.rs b/pallets/gear/src/runtime_api.rs index d3dc1d94a9a..f2f09ac4bc6 100644 --- a/pallets/gear/src/runtime_api.rs +++ b/pallets/gear/src/runtime_api.rs @@ -17,9 +17,10 @@ // along with this program. If not, see . use super::*; -use crate::queue::QueueStep; +use crate::queue::{ActorResult, QueueStep}; use common::ActiveProgram; use core::convert::TryFrom; +use core_processor::common::PrechargedDispatch; use gear_core::{code::TryNewCodeConfig, pages::WasmPage}; use gear_wasm_instrument::syscalls::SysCallName; @@ -126,10 +127,20 @@ where }; let actor_id = queued_dispatch.destination(); + let dispatch_id = queued_dispatch.id(); + let dispatch_reply = queued_dispatch.reply_details().is_some(); + + let balance = CurrencyOf::::free_balance(&::from_origin( + actor_id.into_origin(), + )); - let actor = ext_manager - .get_actor(actor_id) - .ok_or_else(|| b"Program not found in the storage".to_vec())?; + let get_actor_data = |precharged_dispatch: PrechargedDispatch| { + // At this point gas counters should be changed accordingly so fetch the program data. + match Self::get_active_actor_data(actor_id, dispatch_id, dispatch_reply) { + ActorResult::Data(data) => Ok((precharged_dispatch, data)), + ActorResult::Continue => Err(precharged_dispatch), + } + }; let dispatch_id = queued_dispatch.id(); let success_reply = queued_dispatch @@ -146,8 +157,8 @@ where ext_manager: &mut ext_manager, gas_limit, dispatch: queued_dispatch, - balance: actor.balance, - get_actor_data: |dispatch| Ok((dispatch, actor.executable_data)), + balance: balance.unique_saturated_into(), + get_actor_data, }; let journal = step.execute().unwrap_or_else(|e| unreachable!("{e:?}")); diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index fef0874afa1..1a234ddff62 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -14317,6 +14317,45 @@ fn gear_block_number_math_adds_up() { }) } +#[test] +fn test_gas_info_of_terminated_program() { + use demo_constructor::{Calls, Scheme}; + + init_logger(); + new_test_ext().execute_with(|| { + // Dies in init + let init_dead = Calls::builder().panic("Die in init"); + let handle_dead = Calls::builder().panic("Called after being terminated!"); + let (_, pid_dead) = utils::submit_constructor_with_args( + USER_1, + b"salt1", + Scheme::predefined(init_dead, handle_dead, Calls::default()), + 0, + ); + + // Sends in handle message do dead program + let handle_proxy = Calls::builder().send(pid_dead.into_bytes(), []); + let (_, proxy_pid) = utils::submit_constructor_with_args( + USER_1, + b"salt2", + Scheme::predefined(Calls::default(), handle_proxy, Calls::default()), + 0, + ); + + run_to_next_block(None); + + let _gas_info = Gear::calculate_gas_info( + USER_1.into_origin(), + HandleKind::Handle(proxy_pid), + EMPTY_PAYLOAD.to_vec(), + 0, + true, + true, + ) + .expect("failed getting gas info"); + }) +} + mod utils { #![allow(unused)]