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