diff --git a/Cargo.lock b/Cargo.lock index b10e758b71c..145d3aebca6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -337,7 +337,7 @@ dependencies = [ "cfg-if", "libc", "miniz_oxide", - "object 0.28.4", + "object", "rustc-demangle", ] @@ -789,9 +789,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.12" +version = "3.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8b79fe3946ceb4a0b1c080b4018992b8d27e9ff363644c1c9b6387c854614d" +checksum = "ac2bd7a1eb07da9ac757c923f69373deb7bc2ba5efc951b873bcb5e693992dca" dependencies = [ "atty", "bitflags", @@ -940,39 +940,13 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-bforest" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38faa2a16616c8e78a18d37b4726b98bfd2de192f2fdc8a39ddf568a408a0f75" -dependencies = [ - "cranelift-entity 0.82.3", -] - [[package]] name = "cranelift-bforest" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7901fbba05decc537080b07cb3f1cadf53be7b7602ca8255786288a8692ae29a" dependencies = [ - "cranelift-entity 0.85.1", -] - -[[package]] -name = "cranelift-codegen" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26f192472a3ba23860afd07d2b0217dc628f21fcc72617aa1336d98e1671f33b" -dependencies = [ - "cranelift-bforest 0.82.3", - "cranelift-codegen-meta 0.82.3", - "cranelift-codegen-shared 0.82.3", - "cranelift-entity 0.82.3", - "gimli", - "log", - "regalloc", - "smallvec", - "target-lexicon", + "cranelift-entity", ] [[package]] @@ -981,10 +955,10 @@ version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ba1b45d243a4a28e12d26cd5f2507da74e77c45927d40de8b6ffbf088b46b5" dependencies = [ - "cranelift-bforest 0.85.1", - "cranelift-codegen-meta 0.85.1", - "cranelift-codegen-shared 0.85.1", - "cranelift-entity 0.85.1", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", "cranelift-isle", "gimli", "log", @@ -993,45 +967,21 @@ dependencies = [ "target-lexicon", ] -[[package]] -name = "cranelift-codegen-meta" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32ddb89e9b89d3d9b36a5b7d7ea3261c98235a76ac95ba46826b8ec40b1a24" -dependencies = [ - "cranelift-codegen-shared 0.82.3", -] - [[package]] name = "cranelift-codegen-meta" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54cc30032171bf230ce22b99c07c3a1de1221cb5375bd6dbe6dbe77d0eed743c" dependencies = [ - "cranelift-codegen-shared 0.85.1", + "cranelift-codegen-shared", ] -[[package]] -name = "cranelift-codegen-shared" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01fd0d9f288cc1b42d9333b7a776b17e278fc888c28e6a0f09b5573d45a150bc" - [[package]] name = "cranelift-codegen-shared" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23f2672426d2bb4c9c3ef53e023076cfc4d8922f0eeaebaf372c92fae8b5c69" -[[package]] -name = "cranelift-entity" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3bfe172b83167604601faf9dc60453e0d0a93415b57a9c4d1a7ae6849185cf" -dependencies = [ - "serde", -] - [[package]] name = "cranelift-entity" version = "0.85.1" @@ -1041,25 +991,13 @@ dependencies = [ "serde", ] -[[package]] -name = "cranelift-frontend" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a006e3e32d80ce0e4ba7f1f9ddf66066d052a8c884a110b91d05404d6ce26dce" -dependencies = [ - "cranelift-codegen 0.82.3", - "log", - "smallvec", - "target-lexicon", -] - [[package]] name = "cranelift-frontend" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace74eeca11c439a9d4ed1a5cb9df31a54cd0f7fbddf82c8ce4ea8e9ad2a8fe0" dependencies = [ - "cranelift-codegen 0.85.1", + "cranelift-codegen", "log", "smallvec", "target-lexicon", @@ -1071,58 +1009,31 @@ version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db1ae52a5cc2cad0d86fdd3dcb16b7217d2f1e65ab4f5814aa4f014ad335fa43" -[[package]] -name = "cranelift-native" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501241b0cdf903412ec9075385ac9f2b1eb18a89044d1538e97fab603231f70c" -dependencies = [ - "cranelift-codegen 0.82.3", - "libc", - "target-lexicon", -] - [[package]] name = "cranelift-native" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dadcfb7852900780d37102bce5698bcd401736403f07b52e714ff7a180e0e22f" dependencies = [ - "cranelift-codegen 0.85.1", + "cranelift-codegen", "libc", "target-lexicon", ] -[[package]] -name = "cranelift-wasm" -version = "0.82.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16d9e4211bbc3268042a96dd4de5bd979cda22434991d035f5f8eacba987fad2" -dependencies = [ - "cranelift-codegen 0.82.3", - "cranelift-entity 0.82.3", - "cranelift-frontend 0.82.3", - "itertools", - "log", - "smallvec", - "wasmparser 0.83.0", - "wasmtime-types 0.35.3", -] - [[package]] name = "cranelift-wasm" version = "0.85.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c84e3410960389110b88f97776f39f6d2c8becdaa4cd59e390e6b76d9d0e7190" dependencies = [ - "cranelift-codegen 0.85.1", - "cranelift-entity 0.85.1", - "cranelift-frontend 0.85.1", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", "itertools", "log", "smallvec", "wasmparser 0.85.0", - "wasmtime-types 0.38.1", + "wasmtime-types", ] [[package]] @@ -2544,17 +2455,17 @@ dependencies = [ ] [[package]] -name = "gear-backend-wasmtime" +name = "gear-backend-wasmi" version = "0.1.0" dependencies = [ - "anyhow", "derive_more", "gear-backend-common", "gear-core", "gear-core-errors", "log", "parity-scale-codec", - "wasmtime 0.35.3", + "parity-wasm 0.42.2", + "wasmi", ] [[package]] @@ -2806,7 +2717,7 @@ dependencies = [ "derive_more", "env_logger", "gear-backend-common", - "gear-backend-wasmtime", + "gear-backend-wasmi", "gear-common", "gear-core", "gear-core-processor", @@ -2994,7 +2905,7 @@ dependencies = [ "derive_more", "env_logger", "gear-backend-common", - "gear-backend-wasmtime", + "gear-backend-wasmi", "gear-core", "gear-core-processor", "gear-wasm-builder", @@ -3003,7 +2914,7 @@ dependencies = [ "parity-scale-codec", "path-clean", "wasm-instrument", - "wasmtime 0.35.3", + "wasmi", ] [[package]] @@ -4862,17 +4773,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "crc32fast", - "indexmap", - "memchr", -] - [[package]] name = "object" version = "0.28.4" @@ -6165,17 +6065,6 @@ dependencies = [ "syn", ] -[[package]] -name = "regalloc" -version = "0.0.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62446b1d3ebf980bdc68837700af1d77b37bc430e524bf95319c6eada2a4cc02" -dependencies = [ - "log", - "rustc-hash", - "smallvec", -] - [[package]] name = "regalloc2" version = "0.2.3" @@ -6856,7 +6745,7 @@ dependencies = [ "sp-runtime-interface", "sp-sandbox", "sp-wasm-interface", - "wasmtime 0.38.1", + "wasmtime", ] [[package]] @@ -7556,9 +7445,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] @@ -7574,9 +7463,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", @@ -8455,7 +8344,7 @@ dependencies = [ "parity-scale-codec", "sp-std", "wasmi", - "wasmtime 0.38.1", + "wasmtime", ] [[package]] @@ -9608,7 +9497,7 @@ dependencies = [ "leb128", "libloading 0.7.3", "loupe", - "object 0.28.4", + "object", "rkyv", "serde", "tempfile", @@ -9664,7 +9553,7 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d831335ff3a44ecf451303f6f891175c642488036b92ceceb24ac8623a8fa8b" dependencies = [ - "object 0.28.4", + "object", "thiserror", "wasmer-compiler", "wasmer-types", @@ -9773,36 +9662,6 @@ dependencies = [ "wasmparser 0.87.0", ] -[[package]] -name = "wasmtime" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21ffb4705016d5ca91e18a72ed6822dab50e6d5ddd7045461b17ef19071cdef1" -dependencies = [ - "anyhow", - "backtrace", - "bincode", - "cfg-if", - "indexmap", - "lazy_static", - "libc", - "log", - "object 0.27.1", - "once_cell", - "paste", - "psm", - "rayon", - "region 2.2.0", - "serde", - "target-lexicon", - "wasmparser 0.83.0", - "wasmtime-cranelift 0.35.3", - "wasmtime-environ 0.35.3", - "wasmtime-jit 0.35.3", - "wasmtime-runtime 0.35.3", - "winapi", -] - [[package]] name = "wasmtime" version = "0.38.1" @@ -9817,7 +9676,7 @@ dependencies = [ "lazy_static", "libc", "log", - "object 0.28.4", + "object", "once_cell", "paste", "psm", @@ -9827,10 +9686,10 @@ dependencies = [ "target-lexicon", "wasmparser 0.85.0", "wasmtime-cache", - "wasmtime-cranelift 0.38.1", - "wasmtime-environ 0.38.1", - "wasmtime-jit 0.38.1", - "wasmtime-runtime 0.38.1", + "wasmtime-cranelift", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", "winapi", ] @@ -9854,28 +9713,6 @@ dependencies = [ "zstd", ] -[[package]] -name = "wasmtime-cranelift" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04c810078a491b7bc4866ebe045f714d2b95e6b539e1f64009a4a7606be11de" -dependencies = [ - "anyhow", - "cranelift-codegen 0.82.3", - "cranelift-entity 0.82.3", - "cranelift-frontend 0.82.3", - "cranelift-native 0.82.3", - "cranelift-wasm 0.82.3", - "gimli", - "log", - "more-asserts", - "object 0.27.1", - "target-lexicon", - "thiserror", - "wasmparser 0.83.0", - "wasmtime-environ 0.35.3", -] - [[package]] name = "wasmtime-cranelift" version = "0.38.1" @@ -9883,39 +9720,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dc0f80afa1ce97083a7168e6b6948d015d6237369e9f4a511d38c9c4ac8fbb9" dependencies = [ "anyhow", - "cranelift-codegen 0.85.1", - "cranelift-entity 0.85.1", - "cranelift-frontend 0.85.1", - "cranelift-native 0.85.1", - "cranelift-wasm 0.85.1", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", + "cranelift-native", + "cranelift-wasm", "gimli", "log", "more-asserts", - "object 0.28.4", + "object", "target-lexicon", "thiserror", "wasmparser 0.85.0", - "wasmtime-environ 0.38.1", -] - -[[package]] -name = "wasmtime-environ" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61448266ea164b1ac406363cdcfac81c7c44db4d94c7a81c8620ac6c5c6cdf59" -dependencies = [ - "anyhow", - "cranelift-entity 0.82.3", - "gimli", - "indexmap", - "log", - "more-asserts", - "object 0.27.1", - "serde", - "target-lexicon", - "thiserror", - "wasmparser 0.83.0", - "wasmtime-types 0.35.3", + "wasmtime-environ", ] [[package]] @@ -9925,42 +9742,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0816d9365196f1f447060087e0f87239ccded830bd54970a1168b0c9c8e824c9" dependencies = [ "anyhow", - "cranelift-entity 0.85.1", + "cranelift-entity", "gimli", "indexmap", "log", "more-asserts", - "object 0.28.4", + "object", "serde", "target-lexicon", "thiserror", "wasmparser 0.85.0", - "wasmtime-types 0.38.1", -] - -[[package]] -name = "wasmtime-jit" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "156b4623c6b0d4b8c24afb846c20525922f538ef464cc024abab7ea8de2109a2" -dependencies = [ - "addr2line", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli", - "log", - "object 0.27.1", - "region 2.2.0", - "rustc-demangle", - "rustix 0.33.7", - "serde", - "target-lexicon", - "thiserror", - "wasmtime-environ 0.35.3", - "wasmtime-runtime 0.35.3", - "winapi", + "wasmtime-types", ] [[package]] @@ -9976,28 +9768,19 @@ dependencies = [ "cpp_demangle", "gimli", "log", - "object 0.28.4", + "object", "region 2.2.0", "rustc-demangle", "rustix 0.33.7", "serde", "target-lexicon", "thiserror", - "wasmtime-environ 0.38.1", - "wasmtime-jit-debug 0.38.1", - "wasmtime-runtime 0.38.1", + "wasmtime-environ", + "wasmtime-jit-debug", + "wasmtime-runtime", "winapi", ] -[[package]] -name = "wasmtime-jit-debug" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5dc31f811760a6c76b2672c404866fd19b75e5fb3b0075a3e377a6846490654" -dependencies = [ - "lazy_static", -] - [[package]] name = "wasmtime-jit-debug" version = "0.38.1" @@ -10005,35 +9788,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b252d1d025f94f3954ba2111f12f3a22826a0764a11c150c2d46623115a69e27" dependencies = [ "lazy_static", - "object 0.28.4", + "object", "rustix 0.33.7", ] -[[package]] -name = "wasmtime-runtime" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f907beaff69d4d920fa4688411ee4cc75c0f01859e424677f9e426e2ef749864" -dependencies = [ - "anyhow", - "backtrace", - "cc", - "cfg-if", - "indexmap", - "libc", - "log", - "mach", - "memoffset", - "more-asserts", - "rand 0.8.5", - "region 2.2.0", - "rustix 0.33.7", - "thiserror", - "wasmtime-environ 0.35.3", - "wasmtime-jit-debug 0.35.3", - "winapi", -] - [[package]] name = "wasmtime-runtime" version = "0.38.1" @@ -10055,30 +9813,18 @@ dependencies = [ "region 2.2.0", "rustix 0.33.7", "thiserror", - "wasmtime-environ 0.38.1", - "wasmtime-jit-debug 0.38.1", + "wasmtime-environ", + "wasmtime-jit-debug", "winapi", ] -[[package]] -name = "wasmtime-types" -version = "0.35.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514ef0e5fd197b9609dc9eb74beba0c84d5a12b2417cbae55534633329ba4852" -dependencies = [ - "cranelift-entity 0.82.3", - "serde", - "thiserror", - "wasmparser 0.83.0", -] - [[package]] name = "wasmtime-types" version = "0.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d129b0487a95986692af8708ffde9c50b0568dcefd79200941d475713b4f40bb" dependencies = [ - "cranelift-entity 0.85.1", + "cranelift-entity", "serde", "thiserror", "wasmparser 0.85.0", diff --git a/Cargo.toml b/Cargo.toml index aa7452709ff..65a4b80b96c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,8 +20,8 @@ members = [ "common/codegen", "core", "core-backend/common", - "core-backend/wasmtime", "core-backend/sandbox", + "core-backend/wasmi", "core-processor", "core-errors", "examples/binaries/*", diff --git a/common/src/lib.rs b/common/src/lib.rs index 47d4996eefc..106c3cd8e98 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -171,11 +171,24 @@ pub enum Program { Terminated, } -#[derive(Clone, Copy, Debug)] -pub enum ProgramError { - CodeHashNotFound, +#[derive(Clone, Debug, derive_more::Display)] +pub enum CommonError { + #[display(fmt = "Program is terminated")] IsTerminated, - DoesNotExist, + #[display(fmt = "Program does not exist for id = {}", _0)] + DoesNotExist(H256), + #[display(fmt = "Cannot find data for {:?}, program {}", page, program_id)] + CannotFindDataForPage { + program_id: H256, + page: PageNumber, + }, + MemoryError(MemoryError), +} + +impl From for CommonError { + fn from(err: MemoryError) -> Self { + Self::MemoryError(err) + } } impl Program { @@ -209,12 +222,12 @@ impl Program { } impl core::convert::TryFrom for ActiveProgram { - type Error = ProgramError; + type Error = CommonError; fn try_from(prog_with_status: Program) -> Result { match prog_with_status { Program::Active(p) => Ok(p), - Program::Terminated => Err(ProgramError::IsTerminated), + Program::Terminated => Err(CommonError::IsTerminated), } } } @@ -297,10 +310,10 @@ pub fn set_program_initialized(id: H256) { } } -pub fn set_program_terminated_status(id: H256) -> Result<(), ProgramError> { +pub fn set_program_terminated_status(id: H256) -> Result<(), CommonError> { if let Some(program) = get_program(id) { if program.is_terminated() { - return Err(ProgramError::IsTerminated); + return Err(CommonError::IsTerminated); } sp_io::storage::clear_prefix(&pages_prefix(id), None); @@ -308,7 +321,7 @@ pub fn set_program_terminated_status(id: H256) -> Result<(), ProgramError> { Ok(()) } else { - Err(ProgramError::DoesNotExist) + Err(CommonError::DoesNotExist(id)) } } @@ -318,34 +331,49 @@ pub fn get_program(id: H256) -> Option { } /// Returns mem page data from storage for program `id` and `page_idx` -pub fn get_program_page_data( - id: H256, - page_idx: PageNumber, -) -> Option> { - let key = page_key(id, page_idx); - let data = sp_io::storage::get(&key)?; - Some(PageBuf::new_from_vec(data)) +pub fn get_program_page_data(program_id: H256, page: PageNumber) -> Result { + let key = page_key(program_id, page); + let data = + sp_io::storage::get(&key).ok_or(CommonError::CannotFindDataForPage { program_id, page })?; + PageBuf::new_from_vec(data).map_err(Into::into) } +/// Returns data for all program pages, that have data in storage. pub fn get_program_pages_data( - id: H256, + program_id: H256, program: &ActiveProgram, -) -> Result, MemoryError> { - get_program_data_for_pages(id, program.pages_with_data.iter()) +) -> Result, CommonError> { + get_program_data_for_pages(program_id, program.pages_with_data.iter()) } -/// Returns data for all pages from `pages` arg, which has data in storage. +/// Returns program data for each page from `pages` pub fn get_program_data_for_pages<'a>( - id: H256, + program_id: H256, pages: impl Iterator, -) -> Result, MemoryError> { +) -> Result, CommonError> { + let mut pages_data = BTreeMap::new(); + for &page in pages { + let key = page_key(program_id, page); + let data = sp_io::storage::get(&key) + .ok_or(CommonError::CannotFindDataForPage { program_id, page })?; + let page_buf = PageBuf::new_from_vec(data)?; + pages_data.insert(page, page_buf); + } + Ok(pages_data) +} + +/// Returns program data for each page from `pages`, +/// which has data in storage. +pub fn get_program_data_for_pages_optional( + program_id: H256, + pages: impl Iterator, +) -> Result, CommonError> { let mut pages_data = BTreeMap::new(); for page in pages { - let key = page_key(id, *page); - let data = sp_io::storage::get(&key); - if let Some(data) = data { + let key = page_key(program_id, page); + if let Some(data) = sp_io::storage::get(&key) { let page_buf = PageBuf::new_from_vec(data)?; - pages_data.insert(*page, page_buf); + pages_data.insert(page, page_buf); } } Ok(pages_data) diff --git a/core-backend/sandbox/src/funcs.rs b/core-backend/sandbox/src/funcs.rs index 0bdd2e608f4..bbcaca2f98e 100644 --- a/core-backend/sandbox/src/funcs.rs +++ b/core-backend/sandbox/src/funcs.rs @@ -66,6 +66,7 @@ pub(crate) fn return_i32>(val: T) -> SyscallOutput { } pub(crate) fn return_i64>(val: T) -> SyscallOutput { + // Issue (#1208) val.try_into() .map(|v| Value::I64(v).into()) .map_err(|_| HostError) diff --git a/core-backend/wasmtime/Cargo.toml b/core-backend/wasmi/Cargo.toml similarity index 53% rename from core-backend/wasmtime/Cargo.toml rename to core-backend/wasmi/Cargo.toml index 40b3835bcd0..63ddd494ba1 100644 --- a/core-backend/wasmtime/Cargo.toml +++ b/core-backend/wasmi/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "gear-backend-wasmtime" +name = "gear-backend-wasmi" version = "0.1.0" authors = ["Gear Technologies"] edition = "2018" @@ -10,8 +10,12 @@ gear-core = { path = "../../core" } gear-core-errors = { path = "../../core-errors", features = ["codec"] } gear-backend-common = { path = "../common" } -wasmtime = { version = "0.35.1", default-features = false, features = ["parallel-compilation", "cranelift"] } -anyhow = { version = "1.0.57", default-features = false } -codec = { package = "parity-scale-codec", version = "3.1.2", default-features = false, features = ["derive"] } -log = "0.4.17" +parity-wasm = { version = "0.42.2", default-features = false } +wasmi = { version = "0.11.0", default-features = false } +log = { version = "0.4.17", default-features = false } derive_more = "0.99.17" +codec = { package = "parity-scale-codec", version = "3.1.2", default-features = false } + +[features] +default = ["std"] +std = ["wasmi/std", "parity-wasm/std", "log/std"] diff --git a/core-backend/wasmi/src/env.rs b/core-backend/wasmi/src/env.rs new file mode 100644 index 00000000000..91c69363ab8 --- /dev/null +++ b/core-backend/wasmi/src/env.rs @@ -0,0 +1,505 @@ +// This file is part of Gear. + +// Copyright (C) 2022 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-lat&er 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 . + +//! wasmi environment for running a module. + +use crate::{ + funcs::{FuncError, FuncsHandler as Funcs}, + memory::MemoryWrap, +}; +use alloc::{ + collections::{BTreeMap, BTreeSet}, + string::{String, ToString}, +}; +use core::fmt; +use gear_backend_common::{ + error_processor::IntoExtError, AsTerminationReason, BackendError, BackendReport, Environment, + IntoExtInfo, TerminationReason, TrapExplanation, +}; +use gear_core::{ + env::{Ext, ExtCarrier}, + gas::GasAmount, + memory::WasmPageNumber, + message::DispatchKind, +}; +use gear_core_errors::MemoryError; +use wasmi::{ + memory_units::Pages, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, + ImportResolver, MemoryDescriptor, MemoryInstance, MemoryRef, ModuleInstance, ModuleRef, + RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, TrapKind, +}; + +#[derive(Debug, derive_more::Display)] +pub enum WasmiEnvironmentError { + #[display(fmt = "Unable to instantiate module: {:?}", _0)] + ModuleInstantiation(wasmi::Error), + #[display(fmt = "Unable to get wasm module exports: {}", _0)] + GetWasmExports(String), + #[display(fmt = "Unable to set module memory data")] + SetModuleMemoryData, + #[display(fmt = "Unable to save static pages initial data")] + SaveStaticPagesInitialData, + #[display(fmt = "Failed to create env memory: {:?}", _0)] + CreateEnvMemory(wasmi::Error), + #[display(fmt = "{}", _0)] + Memory(MemoryError), + #[display(fmt = "{}", _0)] + PostExecutionHandler(String), +} + +/// Environment to run one module at a time providing Ext. +pub struct WasmiEnvironment { + runtime: Runtime, + instance: ModuleRef, + defined_host_functions: DefinedHostFunctions, E::Error>, + entries: BTreeSet, +} + +pub struct Runtime { + pub ext: ExtCarrier, + pub memory: MemoryWrap, + pub err: FuncError, +} + +struct HostFuncIndex(usize); + +/// Function pointer for specifying functions by the +/// supervisor in [`EnvironmentDefinitionBuilder`]. +pub type HostFuncType = fn(&mut T, &[RuntimeValue]) -> Result>; + +pub struct DefinedHostFunctions { + funcs: Vec>, +} + +impl Clone for DefinedHostFunctions { + fn clone(&self) -> DefinedHostFunctions { + DefinedHostFunctions { + funcs: self.funcs.clone(), + } + } +} + +impl DefinedHostFunctions { + fn new() -> DefinedHostFunctions { + DefinedHostFunctions { funcs: Vec::new() } + } + + fn define(&mut self, f: HostFuncType) -> HostFuncIndex { + let idx = self.funcs.len(); + self.funcs.push(f); + HostFuncIndex(idx) + } +} + +#[derive(Debug)] +struct DummyHostError; + +impl fmt::Display for DummyHostError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "DummyHostError") + } +} + +impl wasmi::HostError for DummyHostError {} + +pub struct GuestExternals<'a, T: 'a, E> { + pub state: &'a mut T, + pub defined_host_functions: &'a DefinedHostFunctions, +} + +impl<'a, T, E> Externals for GuestExternals<'a, T, E> { + fn invoke_index( + &mut self, + index: usize, + args: RuntimeArgs, + ) -> Result, Trap> { + let args = args.as_ref().to_vec(); + + let result = (self.defined_host_functions.funcs[index])(self.state, &args); + match result { + Ok(value) => Ok(match value { + ReturnValue::Value(v) => Some(v), + ReturnValue::Unit => None, + }), + Err(_e) => Err(TrapKind::Host(Box::new(DummyHostError)).into()), + } + } +} + +/// Typed value that can be returned from a function. +/// +/// Basically a `TypedValue` plus `Unit`, for functions which return nothing. +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum ReturnValue { + /// For returning nothing. + Unit, + /// For returning some concrete value. + Value(RuntimeValue), +} + +impl From for ReturnValue { + fn from(v: RuntimeValue) -> ReturnValue { + ReturnValue::Value(v) + } +} + +enum ExternVal { + HostFunc(HostFuncIndex), + Memory(MemoryRef), +} + +/// A builder for the environment of the WASM module. +pub struct EnvironmentDefinitionBuilder { + map: BTreeMap<(Vec, Vec), ExternVal>, + pub defined_host_functions: DefinedHostFunctions, + pub forbidden_funcs: BTreeSet, +} + +impl EnvironmentDefinitionBuilder { + pub fn new(forbidden_funcs: BTreeSet) -> EnvironmentDefinitionBuilder { + EnvironmentDefinitionBuilder { + map: BTreeMap::new(), + defined_host_functions: DefinedHostFunctions::new(), + forbidden_funcs, + } + } + + pub fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) + where + N1: Into>, + N2: Into>, + { + let idx = self.defined_host_functions.define(f); + self.map + .insert((module.into(), field.into()), ExternVal::HostFunc(idx)); + } + + pub fn add_memory(&mut self, module: N1, field: N2, mem: MemoryRef) + where + N1: Into>, + N2: Into>, + { + self.map + .insert((module.into(), field.into()), ExternVal::Memory(mem)); + } +} + +impl ImportResolver for EnvironmentDefinitionBuilder { + fn resolve_func( + &self, + module_name: &str, + field_name: &str, + signature: &Signature, + ) -> Result { + let key = ( + module_name.as_bytes().to_owned(), + field_name.as_bytes().to_owned(), + ); + + let externval = if self.forbidden_funcs.contains(field_name) { + self.map + .get(&(b"env".to_vec(), b"forbidden".to_vec())) + .ok_or_else(|| { + log::debug!( + target: "gwasm", + "Export {}:{} is forbidden", + module_name, + field_name + ); + wasmi::Error::Instantiation(String::new()) + })? + } else { + self.map.get(&key).ok_or_else(|| { + log::debug!( + target: "gwasm", + "Export {}:{} not found", + module_name, + field_name + ); + wasmi::Error::Instantiation(String::new()) + })? + }; + + let host_func_idx = match *externval { + ExternVal::HostFunc(ref idx) => idx, + _ => { + log::debug!( + target: "gwasm", + "Export {}:{} is not a host func", + module_name, + field_name, + ); + return Err(wasmi::Error::Instantiation(String::new())); + } + }; + Ok(FuncInstance::alloc_host(signature.clone(), host_func_idx.0)) + } + + fn resolve_global( + &self, + _module_name: &str, + _field_name: &str, + _global_type: &GlobalDescriptor, + ) -> Result { + log::debug!(target: "gwasm", "Importing globals is not supported yet"); + Err(wasmi::Error::Instantiation(String::new())) + } + + fn resolve_memory( + &self, + module_name: &str, + field_name: &str, + _memory_type: &MemoryDescriptor, + ) -> Result { + let key = ( + module_name.as_bytes().to_owned(), + field_name.as_bytes().to_owned(), + ); + let externval = self.map.get(&key).ok_or_else(|| { + log::debug!( + target: "gwasm", + "Memory export {}:{} not found", + module_name, + field_name + ); + wasmi::Error::Instantiation(String::new()) + })?; + let memory = match *externval { + ExternVal::Memory(ref m) => m, + _ => { + log::debug!( + target: "gwasm", + "Export {}:{} is not a memory", + module_name, + field_name, + ); + return Err(wasmi::Error::Instantiation(String::new())); + } + }; + Ok(memory.clone()) + } + + fn resolve_table( + &self, + _module_name: &str, + _field_name: &str, + _table_type: &TableDescriptor, + ) -> Result { + log::debug!("Importing tables is not supported yet"); + Err(wasmi::Error::Instantiation(String::new())) + } +} + +impl Environment for WasmiEnvironment +where + E: Ext + IntoExtInfo + 'static, + E::Error: AsTerminationReason + IntoExtError, +{ + type Memory = MemoryWrap; + type Error = WasmiEnvironmentError; + + fn new( + ext: E, + binary: &[u8], + entries: BTreeSet, + mem_size: WasmPageNumber, + ) -> Result> { + let mut builder = EnvironmentDefinitionBuilder::new( + ext.forbidden_funcs() + .clone() + .into_iter() + .map(|s| s.to_string()) + .collect(), + ); + + builder.add_host_func("env", "forbidden", Funcs::forbidden); + builder.add_host_func("env", "gr_block_height", Funcs::block_height); + builder.add_host_func("env", "gr_block_timestamp", Funcs::block_timestamp); + builder.add_host_func("env", "gr_create_program", Funcs::create_program); + builder.add_host_func("env", "gr_create_program_wgas", Funcs::create_program_wgas); + builder.add_host_func("env", "gr_debug", Funcs::debug); + builder.add_host_func("env", "gr_error", Funcs::error); + builder.add_host_func("env", "gr_exit", Funcs::exit); + builder.add_host_func("env", "gr_exit_code", Funcs::exit_code); + builder.add_host_func("env", "gr_gas_available", Funcs::gas_available); + builder.add_host_func("env", "gr_leave", Funcs::leave); + builder.add_host_func("env", "gr_msg_id", Funcs::msg_id); + builder.add_host_func("env", "gr_origin", Funcs::origin); + builder.add_host_func("env", "gr_program_id", Funcs::program_id); + builder.add_host_func("env", "gr_read", Funcs::read); + builder.add_host_func("env", "gr_reply", Funcs::reply); + builder.add_host_func("env", "gr_reply_commit", Funcs::reply_commit); + builder.add_host_func("env", "gr_reply_commit_wgas", Funcs::reply_commit_wgas); + builder.add_host_func("env", "gr_reply_push", Funcs::reply_push); + builder.add_host_func("env", "gr_reply_to", Funcs::reply_to); + builder.add_host_func("env", "gr_reply_wgas", Funcs::reply_wgas); + builder.add_host_func("env", "gr_send", Funcs::send); + builder.add_host_func("env", "gr_send_commit", Funcs::send_commit); + builder.add_host_func("env", "gr_send_commit_wgas", Funcs::send_commit_wgas); + builder.add_host_func("env", "gr_send_init", Funcs::send_init); + builder.add_host_func("env", "gr_send_push", Funcs::send_push); + builder.add_host_func("env", "gr_send_wgas", Funcs::send_wgas); + builder.add_host_func("env", "gr_size", Funcs::size); + builder.add_host_func("env", "gr_source", Funcs::source); + builder.add_host_func("env", "gr_value", Funcs::value); + builder.add_host_func("env", "gr_value_available", Funcs::value_available); + builder.add_host_func("env", "gr_wait", Funcs::wait); + builder.add_host_func("env", "gr_wake", Funcs::wake); + + let ext_carrier = ExtCarrier::new(ext); + + let mem: MemoryRef = match MemoryInstance::alloc(Pages(mem_size.0 as usize), None) { + Ok(mem) => mem, + Err(e) => { + return Err(BackendError { + reason: WasmiEnvironmentError::CreateEnvMemory(e), + gas_amount: ext_carrier.into_inner().into_gas_amount(), + }) + } + }; + + builder.add_memory("env", "memory", mem.clone()); + builder.add_host_func("env", "alloc", Funcs::alloc); + builder.add_host_func("env", "free", Funcs::free); + builder.add_host_func("env", "gas", Funcs::gas); + + let runtime = Runtime { + ext: ext_carrier, + memory: MemoryWrap::new(mem), + err: FuncError::Terminated(TerminationReason::Success), + }; + + let defined_host_functions = builder.defined_host_functions.clone(); + let module = match wasmi::Module::from_buffer(binary) { + Ok(module) => module, + Err(e) => { + return Err(BackendError { + reason: WasmiEnvironmentError::ModuleInstantiation(e), + gas_amount: runtime.ext.into_inner().into_gas_amount(), + }) + } + }; + let instance = match ModuleInstance::new(&module, &builder) { + Ok(inst) => inst.not_started_instance().clone(), + Err(e) => { + return Err(BackendError { + reason: WasmiEnvironmentError::ModuleInstantiation(e), + gas_amount: runtime.ext.into_inner().into_gas_amount(), + }) + } + }; + + Ok(WasmiEnvironment { + runtime, + instance, + defined_host_functions, + entries, + }) + } + + fn get_stack_mem_end(&mut self) -> Option { + // '__gear_stack_end' export is inserted in wasm-proc or wasm-builder + let global = self + .instance + .export_by_name("__gear_stack_end")? + .as_global()? + .get(); + global.try_into::().and_then(|addr| { + if addr < 0 { + None + } else { + Some(WasmPageNumber( + (addr as usize / WasmPageNumber::size()) as u32, + )) + } + }) + } + + fn get_mem(&self) -> &Self::Memory { + &self.runtime.memory + } + + fn get_mem_mut(&mut self) -> &mut Self::Memory { + &mut self.runtime.memory + } + + fn execute( + mut self, + entry_point: &DispatchKind, + post_execution_handler: F, + ) -> Result> + where + F: FnOnce(&Self::Memory) -> Result<(), T>, + T: fmt::Display, + { + let res = if self.entries.contains(entry_point) { + let mut externals = GuestExternals { + state: &mut self.runtime, + defined_host_functions: &self.defined_host_functions, + }; + self.instance + .invoke_export(entry_point.into_entry(), &[], &mut externals) + .map(|_| ()) + } else { + Ok(()) + }; + + let Runtime { + ext, + memory, + err: trap, + } = self.runtime; + + log::debug!("WasmiEnvironment::execute result = {res:?}"); + + let (info, trap_explanation) = + ext.into_inner() + .into_ext_info(&memory) + .map_err(|(reason, gas_amount)| BackendError { + reason: WasmiEnvironmentError::Memory(reason), + gas_amount, + })?; + + let termination = if res.is_err() { + let reason = trap_explanation + .map(TerminationReason::Trap) + .unwrap_or_else(|| trap.into_termination_reason()); + + // success is unacceptable when there is error + if let TerminationReason::Success = reason { + TerminationReason::Trap(TrapExplanation::Unknown) + } else { + reason + } + } else { + TerminationReason::Success + }; + + match post_execution_handler(&memory) { + Ok(_) => Ok(BackendReport { termination, info }), + Err(e) => Err(BackendError { + reason: WasmiEnvironmentError::PostExecutionHandler(e.to_string()), + gas_amount: info.gas_amount, + }), + } + } + + fn into_gas_amount(self) -> GasAmount { + self.runtime.ext.into_inner().into_gas_amount() + } +} diff --git a/core-backend/wasmi/src/funcs.rs b/core-backend/wasmi/src/funcs.rs new file mode 100644 index 00000000000..2daa97ff3de --- /dev/null +++ b/core-backend/wasmi/src/funcs.rs @@ -0,0 +1,920 @@ +// This file is part of Gear. + +// Copyright (C) 2022 Gear Techn&&ologies 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::env::{ReturnValue, Runtime}; +#[cfg(not(feature = "std"))] +use alloc::string::ToString; +use alloc::{ + string::{FromUtf8Error, String}, + vec, +}; +use codec::Encode; +use core::{ + convert::{TryFrom, TryInto}, + fmt, + marker::PhantomData, + slice::Iter, +}; +use gear_backend_common::{ + error_processor::{IntoExtError, ProcessError}, + funcs, AsTerminationReason, IntoExtInfo, TerminationReason, TrapExplanation, +}; +use gear_core::{ + env::{Ext, ExtCarrierWithError}, + ids::{MessageId, ProgramId}, + memory::Memory, + message::{HandlePacket, InitPacket, ReplyPacket}, +}; +use gear_core_errors::MemoryError; +use wasmi::{Error, RuntimeValue}; + +pub(crate) type SyscallOutput = Result>; + +pub(crate) fn pop_i32>(arg: &mut Iter<'_, RuntimeValue>) -> Result +where + >::Error: std::fmt::Display, +{ + match arg.next() { + Some(RuntimeValue::I32(val)) => Ok((*val) + .try_into() + .map_err(|e| Error::Value(format!("{}", e)))?), + _ => Err(Error::Value("popi32".to_string())), + } +} + +pub(crate) fn pop_i64>(arg: &mut Iter<'_, RuntimeValue>) -> Result +where + >::Error: std::fmt::Display, +{ + match arg.next() { + Some(RuntimeValue::I64(val)) => Ok((*val) + .try_into() + .map_err(|e| Error::Value(format!("{}", e)))?), + _ => Err(Error::Value("popi64".to_string())), + } +} + +pub(crate) fn return_i32>(val: T) -> Result { + val.try_into() + .map(|v| RuntimeValue::I32(v).into()) + .map_err(|_| Error::Value("return_i32 err".to_string())) +} + +pub(crate) fn return_i64 + fmt::Display>(val: T) -> Result { + val.try_into() + .map(|v| RuntimeValue::I64(v).into()) + .map_err(|_| Error::Value("return_i64 err".to_string())) +} + +fn wto(memory: &mut impl Memory, ptr: usize, buff: &[u8]) -> Result<(), FuncError> { + memory.write(ptr, buff).map_err(FuncError::Memory) +} + +#[derive(Debug, derive_more::Display)] +pub enum FuncError { + #[display(fmt = "{}", _0)] + Core(E), + #[display(fmt = "{}", _0)] + LaterExtWith(ExtCarrierWithError), + #[display(fmt = "Runtime Error")] + HostError, + #[display(fmt = "{}", _0)] + Memory(MemoryError), + #[display(fmt = "Cannot set u128: {}", _0)] + SetU128(MemoryError), + #[display(fmt = "Exit code ran into non-reply scenario")] + NonReplyExitCode, + #[display(fmt = "Not running in reply context")] + NoReplyContext, + #[display(fmt = "Failed to parse debug string: {}", _0)] + DebugString(FromUtf8Error), + #[display(fmt = "`gr_error` expects error occurred earlier")] + SyscallErrorExpected, + #[display(fmt = "Terminated: {:?}", _0)] + Terminated(TerminationReason), +} + +impl FuncError +where + E: fmt::Display, +{ + fn as_core(&self) -> Option<&E> { + match self { + Self::Core(err) => Some(err), + _ => None, + } + } + + pub fn into_termination_reason(self) -> TerminationReason { + match self { + Self::Terminated(reason) => reason, + err => TerminationReason::Trap(TrapExplanation::Other(err.to_string().into())), + } + } +} + +impl From for FuncError { + fn from(err: ExtCarrierWithError) -> Self { + Self::LaterExtWith(err) + } +} + +impl From for FuncError { + fn from(err: MemoryError) -> Self { + Self::Memory(err) + } +} + +pub struct FuncsHandler { + _phantom: PhantomData, +} + +impl FuncsHandler +where + E: Ext + IntoExtInfo + 'static, + E::Error: AsTerminationReason + IntoExtError, +{ + pub fn send(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let dest: ProgramId = funcs::get_bytes32(memory, program_id_ptr)?.into(); + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .send(HandlePacket::new(dest, payload, value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn send_wgas(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let gas_limit = pop_i64(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let dest: ProgramId = funcs::get_bytes32(memory, program_id_ptr)?.into(); + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + + let error_len = ext + .send(HandlePacket::new_with_gas(dest, payload, gas_limit, value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn send_commit(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let handle_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let dest: ProgramId = funcs::get_bytes32(memory, program_id_ptr)?.into(); + let value = funcs::get_u128(memory, value_ptr)?; + + let error_len = ext + .send_commit( + handle_ptr, + HandlePacket::new(dest, Default::default(), value), + ) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn send_commit_wgas( + ctx: &mut Runtime, + args: &[RuntimeValue], + ) -> SyscallOutput { + let mut args = args.iter(); + + let handle_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let gas_limit = pop_i64(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let dest: ProgramId = funcs::get_bytes32(memory, program_id_ptr)?.into(); + let value = funcs::get_u128(memory, value_ptr)?; + + let error_len = ext + .send_commit( + handle_ptr, + HandlePacket::new_with_gas(dest, Default::default(), gas_limit, value), + ) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn send_init(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let handle_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let error_len = ext + .send_init() + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|handle| wto(memory, handle_ptr, &handle.to_le_bytes()))?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn send_push(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let handle_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let error_len = ext + .send_push(handle_ptr, &payload) + .process_error() + .map_err(FuncError::Core)? + .error_len(); + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn read(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let at = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let len: usize = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let dest = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let msg = ext.msg().to_vec(); + wto(memory, dest, &msg[at..(at + len)]) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn size(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + ctx.ext + .with(|ext| ext.msg().len()) + .map(return_i32) + .unwrap_or_else(|_| return_i32(0)) + .map_err(|_| FuncError::HostError) + } + + pub fn exit(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let value_dest_ptr = pop_i32(&mut args.iter()).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + let res = ext.with_fallible(|ext| -> Result<(), _> { + let value_dest: ProgramId = funcs::get_bytes32(memory, value_dest_ptr)?.into(); + ext.exit().map_err(FuncError::Core)?; + Err(FuncError::Terminated(TerminationReason::Exit(value_dest))) + }); + if let Err(err) = res { + ctx.err = err; + } + + Err(FuncError::HostError) + } + + pub fn exit_code(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + let opt_details = ctx + .ext + .with_fallible(|ext| ext.reply_details().map_err(FuncError::Core)) + .map_err(|e| { + ctx.err = e; + FuncError::HostError + })?; + + if let Some(details) = opt_details { + return_i32(details.into_exit_code()).map_err(|_| FuncError::HostError) + } else { + ctx.err = FuncError::NonReplyExitCode; + Err(FuncError::HostError) + } + } + + pub fn gas(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let val = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + ctx.ext + .with_fallible(|ext| ext.gas(val).map_err(FuncError::Core)) + .map(|()| ReturnValue::Unit) + .map_err(|e| { + if let Some(TerminationReason::GasAllowanceExceeded) = e + .as_core() + .and_then(AsTerminationReason::as_termination_reason) + .cloned() + { + ctx.err = FuncError::Terminated(TerminationReason::GasAllowanceExceeded); + } + FuncError::HostError + }) + } + + pub fn alloc(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let pages: u32 = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| ext.alloc(pages.into(), memory).map_err(FuncError::Core)) + .map(|page| { + log::debug!("ALLOC: {} pages at {:?}", pages, page); + RuntimeValue::I32(page.0 as i32).into() + }) + .map_err(|e| { + ctx.err = e; + FuncError::HostError + }) + } + + pub fn free(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let page: u32 = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + if let Err(err) = ctx + .ext + .with_fallible(|ext| ext.free(page.into()).map_err(FuncError::Core)) + { + log::debug!("FREE ERROR: {}", err); + ctx.err = err; + Err(FuncError::HostError) + } else { + log::debug!("FREE: {}", page); + Ok(ReturnValue::Unit) + } + } + + pub fn block_height(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + let block_height = ctx + .ext + .with_fallible(|ext| ext.block_height().map_err(FuncError::Core))?; + + return_i32(block_height).map_err(|_| FuncError::HostError) + } + + pub fn block_timestamp( + ctx: &mut Runtime, + _args: &[RuntimeValue], + ) -> SyscallOutput { + let block_timestamp = ctx + .ext + .with_fallible(|ext| ext.block_timestamp().map_err(FuncError::Core))?; + + return_i64(block_timestamp).map_err(|_| FuncError::HostError) + } + + pub fn origin(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let origin_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let origin = ext.origin().map_err(FuncError::Core)?; + wto(memory, origin_ptr, origin.as_ref()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn reply(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .reply(ReplyPacket::new(payload, value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn reply_wgas(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let gas_limit = pop_i64(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .reply(ReplyPacket::new_with_gas(payload, gas_limit, value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn reply_commit(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .reply_commit(ReplyPacket::new(Default::default(), value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn reply_commit_wgas( + ctx: &mut Runtime, + args: &[RuntimeValue], + ) -> SyscallOutput { + let mut args = args.iter(); + + let gas_limit = pop_i64(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let message_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .reply_commit(ReplyPacket::new_with_gas( + Default::default(), + gas_limit, + value, + )) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|message_id| { + wto(memory, message_id_ptr, message_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn reply_to(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let dest = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let opt_details = ctx + .ext + .with_fallible(|ext| ext.reply_details().map_err(FuncError::Core)) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + })?; + + if let Some(details) = opt_details { + wto(&mut ctx.memory, dest, details.into_reply_to().as_ref()).map_err(|err| { + ctx.err = err; + FuncError::HostError + })?; + + Ok(ReturnValue::Unit) + } else { + ctx.err = FuncError::NoReplyContext; + Err(FuncError::HostError) + } + } + + pub fn reply_push(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let error_len = ext + .reply_push(&payload) + .process_error() + .map_err(FuncError::Core)? + .error_len(); + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn debug(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let str_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let str_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let mut data = vec![0u8; str_len]; + memory.read(str_ptr, &mut data)?; + let s = String::from_utf8(data).map_err(FuncError::DebugString)?; + ext.debug(&s).map_err(FuncError::Core)?; + Ok(()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn gas_available(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + let gas_available = ctx + .ext + .with_fallible(|ext| ext.gas_available().map_err(FuncError::Core)) + .map_err(|_| FuncError::HostError)?; + + Ok(return_i64(gas_available).unwrap_or_else(|_| ReturnValue::Value(i64::MAX.into()))) + } + + pub fn msg_id(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let msg_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let message_id = ext.message_id().map_err(FuncError::Core)?; + wto(memory, msg_id_ptr, message_id.as_ref()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn program_id(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let program_id = ext.program_id().map_err(FuncError::Core)?; + wto(memory, program_id_ptr, program_id.as_ref()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn source(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let source_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let source = ext.source().map_err(FuncError::Core)?; + wto(memory, source_ptr, source.as_ref()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn value(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let value = ext.value().map_err(FuncError::Core)?; + funcs::set_u128(memory, value_ptr, value).map_err(FuncError::SetU128) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn value_available(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let value_available = ext.value_available().map_err(FuncError::Core)?; + funcs::set_u128(memory, value_ptr, value_available).map_err(FuncError::SetU128) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn leave(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + ctx.err = ctx + .ext + .with_fallible(|ext| ext.leave().map_err(FuncError::Core)) + .err() + .unwrap_or(FuncError::Terminated(TerminationReason::Leave)); + Err(FuncError::HostError) + } + + pub fn wait(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + ctx.err = ctx + .ext + .with_fallible(|ext| ext.wait().map_err(FuncError::Core)) + .err() + .unwrap_or(FuncError::Terminated(TerminationReason::Wait)); + Err(FuncError::HostError) + } + + pub fn wake(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let waker_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let waker_id: MessageId = funcs::get_bytes32(memory, waker_id_ptr)?.into(); + ext.wake(waker_id).map_err(FuncError::Core) + }) + .map(|_| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn create_program(ctx: &mut Runtime, args: &[RuntimeValue]) -> SyscallOutput { + let mut args = args.iter(); + + let code_hash_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let salt_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let salt_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext: &mut E| { + let code_hash = funcs::get_bytes32(memory, code_hash_ptr)?; + let salt = funcs::get_vec(memory, salt_ptr, salt_len)?; + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .create_program(InitPacket::new(code_hash.into(), salt, payload, value)) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + wto(memory, program_id_ptr, new_actor_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn create_program_wgas( + ctx: &mut Runtime, + args: &[RuntimeValue], + ) -> SyscallOutput { + let mut args = args.iter(); + + let code_hash_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let salt_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let salt_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let payload_len = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let gas_limit = pop_i64(&mut args).map_err(|_| FuncError::HostError)?; + let value_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + let program_id_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let code_hash = funcs::get_bytes32(memory, code_hash_ptr)?; + let salt = funcs::get_vec(memory, salt_ptr, salt_len)?; + let payload = funcs::get_vec(memory, payload_ptr, payload_len)?; + let value = funcs::get_u128(memory, value_ptr)?; + let error_len = ext + .create_program(InitPacket::new_with_gas( + code_hash.into(), + salt, + payload, + gas_limit, + value, + )) + .process_error() + .map_err(FuncError::Core)? + .error_len_on_success(|new_actor_id| { + wto(memory, program_id_ptr, new_actor_id.as_ref()) + })?; + Ok(error_len) + }) + .map(|code| RuntimeValue::I32(code as i32).into()) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn error( + ctx: &mut Runtime, + args: &[RuntimeValue], + ) -> Result> { + let mut args = args.iter(); + + let data_ptr = pop_i32(&mut args).map_err(|_| FuncError::HostError)?; + + let Runtime { ext, memory, .. } = ctx; + + ext.with_fallible(|ext| { + let err = ext.last_error().ok_or(FuncError::SyscallErrorExpected)?; + let err = err.encode(); + wto(memory, data_ptr, &err)?; + Ok(()) + }) + .map(|()| ReturnValue::Unit) + .map_err(|err| { + ctx.err = err; + FuncError::HostError + }) + } + + pub fn forbidden(ctx: &mut Runtime, _args: &[RuntimeValue]) -> SyscallOutput { + ctx.err = + FuncError::Terminated(TerminationReason::Trap(TrapExplanation::ForbiddenFunction)); + Err(FuncError::HostError) + } +} diff --git a/core-backend/wasmtime/src/lib.rs b/core-backend/wasmi/src/lib.rs similarity index 80% rename from core-backend/wasmtime/src/lib.rs rename to core-backend/wasmi/src/lib.rs index f60ebfdb91b..e1d5af34c1c 100644 --- a/core-backend/wasmtime/src/lib.rs +++ b/core-backend/wasmi/src/lib.rs @@ -1,6 +1,6 @@ // This file is part of Gear. -// Copyright (C) 2021-2022 Gear Technologies Inc. +// Copyright (C) 2022 Gear Technologies Inc. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify @@ -16,15 +16,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -//! Provide wasmtime-runtime support. +//! Provide wasmi support. -#![no_std] +#![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; pub mod env; -mod funcs; -pub mod funcs_tree; +pub mod funcs; pub mod memory; -pub use env::WasmtimeEnvironment; +pub use env::WasmiEnvironment; +pub use memory::MemoryWrap; diff --git a/core-backend/wasmi/src/memory.rs b/core-backend/wasmi/src/memory.rs new file mode 100644 index 00000000000..144bc26fa43 --- /dev/null +++ b/core-backend/wasmi/src/memory.rs @@ -0,0 +1,135 @@ +// This file is part of Gear. + +// Copyright (C) 2022 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 . + +//! wasmi extensions for memory. + +use gear_core::memory::{Error, HostPointer, Memory, PageNumber, WasmPageNumber}; +use wasmi::{memory_units::Pages, MemoryRef}; + +/// Wrapper for [`wasmi::MemoryRef`]. +pub struct MemoryWrap(MemoryRef); + +impl MemoryWrap { + /// Wrap [`wasmi::MemoryRef`] for Memory trait. + pub fn new(mem: MemoryRef) -> Self { + MemoryWrap(mem) + } +} + +/// Memory interface for the allocator. +impl Memory for MemoryWrap { + fn grow(&mut self, pages: WasmPageNumber) -> Result { + self.0 + .grow(Pages(pages.0 as usize)) + .map(|prev| (prev.0 as u32).into()) + .map_err(|_| Error::OutOfBounds) + } + + fn size(&self) -> WasmPageNumber { + (self.0.current_size().0 as u32).into() + } + + fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), Error> { + self.0 + .set(offset as u32, buffer) + .map_err(|_| Error::MemoryAccessError) + } + + fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), Error> { + self.0 + .get_into(offset as u32, buffer) + .map_err(|_| Error::MemoryAccessError) + } + + fn data_size(&self) -> usize { + self.0.current_size().0 * WasmPageNumber::size() + } + + unsafe fn get_buffer_host_addr_unsafe(&self) -> HostPointer { + self.0.direct_access_mut().as_mut().as_mut_ptr() as HostPointer + } +} + +#[cfg(test)] +mod tests { + use super::*; + use gear_core::memory::AllocationsContext; + + fn new_test_memory(static_pages: u32, max_pages: u32) -> (AllocationsContext, MemoryWrap) { + use wasmi::MemoryInstance as WasmMemory; + + let memory = MemoryWrap::new( + WasmMemory::alloc( + Pages(static_pages as usize), + Some(Pages(max_pages as usize)), + ) + .expect("Memory creation failed"), + ); + + ( + AllocationsContext::new(Default::default(), static_pages.into(), max_pages.into()), + memory, + ) + } + + #[test] + fn smoky() { + let (mut mem, mut mem_wrap) = new_test_memory(16, 256); + + assert_eq!( + mem.alloc(16.into(), &mut mem_wrap) + .expect("allocation failed"), + 16.into() + ); + + // there is a space for 14 more + for _ in 0..14 { + mem.alloc(16.into(), &mut mem_wrap) + .expect("allocation failed"); + } + + // no more mem! + assert!(mem.alloc(1.into(), &mut mem_wrap).is_err()); + + // but we free some + mem.free(137.into()).expect("free failed"); + + // and now can allocate page that was freed + assert_eq!( + mem.alloc(1.into(), &mut mem_wrap) + .expect("allocation failed"), + 137.into() + ); + + // if we have 2 in a row we can allocate even 2 + mem.free(117.into()).expect("free failed"); + mem.free(118.into()).expect("free failed"); + + assert_eq!( + mem.alloc(2.into(), &mut mem_wrap) + .expect("allocation failed"), + 117.into() + ); + + // but if 2 are not in a row, bad luck + mem.free(117.into()).expect("free failed"); + mem.free(158.into()).expect("free failed"); + + assert!(mem.alloc(2.into(), &mut mem_wrap).is_err()); + } +} diff --git a/core-backend/wasmtime/src/env.rs b/core-backend/wasmtime/src/env.rs deleted file mode 100644 index 1ddef119534..00000000000 --- a/core-backend/wasmtime/src/env.rs +++ /dev/null @@ -1,299 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 2021-2022 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 . - -//! Wasmtime environment for running a module. - -use core::fmt; - -use crate::{funcs_tree, memory::MemoryWrapExternal}; -use alloc::{ - collections::BTreeSet, - string::{String, ToString}, - vec::Vec, -}; -use gear_backend_common::{ - error_processor::IntoExtError, AsTerminationReason, BackendError, BackendReport, Environment, - ExtInfo, IntoExtInfo, TerminationReason, TrapExplanation, -}; -use gear_core::{ - env::{ClonedExtCarrier, Ext, ExtCarrier}, - gas::GasAmount, - memory::WasmPageNumber, - message::DispatchKind, -}; -use gear_core_errors::MemoryError; -use wasmtime::{Engine, Extern, Instance, Memory as WasmtimeMemory, MemoryType, Module, Store}; - -/// Data type in wasmtime store -pub struct StoreData { - pub ext: ClonedExtCarrier, - pub termination_reason: TerminationReason, -} - -#[derive(Debug, derive_more::Display)] -pub enum WasmtimeEnvironmentError { - #[display(fmt = "Function {:?} is not env", _0)] - NonEnvImport(Option), - #[display(fmt = "Function {:?} definition wasn't found", _0)] - MissingImport(Option), - #[display(fmt = "Unable to create module: {}", _0)] - ModuleCreation(anyhow::Error), - #[display(fmt = "Unable to create instance: {}", _0)] - InstanceCreation(anyhow::Error), - #[display(fmt = "Unable to set module memory data")] - SetModuleMemoryData, - #[display(fmt = "Unable to save static pages initial data")] - SaveStaticPagesInitialData, - #[display(fmt = "Failed to create env memory: {}", _0)] - CreateEnvMemory(anyhow::Error), - #[display(fmt = "{}", _0)] - MemoryAccess(MemoryError), - #[display(fmt = "{}", _0)] - PostExecutionHandler(String), -} - -/// Environment to run one module at a time providing Ext. -pub struct WasmtimeEnvironment { - ext: ExtCarrier, - memory_wrap: MemoryWrapExternal, - instance: Instance, -} - -impl Environment for WasmtimeEnvironment -where - E: Ext + IntoExtInfo, - E::Error: AsTerminationReason + IntoExtError, -{ - type Memory = MemoryWrapExternal; - type Error = WasmtimeEnvironmentError; - - fn new( - ext: E, - binary: &[u8], - _entries: BTreeSet, - mem_size: WasmPageNumber, - ) -> Result> { - let forbidden_funcs = ext.forbidden_funcs().clone(); - let ext_carrier = ExtCarrier::new(ext); - - let engine = Engine::default(); - let store_data = StoreData { - ext: ext_carrier.cloned(), - termination_reason: TerminationReason::Success, - }; - let mut store = Store::>::new(&engine, store_data); - - // Creates new wasm memory - let memory = match WasmtimeMemory::new(&mut store, MemoryType::new(mem_size.0, None)) { - Ok(mem) => mem, - Err(e) => { - return Err(BackendError { - reason: WasmtimeEnvironmentError::CreateEnvMemory(e), - gas_amount: ext_carrier.into_inner().into_gas_amount(), - }) - } - }; - - let funcs = funcs_tree::build(&mut store, memory, Some(forbidden_funcs)); - let module = match Module::new(&engine, binary) { - Ok(module) => module, - Err(e) => { - return Err(BackendError { - reason: WasmtimeEnvironmentError::ModuleCreation(e), - gas_amount: ext_carrier.into_inner().into_gas_amount(), - }) - } - }; - - let mut imports = Vec::with_capacity(module.imports().len()); - for import in module.imports() { - if import.module() != "env" { - return Err(BackendError { - reason: WasmtimeEnvironmentError::NonEnvImport(import.name().map(Into::into)), - gas_amount: ext_carrier.into_inner().into_gas_amount(), - }); - } - imports.push((import.name(), Option::::None)); - } - - for (import_name, ref mut ext) in imports.iter_mut() { - if let Some(name) = import_name { - *ext = match *name { - "memory" => Some(Extern::Memory(memory)), - key if funcs.contains_key(key) => Some(funcs[key].into()), - _ => continue, - } - } - } - - let mut externs = Vec::with_capacity(imports.len()); - for (name, host_function) in imports { - if let Some(host_function) = host_function { - externs.push(host_function); - } else { - return Err(BackendError { - reason: WasmtimeEnvironmentError::MissingImport(name.map(Into::into)), - gas_amount: ext_carrier.into_inner().into_gas_amount(), - }); - } - } - - let instance = match Instance::new(&mut store, &module, &externs) { - Ok(instance) => instance, - Err(e) => { - return Err(BackendError { - reason: WasmtimeEnvironmentError::InstanceCreation(e), - gas_amount: ext_carrier.into_inner().into_gas_amount(), - }) - } - }; - - let memory_wrap = MemoryWrapExternal { mem: memory, store }; - - Ok(WasmtimeEnvironment { - ext: ext_carrier, - memory_wrap, - instance, - }) - } - - fn get_stack_mem_end(&mut self) -> Option { - // `__gear_stack_end` export is inserted in wasm-proc or wasm-builder - let global = self - .instance - .get_global(&mut self.memory_wrap.store, "__gear_stack_end")?; - global - .get(&mut self.memory_wrap.store) - .i32() - .and_then(|addr| { - if addr < 0 { - None - } else { - Some(WasmPageNumber( - (addr as usize / WasmPageNumber::size()) as u32, - )) - } - }) - } - - fn get_mem(&self) -> &Self::Memory { - &self.memory_wrap - } - - fn get_mem_mut(&mut self) -> &mut Self::Memory { - &mut self.memory_wrap - } - - fn execute( - mut self, - entry_point: &DispatchKind, - post_execution_handler: F, - ) -> Result> - where - F: FnOnce(&Self::Memory) -> Result<(), T>, - T: fmt::Display, - { - struct PreparedInfo { - info: ExtInfo, - trap_explanation: Option, - memory_wrap: MemoryWrapExternal, - } - - let func = self - .instance - .get_func(&mut self.memory_wrap.store, entry_point.into_entry()); - - let prepare_info = |this: Self| -> Result, BackendError> { - let WasmtimeEnvironment { - ext, memory_wrap, .. - } = this; - ext.into_inner() - .into_ext_info(&memory_wrap) - .map_err(|(reason, gas_amount)| BackendError { - reason: WasmtimeEnvironmentError::MemoryAccess(reason), - gas_amount, - }) - .map(|(info, trap_explanation)| PreparedInfo { - info, - trap_explanation, - memory_wrap, - }) - }; - - let entry_func = if let Some(f) = func { - // Entry function found - f - } else { - let PreparedInfo { - info, - trap_explanation: _, - memory_wrap, - } = prepare_info(self)?; - - // Entry function not found, so we mean this as empty function - return match post_execution_handler(&memory_wrap) { - Ok(_) => Ok(BackendReport { - termination: TerminationReason::Success, - info, - }), - Err(e) => Err(BackendError { - reason: WasmtimeEnvironmentError::PostExecutionHandler(e.to_string()), - gas_amount: info.gas_amount, - }), - }; - }; - - let res = entry_func.call(&mut self.memory_wrap.store, &[], &mut []); - log::debug!("execution result: {:?}", res); - - let termination_reason = self.memory_wrap.store.data().termination_reason.clone(); - - let PreparedInfo { - info, - trap_explanation, - memory_wrap, - } = prepare_info(self)?; - - let termination = if res.is_err() { - let reason = trap_explanation - .map(TerminationReason::Trap) - .unwrap_or(termination_reason); - - // success is unacceptable when there is error - if let TerminationReason::Success = reason { - TerminationReason::Trap(TrapExplanation::Unknown) - } else { - reason - } - } else { - TerminationReason::Success - }; - - match post_execution_handler(&memory_wrap) { - Ok(_) => Ok(BackendReport { termination, info }), - Err(e) => Err(BackendError { - reason: WasmtimeEnvironmentError::PostExecutionHandler(e.to_string()), - gas_amount: info.gas_amount, - }), - } - } - - fn into_gas_amount(self) -> GasAmount { - self.ext.into_inner().into_gas_amount() - } -} diff --git a/core-backend/wasmtime/src/funcs.rs b/core-backend/wasmtime/src/funcs.rs deleted file mode 100644 index 92a7a2ba63a..00000000000 --- a/core-backend/wasmtime/src/funcs.rs +++ /dev/null @@ -1,842 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 2022 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 core::marker::PhantomData; - -use crate::{env::StoreData, memory::MemoryWrap}; -use alloc::{ - string::{FromUtf8Error, String, ToString}, - vec, -}; -use codec::Encode; -use gear_backend_common::{ - error_processor::{IntoExtError, ProcessError}, - funcs::*, - AsTerminationReason, IntoExtInfo, TerminationReason, TrapExplanation, -}; -use gear_core::{ - env::{Ext, ExtCarrierWithError}, - ids::{MessageId, ProgramId}, - memory::Memory, - message::{HandlePacket, InitPacket, ReplyPacket}, -}; -use gear_core_errors::{CoreError, MemoryError}; -use wasmtime::{AsContextMut, Caller, Func, Memory as WasmtimeMemory, Store, Trap}; - -pub struct FuncsHandler(PhantomData); - -#[derive(Debug, derive_more::Display)] -enum FuncError { - #[display(fmt = "{}", _0)] - Core(E), - #[display(fmt = "{}", _0)] - Memory(MemoryError), - #[display(fmt = "{}", _0)] - SetU128(MemoryError), - #[display(fmt = "{}", _0)] - LaterExtWith(ExtCarrierWithError), - #[display(fmt = "Failed to parse debug string: {}", _0)] - DebugString(FromUtf8Error), - #[display(fmt = "Not running in the reply context")] - NoReplyContext, - #[display(fmt = "`gr_exit` has been called")] - Exit, - #[display(fmt = "`gr_leave` has been called")] - Leave, - #[display(fmt = "`gr_wait` has been called")] - Wait, - #[display(fmt = "`gr_error` expects error occurred earlier")] - SyscallErrorExpected, - #[display(fmt = "Unable to call a forbidden function")] - ForbiddenFunction, -} - -impl FuncError { - fn as_core(&self) -> Option<&E> { - match self { - Self::Core(err) => Some(err), - _ => None, - } - } -} - -impl From for FuncError { - fn from(err: ExtCarrierWithError) -> Self { - Self::LaterExtWith(err) - } -} - -impl From for FuncError { - fn from(err: MemoryError) -> Self { - Self::Memory(err) - } -} - -// for Trap::new -impl From> for String -where - E: CoreError, -{ - fn from(err: FuncError) -> Self { - err.to_string() - } -} - -fn get_caller_memory<'a, T: Ext>( - caller: &'a mut Caller<'_, StoreData>, - mem: &WasmtimeMemory, -) -> MemoryWrap<'a, T> { - let store = caller.as_context_mut(); - MemoryWrap { mem: *mem, store } -} - -fn write_to_caller_memory<'a, T: Ext>( - caller: &'a mut Caller<'_, StoreData>, - mem: &WasmtimeMemory, - offset: usize, - buffer: &[u8], -) -> Result<(), FuncError> { - get_caller_memory(caller, mem) - .write(offset, buffer) - .map_err(FuncError::Memory) -} - -impl FuncsHandler -where - E: Ext + IntoExtInfo + 'static, - E::Error: AsTerminationReason + IntoExtError, -{ - pub fn alloc(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, pages: i32| { - let ext = caller.data().ext.clone(); - let pages = pages as u32; - let page = ext - .with_fallible(|ext| { - ext.alloc(pages.into(), &mut get_caller_memory(&mut caller, &mem)) - .map_err(FuncError::Core) - }) - .map_err(Trap::new)?; - log::debug!("ALLOC PAGES: {} pages at {:?}", pages, page); - Ok(page.0) - }; - Func::wrap(store, func) - } - - pub fn block_height(store: &mut Store>) -> Func { - let f = move |caller: Caller<'_, StoreData>| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.block_height().map_err(FuncError::Core)) - .unwrap_or(0) as i32 - }; - Func::wrap(store, f) - } - - pub fn block_timestamp(store: &mut Store>) -> Func { - let f = move |caller: Caller<'_, StoreData>| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.block_timestamp().map_err(FuncError::Core)) - .unwrap_or(0) as i64 - }; - Func::wrap(store, f) - } - - pub fn exit_code(store: &mut Store>) -> Func { - let f = move |caller: Caller<'_, StoreData>| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.reply_details().map_err(FuncError::Core)) - .and_then(|v| v.ok_or(FuncError::NoReplyContext)) - .map(|details| details.into_exit_code()) - .map_err(Trap::new) - }; - Func::wrap(store, f) - } - - pub fn free(store: &mut Store>) -> Func { - let func = move |caller: Caller<'_, StoreData>, page: i32| { - let ext = &caller.data().ext; - let page = page as u32; - if let Err(err) = - ext.with_fallible(|ext| ext.free(page.into()).map_err(FuncError::Core)) - { - log::debug!("FREE PAGE ERROR: {}", err); - Err(Trap::new(err)) - } else { - log::debug!("FREE PAGE: {}", page); - Ok(()) - } - }; - Func::wrap(store, func) - } - - pub fn debug(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let f = move |mut caller: Caller<'_, StoreData>, str_ptr: i32, str_len: i32| { - let ext = caller.data().ext.clone(); - let str_ptr = str_ptr as u32 as usize; - let str_len = str_len as u32 as usize; - ext.with_fallible(|ext| -> Result<(), FuncError<_>> { - let mut data = vec![0u8; str_len]; - let mem = get_caller_memory(&mut caller, &mem); - mem.read(str_ptr, &mut data)?; - let s = String::from_utf8(data).map_err(FuncError::DebugString)?; - ext.debug(&s).map_err(FuncError::Core)?; - Ok(()) - }) - .map_err(Trap::new) - }; - Func::wrap(store, f) - } - - pub fn gas(store: &mut Store>) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, val: i32| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.gas(val as _).map_err(FuncError::Core)) - .map_err(|e| { - if let Some(TerminationReason::GasAllowanceExceeded) = e - .as_core() - .and_then(AsTerminationReason::as_termination_reason) - { - caller.data_mut().termination_reason = - TerminationReason::GasAllowanceExceeded; - } - - Trap::new(e) - }) - }; - Func::wrap(store, func) - } - - pub fn gas_available(store: &mut Store>) -> Func { - let func = move |caller: Caller<'_, StoreData>| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.gas_available().map_err(FuncError::Core)) - .unwrap_or(0) as i64 - }; - Func::wrap(store, func) - } - - pub fn exit(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = - move |mut caller: Caller<'_, StoreData>, program_id_ptr: i32| -> Result<(), Trap> { - let ext = caller.data().ext.clone(); - - let program_id = ext - .with_fallible(|ext| -> Result<_, FuncError<_>> { - let value_dest: ProgramId = get_bytes32( - &get_caller_memory(&mut caller, &mem), - program_id_ptr as u32 as _, - )? - .into(); - ext.exit().map_err(FuncError::Core)?; - Ok(value_dest) - }) - .map_err(Trap::new)?; - - caller.data_mut().termination_reason = TerminationReason::Exit(program_id); - Err(Trap::new(FuncError::::Exit)) - }; - Func::wrap(store, func) - } - - pub fn origin(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, origin_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let id = ext.origin().map_err(FuncError::Core)?; - write_to_caller_memory(&mut caller, &mem, origin_ptr as _, id.as_ref()) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn msg_id(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, msg_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let message_id = ext.message_id().map_err(FuncError::Core)?; - write_to_caller_memory( - &mut caller, - &mem, - msg_id_ptr as isize as _, - message_id.as_ref(), - ) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn read(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, at: i32, len: i32, dest: i32| { - let ext = caller.data().ext.clone(); - let at = at as u32 as usize; - let len = len as u32 as usize; - ext.with_fallible(|ext| -> Result<_, FuncError> { - let msg = ext.msg().to_vec(); - write_to_caller_memory(&mut caller, &mem, dest as _, &msg[at..(at + len)]) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - payload_ptr: i32, - payload_len: i32, - value_ptr: i32, - message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - - let error_len = ext - .reply(ReplyPacket::new(payload, value)) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply_wgas(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - payload_ptr: i32, - payload_len: i32, - gas_limit: i64, - value_ptr: i32, - message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - - let error_len = ext - .reply(ReplyPacket::new_with_gas(payload, gas_limit as _, value)) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply_commit(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = - move |mut caller: Caller<'_, StoreData>, value_ptr: i32, message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .reply_commit(ReplyPacket::new(Default::default(), value)) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply_commit_wgas(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - gas_limit: i64, - value_ptr: i32, - message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .reply_commit(ReplyPacket::new_with_gas( - Default::default(), - gas_limit as _, - value, - )) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply_push(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = - move |mut caller: Caller<'_, StoreData>, payload_ptr: i32, payload_len: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let error_len = ext - .reply_push(&payload) - .process_error() - .map_err(FuncError::Core)? - .error_len(); - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn reply_to(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, dest: i32| { - let ext = &caller.data().ext; - ext.with_fallible(|ext| ext.reply_details().map_err(FuncError::Core)) - .and_then(|v| v.ok_or(FuncError::NoReplyContext)) - .and_then(|details| { - write_to_caller_memory( - &mut caller, - &mem, - dest as isize as _, - details.into_reply_to().as_ref(), - ) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - program_id_ptr: i32, - payload_ptr: i32, - payload_len: i32, - value_ptr: i32, - message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let dest: ProgramId = get_bytes32(&mem_wrap, program_id_ptr as usize)?.into(); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .send(HandlePacket::new(dest, payload, value)) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send_wgas(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - program_id_ptr: i32, - payload_ptr: i32, - payload_len: i32, - gas_limit: i64, - value_ptr: i32, - message_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let dest: ProgramId = get_bytes32(&mem_wrap, program_id_ptr as usize)?.into(); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - - let error_len = ext - .send(HandlePacket::new_with_gas( - dest, - payload, - gas_limit as _, - value, - )) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send_commit(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - handle_ptr: i32, - message_id_ptr: i32, - program_id_ptr: i32, - value_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let dest: ProgramId = get_bytes32(&mem_wrap, program_id_ptr as usize)?.into(); - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .send_commit( - handle_ptr as _, - HandlePacket::new(dest, Default::default(), value), - ) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send_commit_wgas(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - handle_ptr: i32, - message_id_ptr: i32, - program_id_ptr: i32, - gas_limit: i64, - value_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let dest: ProgramId = get_bytes32(&mem_wrap, program_id_ptr as usize)?.into(); - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .send_commit( - handle_ptr as _, - HandlePacket::new_with_gas(dest, Default::default(), gas_limit as _, value), - ) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|message_id| { - write_to_caller_memory( - &mut caller, - &mem, - message_id_ptr as isize as _, - message_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send_init(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, handle_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let error_len = ext - .send_init() - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|handle| { - write_to_caller_memory( - &mut caller, - &mem, - handle_ptr as _, - &handle.to_le_bytes(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn send_push(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - handle_ptr: i32, - payload_ptr: i32, - payload_len: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let error_len = ext - .send_push(handle_ptr as _, &payload) - .process_error() - .map_err(FuncError::Core)? - .error_len(); - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn create_program(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - code_hash_ptr: i32, - salt_ptr: i32, - salt_len: i32, - payload_ptr: i32, - payload_len: i32, - value_ptr: i32, - program_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext: &mut E| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let code_hash = get_bytes32(&mem_wrap, code_hash_ptr as usize)?; - let salt = get_vec(&mem_wrap, salt_ptr as usize, salt_len as usize)?; - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .create_program(InitPacket::new(code_hash.into(), salt, payload, value)) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|new_actor_id| { - write_to_caller_memory( - &mut caller, - &mem, - program_id_ptr as isize as _, - new_actor_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn create_program_wgas(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, - code_hash_ptr: i32, - salt_ptr: i32, - salt_len: i32, - payload_ptr: i32, - payload_len: i32, - gas_limit: i64, - value_ptr: i32, - program_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let code_hash = get_bytes32(&mem_wrap, code_hash_ptr as usize)?; - let salt = get_vec(&mem_wrap, salt_ptr as usize, salt_len as usize)?; - let payload = get_vec(&mem_wrap, payload_ptr as usize, payload_len as usize)?; - let value = get_u128(&mem_wrap, value_ptr as usize)?; - let error_len = ext - .create_program(InitPacket::new_with_gas( - code_hash.into(), - salt, - payload, - gas_limit as _, - value, - )) - .process_error() - .map_err(FuncError::Core)? - .error_len_on_success(|new_actor_id| { - write_to_caller_memory( - &mut caller, - &mem, - program_id_ptr as isize as _, - new_actor_id.as_ref(), - ) - })?; - Ok(error_len) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn size(store: &mut Store>) -> Func { - let func = move |caller: Caller<'_, StoreData>| { - let ext = &caller.data().ext; - ext.with(|ext| ext.msg().len() as _).unwrap_or(0) - }; - Func::wrap(store, func) - } - - pub fn source(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, source_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let source = ext.source().map_err(FuncError::Core)?; - write_to_caller_memory(&mut caller, &mem, source_ptr as _, source.as_ref()) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn program_id(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, source_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let actor_id = ext.program_id().map_err(FuncError::Core)?; - write_to_caller_memory(&mut caller, &mem, source_ptr as _, actor_id.as_ref()) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn value(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, value_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<(), FuncError> { - let mut mem_wrap = get_caller_memory(&mut caller, &mem); - let value = ext.value().map_err(FuncError::Core)?; - set_u128(&mut mem_wrap, value_ptr as usize, value).map_err(FuncError::SetU128) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn value_available(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, value_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<(), FuncError> { - let mut mem_wrap = get_caller_memory(&mut caller, &mem); - let value_available = ext.value_available().map_err(FuncError::Core)?; - set_u128(&mut mem_wrap, value_ptr as usize, value_available) - .map_err(FuncError::SetU128) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn leave(store: &mut Store>) -> Func { - let func = move |mut caller: Caller<'_, StoreData>| -> Result<(), Trap> { - let ext = &caller.data().ext; - let trap = - if let Err(err) = ext.with_fallible(|ext| ext.leave().map_err(FuncError::Core)) { - Trap::new(err) - } else { - caller.data_mut().termination_reason = TerminationReason::Leave; - Trap::new(FuncError::::Leave) - }; - // Intentionally return an error to break the execution - Err(trap) - }; - Func::wrap(store, func) - } - - pub fn wait(store: &mut Store>) -> Func { - let func = move |mut caller: Caller<'_, StoreData>| -> Result<(), Trap> { - let ext = &caller.data().ext; - let trap = - if let Err(err) = ext.with_fallible(|ext| ext.wait().map_err(FuncError::Core)) { - Trap::new(err) - } else { - caller.data_mut().termination_reason = TerminationReason::Wait; - Trap::new(FuncError::::Wait) - }; - // Intentionally return an error to break the execution - Err(trap) - }; - Func::wrap(store, func) - } - - pub fn wake(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, waker_id_ptr: i32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<_, FuncError> { - let mem_wrap = get_caller_memory(&mut caller, &mem); - let waker_id: MessageId = get_bytes32(&mem_wrap, waker_id_ptr as usize)?.into(); - ext.wake(waker_id).map_err(FuncError::Core) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn error(store: &mut Store>, mem: WasmtimeMemory) -> Func { - let func = move |mut caller: Caller<'_, StoreData>, data_ptr: u32| { - let ext = caller.data().ext.clone(); - ext.with_fallible(|ext| -> Result<(), FuncError> { - let mut mem_wrap = get_caller_memory(&mut caller, &mem); - let err = ext.last_error().ok_or(FuncError::SyscallErrorExpected)?; - let err = err.encode(); - mem_wrap.write(data_ptr as usize, &err)?; - Ok(()) - }) - .map_err(Trap::new) - }; - Func::wrap(store, func) - } - - pub fn forbidden(store: &mut Store>) -> Func { - let func = move |mut caller: Caller<'_, StoreData>| -> Result<(), Trap> { - caller.data_mut().termination_reason = - TerminationReason::Trap(TrapExplanation::ForbiddenFunction); - Err(Trap::new(FuncError::::ForbiddenFunction)) - }; - Func::wrap(store, func) - } -} diff --git a/core-backend/wasmtime/src/funcs_tree.rs b/core-backend/wasmtime/src/funcs_tree.rs deleted file mode 100644 index 48ba805c516..00000000000 --- a/core-backend/wasmtime/src/funcs_tree.rs +++ /dev/null @@ -1,77 +0,0 @@ -use crate::{env::StoreData, funcs::FuncsHandler}; -use alloc::collections::{BTreeMap, BTreeSet}; -use gear_backend_common::{error_processor::IntoExtError, AsTerminationReason, IntoExtInfo}; -use gear_core::env::Ext; -use wasmtime::{Func, Memory, Store}; - -pub fn build<'a, E>( - store: &'a mut Store>, - memory: Memory, - forbidden_funcs: Option>, -) -> BTreeMap<&'a str, Func> -where - E: Ext + IntoExtInfo + 'static, - E::Error: AsTerminationReason + IntoExtError, -{ - let mut funcs: BTreeMap<&str, Func> = [ - ("alloc", FuncsHandler::alloc(store, memory)), - ("free", FuncsHandler::free(store)), - ("gas", FuncsHandler::gas(store)), - ("gr_block_height", FuncsHandler::block_height(store)), - ("gr_block_timestamp", FuncsHandler::block_timestamp(store)), - ( - "gr_create_program", - FuncsHandler::create_program(store, memory), - ), - ( - "gr_create_program_wgas", - FuncsHandler::create_program_wgas(store, memory), - ), - ("gr_exit_code", FuncsHandler::exit_code(store)), - ("gr_gas_available", FuncsHandler::gas_available(store)), - ("gr_debug", FuncsHandler::debug(store, memory)), - ("gr_exit", FuncsHandler::exit(store, memory)), - ("gr_origin", FuncsHandler::origin(store, memory)), - ("gr_msg_id", FuncsHandler::msg_id(store, memory)), - ("gr_program_id", FuncsHandler::program_id(store, memory)), - ("gr_read", FuncsHandler::read(store, memory)), - ("gr_reply", FuncsHandler::reply(store, memory)), - ("gr_reply_wgas", FuncsHandler::reply_wgas(store, memory)), - ("gr_reply_commit", FuncsHandler::reply_commit(store, memory)), - ( - "gr_reply_commit_wgas", - FuncsHandler::reply_commit_wgas(store, memory), - ), - ("gr_reply_push", FuncsHandler::reply_push(store, memory)), - ("gr_reply_to", FuncsHandler::reply_to(store, memory)), - ("gr_send_wgas", FuncsHandler::send_wgas(store, memory)), - ("gr_send", FuncsHandler::send(store, memory)), - ( - "gr_send_commit_wgas", - FuncsHandler::send_commit_wgas(store, memory), - ), - ("gr_send_commit", FuncsHandler::send_commit(store, memory)), - ("gr_send_init", FuncsHandler::send_init(store, memory)), - ("gr_send_push", FuncsHandler::send_push(store, memory)), - ("gr_size", FuncsHandler::size(store)), - ("gr_source", FuncsHandler::source(store, memory)), - ("gr_value", FuncsHandler::value(store, memory)), - ( - "gr_value_available", - FuncsHandler::value_available(store, memory), - ), - ("gr_leave", FuncsHandler::leave(store)), - ("gr_wait", FuncsHandler::wait(store)), - ("gr_wake", FuncsHandler::wake(store, memory)), - ("gr_error", FuncsHandler::error(store, memory)), - ] - .into(); - - if let Some(forbidden_funcs) = forbidden_funcs { - forbidden_funcs.iter().for_each(|func_name| { - funcs.insert(*func_name, FuncsHandler::forbidden(store)); - }); - } - - funcs -} diff --git a/core-backend/wasmtime/src/memory.rs b/core-backend/wasmtime/src/memory.rs deleted file mode 100644 index cd51423dc74..00000000000 --- a/core-backend/wasmtime/src/memory.rs +++ /dev/null @@ -1,104 +0,0 @@ -// This file is part of Gear. - -// Copyright (C) 2021-2022 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 . - -//! Wasmtime extensions for memory. - -use crate::env::StoreData; -use gear_core::{ - env::Ext, - memory::{Error, HostPointer, Memory, PageNumber, WasmPageNumber}, -}; -use wasmtime::{Store, StoreContextMut}; - -/// Wrapper for wasmtime memory. -pub struct MemoryWrap<'a, E: Ext> { - pub mem: wasmtime::Memory, - pub store: StoreContextMut<'a, StoreData>, -} - -/// Memory interface for the allocator. -impl<'a, E: Ext> Memory for MemoryWrap<'a, E> { - fn grow(&mut self, pages: WasmPageNumber) -> Result { - self.mem - .grow(&mut self.store, pages.0 as u64) - .map(|offset| (offset as u32).into()) - .map_err(|_| Error::OutOfBounds) - } - - fn size(&self) -> WasmPageNumber { - (self.mem.size(&self.store) as u32).into() - } - - fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), Error> { - self.mem - .write(&mut self.store, offset, buffer) - .map_err(|_| Error::MemoryAccessError) - } - - fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), Error> { - self.mem - .read(&self.store, offset, buffer) - .map_err(|_| Error::MemoryAccessError) - } - - fn data_size(&self) -> usize { - self.mem.data_size(&self.store) - } - - unsafe fn get_buffer_host_addr_unsafe(&self) -> HostPointer { - self.mem.data_ptr(&self.store) as HostPointer - } -} - -pub struct MemoryWrapExternal { - pub mem: wasmtime::Memory, - pub store: Store>, -} - -impl Memory for MemoryWrapExternal { - fn grow(&mut self, pages: WasmPageNumber) -> Result { - self.mem - .grow(&mut self.store, pages.0 as u64) - .map(|offset| (offset as u32).into()) - .map_err(|_| Error::OutOfBounds) - } - - fn size(&self) -> WasmPageNumber { - (self.mem.size(&self.store) as u32).into() - } - - fn write(&mut self, offset: usize, buffer: &[u8]) -> Result<(), Error> { - self.mem - .write(&mut self.store, offset, buffer) - .map_err(|_| Error::MemoryAccessError) - } - - fn read(&self, offset: usize, buffer: &mut [u8]) -> Result<(), Error> { - self.mem - .read(&self.store, offset, buffer) - .map_err(|_| Error::MemoryAccessError) - } - - fn data_size(&self) -> usize { - self.mem.data_size(&self.store) - } - - unsafe fn get_buffer_host_addr_unsafe(&self) -> HostPointer { - self.mem.data_ptr(&self.store) as HostPointer - } -} diff --git a/gear-test/Cargo.toml b/gear-test/Cargo.toml index 7fcadbef9cd..f156422372c 100644 --- a/gear-test/Cargo.toml +++ b/gear-test/Cargo.toml @@ -27,7 +27,7 @@ gear-core = { path = "../core" } common = { package = "gear-common", path = "../common" } core-processor = { package = "gear-core-processor", path = "../core-processor", default-features = false } gear-backend-common = { path = "../core-backend/common" } -gear-backend-wasmtime = { path = "../core-backend/wasmtime" } +gear-backend-wasmi = { path = "../core-backend/wasmi" } [dev-dependencies] parity-scale-codec = { version = "3.1.2", features = ["derive"], default-features = false } diff --git a/gear-test/src/main.rs b/gear-test/src/main.rs index 45a9ba85477..2935605b9da 100644 --- a/gear-test/src/main.rs +++ b/gear-test/src/main.rs @@ -25,7 +25,7 @@ mod sample; use clap::Parser; use core_processor::Ext; -use gear_backend_wasmtime::WasmtimeEnvironment; +use gear_backend_wasmi::WasmiEnvironment; use manager::InMemoryExtManager; #[derive(Parser)] @@ -50,7 +50,7 @@ struct Opts { pub fn main() -> anyhow::Result<()> { let opts: Opts = Opts::parse(); let print_logs = !matches!(opts.verbose, 0); - check::check_main::, _>( + check::check_main::, _>( opts.input.to_vec(), opts.skip_messages, opts.skip_allocations, diff --git a/gtest/Cargo.toml b/gtest/Cargo.toml index bfa111c3555..00f4f6e4819 100644 --- a/gtest/Cargo.toml +++ b/gtest/Cargo.toml @@ -8,7 +8,7 @@ license = "GPL-3.0" [dependencies] gear-core = { path = "../core" } gear-backend-common = { path = "../core-backend/common" } -gear-backend-wasmtime = { path = "../core-backend/wasmtime" } +gear-backend-wasmi = { path = "../core-backend/wasmi" } core-processor = { package = "gear-core-processor", path = "../core-processor" } gear-wasm-builder = { path = "../utils/wasm-builder" } @@ -21,4 +21,4 @@ derive_more = { version = "0.99.17", features = ["add", "add_assign", "display", env_logger = "0.9.0" path-clean = "0.1.0" wasm-instrument = "0.1" -wasmtime = { version = "0.35.1", default-features = false, features = ["parallel-compilation", "cranelift"]} +wasmi = { version = "0.11.0", default-features = false } \ No newline at end of file diff --git a/gtest/src/error.rs b/gtest/src/error.rs index 43a3a7a4e75..70f6f6ef6a7 100644 --- a/gtest/src/error.rs +++ b/gtest/src/error.rs @@ -20,7 +20,6 @@ use anyhow::Error as AnyhowError; use codec::Error as CodecError; use core_processor::ProcessorError; use gear_core::{ids::ProgramId, memory::WasmPageNumber}; -use wasmtime::MemoryAccessError; /// Type alias for the testing functions running result. pub type Result = core::result::Result; @@ -69,13 +68,13 @@ pub enum TestError { #[display(fmt = "{}", _0)] ExecutionError(ProcessorError), - /// Wrapper for [`wasmtime::MemoryAccessError`](https://docs.rs/wasmtime/latest/wasmtime/struct.MemoryAccessError.html). + /// Wrapper for [`wasmi::Error`](https://paritytech.github.io/wasmi/wasmi/enum.Error.html). #[display(fmt = "{}", _0)] - MemoryError(MemoryAccessError), + MemoryError(wasmi::Error), - /// Wrapper for `wasmtime` error (used [`anyhow::Error`] for that). + /// Wrapper for `wasmi` error (used [`anyhow::Error`] for that). #[display(fmt = "{}", _0)] - WasmtimeError(AnyhowError), + WasmiError(AnyhowError), /// Wrapper for [`parity_scale_codec::Error`](https://docs.rs/parity-scale-codec/latest/parity_scale_codec/struct.Error.html). #[display(fmt = "{}", _0)] diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 602360b9f22..159bdde380c 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -27,7 +27,7 @@ use core_processor::{ configs::{BlockConfig, BlockInfo, MessageExecutionContext}, Ext, PrepareResult, }; -use gear_backend_wasmtime::WasmtimeEnvironment; +use gear_backend_wasmi::WasmiEnvironment; use gear_core::{ code::{Code, CodeAndId, InstrumentedCodeAndId}, ids::{CodeId, MessageId, ProgramId}, @@ -598,7 +598,7 @@ impl ExtManager { PrepareResult::WontExecute(journal) | PrepareResult::Error(journal) => journal, PrepareResult::Ok { context, .. } => core_processor::process::< Ext, - WasmtimeEnvironment, + WasmiEnvironment, >(&block_config, context, memory_pages), }; diff --git a/gtest/src/wasm_executor.rs b/gtest/src/wasm_executor.rs index 510d0e9f670..17d36236e8c 100644 --- a/gtest/src/wasm_executor.rs +++ b/gtest/src/wasm_executor.rs @@ -18,7 +18,11 @@ use core_processor::{Ext, ProcessorContext, ProcessorExt}; use gear_backend_common::TerminationReason; -use gear_backend_wasmtime::{env::StoreData, funcs_tree}; +use gear_backend_wasmi::{ + env::{DefinedHostFunctions, EnvironmentDefinitionBuilder, GuestExternals, Runtime}, + funcs::{FuncError, FuncsHandler as Funcs}, + MemoryWrap, +}; use gear_core::{ env::{Ext as ExtTrait, ExtCarrier}, gas::{GasAllowanceCounter, GasCounter, ValueCounter}, @@ -27,18 +31,18 @@ use gear_core::{ program::Program, }; use std::{collections::BTreeMap, mem}; -use wasmtime::{ - Config, Engine, Extern, Func, Instance, Memory as WasmtimeMemory, MemoryType, Module, Store, - Val, +use wasmi::{ + memory_units::Pages, MemoryInstance, MemoryRef, ModuleInstance, ModuleRef, RuntimeValue, }; use crate::{Result, TestError, MAILBOX_THRESHOLD}; /// Binary meta-functions executor for testing purposes pub(crate) struct WasmExecutor { - instance: Instance, - store: Store>, - memory: WasmtimeMemory, + instance: ModuleRef, + store: Runtime, + memory: MemoryRef, + defined_host_functions: DefinedHostFunctions, ::Error>, } impl WasmExecutor { @@ -51,78 +55,110 @@ impl WasmExecutor { payload: Option, ) -> Result { let ext = WasmExecutor::build_ext(program, payload.unwrap_or_default()); - let ext_carrier = ExtCarrier::new(ext); - let store_data = StoreData { - ext: ext_carrier.cloned(), - termination_reason: TerminationReason::Success, - }; + let mut builder: EnvironmentDefinitionBuilder, ::Error> = + EnvironmentDefinitionBuilder::new( + ext.forbidden_funcs() + .clone() + .into_iter() + .map(|s| s.to_string()) + .collect(), + ); + + builder.add_host_func("env", "forbidden", Funcs::forbidden); + builder.add_host_func("env", "gr_block_height", Funcs::block_height); + builder.add_host_func("env", "gr_block_timestamp", Funcs::block_timestamp); + builder.add_host_func("env", "gr_create_program", Funcs::create_program); + builder.add_host_func("env", "gr_create_program_wgas", Funcs::create_program_wgas); + builder.add_host_func("env", "gr_debug", Funcs::debug); + builder.add_host_func("env", "gr_error", Funcs::error); + builder.add_host_func("env", "gr_exit", Funcs::exit); + builder.add_host_func("env", "gr_exit_code", Funcs::exit_code); + builder.add_host_func("env", "gr_gas_available", Funcs::gas_available); + builder.add_host_func("env", "gr_leave", Funcs::leave); + builder.add_host_func("env", "gr_msg_id", Funcs::msg_id); + builder.add_host_func("env", "gr_origin", Funcs::origin); + builder.add_host_func("env", "gr_program_id", Funcs::program_id); + builder.add_host_func("env", "gr_read", Funcs::read); + builder.add_host_func("env", "gr_reply", Funcs::reply); + builder.add_host_func("env", "gr_reply_commit", Funcs::reply_commit); + builder.add_host_func("env", "gr_reply_commit_wgas", Funcs::reply_commit_wgas); + builder.add_host_func("env", "gr_reply_push", Funcs::reply_push); + builder.add_host_func("env", "gr_reply_to", Funcs::reply_to); + builder.add_host_func("env", "gr_reply_wgas", Funcs::reply_wgas); + builder.add_host_func("env", "gr_send", Funcs::send); + builder.add_host_func("env", "gr_send_commit", Funcs::send_commit); + builder.add_host_func("env", "gr_send_commit_wgas", Funcs::send_commit_wgas); + builder.add_host_func("env", "gr_send_init", Funcs::send_init); + builder.add_host_func("env", "gr_send_push", Funcs::send_push); + builder.add_host_func("env", "gr_send_wgas", Funcs::send_wgas); + builder.add_host_func("env", "gr_size", Funcs::size); + builder.add_host_func("env", "gr_source", Funcs::source); + builder.add_host_func("env", "gr_value", Funcs::value); + builder.add_host_func("env", "gr_value_available", Funcs::value_available); + builder.add_host_func("env", "gr_wait", Funcs::wait); + builder.add_host_func("env", "gr_wake", Funcs::wake); - let config = Config::new(); - let engine = Engine::new(&config)?; - let mut store = Store::>::new(&engine, store_data); - - let module = Module::new(&engine, meta_binary)?; + let ext_carrier = ExtCarrier::new(ext); - let mut linker = wasmtime::Linker::>::new(&engine); + let mem: MemoryRef = MemoryInstance::alloc(Pages(program.static_pages().0 as usize), None)?; - let mut memory = - WasmtimeMemory::new(&mut store, MemoryType::new(program.static_pages().0, None))?; + builder.add_memory("env", "memory", mem.clone()); + builder.add_host_func("env", "alloc", Funcs::alloc); + builder.add_host_func("env", "free", Funcs::free); + builder.add_host_func("env", "gas", Funcs::gas); - let funcs = funcs_tree::build(&mut store, memory, None); - for import in module.imports() { - if import.module() != "env" { - return Err(TestError::InvalidImportModule(import.module().to_string())); - } - match import.name() { - Some("memory") => { - linker.define("env", "memory", Extern::Memory(memory))?; - } - Some(key) => { - if funcs.contains_key(key) { - linker.define("env", key, funcs[key])?; - } else { - return Err(TestError::UnsupportedFunction(key.to_string())); - } - } - _ => continue, - }; - } - - let instance = linker.instantiate(&mut store, &module)?; + let runtime = Runtime { + ext: ext_carrier, + err: FuncError::Terminated(TerminationReason::Success), + memory: MemoryWrap::new(mem.clone()), + }; - WasmExecutor::set_pages(&mut store, &mut memory, memory_pages)?; + let defined_host_functions = builder.defined_host_functions.clone(); + let instance = match ModuleInstance::new( + &wasmi::Module::from_buffer(meta_binary).expect("wasmi can't load module binary"), + &builder, + ) { + Ok(inst) => inst.not_started_instance().clone(), + Err(e) => return Err(TestError::WasmiError(e.into())), + }; + WasmExecutor::set_pages(mem.clone(), memory_pages)?; Ok(Self { instance, - store, - memory, + store: runtime, + memory: mem, + defined_host_functions, }) } /// Executes non-void function by provided name. /// Panics if function is void pub(crate) fn execute(&mut self, function_name: &str) -> Result> { - let function = self.get_function(function_name)?; - let mut ptr_to_result_array = [Val::I32(0)]; - - function - .call(&mut self.store, &[], &mut ptr_to_result_array) + let mut externals = GuestExternals { + state: &mut self.store, + defined_host_functions: &self.defined_host_functions, + }; + let res = self + .instance + .invoke_export(function_name, &[], &mut externals) .map_err(|err| { + if let wasmi::Error::Function(_) = err { + return TestError::FunctionNotFound(function_name.to_string()); + } if let Some(processor_error) = self .store - .data() .ext .with(|a| a.error_explanation.clone()) .expect("`with` is expected to be called only after `inner` is set") { processor_error.into() } else { - TestError::WasmtimeError(err) + TestError::WasmiError(err.into()) } })?; - match ptr_to_result_array[0] { - Val::I32(ptr_to_result) => self.read_result(ptr_to_result), + match res { + Some(RuntimeValue::I32(ptr_to_result)) => self.read_result(ptr_to_result), _ => Err(TestError::InvalidReturnType), } } @@ -154,12 +190,6 @@ impl WasmExecutor { }) } - fn get_function(&mut self, function_name: &str) -> Result { - self.instance - .get_func(&mut self.store, function_name) - .ok_or_else(|| TestError::FunctionNotFound(function_name.to_string())) - } - fn read_result(&mut self, ptr_to_result_data: i32) -> Result> { let offset = ptr_to_result_data as usize; @@ -167,10 +197,10 @@ impl WasmExecutor { let mut ptr = [0_u8; mem::size_of::()]; let mut len = [0_u8; mem::size_of::()]; - self.memory.read(&self.store, offset, &mut ptr)?; + self.memory.get_into(offset as u32, &mut ptr)?; self.memory - .read(&self.store, offset + ptr.len(), &mut len)?; + .get_into((offset + ptr.len()) as u32, &mut len)?; let ptr = i32::from_ne_bytes(ptr) as usize; let len = i32::from_ne_bytes(len) as usize; @@ -178,23 +208,19 @@ impl WasmExecutor { // Reading a vector from `ptr` let mut result = vec![0; len]; - self.memory.read(&self.store, ptr, &mut result)?; + self.memory.get_into(ptr as u32, &mut result)?; Ok(result) } - fn set_pages( - mut store: &mut Store>, - memory: &mut WasmtimeMemory, - pages: &BTreeMap>, - ) -> Result<()> { - let memory_size = WasmPageNumber(memory.size(&mut store) as u32); + fn set_pages(memory: MemoryRef, pages: &BTreeMap>) -> Result<()> { + let memory_size = WasmPageNumber(memory.current_size().0 as u32); for (page_number, buffer) in pages { let wasm_page_number = page_number.to_wasm_page(); if memory_size <= wasm_page_number { return Err(TestError::InsufficientMemory(memory_size, wasm_page_number)); } - memory.write(&mut store, page_number.offset(), &buffer[..])?; + memory.set(page_number.offset() as u32, &buffer[..])?; } Ok(()) } diff --git a/pallets/gas/Cargo.toml b/pallets/gas/Cargo.toml index 7aab79e4829..b9aeeb360a9 100644 --- a/pallets/gas/Cargo.toml +++ b/pallets/gas/Cargo.toml @@ -32,7 +32,7 @@ sp-runtime = { version = "6.0.0", git = "https://github.com/gear-tech/substrate. pallet-balances = { version = "4.0.0-dev", default-features = false, git = "https://github.com/gear-tech/substrate.git", branch = "gear-stable" } [dev-dependencies] -serde = "1.0.139" +serde = "1.0.140" env_logger = "0.9" wabt = "0.10" gear-core = { path = "../../core" } diff --git a/pallets/gear-debug/Cargo.toml b/pallets/gear-debug/Cargo.toml index 725dc8c57d8..e5594b51988 100644 --- a/pallets/gear-debug/Cargo.toml +++ b/pallets/gear-debug/Cargo.toml @@ -35,7 +35,7 @@ pallet-balances = { version = "4.0.0-dev", default-features = false, git = "http pallet-authorship = { version = "4.0.0-dev", default-features = false, git = "https://github.com/gear-tech/substrate.git", branch = "gear-stable" } [dev-dependencies] -serde = "1.0.139" +serde = "1.0.140" env_logger = "0.9" wabt = "0.10" gear-core = { path = "../../core" } diff --git a/pallets/gear-program/src/tests.rs b/pallets/gear-program/src/tests.rs index e7017a17bda..95b687f176d 100644 --- a/pallets/gear-program/src/tests.rs +++ b/pallets/gear-program/src/tests.rs @@ -102,7 +102,7 @@ fn pause_program_works() { // although the memory pages should be removed assert!( - common::get_program_data_for_pages(program_id.into_origin(), memory_pages.keys()) + common::get_program_data_for_pages_optional(program_id.into_origin(), memory_pages.keys().copied()) .unwrap() .is_empty() ); @@ -204,11 +204,12 @@ fn pause_uninitialized_program_works() { assert!(Pallet::::get_code(code_id).is_some()); // although the memory pages should be removed - assert!( - common::get_program_data_for_pages(program_id.into_origin(), memory_pages.keys()) - .unwrap() - .is_empty() - ); + assert!(common::get_program_data_for_pages_optional( + program_id.into_origin(), + memory_pages.keys().copied() + ) + .unwrap() + .is_empty()); assert!(WaitlistOf::::remove(program_id, msg_1.id()).is_err()); assert!(WaitlistOf::::remove(program_id, msg_2.id()).is_err()); @@ -251,9 +252,11 @@ fn resume_uninitialized_program_works() { )); assert!(!GearProgram::program_paused(program_id)); - let new_memory_pages = - common::get_program_data_for_pages(program_id.into_origin(), memory_pages.keys()) - .unwrap(); + let new_memory_pages = common::get_program_data_for_pages_optional( + program_id.into_origin(), + memory_pages.keys().copied(), + ) + .unwrap(); assert_eq!(memory_pages, new_memory_pages); let waiting_init = common::waiting_init_take_messages(program_id); diff --git a/pallets/gear/src/lib.rs b/pallets/gear/src/lib.rs index f5acd4fa664..06087f0ebe0 100644 --- a/pallets/gear/src/lib.rs +++ b/pallets/gear/src/lib.rs @@ -1249,10 +1249,7 @@ pub mod pallet { ) { Ok(data) => data, Err(err) => { - log::error!( - "Page data in storage is in invalid state: {}", - err - ); + log::error!("Cannot get data for program pages: {err}"); continue; } } diff --git a/pallets/payment/Cargo.toml b/pallets/payment/Cargo.toml index 67944e791b3..e02fc3a7d6c 100644 --- a/pallets/payment/Cargo.toml +++ b/pallets/payment/Cargo.toml @@ -33,7 +33,7 @@ pallet-balances = { version = "4.0.0-dev", default-features = false, git = "http pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, git = "https://github.com/gear-tech/substrate.git", branch = "gear-stable" } [dev-dependencies] -serde = "1.0.139" +serde = "1.0.140" env_logger = "0.9" wabt = "0.10" gear-core = { path = "../../core" }