From 54f23c05f6f06f5420c9380fa12f3ffeb5730090 Mon Sep 17 00:00:00 2001 From: Arsenii Lyashenko Date: Tue, 26 Sep 2023 15:53:37 +0300 Subject: [PATCH] refactor: Merge `core-backend-common` and `core-backend-sandbox` (#3249) --- Cargo.lock | 119 ++-- Cargo.toml | 11 +- Makefile | 2 +- core-backend/{sandbox => }/Cargo.toml | 20 +- core-backend/codegen/Cargo.toml | 2 +- core-backend/common/Cargo.toml | 32 - core-backend/common/src/lib.rs | 550 ------------------ core-backend/common/src/runtime.rs | 74 --- core-backend/common/src/state.rs | 44 -- core-backend/sandbox/src/memory.rs | 213 ------- core-backend/{sandbox => }/src/env.rs | 89 ++- core-backend/src/error.rs | 264 +++++++++ core-backend/{common => }/src/funcs.rs | 355 +++++++---- core-backend/{sandbox => }/src/lib.rs | 32 +- core-backend/{common => }/src/memory.rs | 294 +++++++--- core-backend/{common => }/src/mock.rs | 35 +- core-backend/{sandbox => }/src/runtime.rs | 129 ++-- core-backend/src/state.rs | 96 +++ core-backend/{common => }/src/tests.rs | 18 +- core-processor/Cargo.toml | 5 +- core-processor/src/common.rs | 10 +- core-processor/src/configs.rs | 2 +- core-processor/src/context.rs | 24 + core-processor/src/executor.rs | 102 +--- core-processor/src/ext.rs | 102 ++-- core-processor/src/precharge.rs | 12 +- core-processor/src/processing.rs | 19 +- core/Cargo.toml | 1 + core/src/lib.rs | 1 + .../common/src/utils.rs => core/src/str.rs | 38 +- gcli/Cargo.toml | 3 +- gcli/src/meta/mod.rs | 7 +- gtest/Cargo.toml | 4 +- gtest/src/manager.rs | 7 +- gtest/src/system.rs | 2 +- lazy-pages/Cargo.toml | 4 +- lazy-pages/common/Cargo.toml | 15 + .../common/src/lib.rs | 30 +- .../interface}/Cargo.toml | 9 +- .../interface}/src/lib.rs | 7 +- lazy-pages/src/common.rs | 11 +- lazy-pages/src/globals.rs | 7 +- lazy-pages/src/host_func.rs | 2 +- lazy-pages/src/lib.rs | 6 +- lazy-pages/src/process.rs | 2 +- lazy-pages/src/signal.rs | 2 +- lazy-pages/src/tests.rs | 6 +- pallets/gear-debug/Cargo.toml | 1 - pallets/gear/Cargo.toml | 10 +- pallets/gear/src/benchmarking/mod.rs | 28 +- .../gear/src/benchmarking/tests/lazy_pages.rs | 53 +- pallets/gear/src/benchmarking/tests/mod.rs | 35 +- pallets/gear/src/benchmarking/utils.rs | 2 +- pallets/gear/src/lib.rs | 5 +- pallets/gear/src/queue.rs | 2 +- pallets/gear/src/runtime_api.rs | 6 +- pallets/gear/src/tests.rs | 7 +- runtime-interface/Cargo.toml | 3 +- runtime-interface/src/lib.rs | 37 +- runtime/common/Cargo.toml | 2 +- runtime/common/src/weights.rs | 2 +- runtime/gear/Cargo.toml | 2 +- runtime/gear/src/tests.rs | 2 +- runtime/vara/Cargo.toml | 2 +- runtime/vara/src/tests.rs | 2 +- utils/wasm-gen/Cargo.toml | 5 +- utils/wasm-gen/src/tests.rs | 14 +- utils/wasm-instrument/Cargo.toml | 5 +- utils/wasm-instrument/src/tests.rs | 11 +- 69 files changed, 1390 insertions(+), 1665 deletions(-) rename core-backend/{sandbox => }/Cargo.toml (59%) delete mode 100644 core-backend/common/Cargo.toml delete mode 100644 core-backend/common/src/lib.rs delete mode 100644 core-backend/common/src/runtime.rs delete mode 100644 core-backend/common/src/state.rs delete mode 100644 core-backend/sandbox/src/memory.rs rename core-backend/{sandbox => }/src/env.rs (90%) create mode 100644 core-backend/src/error.rs rename core-backend/{common => }/src/funcs.rs (67%) rename core-backend/{sandbox => }/src/lib.rs (58%) rename core-backend/{common => }/src/memory.rs (62%) rename core-backend/{common => }/src/mock.rs (90%) rename core-backend/{sandbox => }/src/runtime.rs (64%) create mode 100644 core-backend/src/state.rs rename core-backend/{common => }/src/tests.rs (97%) rename core-backend/common/src/utils.rs => core/src/str.rs (90%) create mode 100644 lazy-pages/common/Cargo.toml rename core-backend/common/src/lazy_pages.rs => lazy-pages/common/src/lib.rs (90%) rename {common/lazy-pages => lazy-pages/interface}/Cargo.toml (70%) rename {common/lazy-pages => lazy-pages/interface}/src/lib.rs (97%) diff --git a/Cargo.lock b/Cargo.lock index 25259b1b46f..2a1e220ef61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3672,11 +3672,10 @@ dependencies = [ "dirs", "env_logger", "etc", - "gear-backend-sandbox", "gear-core", "gear-core-errors", "gear-core-processor", - "gear-lazy-pages-common", + "gear-lazy-pages-interface", "gmeta", "gsdk", "hex", @@ -3802,50 +3801,6 @@ dependencies = [ "vara-runtime", ] -[[package]] -name = "gear-backend-codegen" -version = "1.0.1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.31", -] - -[[package]] -name = "gear-backend-common" -version = "1.0.1" -dependencies = [ - "actor-system-error", - "blake2-rfc", - "derive_more", - "gear-backend-codegen", - "gear-core", - "gear-core-errors", - "gear-wasm-instrument", - "gsys", - "log", - "num_enum", - "parity-scale-codec", - "rand 0.8.5", - "scale-info", -] - -[[package]] -name = "gear-backend-sandbox" -version = "0.1.0" -dependencies = [ - "derive_more", - "gear-backend-common", - "gear-core", - "gear-core-errors", - "gear-sandbox", - "gear-sandbox-env", - "gear-wasm-instrument", - "gsys", - "log", - "parity-scale-codec", -] - [[package]] name = "gear-bags-thresholds" version = "1.0.0" @@ -3957,6 +3912,7 @@ dependencies = [ "parity-scale-codec", "paste", "proptest", + "rand 0.8.5", "scale-info", "serde", "static_assertions", @@ -3964,6 +3920,34 @@ dependencies = [ "wasmparser-nostd 0.100.1", ] +[[package]] +name = "gear-core-backend" +version = "1.0.1" +dependencies = [ + "actor-system-error", + "blake2-rfc", + "derive_more", + "gear-core", + "gear-core-backend-codegen", + "gear-core-errors", + "gear-lazy-pages-common", + "gear-sandbox", + "gear-sandbox-env", + "gear-wasm-instrument", + "gsys", + "log", + "parity-scale-codec", +] + +[[package]] +name = "gear-core-backend-codegen" +version = "1.0.1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.31", +] + [[package]] name = "gear-core-errors" version = "1.0.1" @@ -3981,10 +3965,11 @@ dependencies = [ "derive_more", "enum-iterator 1.4.1", "env_logger", - "gear-backend-common", "gear-core", + "gear-core-backend", "gear-core-errors", "gear-lazy-pages-common", + "gear-lazy-pages-interface", "gear-wasm-instrument", "log", "scale-info", @@ -4007,8 +3992,8 @@ dependencies = [ "derive_more", "env_logger", "errno", - "gear-backend-common", "gear-core", + "gear-lazy-pages-common", "gear-sandbox-host", "libc", "log", @@ -4026,13 +4011,22 @@ dependencies = [ [[package]] name = "gear-lazy-pages-common" -version = "0.1.0" +version = "1.0.1" +dependencies = [ + "gear-core", + "num_enum", + "parity-scale-codec", +] + +[[package]] +name = "gear-lazy-pages-interface" +version = "1.0.1" dependencies = [ "byteorder", "derive_more", - "gear-backend-common", "gear-common", "gear-core", + "gear-lazy-pages-common", "gear-runtime-interface", "gear-wasm-instrument", "log", @@ -4152,9 +4146,9 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "gear-backend-common", "gear-common", "gear-core-processor", + "gear-lazy-pages-common", "gear-runtime-common", "gear-runtime-primitives", "hex-literal", @@ -4210,10 +4204,10 @@ dependencies = [ "frame-support", "frame-system", "frame-system-benchmarking", - "gear-backend-common", "gear-common", "gear-core", "gear-core-processor", + "gear-lazy-pages-common", "gear-runtime-primitives", "log", "pallet-authorship", @@ -4234,9 +4228,9 @@ version = "1.0.1" dependencies = [ "byteorder", "derive_more", - "gear-backend-common", "gear-core", "gear-lazy-pages", + "gear-lazy-pages-common", "gear-sandbox-host", "libc", "log", @@ -4458,11 +4452,10 @@ name = "gear-wasm-gen" version = "0.1.0" dependencies = [ "arbitrary", - "gear-backend-common", - "gear-backend-sandbox", "gear-core", + "gear-core-backend", "gear-core-processor", - "gear-lazy-pages-common", + "gear-lazy-pages-interface", "gear-utils", "gear-wasm-instrument", "gsys", @@ -4483,9 +4476,8 @@ name = "gear-wasm-instrument" version = "1.0.1" dependencies = [ "enum-iterator 1.4.1", - "gear-backend-common", - "gear-backend-sandbox", "gear-core", + "gear-core-backend", "gwasm-instrument", "wasmparser-nostd 0.100.1", "wat", @@ -4794,12 +4786,10 @@ dependencies = [ "demo-ping", "derive_more", "env_logger", - "gear-backend-common", - "gear-backend-sandbox", "gear-core", "gear-core-errors", "gear-core-processor", - "gear-lazy-pages-common", + "gear-lazy-pages-interface", "gear-utils", "gear-wasm-builder", "gear-wasm-instrument", @@ -7405,13 +7395,13 @@ dependencies = [ "frame-support", "frame-support-test", "frame-system", - "gear-backend-common", - "gear-backend-sandbox", "gear-common", "gear-core", + "gear-core-backend", "gear-core-errors", "gear-core-processor", "gear-lazy-pages-common", + "gear-lazy-pages-interface", "gear-runtime-interface", "gear-sandbox", "gear-wasm-instrument", @@ -7478,7 +7468,6 @@ dependencies = [ "frame-support", "frame-support-test", "frame-system", - "gear-backend-sandbox", "gear-common", "gear-core", "gear-wasm-instrument", @@ -13547,9 +13536,9 @@ dependencies = [ "frame-system-benchmarking", "frame-system-rpc-runtime-api", "frame-try-runtime", - "gear-backend-common", "gear-common", "gear-core-processor", + "gear-lazy-pages-common", "gear-runtime-common", "gear-runtime-primitives", "hex-literal", diff --git a/Cargo.toml b/Cargo.toml index a2538f0fcd3..cac8fee647f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,8 @@ members = [ "common", "common/codegen", "core", - "core-backend/*", + "core-backend", + "core-backend/codegen", "core-processor", "core-errors", "examples/async", @@ -195,9 +196,8 @@ gsys = { path = "gsys" } gtest = { path = "gtest" } gmeta = { path = "gmeta" } gear-authorship = { path = "node/authorship" } -gear-backend-codegen = { path = "core-backend/codegen" } -gear-backend-common = { path = "core-backend/common" } -gear-backend-sandbox = { path = "core-backend/sandbox", default-features = false } +gear-core-backend-codegen = { path = "core-backend/codegen" } +gear-core-backend = { path = "core-backend", default-features = false } gear-call-gen = { path = "utils/call-gen" } gear-common = { path = "common", default-features = false } gear-common-codegen = { path = "common/codegen" } @@ -205,7 +205,8 @@ gear-core = { path = "core" } gear-core-errors = { path = "core-errors" } gear-core-processor = { path = "core-processor", default-features = false } gear-lazy-pages = { path = "lazy-pages" } -gear-lazy-pages-common = { path = "common/lazy-pages", default-features = false } +gear-lazy-pages-common = { path = "lazy-pages/common", default-features = false } +gear-lazy-pages-interface = { path = "lazy-pages/interface", default-features = false } gear-node-testing = { path = "node/testing" } gear-runtime = { path = "runtime/gear" } gear-runtime-common = { path = "runtime/common", default-features = false } diff --git a/Makefile b/Makefile index 0e6ee45d5ed..7062514b456 100644 --- a/Makefile +++ b/Makefile @@ -249,7 +249,7 @@ test-syscalls-integrity-release: .PHONY: doc doc: @ RUSTDOCFLAGS="--enable-index-page --generate-link-to-definition -Zunstable-options -D warnings" cargo doc --no-deps \ - -p galloc -p gclient -p gcore -p gear-backend-common -p gear-backend-sandbox \ + -p galloc -p gclient -p gcore -p gear-core-backend \ -p gear-core -p gear-core-processor -p gear-lazy-pages -p gear-core-errors \ -p gmeta -p gstd -p gtest -p gear-wasm-builder -p gear-common \ -p pallet-gear -p pallet-gear-gas -p pallet-gear-messenger -p pallet-gear-payment \ diff --git a/core-backend/sandbox/Cargo.toml b/core-backend/Cargo.toml similarity index 59% rename from core-backend/sandbox/Cargo.toml rename to core-backend/Cargo.toml index 9c0e6e710ef..0a1de42cad0 100644 --- a/core-backend/sandbox/Cargo.toml +++ b/core-backend/Cargo.toml @@ -1,24 +1,36 @@ [package] -name = "gear-backend-sandbox" -version = "0.1.0" +name = "gear-core-backend" +description = "Gear WASM backend" +version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] gear-core.workspace = true gear-core-errors = { workspace = true, features = ["codec"] } -gear-backend-common.workspace = true -gsys ={ workspace = true } +gear-core-backend-codegen.workspace = true +gear-lazy-pages-common.workspace = true +gsys = { workspace = true } gear-wasm-instrument.workspace = true gear-sandbox.workspace = true gear-sandbox-env.workspace = true + +actor-system-error.workspace = true + +blake2-rfc.workspace = true # Use max_level_debug feature to remove tracing in sys-calls by default. log.workspace = true derive_more.workspace = true codec.workspace = true +[dev-dependencies] +codec.workspace = true + [features] default = ["std"] std = ["gear-sandbox/std", "gear-wasm-instrument/std", "log/std"] +mock = [] diff --git a/core-backend/codegen/Cargo.toml b/core-backend/codegen/Cargo.toml index a6f54390cd4..8f7bb632556 100644 --- a/core-backend/codegen/Cargo.toml +++ b/core-backend/codegen/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gear-backend-codegen" +name = "gear-core-backend-codegen" description = "Code generation library for gear-core-backend" keywords = ["gear", "wasm", "codegen"] categories = ["wasm"] diff --git a/core-backend/common/Cargo.toml b/core-backend/common/Cargo.toml deleted file mode 100644 index 9c7d226c5e7..00000000000 --- a/core-backend/common/Cargo.toml +++ /dev/null @@ -1,32 +0,0 @@ -[package] -name = "gear-backend-common" -description = "Common library for gear-core-backend" -keywords = ["gear", "wasm", "codegen"] -categories = ["wasm"] -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true - -[dependencies] -gear-core.workspace = true -gear-core-errors = { workspace = true, features = ["codec"] } -gear-wasm-instrument.workspace = true -gear-backend-codegen.workspace = true - -blake2-rfc.workspace = true -derive_more.workspace = true -gsys.workspace = true -log.workspace = true -num_enum.workspace = true -parity-scale-codec.workspace = true -scale-info = { workspace = true, features = ["derive"] } -actor-system-error.workspace = true - -[dev-dependencies] -rand = { workspace = true, features = ["std", "std_rng"] } - -[features] -mock = [] diff --git a/core-backend/common/src/lib.rs b/core-backend/common/src/lib.rs deleted file mode 100644 index 62d47e1cd99..00000000000 --- a/core-backend/common/src/lib.rs +++ /dev/null @@ -1,550 +0,0 @@ -// 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 . - -//! Crate provides support for wasm runtime. - -#![no_std] - -extern crate alloc; - -pub mod lazy_pages; - -mod utils; - -#[cfg(any(feature = "mock", test))] -pub mod mock; - -pub mod funcs; -pub mod memory; -pub mod runtime; -pub mod state; - -use crate::runtime::RunFallibleError; -use actor_system_error::actor_system_error; -use alloc::{ - collections::{BTreeMap, BTreeSet}, - string::String, - vec::Vec, -}; -use core::{ - convert::Infallible, - fmt::{Debug, Display}, -}; -use gear_core::{ - env::Externalities, - gas::{ChargeError, CounterType, CountersOwner, GasAmount}, - ids::{CodeId, MessageId, ProgramId, ReservationId}, - memory::{Memory, MemoryError, MemoryInterval, PageBuf}, - message::{ - ContextStore, Dispatch, DispatchKind, IncomingDispatch, MessageWaitedType, WasmEntryPoint, - }, - pages::{GearPage, WasmPage}, - reservation::GasReserver, -}; -use lazy_pages::GlobalsAccessConfig; -use memory::ProcessAccessError; -use scale_info::scale::{self, Decode, Encode}; - -pub use crate::utils::LimitedStr; -pub use log; - -pub const PTR_SPECIAL: u32 = u32::MAX; - -actor_system_error! { - pub type TerminationReason = ActorSystemError; -} - -#[derive(Debug, Clone, Eq, PartialEq, derive_more::From)] -pub enum UndefinedTerminationReason { - Actor(ActorTerminationReason), - System(SystemTerminationReason), - /// Undefined reason because we need access to counters owner trait for RI. - ProcessAccessErrorResourcesExceed, -} - -impl UndefinedTerminationReason { - pub fn define(self, current_counter: CounterType) -> TerminationReason { - match self { - Self::Actor(r) => r.into(), - Self::System(r) => r.into(), - Self::ProcessAccessErrorResourcesExceed => { - ActorTerminationReason::from(current_counter).into() - } - } - } -} - -impl From for UndefinedTerminationReason { - fn from(err: ChargeError) -> Self { - match err { - ChargeError::GasLimitExceeded => { - ActorTerminationReason::Trap(TrapExplanation::GasLimitExceeded).into() - } - ChargeError::GasAllowanceExceeded => { - ActorTerminationReason::GasAllowanceExceeded.into() - } - } - } -} - -impl From for UndefinedTerminationReason { - fn from(trap: TrapExplanation) -> Self { - ActorTerminationReason::Trap(trap).into() - } -} - -impl From for UndefinedTerminationReason { - fn from(err: E) -> Self { - err.into_termination_reason() - } -} - -#[derive(Decode, Encode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, derive_more::From)] -#[codec(crate = scale)] -pub enum ActorTerminationReason { - Exit(ProgramId), - Leave, - Success, - Wait(Option, MessageWaitedType), - GasAllowanceExceeded, - #[from] - Trap(TrapExplanation), -} - -impl From for ActorTerminationReason { - fn from(counter_type: CounterType) -> Self { - match counter_type { - CounterType::GasLimit => Self::Trap(TrapExplanation::GasLimitExceeded), - CounterType::GasAllowance => Self::GasAllowanceExceeded, - } - } -} - -/// Non-actor related termination reason. -/// -/// ### NOTICE: -/// It's currently unused, but is left as a stub, until -/// further massive errors refactoring is done. -#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)] -pub struct SystemTerminationReason; - -/// Execution error in infallible sys-call. -#[derive( - Decode, - Encode, - Debug, - Clone, - Eq, - PartialEq, - PartialOrd, - Ord, - derive_more::Display, - derive_more::From, -)] -pub enum UnrecoverableExecutionError { - #[display(fmt = "Invalid debug string passed in `gr_debug` sys-call")] - InvalidDebugString, - #[display(fmt = "Not enough gas for operation")] - NotEnoughGas, - #[display(fmt = "Length is overflowed to read payload")] - TooBigReadLen, - #[display(fmt = "Cannot take data in payload range from message with size")] - ReadWrongRange, -} - -/// Memory error in infallible sys-call. -#[derive( - Decode, - Encode, - Debug, - Clone, - Eq, - PartialEq, - PartialOrd, - Ord, - derive_more::Display, - derive_more::From, -)] -pub enum UnrecoverableMemoryError { - /// The error occurs in attempt to access memory outside wasm program memory. - #[display(fmt = "Trying to access memory outside wasm program memory")] - AccessOutOfBounds, - /// The error occurs, when program tries to allocate in block-chain runtime more memory than allowed. - #[display(fmt = "Trying to allocate more memory in block-chain runtime than allowed")] - RuntimeAllocOutOfBounds, -} - -/// Wait error in infallible sys-call. -#[derive( - Decode, - Encode, - Debug, - Clone, - Eq, - PartialEq, - PartialOrd, - Ord, - derive_more::Display, - derive_more::From, -)] -pub enum UnrecoverableWaitError { - /// An error occurs in attempt to wait for or wait up to zero blocks. - #[display(fmt = "Waiting duration cannot be zero")] - ZeroDuration, - /// An error occurs in attempt to wait after reply sent. - #[display(fmt = "`wait()` is not allowed after reply sent")] - WaitAfterReply, -} - -#[derive( - Decode, - Encode, - Debug, - Clone, - Eq, - PartialEq, - PartialOrd, - Ord, - derive_more::Display, - derive_more::From, -)] -pub enum UnrecoverableExtError { - #[display(fmt = "Execution error: {_0}")] - Execution(UnrecoverableExecutionError), - #[display(fmt = "Memory error: {_0}")] - Memory(UnrecoverableMemoryError), - #[display(fmt = "Waiting error: {_0}")] - Wait(UnrecoverableWaitError), -} - -#[derive( - Decode, - Encode, - Debug, - Clone, - PartialEq, - Eq, - PartialOrd, - Ord, - derive_more::Display, - derive_more::From, -)] -#[codec(crate = scale)] -pub enum TrapExplanation { - /// An error occurs in attempt to charge more gas than available during execution. - #[display(fmt = "Not enough gas to continue execution")] - GasLimitExceeded, - /// An error occurs in attempt to call forbidden sys-call. - #[display(fmt = "Unable to call a forbidden function")] - ForbiddenFunction, - /// The error occurs when a program tries to allocate more memory than - /// allowed. - #[display(fmt = "Trying to allocate more wasm program memory than allowed")] - ProgramAllocOutOfBounds, - #[display(fmt = "Sys-call unrecoverable error: {_0}")] - UnrecoverableExt(UnrecoverableExtError), - #[display(fmt = "{_0}")] - Panic(LimitedStr<'static>), - #[display(fmt = "Reason is unknown. Possibly `unreachable` instruction is occurred")] - Unknown, -} - -#[derive(Debug, Default)] -pub struct SystemReservationContext { - /// Reservation created in current execution. - pub current_reservation: Option, - /// Reservation from `ContextStore`. - pub previous_reservation: Option, -} - -impl SystemReservationContext { - pub fn from_dispatch(dispatch: &IncomingDispatch) -> Self { - Self { - current_reservation: None, - previous_reservation: dispatch - .context() - .as_ref() - .and_then(|ctx| ctx.system_reservation()), - } - } - - pub fn has_any(&self) -> bool { - self.current_reservation.is_some() || self.previous_reservation.is_some() - } -} - -#[derive(Debug)] -pub struct ExtInfo { - pub gas_amount: GasAmount, - pub gas_reserver: GasReserver, - pub system_reservation_context: SystemReservationContext, - pub allocations: BTreeSet, - pub pages_data: BTreeMap, - pub generated_dispatches: Vec<(Dispatch, u32, Option)>, - pub awakening: Vec<(MessageId, u32)>, - pub reply_deposits: Vec<(MessageId, u64)>, - pub program_candidates_data: BTreeMap>, - pub program_rents: BTreeMap, - pub context_store: ContextStore, -} - -/// Extended externalities that can manage gas counters. -pub trait BackendExternalities: Externalities + CountersOwner { - fn into_ext_info(self, memory: &impl Memory) -> Result; - - fn gas_amount(&self) -> GasAmount; - - /// Pre-process memory access if need. - fn pre_process_memory_accesses( - reads: &[MemoryInterval], - writes: &[MemoryInterval], - gas_counter: &mut u64, - ) -> Result<(), ProcessAccessError>; -} - -/// A trait for conversion of the externalities API error -/// to `UndefinedTerminationReason` and `RunFallibleError`. -pub trait BackendSyscallError: Sized { - fn into_termination_reason(self) -> UndefinedTerminationReason; - - fn into_run_fallible_error(self) -> RunFallibleError; -} - -// TODO: consider to remove this trait and use Result, GasError> instead #2571 -/// A trait for conversion of the externalities memory management error to api error. -/// -/// If the conversion fails, then `Self` is returned in the `Err` variant. -pub trait BackendAllocSyscallError: Sized { - type ExtError: BackendSyscallError; - - fn into_backend_error(self) -> Result; -} - -pub struct BackendReport -where - Ext: Externalities, -{ - pub termination_reason: TerminationReason, - pub memory_wrap: EnvMem, - pub ext: Ext, -} - -#[derive(Debug, derive_more::Display)] -pub enum EnvironmentError { - #[display(fmt = "Actor backend error: {_1}")] - Actor(GasAmount, String), - #[display(fmt = "System backend error: {_0}")] - System(EnvSystemError), - #[display(fmt = "Prepare error: {_1}")] - PrepareMemory(GasAmount, PrepareMemoryError), -} - -impl - EnvironmentError -{ - pub fn from_infallible(err: EnvironmentError) -> Self { - match err { - EnvironmentError::System(err) => Self::System(err), - EnvironmentError::PrepareMemory(_, err) => match err {}, - EnvironmentError::Actor(gas_amount, s) => Self::Actor(gas_amount, s), - } - } -} - -type EnvironmentBackendReport = - BackendReport<>::Memory, >::Ext>; - -pub type EnvironmentExecutionResult = Result< - EnvironmentBackendReport, - EnvironmentError<>::SystemError, PrepareMemoryError>, ->; - -pub trait Environment: Sized -where - EntryPoint: WasmEntryPoint, -{ - type Ext: BackendExternalities + 'static; - - /// Memory type for current environment. - type Memory: Memory; - - /// That's an error which originally comes from the primary - /// wasm execution environment (set by wasmi or sandbox). - /// So it's not the error of the `Self` itself, it's a kind - /// of wrapper over the underlying executor error. - type SystemError: Debug + Display; - - /// 1) Instantiates wasm binary. - /// 2) Creates wasm memory - /// 3) Runs `prepare_memory` to fill the memory before running instance. - /// 4) Instantiate external funcs for wasm module. - fn new( - ext: Self::Ext, - binary: &[u8], - entry_point: EntryPoint, - entries: BTreeSet, - mem_size: WasmPage, - ) -> Result>; - - /// Run instance setup starting at `entry_point` - wasm export function name. - fn execute( - self, - prepare_memory: PrepareMemory, - ) -> EnvironmentExecutionResult - where - PrepareMemory: FnOnce( - &mut Self::Memory, - Option, - GlobalsAccessConfig, - ) -> Result<(), PrepareMemoryError>, - PrepareMemoryError: Display; -} - -pub trait BackendState { - /// Set termination reason - fn set_termination_reason(&mut self, reason: UndefinedTerminationReason); - - /// Process fallible syscall function result - fn process_fallible_func_result( - &mut self, - res: Result, - ) -> Result, UndefinedTerminationReason> { - match res { - Err(RunFallibleError::FallibleExt(ext_err)) => { - let code = ext_err.to_u32(); - log::trace!(target: "syscalls", "fallible syscall error: {ext_err}"); - Ok(Err(code)) - } - Err(RunFallibleError::UndefinedTerminationReason(reason)) => Err(reason), - Ok(res) => Ok(Ok(res)), - } - } - - /// Process alloc function result - fn process_alloc_func_result( - &mut self, - res: Result, - ) -> Result, UndefinedTerminationReason> { - match res { - Ok(t) => Ok(Ok(t)), - Err(err) => match err.into_backend_error() { - Ok(ext_err) => Err(ext_err.into()), - Err(alloc_err) => Ok(Err(alloc_err)), - }, - } - } -} - -/// A trait for termination of the gear sys-calls execution backend. -/// -/// Backend termination aims to return to the caller gear wasm program -/// execution outcome, which is the state of externalities, memory and -/// termination reason. -pub trait BackendTermination: Sized { - /// Transforms [`Self`] into tuple of externalities, memory and - /// termination reason returned after the execution. - fn into_parts(self) -> (Ext, UndefinedTerminationReason); - - /// Terminates backend work after execution. - /// - /// The function handles `res`, which is the result of gear wasm - /// program entry point invocation, and the termination reason. - /// - /// If the `res` is `Ok`, then execution considered successful - /// and the termination reason will have the corresponding value. - /// - /// If the `res` is `Err`, then execution is considered to end - /// with an error and the actual termination reason, which stores - /// more precise information about the error, is returned. - /// - /// There's a case, when `res` is `Err`, but termination reason has - /// a value for the successful ending of the execution. This is the - /// case of calling `unreachable` panic in the program. - fn terminate( - self, - res: Result, - gas: u64, - ) -> (Ext, TerminationReason) { - log::trace!("Execution result = {res:?}"); - - let (mut ext, termination_reason) = self.into_parts(); - let termination_reason = termination_reason.define(ext.current_counter_type()); - - ext.decrease_current_counter_to(gas); - - let termination_reason = if res.is_err() { - if matches!( - termination_reason, - TerminationReason::Actor(ActorTerminationReason::Success) - ) { - ActorTerminationReason::Trap(TrapExplanation::Unknown).into() - } else { - termination_reason - } - } else if matches!( - termination_reason, - TerminationReason::Actor(ActorTerminationReason::Success) - ) { - termination_reason - } else { - unreachable!( - "Termination reason is not success, but executor successfully ends execution" - ) - }; - - (ext, termination_reason) - } -} - -#[macro_export] -macro_rules! syscall_args_trace { - ($val:expr) => { - { - let s = stringify!($val); - if s.ends_with("_ptr") { - alloc::format!(", {} = {:#x?}", s, $val) - } else { - alloc::format!(", {} = {:?}", s, $val) - } - } - }; - ($val:expr, $($rest:expr),+) => { - { - let mut s = $crate::syscall_args_trace!($val); - s.push_str(&$crate::syscall_args_trace!($($rest),+)); - s - } - }; -} - -#[macro_export] -macro_rules! syscall_trace { - ($name:expr, $($args:expr),+) => { - { - $crate::log::trace!(target: "syscalls", "{}{}", $name, $crate::syscall_args_trace!($($args),+)); - } - }; - ($name:expr) => { - { - $crate::log::trace!(target: "syscalls", "{}", $name); - } - } -} - -#[cfg(test)] -mod tests; diff --git a/core-backend/common/src/runtime.rs b/core-backend/common/src/runtime.rs deleted file mode 100644 index 308d3a1db53..00000000000 --- a/core-backend/common/src/runtime.rs +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 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 . - -//! Trait that both sandbox and wasmi runtimes must implement. - -use crate::{ - memory::{MemoryAccessRecorder, MemoryOwner}, - BackendExternalities, BackendState, BackendSyscallError, UndefinedTerminationReason, -}; -use gear_core::{costs::RuntimeCosts, pages::WasmPage}; -use gear_core_errors::ExtError as FallibleExtError; - -/// Error returned from closure argument in [`Runtime::run_fallible`]. -#[derive(Debug, Clone)] -pub enum RunFallibleError { - UndefinedTerminationReason(UndefinedTerminationReason), - FallibleExt(FallibleExtError), -} - -impl From for RunFallibleError -where - E: BackendSyscallError, -{ - fn from(err: E) -> Self { - err.into_run_fallible_error() - } -} - -pub trait Runtime: - MemoryOwner + MemoryAccessRecorder + BackendState -{ - type Error; - - fn unreachable_error() -> Self::Error; - - fn ext_mut(&mut self) -> &mut Ext; - - fn run_any( - &mut self, - gas: u64, - cost: RuntimeCosts, - f: F, - ) -> Result<(u64, T), Self::Error> - where - F: FnOnce(&mut Self) -> Result; - - fn run_fallible( - &mut self, - gas: u64, - res_ptr: u32, - cost: RuntimeCosts, - f: F, - ) -> Result<(u64, ()), Self::Error> - where - F: FnOnce(&mut Self) -> Result, - R: From> + Sized; - - fn alloc(&mut self, pages: u32) -> Result; -} diff --git a/core-backend/common/src/state.rs b/core-backend/common/src/state.rs deleted file mode 100644 index a59a7b5116b..00000000000 --- a/core-backend/common/src/state.rs +++ /dev/null @@ -1,44 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 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::{BackendExternalities, BackendState, BackendTermination, UndefinedTerminationReason}; - -pub type HostState = Option>; - -pub struct State { - pub ext: Ext, - pub memory: Mem, - pub termination_reason: UndefinedTerminationReason, -} - -impl BackendTermination for State { - fn into_parts(self) -> (Ext, UndefinedTerminationReason) { - let State { - ext, - termination_reason, - .. - } = self; - (ext, termination_reason) - } -} - -impl BackendState for State { - fn set_termination_reason(&mut self, reason: UndefinedTerminationReason) { - self.termination_reason = reason; - } -} diff --git a/core-backend/sandbox/src/memory.rs b/core-backend/sandbox/src/memory.rs deleted file mode 100644 index 56409841003..00000000000 --- a/core-backend/sandbox/src/memory.rs +++ /dev/null @@ -1,213 +0,0 @@ -// 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 . - -//! sp-sandbox extensions for memory. - -use gear_backend_common::state::HostState; -use gear_core::{ - env::Externalities, - memory::{HostPointer, Memory, MemoryError}, - pages::{PageNumber, PageU32Size, WasmPage}, -}; -use gear_sandbox::{ - default_executor::{Caller, Store}, - SandboxMemory, -}; - -pub type DefaultExecutorMemory = gear_sandbox::default_executor::Memory; - -pub(crate) struct MemoryWrapRef<'a, 'b: 'a, Ext: Externalities + 'static> { - pub memory: DefaultExecutorMemory, - pub caller: &'a mut Caller<'b, HostState>, -} - -impl Memory for MemoryWrapRef<'_, '_, Ext> { - type GrowError = gear_sandbox::Error; - - fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { - self.memory.grow(self.caller, pages.raw()).map(|_| ()) - } - - fn size(&self) -> WasmPage { - WasmPage::new(self.memory.size(self.caller)) - .expect("Unexpected backend behavior: wasm size is bigger then u32::MAX") - } - - fn write(&mut self, offset: u32, buffer: &[u8]) -> Result<(), MemoryError> { - self.memory - .write(self.caller, offset, buffer) - .map_err(|_| MemoryError::AccessOutOfBounds) - } - - fn read(&self, offset: u32, buffer: &mut [u8]) -> Result<(), MemoryError> { - self.memory - .read(self.caller, offset, buffer) - .map_err(|_| MemoryError::AccessOutOfBounds) - } - - unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { - self.memory.get_buff(self.caller) as HostPointer - } -} - -/// Wrapper for [`DefaultExecutorMemory`]. -pub struct MemoryWrap -where - Ext: Externalities + 'static, -{ - pub(crate) memory: DefaultExecutorMemory, - pub(crate) store: Store>, -} - -impl MemoryWrap -where - Ext: Externalities + 'static, -{ - /// Wrap [`DefaultExecutorMemory`] for Memory trait. - pub fn new( - memory: DefaultExecutorMemory, - store: Store>, - ) -> Self { - MemoryWrap { memory, store } - } - - pub(crate) fn into_store(self) -> Store> { - self.store - } -} - -/// Memory interface for the allocator. -impl Memory for MemoryWrap -where - Ext: Externalities + 'static, -{ - type GrowError = gear_sandbox::Error; - - fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { - self.memory.grow(&mut self.store, pages.raw()).map(|_| ()) - } - - fn size(&self) -> WasmPage { - WasmPage::new(self.memory.size(&self.store)) - .expect("Unexpected backend behavior: wasm size is bigger then u32::MAX") - } - - fn write(&mut self, offset: u32, buffer: &[u8]) -> Result<(), MemoryError> { - self.memory - .write(&mut self.store, offset, buffer) - .map_err(|_| MemoryError::AccessOutOfBounds) - } - - fn read(&self, offset: u32, buffer: &mut [u8]) -> Result<(), MemoryError> { - self.memory - .read(&self.store, offset, buffer) - .map_err(|_| MemoryError::AccessOutOfBounds) - } - - unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { - self.memory.get_buff(&mut self.store) as HostPointer - } -} - -/// can't be tested outside the node runtime -#[cfg(test)] -mod tests { - use super::*; - use gear_backend_common::{ - assert_err, assert_ok, mock::MockExt, state::State, ActorTerminationReason, - }; - use gear_core::memory::{AllocError, AllocationsContext, NoopGrowHandler}; - use gear_sandbox::{AsContextExt, SandboxStore}; - - fn new_test_memory( - static_pages: u16, - max_pages: u16, - ) -> (AllocationsContext, MemoryWrap) { - use gear_sandbox::SandboxMemory as WasmMemory; - - let mut store = Store::new(None); - let memory: DefaultExecutorMemory = - WasmMemory::new(&mut store, static_pages as u32, Some(max_pages as u32)) - .expect("Memory creation failed"); - *store.data_mut() = Some(State { - ext: MockExt::default(), - memory: memory.clone(), - termination_reason: ActorTerminationReason::Success.into(), - }); - - let memory = MemoryWrap::new(memory, store); - - ( - AllocationsContext::new(Default::default(), static_pages.into(), max_pages.into()), - memory, - ) - } - - #[test] - fn smoky() { - let (mut ctx, mut mem_wrap) = new_test_memory(16, 256); - - assert_ok!( - ctx.alloc::(16.into(), &mut mem_wrap, |_| Ok(())), - 16.into() - ); - - assert_ok!( - ctx.alloc::(0.into(), &mut mem_wrap, |_| Ok(())), - 16.into() - ); - - // there is a space for 14 more - for _ in 0..14 { - assert_ok!(ctx.alloc::(16.into(), &mut mem_wrap, |_| Ok(()))); - } - - // no more mem! - assert_err!( - ctx.alloc::(1.into(), &mut mem_wrap, |_| Ok(())), - AllocError::ProgramAllocOutOfBounds - ); - - // but we free some - assert_ok!(ctx.free(137.into())); - - // and now can allocate page that was freed - assert_ok!( - ctx.alloc::(1.into(), &mut mem_wrap, |_| Ok(())), - 137.into() - ); - - // if we have 2 in a row we can allocate even 2 - assert_ok!(ctx.free(117.into())); - assert_ok!(ctx.free(118.into())); - - assert_ok!( - ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), - 117.into() - ); - - // but if 2 are not in a row, bad luck - assert_ok!(ctx.free(117.into())); - assert_ok!(ctx.free(158.into())); - - assert_err!( - ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), - AllocError::ProgramAllocOutOfBounds - ); - } -} diff --git a/core-backend/sandbox/src/env.rs b/core-backend/src/env.rs similarity index 90% rename from core-backend/sandbox/src/env.rs rename to core-backend/src/env.rs index ca5c9f0f81a..625eec6e8b0 100644 --- a/core-backend/sandbox/src/env.rs +++ b/core-backend/src/env.rs @@ -18,23 +18,34 @@ //! sp-sandbox environment for running a module. -use crate::{memory::MemoryWrap, runtime, runtime::CallerWrap}; -use alloc::{collections::BTreeSet, format}; -use core::{any::Any, convert::Infallible, fmt::Display}; -use gear_backend_common::{ +use crate::{ + error::{ + ActorTerminationReason, BackendAllocSyscallError, BackendSyscallError, RunFallibleError, + TerminationReason, + }, funcs::FuncsHandler, - lazy_pages::{GlobalsAccessConfig, GlobalsAccessError, GlobalsAccessMod, GlobalsAccessor}, - runtime::RunFallibleError, + memory::MemoryWrap, + runtime, + runtime::CallerWrap, state::{HostState, State}, - ActorTerminationReason, BackendAllocSyscallError, BackendExternalities, BackendReport, - BackendSyscallError, BackendTermination, Environment, EnvironmentError, - EnvironmentExecutionResult, LimitedStr, + BackendExternalities, +}; +use alloc::{collections::BTreeSet, format, string::String}; +use core::{ + any::Any, + convert::Infallible, + fmt::{Debug, Display}, }; use gear_core::{ env::Externalities, + gas::GasAmount, memory::HostPointer, message::{DispatchKind, WasmEntryPoint}, pages::{PageNumber, WasmPage}, + str::LimitedStr, +}; +use gear_lazy_pages_common::{ + GlobalsAccessConfig, GlobalsAccessError, GlobalsAccessMod, GlobalsAccessor, }; use gear_sandbox::{ default_executor::{ @@ -155,8 +166,31 @@ fn store_host_state_mut( .unwrap_or_else(|| unreachable!("State must be set in `WasmiEnvironment::new`; qed")) } +pub type EnvironmentExecutionResult = + Result, EnvironmentError>; + +#[derive(Debug, derive_more::Display)] +pub enum EnvironmentError { + #[display(fmt = "Actor backend error: {_1}")] + Actor(GasAmount, String), + #[display(fmt = "System backend error: {_0}")] + System(SystemEnvironmentError), + #[display(fmt = "Prepare error: {_1}")] + PrepareMemory(GasAmount, PrepareMemoryError), +} + +impl EnvironmentError { + pub fn from_infallible(err: EnvironmentError) -> Self { + match err { + EnvironmentError::System(err) => Self::System(err), + EnvironmentError::PrepareMemory(_, err) => match err {}, + EnvironmentError::Actor(gas_amount, s) => Self::Actor(gas_amount, s), + } + } +} + #[derive(Debug, derive_more::Display)] -pub enum SandboxEnvironmentError { +pub enum SystemEnvironmentError { #[display(fmt = "Failed to create env memory: {_0:?}")] CreateEnvMemory(gear_sandbox::Error), #[display(fmt = "Globals are not supported")] @@ -166,7 +200,7 @@ pub enum SandboxEnvironmentError { } /// Environment to run one module at a time providing Ext. -pub struct SandboxEnvironment +pub struct Environment where Ext: BackendExternalities, EntryPoint: WasmEntryPoint, @@ -178,6 +212,15 @@ where memory: DefaultExecutorMemory, } +pub struct BackendReport +where + Ext: Externalities + 'static, +{ + pub termination_reason: TerminationReason, + pub memory_wrap: MemoryWrap, + pub ext: Ext, +} + // A helping wrapper for `EnvironmentDefinitionBuilder` and `forbidden_funcs`. // It makes adding functions to `EnvironmentDefinitionBuilder` shorter. struct EnvBuilder { @@ -224,7 +267,7 @@ impl From> } } -impl SandboxEnvironment +impl Environment where Ext: BackendExternalities + 'static, Ext::UnrecoverableError: BackendSyscallError, @@ -319,7 +362,7 @@ impl GlobalsAccessor for GlobalsAccessProvider Environment for SandboxEnvironment +impl Environment where EnvExt: BackendExternalities + 'static, EnvExt::UnrecoverableError: BackendSyscallError, @@ -327,19 +370,15 @@ where EnvExt::AllocError: BackendAllocSyscallError, EntryPoint: WasmEntryPoint, { - type Ext = EnvExt; - type Memory = MemoryWrap; - type SystemError = SandboxEnvironmentError; - - fn new( - ext: Self::Ext, + pub fn new( + ext: EnvExt, binary: &[u8], entry_point: EntryPoint, entries: BTreeSet, mem_size: WasmPage, - ) -> Result> { + ) -> Result> { use EnvironmentError::*; - use SandboxEnvironmentError::*; + use SystemEnvironmentError::*; let entry_forbidden = entry_point .try_into_kind() @@ -403,20 +442,20 @@ where }) } - fn execute( + pub fn execute( self, prepare_memory: PrepareMemory, - ) -> EnvironmentExecutionResult + ) -> EnvironmentExecutionResult where PrepareMemory: FnOnce( - &mut Self::Memory, + &mut MemoryWrap, Option, GlobalsAccessConfig, ) -> Result<(), PrepareMemoryError>, PrepareMemoryError: Display, { use EnvironmentError::*; - use SandboxEnvironmentError::*; + use SystemEnvironmentError::*; let Self { mut instance, diff --git a/core-backend/src/error.rs b/core-backend/src/error.rs new file mode 100644 index 00000000000..baabe2bba54 --- /dev/null +++ b/core-backend/src/error.rs @@ -0,0 +1,264 @@ +// This file is part of Gear. + +// Copyright (C) 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 actor_system_error::actor_system_error; +use codec::{Decode, Encode}; +use gear_core::{ + gas::{ChargeError, CounterType}, + ids::ProgramId, + message::MessageWaitedType, + str::LimitedStr, +}; +use gear_core_errors::ExtError as FallibleExtError; + +actor_system_error! { + pub type TerminationReason = ActorSystemError; +} + +#[derive(Debug, Clone, Eq, PartialEq, derive_more::From)] +pub enum UndefinedTerminationReason { + Actor(ActorTerminationReason), + System(SystemTerminationReason), + /// Undefined reason because we need access to counters owner trait for RI. + ProcessAccessErrorResourcesExceed, +} + +impl UndefinedTerminationReason { + pub fn define(self, current_counter: CounterType) -> TerminationReason { + match self { + Self::Actor(r) => r.into(), + Self::System(r) => r.into(), + Self::ProcessAccessErrorResourcesExceed => { + ActorTerminationReason::from(current_counter).into() + } + } + } +} + +impl From for UndefinedTerminationReason { + fn from(err: ChargeError) -> Self { + match err { + ChargeError::GasLimitExceeded => { + ActorTerminationReason::Trap(TrapExplanation::GasLimitExceeded).into() + } + ChargeError::GasAllowanceExceeded => { + ActorTerminationReason::GasAllowanceExceeded.into() + } + } + } +} + +impl From for UndefinedTerminationReason { + fn from(trap: TrapExplanation) -> Self { + ActorTerminationReason::Trap(trap).into() + } +} + +impl From for UndefinedTerminationReason { + fn from(err: E) -> Self { + err.into_termination_reason() + } +} + +#[derive(Decode, Encode, Debug, PartialEq, Eq, PartialOrd, Ord, Clone, derive_more::From)] +#[codec(crate = codec)] +pub enum ActorTerminationReason { + Exit(ProgramId), + Leave, + Success, + Wait(Option, MessageWaitedType), + GasAllowanceExceeded, + #[from] + Trap(TrapExplanation), +} + +impl From for ActorTerminationReason { + fn from(counter_type: CounterType) -> Self { + match counter_type { + CounterType::GasLimit => Self::Trap(TrapExplanation::GasLimitExceeded), + CounterType::GasAllowance => Self::GasAllowanceExceeded, + } + } +} + +/// Non-actor related termination reason. +/// +/// ### NOTICE: +/// It's currently unused, but is left as a stub, until +/// further massive errors refactoring is done. +#[derive(Debug, Clone, Eq, PartialEq, derive_more::Display)] +pub struct SystemTerminationReason; + +/// Execution error in infallible sys-call. +#[derive( + Decode, + Encode, + Debug, + Clone, + Eq, + PartialEq, + PartialOrd, + Ord, + derive_more::Display, + derive_more::From, +)] +#[codec(crate = codec)] +pub enum UnrecoverableExecutionError { + #[display(fmt = "Invalid debug string passed in `gr_debug` sys-call")] + InvalidDebugString, + #[display(fmt = "Not enough gas for operation")] + NotEnoughGas, + #[display(fmt = "Length is overflowed to read payload")] + TooBigReadLen, + #[display(fmt = "Cannot take data in payload range from message with size")] + ReadWrongRange, +} + +/// Memory error in infallible sys-call. +#[derive( + Decode, + Encode, + Debug, + Clone, + Eq, + PartialEq, + PartialOrd, + Ord, + derive_more::Display, + derive_more::From, +)] +#[codec(crate = codec)] +pub enum UnrecoverableMemoryError { + /// The error occurs in attempt to access memory outside wasm program memory. + #[display(fmt = "Trying to access memory outside wasm program memory")] + AccessOutOfBounds, + /// The error occurs, when program tries to allocate in block-chain runtime more memory than allowed. + #[display(fmt = "Trying to allocate more memory in block-chain runtime than allowed")] + RuntimeAllocOutOfBounds, +} + +/// Wait error in infallible sys-call. +#[derive( + Decode, + Encode, + Debug, + Clone, + Eq, + PartialEq, + PartialOrd, + Ord, + derive_more::Display, + derive_more::From, +)] +#[codec(crate = codec)] +pub enum UnrecoverableWaitError { + /// An error occurs in attempt to wait for or wait up to zero blocks. + #[display(fmt = "Waiting duration cannot be zero")] + ZeroDuration, + /// An error occurs in attempt to wait after reply sent. + #[display(fmt = "`wait()` is not allowed after reply sent")] + WaitAfterReply, +} + +#[derive( + Decode, + Encode, + Debug, + Clone, + Eq, + PartialEq, + PartialOrd, + Ord, + derive_more::Display, + derive_more::From, +)] +#[codec(crate = codec)] +pub enum UnrecoverableExtError { + #[display(fmt = "Execution error: {_0}")] + Execution(UnrecoverableExecutionError), + #[display(fmt = "Memory error: {_0}")] + Memory(UnrecoverableMemoryError), + #[display(fmt = "Waiting error: {_0}")] + Wait(UnrecoverableWaitError), +} + +#[derive( + Decode, + Encode, + Debug, + Clone, + PartialEq, + Eq, + PartialOrd, + Ord, + derive_more::Display, + derive_more::From, +)] +#[codec(crate = codec)] +pub enum TrapExplanation { + /// An error occurs in attempt to charge more gas than available during execution. + #[display(fmt = "Not enough gas to continue execution")] + GasLimitExceeded, + /// An error occurs in attempt to call forbidden sys-call. + #[display(fmt = "Unable to call a forbidden function")] + ForbiddenFunction, + /// The error occurs when a program tries to allocate more memory than + /// allowed. + #[display(fmt = "Trying to allocate more wasm program memory than allowed")] + ProgramAllocOutOfBounds, + #[display(fmt = "Sys-call unrecoverable error: {_0}")] + UnrecoverableExt(UnrecoverableExtError), + #[display(fmt = "{_0}")] + Panic(LimitedStr<'static>), + #[display(fmt = "Reason is unknown. Possibly `unreachable` instruction is occurred")] + Unknown, +} + +/// Error returned by fallible sys-call. +#[derive(Debug, Clone)] +pub enum RunFallibleError { + UndefinedTerminationReason(UndefinedTerminationReason), + FallibleExt(FallibleExtError), +} + +impl From for RunFallibleError +where + E: BackendSyscallError, +{ + fn from(err: E) -> Self { + err.into_run_fallible_error() + } +} + +/// A trait for conversion of the externalities API error +/// to `UndefinedTerminationReason` and `RunFallibleError`. +pub trait BackendSyscallError: Sized { + fn into_termination_reason(self) -> UndefinedTerminationReason; + + fn into_run_fallible_error(self) -> RunFallibleError; +} + +// TODO: consider to remove this trait and use Result, GasError> instead #2571 +/// A trait for conversion of the externalities memory management error to api error. +/// +/// If the conversion fails, then `Self` is returned in the `Err` variant. +pub trait BackendAllocSyscallError: Sized { + type ExtError: BackendSyscallError; + + fn into_backend_error(self) -> Result; +} diff --git a/core-backend/common/src/funcs.rs b/core-backend/src/funcs.rs similarity index 67% rename from core-backend/common/src/funcs.rs rename to core-backend/src/funcs.rs index fc2f8554f30..3b064431d60 100644 --- a/core-backend/common/src/funcs.rs +++ b/core-backend/src/funcs.rs @@ -19,48 +19,92 @@ //! Syscall implementations generic over wasmi and sandbox backends. use crate::{ + error::{ + ActorTerminationReason, BackendAllocSyscallError, BackendSyscallError, RunFallibleError, + TrapExplanation, UndefinedTerminationReason, UnrecoverableExecutionError, + UnrecoverableMemoryError, + }, memory::{MemoryAccessError, WasmMemoryRead}, - runtime::{RunFallibleError, Runtime}, - syscall_trace, ActorTerminationReason, BackendAllocSyscallError, BackendExternalities, - BackendSyscallError, MessageWaitedType, TrapExplanation, UndefinedTerminationReason, - UnrecoverableExecutionError, UnrecoverableMemoryError, PTR_SPECIAL, + runtime::CallerWrap, + BackendExternalities, }; use alloc::string::{String, ToString}; use blake2_rfc::blake2b::blake2b; use core::marker::PhantomData; -use gear_backend_codegen::host; use gear_core::{ buffer::{RuntimeBuffer, RuntimeBufferSizeError}, costs::RuntimeCosts, env::{DropPayloadLockBound, Externalities}, gas::CounterType, - message::{HandlePacket, InitPacket, Payload, PayloadSizeError, ReplyPacket}, + message::{ + HandlePacket, InitPacket, MessageWaitedType, Payload, PayloadSizeError, ReplyPacket, + }, pages::{PageNumber, PageU32Size, WasmPage}, }; +use gear_core_backend_codegen::host; use gear_core_errors::{MessageError, ReplyCode, SignalCode}; +use gear_sandbox_env::HostError; use gsys::{ BlockNumberWithHash, ErrorBytes, ErrorWithBlockNumberAndValue, ErrorWithGas, ErrorWithHandle, ErrorWithHash, ErrorWithReplyCode, ErrorWithSignalCode, ErrorWithTwoHashes, Hash, HashWithValue, TwoHashesWithValue, }; -pub struct FuncsHandler { - _phantom: PhantomData<(Ext, Runtime)>, +#[macro_export(local_inner_macros)] +macro_rules! syscall_args_trace { + ($val:expr) => { + { + let s = ::core::stringify!($val); + if s.ends_with("_ptr") { + alloc::format!(", {} = {:#x?}", s, $val) + } else { + alloc::format!(", {} = {:?}", s, $val) + } + } + }; + ($val:expr, $($rest:expr),+) => { + { + let mut s = syscall_args_trace!($val); + s.push_str(&syscall_args_trace!($($rest),+)); + s + } + }; } -impl FuncsHandler +macro_rules! syscall_trace { + ($name:expr, $($args:expr),+) => { + { + ::log::trace!(target: "syscalls", "{}{}", $name, syscall_args_trace!($($args),+)); + } + }; + ($name:expr) => { + { + ::log::trace!(target: "syscalls", "{}", $name); + } + } +} + +const PTR_SPECIAL: u32 = u32::MAX; + +pub(crate) struct FuncsHandler { + _phantom: PhantomData, +} + +impl FuncsHandler where Ext: BackendExternalities + 'static, Ext::UnrecoverableError: BackendSyscallError, RunFallibleError: From, Ext::AllocError: BackendAllocSyscallError, - R: Runtime, { /// !!! Usage warning: make sure to do it before any other read/write, /// because it may contain registered read. - fn register_and_read_value(ctx: &mut R, value_ptr: u32) -> Result { + fn register_and_read_value( + ctx: &mut CallerWrap<'_, '_, Ext>, + value_ptr: u32, + ) -> Result { if value_ptr != PTR_SPECIAL { - let read_value = ctx.register_read_decoded(value_ptr); + let read_value = ctx.manager.register_read_decoded(value_ptr); return ctx.read_decoded(read_value); } @@ -68,7 +112,7 @@ where } fn read_message_payload( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, read_payload: WasmMemoryRead, ) -> Result { ctx.read(read_payload)? @@ -81,15 +125,15 @@ where #[allow(clippy::too_many_arguments)] #[host(fallible, wgas, cost = RuntimeCosts::Send(len))] pub fn send( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, pid_value_ptr: u32, payload_ptr: u32, len: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_hash_val = ctx.register_read_as(pid_value_ptr); - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_hash_val = ctx.manager.register_read_as(pid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); let HashWithValue { hash: destination, value, @@ -103,13 +147,13 @@ where #[host(fallible, wgas, cost = RuntimeCosts::SendCommit)] pub fn send_commit( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, handle: u32, pid_value_ptr: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_pid_value = ctx.register_read_as(pid_value_ptr); + ) -> Result<(u64, ()), HostError> { + let read_pid_value = ctx.manager.register_read_as(pid_value_ptr); let HashWithValue { hash: destination, value, @@ -125,19 +169,19 @@ where } #[host(fallible, cost = RuntimeCosts::SendInit, err = ErrorWithHandle)] - pub fn send_init(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn send_init(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { ctx.ext_mut().send_init().map_err(Into::into) } #[host(fallible, cost = RuntimeCosts::SendPush(len), err = ErrorBytes)] pub fn send_push( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, handle: u32, payload_ptr: u32, len: u32, - ) -> Result<(u64, ()), R::Error> { - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_payload = ctx.manager.register_read(payload_ptr, len); let payload = ctx.read(read_payload)?; ctx.ext_mut() @@ -147,15 +191,15 @@ where #[host(fallible, cost = RuntimeCosts::ReservationSend(len))] pub fn reservation_send( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, rid_pid_value_ptr: u32, payload_ptr: u32, len: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_rid_pid_value = ctx.register_read_as(rid_pid_value_ptr); - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); let TwoHashesWithValue { hash1: reservation_id, hash2: destination, @@ -174,13 +218,13 @@ where #[host(fallible, cost = RuntimeCosts::ReservationSendCommit)] pub fn reservation_send_commit( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, handle: u32, rid_pid_value_ptr: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_rid_pid_value = ctx.register_read_as(rid_pid_value_ptr); + ) -> Result<(u64, ()), HostError> { + let read_rid_pid_value = ctx.manager.register_read_as(rid_pid_value_ptr); let TwoHashesWithValue { hash1: reservation_id, hash2: destination, @@ -199,16 +243,16 @@ where #[host(fallible, cost = RuntimeCosts::Read, err = ErrorBytes)] pub fn read( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, at: u32, len: u32, buffer_ptr: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { let payload_lock = ctx.ext_mut().lock_payload(at, len)?; payload_lock .drop_with::(|payload_access| { - let write_buffer = ctx.register_write(buffer_ptr, len); + let write_buffer = ctx.manager.register_write(buffer_ptr, len); let write_res = ctx.write(write_buffer, payload_access.as_slice()); let unlock_bound = ctx.ext_mut().unlock_payload(payload_access.into_lock()); @@ -219,23 +263,31 @@ where } #[host(cost = RuntimeCosts::Size)] - pub fn size(ctx: &mut R, gas: u64, size_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn size( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + size_ptr: u32, + ) -> Result<(u64, ()), HostError> { let size = ctx.ext_mut().size()? as u32; - let write_size = ctx.register_write_as(size_ptr); + let write_size = ctx.manager.register_write_as(size_ptr); ctx.write_as(write_size, size.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::Exit)] - pub fn exit(ctx: &mut R, gas: u64, inheritor_id_ptr: u32) -> Result<(u64, ()), R::Error> { - let read_inheritor_id = ctx.register_read_decoded(inheritor_id_ptr); + pub fn exit( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + inheritor_id_ptr: u32, + ) -> Result<(u64, ()), HostError> { + let read_inheritor_id = ctx.manager.register_read_decoded(inheritor_id_ptr); let inheritor_id = ctx.read_decoded(read_inheritor_id)?; Err(ActorTerminationReason::Exit(inheritor_id).into()) } #[host(fallible, cost = RuntimeCosts::ReplyCode, err = ErrorWithReplyCode)] - pub fn reply_code(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn reply_code(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { ctx.ext_mut() .reply_code() .map(ReplyCode::to_bytes) @@ -243,7 +295,10 @@ where } #[host(fallible, cost = RuntimeCosts::SignalCode, err = ErrorWithSignalCode)] - pub fn signal_code(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn signal_code( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + ) -> Result<(u64, ()), HostError> { ctx.ext_mut() .signal_code() .map(SignalCode::to_u32) @@ -251,7 +306,11 @@ where } #[host(cost = RuntimeCosts::Alloc(pages))] - pub fn alloc(ctx: &mut R, gas: u64, pages: u32) -> Result<(u64, u32), R::Error> { + pub fn alloc( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + pages: u32, + ) -> Result<(u64, u32), HostError> { let res = ctx.alloc(pages); let res = ctx.process_alloc_func_result(res)?; @@ -269,7 +328,11 @@ where } #[host(cost = RuntimeCosts::Free)] - pub fn free(ctx: &mut R, gas: u64, page_no: u32) -> Result<(u64, i32), R::Error> { + pub fn free( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + page_no: u32, + ) -> Result<(u64, i32), HostError> { let page = WasmPage::new(page_no).map_err(|_| { UndefinedTerminationReason::Actor(ActorTerminationReason::Trap( TrapExplanation::Unknown, @@ -292,36 +355,40 @@ where } #[host(cost = RuntimeCosts::BlockHeight)] - pub fn block_height(ctx: &mut R, gas: u64, height_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn block_height( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + height_ptr: u32, + ) -> Result<(u64, ()), HostError> { let height = ctx.ext_mut().block_height()?; - let write_height = ctx.register_write_as(height_ptr); + let write_height = ctx.manager.register_write_as(height_ptr); ctx.write_as(write_height, height.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::BlockTimestamp)] pub fn block_timestamp( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, timestamp_ptr: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { let timestamp = ctx.ext_mut().block_timestamp()?; - let write_timestamp = ctx.register_write_as(timestamp_ptr); + let write_timestamp = ctx.manager.register_write_as(timestamp_ptr); ctx.write_as(write_timestamp, timestamp.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::Random)] pub fn random( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, subject_ptr: u32, bn_random_ptr: u32, - ) -> Result<(u64, ()), R::Error> { - let read_subject = ctx.register_read_decoded(subject_ptr); - let write_bn_random = ctx.register_write_as(bn_random_ptr); + ) -> Result<(u64, ()), HostError> { + let read_subject = ctx.manager.register_read_decoded(subject_ptr); + let write_bn_random = ctx.manager.register_write_as(bn_random_ptr); let raw_subject: Hash = ctx.read_decoded(read_subject)?; @@ -337,13 +404,13 @@ where #[host(fallible, wgas, cost = RuntimeCosts::Reply(len))] pub fn reply( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, payload_ptr: u32, len: u32, value_ptr: u32, - ) -> Result<(u64, ()), R::Error> { - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_payload = ctx.manager.register_read(payload_ptr, len); let value = Self::register_and_read_value(ctx, value_ptr)?; let payload = Self::read_message_payload(ctx, read_payload)?; @@ -353,7 +420,11 @@ where } #[host(fallible, wgas, cost = RuntimeCosts::ReplyCommit)] - pub fn reply_commit(ctx: &mut R, gas: u64, value_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn reply_commit( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + value_ptr: u32, + ) -> Result<(u64, ()), HostError> { let value = Self::register_and_read_value(ctx, value_ptr)?; ctx.ext_mut() @@ -363,14 +434,14 @@ where #[host(fallible, cost = RuntimeCosts::ReservationReply(len))] pub fn reservation_reply( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, rid_value_ptr: u32, payload_ptr: u32, len: u32, - ) -> Result<(u64, ()), R::Error> { - let read_rid_value = ctx.register_read_as(rid_value_ptr); - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); + let read_payload = ctx.manager.register_read(payload_ptr, len); let HashWithValue { hash: reservation_id, value, @@ -384,11 +455,11 @@ where #[host(fallible, cost = RuntimeCosts::ReservationReplyCommit)] pub fn reservation_reply_commit( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, rid_value_ptr: u32, - ) -> Result<(u64, ()), R::Error> { - let read_rid_value = ctx.register_read_as(rid_value_ptr); + ) -> Result<(u64, ()), HostError> { + let read_rid_value = ctx.manager.register_read_as(rid_value_ptr); let HashWithValue { hash: reservation_id, value, @@ -403,23 +474,26 @@ where } #[host(fallible, cost = RuntimeCosts::ReplyTo)] - pub fn reply_to(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn reply_to(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { ctx.ext_mut().reply_to().map_err(Into::into) } #[host(fallible, cost = RuntimeCosts::SignalFrom)] - pub fn signal_from(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn signal_from( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + ) -> Result<(u64, ()), HostError> { ctx.ext_mut().signal_from().map_err(Into::into) } #[host(fallible, cost = RuntimeCosts::ReplyPush(len), err = ErrorBytes)] pub fn reply_push( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, payload_ptr: u32, len: u32, - ) -> Result<(u64, ()), R::Error> { - let read_payload = ctx.register_read(payload_ptr, len); + ) -> Result<(u64, ()), HostError> { + let read_payload = ctx.manager.register_read(payload_ptr, len); let payload = ctx.read(read_payload)?; ctx.ext_mut().reply_push(&payload).map_err(Into::into) @@ -427,12 +501,12 @@ where #[host(fallible, wgas, cost = RuntimeCosts::ReplyInput)] pub fn reply_input( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, offset: u32, len: u32, value_ptr: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { // Charge for `len` is inside `reply_push_input` let value = Self::register_and_read_value(ctx, value_ptr)?; @@ -447,11 +521,11 @@ where #[host(fallible, cost = RuntimeCosts::ReplyPushInput, err = ErrorBytes)] pub fn reply_push_input( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, offset: u32, len: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { ctx.ext_mut() .reply_push_input(offset, len) .map_err(Into::into) @@ -460,15 +534,15 @@ where #[allow(clippy::too_many_arguments)] #[host(fallible, wgas, cost = RuntimeCosts::SendInput)] pub fn send_input( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, pid_value_ptr: u32, offset: u32, len: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { // Charge for `len` inside `send_push_input` - let read_pid_value = ctx.register_read_as(pid_value_ptr); + let read_pid_value = ctx.manager.register_read_as(pid_value_ptr); let HashWithValue { hash: destination, value, @@ -489,12 +563,12 @@ where #[host(fallible, cost = RuntimeCosts::SendPushInput, err = ErrorBytes)] pub fn send_push_input( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, handle: u32, offset: u32, len: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { ctx.ext_mut() .send_push_input(handle, offset, len) .map_err(Into::into) @@ -502,12 +576,12 @@ where #[host(cost = RuntimeCosts::Debug(data_len))] pub fn debug( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, data_ptr: u32, data_len: u32, - ) -> Result<(u64, ()), R::Error> { - let read_data = ctx.register_read(data_ptr, data_len); + ) -> Result<(u64, ()), HostError> { + let read_data = ctx.manager.register_read(data_ptr, data_len); let data: RuntimeBuffer = ctx .read(read_data)? .try_into() @@ -526,12 +600,12 @@ where #[host(cost = RuntimeCosts::Null)] pub fn panic( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, data_ptr: u32, data_len: u32, - ) -> Result<(u64, ()), R::Error> { - let read_data = ctx.register_read(data_ptr, data_len); + ) -> Result<(u64, ()), HostError> { + let read_data = ctx.manager.register_read(data_ptr, data_len); let data = ctx.read(read_data).unwrap_or_default(); let s = String::from_utf8_lossy(&data).to_string(); @@ -540,17 +614,17 @@ where } #[host(cost = RuntimeCosts::Null)] - pub fn oom_panic(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn oom_panic(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { Err(ActorTerminationReason::Trap(TrapExplanation::ProgramAllocOutOfBounds).into()) } #[host(fallible, cost = RuntimeCosts::ReserveGas)] pub fn reserve_gas( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, gas_value: u64, duration: u32, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { ctx.ext_mut() .reserve_gas(gas_value, duration) .map_err(Into::into) @@ -558,12 +632,12 @@ where #[host(fallible, cost = RuntimeCosts::ReplyDeposit, err = ErrorBytes)] pub fn reply_deposit( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, message_id_ptr: u32, gas_value: u64, - ) -> Result<(u64, ()), R::Error> { - let read_message_id = ctx.register_read_decoded(message_id_ptr); + ) -> Result<(u64, ()), HostError> { + let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); let message_id = ctx.read_decoded(read_message_id)?; ctx.ext_mut() @@ -573,11 +647,11 @@ where #[host(fallible, cost = RuntimeCosts::UnreserveGas, err = ErrorWithGas)] pub fn unreserve_gas( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, reservation_id_ptr: u32, - ) -> Result<(u64, ()), R::Error> { - let read_reservation_id = ctx.register_read_decoded(reservation_id_ptr); + ) -> Result<(u64, ()), HostError> { + let read_reservation_id = ctx.manager.register_read_decoded(reservation_id_ptr); let reservation_id = ctx.read_decoded(read_reservation_id)?; ctx.ext_mut() @@ -587,49 +661,61 @@ where #[host(fallible, cost = RuntimeCosts::SystemReserveGas, err = ErrorBytes)] pub fn system_reserve_gas( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, gas_value: u64, - ) -> Result<(u64, ()), R::Error> { + ) -> Result<(u64, ()), HostError> { ctx.ext_mut() .system_reserve_gas(gas_value) .map_err(Into::into) } #[host(cost = RuntimeCosts::GasAvailable)] - pub fn gas_available(ctx: &mut R, gas: u64, gas_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn gas_available( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + gas_ptr: u32, + ) -> Result<(u64, ()), HostError> { let gas_available = ctx.ext_mut().gas_available()?; - let write_gas = ctx.register_write_as(gas_ptr); + let write_gas = ctx.manager.register_write_as(gas_ptr); ctx.write_as(write_gas, gas_available.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::MsgId)] - pub fn message_id(ctx: &mut R, gas: u64, message_id_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn message_id( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + message_id_ptr: u32, + ) -> Result<(u64, ()), HostError> { let message_id = ctx.ext_mut().message_id()?; - let write_message_id = ctx.register_write_as(message_id_ptr); + let write_message_id = ctx.manager.register_write_as(message_id_ptr); ctx.write_as(write_message_id, message_id.into_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::ProgramId)] - pub fn program_id(ctx: &mut R, gas: u64, program_id_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn program_id( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + program_id_ptr: u32, + ) -> Result<(u64, ()), HostError> { let program_id = ctx.ext_mut().program_id()?; - let write_program_id = ctx.register_write_as(program_id_ptr); + let write_program_id = ctx.manager.register_write_as(program_id_ptr); ctx.write_as(write_program_id, program_id.into_bytes()) .map_err(Into::into) } #[host(fallible, cost = RuntimeCosts::PayProgramRent, err = ErrorWithBlockNumberAndValue)] pub fn pay_program_rent( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, rent_pid_ptr: u32, - ) -> Result<(u64, ()), R::Error> { - let read_rent_pid = ctx.register_read_as(rent_pid_ptr); + ) -> Result<(u64, ()), HostError> { + let read_rent_pid = ctx.manager.register_read_as(rent_pid_ptr); let HashWithValue { hash: program_id, @@ -642,51 +728,71 @@ where } #[host(cost = RuntimeCosts::Source)] - pub fn source(ctx: &mut R, gas: u64, source_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn source( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + source_ptr: u32, + ) -> Result<(u64, ()), HostError> { let source = ctx.ext_mut().source()?; - let write_source = ctx.register_write_as(source_ptr); + let write_source = ctx.manager.register_write_as(source_ptr); ctx.write_as(write_source, source.into_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::Value)] - pub fn value(ctx: &mut R, gas: u64, value_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn value( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + value_ptr: u32, + ) -> Result<(u64, ()), HostError> { let value = ctx.ext_mut().value()?; - let write_value = ctx.register_write_as(value_ptr); + let write_value = ctx.manager.register_write_as(value_ptr); ctx.write_as(write_value, value.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::ValueAvailable)] - pub fn value_available(ctx: &mut R, gas: u64, value_ptr: u32) -> Result<(u64, ()), R::Error> { + pub fn value_available( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + value_ptr: u32, + ) -> Result<(u64, ()), HostError> { let value_available = ctx.ext_mut().value_available()?; - let write_value = ctx.register_write_as(value_ptr); + let write_value = ctx.manager.register_write_as(value_ptr); ctx.write_as(write_value, value_available.to_le_bytes()) .map_err(Into::into) } #[host(cost = RuntimeCosts::Leave)] - pub fn leave(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn leave(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { Err(ActorTerminationReason::Leave.into()) } #[host(cost = RuntimeCosts::Wait)] - pub fn wait(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn wait(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { ctx.ext_mut().wait()?; Err(ActorTerminationReason::Wait(None, MessageWaitedType::Wait).into()) } #[host(cost = RuntimeCosts::WaitFor)] - pub fn wait_for(ctx: &mut R, gas: u64, duration: u32) -> Result<(u64, ()), R::Error> { + pub fn wait_for( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + duration: u32, + ) -> Result<(u64, ()), HostError> { ctx.ext_mut().wait_for(duration)?; Err(ActorTerminationReason::Wait(Some(duration), MessageWaitedType::WaitFor).into()) } #[host(cost = RuntimeCosts::WaitUpTo)] - pub fn wait_up_to(ctx: &mut R, gas: u64, duration: u32) -> Result<(u64, ()), R::Error> { + pub fn wait_up_to( + ctx: &mut CallerWrap<'_, '_, Ext>, + gas: u64, + duration: u32, + ) -> Result<(u64, ()), HostError> { let waited_type = if ctx.ext_mut().wait_up_to(duration)? { MessageWaitedType::WaitUpToFull } else { @@ -697,12 +803,12 @@ where #[host(fallible, cost = RuntimeCosts::Wake, err = ErrorBytes)] pub fn wake( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, message_id_ptr: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_message_id = ctx.register_read_decoded(message_id_ptr); + ) -> Result<(u64, ()), HostError> { + let read_message_id = ctx.manager.register_read_decoded(message_id_ptr); let message_id = ctx.read_decoded(read_message_id)?; ctx.ext_mut().wake(message_id, delay).map_err(Into::into) @@ -711,7 +817,7 @@ where #[allow(clippy::too_many_arguments)] #[host(fallible, wgas, cost = RuntimeCosts::CreateProgram(payload_len, salt_len), err = ErrorWithTwoHashes)] pub fn create_program( - ctx: &mut R, + ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64, cid_value_ptr: u32, salt_ptr: u32, @@ -719,10 +825,10 @@ where payload_ptr: u32, payload_len: u32, delay: u32, - ) -> Result<(u64, ()), R::Error> { - let read_cid_value = ctx.register_read_as(cid_value_ptr); - let read_salt = ctx.register_read(salt_ptr, salt_len); - let read_payload = ctx.register_read(payload_ptr, payload_len); + ) -> Result<(u64, ()), HostError> { + let read_cid_value = ctx.manager.register_read_as(cid_value_ptr); + let read_salt = ctx.manager.register_read(salt_ptr, salt_len); + let read_payload = ctx.manager.register_read(payload_ptr, payload_len); let HashWithValue { hash: code_id, value, @@ -740,7 +846,7 @@ where .map_err(Into::into) } - pub fn forbidden(ctx: &mut R, gas: u64) -> Result<(u64, ()), R::Error> { + pub fn forbidden(ctx: &mut CallerWrap<'_, '_, Ext>, gas: u64) -> Result<(u64, ()), HostError> { syscall_trace!("forbidden"); ctx.run_any(gas, RuntimeCosts::Null, |_| { @@ -748,7 +854,10 @@ where }) } - pub fn out_of_gas(ctx: &mut R, _gas: u64) -> Result<(u64, ()), R::Error> { + pub fn out_of_gas( + ctx: &mut CallerWrap<'_, '_, Ext>, + _gas: u64, + ) -> Result<(u64, ()), HostError> { syscall_trace!("out_of_gas"); let ext = ctx.ext_mut(); @@ -764,6 +873,6 @@ where let termination_reason: ActorTerminationReason = current_counter.into(); ctx.set_termination_reason(termination_reason.into()); - Err(R::unreachable_error()) + Err(HostError) } } diff --git a/core-backend/sandbox/src/lib.rs b/core-backend/src/lib.rs similarity index 58% rename from core-backend/sandbox/src/lib.rs rename to core-backend/src/lib.rs index 196e42589f6..1a20d9bae1f 100644 --- a/core-backend/sandbox/src/lib.rs +++ b/core-backend/src/lib.rs @@ -23,8 +23,32 @@ extern crate alloc; pub mod env; +pub mod error; +mod funcs; pub mod memory; -pub mod runtime; - -pub use env::SandboxEnvironment; -pub use memory::{DefaultExecutorMemory, MemoryWrap}; +#[cfg(any(feature = "mock", test))] +pub mod mock; +mod runtime; +mod state; + +use gear_core::{ + env::Externalities, + gas::{CountersOwner, GasAmount}, + memory::MemoryInterval, +}; +use gear_lazy_pages_common::ProcessAccessError; + +/// Extended externalities that can manage gas counters. +pub trait BackendExternalities: Externalities + CountersOwner { + fn gas_amount(&self) -> GasAmount; + + /// Pre-process memory access if need. + fn pre_process_memory_accesses( + reads: &[MemoryInterval], + writes: &[MemoryInterval], + gas_counter: &mut u64, + ) -> Result<(), ProcessAccessError>; +} + +#[cfg(test)] +mod tests; diff --git a/core-backend/common/src/memory.rs b/core-backend/src/memory.rs similarity index 62% rename from core-backend/common/src/memory.rs rename to core-backend/src/memory.rs index 59a0a0a2fc3..626757962a5 100644 --- a/core-backend/common/src/memory.rs +++ b/core-backend/src/memory.rs @@ -16,40 +16,126 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Work with WASM program memory in backends. +//! sp-sandbox extensions for memory. use crate::{ - runtime::RunFallibleError, BackendExternalities, BackendSyscallError, TrapExplanation, - UndefinedTerminationReason, UnrecoverableMemoryError, + error::{ + BackendSyscallError, RunFallibleError, TrapExplanation, UndefinedTerminationReason, + UnrecoverableMemoryError, + }, + state::HostState, + BackendExternalities, }; use alloc::vec::Vec; -use core::{ - fmt::Debug, - marker::PhantomData, - mem, - mem::{size_of, MaybeUninit}, - result::Result, - slice, -}; +use codec::{Decode, DecodeAll, MaxEncodedLen}; +use core::{marker::PhantomData, mem, mem::MaybeUninit, slice}; use gear_core::{ buffer::{RuntimeBuffer, RuntimeBufferSizeError}, - memory::{Memory, MemoryError, MemoryInterval}, + env::Externalities, + memory::{HostPointer, Memory, MemoryError, MemoryInterval}, + pages::{PageNumber, PageU32Size, WasmPage}, }; use gear_core_errors::MemoryError as FallibleMemoryError; -use num_enum::{IntoPrimitive, TryFromPrimitive}; -use scale_info::scale::{Decode, DecodeAll, MaxEncodedLen}; - -/// Memory access error during sys-call that lazy-pages have caught. -/// 0 index is reserved for an ok result. -#[derive(Debug, Clone, IntoPrimitive, TryFromPrimitive)] -#[repr(u8)] -pub enum ProcessAccessError { - OutOfBounds = 1, - GasLimitExceeded = 2, +use gear_lazy_pages_common::ProcessAccessError; +use gear_sandbox::{ + default_executor::{Caller, Store}, + SandboxMemory, +}; + +pub type ExecutorMemory = gear_sandbox::default_executor::Memory; + +pub(crate) struct MemoryWrapRef<'a, 'b: 'a, Ext: Externalities + 'static> { + pub memory: ExecutorMemory, + pub caller: &'a mut Caller<'b, HostState>, +} + +impl Memory for MemoryWrapRef<'_, '_, Ext> { + type GrowError = gear_sandbox::Error; + + fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { + self.memory.grow(self.caller, pages.raw()).map(|_| ()) + } + + fn size(&self) -> WasmPage { + WasmPage::new(self.memory.size(self.caller)) + .expect("Unexpected backend behavior: wasm size is bigger then u32::MAX") + } + + fn write(&mut self, offset: u32, buffer: &[u8]) -> Result<(), MemoryError> { + self.memory + .write(self.caller, offset, buffer) + .map_err(|_| MemoryError::AccessOutOfBounds) + } + + fn read(&self, offset: u32, buffer: &mut [u8]) -> Result<(), MemoryError> { + self.memory + .read(self.caller, offset, buffer) + .map_err(|_| MemoryError::AccessOutOfBounds) + } + + unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { + self.memory.get_buff(self.caller) as HostPointer + } +} + +/// Wrapper for executor memory. +pub struct MemoryWrap +where + Ext: Externalities + 'static, +{ + pub(crate) memory: ExecutorMemory, + pub(crate) store: Store>, +} + +impl MemoryWrap +where + Ext: Externalities + 'static, +{ + /// Wrap [`ExecutorMemory`] for Memory trait. + pub fn new(memory: ExecutorMemory, store: Store>) -> Self { + MemoryWrap { memory, store } + } + + pub(crate) fn into_store(self) -> Store> { + self.store + } +} + +/// Memory interface for the allocator. +impl Memory for MemoryWrap +where + Ext: Externalities + 'static, +{ + type GrowError = gear_sandbox::Error; + + fn grow(&mut self, pages: WasmPage) -> Result<(), Self::GrowError> { + self.memory.grow(&mut self.store, pages.raw()).map(|_| ()) + } + + fn size(&self) -> WasmPage { + WasmPage::new(self.memory.size(&self.store)) + .expect("Unexpected backend behavior: wasm size is bigger then u32::MAX") + } + + fn write(&mut self, offset: u32, buffer: &[u8]) -> Result<(), MemoryError> { + self.memory + .write(&mut self.store, offset, buffer) + .map_err(|_| MemoryError::AccessOutOfBounds) + } + + fn read(&self, offset: u32, buffer: &mut [u8]) -> Result<(), MemoryError> { + self.memory + .read(&self.store, offset, buffer) + .map_err(|_| MemoryError::AccessOutOfBounds) + } + + unsafe fn get_buffer_host_addr_unsafe(&mut self) -> HostPointer { + self.memory.get_buff(&mut self.store) as HostPointer + } } #[derive(Debug, Clone, derive_more::From)] -pub enum MemoryAccessError { +pub(crate) enum MemoryAccessError { Memory(MemoryError), ProcessAccess(ProcessAccessError), RuntimeBuffer(RuntimeBufferSizeError), @@ -98,51 +184,6 @@ impl BackendSyscallError for MemoryAccessError { } } -/// Memory accesses recorder/registrar, which allow to register new accesses. -pub trait MemoryAccessRecorder { - /// Register new read access. - fn register_read(&mut self, ptr: u32, size: u32) -> WasmMemoryRead; - - /// Register new read static size type access. - fn register_read_as(&mut self, ptr: u32) -> WasmMemoryReadAs; - - /// Register new read decoded type access. - fn register_read_decoded( - &mut self, - ptr: u32, - ) -> WasmMemoryReadDecoded; - - /// Register new write access. - fn register_write(&mut self, ptr: u32, size: u32) -> WasmMemoryWrite; - - /// Register new write static size access. - fn register_write_as(&mut self, ptr: u32) -> WasmMemoryWriteAs; -} - -pub trait MemoryOwner { - /// Read from owned memory to new byte vector. - fn read(&mut self, read: WasmMemoryRead) -> Result, MemoryAccessError>; - - /// Read from owned memory to new object `T`. - fn read_as(&mut self, read: WasmMemoryReadAs) -> Result; - - /// Read from owned memory and decoded data into object `T`. - fn read_decoded( - &mut self, - read: WasmMemoryReadDecoded, - ) -> Result; - - /// Write data from `buff` to owned memory. - fn write(&mut self, write: WasmMemoryWrite, buff: &[u8]) -> Result<(), MemoryAccessError>; - - /// Write data from `obj` to owned memory. - fn write_as( - &mut self, - write: WasmMemoryWriteAs, - obj: T, - ) -> Result<(), MemoryAccessError>; -} - /// Memory access manager. Allows to pre-register memory accesses, /// and pre-process, them together. For example: /// ```ignore @@ -160,7 +201,7 @@ pub trait MemoryOwner { /// manager.write_as(write1, 111).unwrap(); /// ``` #[derive(Debug)] -pub struct MemoryAccessManager { +pub(crate) struct MemoryAccessManager { // Contains non-zero length intervals only. pub(crate) reads: Vec, pub(crate) writes: Vec, @@ -177,16 +218,16 @@ impl Default for MemoryAccessManager { } } -impl MemoryAccessRecorder for MemoryAccessManager { - fn register_read(&mut self, ptr: u32, size: u32) -> WasmMemoryRead { +impl MemoryAccessManager { + pub fn register_read(&mut self, ptr: u32, size: u32) -> WasmMemoryRead { if size > 0 { self.reads.push(MemoryInterval { offset: ptr, size }); } WasmMemoryRead { ptr, size } } - fn register_read_as(&mut self, ptr: u32) -> WasmMemoryReadAs { - let size = size_of::() as u32; + pub fn register_read_as(&mut self, ptr: u32) -> WasmMemoryReadAs { + let size = mem::size_of::() as u32; if size > 0 { self.reads.push(MemoryInterval { offset: ptr, size }); } @@ -196,7 +237,7 @@ impl MemoryAccessRecorder for MemoryAccessManager { } } - fn register_read_decoded( + pub fn register_read_decoded( &mut self, ptr: u32, ) -> WasmMemoryReadDecoded { @@ -210,15 +251,15 @@ impl MemoryAccessRecorder for MemoryAccessManager { } } - fn register_write(&mut self, ptr: u32, size: u32) -> WasmMemoryWrite { + pub fn register_write(&mut self, ptr: u32, size: u32) -> WasmMemoryWrite { if size > 0 { self.writes.push(MemoryInterval { offset: ptr, size }); } WasmMemoryWrite { ptr, size } } - fn register_write_as(&mut self, ptr: u32) -> WasmMemoryWriteAs { - let size = size_of::() as u32; + pub fn register_write_as(&mut self, ptr: u32) -> WasmMemoryWriteAs { + let size = mem::size_of::() as u32; if size > 0 { self.writes.push(MemoryInterval { offset: ptr, size }); } @@ -395,31 +436,120 @@ fn read_memory_as(memory: &impl Memory, ptr: u32) -> Result { +pub(crate) struct WasmMemoryReadAs { pub(crate) ptr: u32, pub(crate) _phantom: PhantomData, } /// Read decoded type access wrapper. -pub struct WasmMemoryReadDecoded { +pub(crate) struct WasmMemoryReadDecoded { pub(crate) ptr: u32, pub(crate) _phantom: PhantomData, } /// Read access wrapper. -pub struct WasmMemoryRead { +pub(crate) struct WasmMemoryRead { pub(crate) ptr: u32, pub(crate) size: u32, } /// Write static size type access wrapper. -pub struct WasmMemoryWriteAs { +pub(crate) struct WasmMemoryWriteAs { pub(crate) ptr: u32, pub(crate) _phantom: PhantomData, } /// Write access wrapper. -pub struct WasmMemoryWrite { +pub(crate) struct WasmMemoryWrite { pub(crate) ptr: u32, pub(crate) size: u32, } + +/// can't be tested outside the node runtime +#[cfg(test)] +mod tests { + use super::*; + use crate::{error::ActorTerminationReason, mock::MockExt, state::State}; + use gear_core::memory::{AllocError, AllocationsContext, NoopGrowHandler}; + use gear_sandbox::{AsContextExt, SandboxStore}; + + fn new_test_memory( + static_pages: u16, + max_pages: u16, + ) -> (AllocationsContext, MemoryWrap) { + use gear_sandbox::SandboxMemory as WasmMemory; + + let mut store = Store::new(None); + let memory: ExecutorMemory = + WasmMemory::new(&mut store, static_pages as u32, Some(max_pages as u32)) + .expect("Memory creation failed"); + *store.data_mut() = Some(State { + ext: MockExt::default(), + memory: memory.clone(), + termination_reason: ActorTerminationReason::Success.into(), + }); + + let memory = MemoryWrap::new(memory, store); + + ( + AllocationsContext::new(Default::default(), static_pages.into(), max_pages.into()), + memory, + ) + } + + #[test] + fn smoky() { + let (mut ctx, mut mem_wrap) = new_test_memory(16, 256); + + assert_eq!( + ctx.alloc::(16.into(), &mut mem_wrap, |_| Ok(())) + .unwrap(), + 16.into() + ); + + assert_eq!( + ctx.alloc::(0.into(), &mut mem_wrap, |_| Ok(())) + .unwrap(), + 16.into() + ); + + // there is a space for 14 more + for _ in 0..14 { + ctx.alloc::(16.into(), &mut mem_wrap, |_| Ok(())) + .unwrap(); + } + + // no more mem! + assert_eq!( + ctx.alloc::(1.into(), &mut mem_wrap, |_| Ok(())), + Err(AllocError::ProgramAllocOutOfBounds) + ); + + // but we free some + ctx.free(137.into()).unwrap(); + + // and now can allocate page that was freed + assert_eq!( + ctx.alloc::(1.into(), &mut mem_wrap, |_| Ok(())), + Ok(137.into()) + ); + + // if we have 2 in a row we can allocate even 2 + ctx.free(117.into()).unwrap(); + ctx.free(118.into()).unwrap(); + + assert_eq!( + ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), + Ok(117.into()) + ); + + // but if 2 are not in a row, bad luck + ctx.free(117.into()).unwrap(); + ctx.free(158.into()).unwrap(); + + assert_eq!( + ctx.alloc::(2.into(), &mut mem_wrap, |_| Ok(())), + Err(AllocError::ProgramAllocOutOfBounds) + ); + } +} diff --git a/core-backend/common/src/mock.rs b/core-backend/src/mock.rs similarity index 90% rename from core-backend/common/src/mock.rs rename to core-backend/src/mock.rs index 9efb5f4dc21..4264aabedab 100644 --- a/core-backend/common/src/mock.rs +++ b/core-backend/src/mock.rs @@ -17,11 +17,13 @@ // along with this program. If not, see . use crate::{ - memory::ProcessAccessError, runtime::RunFallibleError, BackendAllocSyscallError, - BackendExternalities, BackendSyscallError, ExtInfo, SystemReservationContext, - UndefinedTerminationReason, + error::{ + BackendAllocSyscallError, BackendSyscallError, RunFallibleError, UndefinedTerminationReason, + }, + BackendExternalities, }; use alloc::{collections::BTreeSet, vec, vec::Vec}; +use codec::{Decode, Encode}; use core::{cell::Cell, fmt, fmt::Debug}; use gear_core::{ costs::RuntimeCosts, @@ -29,17 +31,16 @@ use gear_core::{ gas::{ChargeError, CounterType, CountersOwner, GasAmount, GasCounter, GasLeft}, ids::{MessageId, ProgramId, ReservationId}, memory::{Memory, MemoryError, MemoryInterval}, - message::{HandlePacket, IncomingDispatch, InitPacket, ReplyPacket}, + message::{HandlePacket, InitPacket, ReplyPacket}, pages::{PageNumber, PageU32Size, WasmPage, WASM_PAGE_SIZE}, - reservation::GasReserver, }; use gear_core_errors::{ReplyCode, SignalCode}; +use gear_lazy_pages_common::ProcessAccessError; use gear_wasm_instrument::syscalls::SysCallName; -use scale_info::scale::{self, Decode, Encode}; /// Mock error #[derive(Debug, Clone, Encode, Decode)] -#[codec(crate = scale)] +#[codec(crate = codec)] pub struct Error; impl fmt::Display for Error { @@ -277,26 +278,6 @@ impl Externalities for MockExt { } impl BackendExternalities for MockExt { - fn into_ext_info(self, _memory: &impl Memory) -> Result { - Ok(ExtInfo { - gas_amount: GasCounter::new(0).to_amount(), - gas_reserver: GasReserver::new( - &::default(), - Default::default(), - 1024, - ), - system_reservation_context: SystemReservationContext::default(), - allocations: Default::default(), - pages_data: Default::default(), - generated_dispatches: Default::default(), - awakening: Default::default(), - reply_deposits: Default::default(), - program_candidates_data: Default::default(), - program_rents: Default::default(), - context_store: Default::default(), - }) - } - fn gas_amount(&self) -> GasAmount { GasCounter::new(0).to_amount() } diff --git a/core-backend/sandbox/src/runtime.rs b/core-backend/src/runtime.rs similarity index 64% rename from core-backend/sandbox/src/runtime.rs rename to core-backend/src/runtime.rs index 73dc54c5a8f..c5ee4388ab3 100644 --- a/core-backend/sandbox/src/runtime.rs +++ b/core-backend/src/runtime.rs @@ -18,18 +18,17 @@ //! sp-sandbox runtime (here it's contract execution state) realization. -use crate::{memory::MemoryWrapRef, DefaultExecutorMemory}; -use alloc::vec::Vec; -use codec::{Decode, MaxEncodedLen}; -use gear_backend_common::{ +use crate::{ + error::{BackendAllocSyscallError, RunFallibleError, UndefinedTerminationReason}, memory::{ - MemoryAccessError, MemoryAccessManager, MemoryAccessRecorder, MemoryOwner, WasmMemoryRead, + ExecutorMemory, MemoryAccessError, MemoryAccessManager, MemoryWrapRef, WasmMemoryRead, WasmMemoryReadAs, WasmMemoryReadDecoded, WasmMemoryWrite, WasmMemoryWriteAs, }, - runtime::{RunFallibleError, Runtime as CommonRuntime}, state::{HostState, State}, - BackendExternalities, BackendState, UndefinedTerminationReason, + BackendExternalities, }; +use alloc::vec::Vec; +use codec::{Decode, MaxEncodedLen}; use gear_core::{costs::RuntimeCosts, pages::WasmPage}; use gear_sandbox::{default_executor::Caller, AsContextExt, HostError, Value}; @@ -42,8 +41,8 @@ pub(crate) fn as_i64(v: Value) -> Option { #[track_caller] pub(crate) fn caller_host_state_mut<'a, 'b: 'a, Ext>( - caller: &'a mut Caller<'_, HostState>, -) -> &'a mut State { + caller: &'a mut Caller<'_, HostState>, +) -> &'a mut State { caller .data_mut() .as_mut() @@ -52,8 +51,8 @@ pub(crate) fn caller_host_state_mut<'a, 'b: 'a, Ext>( #[track_caller] pub(crate) fn caller_host_state_take( - caller: &mut Caller<'_, HostState>, -) -> State { + caller: &mut Caller<'_, HostState>, +) -> State { caller .data_mut() .take() @@ -61,24 +60,23 @@ pub(crate) fn caller_host_state_take( } pub(crate) struct CallerWrap<'a, 'b: 'a, Ext> { - pub caller: &'a mut Caller<'b, HostState>, + pub caller: &'a mut Caller<'b, HostState>, pub manager: MemoryAccessManager, - pub memory: DefaultExecutorMemory, + pub memory: ExecutorMemory, } -impl<'a, 'b, Ext: BackendExternalities + 'static> CommonRuntime for CallerWrap<'a, 'b, Ext> { - type Error = HostError; - - fn ext_mut(&mut self) -> &mut Ext { +impl<'a, 'b, Ext: BackendExternalities + 'static> CallerWrap<'a, 'b, Ext> { + pub fn ext_mut(&mut self) -> &mut Ext { &mut self.host_state_mut().ext } - fn unreachable_error() -> Self::Error { - HostError - } - #[track_caller] - fn run_any(&mut self, gas: u64, cost: RuntimeCosts, f: F) -> Result<(u64, T), Self::Error> + pub fn run_any( + &mut self, + gas: u64, + cost: RuntimeCosts, + f: F, + ) -> Result<(u64, T), HostError> where F: FnOnce(&mut Self) -> Result, { @@ -98,13 +96,13 @@ impl<'a, 'b, Ext: BackendExternalities + 'static> CommonRuntime for CallerW } #[track_caller] - fn run_fallible( + pub fn run_fallible( &mut self, gas: u64, res_ptr: u32, cost: RuntimeCosts, f: F, - ) -> Result<(u64, ()), Self::Error> + ) -> Result<(u64, ()), HostError> where F: FnOnce(&mut Self) -> Result, R: From> + Sized, @@ -114,29 +112,27 @@ impl<'a, 'b, Ext: BackendExternalities + 'static> CommonRuntime for CallerW cost, |ctx: &mut Self| -> Result<_, UndefinedTerminationReason> { let res = f(ctx); - let res = ctx.host_state_mut().process_fallible_func_result(res)?; + let res = ctx.process_fallible_func_result(res)?; // TODO: move above or make normal process memory access. - let write_res = ctx.register_write_as::(res_ptr); + let write_res = ctx.manager.register_write_as::(res_ptr); ctx.write_as(write_res, R::from(res)).map_err(Into::into) }, ) } - fn alloc(&mut self, pages: u32) -> Result::AllocError> { + pub fn alloc(&mut self, pages: u32) -> Result::AllocError> { let mut state = caller_host_state_take(self.caller); let mut mem = CallerWrap::memory(self.caller, self.memory.clone()); let res = state.ext.alloc(pages, &mut mem); self.caller.data_mut().replace(state); res } -} -impl<'a, 'b, Ext: BackendExternalities + 'static> CallerWrap<'a, 'b, Ext> { #[track_caller] pub fn prepare( - caller: &'a mut Caller<'b, HostState>, + caller: &'a mut Caller<'b, HostState>, ) -> Result { let memory = caller_host_state_mut(caller).memory.clone(); Ok(Self { @@ -147,14 +143,14 @@ impl<'a, 'b, Ext: BackendExternalities + 'static> CallerWrap<'a, 'b, Ext> { } #[track_caller] - pub fn host_state_mut(&mut self) -> &mut State { + pub fn host_state_mut(&mut self) -> &mut State { caller_host_state_mut(self.caller) } #[track_caller] pub fn memory<'c, 'd: 'c>( - caller: &'c mut Caller<'d, HostState>, - memory: DefaultExecutorMemory, + caller: &'c mut Caller<'d, HostState>, + memory: ExecutorMemory, ) -> MemoryWrapRef<'c, 'd, Ext> { MemoryWrapRef::<'c, 'd, _> { memory, caller } } @@ -179,49 +175,52 @@ impl<'a, 'b, Ext: BackendExternalities + 'static> CallerWrap<'a, 'b, Ext> { .decrease_current_counter_to(gas_counter); res } -} - -impl<'a, 'b, Ext> MemoryAccessRecorder for CallerWrap<'a, 'b, Ext> { - fn register_read(&mut self, ptr: u32, size: u32) -> WasmMemoryRead { - self.manager.register_read(ptr, size) - } - fn register_read_as(&mut self, ptr: u32) -> WasmMemoryReadAs { - self.manager.register_read_as(ptr) + pub fn set_termination_reason(&mut self, reason: UndefinedTerminationReason) { + self.host_state_mut().termination_reason = reason; } - fn register_read_decoded( + /// Process fallible syscall function result + pub fn process_fallible_func_result( &mut self, - ptr: u32, - ) -> WasmMemoryReadDecoded { - self.manager.register_read_decoded(ptr) - } - - fn register_write(&mut self, ptr: u32, size: u32) -> WasmMemoryWrite { - self.manager.register_write(ptr, size) - } - - fn register_write_as(&mut self, ptr: u32) -> WasmMemoryWriteAs { - self.manager.register_write_as(ptr) - } -} - -impl BackendState for CallerWrap<'_, '_, Ext> { - fn set_termination_reason(&mut self, reason: UndefinedTerminationReason) { - self.host_state_mut().set_termination_reason(reason); + res: Result, + ) -> Result, UndefinedTerminationReason> { + match res { + Err(RunFallibleError::FallibleExt(ext_err)) => { + let code = ext_err.to_u32(); + log::trace!(target: "syscalls", "fallible syscall error: {ext_err}"); + Ok(Err(code)) + } + Err(RunFallibleError::UndefinedTerminationReason(reason)) => Err(reason), + Ok(res) => Ok(Ok(res)), + } + } + + /// Process alloc function result + pub fn process_alloc_func_result( + &mut self, + res: Result, + ) -> Result, UndefinedTerminationReason> { + match res { + Ok(t) => Ok(Ok(t)), + Err(err) => match err.into_backend_error() { + Ok(ext_err) => Err(ext_err.into()), + Err(alloc_err) => Ok(Err(alloc_err)), + }, + } } } -impl<'a, 'b, Ext: BackendExternalities + 'static> MemoryOwner for CallerWrap<'a, 'b, Ext> { - fn read(&mut self, read: WasmMemoryRead) -> Result, MemoryAccessError> { +impl<'a, 'b, Ext: BackendExternalities + 'static> CallerWrap<'a, 'b, Ext> { + pub fn read(&mut self, read: WasmMemoryRead) -> Result, MemoryAccessError> { self.with_memory(|manager, memory, gas_left| manager.read(memory, read, gas_left)) } - fn read_as(&mut self, read: WasmMemoryReadAs) -> Result { + pub fn read_as(&mut self, read: WasmMemoryReadAs) -> Result { self.with_memory(|manager, memory, gas_left| manager.read_as(memory, read, gas_left)) } - fn read_decoded( + pub fn read_decoded( &mut self, read: WasmMemoryReadDecoded, ) -> Result { @@ -230,13 +229,13 @@ impl<'a, 'b, Ext: BackendExternalities + 'static> MemoryOwner for CallerWrap<'a, }) } - fn write(&mut self, write: WasmMemoryWrite, buff: &[u8]) -> Result<(), MemoryAccessError> { + pub fn write(&mut self, write: WasmMemoryWrite, buff: &[u8]) -> Result<(), MemoryAccessError> { self.with_memory(move |manager, memory, gas_left| { manager.write(memory, write, buff, gas_left) }) } - fn write_as( + pub fn write_as( &mut self, write: WasmMemoryWriteAs, obj: T, diff --git a/core-backend/src/state.rs b/core-backend/src/state.rs new file mode 100644 index 00000000000..6159391c390 --- /dev/null +++ b/core-backend/src/state.rs @@ -0,0 +1,96 @@ +// This file is part of Gear. + +// Copyright (C) 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::{ + error::{ + ActorTerminationReason, TerminationReason, TrapExplanation, UndefinedTerminationReason, + }, + BackendExternalities, +}; +use core::fmt::Debug; + +pub type HostState = Option>; + +pub struct State { + pub ext: Ext, + pub memory: Mem, + pub termination_reason: UndefinedTerminationReason, +} + +impl State { + /// Transforms [`Self`] into tuple of externalities, memory and + /// termination reason returned after the execution. + pub fn into_parts(self) -> (Ext, UndefinedTerminationReason) { + let State { + ext, + termination_reason, + .. + } = self; + (ext, termination_reason) + } + + /// Terminates backend work after execution. + /// + /// The function handles `res`, which is the result of gear wasm + /// program entry point invocation, and the termination reason. + /// + /// If the `res` is `Ok`, then execution considered successful + /// and the termination reason will have the corresponding value. + /// + /// If the `res` is `Err`, then execution is considered to end + /// with an error and the actual termination reason, which stores + /// more precise information about the error, is returned. + /// + /// There's a case, when `res` is `Err`, but termination reason has + /// a value for the successful ending of the execution. This is the + /// case of calling `unreachable` panic in the program. + pub fn terminate( + self, + res: Result, + gas: u64, + ) -> (Ext, TerminationReason) { + log::trace!("Execution result = {res:?}"); + + let (mut ext, termination_reason) = self.into_parts(); + let termination_reason = termination_reason.define(ext.current_counter_type()); + + ext.decrease_current_counter_to(gas); + + let termination_reason = if res.is_err() { + if matches!( + termination_reason, + TerminationReason::Actor(ActorTerminationReason::Success) + ) { + ActorTerminationReason::Trap(TrapExplanation::Unknown).into() + } else { + termination_reason + } + } else if matches!( + termination_reason, + TerminationReason::Actor(ActorTerminationReason::Success) + ) { + termination_reason + } else { + unreachable!( + "Termination reason is not success, but executor successfully ends execution" + ) + }; + + (ext, termination_reason) + } +} diff --git a/core-backend/common/src/tests.rs b/core-backend/src/tests.rs similarity index 97% rename from core-backend/common/src/tests.rs rename to core-backend/src/tests.rs index b067de28eb0..f2ca2f96de9 100644 --- a/core-backend/common/src/tests.rs +++ b/core-backend/src/tests.rs @@ -1,16 +1,18 @@ -use super::*; use crate::{ - memory::*, + memory::{ + MemoryAccessManager, WasmMemoryRead, WasmMemoryReadAs, WasmMemoryReadDecoded, + WasmMemoryWrite, WasmMemoryWriteAs, + }, mock::{MockExt, MockMemory}, }; - +use codec::{self, Decode, Encode, MaxEncodedLen}; use core::{fmt::Debug, marker::PhantomData}; use gear_core::{memory::Memory, pages::WASM_PAGE_SIZE}; -use scale_info::scale::{self, Decode, Encode, MaxEncodedLen}; const GAS_COUNTER: u64 = u64::MAX; #[derive(Encode, Decode, MaxEncodedLen)] +#[codec(crate = codec)] struct ZeroSizeStruct; #[test] @@ -150,7 +152,7 @@ fn test_read_with_empty_memory_access() { #[test] fn test_read_decoded_with_valid_encoded_data() { #[derive(Encode, Decode, Debug, PartialEq)] - #[codec(crate = scale)] + #[codec(crate = codec)] struct MockEncodeData { data: u64, } @@ -182,13 +184,13 @@ fn test_read_decoded_with_invalid_encoded_data() { struct InvalidDecode {} impl Decode for InvalidDecode { - fn decode(_input: &mut T) -> Result { + fn decode(_input: &mut T) -> Result { Err("Invalid decoding".into()) } } impl Encode for InvalidDecode { - fn encode_to(&self, _dest: &mut T) {} + fn encode_to(&self, _dest: &mut T) {} } impl MaxEncodedLen for InvalidDecode { @@ -491,7 +493,7 @@ fn test_register_read_as_with_zero_size() { } #[derive(Debug, PartialEq, Eq, Encode, Decode, MaxEncodedLen)] -#[codec(crate = scale)] +#[codec(crate = codec)] struct TestStruct { a: u32, b: u64, diff --git a/core-processor/Cargo.toml b/core-processor/Cargo.toml index cc07d9088ee..aa54468fbbd 100644 --- a/core-processor/Cargo.toml +++ b/core-processor/Cargo.toml @@ -13,9 +13,10 @@ repository.workspace = true [dependencies] gear-core.workspace = true gear-core-errors = { workspace = true, features = ["codec"] } -gear-backend-common.workspace = true +gear-core-backend.workspace = true gear-wasm-instrument.workspace = true gear-lazy-pages-common.workspace = true +gear-lazy-pages-interface.workspace = true scale-info = { workspace = true, features = ["derive"] } log.workspace = true @@ -29,6 +30,6 @@ enum-iterator.workspace = true [features] default = ["std"] -std = ["gear-lazy-pages-common/std"] +std = ["gear-lazy-pages-interface/std", "gear-core-backend/std"] strict = [] mock = [] diff --git a/core-processor/src/common.rs b/core-processor/src/common.rs index 16d0f842f07..748293b0013 100644 --- a/core-processor/src/common.rs +++ b/core-processor/src/common.rs @@ -19,7 +19,8 @@ //! Common structures for processing. use crate::{ - executor::SystemPrepareMemoryError, precharge::PreChargeGasOperation, ActorPrepareMemoryError, + context::SystemReservationContext, executor::SystemPrepareMemoryError, + precharge::PreChargeGasOperation, ActorPrepareMemoryError, }; use actor_system_error::actor_system_error; use alloc::{ @@ -27,7 +28,6 @@ use alloc::{ string::String, vec::Vec, }; -use gear_backend_common::{SystemReservationContext, SystemTerminationReason, TrapExplanation}; use gear_core::{ gas::{GasAllowanceCounter, GasAmount, GasCounter}, ids::{CodeId, MessageId, ProgramId, ReservationId}, @@ -39,6 +39,10 @@ use gear_core::{ program::Program, reservation::{GasReservationMap, GasReserver}, }; +use gear_core_backend::{ + env::SystemEnvironmentError, + error::{SystemTerminationReason, TrapExplanation}, +}; use gear_core_errors::{SignalCode, SimpleExecutionError}; use scale_info::scale::{self, Decode, Encode}; @@ -492,7 +496,7 @@ pub enum SystemExecutionError { PrepareMemory(SystemPrepareMemoryError), /// Environment error #[display(fmt = "Backend error: {_0}")] - Environment(String), + Environment(SystemEnvironmentError), /// Termination reason #[from] #[display(fmt = "Syscall function error: {_0}")] diff --git a/core-processor/src/configs.rs b/core-processor/src/configs.rs index e46e856781d..5d04d70271b 100644 --- a/core-processor/src/configs.rs +++ b/core-processor/src/configs.rs @@ -19,11 +19,11 @@ //! Configurations. use alloc::{collections::BTreeSet, vec::Vec}; -use gear_backend_common::lazy_pages::LazyPagesWeights; use gear_core::{ costs::{CostPerPage, HostFnWeights}, pages::{GearPage, WasmPage}, }; +use gear_lazy_pages_common::LazyPagesWeights; use gear_wasm_instrument::syscalls::SysCallName; use scale_info::scale::{self, Decode, Encode}; diff --git a/core-processor/src/context.rs b/core-processor/src/context.rs index 7f647aec2c0..0946b1d35d1 100644 --- a/core-processor/src/context.rs +++ b/core-processor/src/context.rs @@ -157,3 +157,27 @@ impl ProcessExecutionContext { &self.program } } + +#[derive(Debug, Default)] +pub struct SystemReservationContext { + /// Reservation created in current execution. + pub current_reservation: Option, + /// Reservation from `ContextStore`. + pub previous_reservation: Option, +} + +impl SystemReservationContext { + pub fn from_dispatch(dispatch: &IncomingDispatch) -> Self { + Self { + current_reservation: None, + previous_reservation: dispatch + .context() + .as_ref() + .and_then(|ctx| ctx.system_reservation()), + } + } + + pub fn has_any(&self) -> bool { + self.current_reservation.is_some() || self.previous_reservation.is_some() + } +} diff --git a/core-processor/src/executor.rs b/core-processor/src/executor.rs index c1fa182a76e..1f71adaf08f 100644 --- a/core-processor/src/executor.rs +++ b/core-processor/src/executor.rs @@ -31,15 +31,10 @@ use alloc::{ string::{String, ToString}, vec::Vec, }; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, LazyPagesWeights}, - ActorTerminationReason, BackendExternalities, BackendReport, BackendSyscallError, Environment, - EnvironmentError, TerminationReason, -}; use gear_core::{ code::InstrumentedCode, env::Externalities, - gas::{CountersOwner, GasAllowanceCounter, GasCounter, ValueCounter}, + gas::{GasAllowanceCounter, GasCounter, ValueCounter}, ids::ProgramId, memory::{AllocationsContext, Memory}, message::{ @@ -50,6 +45,16 @@ use gear_core::{ program::Program, reservation::GasReserver, }; +use gear_core_backend::{ + env::{BackendReport, Environment, EnvironmentError}, + error::{ + ActorTerminationReason, BackendAllocSyscallError, BackendSyscallError, RunFallibleError, + TerminationReason, + }, + memory::MemoryWrap, + BackendExternalities, +}; +use gear_lazy_pages_common::{GlobalsAccessConfig, LazyPagesWeights}; use scale_info::{ scale::{self, Decode, Encode}, TypeInfo, @@ -135,7 +140,7 @@ fn prepare_memory( } /// Execute wasm with dispatch and return dispatch result. -pub fn execute_wasm( +pub fn execute_wasm( balance: u128, dispatch: IncomingDispatch, context: WasmExecutionContext, @@ -143,9 +148,11 @@ pub fn execute_wasm( msg_ctx_settings: ContextSettings, ) -> Result where - E: Environment, - E::Ext: ProcessorExternalities + BackendExternalities + 'static, - ::UnrecoverableError: BackendSyscallError, + Ext: ProcessorExternalities + BackendExternalities + 'static, + ::AllocError: + BackendAllocSyscallError, + RunFallibleError: From, + ::UnrecoverableError: BackendSyscallError, { let WasmExecutionContext { gas_counter, @@ -218,11 +225,11 @@ where let lazy_pages_weights = context.page_costs.lazy_pages_weights(); // Creating externalities. - let ext = E::Ext::new(context); + let ext = Ext::new(context); // Execute program in backend env. let execute = || { - let env = E::new( + let env = Environment::new( ext, program.code_bytes(), kind, @@ -231,7 +238,7 @@ where ) .map_err(EnvironmentError::from_infallible)?; env.execute(|memory, stack_end, globals_config| { - prepare_memory::( + prepare_memory::>( memory, program_id, static_pages, @@ -257,18 +264,16 @@ where }; // released pages initial data will be added to `pages_initial_data` after execution. - E::Ext::lazy_pages_post_execution_actions(&mut memory); + Ext::lazy_pages_post_execution_actions(&mut memory); - if !E::Ext::lazy_pages_status().is_normal() { + if !Ext::lazy_pages_status().is_normal() { termination = ext.current_counter_type().into() } (termination, memory, ext) } Err(EnvironmentError::System(e)) => { - return Err(ExecutionError::System(SystemExecutionError::Environment( - e.to_string(), - ))) + return Err(ExecutionError::System(SystemExecutionError::Environment(e))) } Err(EnvironmentError::PrepareMemory(gas_amount, PrepareMemoryError::Actor(e))) => { return Err(ExecutionError::Actor(ActorExecutionError { @@ -338,7 +343,7 @@ where /// !!! FOR TESTING / INFORMATIONAL USAGE ONLY #[allow(clippy::too_many_arguments)] -pub fn execute_for_reply( +pub fn execute_for_reply( function: EP, instrumented_code: InstrumentedCode, allocations: Option>, @@ -348,9 +353,11 @@ pub fn execute_for_reply( block_info: BlockInfo, ) -> Result, String> where - E: Environment, - E::Ext: ProcessorExternalities + BackendExternalities + 'static, - ::UnrecoverableError: BackendSyscallError, + Ext: ProcessorExternalities + BackendExternalities + 'static, + ::AllocError: + BackendAllocSyscallError, + RunFallibleError: From, + ::UnrecoverableError: BackendSyscallError, EP: WasmEntryPoint, { let program = Program::new(program_id.unwrap_or_default(), instrumented_code); @@ -413,11 +420,11 @@ where let lazy_pages_weights = context.page_costs.lazy_pages_weights(); // Creating externalities. - let ext = E::Ext::new(context); + let ext = Ext::new(context); // Execute program in backend env. let f = || { - let env = E::new( + let env = Environment::new( ext, program.code_bytes(), function, @@ -426,7 +433,7 @@ where ) .map_err(EnvironmentError::from_infallible)?; env.execute(|memory, stack_end, globals_config| { - prepare_memory::( + prepare_memory::>( memory, program.id(), static_pages, @@ -493,51 +500,6 @@ where #[cfg(test)] mod tests { use super::*; - use gear_backend_common::lazy_pages::Status; - use gear_core::pages::WasmPage; - - struct TestExt; - struct LazyTestExt; - - impl ProcessorExternalities for TestExt { - fn new(_context: ProcessorContext) -> Self { - Self - } - - fn lazy_pages_init_for_program( - _mem: &mut impl Memory, - _prog_id: ProgramId, - _stack_end: Option, - _globals_config: GlobalsAccessConfig, - _lazy_pages_weights: LazyPagesWeights, - ) { - } - - fn lazy_pages_post_execution_actions(_mem: &mut impl Memory) {} - fn lazy_pages_status() -> Status { - Status::Normal - } - } - - impl ProcessorExternalities for LazyTestExt { - fn new(_context: ProcessorContext) -> Self { - Self - } - - fn lazy_pages_init_for_program( - _mem: &mut impl Memory, - _prog_id: ProgramId, - _stack_end: Option, - _globals_config: GlobalsAccessConfig, - _lazy_pages_weights: LazyPagesWeights, - ) { - } - - fn lazy_pages_post_execution_actions(_mem: &mut impl Memory) {} - fn lazy_pages_status() -> Status { - Status::Normal - } - } #[test] fn check_memory_insufficient() { diff --git a/core-processor/src/ext.rs b/core-processor/src/ext.rs index 8fa70c1d790..55d1f96dec9 100644 --- a/core-processor/src/ext.rs +++ b/core-processor/src/ext.rs @@ -16,20 +16,14 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use crate::configs::{BlockInfo, PageCosts}; +use crate::{ + configs::{BlockInfo, PageCosts}, + context::SystemReservationContext, +}; use alloc::{ collections::{BTreeMap, BTreeSet}, vec::Vec, }; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, LazyPagesWeights, Status}, - memory::ProcessAccessError, - runtime::RunFallibleError, - ActorTerminationReason, BackendAllocSyscallError, BackendExternalities, BackendSyscallError, - ExtInfo, SystemReservationContext, TrapExplanation, UndefinedTerminationReason, - UnrecoverableExecutionError, UnrecoverableExtError as UnrecoverableExtErrorCore, - UnrecoverableWaitError, -}; use gear_core::{ costs::{HostFnWeights, RuntimeCosts}, env::{Externalities, PayloadSliceLock, UnlockPayloadBound}, @@ -42,17 +36,25 @@ use gear_core::{ AllocError, AllocationsContext, GrowHandler, Memory, MemoryError, MemoryInterval, PageBuf, }, message::{ - ContextOutcomeDrain, GasLimit, HandlePacket, InitPacket, MessageContext, Packet, - ReplyPacket, + ContextOutcomeDrain, ContextStore, Dispatch, GasLimit, HandlePacket, InitPacket, + MessageContext, Packet, ReplyPacket, }, - pages::{PageU32Size, WasmPage}, + pages::{GearPage, PageU32Size, WasmPage}, reservation::GasReserver, }; +use gear_core_backend::{ + error::{ + ActorTerminationReason, BackendAllocSyscallError, BackendSyscallError, RunFallibleError, + TrapExplanation, UndefinedTerminationReason, UnrecoverableExecutionError, + UnrecoverableExtError as UnrecoverableExtErrorCore, UnrecoverableWaitError, + }, + BackendExternalities, +}; 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_lazy_pages_common::{GlobalsAccessConfig, LazyPagesWeights, ProcessAccessError, Status}; use gear_wasm_instrument::syscalls::SysCallName; /// Processor context. @@ -152,12 +154,30 @@ impl ProcessorContext { } } +#[derive(Debug)] +pub struct ExtInfo { + pub gas_amount: GasAmount, + pub gas_reserver: GasReserver, + pub system_reservation_context: SystemReservationContext, + pub allocations: BTreeSet, + pub pages_data: BTreeMap, + pub generated_dispatches: Vec<(Dispatch, u32, Option)>, + pub awakening: Vec<(MessageId, u32)>, + pub reply_deposits: Vec<(MessageId, u64)>, + pub program_candidates_data: BTreeMap>, + pub program_rents: BTreeMap, + pub context_store: ContextStore, +} + /// Trait to which ext must have to work in processor wasm executor. /// Currently used only for lazy-pages support. pub trait ProcessorExternalities { /// Create new fn new(context: ProcessorContext) -> Self; + /// Convert externalities into info. + fn into_ext_info(self, memory: &impl Memory) -> Result; + /// Protect and save storage keys for pages which has no data fn lazy_pages_init_for_program( mem: &mut impl Memory, @@ -294,7 +314,7 @@ impl GrowHandler for LazyGrowHandler { // 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); + gear_lazy_pages_interface::remove_lazy_pages_prot(mem); Self { old_mem_addr, old_mem_size: mem.size(), @@ -307,7 +327,7 @@ impl GrowHandler for LazyGrowHandler { 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( + gear_lazy_pages_interface::update_lazy_pages_and_protect_again( mem, self.old_mem_addr, self.old_mem_size, @@ -345,26 +365,6 @@ 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, - ) { - 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() - } -} - -impl BackendExternalities for Ext { fn into_ext_info(self, memory: &impl Memory) -> Result { let ProcessorContext { allocations_context, @@ -380,7 +380,7 @@ impl BackendExternalities for Ext { 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(); + let mut accessed_pages = gear_lazy_pages_interface::get_write_accessed_pages(); accessed_pages.retain(|p| { let wasm_page = p.to_page(); wasm_page < static_pages || allocations.contains(&wasm_page) @@ -429,6 +429,32 @@ impl BackendExternalities for Ext { Ok(info) } + fn lazy_pages_init_for_program( + mem: &mut impl Memory, + prog_id: ProgramId, + stack_end: Option, + globals_config: GlobalsAccessConfig, + lazy_pages_weights: LazyPagesWeights, + ) { + gear_lazy_pages_interface::init_for_program( + mem, + prog_id, + stack_end, + globals_config, + lazy_pages_weights, + ); + } + + fn lazy_pages_post_execution_actions(mem: &mut impl Memory) { + gear_lazy_pages_interface::remove_lazy_pages_prot(mem); + } + + fn lazy_pages_status() -> Status { + gear_lazy_pages_interface::get_status() + } +} + +impl BackendExternalities for Ext { fn gas_amount(&self) -> GasAmount { self.context.gas_counter.to_amount() } @@ -438,7 +464,7 @@ impl BackendExternalities for Ext { writes: &[MemoryInterval], gas_counter: &mut u64, ) -> Result<(), ProcessAccessError> { - lazy_pages::pre_process_memory_accesses(reads, writes, gas_counter) + gear_lazy_pages_interface::pre_process_memory_accesses(reads, writes, gas_counter) } } diff --git a/core-processor/src/precharge.rs b/core-processor/src/precharge.rs index faad95e95d2..26b0b3191d8 100644 --- a/core-processor/src/precharge.rs +++ b/core-processor/src/precharge.rs @@ -20,14 +20,15 @@ use crate::{ PrechargedDispatch, }, configs::{BlockConfig, PageCosts}, - context::{ContextChargedForCodeLength, ContextChargedForMemory, ContextData}, + context::{ + ContextChargedForCodeLength, ContextChargedForMemory, ContextData, SystemReservationContext, + }, processing::{ process_allowance_exceed, process_error, process_non_executable, process_success, }, ContextChargedForCode, ContextChargedForInstrumentation, }; use alloc::{collections::BTreeSet, vec::Vec}; -use gear_backend_common::SystemReservationContext; use gear_core::{ gas::{ChargeResult, GasAllowanceCounter, GasCounter}, ids::ProgramId, @@ -456,7 +457,6 @@ pub fn precharge_for_memory( #[cfg(test)] mod tests { use super::*; - use gear_backend_common::assert_ok; fn prepare_gas_counters() -> (GasCounter, GasAllowanceCounter) { ( @@ -471,9 +471,11 @@ mod tests { let (mut gas_counter, mut gas_allowance_counter) = prepare_gas_counters(); let mut charger = GasPrecharger::new(&mut gas_counter, &mut gas_allowance_counter); let static_pages = 4.into(); - let res = charger.charge_gas_for_pages(&costs, &Default::default(), static_pages); + let res = charger + .charge_gas_for_pages(&costs, &Default::default(), static_pages) + .unwrap(); // Result is static pages count - assert_ok!(res, static_pages); + assert_eq!(res, static_pages); // Charging for static pages initialization let charge = costs.static_page.calc(static_pages); assert_eq!(charger.counter.left(), 1_000_000 - charge); diff --git a/core-processor/src/processing.rs b/core-processor/src/processing.rs index 094dc7117be..8becd057446 100644 --- a/core-processor/src/processing.rs +++ b/core-processor/src/processing.rs @@ -28,27 +28,30 @@ use crate::{ precharge::SuccessfulDispatchResultKind, }; use alloc::{string::ToString, vec::Vec}; -use gear_backend_common::{ - BackendExternalities, BackendSyscallError, Environment, SystemReservationContext, -}; use gear_core::{ env::Externalities, ids::{MessageId, ProgramId}, message::{ContextSettings, DispatchKind, IncomingDispatch, ReplyMessage, StoredDispatch}, reservation::GasReservationState, }; +use gear_core_backend::{ + error::{BackendAllocSyscallError, BackendSyscallError, RunFallibleError}, + BackendExternalities, +}; use gear_core_errors::{ErrorReplyReason, SignalCode}; /// Process program & dispatch for it and return journal for updates. -pub fn process( +pub fn process( block_config: &BlockConfig, execution_context: ProcessExecutionContext, random_data: (Vec, u32), ) -> Result, SystemExecutionError> where - E: Environment, - E::Ext: ProcessorExternalities + BackendExternalities + 'static, - ::UnrecoverableError: BackendSyscallError, + Ext: ProcessorExternalities + BackendExternalities + 'static, + ::AllocError: + BackendAllocSyscallError, + RunFallibleError: From, + ::UnrecoverableError: BackendSyscallError, { use crate::precharge::SuccessfulDispatchResultKind::*; @@ -117,7 +120,7 @@ where outgoing_limit, ); - let exec_result = executor::execute_wasm::( + let exec_result = executor::execute_wasm::( balance, dispatch.clone(), execution_context, diff --git a/core/Cargo.toml b/core/Cargo.toml index 6fb6635d08e..22a2d47f2ee 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -33,6 +33,7 @@ serde = { workspace = true, features = [ "derive" ], optional = true } wabt.workspace = true env_logger.workspace = true proptest.workspace = true +rand = { workspace = true, features = ["std", "std_rng"] } [features] default = [] diff --git a/core/src/lib.rs b/core/src/lib.rs index 5a15add4cfb..1996e7492dd 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,6 +39,7 @@ pub mod program; pub mod reservation; pub mod buffer; +pub mod str; use core::mem::size_of; use static_assertions::const_assert; diff --git a/core-backend/common/src/utils.rs b/core/src/str.rs similarity index 90% rename from core-backend/common/src/utils.rs rename to core/src/str.rs index 923685bd214..84f09c1e667 100644 --- a/core-backend/common/src/utils.rs +++ b/core/src/str.rs @@ -1,6 +1,6 @@ // This file is part of Gear. -// Copyright (C) 2022-2023 Gear Technologies Inc. +// Copyright (C) 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 @@ -16,34 +16,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use alloc::{borrow::Cow, string::String}; -use scale_info::{ - scale::{Decode, Encode}, - TypeInfo, -}; - -#[macro_export] -macro_rules! assert_ok { - ( $x:expr $(,)? ) => { - let is = $x; - match is { - Ok(_) => (), - _ => assert!(false, "Expected Ok(_). Got {:#?}", is), - } - }; - ( $x:expr, $y:expr $(,)? ) => { - assert_eq!($x, Ok($y)); - }; -} +//! String with limited length implementation -#[macro_export] -macro_rules! assert_err { - ( $x:expr , $y:expr $(,)? ) => { - assert_eq!($x, Err($y.into())); - }; -} +use alloc::{borrow::Cow, string::String}; +use parity_scale_codec::{Decode, Encode}; +use scale_info::TypeInfo; -// Max amount of bytes allowed to be thrown as string explanation of the error. +/// Max amount of bytes allowed to be thrown as string explanation of the error. pub const TRIMMED_MAX_LEN: usize = 1024; fn smart_truncate(s: &mut String, max_bytes: usize) { @@ -58,7 +37,7 @@ fn smart_truncate(s: &mut String, max_bytes: usize) { } } -/// Wrapped string to fit `core_backend::TRIMMED_MAX_LEN` amount of bytes. +/// Wrapped string to fit [`TRIMMED_MAX_LEN`] amount of bytes. /// /// The `Cow` is used to avoid allocating a new `String` when the `LimitedStr` is /// created from a `&str`. @@ -76,6 +55,7 @@ impl<'a> LimitedStr<'a> { " bytes." ); + /// Convert from `&str` in compile-time. #[track_caller] pub const fn from_small_str(s: &'a str) -> Self { if s.len() > TRIMMED_MAX_LEN { @@ -85,11 +65,13 @@ impl<'a> LimitedStr<'a> { Self(Cow::Borrowed(s)) } + /// Return string slice. pub fn as_str(&self) -> &str { self.0.as_ref() } } +/// The error type returned when a conversion from `&str` to [`LimitedStr`] fails. #[derive(Clone, Debug, derive_more::Display)] #[display(fmt = "String must be less than {} bytes.", TRIMMED_MAX_LEN)] pub struct LimitedStrTryFromError; diff --git a/gcli/Cargo.toml b/gcli/Cargo.toml index 8be088e5f8f..e6e0a52ab4e 100644 --- a/gcli/Cargo.toml +++ b/gcli/Cargo.toml @@ -41,8 +41,7 @@ thiserror.workspace = true tokio = { workspace = true, features = [ "full" ] } whoami.workspace = true core-processor = { workspace = true, features = [ "std" ] } -gear-backend-sandbox = { workspace = true, features = [ "std" ] } -gear-lazy-pages-common = { workspace = true, features = [ "std" ] } +gear-lazy-pages-interface = { workspace = true, features = [ "std" ] } reqwest = { workspace = true, default-features = false, features = [ "json", "rustls-tls" ] } etc.workspace = true sp-io = { workspace = true, features = [ "std" ] } diff --git a/gcli/src/meta/mod.rs b/gcli/src/meta/mod.rs index 059d0fac8bc..4028cccb4a1 100644 --- a/gcli/src/meta/mod.rs +++ b/gcli/src/meta/mod.rs @@ -109,15 +109,12 @@ impl Meta { /// Execute meta method. fn execute(wasm: InstrumentedCode, method: &str) -> Result> { - assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + assert!(gear_lazy_pages_interface::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, - >( + core_processor::informational::execute_for_reply::( method.into(), wasm, None, diff --git a/gtest/Cargo.toml b/gtest/Cargo.toml index fc1ab748a54..169c292db3a 100644 --- a/gtest/Cargo.toml +++ b/gtest/Cargo.toml @@ -8,10 +8,8 @@ license.workspace = true [dependencies] 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, features = ["std"] } -gear-lazy-pages-common = { workspace = true, features = ["std"] } +gear-lazy-pages-interface = { workspace = true, features = ["std"] } gear-wasm-builder.workspace = true gear-utils.workspace = true diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 48cbe8ebfbb..4a45dda5dae 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -29,7 +29,6 @@ use core_processor::{ configs::{BlockConfig, BlockInfo, PageCosts, TESTS_MAX_PAGES_NUMBER}, ContextChargedForCode, ContextChargedForInstrumentation, Ext, }; -use gear_backend_sandbox::SandboxEnvironment; use gear_core::{ code::{Code, CodeAndId, InstrumentedCode, InstrumentedCodeAndId, TryNewCodeConfig}, ids::{CodeId, MessageId, ProgramId, ReservationId}, @@ -467,7 +466,7 @@ impl ExtManager { .ok_or_else(|| TestError::ActorNotFound(*program_id))?; if let Some((_, program)) = actor.get_executable_actor_data() { - core_processor::informational::execute_for_reply::, _>( + core_processor::informational::execute_for_reply::( String::from("state"), program.code().clone(), Some(program.allocations().clone()), @@ -508,7 +507,7 @@ impl ExtManager { let mut mapping_code_payload = args.unwrap_or_default(); mapping_code_payload.append(&mut self.read_state_bytes(payload, program_id)?); - core_processor::informational::execute_for_reply::, _>( + core_processor::informational::execute_for_reply::( String::from(fn_name), mapping_code, None, @@ -882,7 +881,7 @@ impl ExtManager { } }; - let journal = core_processor::process::>( + let journal = core_processor::process::( &block_config, (context, code, balance).into(), self.random_data.clone(), diff --git a/gtest/src/system.rs b/gtest/src/system.rs index b4e3c3a70b1..7d1c46e512f 100644 --- a/gtest/src/system.rs +++ b/gtest/src/system.rs @@ -41,7 +41,7 @@ impl System { /// Create a new system. pub fn new() -> Self { - assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + assert!(gear_lazy_pages_interface::try_to_enable_lazy_pages( Self::PAGE_STORAGE_PREFIX )); Default::default() diff --git a/lazy-pages/Cargo.toml b/lazy-pages/Cargo.toml index 56510ab1ed5..3af1ee4ed69 100644 --- a/lazy-pages/Cargo.toml +++ b/lazy-pages/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "gear-lazy-pages" -description = "Gear lazy-pages support" +description = "Gear lazy-pages implementation" version.workspace = true authors.workspace = true edition.workspace = true @@ -22,7 +22,7 @@ once_cell.workspace = true gear-sandbox-host.workspace = true gear-core.workspace = true -gear-backend-common.workspace = true +gear-lazy-pages-common.workspace = true [target."cfg(target_vendor = \"apple\")".dependencies.mach] version = "0.3.2" diff --git a/lazy-pages/common/Cargo.toml b/lazy-pages/common/Cargo.toml new file mode 100644 index 00000000000..d449c6c6a24 --- /dev/null +++ b/lazy-pages/common/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "gear-lazy-pages-common" +description = "Gear lazy-pages commons" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[dependencies] +gear-core.workspace = true + +num_enum.workspace = true +codec.workspace = true diff --git a/core-backend/common/src/lazy_pages.rs b/lazy-pages/common/src/lib.rs similarity index 90% rename from core-backend/common/src/lazy_pages.rs rename to lazy-pages/common/src/lib.rs index b5cdbd6c142..a925d4daf0f 100644 --- a/core-backend/common/src/lazy_pages.rs +++ b/lazy-pages/common/src/lib.rs @@ -18,17 +18,25 @@ //! Core logic for usage both in runtime and in lazy-pages native part. -use core::fmt::Debug; +#![no_std] -use core::any::Any; -use gear_core::{costs::CostPerPage, memory::HostPointer, pages::GearPage}; -use scale_info::scale::{self, Decode, Encode}; +use codec::{Decode, Encode}; +use core::{any::Any, fmt::Debug}; +use gear_core::{costs::CostPerPage, memory::HostPointer, pages::GearPage, str::LimitedStr}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; -use crate::utils::LimitedStr; +/// Memory access error during sys-call that lazy-pages have caught. +/// 0 index is reserved for an ok result. +#[derive(Debug, Clone, IntoPrimitive, TryFromPrimitive)] +#[repr(u8)] +pub enum ProcessAccessError { + OutOfBounds = 1, + GasLimitExceeded = 2, +} /// Informs lazy-pages whether they work with native or WASM runtime. #[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] -#[codec(crate = scale)] +#[codec(crate = codec)] pub enum GlobalsAccessMod { /// Is wasm runtime. WasmRuntime, @@ -38,7 +46,7 @@ pub enum GlobalsAccessMod { /// Lazy-pages cases weights. #[derive(Debug, Default, Clone, PartialEq, Eq, Encode, Decode)] -#[codec(crate = scale)] +#[codec(crate = codec)] pub struct LazyPagesWeights { /// First read page access cost. pub signal_read: CostPerPage, @@ -58,7 +66,7 @@ pub struct LazyPagesWeights { /// Globals ctx for lazy-pages initialization for program. #[derive(Debug, Clone, PartialEq, Eq, Encode, Decode)] -#[codec(crate = scale)] +#[codec(crate = codec)] pub struct GlobalsAccessConfig { /// Raw pointer to the globals access provider. pub access_ptr: HostPointer, @@ -74,16 +82,20 @@ pub struct GlobalsAccessError; pub trait GlobalsAccessor { /// Returns global `name` value, if `name` is I64 global export. fn get_i64(&self, name: &LimitedStr) -> Result; + /// Set global `name` == `value`, if `name` is I64 global export. fn set_i64(&mut self, name: &LimitedStr, value: i64) -> Result<(), GlobalsAccessError>; + /// Returns global `name` value, if `name` is I32 global export. fn get_i32(&self, _name: &LimitedStr) -> Result { unimplemented!("Currently has no i32 system globals") } + /// Set global `name` == `value`, if `name` is I32 global export. fn set_i32(&mut self, _name: &LimitedStr, _value: i32) -> Result<(), GlobalsAccessError> { unimplemented!("Currently has no i32 system globals") } + /// Returns as `&mut dyn Any`. fn as_any_mut(&mut self) -> &mut dyn Any; } @@ -98,7 +110,7 @@ pub trait GlobalsAccessor { /// termination reason sets as `gas limit exceeded` or `gas allowance exceeded`, depending on status. /// NOTE: `repr(i64)` is important to be able add additional fields, without old runtimes separate support logic. #[derive(Debug, Clone, Copy, Encode, Decode, PartialEq, Eq)] -#[codec(crate = scale)] +#[codec(crate = codec)] #[repr(i64)] // TODO: consider removal of two exceed options in favor of one global (issue #3018). // Will require bump of many RI func's versions. diff --git a/common/lazy-pages/Cargo.toml b/lazy-pages/interface/Cargo.toml similarity index 70% rename from common/lazy-pages/Cargo.toml rename to lazy-pages/interface/Cargo.toml index 821f15cef8a..9aca80bf669 100644 --- a/common/lazy-pages/Cargo.toml +++ b/lazy-pages/interface/Cargo.toml @@ -1,9 +1,12 @@ [package] -name = "gear-lazy-pages-common" -version = "0.1.0" +name = "gear-lazy-pages-interface" +description = "Gear lazy-pages actual interface" +version.workspace = true authors.workspace = true edition.workspace = true license.workspace = true +homepage.workspace = true +repository.workspace = true [dependencies] derive_more.workspace = true @@ -11,8 +14,8 @@ log.workspace = true byteorder.workspace = true gear-core.workspace = true -gear-backend-common.workspace = true gear-common.workspace = true +gear-lazy-pages-common.workspace = true gear-runtime-interface.workspace = true gear-wasm-instrument.workspace = true diff --git a/common/lazy-pages/src/lib.rs b/lazy-pages/interface/src/lib.rs similarity index 97% rename from common/lazy-pages/src/lib.rs rename to lazy-pages/interface/src/lib.rs index 526717ddc61..9aab7a86f27 100644 --- a/common/lazy-pages/src/lib.rs +++ b/lazy-pages/interface/src/lib.rs @@ -24,17 +24,14 @@ extern crate alloc; use byteorder::{ByteOrder, LittleEndian}; use core::fmt; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, LazyPagesWeights, Status}, - memory::ProcessAccessError, - LimitedStr, -}; use gear_common::Origin; use gear_core::{ ids::ProgramId, memory::{HostPointer, Memory, MemoryInterval}, pages::{GearPage, PageNumber, PageU32Size, WasmPage}, + str::LimitedStr, }; +use gear_lazy_pages_common::{GlobalsAccessConfig, LazyPagesWeights, ProcessAccessError, Status}; use gear_runtime_interface::{gear_ri, LazyPagesProgramContext, LazyPagesRuntimeContext}; use gear_wasm_instrument::GLOBAL_NAME_GAS; use sp_std::{mem, vec, vec::Vec}; diff --git a/lazy-pages/src/common.rs b/lazy-pages/src/common.rs index 07f84200303..a1841e56dcf 100644 --- a/lazy-pages/src/common.rs +++ b/lazy-pages/src/common.rs @@ -20,13 +20,12 @@ use std::{collections::BTreeSet, mem::size_of, num::NonZeroU32}; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessError, Status}, - LimitedStr, -}; - use crate::{globals::GlobalsContext, mprotect::MprotectError}; -use gear_core::pages::{GearPage, PageDynSize, PageSizeNo, SizeManager, WasmPage}; +use gear_core::{ + pages::{GearPage, PageDynSize, PageSizeNo, SizeManager, WasmPage}, + str::LimitedStr, +}; +use gear_lazy_pages_common::{GlobalsAccessError, Status}; // TODO: investigate error allocations #2441 #[derive(Debug, derive_more::Display, derive_more::From)] diff --git a/lazy-pages/src/globals.rs b/lazy-pages/src/globals.rs index a2ac73f6273..6418a13bec9 100644 --- a/lazy-pages/src/globals.rs +++ b/lazy-pages/src/globals.rs @@ -20,11 +20,8 @@ use crate::common::{Error, GlobalNames}; use core::any::Any; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessError, GlobalsAccessMod, GlobalsAccessor}, - LimitedStr, -}; -use gear_core::memory::HostPointer; +use gear_core::{memory::HostPointer, str::LimitedStr}; +use gear_lazy_pages_common::{GlobalsAccessError, GlobalsAccessMod, GlobalsAccessor}; use gear_sandbox_host::sandbox::SandboxInstance; use sp_wasm_interface::Value; diff --git a/lazy-pages/src/host_func.rs b/lazy-pages/src/host_func.rs index ee997244641..b6dc7d793ca 100644 --- a/lazy-pages/src/host_func.rs +++ b/lazy-pages/src/host_func.rs @@ -23,12 +23,12 @@ use crate::{ process::{self, AccessHandler}, LAZY_PAGES_CONTEXT, }; -use gear_backend_common::{lazy_pages::Status, memory::ProcessAccessError}; use gear_core::{ self, memory::MemoryInterval, pages::{GearPage, PageDynSize}, }; +use gear_lazy_pages_common::{ProcessAccessError, Status}; use std::collections::BTreeSet; pub(crate) struct HostFuncAccessHandler<'a> { diff --git a/lazy-pages/src/lib.rs b/lazy-pages/src/lib.rs index 03e1278ac11..7b384255a06 100644 --- a/lazy-pages/src/lib.rs +++ b/lazy-pages/src/lib.rs @@ -29,10 +29,6 @@ #![allow(clippy::items_after_test_module)] use common::{LazyPagesExecutionContext, LazyPagesRuntimeContext}; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, Status}, - LimitedStr, -}; use gear_core::pages::{PageDynSize, PageNumber, PageSizeNo, WasmPage}; use sp_std::vec::Vec; use std::{cell::RefCell, convert::TryInto, num::NonZeroU32}; @@ -57,6 +53,8 @@ use crate::{ mod tests; pub use common::LazyPagesVersion; +use gear_core::str::LimitedStr; +use gear_lazy_pages_common::{GlobalsAccessConfig, Status}; pub use host_func::pre_process_memory_accesses; use mprotect::MprotectError; diff --git a/lazy-pages/src/process.rs b/lazy-pages/src/process.rs index 3807841cf8f..4e54e9ad54a 100644 --- a/lazy-pages/src/process.rs +++ b/lazy-pages/src/process.rs @@ -22,8 +22,8 @@ use crate::{ common::{Error, LazyPagesExecutionContext}, mprotect, }; -use gear_backend_common::lazy_pages::Status; use gear_core::pages::{GearPage, PageDynSize}; +use gear_lazy_pages_common::Status; use std::slice; /// `process_lazy_pages` use struct which implements this trait, diff --git a/lazy-pages/src/signal.rs b/lazy-pages/src/signal.rs index 5cae9a7e654..c04e4790200 100644 --- a/lazy-pages/src/signal.rs +++ b/lazy-pages/src/signal.rs @@ -24,8 +24,8 @@ use crate::{ process::{self, AccessHandler}, LAZY_PAGES_CONTEXT, }; -use gear_backend_common::lazy_pages::Status; use gear_core::pages::{GearPage, PageDynSize}; +use gear_lazy_pages_common::Status; use std::convert::TryFrom; pub(crate) trait UserSignalHandler { diff --git a/lazy-pages/src/tests.rs b/lazy-pages/src/tests.rs index ae4852df027..a5af3986526 100644 --- a/lazy-pages/src/tests.rs +++ b/lazy-pages/src/tests.rs @@ -20,8 +20,10 @@ use crate::{ common::Error, init_with_handler, mprotect, signal::ExceptionInfo, LazyPagesVersion, UserSignalHandler, }; -use gear_backend_common::LimitedStr; -use gear_core::pages::{GearPage, PageDynSize, PageU32Size, WasmPage}; +use gear_core::{ + pages::{GearPage, PageDynSize, PageU32Size, WasmPage}, + str::LimitedStr, +}; use region::Protection; fn handler_tester(f: F) { diff --git a/pallets/gear-debug/Cargo.toml b/pallets/gear-debug/Cargo.toml index f139f859414..6170e773756 100644 --- a/pallets/gear-debug/Cargo.toml +++ b/pallets/gear-debug/Cargo.toml @@ -37,7 +37,6 @@ pallet-authorship.workspace = true serde.workspace = true env_logger.workspace = true wabt.workspace = true -gear-backend-sandbox.workspace = true hex-literal.workspace = true frame-support-test = { workspace = true, features = ["std"] } pallet-timestamp = { workspace = true, features = ["std"] } diff --git a/pallets/gear/Cargo.toml b/pallets/gear/Cargo.toml index cfb8ed21892..6b412909f43 100644 --- a/pallets/gear/Cargo.toml +++ b/pallets/gear/Cargo.toml @@ -26,12 +26,12 @@ static_assertions.workspace = true # Internal deps common.workspace = true gear-runtime-interface = { workspace = true } +gear-lazy-pages-interface.workspace = true gear-lazy-pages-common.workspace = true core-processor.workspace = true gear-core.workspace = true gear-core-errors.workspace = true -gear-backend-common.workspace = true -gear-backend-sandbox.workspace = true +gear-core-backend.workspace = true pallet-gear-proc-macro = { path = "proc-macro" } gsys = { workspace = true, optional = true } pallet-gear-voucher.workspace = true @@ -137,8 +137,8 @@ std = [ "gear-wasm-instrument/std", "scopeguard/use_std", "core-processor/std", - "gear-backend-sandbox/std", - "gear-lazy-pages-common/std", + "gear-core-backend/std", + "gear-lazy-pages-interface/std", "scale-info/std", "sp-io/std", "sp-std/std", @@ -172,7 +172,7 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "frame-support/runtime-benchmarks", "common/runtime-benchmarks", - "gear-backend-common/mock", + "gear-core-backend/mock", "gear-core-errors/codec", "gear-sandbox", "sp-consensus-slots", diff --git a/pallets/gear/src/benchmarking/mod.rs b/pallets/gear/src/benchmarking/mod.rs index a5a83e3f9e8..7a2c82c7c3a 100644 --- a/pallets/gear/src/benchmarking/mod.rs +++ b/pallets/gear/src/benchmarking/mod.rs @@ -58,9 +58,9 @@ use crate::{ manager::ExtManager, pallet, 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, TaskPoolOf, + BalanceOf, BenchmarkStorage, Call, Config, CurrencyOf, Event, Ext as Externalities, + GasHandlerOf, GearBank, MailboxOf, Pallet as Gear, Pallet, ProgramStorageOf, QueueOf, + RentFreePeriodOf, ResumeMinimalPeriodOf, Schedule, TaskPoolOf, }; use ::alloc::{ collections::{BTreeMap, BTreeSet}, @@ -77,7 +77,7 @@ use common::{ use core_processor::{ common::{DispatchOutcome, JournalNote}, configs::{BlockConfig, PageCosts, TESTS_MAX_PAGES_NUMBER}, - ProcessExecutionContext, ProcessorContext, ProcessorExternalities, + Ext, ProcessExecutionContext, ProcessorContext, ProcessorExternalities, }; use frame_benchmarking::{benchmarks, whitelisted_caller}; use frame_support::{ @@ -85,8 +85,6 @@ use frame_support::{ traits::{Currency, Get, Hooks}, }; use frame_system::{Pallet as SystemPallet, RawOrigin}; -use gear_backend_common::Environment; -use gear_backend_sandbox::{DefaultExecutorMemory, MemoryWrap}; use gear_core::{ code::{Code, CodeAndId}, gas::{GasAllowanceCounter, GasCounter, ValueCounter}, @@ -96,6 +94,10 @@ use gear_core::{ pages::{GearPage, PageU32Size, WasmPage, GEAR_PAGE_SIZE, WASM_PAGE_SIZE}, reservation::GasReserver, }; +use gear_core_backend::{ + env::Environment, + memory::{ExecutorMemory, MemoryWrap}, +}; use gear_core_errors::*; use gear_sandbox::{default_executor::Store, SandboxMemory, SandboxStore}; use gear_wasm_instrument::{ @@ -234,12 +236,8 @@ where T: Config, T::AccountId: Origin, { - core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)) + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)) } fn get_last_session_id() -> Option { @@ -460,7 +458,7 @@ benchmarks! { let WasmModule { code, .. } = WasmModule::::sized(c * 1024, Location::Init); }: { let ext = Externalities::new(default_processor_context::()); - ExecutionEnvironment::new(ext, &code, DispatchKind::Init, Default::default(), max_pages::().into()).unwrap(); + Environment::new(ext, &code, DispatchKind::Init, Default::default(), max_pages::().into()).unwrap(); } claim_value { @@ -1655,8 +1653,8 @@ benchmarks! { mem_grow { let r in 0 .. API_BENCHMARK_BATCHES; let mut store = Store::new(None); - let mem = DefaultExecutorMemory::new(&mut store, 1, None).unwrap(); - let mut mem = MemoryWrap::::new(mem, store); + let mem = ExecutorMemory::new(&mut store, 1, None).unwrap(); + let mut mem = MemoryWrap::::new(mem, store); }: { for _ in 0..(r * API_BENCHMARK_BATCH_SIZE) { mem.grow(1.into()).unwrap(); diff --git a/pallets/gear/src/benchmarking/tests/lazy_pages.rs b/pallets/gear/src/benchmarking/tests/lazy_pages.rs index 94a92d7e6df..de869b20aef 100644 --- a/pallets/gear/src/benchmarking/tests/lazy_pages.rs +++ b/pallets/gear/src/benchmarking/tests/lazy_pages.rs @@ -22,13 +22,13 @@ use core::mem::size_of; use ::alloc::{collections::BTreeSet, format}; use common::ProgramStorage; +use core_processor::Ext; use frame_support::codec::MaxEncodedLen; -use gear_backend_common::lazy_pages::Status; use gear_core::{ memory::MemoryInterval, pages::{PageNumber, PageU32Size}, }; -use gear_lazy_pages_common as lazy_pages; +use gear_lazy_pages_common::Status; use rand::{Rng, SeedableRng}; use super::*; @@ -303,12 +303,9 @@ where let charged_for_pages = page_sets.charged_for_pages(&exec.block_config.page_costs); - let notes = core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); + let notes = + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); let mut gas_burned = 0; for note in notes.into_iter() { @@ -379,12 +376,9 @@ where .page_costs .lazy_pages_signal_write_after_read = (write_after_read_cost * i).into(); - let notes = core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); + let notes = + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); let mut gas_burned = 0; for note in notes.into_iter() { @@ -538,12 +532,9 @@ where exec.block_config.page_costs = Default::default(); - let notes = core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); + let notes = + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); let mut gas_burned = None; for note in notes.into_iter() { @@ -581,12 +572,9 @@ where ..Default::default() }; - let notes = core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); + let notes = + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); for note in notes.into_iter() { match note { @@ -601,7 +589,7 @@ where } } - assert_ne!(lazy_pages::get_status(), Status::Normal); + assert_ne!(gear_lazy_pages_interface::get_status(), Status::Normal); }; // Check gas allowance exceeded. @@ -622,12 +610,9 @@ where ..Default::default() }; - let notes = core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); + let notes = + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap_or_else(|e| unreachable!("core-processor logic invalidated: {}", e)); for note in notes.into_iter() { match note { @@ -638,6 +623,6 @@ where } } - assert_ne!(lazy_pages::get_status(), Status::Normal); + assert_ne!(gear_lazy_pages_interface::get_status(), Status::Normal); }; } diff --git a/pallets/gear/src/benchmarking/tests/mod.rs b/pallets/gear/src/benchmarking/tests/mod.rs index e07981baff9..52ddf0870f0 100644 --- a/pallets/gear/src/benchmarking/tests/mod.rs +++ b/pallets/gear/src/benchmarking/tests/mod.rs @@ -36,8 +36,7 @@ use crate::{ HandleKind, }; use common::benchmarking; -use gear_backend_common::TrapExplanation; - +use gear_core_backend::error::TrapExplanation; use gear_wasm_instrument::parity_wasm::elements::Instruction; pub fn check_stack_overflow() @@ -71,22 +70,18 @@ where ) .unwrap(); - core_processor::process::( - &exec.block_config, - exec.context, - exec.random_data, - ) - .unwrap() - .into_iter() - .find_map(|note| match note { - JournalNote::MessageDispatched { outcome, .. } => Some(outcome), - _ => None, - }) - .map(|outcome| match outcome { - DispatchOutcome::InitFailure { reason, .. } => { - assert_eq!(reason, TrapExplanation::Unknown.to_string()); - } - _ => panic!("Unexpected dispatch outcome: {:?}", outcome), - }) - .unwrap(); + core_processor::process::(&exec.block_config, exec.context, exec.random_data) + .unwrap() + .into_iter() + .find_map(|note| match note { + JournalNote::MessageDispatched { outcome, .. } => Some(outcome), + _ => None, + }) + .map(|outcome| match outcome { + DispatchOutcome::InitFailure { reason, .. } => { + assert_eq!(reason, TrapExplanation::Unknown.to_string()); + } + _ => panic!("Unexpected dispatch outcome: {:?}", outcome), + }) + .unwrap(); } diff --git a/pallets/gear/src/benchmarking/utils.rs b/pallets/gear/src/benchmarking/utils.rs index 35d6eb342a6..ad918924ca8 100644 --- a/pallets/gear/src/benchmarking/utils.rs +++ b/pallets/gear/src/benchmarking/utils.rs @@ -114,7 +114,7 @@ where T: Config, T::AccountId: Origin, { - assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + assert!(gear_lazy_pages_interface::try_to_enable_lazy_pages( ProgramStorageOf::::pages_final_prefix() )); diff --git a/pallets/gear/src/lib.rs b/pallets/gear/src/lib.rs index ecf8135b887..ff267066288 100644 --- a/pallets/gear/src/lib.rs +++ b/pallets/gear/src/lib.rs @@ -73,7 +73,6 @@ 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,8 +85,6 @@ use sp_std::{ prelude::*, }; -type ExecutionEnvironment = gear_backend_sandbox::SandboxEnvironment; - pub(crate) type AccountIdOf = ::AccountId; pub(crate) type CurrencyOf = ::Currency; pub(crate) type BalanceOf = as Currency>>::Balance; @@ -1014,7 +1011,7 @@ pub mod pallet { pub(crate) fn enable_lazy_pages() { let prefix = ProgramStorageOf::::pages_final_prefix(); - if !lazy_pages::try_to_enable_lazy_pages(prefix) { + if !gear_lazy_pages_interface::try_to_enable_lazy_pages(prefix) { unreachable!("By some reasons we cannot run lazy-pages on this machine"); } } diff --git a/pallets/gear/src/queue.rs b/pallets/gear/src/queue.rs index 7bc4280f9a3..c4de95c7f11 100644 --- a/pallets/gear/src/queue.rs +++ b/pallets/gear/src/queue.rs @@ -137,7 +137,7 @@ where let (random, bn) = T::Randomness::random(dispatch_id.as_ref()); - let journal = core_processor::process::( + let journal = core_processor::process::( block_config, (context, code, balance).into(), (random.encode(), bn.unique_saturated_into()), diff --git a/pallets/gear/src/runtime_api.rs b/pallets/gear/src/runtime_api.rs index be2af0954ad..67984db1a93 100644 --- a/pallets/gear/src/runtime_api.rs +++ b/pallets/gear/src/runtime_api.rs @@ -328,7 +328,7 @@ where Self::update_gas_allowance(gas_allowance); - core_processor::informational::execute_for_reply::, String>( + core_processor::informational::execute_for_reply::( function.into(), instrumented_code, None, @@ -364,7 +364,7 @@ where Self::update_gas_allowance(gas_allowance); - core_processor::informational::execute_for_reply::, String>( + core_processor::informational::execute_for_reply::( String::from("state"), instrumented_code, Some(allocations), @@ -399,7 +399,7 @@ where Self::update_gas_allowance(gas_allowance); - core_processor::informational::execute_for_reply::, String>( + core_processor::informational::execute_for_reply::( String::from("metahash"), instrumented_code, Some(allocations), diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index ce67a1dff00..965147d615f 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -63,15 +63,15 @@ use frame_support::{ traits::{Currency, Randomness}, }; use frame_system::pallet_prelude::BlockNumberFor; -use gear_backend_common::{ - TrapExplanation, UnrecoverableExecutionError, UnrecoverableExtError, UnrecoverableWaitError, -}; use gear_core::{ code::{self, Code}, ids::{CodeId, MessageId, ProgramId}, message::UserStoredMessage, pages::{PageNumber, PageU32Size, WasmPage}, }; +use gear_core_backend::error::{ + TrapExplanation, UnrecoverableExecutionError, UnrecoverableExtError, UnrecoverableWaitError, +}; use gear_core_errors::*; use gear_wasm_instrument::STACK_END_EXPORT_NAME; use gstd::{collections::BTreeMap, errors::Error as GstdError}; @@ -14517,7 +14517,6 @@ mod utils { traits::tokens::{currency::Currency, Balance}, }; use frame_system::pallet_prelude::{BlockNumberFor, OriginFor}; - use gear_backend_common::TrapExplanation; use gear_core::{ ids::{CodeId, MessageId, ProgramId}, message::{Message, Payload, ReplyDetails, UserMessage, UserStoredMessage}, diff --git a/runtime-interface/Cargo.toml b/runtime-interface/Cargo.toml index f2dd78dbb68..f992014ca26 100644 --- a/runtime-interface/Cargo.toml +++ b/runtime-interface/Cargo.toml @@ -10,7 +10,8 @@ repository.workspace = true [dependencies] gear-core.workspace = true -gear-backend-common = { workspace = true } +gear-lazy-pages-common.workspace = true + log = { workspace = true, optional = true } libc.workspace = true sp-allocator.workspace = true diff --git a/runtime-interface/src/lib.rs b/runtime-interface/src/lib.rs index e7e1d3329e4..fecc627ef0a 100644 --- a/runtime-interface/src/lib.rs +++ b/runtime-interface/src/lib.rs @@ -21,31 +21,24 @@ #![allow(useless_deprecated, deprecated)] #![cfg_attr(not(feature = "std"), no_std)] +extern crate alloc; + use byteorder::{ByteOrder, LittleEndian}; use codec::{Decode, Encode}; -use gear_backend_common::{ - lazy_pages::{GlobalsAccessConfig, Status}, - memory::ProcessAccessError, - LimitedStr, -}; use gear_core::{ gas::GasLeft, memory::{HostPointer, MemoryInterval}, + str::LimitedStr, }; +use gear_lazy_pages_common::{GlobalsAccessConfig, ProcessAccessError, Status}; use sp_runtime_interface::{ pass_by::{Codec, PassBy}, runtime_interface, }; -use sp_std::mem; - -extern crate alloc; - -#[cfg(feature = "std")] -use gear_lazy_pages as lazy_pages; - -pub use sp_std::{convert::TryFrom, result::Result, vec::Vec}; +use sp_std::{convert::TryFrom, mem, result::Result, vec::Vec}; mod gear_sandbox; + #[cfg(feature = "std")] pub use gear_sandbox::init as sandbox_init; pub use gear_sandbox::sandbox; @@ -114,7 +107,7 @@ pub trait GearRI { ) -> (GasLeft, Result<(), ProcessAccessErrorVer1>) { let mut gas_left = gas_left.0; let gas_before = gas_left.gas; - let res = lazy_pages::pre_process_memory_accesses(reads, writes, &mut gas_left.gas); + let res = gear_lazy_pages::pre_process_memory_accesses(reads, writes, &mut gas_left.gas); // Support charge for allowance otherwise DB will be corrupted. gas_left.allowance = gas_left @@ -151,7 +144,7 @@ pub trait GearRI { let mut gas_counter = LittleEndian::read_u64(gas_bytes); - let res = match lazy_pages::pre_process_memory_accesses( + let res = match gear_lazy_pages::pre_process_memory_accesses( &reads_intervals, &writes_intervals, &mut gas_counter, @@ -166,16 +159,16 @@ pub trait GearRI { } fn lazy_pages_status() -> (Status,) { - (lazy_pages::status() + (gear_lazy_pages::status() .unwrap_or_else(|err| unreachable!("Cannot get lazy-pages status: {err}")),) } /// Init lazy-pages. /// Returns whether initialization was successful. fn init_lazy_pages(ctx: LazyPagesRuntimeContext) -> bool { - use lazy_pages::LazyPagesVersion; + use gear_lazy_pages::LazyPagesVersion; - lazy_pages::init( + gear_lazy_pages::init( LazyPagesVersion::Version1, ctx.page_sizes, ctx.global_names, @@ -193,7 +186,7 @@ pub trait GearRI { .unwrap_or_else(|err| unreachable!("Cannot cast wasm mem addr to `usize`: {}", err)) }); - lazy_pages::initialize_for_program( + gear_lazy_pages::initialize_for_program( wasm_mem_addr, ctx.wasm_mem_size, ctx.stack_end, @@ -210,9 +203,9 @@ pub trait GearRI { /// else allows read and write accesses. fn mprotect_lazy_pages(protect: bool) { if protect { - lazy_pages::set_lazy_pages_protection() + gear_lazy_pages::set_lazy_pages_protection() } else { - lazy_pages::unset_lazy_pages_protection() + gear_lazy_pages::unset_lazy_pages_protection() } .map_err(|err| err.to_string()) .expect("Cannot set/unset mprotection for lazy pages"); @@ -225,7 +218,7 @@ pub trait GearRI { } fn write_accessed_pages() -> Vec { - lazy_pages::write_accessed_pages() + gear_lazy_pages::write_accessed_pages() .unwrap_or_else(|err| unreachable!("Cannot get write accessed pages: {err}")) } diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index a35574095b4..e52e9865188 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -28,7 +28,7 @@ frame-system-benchmarking = { workspace = true, optional = true } # Internal deps runtime-primitives.workspace = true gear-common.workspace = true -gear-backend-common.workspace = true +gear-lazy-pages-common.workspace = true gear-core-processor.workspace = true gear-core.workspace = true pallet-gear.workspace = true diff --git a/runtime/common/src/weights.rs b/runtime/common/src/weights.rs index 95317316ac7..d5a7b168135 100644 --- a/runtime/common/src/weights.rs +++ b/runtime/common/src/weights.rs @@ -1,5 +1,5 @@ -use gear_backend_common::lazy_pages::LazyPagesWeights; use gear_core_processor::configs::PageCosts; +use gear_lazy_pages_common::LazyPagesWeights; use pallet_gear::InstructionWeights; const INSTRUCTIONS_SPREAD: u8 = 50; diff --git a/runtime/gear/Cargo.toml b/runtime/gear/Cargo.toml index 77751e6991e..21393bb45c1 100644 --- a/runtime/gear/Cargo.toml +++ b/runtime/gear/Cargo.toml @@ -79,7 +79,7 @@ substrate-wasm-builder = { workspace = true, optional = true } [dev-dependencies] gear-core-processor.workspace = true -gear-backend-common.workspace = true +gear-lazy-pages-common.workspace = true [features] default = ["std"] diff --git a/runtime/gear/src/tests.rs b/runtime/gear/src/tests.rs index 95afd48443d..fdac76f2217 100644 --- a/runtime/gear/src/tests.rs +++ b/runtime/gear/src/tests.rs @@ -18,8 +18,8 @@ use super::*; use crate::Runtime; -use gear_backend_common::lazy_pages::LazyPagesWeights; use gear_core_processor::configs::PageCosts; +use gear_lazy_pages_common::LazyPagesWeights; use pallet_gear::{InstructionWeights, MemoryWeights}; use runtime_common::weights::{check_instructions_weights, check_pages_weights}; diff --git a/runtime/vara/Cargo.toml b/runtime/vara/Cargo.toml index 1640bc001fb..1b70602582f 100644 --- a/runtime/vara/Cargo.toml +++ b/runtime/vara/Cargo.toml @@ -106,7 +106,7 @@ sp-keyring.workspace = true env_logger.workspace = true wat.workspace = true gear-core-processor.workspace = true -gear-backend-common.workspace = true +gear-lazy-pages-common.workspace = true [build-dependencies] substrate-build-script-utils.workspace = true diff --git a/runtime/vara/src/tests.rs b/runtime/vara/src/tests.rs index ab677a238d5..e784daf1847 100644 --- a/runtime/vara/src/tests.rs +++ b/runtime/vara/src/tests.rs @@ -18,8 +18,8 @@ use super::*; use crate::Runtime; -use gear_backend_common::lazy_pages::LazyPagesWeights; use gear_core_processor::configs::PageCosts; +use gear_lazy_pages_common::LazyPagesWeights; use pallet_gear::{InstructionWeights, MemoryWeights}; use runtime_common::weights::{check_instructions_weights, check_pages_weights}; diff --git a/utils/wasm-gen/Cargo.toml b/utils/wasm-gen/Cargo.toml index 56b43edcf52..c732b30a91b 100644 --- a/utils/wasm-gen/Cargo.toml +++ b/utils/wasm-gen/Cargo.toml @@ -25,7 +25,6 @@ indicatif.workspace = true proptest.workspace = true gear-utils.workspace = true -gear-backend-sandbox = { workspace = true, features = ["std"] } -gear-backend-common = { workspace = true, features = ["mock"] } +gear-core-backend = { workspace = true, features = ["std"] } gear-core-processor = { workspace = true, features = ["std", "mock"] } -gear-lazy-pages-common = { workspace = true, features = ["std"] } +gear-lazy-pages-interface = { workspace = true, features = ["std"] } diff --git a/utils/wasm-gen/src/tests.rs b/utils/wasm-gen/src/tests.rs index 623c02c3ab3..fa5887bbe6a 100644 --- a/utils/wasm-gen/src/tests.rs +++ b/utils/wasm-gen/src/tests.rs @@ -18,8 +18,6 @@ use super::*; use arbitrary::Unstructured; -use gear_backend_common::{BackendReport, Environment, TerminationReason, TrapExplanation}; -use gear_backend_sandbox::SandboxEnvironment; use gear_core::{ code::Code, ids::{CodeId, ProgramId}, @@ -30,6 +28,10 @@ use gear_core::{ }, pages::WASM_PAGE_SIZE, }; +use gear_core_backend::{ + env::{BackendReport, Environment}, + error::{TerminationReason, TrapExplanation}, +}; use gear_core_processor::{ProcessorContext, ProcessorExternalities}; use gear_utils::NonEmpty; use gear_wasm_instrument::{ @@ -174,7 +176,7 @@ fn injecting_addresses_works() { #[test] fn error_processing_works_for_fallible_syscalls() { - use gear_backend_common::ActorTerminationReason; + use gear_core_backend::error::ActorTerminationReason; gear_utils::init_default_logger(); @@ -241,7 +243,7 @@ fn error_processing_works_for_fallible_syscalls() { #[test] fn precise_syscalls_works() { - use gear_backend_common::ActorTerminationReason; + use gear_core_backend::error::ActorTerminationReason; gear_utils::init_default_logger(); @@ -328,7 +330,7 @@ fn execute_wasm_with_custom_configs( const PROGRAM_STORAGE_PREFIX: [u8; 32] = *b"execute_wasm_with_custom_configs"; const INITIAL_PAGES: u16 = 1; - assert!(gear_lazy_pages_common::try_to_enable_lazy_pages( + assert!(gear_lazy_pages_interface::try_to_enable_lazy_pages( PROGRAM_STORAGE_PREFIX )); @@ -378,7 +380,7 @@ fn execute_wasm_with_custom_configs( }; let ext = gear_core_processor::Ext::new(processor_context); - let env = SandboxEnvironment::new( + let env = Environment::new( ext, code.code(), DispatchKind::Init, diff --git a/utils/wasm-instrument/Cargo.toml b/utils/wasm-instrument/Cargo.toml index b09d44adca9..c79f9f48507 100644 --- a/utils/wasm-instrument/Cargo.toml +++ b/utils/wasm-instrument/Cargo.toml @@ -17,13 +17,12 @@ enum-iterator.workspace = true [dev-dependencies] wasmparser.workspace = true wat.workspace = true -gear-backend-sandbox.workspace = true -gear-backend-common = { workspace = true, features = ["mock"] } +gear-core-backend = { workspace = true, features = ["mock"] } gear-core.workspace = true [features] default = ["std"] std = [ - "gear-backend-sandbox/std", + "gear-core-backend/std", "gwasm-instrument/std", ] diff --git a/utils/wasm-instrument/src/tests.rs b/utils/wasm-instrument/src/tests.rs index ac102195adf..a29fd16c75b 100644 --- a/utils/wasm-instrument/src/tests.rs +++ b/utils/wasm-instrument/src/tests.rs @@ -622,9 +622,12 @@ test_gas_counter_injection! { #[test] fn test_sys_calls_table() { use gas_metering::ConstantCostRules; - use gear_backend_common::{mock::MockExt, ActorTerminationReason, BackendReport, Environment}; - use gear_backend_sandbox::SandboxEnvironment; use gear_core::message::DispatchKind; + use gear_core_backend::{ + env::{BackendReport, Environment}, + error::ActorTerminationReason, + mock::MockExt, + }; use parity_wasm::builder; // Make module with one empty function. @@ -657,8 +660,8 @@ fn test_sys_calls_table() { // Execute wasm and check success. let ext = MockExt::default(); - let env = SandboxEnvironment::new(ext, &code, DispatchKind::Init, Default::default(), 0.into()) - .unwrap(); + let env = + Environment::new(ext, &code, DispatchKind::Init, Default::default(), 0.into()).unwrap(); let report = env .execute(|_, _, _| -> Result<(), u32> { Ok(()) }) .unwrap();