From ee15e3d60e2d6273d0652967e1409502eccf8166 Mon Sep 17 00:00:00 2001 From: Harrm Date: Fri, 1 Dec 2023 15:09:21 +0300 Subject: [PATCH] Wasm Edge (#1864) * Integrate WasmEdge to Kagome --------- Co-authored-by: Alexander Krutikov --- .github/workflows/test.yml | 2 +- CMakeLists.txt | 18 +- cmake/Hunter/config.cmake | 61 ++- cmake/Hunter/hunter-gate-url.cmake | 4 +- cmake/dependencies.cmake | 27 +- cmake/install.cmake | 2 +- cmake/kagomeConfig.cmake.in | 6 + .../api/service/state/impl/state_api_impl.cpp | 3 +- core/application/app_configuration.hpp | 6 +- .../impl/app_configuration_impl.cpp | 21 +- .../impl/app_state_manager_impl.cpp | 1 + core/benchmark/block_execution_benchmark.cpp | 69 ++- core/blockchain/impl/block_tree_impl.cpp | 1 - core/common/buffer_or_view.hpp | 14 +- .../impl/dispute_coordinator_impl.cpp | 3 +- core/host_api/host_api.hpp | 8 +- .../host_api/impl/child_storage_extension.cpp | 5 +- .../host_api/impl/child_storage_extension.hpp | 2 +- core/host_api/impl/host_api_impl.cpp | 8 +- core/host_api/impl/host_api_impl.hpp | 8 +- core/host_api/impl/memory_extension.cpp | 9 +- core/host_api/impl/misc_extension.cpp | 9 +- core/host_api/impl/misc_extension.hpp | 2 +- core/host_api/impl/offchain_extension.cpp | 4 +- core/host_api/impl/offchain_extension.hpp | 12 +- core/host_api/impl/storage_extension.cpp | 7 +- core/injector/CMakeLists.txt | 60 ++- core/injector/application_injector.cpp | 151 +++--- core/injector/calculate_genesis_state.hpp | 1 + core/log/configurator.cpp | 1 + core/log/formatters/optional.hpp | 66 --- core/log/trace_macros.hpp | 26 +- core/network/CMakeLists.txt | 3 +- core/network/impl/protocols/light.cpp | 11 +- core/network/impl/protocols/light.hpp | 4 +- core/network/impl/state_sync_request_flow.cpp | 12 +- core/network/impl/state_sync_request_flow.hpp | 9 +- core/network/impl/stream_engine.cpp | 1 - core/network/impl/synchronizer_impl.cpp | 20 +- core/network/impl/synchronizer_impl.hpp | 4 +- core/offchain/impl/CMakeLists.txt | 1 + core/offchain/impl/offchain_local_storage.cpp | 2 +- .../approval/approval_distribution.cpp | 5 +- core/parachain/pvf/pvf_impl.cpp | 2 +- .../validator/impl/parachain_processor.cpp | 3 +- core/runtime/CMakeLists.txt | 21 +- core/runtime/binaryen/CMakeLists.txt | 15 +- .../binaryen/binaryen_memory_provider.hpp | 2 + .../binaryen/core_api_factory_impl.cpp | 78 ---- .../binaryen/core_api_factory_impl.hpp | 51 -- .../binaryen/instance_environment_factory.cpp | 18 +- .../binaryen/instance_environment_factory.hpp | 15 +- core/runtime/binaryen/memory_impl.cpp | 3 + .../binaryen/module/module_factory_impl.cpp | 9 +- .../binaryen/module/module_factory_impl.hpp | 4 +- core/runtime/binaryen/module/module_impl.cpp | 14 +- core/runtime/binaryen/module/module_impl.hpp | 13 +- .../binaryen/module/module_instance_impl.cpp | 25 +- .../binaryen/module/module_instance_impl.hpp | 6 +- .../binaryen/runtime_external_interface.cpp | 9 +- core/runtime/common/CMakeLists.txt | 4 + core/runtime/common/core_api_factory_impl.cpp | 32 ++ core/runtime/common/core_api_factory_impl.hpp | 43 ++ core/runtime/common/executor.cpp | 19 +- core/runtime/common/memory_allocator.cpp | 4 +- core/runtime/common/memory_allocator.hpp | 19 +- core/runtime/common/module_instance.cpp | 32 +- .../runtime/common/module_repository_impl.cpp | 18 +- core/runtime/common/runtime_context.cpp | 27 +- .../runtime/common/runtime_instances_pool.cpp | 18 +- .../runtime/common/runtime_instances_pool.hpp | 4 +- core/runtime/core_api_factory.hpp | 6 +- core/runtime/executor.hpp | 167 +------ core/runtime/module.hpp | 2 +- core/runtime/module_instance.hpp | 63 ++- core/runtime/runtime_api/core.hpp | 27 +- .../runtime_api/impl/account_nonce_api.cpp | 5 +- core/runtime/runtime_api/impl/babe_api.cpp | 13 +- core/runtime/runtime_api/impl/beefy.cpp | 10 +- .../runtime_api/impl/block_builder.cpp | 20 +- core/runtime/runtime_api/impl/core.cpp | 43 +- core/runtime/runtime_api/impl/core.hpp | 14 +- core/runtime/runtime_api/impl/grandpa_api.cpp | 8 +- core/runtime/runtime_api/impl/lru.hpp | 36 +- core/runtime/runtime_api/impl/mmr.cpp | 20 +- .../runtime_api/impl/offchain_worker_api.cpp | 7 +- .../runtime_api/impl/parachain_host.cpp | 57 ++- .../runtime_api/impl/session_keys_api.cpp | 10 +- .../impl/tagged_transaction_queue.cpp | 5 +- .../impl/transaction_payment_api.cpp | 9 +- core/runtime/runtime_context.hpp | 34 +- core/runtime/types.hpp | 37 +- core/runtime/wasm_edge/CMakeLists.txt | 4 + core/runtime/wasm_edge/memory_impl.cpp | 85 ++++ core/runtime/wasm_edge/memory_impl.hpp | 207 +++++++++ .../runtime/wasm_edge/module_factory_impl.cpp | 438 ++++++++++++++++++ .../runtime/wasm_edge/module_factory_impl.hpp | 72 +++ core/runtime/wasm_edge/register_host_api.hpp | 286 ++++++++++++ core/runtime/wasm_edge/wrappers.hpp | 126 +++++ core/runtime/wavm/CMakeLists.txt | 28 +- core/runtime/wavm/core_api_factory_impl.cpp | 126 ----- core/runtime/wavm/core_api_factory_impl.hpp | 64 --- .../wavm/instance_environment_factory.cpp | 33 +- .../wavm/instance_environment_factory.hpp | 21 +- core/runtime/wavm/module.cpp | 3 +- core/runtime/wavm/module.hpp | 2 +- core/runtime/wavm/module_factory_impl.cpp | 20 +- core/runtime/wavm/module_factory_impl.hpp | 30 +- core/runtime/wavm/module_instance.cpp | 14 +- core/runtime/wavm/module_instance.hpp | 6 +- core/storage/rocksdb/rocksdb_spaces.cpp | 1 + core/storage/spaces.hpp | 1 + .../trie/impl/trie_storage_backend_impl.cpp | 5 +- .../trie/impl/trie_storage_backend_impl.hpp | 3 +- .../serialization/trie_serializer_impl.cpp | 12 +- .../serialization/trie_serializer_impl.hpp | 4 +- core/storage/trie/trie_storage_backend.hpp | 2 +- .../trie_pruner/impl/trie_pruner_impl.cpp | 54 ++- .../trie_pruner/impl/trie_pruner_impl.hpp | 6 +- core/telemetry/CMakeLists.txt | 1 + core/utils/kagome_db_editor.cpp | 66 +-- core/utils/storage_explorer.cpp | 37 +- .../core/api/service/state/state_api_test.cpp | 14 +- test/core/blockchain/block_storage_test.cpp | 2 +- .../network/state_protocol_observer_test.cpp | 12 +- test/core/network/synchronizer_test.cpp | 8 +- test/core/parachain/pvf_test.cpp | 12 +- test/core/runtime/CMakeLists.txt | 4 +- test/core/runtime/binaryen/CMakeLists.txt | 4 +- .../binaryen/binaryen_runtime_test.hpp | 8 +- test/core/runtime/executor_test.cpp | 92 ++-- .../runtime/trie_storage_provider_test.cpp | 13 +- test/core/runtime/wavm/CMakeLists.txt | 2 +- .../runtime/wavm/core_integration_test.cpp | 12 +- .../runtime/wavm/wavm_module_init_test.cpp | 43 +- test/core/runtime/wavm/wavm_runtime_test.hpp | 28 +- .../changes_trie/changes_tracker_test.cpp | 10 +- .../trie/trie_storage/trie_batch_test.cpp | 14 +- .../trie_storage_backend_test.cpp | 20 +- .../trie/trie_storage/trie_storage_test.cpp | 10 +- .../storage/trie_pruner/trie_pruner_test.cpp | 75 +-- .../link_libraries.cmake | 8 +- test/external-project-test/src/main.cpp | 45 +- test/mock/core/host_api/host_api_mock.hpp | 8 +- .../core/runtime/core_api_factory_mock.hpp | 2 +- test/mock/core/runtime/core_mock.hpp | 2 +- test/mock/core/runtime/executor_mock.hpp | 36 -- .../core/runtime/module_instance_mock.hpp | 11 +- test/mock/core/runtime/module_mock.hpp | 2 +- .../runtime/runtime_context_factory_mock.hpp | 14 +- ..._map_mock.hpp => generic_storage_mock.hpp} | 0 151 files changed, 2580 insertions(+), 1478 deletions(-) delete mode 100644 core/log/formatters/optional.hpp delete mode 100644 core/runtime/binaryen/core_api_factory_impl.hpp create mode 100644 core/runtime/common/core_api_factory_impl.cpp create mode 100644 core/runtime/common/core_api_factory_impl.hpp create mode 100644 core/runtime/wasm_edge/CMakeLists.txt create mode 100644 core/runtime/wasm_edge/memory_impl.cpp create mode 100644 core/runtime/wasm_edge/memory_impl.hpp create mode 100644 core/runtime/wasm_edge/module_factory_impl.cpp create mode 100644 core/runtime/wasm_edge/module_factory_impl.hpp create mode 100644 core/runtime/wasm_edge/register_host_api.hpp create mode 100644 core/runtime/wasm_edge/wrappers.hpp delete mode 100644 core/runtime/wavm/core_api_factory_impl.hpp delete mode 100644 test/mock/core/runtime/executor_mock.hpp rename test/mock/core/storage/{persistent_map_mock.hpp => generic_storage_mock.hpp} (100%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 10dfd06d03..ee8849ced3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,7 +44,7 @@ jobs: - name: install run: ./housekeeping/macos/dependency.sh - name: build - run: ./housekeeping/make_build.sh -DCLEAR_OBJS=ON -DCOVERAGE=OFF -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain/cxx20.cmake + run: ./housekeeping/make_build.sh -DCLEAR_OBJS=ON -DCOVERAGE=OFF -DWASM_COMPILER=WasmEdge -DCMAKE_TOOLCHAIN_FILE=cmake/toolchain/cxx20.cmake env: CI: diff --git a/CMakeLists.txt b/CMakeLists.txt index c972e7ee05..d6ce6e35a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,12 @@ if(NOT($ENV{CI}) OR NOT($ENV{GITHUB_ACTIONS})) endif() option(EXTERNAL_PROJECT "Build external project" ${_EXTERNAL_PROJECT_DEFAULT}) +set(WASM_COMPILER WAVM CACHE STRING "WebAssembly compiler built into Kagome: one of [WAVM, WasmEdge]") + +if (NOT ${WASM_COMPILER} MATCHES "^(WAVM|WasmEdge)$") + fatal_error("WASM_COMPILER is set to ${WASM_COMPILER} but should be one of [WAVM, WasmEdge]") +endif() + # sanitizers will be enabled only for Kagome, and will be disabled for dependencies option(ASAN "Enable address sanitizer" OFF) option(LSAN "Enable leak sanitizer" OFF) @@ -80,6 +86,8 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(AppleClang|Clang|GNU)$") # cmake-format: off # enable those flags + add_flag(-fdiagnostics-show-template-tree) + add_flag(-Wall) add_flag(-Wextra) add_flag(-Woverloaded-virtual) # warn if you overload (not override) a virtual function @@ -108,7 +116,8 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(AppleClang|Clang|GNU)$") add_flag(-Werror=unused-lambda-capture) # error if lambda capture is unused add_flag(-Werror=return-type) # warning: control reaches end of non-void function [-Wreturn-type] add_flag(-Werror=sign-compare) # warn the user if they compare a signed and unsigned numbers - add_flag(-Werror=reorder) # field '$1' will be initialized after field '$2' + # breaks soralog headers + # add_flag(-Werror=reorder) # field '$1' will be initialized after field '$2' add_flag(-Werror=mismatched-tags) # warning: class '$1' was previously declared as struct add_flag(-Werror=switch) # unhandled values in a switch statement # cmake-format: on @@ -124,9 +133,9 @@ if("${CMAKE_CXX_COMPILER_ID}" MATCHES "^(AppleClang|Clang|GNU)$") # cmake-format: on endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # using Visual Studio C++ - # TODO(warchant): add flags # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#msvc + + fatal_error("MSVC is not supported") endif() print("C flags: ${CMAKE_C_FLAGS}") @@ -186,7 +195,8 @@ kagome_install_setup( core/storage core/subscription core/telemetry - core/utils) + core/utils +) include(CMakePackageConfigHelpers) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index e3e3c9d48c..a36c285bd0 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -50,19 +50,44 @@ hunter_config( CMAKE_ARGS WITH_GFLAGS=OFF ) -hunter_config( - wavm - VERSION 1.0.14 - KEEP_PACKAGE_SOURCES -) +if ("${WASM_COMPILER}" STREQUAL "WasmEdge") + hunter_config( + LLVM + VERSION 16.0.1 + CMAKE_ARGS LLVM_ENABLE_PROJECTS=compiler-rt + ) -hunter_config( - LLVM - VERSION 12.0.1-p4 - CMAKE_ARGS + hunter_config( + WasmEdge + URL https://github.com/harrm/WasmEdge/archive/8a52b27e2592fc9934b6132e80f69ae6d6925997.zip + SHA1 fbdd08bd648faffa7d140387eb98b437d0f6d5ea + KEEP_PACKAGE_SOURCES + ) +endif () + +if ("${WASM_COMPILER}" STREQUAL "WAVM") + hunter_config( + LLVM + VERSION 12.0.1-p4 + CMAKE_ARGS LLVM_ENABLE_PROJECTS=ir - KEEP_PACKAGE_SOURCES -) + KEEP_PACKAGE_SOURCES + ) + + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(WAVM_CXX_FLAGS -Wno-redundant-move;-Wno-dangling-reference;-Wno-error=extra;) + else () + set(WAVM_CXX_FLAGS -Wno-redundant-move) + endif () + + hunter_config( + wavm + VERSION 1.0.14 + CMAKE_ARGS + WAVM_CXX_FLAGS=${WAVM_CXX_FLAGS} + KEEP_PACKAGE_SOURCES + ) +endif () hunter_config( scale @@ -71,10 +96,10 @@ hunter_config( ) # Fix for Apple clang (or clang from brew) of versions 15 and higher -if(APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL "15.0.0") - hunter_config( - binaryen - URL https://github.com/qdrvm/binaryen/archive/0744f64a584cae5b9255b1c2f0a4e0b5e06d7038.zip - SHA1 f953c5f38a0417e494901e15ab6f5d8267388d18 - ) -endif() +if (APPLE AND (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") AND CMAKE_CXX_COMPILER_VERSION GREATER_EQUAL "15.0.0") + hunter_config( + binaryen + URL https://github.com/qdrvm/binaryen/archive/0744f64a584cae5b9255b1c2f0a4e0b5e06d7038.zip + SHA1 f953c5f38a0417e494901e15ab6f5d8267388d18 + ) +endif () diff --git a/cmake/Hunter/hunter-gate-url.cmake b/cmake/Hunter/hunter-gate-url.cmake index 14ea3c1954..e518d01814 100644 --- a/cmake/Hunter/hunter-gate-url.cmake +++ b/cmake/Hunter/hunter-gate-url.cmake @@ -1,5 +1,5 @@ HunterGate( - URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.23.257-qdrvm11.tar.gz - SHA1 b2a69ae501bdc99006fb86e55930640004468556 + URL "https://github.com/qdrvm/hunter/archive/heads/feature/wasm-edge.zip" + SHA1 "27ee523652a59a8e88b50c9f28daf212d5a33fef" LOCAL ) diff --git a/cmake/dependencies.cmake b/cmake/dependencies.cmake index ece0afc3bd..35562c5079 100644 --- a/cmake/dependencies.cmake +++ b/cmake/dependencies.cmake @@ -2,15 +2,15 @@ # https://docs.hunter.sh/en/latest/packages/ if (TESTING) - # https://docs.hunter.sh/en/latest/packages/pkg/GTest.html - hunter_add_package(GTest) - find_package(GTest CONFIG REQUIRED) -endif() + # https://docs.hunter.sh/en/latest/packages/pkg/GTest.html + hunter_add_package(GTest) + find_package(GTest CONFIG REQUIRED) +endif () if (BACKWARD) - hunter_add_package(backward-cpp) - find_package(Backward) -endif() + hunter_add_package(backward-cpp) + find_package(Backward) +endif () # https://docs.hunter.sh/en/latest/packages/pkg/Boost.html hunter_add_package(Boost COMPONENTS random filesystem program_options date_time) @@ -71,9 +71,16 @@ find_package(libsecp256k1 CONFIG REQUIRED) hunter_add_package(scale) find_package(scale CONFIG REQUIRED) -hunter_add_package(wavm) -find_package(LLVM CONFIG REQUIRED) -find_package(WAVM CONFIG REQUIRED) +if ("${WASM_COMPILER}" STREQUAL "WAVM") + hunter_add_package(wavm) + find_package(LLVM CONFIG REQUIRED) + find_package(WAVM CONFIG REQUIRED) +endif () + +if ("${WASM_COMPILER}" STREQUAL "WasmEdge") + hunter_add_package(WasmEdge) + find_library(WASM_EDGE_LIBRARY NAMES wasmedge REQUIRED PATHS "${WASMEDGE_ROOT}") +endif () hunter_add_package(zstd) find_package(zstd CONFIG REQUIRED) diff --git a/cmake/install.cmake b/cmake/install.cmake index 24df6449c9..48161dc543 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -25,7 +25,7 @@ endfunction() ### workaround for imported libraries function(kagome_install_mini target) install(TARGETS ${target} EXPORT kagomeTargets - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/kagome + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/kagome ) endfunction() diff --git a/cmake/kagomeConfig.cmake.in b/cmake/kagomeConfig.cmake.in index 5d36035060..a8decd5d71 100644 --- a/cmake/kagomeConfig.cmake.in +++ b/cmake/kagomeConfig.cmake.in @@ -23,6 +23,12 @@ if (KAGOME_ENABLE_WAVM) find_dependency(WAVM REQUIRED) endif() +option(KAGOME_ENABLE_WASMEDGE "Include WasmEdge webassembly executor dependency" OFF) +if (KAGOME_ENABLE_WASMEDGE) + find_dependency(LLVM REQUIRED) + find_dependency(WasmEdge REQUIRED) +endif() + option(KAGOME_ENABLE_BINARYEN "Include Binaryen webassembly executor dependency" ON) if (KAGOME_ENABLE_BINARYEN) find_dependency(binaryen REQUIRED) diff --git a/core/api/service/state/impl/state_api_impl.cpp b/core/api/service/state/impl/state_api_impl.cpp index 3742cfaec9..9b4da2ac3e 100644 --- a/core/api/service/state/impl/state_api_impl.cpp +++ b/core/api/service/state/impl/state_api_impl.cpp @@ -67,7 +67,8 @@ namespace kagome::api { const std::optional &opt_at) const { auto at = opt_at.has_value() ? opt_at.value() : block_tree_->bestBlock().hash; - return executor_->callAt(at, method, data); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(at)); + return ctx.module_instance->callExportFunction(ctx, method, data); } outcome::result> StateApiImpl::getKeysPaged( diff --git a/core/application/app_configuration.hpp b/core/application/app_configuration.hpp index 77a7d89adc..ddacbded9a 100644 --- a/core/application/app_configuration.hpp +++ b/core/application/app_configuration.hpp @@ -215,7 +215,11 @@ namespace kagome::application { */ virtual SyncMethod syncMethod() const = 0; - enum class RuntimeExecutionMethod { Compile, Interpret }; + enum class RuntimeExecutionMethod { + Compile, + Interpret, + }; + /** * @return enum constant of the chosen runtime backend */ diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index d1b103b187..e2e311f0b1 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -6,10 +6,12 @@ #include "application/impl/app_configuration_impl.hpp" +#include #include #include #include +#include #include #include #include @@ -17,7 +19,6 @@ #include #include #include -#include #include #include "api/transport/tuner.hpp" @@ -30,7 +31,6 @@ #include "filesystem/common.hpp" #include "filesystem/directories.hpp" #include "log/formatters/filepath.hpp" -#include "log/formatters/optional.hpp" #include "utils/read_file.hpp" namespace { @@ -156,6 +156,11 @@ namespace { return std::nullopt; } + std::array execution_methods{"Interpreted", "Compiled"}; + + std::string execution_methods_str = + fmt::format("[{}]", fmt::join(execution_methods, ", ")); + std::optional str_to_runtime_exec_method(std::string_view str) { using REM = kagome::application::AppConfiguration::RuntimeExecutionMethod; @@ -185,12 +190,12 @@ namespace { std::optional str_to_recovery_state( std::string_view str) { - kagome::primitives::BlockNumber bn; auto res = kagome::primitives::BlockHash::fromHex(str); if (res.has_value()) { return {{res.value()}}; } + kagome::primitives::BlockNumber bn{}; auto result = std::from_chars(str.data(), str.data() + str.size(), bn); if (result.ec != std::errc::invalid_argument && std::to_string(bn) == str) { return {{bn}}; @@ -839,7 +844,7 @@ namespace kagome::application { ("sync", po::value()->default_value(def_full_sync), "choose the desired sync method (Full, Fast). Full is used by default.") ("wasm-execution", po::value()->default_value(def_wasm_execution), - "choose the desired wasm execution method (Compiled, Interpreted)") + fmt::format("choose the desired wasm execution method ({})", execution_methods_str).c_str()) ("unsafe-cached-wavm-runtime", "use WAVM runtime cache") ("purge-wavm-cache", "purge WAVM runtime cache") ("parachain-runtime-instance-cache-size", @@ -1372,8 +1377,10 @@ namespace kagome::application { if (not runtime_exec_method_opt) { exec_method_value_error = true; SL_ERROR(logger_, - "Invalid runtime execution method specified: '{}'", - val); + "Invalid runtime execution method specified: '{}'. " + "Available methods are: {}", + val, + execution_methods_str); } else { runtime_exec_method_ = runtime_exec_method_opt.value(); } @@ -1455,7 +1462,7 @@ namespace kagome::application { return false; } auto repeat_opt = find_argument(vm, "repeat"); - if (!to_opt) { + if (!repeat_opt) { SL_ERROR(logger_, "Required argument --repeat is not provided"); return false; } diff --git a/core/application/impl/app_state_manager_impl.cpp b/core/application/impl/app_state_manager_impl.cpp index 55040fd5ad..9eda90f16c 100644 --- a/core/application/impl/app_state_manager_impl.cpp +++ b/core/application/impl/app_state_manager_impl.cpp @@ -139,6 +139,7 @@ namespace kagome::application { if (state_ == State::Prepare) { auto success = cb(); if (not success) { + SL_ERROR(logger_, "Preparation stage failed"); state_ = State::ShuttingDown; } } diff --git a/core/benchmark/block_execution_benchmark.cpp b/core/benchmark/block_execution_benchmark.cpp index 89b8db4aa1..c4f9e775c5 100644 --- a/core/benchmark/block_execution_benchmark.cpp +++ b/core/benchmark/block_execution_benchmark.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "blockchain/block_tree.hpp" #include "primitives/runtime_dispatch_info.hpp" #include "runtime/module_repository.hpp" @@ -64,6 +66,55 @@ static_assert(STR(CONCAT(2, 3)) == std::string_view("23")); } \ } while (false); +namespace { + + template + struct pretty_duration { + std::chrono::duration dur; + }; + + template + pretty_duration(std::chrono::duration) + -> pretty_duration; + + const char *suffix(unsigned denominator) { + switch (denominator) { + case 1: + return "s"; + case 1000: + return "ms"; + case 1'000'000: + return "us"; + case 1'000'000'000: + return "ns"; + default: + return "??"; + } + } + +} // namespace + +template +struct fmt::formatter> { + constexpr auto parse(format_parse_context &ctx) + -> format_parse_context::iterator { + return ctx.end(); + } + + auto format(const pretty_duration &p, format_context &ctx) const + -> format_context::iterator { + auto denominator = 1; + static_assert(Period::num == 1); + while (p.dur.count() / denominator > 1000 && denominator < Period::den) { + denominator *= 1000; + } + return fmt::format_to(ctx.out(), + "{:.2f} {}", + static_cast(p.dur.count()) / denominator, + suffix(Period::den / denominator)); + } +}; + namespace kagome::benchmark { using common::literals::operator""_hex2buf; @@ -288,28 +339,28 @@ namespace kagome::benchmark { SL_VERBOSE(logger_, "Block #{}, {} ns", blocks[block_i].header.number, - duration_ns.count()); + duration_ns); } duration_stat_it++; } for (auto &stat : duration_stats) { - fmt::print("Block #{}, min {} ns, avg {} ns, median {} ns, max {} ns\n", + fmt::print("Block #{}, min {}, avg {}, median {}, max {}\n", stat.getBlock().number, - stat.min().count(), - stat.avg().count(), - stat.median().count(), - stat.max().count()); + pretty_duration{stat.min()}, + pretty_duration{stat.avg()}, + pretty_duration{stat.median()}, + pretty_duration{stat.max()}); OUTCOME_TRY( block_weight_ns, getBlockWeightAsNanoseconds( *trie_storage_, blocks[stat.getBlock().number - config.start].header.state_root)); fmt::print( - "Block #{}: consumed {} ns out of declared {} ns on average. ({} " + "Block #{}: consumed {} out of declared {} on average. ({:.2f} " "%)\n", stat.getBlock().number, - stat.avg().count(), - block_weight_ns.count(), + pretty_duration{stat.avg()}, + pretty_duration{block_weight_ns}, (static_cast(stat.avg().count()) / static_cast(block_weight_ns.count())) * 100.0); diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index 8cea23b1f9..c9d84f8b5c 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -16,7 +16,6 @@ #include "consensus/babe/impl/babe_digests_util.hpp" #include "consensus/babe/is_primary.hpp" #include "crypto/blake2/blake2b.h" -#include "log/formatters/optional.hpp" #include "log/profiling_logger.hpp" #include "storage/database_error.hpp" #include "storage/trie_pruner/trie_pruner.hpp" diff --git a/core/common/buffer_or_view.hpp b/core/common/buffer_or_view.hpp index b8d1b71f0e..fc09220105 100644 --- a/core/common/buffer_or_view.hpp +++ b/core/common/buffer_or_view.hpp @@ -43,18 +43,18 @@ namespace kagome::common { /// Is buffer owned. bool isOwned() const { - if (variant.which() == 2) { + if (variant.index() == 2) { throw std::logic_error{"Tried to use moved BufferOrView"}; } - return variant.which() == 1; + return variant.index() == 1; } /// Get view. BufferView view() const { if (!isOwned()) { - return boost::get(variant); + return std::get(variant); } - return BufferView{boost::get(variant)}; + return BufferView{std::get(variant)}; } /// Get view. @@ -85,10 +85,10 @@ namespace kagome::common { /// Get mutable buffer reference. Copy once if view. Buffer &mut() { if (!isOwned()) { - auto view = boost::get(variant); + auto view = std::get(variant); variant = Buffer{view}; } - return boost::get(variant); + return std::get(variant); } /// Move buffer away. Copy once if view. @@ -99,7 +99,7 @@ namespace kagome::common { } private: - boost::variant variant; + std::variant variant; template > friend bool operator==(const BufferOrView &l, const T &r) { diff --git a/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp b/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp index 17d1ffd229..f835e94458 100644 --- a/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp +++ b/core/dispute_coordinator/impl/dispute_coordinator_impl.cpp @@ -2485,7 +2485,8 @@ namespace kagome::dispute { auto &apis = version.apis; - static const common::Hash64 parachain_host_api_hash = + // usage in lambda is not detected for some reason causing a warning + [[maybe_unused]] static const common::Hash64 parachain_host_api_hash = hasher_->blake2b_64(common::Buffer::fromString("ParachainHost")); auto it = std::find_if(apis.begin(), apis.end(), [](auto &api_version) { diff --git a/core/host_api/host_api.hpp b/core/host_api/host_api.hpp index 73faedbca6..b6ea04c7b3 100644 --- a/core/host_api/host_api.hpp +++ b/core/host_api/host_api.hpp @@ -493,7 +493,7 @@ namespace kagome::host_api { * Print a number * @param value - number to be printed */ - virtual void ext_misc_print_num_version_1(uint64_t value) const = 0; + virtual void ext_misc_print_num_version_1(int64_t value) const = 0; /** * Print a UTF-8-encoded string @@ -517,12 +517,12 @@ namespace kagome::host_api { ext_offchain_network_state_version_1() = 0; /// @copydoc OffchainExtension::ext_offchain_timestamp_version_1 - [[nodiscard]] virtual runtime::WasmU64 + [[nodiscard]] virtual runtime::WasmI64 ext_offchain_timestamp_version_1() = 0; /// @copydoc OffchainExtension::ext_offchain_sleep_until_version_1 virtual void ext_offchain_sleep_until_version_1( - runtime::WasmU64 deadline) = 0; + runtime::WasmI64 deadline) = 0; /// @copydoc OffchainExtension::ext_offchain_random_seed_version_1 [[nodiscard]] virtual runtime::WasmPointer @@ -722,7 +722,7 @@ namespace kagome::host_api { * @return a boolean equal to true if the key does exist, false if * otherwise. */ - virtual uint32_t ext_default_child_storage_exists_version_1( + virtual int32_t ext_default_child_storage_exists_version_1( runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const = 0; /** diff --git a/core/host_api/impl/child_storage_extension.cpp b/core/host_api/impl/child_storage_extension.cpp index 5ad3f4e338..4863d12e5a 100644 --- a/core/host_api/impl/child_storage_extension.cpp +++ b/core/host_api/impl/child_storage_extension.cpp @@ -9,10 +9,11 @@ #include #include +#include + #include "common/monadic_utils.hpp" #include "common/tagged.hpp" #include "host_api/impl/storage_util.hpp" -#include "log/formatters/optional.hpp" #include "log/trace_macros.hpp" #include "runtime/memory_provider.hpp" #include "runtime/ptr_size.hpp" @@ -365,7 +366,7 @@ namespace kagome::host_api { return memory.storeBuffer(scale::encode(res).value()); } - uint32_t ChildStorageExtension::ext_default_child_storage_exists_version_1( + int32_t ChildStorageExtension::ext_default_child_storage_exists_version_1( runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const { auto &memory = memory_provider_->getCurrentMemory()->get(); auto [child_key_buffer, key_buffer] = diff --git a/core/host_api/impl/child_storage_extension.hpp b/core/host_api/impl/child_storage_extension.hpp index ab2f330675..19e86c095b 100644 --- a/core/host_api/impl/child_storage_extension.hpp +++ b/core/host_api/impl/child_storage_extension.hpp @@ -95,7 +95,7 @@ namespace kagome::host_api { /** * @see HostApi::ext_default_child_storage_exists_version_1 */ - uint32_t ext_default_child_storage_exists_version_1( + int32_t ext_default_child_storage_exists_version_1( runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const; /** diff --git a/core/host_api/impl/host_api_impl.cpp b/core/host_api/impl/host_api_impl.cpp index c94f9b5900..4d4c611572 100644 --- a/core/host_api/impl/host_api_impl.cpp +++ b/core/host_api/impl/host_api_impl.cpp @@ -351,7 +351,7 @@ namespace kagome::host_api { return misc_ext_.ext_misc_print_hex_version_1(data); } - void HostApiImpl::ext_misc_print_num_version_1(uint64_t value) const { + void HostApiImpl::ext_misc_print_num_version_1(int64_t value) const { return misc_ext_.ext_misc_print_num_version_1(value); } @@ -399,12 +399,12 @@ namespace kagome::host_api { return offchain_ext_.ext_offchain_network_state_version_1(); } - runtime::WasmU64 HostApiImpl::ext_offchain_timestamp_version_1() { + runtime::WasmI64 HostApiImpl::ext_offchain_timestamp_version_1() { return offchain_ext_.ext_offchain_timestamp_version_1(); } void HostApiImpl::ext_offchain_sleep_until_version_1( - runtime::WasmU64 deadline) { + runtime::WasmI64 deadline) { return offchain_ext_.ext_offchain_sleep_until_version_1(deadline); } @@ -558,7 +558,7 @@ namespace kagome::host_api { child_storage_key, key, value_out, offset); } - uint32_t HostApiImpl::ext_default_child_storage_exists_version_1( + int32_t HostApiImpl::ext_default_child_storage_exists_version_1( runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const { return child_storage_ext_.ext_default_child_storage_exists_version_1( child_storage_key, key); diff --git a/core/host_api/impl/host_api_impl.hpp b/core/host_api/impl/host_api_impl.hpp index 6265d675d4..ab5a84fd38 100644 --- a/core/host_api/impl/host_api_impl.hpp +++ b/core/host_api/impl/host_api_impl.hpp @@ -240,7 +240,7 @@ namespace kagome::host_api { void ext_misc_print_hex_version_1(runtime::WasmSpan data) const override; - void ext_misc_print_num_version_1(uint64_t value) const override; + void ext_misc_print_num_version_1(int64_t value) const override; void ext_misc_print_utf8_version_1(runtime::WasmSpan data) const override; @@ -253,9 +253,9 @@ namespace kagome::host_api { runtime::WasmSpan ext_offchain_network_state_version_1() override; - runtime::WasmU64 ext_offchain_timestamp_version_1() override; + runtime::WasmI64 ext_offchain_timestamp_version_1() override; - void ext_offchain_sleep_until_version_1(runtime::WasmU64 deadline) override; + void ext_offchain_sleep_until_version_1(runtime::WasmI64 deadline) override; runtime::WasmPointer ext_offchain_random_seed_version_1() override; @@ -349,7 +349,7 @@ namespace kagome::host_api { runtime::WasmSpan value_out, runtime::WasmOffset offset) const override; - virtual uint32_t ext_default_child_storage_exists_version_1( + virtual int32_t ext_default_child_storage_exists_version_1( runtime::WasmSpan child_storage_key, runtime::WasmSpan key) const override; diff --git a/core/host_api/impl/memory_extension.cpp b/core/host_api/impl/memory_extension.cpp index 8197236780..9887200243 100644 --- a/core/host_api/impl/memory_extension.cpp +++ b/core/host_api/impl/memory_extension.cpp @@ -9,6 +9,7 @@ #include #include "host_api/impl/memory_extension.hpp" +#include "log/trace_macros.hpp" #include "runtime/memory_provider.hpp" namespace kagome::host_api { @@ -17,15 +18,21 @@ namespace kagome::host_api { : memory_provider_(std::move(memory_provider)), logger_{log::createLogger("MemoryExtension", "memory_extension")} { BOOST_ASSERT_MSG(memory_provider_ != nullptr, "memory provider is nullptr"); + SL_DEBUG(logger_, + "Memory extension {} initialized with memory provider {}", + fmt::ptr(this), fmt::ptr(memory_provider_)); } runtime::WasmPointer MemoryExtension::ext_allocator_malloc_version_1( runtime::WasmSize size) { - return memory_provider_->getCurrentMemory()->get().allocate(size); + auto res = memory_provider_->getCurrentMemory()->get().allocate(size); + SL_TRACE_FUNC_CALL(logger_, res, size); + return res; } void MemoryExtension::ext_allocator_free_version_1(runtime::WasmPointer ptr) { auto opt_size = memory_provider_->getCurrentMemory()->get().deallocate(ptr); + SL_TRACE_FUNC_CALL(logger_, ptr); if (not opt_size) { logger_->warn( "Ptr {} does not point to any memory chunk in wasm memory. Nothing " diff --git a/core/host_api/impl/misc_extension.cpp b/core/host_api/impl/misc_extension.cpp index d2d5633f2c..f37bb2afe0 100644 --- a/core/host_api/impl/misc_extension.cpp +++ b/core/host_api/impl/misc_extension.cpp @@ -11,7 +11,9 @@ #include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/core_api_factory.hpp" #include "runtime/memory_provider.hpp" -#include "runtime/module_repository.hpp" +#include "runtime/module.hpp" +#include "runtime/module_factory.hpp" +#include "runtime/module_instance.hpp" #include "runtime/runtime_api/core.hpp" #include "scale/scale.hpp" @@ -49,7 +51,8 @@ namespace kagome::host_api { return memory.storeBuffer(kErrorRes); } - auto core_api = core_factory_->make(hasher_, uncompressed_code.asVector()); + auto core_api = + core_factory_->make(hasher_, uncompressed_code.asVector()).value(); auto version_res = core_api->version(); SL_TRACE_FUNC_CALL(logger_, version_res.has_value(), data); @@ -76,7 +79,7 @@ namespace kagome::host_api { logger_->info("hex: {}", buf.toHex()); } - void MiscExtension::ext_misc_print_num_version_1(uint64_t value) const { + void MiscExtension::ext_misc_print_num_version_1(int64_t value) const { logger_->info("num: {}", value); } diff --git a/core/host_api/impl/misc_extension.hpp b/core/host_api/impl/misc_extension.hpp index f4913a7367..94ec3780b2 100644 --- a/core/host_api/impl/misc_extension.hpp +++ b/core/host_api/impl/misc_extension.hpp @@ -44,7 +44,7 @@ namespace kagome::host_api { void ext_misc_print_hex_version_1(runtime::WasmSpan data) const; - void ext_misc_print_num_version_1(uint64_t value) const; + void ext_misc_print_num_version_1(int64_t value) const; void ext_misc_print_utf8_version_1(runtime::WasmSpan data) const; diff --git a/core/host_api/impl/offchain_extension.cpp b/core/host_api/impl/offchain_extension.cpp index 01922368b4..9a383d7e68 100644 --- a/core/host_api/impl/offchain_extension.cpp +++ b/core/host_api/impl/offchain_extension.cpp @@ -83,14 +83,14 @@ namespace kagome::host_api { return memory.storeBuffer(scale::encode(result).value()); } - runtime::WasmU64 OffchainExtension::ext_offchain_timestamp_version_1() { + runtime::WasmI64 OffchainExtension::ext_offchain_timestamp_version_1() { auto worker = getWorker(); auto result = worker->timestamp(); return result; } void OffchainExtension::ext_offchain_sleep_until_version_1( - runtime::WasmU64 deadline) { + runtime::WasmI64 deadline) { auto worker = getWorker(); worker->sleepUntil(deadline); } diff --git a/core/host_api/impl/offchain_extension.hpp b/core/host_api/impl/offchain_extension.hpp index 0180172cf7..88395dbdc0 100644 --- a/core/host_api/impl/offchain_extension.hpp +++ b/core/host_api/impl/offchain_extension.hpp @@ -79,22 +79,22 @@ namespace kagome::host_api { /** * @brief Returns current timestamp * @code{.wasm} - * (func $ext_offchain_timestamp_version_1 (result u64)) + * (func $ext_offchain_timestamp_version_1 (result i64)) * @endcode - * @return an u64 integer indicating the current UNIX timestamp + * @return an i64 integer indicating the current UNIX timestamp * (milliseconds) */ - runtime::WasmU64 ext_offchain_timestamp_version_1(); + runtime::WasmI64 ext_offchain_timestamp_version_1(); /** * @brief Pause the execution until `deadline` is reached * @code{.wasm} - * (func $ext_offchain_sleep_until_version_1 (param $deadline u64)) + * (func $ext_offchain_sleep_until_version_1 (param $deadline i64)) * @endcode - * @param deadline an u64 integer indicating the current UNIX timestamp + * @param deadline an i64 integer indicating the current UNIX timestamp * (milliseconds) */ - void ext_offchain_sleep_until_version_1(runtime::WasmU64 deadline); + void ext_offchain_sleep_until_version_1(runtime::WasmI64 deadline); /** * @brief Generates a random seed. This is a truly random non deterministic diff --git a/core/host_api/impl/storage_extension.cpp b/core/host_api/impl/storage_extension.cpp index 4eaf0ddb85..9e1e6831f0 100644 --- a/core/host_api/impl/storage_extension.cpp +++ b/core/host_api/impl/storage_extension.cpp @@ -7,11 +7,13 @@ #include "host_api/impl/storage_extension.hpp" #include +#include + +#include #include "clock/impl/clock_impl.hpp" #include "common/monadic_utils.hpp" #include "host_api/impl/storage_util.hpp" -#include "log/formatters/optional.hpp" #include "log/trace_macros.hpp" #include "runtime/common/runtime_execution_error.hpp" #include "runtime/memory_provider.hpp" @@ -293,6 +295,7 @@ namespace kagome::host_api { void StorageExtension::ext_storage_start_transaction_version_1() { auto res = storage_provider_->startTransaction(); + SL_TRACE_VOID_FUNC_CALL(logger_); if (res.has_error()) { logger_->error("Storage transaction start has failed: {}", res.error()); throw std::runtime_error(res.error().message()); @@ -315,7 +318,7 @@ namespace kagome::host_api { auto res = storage_provider_->rollbackTransaction(); SL_TRACE_VOID_FUNC_CALL(logger_); if (res.has_error()) { - logger_->error("Storage transaction commit has failed: {}", res.error()); + logger_->error("Storage transaction rollback has failed: {}", res.error()); throw std::runtime_error(res.error().message()); } --transactions_; diff --git a/core/injector/CMakeLists.txt b/core/injector/CMakeLists.txt index 48f39564f5..4a23aec877 100644 --- a/core/injector/CMakeLists.txt +++ b/core/injector/CMakeLists.txt @@ -8,84 +8,80 @@ add_library(application_injector application_injector.cpp ) target_link_libraries(application_injector - beefy_api Boost::Boost.DI - mmr_api - runtime_wavm account_nonce_api address_publisher api app_config app_state_manager - authority_discovery_api application_modes assets - storage + authority_discovery_api babe_api - kagome_benchmarks - binaryen_executor_factory + beefy_api + binaryen_module_factory binaryen_runtime_external_interface binaryen_wasm_memory binaryen_wasm_memory_factory binaryen_wasm_module - module_repository - binaryen_module_factory bip39_provider + block_builder block_builder_api - consensus blockchain - clock chain_spec + clock + consensus core_api crypto_store + dispute_coordinator ecdsa_provider ed25519_provider + executor + fd_limit filesystem - transaction_pool + grandpa_api host_api_factory + kagome_benchmarks key_file_storage log_configurator metadata_api metrics metrics_watcher + mmr_api + module_repository + network + offchain_local_storage + offchain_persistent_storage + offchain_worker_api + offchain_worker_factory + offchain_worker_pool outcome p2p::p2p p2p::p2p_identify p2p::p2p_kademlia + p2p::p2p_loopback_stream p2p::p2p_ping parachain_host_api pbkdf2_provider - block_builder - runtime_upgrade_tracker - executor + recovery_mode runtime_properties_cache - executor + runtime_upgrade_tracker secp256k1_provider + session_keys_api soralog::fallback_configurator soralog::soralog sr25519_provider - network + storage storage_code_provider - telemetry tagged_transaction_queue_api + telemetry + timeline transaction_payment_api transaction_pool trie_storage_provider + validator_parachain vrf_provider waitable_timer - assets - grandpa_api - p2p::p2p_loopback_stream - offchain_worker_factory - offchain_worker_pool - offchain_worker_api - offchain_persistent_storage - offchain_local_storage - session_keys_api - validator_parachain - fd_limit - recovery_mode - dispute_coordinator - timeline + wasm_compiler ) kagome_clear_objects(application_injector) diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 5d71c60832..99f0e6fd61 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -133,9 +133,9 @@ #include "parachain/validator/impl/parachain_observer_impl.hpp" #include "parachain/validator/parachain_processor.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/binaryen/core_api_factory_impl.hpp" #include "runtime/binaryen/instance_environment_factory.hpp" #include "runtime/binaryen/module/module_factory_impl.hpp" +#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/common/module_repository_impl.hpp" #include "runtime/common/runtime_instances_pool.hpp" #include "runtime/common/runtime_properties_cache_impl.hpp" @@ -143,6 +143,7 @@ #include "runtime/common/storage_code_provider.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" #include "runtime/executor.hpp" +#include "runtime/module.hpp" #include "runtime/module_factory.hpp" #include "runtime/runtime_api/impl/account_nonce_api.hpp" #include "runtime/runtime_api/impl/authority_discovery_api.hpp" @@ -158,8 +159,16 @@ #include "runtime/runtime_api/impl/session_keys_api.hpp" #include "runtime/runtime_api/impl/tagged_transaction_queue.hpp" #include "runtime/runtime_api/impl/transaction_payment_api.hpp" + +#if KAGOME_WASM_COMPILER_WASM_EDGE == 1 + +#include "runtime/wasm_edge/module_factory_impl.hpp" + +#endif + +#if KAGOME_WASM_COMPILER_WAVM == 1 + #include "runtime/wavm/compartment_wrapper.hpp" -#include "runtime/wavm/core_api_factory_impl.hpp" #include "runtime/wavm/instance_environment_factory.hpp" #include "runtime/wavm/intrinsics/intrinsic_functions.hpp" #include "runtime/wavm/intrinsics/intrinsic_module.hpp" @@ -168,6 +177,9 @@ #include "runtime/wavm/module.hpp" #include "runtime/wavm/module_cache.hpp" #include "runtime/wavm/module_factory_impl.hpp" + +#endif + #include "storage/changes_trie/impl/storage_changes_tracker_impl.hpp" #include "storage/rocksdb/rocksdb.hpp" #include "storage/spaces.hpp" @@ -202,9 +214,8 @@ namespace { sptr get_trie_storage_backend( sptr spaced_storage) { - auto storage = spaced_storage->getSpace(storage::Space::kTrieNode); auto backend = - std::make_shared(storage); + std::make_shared(spaced_storage); return backend; } @@ -351,10 +362,9 @@ namespace { } template - auto makeWavmInjector( - application::AppConfiguration::RuntimeExecutionMethod method, - Ts &&...args) { + auto makeWavmInjector(Ts &&...args) { return di::make_injector( +#if KAGOME_WASM_COMPILER_WAVM == 1 bind_by_lambda([](const auto &injector) { return std::make_shared( @@ -377,14 +387,36 @@ namespace { .template create>(); return module->instantiate(); }), + bind_by_lambda([](const auto + &injector) { + std::optional> + module_cache_opt; + auto &app_config = + injector.template create(); + if (app_config.useWavmCache()) { + module_cache_opt = std::make_shared( + injector.template create>(), + app_config.runtimeCacheDirPath()); + } + return std::make_shared( + injector + .template create>(), + injector.template create>(), + injector.template create>(), + injector.template create>(), + injector.template create>(), + injector.template create>(), + injector.template create>(), + module_cache_opt, + injector.template create>()); + }), di::bind.template to(), +#endif std::forward(args)...); } template - auto makeBinaryenInjector( - application::AppConfiguration::RuntimeExecutionMethod method, - Ts &&...args) { + auto makeBinaryenInjector(Ts &&...args) { return di::make_injector( bind_by_lambda( [](const auto &injector) { @@ -401,9 +433,18 @@ namespace { std::forward(args)...); } + template + auto makeWasmEdgeInjector(Ts &&...args) { + return di::make_injector( +#if KAGOME_WASM_COMPILER_WASM_EDGE == 1 + di::bind.template to(), +#endif + std::forward(args)...); + } + template auto choose_runtime_implementation( const Injector &injector, @@ -413,10 +454,10 @@ namespace { switch (method) { case RuntimeExecutionMethod::Interpret: return std::static_pointer_cast( - injector.template create>()); + injector.template create>()); case RuntimeExecutionMethod::Compile: return std::static_pointer_cast( - injector.template create>()); + injector.template create>()); } throw std::runtime_error("Unknown runtime execution method"); } @@ -446,6 +487,16 @@ namespace { std::move(res.value())); } +#if KAGOME_WASM_COMPILER_WAVM == 1 + + using ModuleFactory = runtime::wavm::ModuleFactoryImpl; + +#elif KAGOME_WASM_COMPILER_WASM_EDGE == 1 + + using ModuleFactory = runtime::wasm_edge::ModuleFactoryImpl; + +#endif + template auto makeRuntimeInjector( application::AppConfiguration::RuntimeExecutionMethod method, @@ -460,8 +511,8 @@ namespace { return injector .template create>(); }), - makeWavmInjector(method), - makeBinaryenInjector(method), + makeBinaryenInjector(), + makeWavmInjector(), bind_by_lambda([](const auto &injector) { auto module_factory = injector.template create>(); @@ -469,38 +520,21 @@ namespace { module_factory); }), di::bind.template to(), - bind_by_lambda([method](const auto &injector) { - return choose_runtime_implementation< - runtime::CoreApiFactory, - runtime::binaryen::CoreApiFactoryImpl, - runtime::wavm::CoreApiFactoryImpl>(injector, method); - }), - bind_by_lambda([](const auto - &injector) { - std::optional> - module_cache_opt; - auto &app_config = - injector.template create(); - if (app_config.useWavmCache()) { - module_cache_opt = std::make_shared( - injector.template create>(), - app_config.runtimeCacheDirPath()); - } - return std::make_shared( - injector - .template create>(), - injector.template create>(), - injector.template create< - sptr>(), - injector.template create>(), - module_cache_opt, - injector.template create>()); - }), - bind_by_lambda([method](const auto &injector) { - return choose_runtime_implementation< - runtime::ModuleFactory, - runtime::binaryen::ModuleFactoryImpl, - runtime::wavm::ModuleFactoryImpl>(injector, method); + di::bind.template to(), + bind_by_lambda( + [method](const auto &injector) -> sptr { + return choose_runtime_implementation< + runtime::ModuleFactory, + runtime::binaryen::ModuleFactoryImpl, + ModuleFactory>(injector, method); + }), + bind_by_lambda([](const auto &injector) + -> sptr { + auto ctx_factory = + injector.template create>(); + auto cache = + injector.template create>(); + return std::make_shared(ctx_factory, cache); }), di::bind.template to(), di::bind.template to(), @@ -567,8 +601,7 @@ namespace { }; // clang-format off - return di:: - make_injector( + return di::make_injector( // bind configs useConfig(rpc_thread_pool_config), useConfig(ws_config), @@ -675,22 +708,24 @@ namespace { return get_rocks_db(config, chain_spec); }), bind_by_lambda([](const auto &injector) { - auto root = + auto module_factory = injector.template create>(); + auto root_res = injector::calculate_genesis_state( injector .template create(), - injector - .template create(), + *module_factory, injector .template create(), injector.template create< - sptr>()) - .value(); + sptr>()); + if (!root_res) { + throw std::runtime_error{fmt::format("Failed to calculate genesis state: {}", root_res.error())}; + } const auto &hasher = injector.template create>(); const auto &storage = injector.template create>(); - return blockchain::BlockStorageImpl::create(root, storage, hasher) + return blockchain::BlockStorageImpl::create(root_res.value(), storage, hasher) .value(); }), di::bind.template to(), @@ -748,7 +783,9 @@ namespace { [](const auto &injector) { auto storage = injector.template create>(); - return get_trie_storage_backend(storage); + return get_trie_storage_backend( + storage + ); }), bind_by_lambda([](const auto &injector) { diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index 86033cc5c3..77c3ae78fe 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -14,6 +14,7 @@ #include "storage/trie_pruner/trie_pruner.hpp" namespace kagome::injector { + inline outcome::result calculate_genesis_state( const application::ChainSpec &chain_spec, const runtime::ModuleFactory &module_factory, diff --git a/core/log/configurator.cpp b/core/log/configurator.cpp index e0e0055147..121830c549 100644 --- a/core/log/configurator.cpp +++ b/core/log/configurator.cpp @@ -82,6 +82,7 @@ namespace kagome::log { - name: runtime_cache - name: binaryen - name: wavm + - name: wasmedge - name: metrics - name: telemetry - name: network diff --git a/core/log/formatters/optional.hpp b/core/log/formatters/optional.hpp deleted file mode 100644 index 4519110c94..0000000000 --- a/core/log/formatters/optional.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include -#include - -template -struct fmt::formatter> { - // Parses format specifications. Must be empty - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - // Parse the presentation format and store it in the formatter: - auto it = ctx.begin(), end = ctx.end(); - - // Check if reached the end of the range: - if (it != end && *it != '}') { - throw format_error("invalid format"); - } - - // Return an iterator past the end of the parsed range: - return it; - } - - // Formats the optional value - template - auto format(const std::optional &opt, FormatContext &ctx) const - -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - if (opt.has_value()) { - return fmt::format_to(ctx.out(), "{}", opt.value()); - } else { - static constexpr string_view message(""); - return std::copy(std::begin(message), std::end(message), ctx.out()); - } - } -}; - -template <> -struct fmt::formatter { - // Parses format specifications. Must be empty - constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { - // Parse the presentation format and store it in the formatter: - auto it = ctx.begin(), end = ctx.end(); - - // Check if reached the end of the range: - if (it != end && *it != '}') { - throw format_error("invalid format"); - } - - // Return an iterator past the end of the parsed range: - return it; - } - - // Formats the optional value - template - auto format(const std::nullopt_t &, FormatContext &ctx) const - -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - static constexpr string_view message(""); - return std::copy(std::begin(message), std::end(message), ctx.out()); - } -}; diff --git a/core/log/trace_macros.hpp b/core/log/trace_macros.hpp index baf1db44e3..47a22b9ae6 100644 --- a/core/log/trace_macros.hpp +++ b/core/log/trace_macros.hpp @@ -16,6 +16,7 @@ namespace kagome::log { template void trace_function_call(const Logger &logger, + const void *caller, std::string_view func_name, Ret &&ret, Args &&...args) { @@ -25,19 +26,23 @@ namespace kagome::log { (fmt::format_to( std::back_inserter(ss), "{}, ", std::forward(args)), ...); - logger->trace("call '{}', args: {} -> ret: {}", + logger->trace("call '{}' from {}, args: {} -> ret: {}", func_name, + fmt::ptr(caller), ss, std::forward(ret)); } else { - logger->trace( - "call '{}' -> ret: {}", func_name, std::forward(ret)); + logger->trace("call '{}' from {} -> ret: {}", + func_name, + fmt::ptr(caller), + std::forward(ret)); } } } template void trace_void_function_call(const Logger &logger, + const void *caller, std::string_view func_name, Args &&...args) { if (logger->level() >= Level::TRACE) { @@ -60,12 +65,19 @@ namespace kagome::log { #else -#define SL_TRACE_FUNC_CALL(logger, ret, ...) \ - ::kagome::log::trace_function_call( \ - (logger), __FUNCTION__, (ret), ##__VA_ARGS__) +#define SL_TRACE_FUNC_CALL(logger, ret, ...) \ + ::kagome::log::trace_function_call((logger), \ + reinterpret_cast(this), \ + __FUNCTION__, \ + (ret), \ + ##__VA_ARGS__) #define SL_TRACE_VOID_FUNC_CALL(logger, ...) \ - ::kagome::log::trace_void_function_call((logger), __FUNCTION__, ##__VA_ARGS__) + ::kagome::log::trace_void_function_call( \ + (logger), \ + reinterpret_cast(this), \ + __FUNCTION__, \ + ##__VA_ARGS__) #endif diff --git a/core/network/CMakeLists.txt b/core/network/CMakeLists.txt index 9f789cf101..fe70c6d434 100644 --- a/core/network/CMakeLists.txt +++ b/core/network/CMakeLists.txt @@ -56,6 +56,7 @@ target_link_libraries(network blob node_api_proto p2p::p2p_loopback_stream + trie_storage_provider ) +kagome_install(network) kagome_clear_objects(network) -kagome_install(network) \ No newline at end of file diff --git a/core/network/impl/protocols/light.cpp b/core/network/impl/protocols/light.cpp index 79d0081960..0aeca423e8 100644 --- a/core/network/impl/protocols/light.cpp +++ b/core/network/impl/protocols/light.cpp @@ -21,8 +21,7 @@ namespace kagome::network { std::shared_ptr repository, std::shared_ptr storage, std::shared_ptr module_repo, - std::shared_ptr executor, - std::shared_ptr ctx_factory) + std::shared_ptr executor) : RequestResponseProtocolType{ kName, host, @@ -32,8 +31,7 @@ namespace kagome::network { repository_{std::move(repository)}, storage_{std::move(storage)}, module_repo_{std::move(module_repo)}, - executor_{std::move(executor)}, - ctx_factory_(std::move(ctx_factory)) {} + executor_{std::move(executor)} {} std::optional> LightProtocol::onRxRequest(RequestType req, std::shared_ptr) { @@ -49,10 +47,11 @@ namespace kagome::network { header.state_root)); OUTCOME_TRY( ctx, - ctx_factory_->fromBatch( + executor_->ctx().fromBatch( instance, std::shared_ptr{std::move(batch)})); - OUTCOME_TRY(executor_->callWithCtx(ctx, call->method, call->args)); + OUTCOME_TRY(ctx.module_instance->callExportFunction( + ctx, call->method, call->args)); } else { auto &read = boost::get(req.op); runtime::TrieStorageProviderImpl provider{storage_, nullptr}; diff --git a/core/network/impl/protocols/light.hpp b/core/network/impl/protocols/light.hpp index 91fadda415..cd2ab4b3e3 100644 --- a/core/network/impl/protocols/light.hpp +++ b/core/network/impl/protocols/light.hpp @@ -49,8 +49,7 @@ namespace kagome::network { std::shared_ptr repository, std::shared_ptr storage, std::shared_ptr module_repo, - std::shared_ptr executor, - std::shared_ptr ctx_factory); + std::shared_ptr executor); std::optional> onRxRequest( RequestType req, std::shared_ptr) override; @@ -62,6 +61,5 @@ namespace kagome::network { std::shared_ptr storage_; std::shared_ptr module_repo_; std::shared_ptr executor_; - std::shared_ptr ctx_factory_; }; } // namespace kagome::network diff --git a/core/network/impl/state_sync_request_flow.cpp b/core/network/impl/state_sync_request_flow.cpp index f3103f8e7c..b7f1f0a1f9 100644 --- a/core/network/impl/state_sync_request_flow.cpp +++ b/core/network/impl/state_sync_request_flow.cpp @@ -14,10 +14,10 @@ namespace kagome::network { StateSyncRequestFlow::StateSyncRequestFlow( - std::shared_ptr db, + std::shared_ptr node_db, const primitives::BlockInfo &block_info, const primitives::BlockHeader &block) - : db_{std::move(db)}, + : node_db_{std::move(node_db)}, block_info_{block_info}, block_{block}, log_{log::createLogger("StateSync")} { @@ -103,7 +103,7 @@ namespace kagome::network { if (it == nodes.end()) { return outcome::success(); } - OUTCOME_TRY(db_->put(it->first, std::move(it->second.first))); + OUTCOME_TRY(node_db_->put(it->first, std::move(it->second.first))); known_.emplace(it->first); } for (level.branchInit(); not level.branch_end; level.branchNext()) { @@ -119,7 +119,7 @@ namespace kagome::network { } if (level.branch_end) { auto &t = level.stack.back().t; - OUTCOME_TRY(db_->put(t.hash, std::move(t.encoded))); + OUTCOME_TRY(node_db_->put(t.hash, std::move(t.encoded))); known_.emplace(t.hash); level.pop(); if (not level.stack.empty()) { @@ -142,7 +142,9 @@ namespace kagome::network { if (known_.find(hash) != known_.end()) { return true; } - if (auto r = db_->contains(hash); r and r.value()) { + if (auto node_res = node_db_->contains(hash), + value_res = node_db_->contains(hash); + (node_res and node_res.value()) or (value_res and value_res.value())) { known_.emplace(hash); return true; } diff --git a/core/network/impl/state_sync_request_flow.hpp b/core/network/impl/state_sync_request_flow.hpp index b2d5f01b15..d01179c37d 100644 --- a/core/network/impl/state_sync_request_flow.hpp +++ b/core/network/impl/state_sync_request_flow.hpp @@ -33,9 +33,10 @@ namespace kagome::network { using Level = storage::trie::RawCursor; - StateSyncRequestFlow(std::shared_ptr db, - const primitives::BlockInfo &block_info, - const primitives::BlockHeader &block); + StateSyncRequestFlow( + std::shared_ptr node_db, + const primitives::BlockInfo &block_info, + const primitives::BlockHeader &block); auto &blockInfo() const { return block_info_; @@ -54,7 +55,7 @@ namespace kagome::network { private: bool isKnown(const common::Hash256 &hash); - std::shared_ptr db_; + std::shared_ptr node_db_; primitives::BlockInfo block_info_; primitives::BlockHeader block_; diff --git a/core/network/impl/stream_engine.cpp b/core/network/impl/stream_engine.cpp index b1a118f605..0d14ccbc9a 100644 --- a/core/network/impl/stream_engine.cpp +++ b/core/network/impl/stream_engine.cpp @@ -5,7 +5,6 @@ */ #include "network/impl/stream_engine.hpp" -#include "log/formatters/optional.hpp" namespace kagome::network { diff --git a/core/network/impl/synchronizer_impl.cpp b/core/network/impl/synchronizer_impl.cpp index 5817cb3aaf..ddc44a2bc6 100644 --- a/core/network/impl/synchronizer_impl.cpp +++ b/core/network/impl/synchronizer_impl.cpp @@ -83,7 +83,7 @@ namespace kagome::network { std::shared_ptr block_tree, std::shared_ptr block_appender, std::shared_ptr block_executor, - std::shared_ptr trie_db, + std::shared_ptr trie_node_db, std::shared_ptr storage, std::shared_ptr trie_pruner, std::shared_ptr router, @@ -96,7 +96,7 @@ namespace kagome::network { block_tree_(std::move(block_tree)), block_appender_(std::move(block_appender)), block_executor_(std::move(block_executor)), - trie_db_(std::move(trie_db)), + trie_node_db_(std::move(trie_node_db)), storage_(std::move(storage)), trie_pruner_(std::move(trie_pruner)), router_(std::move(router)), @@ -108,7 +108,7 @@ namespace kagome::network { BOOST_ASSERT(app_state_manager_); BOOST_ASSERT(block_tree_); BOOST_ASSERT(block_executor_); - BOOST_ASSERT(trie_db_); + BOOST_ASSERT(trie_node_db_); BOOST_ASSERT(storage_); BOOST_ASSERT(trie_pruner_); BOOST_ASSERT(router_); @@ -584,7 +584,7 @@ namespace kagome::network { if (auto r = recent_requests_.emplace( std::make_tuple(peer_id, request_fingerprint), "load blocks"); not r.second) { - SL_ERROR(log_, + SL_VERBOSE(log_, "Can't load blocks from {} beginning block {}: {}", peer_id, from, @@ -610,7 +610,7 @@ namespace kagome::network { // Any error interrupts loading of blocks if (response_res.has_error()) { - SL_ERROR(self->log_, + SL_VERBOSE(self->log_, "Can't load blocks from {} beginning block {}: {}", peer_id, from, @@ -625,7 +625,7 @@ namespace kagome::network { // No block in response is abnormal situation. // At least one starting block should be returned as existing if (blocks.empty()) { - SL_ERROR(self->log_, + SL_VERBOSE(self->log_, "Can't load blocks from {} beginning block {}: " "Response does not have any blocks", peer_id, @@ -648,7 +648,7 @@ namespace kagome::network { for (auto &block : blocks) { // Check if header is provided if (not block.header.has_value()) { - SL_ERROR(self->log_, + SL_VERBOSE(self->log_, "Can't load blocks from {} starting from block {}: " "Received block without header", peer_id, @@ -660,7 +660,7 @@ namespace kagome::network { } // Check if body is provided if (not block.header.has_value()) { - SL_ERROR(self->log_, + SL_VERBOSE(self->log_, "Can't load blocks from {} starting from block {}: " "Received block without body", peer_id, @@ -679,7 +679,7 @@ namespace kagome::network { if (last_finalized_block.number >= header.number) { if (last_finalized_block.number == header.number) { if (last_finalized_block.hash != block.hash) { - SL_ERROR(self->log_, + SL_VERBOSE(self->log_, "Can't load blocks from {} starting from block {}: " "Received discarded block {}", peer_id, @@ -992,7 +992,7 @@ namespace kagome::network { return; } if (not state_sync_flow_ or state_sync_flow_->blockInfo() != block) { - state_sync_flow_.emplace(trie_db_, block, header); + state_sync_flow_.emplace(trie_node_db_, block, header); } state_sync_.emplace(StateSync{ peer_id, diff --git a/core/network/impl/synchronizer_impl.hpp b/core/network/impl/synchronizer_impl.hpp index cb0f853a57..db77553ea1 100644 --- a/core/network/impl/synchronizer_impl.hpp +++ b/core/network/impl/synchronizer_impl.hpp @@ -93,7 +93,7 @@ namespace kagome::network { std::shared_ptr block_tree, std::shared_ptr block_appender, std::shared_ptr block_executor, - std::shared_ptr trie_db, + std::shared_ptr trie_node_db, std::shared_ptr storage, std::shared_ptr trie_pruner, std::shared_ptr router, @@ -214,7 +214,7 @@ namespace kagome::network { std::shared_ptr block_tree_; std::shared_ptr block_appender_; std::shared_ptr block_executor_; - std::shared_ptr trie_db_; + std::shared_ptr trie_node_db_; std::shared_ptr storage_; std::shared_ptr trie_pruner_; std::shared_ptr router_; diff --git a/core/offchain/impl/CMakeLists.txt b/core/offchain/impl/CMakeLists.txt index d7eaee7172..626c6b776f 100644 --- a/core/offchain/impl/CMakeLists.txt +++ b/core/offchain/impl/CMakeLists.txt @@ -38,6 +38,7 @@ add_library(offchain_worker ) target_link_libraries(offchain_worker http_request + offchain_local_storage ) kagome_install(offchain_worker) diff --git a/core/offchain/impl/offchain_local_storage.cpp b/core/offchain/impl/offchain_local_storage.cpp index fc4665e60f..6879b2415f 100644 --- a/core/offchain/impl/offchain_local_storage.cpp +++ b/core/offchain/impl/offchain_local_storage.cpp @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "offchain_local_storage.hpp" +#include "offchain/impl/offchain_local_storage.hpp" #include "storage/database_error.hpp" #include "storage/predefined_keys.hpp" diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 5f6b6c95dc..304039d2b6 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -4,9 +4,11 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include #include +#include +#include + #include "clock/impl/basic_waitable_timer.hpp" #include "common/visitor.hpp" #include "consensus/babe/babe_config_repository.hpp" @@ -14,7 +16,6 @@ #include "crypto/crypto_store.hpp" #include "crypto/hasher.hpp" #include "crypto/sr25519_provider.hpp" -#include "log/formatters/optional.hpp" #include "network/peer_manager.hpp" #include "network/router.hpp" #include "parachain/approval/approval.hpp" diff --git a/core/parachain/pvf/pvf_impl.cpp b/core/parachain/pvf/pvf_impl.cpp index b1c326d2cf..746b761b22 100644 --- a/core/parachain/pvf/pvf_impl.cpp +++ b/core/parachain/pvf/pvf_impl.cpp @@ -271,7 +271,7 @@ namespace kagome::parachain { OUTCOME_TRY(ctx, ctx_factory_->ephemeral( instance, storage::trie::kEmptyRootHash, executor_params)); - return executor_->decodedCallWithCtx( + return executor_->call( ctx, "validate_block", params); } diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index 9baf31de50..5744e5c45d 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -8,13 +8,14 @@ #include #include +#include +#include #include #include "crypto/hasher.hpp" #include "crypto/sr25519_provider.hpp" #include "dispute_coordinator/impl/runtime_info.hpp" -#include "log/formatters/optional.hpp" #include "network/common.hpp" #include "network/peer_manager.hpp" #include "network/router.hpp" diff --git a/core/runtime/CMakeLists.txt b/core/runtime/CMakeLists.txt index 1ba9aa3534..99d8475d31 100644 --- a/core/runtime/CMakeLists.txt +++ b/core/runtime/CMakeLists.txt @@ -6,6 +6,25 @@ add_subdirectory(common) add_subdirectory(binaryen) -add_subdirectory(wavm) + +add_library(wasm_compiler INTERFACE) + +if ("${WASM_COMPILER}" STREQUAL "WAVM") + add_subdirectory(wavm) + target_link_libraries(wasm_compiler INTERFACE runtime_wavm) + target_compile_definitions(wasm_compiler INTERFACE + KAGOME_WASM_COMPILER_WAVM=1 + KAGOME_WASM_COMPILER_WASM_EDGE=0 + ) +elseif ("${WASM_COMPILER}" STREQUAL "WasmEdge") + add_subdirectory(wasm_edge) + target_link_libraries(wasm_compiler INTERFACE runtime_wasm_edge) + target_compile_definitions(wasm_compiler INTERFACE + KAGOME_WASM_COMPILER_WAVM=0 + KAGOME_WASM_COMPILER_WASM_EDGE=1 + ) +else() + fatal_error("Unknown WASM_COMPILER: ${WASM_COMPILER}") +endif() add_subdirectory(runtime_api/impl) diff --git a/core/runtime/binaryen/CMakeLists.txt b/core/runtime/binaryen/CMakeLists.txt index a1569d9c01..3b0a04a87f 100644 --- a/core/runtime/binaryen/CMakeLists.txt +++ b/core/runtime/binaryen/CMakeLists.txt @@ -51,22 +51,15 @@ target_link_libraries(binaryen_wasm_module ) kagome_install(binaryen_wasm_module) -add_library(binaryen_executor_factory - core_api_factory_impl.cpp - ) -target_link_libraries(binaryen_executor_factory - constant_code_provider - binaryen_memory_provider - core_api - ) -kagome_install(binaryen_executor_factory) - add_library(binaryen_instance_environment_factory instance_environment_factory.cpp ) target_link_libraries(binaryen_instance_environment_factory - binaryen_executor_factory trie_storage_provider + binaryen_wasm_memory_factory + core_api_factory + binaryen_memory_provider + binaryen::binaryen ) kagome_install(binaryen_instance_environment_factory) diff --git a/core/runtime/binaryen/binaryen_memory_provider.hpp b/core/runtime/binaryen/binaryen_memory_provider.hpp index 25c484f5f9..dcebd306df 100644 --- a/core/runtime/binaryen/binaryen_memory_provider.hpp +++ b/core/runtime/binaryen/binaryen_memory_provider.hpp @@ -24,6 +24,8 @@ namespace kagome::runtime::binaryen { BinaryenMemoryProvider( std::shared_ptr memory_factory); + ~BinaryenMemoryProvider() override = default; + std::optional> getCurrentMemory() const override; diff --git a/core/runtime/binaryen/core_api_factory_impl.cpp b/core/runtime/binaryen/core_api_factory_impl.cpp index 71ea9908a3..e69de29bb2 100644 --- a/core/runtime/binaryen/core_api_factory_impl.cpp +++ b/core/runtime/binaryen/core_api_factory_impl.cpp @@ -1,78 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "runtime/binaryen/core_api_factory_impl.hpp" - -#include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/binaryen/instance_environment_factory.hpp" -#include "runtime/binaryen/module/module_impl.hpp" -#include "runtime/common/constant_code_provider.hpp" -#include "runtime/common/runtime_properties_cache_impl.hpp" -#include "runtime/common/trie_storage_provider_impl.hpp" -#include "runtime/executor.hpp" -#include "runtime/module_repository.hpp" -#include "runtime/runtime_api/impl/core.hpp" - -namespace kagome::runtime::binaryen { - - class OneModuleRepository final : public ModuleRepository { - public: - OneModuleRepository( - const std::vector &code, - std::shared_ptr env_factory, - const common::Hash256 &code_hash) - : env_factory_{std::move(env_factory)}, - code_{code}, - code_hash_(code_hash) { - BOOST_ASSERT(env_factory_); - } - - outcome::result> getInstanceAt( - const primitives::BlockInfo &, - const storage::trie::RootHash &) override { - if (instance_ == nullptr) { - auto module_res = - ModuleImpl::createFromCode(code_, env_factory_, code_hash_); - if (!module_res) return make_error_code(module_res.error()); - auto inst = module_res.value()->instantiate(); - instance_ = std::move(inst); - } - return instance_; - } - - private: - std::shared_ptr instance_; - std::shared_ptr env_factory_; - const std::vector &code_; - const common::Hash256 code_hash_; - }; - - CoreApiFactoryImpl::CoreApiFactoryImpl( - std::shared_ptr instance_env_factory, - std::shared_ptr header_repo, - std::shared_ptr cache) - : instance_env_factory_{std::move(instance_env_factory)}, - header_repo_{std::move(header_repo)}, - cache_(std::move(cache)) { - BOOST_ASSERT(instance_env_factory_ != nullptr); - BOOST_ASSERT(header_repo_ != nullptr); - BOOST_ASSERT(cache_ != nullptr); - } - - std::unique_ptr CoreApiFactoryImpl::make( - std::shared_ptr hasher, - const std::vector &runtime_code) const { - auto code_hash = hasher->sha2_256(runtime_code); - auto ctx_factory = std::make_shared( - std::make_shared( - runtime_code, instance_env_factory_, code_hash), - header_repo_); - auto executor = std::make_unique(ctx_factory, cache_); - return std::make_unique( - std::move(executor), ctx_factory, header_repo_, nullptr); - } - -} // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/core_api_factory_impl.hpp b/core/runtime/binaryen/core_api_factory_impl.hpp deleted file mode 100644 index 0f80cc5e05..0000000000 --- a/core/runtime/binaryen/core_api_factory_impl.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "runtime/core_api_factory.hpp" -#include "runtime/runtime_api/core.hpp" - -namespace kagome::storage::trie { - class TrieStorage; -} - -namespace kagome::blockchain { - class BlockHeaderRepository; -} - -namespace kagome::runtime { - class TrieStorageProvider; - class Memory; - class RuntimeEnvironmentFactory; - class RuntimePropertiesCache; -} // namespace kagome::runtime - -namespace kagome::runtime::binaryen { - - class InstanceEnvironmentFactory; - class BinaryenMemoryFactory; - - class CoreApiFactoryImpl final - : public runtime::CoreApiFactory, - public std::enable_shared_from_this { - public: - CoreApiFactoryImpl( - std::shared_ptr instance_env_factory, - std::shared_ptr header_repo, - std::shared_ptr cache); - - std::unique_ptr make( - std::shared_ptr hasher, - const std::vector &runtime_code) const override; - - private: - std::shared_ptr instance_env_factory_; - std::shared_ptr header_repo_; - std::shared_ptr cache_; - }; - -} // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/instance_environment_factory.cpp b/core/runtime/binaryen/instance_environment_factory.cpp index 35b298cdfa..ac3f962cab 100644 --- a/core/runtime/binaryen/instance_environment_factory.cpp +++ b/core/runtime/binaryen/instance_environment_factory.cpp @@ -8,7 +8,7 @@ #include "host_api/host_api_factory.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/binaryen/core_api_factory_impl.hpp" +#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" namespace kagome::runtime::binaryen { @@ -16,29 +16,23 @@ namespace kagome::runtime::binaryen { InstanceEnvironmentFactory::InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, - std::shared_ptr host_api_factory, - std::shared_ptr block_header_repo, - std::shared_ptr cache) + std::shared_ptr host_api_factory) : storage_{std::move(storage)}, serializer_{std::move(serializer)}, - host_api_factory_{std::move(host_api_factory)}, - block_header_repo_{std::move(block_header_repo)}, - cache_(std::move(cache)) { + host_api_factory_{std::move(host_api_factory)} { BOOST_ASSERT(storage_); BOOST_ASSERT(serializer_); BOOST_ASSERT(host_api_factory_); - BOOST_ASSERT(block_header_repo_); - BOOST_ASSERT(cache_); } - BinaryenInstanceEnvironment InstanceEnvironmentFactory::make() const { + BinaryenInstanceEnvironment InstanceEnvironmentFactory::make( + std::shared_ptr module_factory) const { auto memory_factory = std::make_shared(); auto new_memory_provider = std::make_shared(memory_factory); auto new_storage_provider = std::make_shared(storage_, serializer_); - auto core_factory = std::make_shared( - shared_from_this(), block_header_repo_, cache_); + auto core_factory = std::make_shared(module_factory); auto host_api = std::shared_ptr(host_api_factory_->make( core_factory, new_memory_provider, new_storage_provider)); auto rei = std::make_shared(host_api); diff --git a/core/runtime/binaryen/instance_environment_factory.hpp b/core/runtime/binaryen/instance_environment_factory.hpp index ed24924af3..e9c9ef527a 100644 --- a/core/runtime/binaryen/instance_environment_factory.hpp +++ b/core/runtime/binaryen/instance_environment_factory.hpp @@ -17,12 +17,8 @@ namespace kagome::host_api { class HostApiFactory; } -namespace kagome::blockchain { - class BlockHeaderRepository; -} - namespace kagome::runtime { - class RuntimePropertiesCache; + class ModuleFactory; } namespace kagome::runtime::binaryen { @@ -40,18 +36,15 @@ namespace kagome::runtime::binaryen { InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, - std::shared_ptr host_api_factory, - std::shared_ptr block_header_repo, - std::shared_ptr cache); + std::shared_ptr host_api_factory); - [[nodiscard]] BinaryenInstanceEnvironment make() const; + [[nodiscard]] BinaryenInstanceEnvironment make( + std::shared_ptr module_factory) const; private: std::shared_ptr storage_; std::shared_ptr serializer_; std::shared_ptr host_api_factory_; - std::shared_ptr block_header_repo_; - std::shared_ptr cache_; }; } // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/memory_impl.cpp b/core/runtime/binaryen/memory_impl.cpp index b1e7b5f616..6d9cc8466c 100644 --- a/core/runtime/binaryen/memory_impl.cpp +++ b/core/runtime/binaryen/memory_impl.cpp @@ -77,6 +77,7 @@ namespace kagome::runtime::binaryen { common::BufferView MemoryImpl::loadN(kagome::runtime::WasmPointer addr, kagome::runtime::WasmSize n) const { BOOST_ASSERT(size() > addr and size() - addr >= n); + SL_TRACE(logger_, "load buffer with size {} at {}", n, addr); return common::BufferView{memory_->getBuffer(addr, n)}; } @@ -88,6 +89,7 @@ namespace kagome::runtime::binaryen { for (auto i = addr; i < addr + length; i++) { res.push_back(static_cast(memory_->get(i))); } + SL_TRACE(logger_, "load buffer with size {} at {}", length, addr); return res; } @@ -121,6 +123,7 @@ namespace kagome::runtime::binaryen { common::BufferView value) { BOOST_ASSERT((allocator_->checkAddress(addr, value.size()))); memory_->set(addr, std::move(value)); + SL_TRACE(logger_, "store buffer with size {} at {}", value.size(), addr); } WasmSpan MemoryImpl::storeBuffer(common::BufferView value) { diff --git a/core/runtime/binaryen/module/module_factory_impl.cpp b/core/runtime/binaryen/module/module_factory_impl.cpp index 9e8724550d..efb71dbd04 100644 --- a/core/runtime/binaryen/module/module_factory_impl.cpp +++ b/core/runtime/binaryen/module/module_factory_impl.cpp @@ -7,9 +7,10 @@ #include "runtime/binaryen/module/module_factory_impl.hpp" #include "host_api/host_api_factory.hpp" +#include "crypto/hasher.hpp" #include "runtime/binaryen/binaryen_memory_factory.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/binaryen/core_api_factory_impl.hpp" +#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/binaryen/instance_environment_factory.hpp" #include "runtime/binaryen/module/module_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" @@ -31,8 +32,10 @@ namespace kagome::runtime::binaryen { common::BufferView code) const { std::vector code_vec{code.begin(), code.end()}; OUTCOME_TRY(module, - ModuleImpl::createFromCode( - code_vec, env_factory_, hasher_->sha2_256(code))); + ModuleImpl::createFromCode(code_vec, + env_factory_, + shared_from_this(), + hasher_->sha2_256(code))); return module; } diff --git a/core/runtime/binaryen/module/module_factory_impl.hpp b/core/runtime/binaryen/module/module_factory_impl.hpp index a60d4ea4f4..270de1dc83 100644 --- a/core/runtime/binaryen/module/module_factory_impl.hpp +++ b/core/runtime/binaryen/module/module_factory_impl.hpp @@ -32,7 +32,9 @@ namespace kagome::runtime::binaryen { class InstanceEnvironmentFactory; - class ModuleFactoryImpl final : public ModuleFactory { + class ModuleFactoryImpl final + : public ModuleFactory, + public std::enable_shared_from_this { public: ModuleFactoryImpl(std::shared_ptr env_factory, std::shared_ptr storage, diff --git a/core/runtime/binaryen/module/module_impl.cpp b/core/runtime/binaryen/module/module_impl.cpp index c272090b4d..30a01e4b10 100644 --- a/core/runtime/binaryen/module/module_impl.cpp +++ b/core/runtime/binaryen/module/module_impl.cpp @@ -14,6 +14,7 @@ #include "common/int_serialization.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" #include "runtime/binaryen/instance_environment_factory.hpp" +#include "runtime/binaryen/module/module_factory_impl.hpp" #include "runtime/binaryen/module/module_instance_impl.hpp" #include "runtime/binaryen/runtime_external_interface.hpp" #include "storage/trie/polkadot_trie/trie_error.hpp" @@ -35,19 +36,23 @@ namespace kagome::runtime::binaryen { ModuleImpl::ModuleImpl( std::unique_ptr &&module, + std::shared_ptr module_factory, std::shared_ptr env_factory, const common::Hash256 &code_hash) - : env_factory_{std::move(env_factory)}, + : module_factory_{std::move(module_factory)}, + env_factory_{std::move(env_factory)}, module_{std::move(module)}, code_hash_(code_hash) { BOOST_ASSERT(module_ != nullptr); BOOST_ASSERT(env_factory_ != nullptr); + BOOST_ASSERT(module_factory_ != nullptr); } outcome::result, CompilationError> ModuleImpl::createFromCode( const std::vector &code, std::shared_ptr env_factory, + std::shared_ptr module_factory, const common::Hash256 &code_hash) { auto log = log::createLogger("wasm_module", "binaryen"); // that nolint suppresses false positive in a library function @@ -80,11 +85,12 @@ namespace kagome::runtime::binaryen { module->memory.initial = kDefaultHeappages; return std::make_shared( - std::move(module), std::move(env_factory), code_hash); + std::move(module), module_factory, env_factory, code_hash); } - std::shared_ptr ModuleImpl::instantiate() const { - auto env = env_factory_->make(); + outcome::result> ModuleImpl::instantiate() + const { + auto env = env_factory_->make(module_factory_); return std::make_shared( std::move(env.env), shared_from_this(), env.rei, code_hash_); } diff --git a/core/runtime/binaryen/module/module_impl.hpp b/core/runtime/binaryen/module/module_impl.hpp index d478acca97..6687cea64b 100644 --- a/core/runtime/binaryen/module/module_impl.hpp +++ b/core/runtime/binaryen/module/module_impl.hpp @@ -18,9 +18,9 @@ namespace wasm { class Module; } // namespace wasm -namespace kagome::storage::trie { - class EphemeralTrieBatch; -} +namespace kagome::runtime { + class ModuleFactory; +}; namespace kagome::runtime::binaryen { @@ -47,16 +47,19 @@ namespace kagome::runtime::binaryen { static outcome::result, CompilationError> createFromCode( const std::vector &code, - std::shared_ptr env_factory_, + std::shared_ptr env_factory, + std::shared_ptr module_factory, const common::Hash256 &code_hash); - std::shared_ptr instantiate() const override; + outcome::result> instantiate() const override; ModuleImpl(std::unique_ptr &&module, + std::shared_ptr module_factory, std::shared_ptr env_factory, const common::Hash256 &code_hash); private: + std::shared_ptr module_factory_; std::shared_ptr env_factory_; std::shared_ptr module_; // shared to module instances const common::Hash256 code_hash_; diff --git a/core/runtime/binaryen/module/module_instance_impl.cpp b/core/runtime/binaryen/module/module_instance_impl.cpp index 00009885df..c8e8a68843 100644 --- a/core/runtime/binaryen/module/module_instance_impl.cpp +++ b/core/runtime/binaryen/module/module_instance_impl.cpp @@ -7,7 +7,8 @@ // Enables binaryen debug mode: every executed WASM instruction is printed out // using Indenter (defined below) It is a massive amount of information, so it // should be turned on only for specific cases when we need to follow web -// assembly execution very precisely. #define WASM_INTERPRETER_DEBUG +// assembly execution very precisely. +// #define WASM_INTERPRETER_DEBUG #include "runtime/binaryen/module/module_instance_impl.hpp" @@ -58,6 +59,7 @@ namespace wasm { #include "host_api/host_api.hpp" #include "runtime/binaryen/runtime_external_interface.hpp" +#include "runtime/runtime_context.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime::binaryen, ModuleInstanceImpl::Error, @@ -97,11 +99,13 @@ namespace kagome::runtime::binaryen { return parent_; } - outcome::result ModuleInstanceImpl::callExportFunction( - std::string_view name, common::BufferView encoded_args) const { - auto memory = env_.memory_provider->getCurrentMemory().value(); - - PtrSize args{memory.get().storeBuffer(encoded_args)}; + outcome::result ModuleInstanceImpl::callExportFunction( + RuntimeContext &ctx, + std::string_view name, + common::BufferView encoded_args) const { + PtrSize args{ + getEnvironment().memory_provider->getCurrentMemory()->get().storeBuffer( + encoded_args)}; const auto args_list = wasm::LiteralList{wasm::Literal{args.ptr}, wasm::Literal{args.size}}; @@ -114,11 +118,12 @@ namespace kagome::runtime::binaryen { } try { - const auto res = static_cast( + const auto res = module_instance_->callExport(wasm::Name{name.data()}, args_list) - .geti64()); - - return PtrSize{res}; + .geti64(); + auto [ptr, size] = PtrSize{res}; + return getEnvironment().memory_provider->getCurrentMemory()->get().loadN( + ptr, size); } catch (wasm::ExitException &e) { return Error::UNEXPECTED_EXIT; diff --git a/core/runtime/binaryen/module/module_instance_impl.hpp b/core/runtime/binaryen/module/module_instance_impl.hpp index d56a377218..4780dfb5f1 100644 --- a/core/runtime/binaryen/module/module_instance_impl.hpp +++ b/core/runtime/binaryen/module/module_instance_impl.hpp @@ -40,8 +40,10 @@ namespace kagome::runtime::binaryen { std::shared_ptr getModule() const override; - outcome::result callExportFunction( - std::string_view name, common::BufferView args) const override; + outcome::result callExportFunction( + RuntimeContext &ctx, + std::string_view name, + common::BufferView args) const override; outcome::result> getGlobal( std::string_view name) const override; diff --git a/core/runtime/binaryen/runtime_external_interface.cpp b/core/runtime/binaryen/runtime_external_interface.cpp index 62a5b6ebda..993e0fda49 100644 --- a/core/runtime/binaryen/runtime_external_interface.cpp +++ b/core/runtime/binaryen/runtime_external_interface.cpp @@ -25,18 +25,13 @@ namespace { } template <> - auto literalMemFun() { + auto literalMemFun() { return &wasm::Literal::geti64; } - template <> - auto literalMemFun() { - return &wasm::Literal::geti32; - } - /** * @brief a meta-layer that places list of arguments into host api method - * invokation using fold expression + * invoсation using fold expression */ template wasm::Literal callInternal(T *host_api, diff --git a/core/runtime/common/CMakeLists.txt b/core/runtime/common/CMakeLists.txt index 9f54a91901..0eb0e3ee4e 100644 --- a/core/runtime/common/CMakeLists.txt +++ b/core/runtime/common/CMakeLists.txt @@ -89,3 +89,7 @@ target_link_libraries(memory_allocator outcome ) kagome_install(memory_allocator) + +add_library(core_api_factory core_api_factory_impl.cpp) +target_link_libraries(core_api_factory outcome) +kagome_install(core_api_factory) diff --git a/core/runtime/common/core_api_factory_impl.cpp b/core/runtime/common/core_api_factory_impl.cpp new file mode 100644 index 0000000000..085659d956 --- /dev/null +++ b/core/runtime/common/core_api_factory_impl.cpp @@ -0,0 +1,32 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "core_api_factory_impl.hpp" + +#include "runtime/common/runtime_properties_cache_impl.hpp" +#include "runtime/common/trie_storage_provider_impl.hpp" +#include "runtime/module_repository.hpp" +#include "runtime/runtime_api/impl/core.hpp" +#include "runtime/runtime_context.hpp" + +namespace kagome::runtime { + + CoreApiFactoryImpl::CoreApiFactoryImpl( + std::shared_ptr module_factory) + : module_factory_{module_factory} { + BOOST_ASSERT(module_factory_); + } + + outcome::result> CoreApiFactoryImpl::make( + std::shared_ptr hasher, + const std::vector &runtime_code) const { + OUTCOME_TRY( + ctx, + RuntimeContextFactory::fromCode(*module_factory_, runtime_code, {})); + return std::make_unique(std::move(ctx)); + } + +} // namespace kagome::runtime diff --git a/core/runtime/common/core_api_factory_impl.hpp b/core/runtime/common/core_api_factory_impl.hpp new file mode 100644 index 0000000000..c1f45d2f03 --- /dev/null +++ b/core/runtime/common/core_api_factory_impl.hpp @@ -0,0 +1,43 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "runtime/core_api_factory.hpp" + +#include + +namespace kagome::storage::trie { + class TrieStorage; +} + +namespace kagome::blockchain { + class BlockHeaderRepository; +} + +namespace kagome::runtime { + class ModuleFactory; + class SingleModuleCache; +} // namespace kagome::runtime + +namespace kagome::runtime { + + class CoreApiFactoryImpl final + : public runtime::CoreApiFactory, + public std::enable_shared_from_this { + public: + explicit CoreApiFactoryImpl(std::shared_ptr module_factory); + ~CoreApiFactoryImpl() = default; + + outcome::result> make( + std::shared_ptr hasher, + const std::vector &runtime_code) const override; + + private: + std::shared_ptr module_factory_; + }; + +} // namespace kagome::runtime diff --git a/core/runtime/common/executor.cpp b/core/runtime/common/executor.cpp index d882c56425..17c7aa45b1 100644 --- a/core/runtime/common/executor.cpp +++ b/core/runtime/common/executor.cpp @@ -20,20 +20,9 @@ namespace kagome::runtime { - Executor::Executor(std::shared_ptr ctx_factory, - std::shared_ptr cache) - : ctx_factory_{ctx_factory}, cache_{cache} {} - - outcome::result Executor::callWithCtx( - RuntimeContext &ctx, std::string_view name, BufferView encoded_args) { - KAGOME_PROFILE_START(call_execution) - OUTCOME_TRY(result, - ctx.module_instance->callExportFunction(name, encoded_args)); - auto memory = ctx.module_instance->getEnvironment() - .memory_provider->getCurrentMemory(); - BOOST_ASSERT(memory.has_value()); - OUTCOME_TRY(ctx.module_instance->resetEnvironment()); - return memory->get().loadN(result.ptr, result.size); - } + Executor::Executor( + std::shared_ptr ctx_factory, + std::optional> cache) + : cache_{cache}, ctx_factory_{ctx_factory} {} } // namespace kagome::runtime diff --git a/core/runtime/common/memory_allocator.cpp b/core/runtime/common/memory_allocator.cpp index 9b280af679..2eca5061bb 100644 --- a/core/runtime/common/memory_allocator.cpp +++ b/core/runtime/common/memory_allocator.cpp @@ -23,7 +23,7 @@ namespace kagome::runtime { : memory_{std::move(memory)}, offset_{roundUpAlign(config.heap_base)}, max_memory_pages_num_{config.limits.max_memory_pages_num.value_or( - std::numeric_limits::max())}, + std::numeric_limits::max())}, logger_{log::createLogger("Allocator", "runtime")} { // Heap base (and offset in according) must be non-zero to prohibit // allocating memory at 0 in the future, as returning 0 from allocate method @@ -34,7 +34,7 @@ namespace kagome::runtime { BOOST_ASSERT(memory_.resize); } - WasmPointer MemoryAllocator::allocate(const WasmSize size) { + WasmPointer MemoryAllocator::allocate(const uint32_t size) { if (size == 0) { return 0; } diff --git a/core/runtime/common/memory_allocator.hpp b/core/runtime/common/memory_allocator.hpp index 73da72cb87..77c03c61fa 100644 --- a/core/runtime/common/memory_allocator.hpp +++ b/core/runtime/common/memory_allocator.hpp @@ -54,16 +54,22 @@ namespace kagome::runtime { MemoryAllocator(MemoryHandle memory, const struct MemoryConfig &config); - WasmPointer allocate(const WasmSize size); + WasmPointer allocate(const uint32_t size); std::optional deallocate(WasmPointer ptr); template bool checkAddress(WasmPointer addr) noexcept { - return offset_ > addr and offset_ - addr >= sizeof(T); + BOOST_ASSERT(addr > 0); + return offset_ > static_cast(addr) + and offset_ - static_cast(addr) >= sizeof(T); } - bool checkAddress(WasmPointer addr, size_t size) noexcept { - return offset_ > addr and offset_ - addr >= size; + bool checkAddress(WasmPointer addr, WasmSize size) noexcept { + BOOST_ASSERT(addr > 0); + BOOST_ASSERT(size > 0); + return offset_ > static_cast(addr) + and offset_ - static_cast(addr) + >= static_cast(size); } /* @@ -73,6 +79,7 @@ namespace kagome::runtime { std::optional getDeallocatedChunkSize(WasmPointer ptr) const; std::optional getAllocatedChunkSize(WasmPointer ptr) const; size_t getDeallocatedChunksNum() const; + void reset(); private: struct AllocationHeader { @@ -113,8 +120,8 @@ namespace kagome::runtime { std::unordered_map> available_; // Offset on the tail of the last allocated MemoryImpl chunk - size_t offset_; - WasmSize max_memory_pages_num_; + uint32_t offset_; + uint32_t max_memory_pages_num_; log::Logger logger_; }; diff --git a/core/runtime/common/module_instance.cpp b/core/runtime/common/module_instance.cpp index 9439a0547d..804985ef5a 100644 --- a/core/runtime/common/module_instance.cpp +++ b/core/runtime/common/module_instance.cpp @@ -18,13 +18,15 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, ModuleInstance::Error, e) { return "Failed to extract heap base from a module"; case E::HEAP_BASE_TOO_LOW: return "Heap base too low"; + case E::INVALID_CALL_RESULT: + return "The size of the buffer returned by the runtime does not match " + "the size of the requested return type"; } return "Unknown ModuleInstance error"; } namespace kagome::runtime { using namespace kagome::common::literals; - outcome::result ModuleInstance::resetMemory( const MemoryLimits &limits) { static auto log = log::createLogger("RuntimeEnvironmentFactory", "runtime"); @@ -39,35 +41,41 @@ namespace kagome::runtime { BOOST_ASSERT(heap_base > 0); auto &memory_provider = getEnvironment().memory_provider; OUTCOME_TRY(const_cast(*memory_provider) - .resetMemory(MemoryConfig{static_cast(heap_base), + .resetMemory(MemoryConfig{static_cast(heap_base), limits})); auto &memory = memory_provider->getCurrentMemory()->get(); - static auto heappages_key = ":heappages"_buf; - OUTCOME_TRY(heappages, - getEnvironment().storage_provider->getCurrentBatch()->tryGet( - heappages_key)); + static const auto heappages_key = ":heappages"_buf; + auto batch = getEnvironment().storage_provider->getCurrentBatch(); + OUTCOME_TRY(heappages, batch->tryGet(heappages_key)); if (heappages) { if (sizeof(uint64_t) != heappages->size()) { - log->error( - "Unable to read :heappages value. Type size mismatch. " - "Required {} bytes, but {} available", - sizeof(uint64_t), - heappages->size()); + SL_ERROR(log, + "Unable to read :heappages value. Type size mismatch. " + "Required {} bytes, but {} available", + sizeof(uint64_t), + heappages->size()); } else { uint64_t pages = common::le_bytes_to_uint64(heappages->view()); memory.resize(pages * kMemoryPageSize); - log->trace( + SL_TRACE( + log, "Creating wasm module with non-default :heappages value set to {}", pages); } } size_t max_data_segment_end = 0; + size_t segments_num = 0; forDataSegment([&](ModuleInstance::SegmentOffset offset, ModuleInstance::SegmentData segment) { max_data_segment_end = std::max(max_data_segment_end, offset + segment.size()); + segments_num++; + SL_DEBUG(log, + "Data segment {} at offset {}", + common::BufferView{segment}, + offset); }); if (static_cast(heap_base) < max_data_segment_end) { return ModuleInstance::Error::HEAP_BASE_TOO_LOW; diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index bedbb56a2d..99f739bef5 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -15,15 +15,6 @@ #include "runtime/runtime_code_provider.hpp" #include "runtime/runtime_upgrade_tracker.hpp" -OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, Error, e) { - using E = kagome::runtime::Error; - switch (e) { - case E::COMPILATION_FAILED: - return "Runtime module compilation failed"; - } - return "Unknown module repository error"; -} - namespace kagome::runtime { using kagome::primitives::ThreadNumber; using soralog::util::getThreadNumber; @@ -72,12 +63,9 @@ namespace kagome::runtime { if (not code.has_value()) { return code.as_failure(); } - auto new_module_res = module_factory_->make(code.value()); - if (!new_module_res) { - return make_error_code(new_module_res.error()); - } - runtime_instances_pool_->putModule(state, - std::move(new_module_res.value())); + OUTCOME_TRY(new_module, + module_factory_->make(code.value())); + runtime_instances_pool_->putModule(state, std::move(new_module)); } } diff --git a/core/runtime/common/runtime_context.cpp b/core/runtime/common/runtime_context.cpp index a1f31d71ae..8d60b874b7 100644 --- a/core/runtime/common/runtime_context.cpp +++ b/core/runtime/common/runtime_context.cpp @@ -17,6 +17,15 @@ #include "runtime/trie_storage_provider.hpp" #include "storage/trie/polkadot_trie/trie_error.hpp" +OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, Error, e) { + using E = kagome::runtime::Error; + switch (e) { + case E::COMPILATION_FAILED: + return "Runtime module compilation failed"; + } + return "Unknown module repository error"; +} + namespace kagome::runtime { using namespace kagome::common::literals; @@ -26,6 +35,8 @@ namespace kagome::runtime { BOOST_ASSERT(this->module_instance); } + RuntimeContext::~RuntimeContext() {} + RuntimeContextFactoryImpl::RuntimeContextFactoryImpl( std::shared_ptr module_repo, std::shared_ptr header_repo) @@ -41,7 +52,7 @@ namespace kagome::runtime { if (!runtime_module_res) { return Error::COMPILATION_FAILED; } - auto instance = runtime_module_res.value()->instantiate(); + OUTCOME_TRY(instance, runtime_module_res.value()->instantiate()); runtime::RuntimeContext ctx{ instance, }; @@ -55,7 +66,7 @@ namespace kagome::runtime { outcome::result RuntimeContextFactoryImpl::fromBatch( std::shared_ptr instance, std::shared_ptr batch, - ContextParams params) { + ContextParams params) const { runtime::RuntimeContext ctx{ instance, }; @@ -69,7 +80,7 @@ namespace kagome::runtime { const storage::trie::RootHash &state, std::optional> changes_tracker_opt, - ContextParams params) { + ContextParams params) const { runtime::RuntimeContext ctx{ instance, }; @@ -82,7 +93,7 @@ namespace kagome::runtime { outcome::result RuntimeContextFactoryImpl::ephemeral( std::shared_ptr instance, const storage::trie::RootHash &state, - ContextParams params) { + ContextParams params) const { runtime::RuntimeContext ctx{ instance, }; @@ -93,7 +104,7 @@ namespace kagome::runtime { } outcome::result RuntimeContextFactoryImpl::ephemeralAtGenesis( - ContextParams params) { + ContextParams params) const { OUTCOME_TRY(genesis_hash, header_repo_->getHashByNumber(0)); OUTCOME_TRY(genesis_header, header_repo_->getBlockHeader(genesis_hash)); OUTCOME_TRY(instance, @@ -108,7 +119,7 @@ namespace kagome::runtime { outcome::result RuntimeContextFactoryImpl::persistentAt( const primitives::BlockHash &block_hash, TrieChangesTrackerOpt changes_tracker, - ContextParams params) { + ContextParams params) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, @@ -121,7 +132,7 @@ namespace kagome::runtime { } outcome::result RuntimeContextFactoryImpl::ephemeralAt( - const primitives::BlockHash &block_hash, ContextParams params) { + const primitives::BlockHash &block_hash, ContextParams params) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, @@ -135,7 +146,7 @@ namespace kagome::runtime { outcome::result RuntimeContextFactoryImpl::ephemeralAt( const primitives::BlockHash &block_hash, const storage::trie::RootHash &state_hash, - ContextParams params) { + ContextParams params) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, diff --git a/core/runtime/common/runtime_instances_pool.cpp b/core/runtime/common/runtime_instances_pool.cpp index 680e4ddfc0..287dcfc43f 100644 --- a/core/runtime/common/runtime_instances_pool.cpp +++ b/core/runtime/common/runtime_instances_pool.cpp @@ -19,7 +19,7 @@ namespace kagome::runtime { * back to the ModuleInstancePool upon destruction of * BorrowedInstance. */ - class BorrowedInstance : public ModuleInstance { + class BorrowedInstance final : public ModuleInstance { public: BorrowedInstance(std::weak_ptr pool, const common::Hash256 &hash, @@ -39,9 +39,11 @@ namespace kagome::runtime { return instance_->getModule(); } - outcome::result callExportFunction( - std::string_view name, common::BufferView encoded_args) const override { - return instance_->callExportFunction(name, encoded_args); + outcome::result callExportFunction( + RuntimeContext &ctx, + std::string_view name, + common::BufferView encoded_args) const override { + return instance_->callExportFunction(ctx, name, encoded_args); } outcome::result> getGlobal( @@ -71,7 +73,7 @@ namespace kagome::runtime { std::shared_ptr module_factory, size_t capacity) : module_factory_{std::move(module_factory)}, pools_{capacity} {} - outcome::result, CompilationError> + outcome::result> RuntimeInstancesPool::instantiateFromCode(const CodeHash &code_hash, common::BufferView code_zstd) { std::unique_lock lock{pools_mtx_}; @@ -87,7 +89,7 @@ namespace kagome::runtime { } } BOOST_ASSERT(pool_opt); - auto instance = pool_opt->get().instantiate(lock); + OUTCOME_TRY(instance, pool_opt->get().instantiate(lock)); return std::make_shared( weak_from_this(), code_hash, std::move(instance)); } @@ -132,7 +134,7 @@ namespace kagome::runtime { std::unique_lock lock{pools_mtx_}; auto entry = pools_.get(state); BOOST_ASSERT(entry); - auto instance = entry->get().instantiate(lock); + OUTCOME_TRY(instance, entry->get().instantiate(lock)); return std::make_shared( weak_from_this(), state, std::move(instance)); } @@ -166,7 +168,7 @@ namespace kagome::runtime { } } - std::shared_ptr + outcome::result> RuntimeInstancesPool::InstancePool::instantiate( std::unique_lock &lock) { if (instances.empty()) { diff --git a/core/runtime/common/runtime_instances_pool.hpp b/core/runtime/common/runtime_instances_pool.hpp index be9813f4d3..9be3e19249 100644 --- a/core/runtime/common/runtime_instances_pool.hpp +++ b/core/runtime/common/runtime_instances_pool.hpp @@ -33,7 +33,7 @@ namespace kagome::runtime { RuntimeInstancesPool(std::shared_ptr module_factory, size_t capacity = DEFAULT_MODULES_CACHE_SIZE); - outcome::result, CompilationError> + outcome::result> instantiateFromCode(const CodeHash &code_hash, common::BufferView code_zstd); @@ -81,7 +81,7 @@ namespace kagome::runtime { std::shared_ptr module; std::vector> instances; - std::shared_ptr instantiate( + outcome::result> instantiate( std::unique_lock &lock); }; diff --git a/core/runtime/core_api_factory.hpp b/core/runtime/core_api_factory.hpp index 441730b30e..d078bac816 100644 --- a/core/runtime/core_api_factory.hpp +++ b/core/runtime/core_api_factory.hpp @@ -9,6 +9,8 @@ #include #include +#include "outcome/outcome.hpp" + namespace kagome::host_api { class HostApiFactory; } @@ -19,7 +21,7 @@ namespace kagome::crypto { namespace kagome::runtime { - class Core; + class RestrictedCore; /** * A factory for Core API, used where an isolated runtime environment @@ -29,7 +31,7 @@ namespace kagome::runtime { public: virtual ~CoreApiFactory() = default; - [[nodiscard]] virtual std::unique_ptr make( + [[nodiscard]] virtual outcome::result> make( std::shared_ptr hasher, const std::vector &runtime_code) const = 0; }; diff --git a/core/runtime/executor.hpp b/core/runtime/executor.hpp index f288ca8ecc..3bc18ebeba 100644 --- a/core/runtime/executor.hpp +++ b/core/runtime/executor.hpp @@ -17,162 +17,41 @@ namespace kagome::runtime { class Executor { public: - using Buffer = common::Buffer; - using BufferView = common::BufferView; - using BlockHash = primitives::BlockHash; - - explicit Executor(std::shared_ptr ctx_factory, - std::shared_ptr cache); - - virtual ~Executor() = default; - - virtual outcome::result callWithCtx(RuntimeContext &ctx, - std::string_view name, - BufferView encoded_args); - - outcome::result callAt(const primitives::BlockHash &block_hash, - std::string_view name, - BufferView encoded_args) { - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAt(block_hash)); - return callWithCtx(ctx, name, encoded_args); - } - - outcome::result callAt(const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state_hash, - std::string_view name, - BufferView encoded_args) { - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAt(block_hash, state_hash)); - return callWithCtx(ctx, name, encoded_args); - } - - outcome::result callAtGenesis(std::string_view name, - BufferView encoded_args) { - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAtGenesis()); - return callWithCtx(ctx, name, encoded_args); - } - - /** - * Method for calling a Runtime API method - * Resets the runtime memory with the module's heap base, - * encodes the arguments with SCALE codec, calls the method from the - * provided module instance and returns a result, decoded from SCALE. - * Changes, made to the Host API state, are reset after the call. - */ - template - outcome::result decodedCallWithCtx(RuntimeContext &ctx, - std::string_view name, - Args &&...args) { - OUTCOME_TRY(encoded_args, encodeArgs(std::forward(args)...)); - return cachedCall( - ctx.module_instance->getCodeHash(), - name, - [this, &ctx, name, &encoded_args]() -> outcome::result { - OUTCOME_TRY(result_buf, callWithCtx(ctx, name, encoded_args)); - return decodeResult(std::move(result_buf)); - }); - } - - template - outcome::result callAt(const primitives::BlockHash &block_hash, - std::string_view name, - Args &&...args) { - OUTCOME_TRY(encoded_args, encodeArgs(std::forward(args)...)); - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAt(block_hash)); - return cachedCall( - ctx.module_instance->getCodeHash(), - name, - [this, &ctx, name, &encoded_args]() -> outcome::result { - OUTCOME_TRY(result_buf, callWithCtx(ctx, name, encoded_args)); - return decodeResult(std::move(result_buf)); - }); - } - - template - outcome::result callAt(const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state_hash, - std::string_view name, - Args &&...args) { - OUTCOME_TRY(encoded_args, encodeArgs(std::forward(args)...)); - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAt(block_hash, state_hash)); - return cachedCall( - ctx.module_instance->getCodeHash(), - name, - [this, &ctx, name, &encoded_args]() -> outcome::result { - OUTCOME_TRY(result_buf, callWithCtx(ctx, name, encoded_args)); - return decodeResult(std::move(result_buf)); - }); - } - - template - outcome::result callAtGenesis(std::string_view name, - Args &&...args) { - OUTCOME_TRY(encoded_args, encodeArgs(std::forward(args)...)); - OUTCOME_TRY(ctx, ctx_factory_->ephemeralAtGenesis()); - return cachedCall( - ctx.module_instance->getCodeHash(), - name, - [this, &ctx, name, &encoded_args]() -> outcome::result { - OUTCOME_TRY(result_buf, callWithCtx(ctx, name, encoded_args)); - return decodeResult(std::move(result_buf)); - }); - } - - template - static outcome::result encodeArgs(Args &&...args) { - if constexpr (sizeof...(args) > 0) { - OUTCOME_TRY(res, scale::encode(std::forward(args)...)); - return Buffer{std::move(res)}; + Executor(std::shared_ptr ctx_factory, + std::optional> cache = {}); + + const RuntimeContextFactory &ctx() const { + return *ctx_factory_; + } + + template + outcome::result call(RuntimeContext &ctx, + std::string_view name, + const Args &...args) { + auto code_hash = ctx.module_instance->getCodeHash(); + auto call = [&]() { + return ctx.module_instance->template callAndDecodeExportFunction( + ctx, name, args...); + }; + if (!cache_) { + return call(); } - return Buffer{}; - } - - template - static outcome::result decodeResult(common::BufferView result) { - if constexpr (std::is_void_v) { - return outcome::success(); - } else { - Result t{}; - scale::ScaleDecoderStream s(result); - try { - s >> t; - // Check whether the whole byte buffer was consumed - if (s.hasMore(1)) { - static auto logger = log::createLogger("Executor", "runtime"); - SL_ERROR(logger, - "Runtime API call result size exceeds the size of the " - "type to initialize {} (read {}, total size {})", - typeid(Result).name(), - s.currentIndex(), - s.span().size_bytes()); - return outcome::failure(std::errc::illegal_byte_sequence); - } - return outcome::success(std::move(t)); - } catch (std::system_error &e) { - return outcome::failure(e.code()); - } - } - } - - private: - template - outcome::result cachedCall(const common::Hash256 &code_hash, - std::string_view name, - const F &call) { + auto &cache = *cache_; if constexpr (std::is_same_v) { [[likely]] if (name == "Core_version") { - return cache_->getVersion(code_hash, call); + return cache->getVersion(code_hash, call); } } if constexpr (std::is_same_v) { [[likely]] if (name == "Metadata_metadata") { - return cache_->getMetadata(code_hash, call); + return cache->getMetadata(code_hash, call); } } return call(); } - std::shared_ptr ctx_factory_; - std::shared_ptr cache_; + std::optional> cache_; + std::shared_ptr ctx_factory_; }; } // namespace kagome::runtime diff --git a/core/runtime/module.hpp b/core/runtime/module.hpp index 10444a8467..103bb6c461 100644 --- a/core/runtime/module.hpp +++ b/core/runtime/module.hpp @@ -25,7 +25,7 @@ namespace kagome::runtime { public: virtual ~Module() = default; - virtual std::shared_ptr instantiate() const = 0; + virtual outcome::result> instantiate() const = 0; }; /** diff --git a/core/runtime/module_instance.hpp b/core/runtime/module_instance.hpp index 6f45ea0024..4fe9819afa 100644 --- a/core/runtime/module_instance.hpp +++ b/core/runtime/module_instance.hpp @@ -13,12 +13,16 @@ #include "common/blob.hpp" #include "common/buffer.hpp" +#include "common/monadic_utils.hpp" +#include "log/logger.hpp" #include "outcome/outcome.hpp" #include "runtime/instance_environment.hpp" #include "runtime/ptr_size.hpp" namespace kagome::runtime { class Module; + class RuntimeContext; + class Memory; static_assert(sizeof(float) == 4); static_assert(sizeof(double) == 8); @@ -33,8 +37,48 @@ namespace kagome::runtime { enum class Error { ABSENT_HEAP_BASE = 1, HEAP_BASE_TOO_LOW, + INVALID_CALL_RESULT, }; + template + static outcome::result encodeArgs(const Args &...args) { + if constexpr (sizeof...(args) > 0) { + return common::map_result(scale::encode(args...), [](auto&& vec) { + return common::Buffer{vec}; + }); + } + return outcome::success(); + } + + template + static outcome::result decodedCall( + outcome::result &&result) { + OUTCOME_TRY(value, result); + if constexpr (std::is_void_v) { + return outcome::success(); + } else { + Result t{}; + scale::ScaleDecoderStream s(value); + try { + s >> t; + // Check whether the whole byte buffer was consumed + if (s.hasMore(1)) { + static auto logger = log::createLogger("Executor", "runtime"); + SL_ERROR(logger, + "Runtime API call result size exceeds the size of the " + "type to initialize {} (read {}, total size {})", + typeid(Result).name(), + s.currentIndex(), + s.span().size_bytes()); + return outcome::failure(ModuleInstance::Error::INVALID_CALL_RESULT); + } + return outcome::success(std::move(t)); + } catch (std::system_error &e) { + return outcome::failure(e.code()); + } + } + } + virtual ~ModuleInstance() = default; virtual const common::Hash256 &getCodeHash() const = 0; @@ -42,14 +86,25 @@ namespace kagome::runtime { virtual std::shared_ptr getModule() const = 0; /** - * Call the instance's function + * Call an export function + * @param ctx - context of the call * @param name - name of the function * @param args - a pointer-size describing a buffer with the function * parameters * @return a pointer-size with the buffer returned by the call */ - virtual outcome::result callExportFunction( - std::string_view name, common::BufferView encoded_args) const = 0; + virtual outcome::result callExportFunction( + RuntimeContext &ctx, + std::string_view name, + common::BufferView encoded_args) const = 0; + + template + outcome::result callAndDecodeExportFunction(RuntimeContext &ctx, + std::string_view name, + const Args &...args) { + OUTCOME_TRY(args_buf, encodeArgs(args...)); + return decodedCall(callExportFunction(ctx, name, args_buf)); + } virtual outcome::result> getGlobal( std::string_view name) const = 0; @@ -63,7 +118,7 @@ namespace kagome::runtime { virtual const InstanceEnvironment &getEnvironment() const = 0; virtual outcome::result resetEnvironment() = 0; - virtual outcome::result resetMemory(const MemoryLimits &config); + outcome::result resetMemory(const MemoryLimits &config); }; } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/core.hpp b/core/runtime/runtime_api/core.hpp index 53c32cc381..fedc4bdf0c 100644 --- a/core/runtime/runtime_api/core.hpp +++ b/core/runtime/runtime_api/core.hpp @@ -11,13 +11,25 @@ #include "primitives/block.hpp" #include "primitives/common.hpp" #include "primitives/version.hpp" -#include "runtime/runtime_context.hpp" #include "storage/changes_trie/changes_tracker.hpp" +#include "runtime/runtime_context.hpp" namespace kagome::runtime { - /** - * Core represents mandatory part of runtime api - */ + + // interface for calls that are done by the runtime via Host API + // (typically Runtime API is the contrary -- calls from host to runtime) + class RestrictedCore { + public: + virtual ~RestrictedCore() = default; + + /** + * @brief Returns the version of the runtime - version for nested calls, + * such as in MiscExtension + * @return runtime version + */ + virtual outcome::result version() = 0; + }; + class Core { public: virtual ~Core() = default; @@ -29,13 +41,6 @@ namespace kagome::runtime { virtual outcome::result version( const primitives::BlockHash &block) = 0; - /** - * @brief Returns the version of the runtime - version for nested calls, - * such as in MiscExtension - * @return runtime version - */ - virtual outcome::result version() = 0; - /** * @brief Executes the given block * @param block block to execute diff --git a/core/runtime/runtime_api/impl/account_nonce_api.cpp b/core/runtime/runtime_api/impl/account_nonce_api.cpp index 0e77b515b0..6ea29592f2 100644 --- a/core/runtime/runtime_api/impl/account_nonce_api.cpp +++ b/core/runtime/runtime_api/impl/account_nonce_api.cpp @@ -18,8 +18,9 @@ namespace kagome::runtime { outcome::result AccountNonceApiImpl::account_nonce( const primitives::BlockHash &block, const primitives::AccountId &account_id) { - return executor_->callAt( - block, "AccountNonceApi_account_nonce", account_id); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call( + ctx, "AccountNonceApi_account_nonce", account_id); }; } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/babe_api.cpp b/core/runtime/runtime_api/impl/babe_api.cpp index 44cd8f605b..ad47ad0c77 100644 --- a/core/runtime/runtime_api/impl/babe_api.cpp +++ b/core/runtime/runtime_api/impl/babe_api.cpp @@ -15,15 +15,16 @@ namespace kagome::runtime { BOOST_ASSERT(executor_); } - outcome::result - BabeApiImpl::configuration(const primitives::BlockHash &block) { - return executor_->callAt( - block, "BabeApi_configuration"); + outcome::result BabeApiImpl::configuration( + const primitives::BlockHash &block) { + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call( + ctx, "BabeApi_configuration"); } outcome::result BabeApiImpl::next_epoch( const primitives::BlockHash &block) { - return executor_->callAt(block, - "BabeApi_next_epoch"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call(ctx, "BabeApi_next_epoch"); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/beefy.cpp b/core/runtime/runtime_api/impl/beefy.cpp index 6c10c61bc2..071fe1bc2c 100644 --- a/core/runtime/runtime_api/impl/beefy.cpp +++ b/core/runtime/runtime_api/impl/beefy.cpp @@ -17,8 +17,9 @@ namespace kagome::runtime { outcome::result> BeefyApiImpl::genesis( const primitives::BlockHash &block) { - auto r = executor_->callAt>( - block, "BeefyApi_beefy_genesis"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + auto r = executor_->call>( + ctx, "BeefyApi_beefy_genesis"); if (r) { return std::move(r.value()); } @@ -30,7 +31,8 @@ namespace kagome::runtime { outcome::result> BeefyApiImpl::validatorSet(const primitives::BlockHash &block) { - return executor_->callAt>( - block, "BeefyApi_validator_set"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "BeefyApi_validator_set"); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/block_builder.cpp b/core/runtime/runtime_api/impl/block_builder.cpp index 29bdc54325..d80aac7c05 100644 --- a/core/runtime/runtime_api/impl/block_builder.cpp +++ b/core/runtime/runtime_api/impl/block_builder.cpp @@ -32,7 +32,7 @@ namespace kagome::runtime { } }); OUTCOME_TRY(result, - executor_->decodedCallWithCtx( + executor_->call( ctx, "BlockBuilder_apply_extrinsic", extrinsic)); if (auto ok = boost::get(&result); ok and boost::get(ok)) { @@ -45,7 +45,7 @@ namespace kagome::runtime { outcome::result BlockBuilderImpl::finalize_block( RuntimeContext &ctx) { - return executor_->decodedCallWithCtx( + return executor_->call( ctx, "BlockBuilder_finalize_block"); } @@ -64,24 +64,24 @@ namespace kagome::runtime { std::ignore = ctx.module_instance->getEnvironment() .storage_provider->rollbackTransaction(); }); - OUTCOME_TRY( - result, - executor_->decodedCallWithCtx>( - ctx, "BlockBuilder_inherent_extrinsics", data)); + OUTCOME_TRY(result, + executor_->call>( + ctx, "BlockBuilder_inherent_extrinsics", data)); return result; } outcome::result BlockBuilderImpl::check_inherents(const primitives::Block &block, const primitives::InherentData &data) { - return executor_->callAt( - block.header.parent_hash, "BlockBuilder_check_inherents", block, data); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block.header.parent_hash)); + return executor_->call( + ctx, "BlockBuilder_check_inherents", block, data); } outcome::result BlockBuilderImpl::random_seed( const primitives::BlockHash &block) { - return executor_->callAt(block, - "BlockBuilder_random_seed"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call(ctx, "BlockBuilder_random_seed"); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/core.cpp b/core/runtime/runtime_api/impl/core.cpp index feb37a10ef..3b6e1577a7 100644 --- a/core/runtime/runtime_api/impl/core.cpp +++ b/core/runtime/runtime_api/impl/core.cpp @@ -13,28 +13,42 @@ #include "runtime/runtime_properties_cache.hpp" namespace kagome::runtime { + outcome::result callCoreVersion( const ModuleFactory &module_factory, common::BufferView code, const std::shared_ptr &runtime_properties_cache) { OUTCOME_TRY(ctx, runtime::RuntimeContextFactory::fromCode(module_factory, code)); - return Executor{nullptr, runtime_properties_cache} - .decodedCallWithCtx(ctx, "Core_version"); + return runtime_properties_cache->getVersion( + ctx.module_instance->getCodeHash(), + [&ctx]() -> outcome::result { + OUTCOME_TRY( + raw_res, + ctx.module_instance->callExportFunction(ctx, "Core_version", {})); + return ModuleInstance::decodedCall(raw_res); + }); + } + + RestrictedCoreImpl::RestrictedCoreImpl(RuntimeContext ctx) + : ctx_{std::move(ctx)} {} + + outcome::result RestrictedCoreImpl::version() { + return ctx_.module_instance + ->callAndDecodeExportFunction(ctx_, + "Core_version"); } CoreImpl::CoreImpl( std::shared_ptr executor, - std::shared_ptr ctx_factory, std::shared_ptr header_repo, std::shared_ptr runtime_upgrade_tracker) : executor_{std::move(executor)}, - ctx_factory_{std::move(ctx_factory)}, header_repo_{std::move(header_repo)}, runtime_upgrade_tracker_{std::move(runtime_upgrade_tracker)} { BOOST_ASSERT(executor_ != nullptr); - BOOST_ASSERT(ctx_factory_ != nullptr); BOOST_ASSERT(header_repo_ != nullptr); + BOOST_ASSERT(runtime_upgrade_tracker_ != nullptr); } outcome::result CoreImpl::version( @@ -46,10 +60,6 @@ namespace kagome::runtime { "Core_version"); } - outcome::result CoreImpl::version() { - return executor_->callAtGenesis("Core_version"); - } - outcome::result CoreImpl::execute_block_ref( const primitives::BlockReflection &block, TrieChangesTrackerOpt changes_tracker) { @@ -59,11 +69,9 @@ namespace kagome::runtime { and parent_res.value().number == block.header.number - 1; }()); OUTCOME_TRY(ctx, - ctx_factory_->persistentAt(block.header.parent_hash, - std::move(changes_tracker))); - OUTCOME_TRY( - executor_->decodedCallWithCtx(ctx, "Core_execute_block", block)); - return outcome::success(); + executor_->ctx().persistentAt(block.header.parent_hash, + std::move(changes_tracker))); + return executor_->call(ctx, "Core_execute_block", block); } outcome::result CoreImpl::execute_block( @@ -80,10 +88,9 @@ namespace kagome::runtime { const primitives::BlockHeader &header, TrieChangesTrackerOpt changes_tracker) { OUTCOME_TRY(ctx, - ctx_factory_->persistentAt(header.parent_hash, - std::move(changes_tracker))); - OUTCOME_TRY(executor_->decodedCallWithCtx( - ctx, "Core_initialize_block", header)); + executor_->ctx().persistentAt(header.parent_hash, + std::move(changes_tracker))); + OUTCOME_TRY(executor_->call(ctx, "Core_initialize_block", header)); return std::make_unique(std::move(ctx)); } diff --git a/core/runtime/runtime_api/impl/core.hpp b/core/runtime/runtime_api/impl/core.hpp index 18d2556efd..ca7b0cb334 100644 --- a/core/runtime/runtime_api/impl/core.hpp +++ b/core/runtime/runtime_api/impl/core.hpp @@ -20,19 +20,26 @@ namespace kagome::runtime { common::BufferView code, const std::shared_ptr &runtime_properties_cache); + class RestrictedCoreImpl final : public RestrictedCore { + public: + explicit RestrictedCoreImpl(RuntimeContext ctx); + + outcome::result version() override; + + private: + RuntimeContext ctx_; + }; + class CoreImpl final : public Core { public: CoreImpl( std::shared_ptr executor, - std::shared_ptr ctx_factory, std::shared_ptr header_repo, std::shared_ptr runtime_upgrade_tracker); outcome::result version( const primitives::BlockHash &block) override; - outcome::result version() override; - outcome::result execute_block( const primitives::Block &block, TrieChangesTrackerOpt changes_tracker) override; @@ -47,7 +54,6 @@ namespace kagome::runtime { private: std::shared_ptr executor_; - std::shared_ptr ctx_factory_; std::shared_ptr header_repo_; std::shared_ptr runtime_upgrade_tracker_; diff --git a/core/runtime/runtime_api/impl/grandpa_api.cpp b/core/runtime/runtime_api/impl/grandpa_api.cpp index 3aa59eed73..35e2d7fbed 100644 --- a/core/runtime/runtime_api/impl/grandpa_api.cpp +++ b/core/runtime/runtime_api/impl/grandpa_api.cpp @@ -17,14 +17,16 @@ namespace kagome::runtime { outcome::result GrandpaApiImpl::authorities( const primitives::BlockHash &block_hash) { - return executor_->callAt(block_hash, + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block_hash)); + return executor_->call(ctx, "GrandpaApi_grandpa_authorities"); } outcome::result GrandpaApiImpl::current_set_id( const primitives::BlockHash &block_hash) { - return executor_->callAt(block_hash, - "GrandpaApi_current_set_id"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block_hash)); + return executor_->call( + ctx, "GrandpaApi_current_set_id"); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/lru.hpp b/core/runtime/runtime_api/impl/lru.hpp index c77a16b95e..926a2181d2 100644 --- a/core/runtime/runtime_api/impl/lru.hpp +++ b/core/runtime/runtime_api/impl/lru.hpp @@ -26,8 +26,9 @@ namespace kagome::runtime { outcome::result> call(Executor &executor, const primitives::BlockHash &block, std::string_view name) { - if (DISABLE_RUNTIME_LRU) { - return executor.callAt>(block, name); + if constexpr (DISABLE_RUNTIME_LRU) { + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block)); + return executor.call>(ctx, name); } if (auto r = lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { @@ -35,15 +36,16 @@ namespace kagome::runtime { })) { return *r; } - OUTCOME_TRY(raw, executor.callAt(block, name, {})); - OUTCOME_TRY(r, Executor::decodeResult(raw)); + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block)); + OUTCOME_TRY(raw, ctx.module_instance->callExportFunction(ctx, name, {})); + OUTCOME_TRY(r, ModuleInstance::decodedCall(raw)); return lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { return lru_.put(block, std::move(r), raw); }); } void erase(const std::vector &blocks) { - if (DISABLE_RUNTIME_LRU) { + if constexpr (DISABLE_RUNTIME_LRU) { return; } lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { @@ -74,8 +76,9 @@ namespace kagome::runtime { const primitives::BlockHash &block, std::string_view name, const Arg &arg) { - if (DISABLE_RUNTIME_LRU) { - return executor.callAt>(block, name, arg); + if constexpr (DISABLE_RUNTIME_LRU) { + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block)); + return executor.call>(ctx, name, arg); } Key key{{block, arg}}; if (auto r = @@ -84,16 +87,19 @@ namespace kagome::runtime { })) { return *r; } - OUTCOME_TRY(raw_arg, Executor::encodeArgs(arg)); - OUTCOME_TRY(raw, executor.callAt(block, name, raw_arg)); - OUTCOME_TRY(r, Executor::decodeResult(raw)); + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block)); + + OUTCOME_TRY(raw_arg, ModuleInstance::encodeArgs(arg)); + OUTCOME_TRY(raw, + ctx.module_instance->callExportFunction(ctx, name, raw_arg)); + OUTCOME_TRY(r, ModuleInstance::decodedCall(raw)); return lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { return lru_.put(key, std::move(r), raw); }); } void erase(const std::vector &blocks) { - if (DISABLE_RUNTIME_LRU) { + if constexpr (DISABLE_RUNTIME_LRU) { return; } lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { @@ -126,8 +132,9 @@ namespace kagome::runtime { Executor &executor, const primitives::BlockHash &block_hash, std::string_view name) { - if (DISABLE_RUNTIME_LRU) { - return executor.callAt(block_hash, name); + if constexpr (DISABLE_RUNTIME_LRU) { + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block_hash)); + return executor.call(ctx, name); } OUTCOME_TRY(block_number, block_header_repository.getNumberByHash(block_hash)); @@ -140,7 +147,8 @@ namespace kagome::runtime { })) { return *r; } - OUTCOME_TRY(r, executor.callAt(block_hash, name)); + OUTCOME_TRY(ctx, executor.ctx().ephemeralAt(block_hash)); + OUTCOME_TRY(r, executor.call(ctx, name)); return lru_.exclusiveAccess([&](typename decltype(lru_)::Type &lru_) { return lru_.put(hash, std::move(r)); }); diff --git a/core/runtime/runtime_api/impl/mmr.cpp b/core/runtime/runtime_api/impl/mmr.cpp index e74c15a1d5..e2f0779a5e 100644 --- a/core/runtime/runtime_api/impl/mmr.cpp +++ b/core/runtime/runtime_api/impl/mmr.cpp @@ -16,24 +16,27 @@ namespace kagome::runtime { MmrApi::Result MmrApiImpl::mmrRoot( const primitives::BlockHash &block) { - return executor_->callAt>( - block, "MmrApi_mmr_root"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "MmrApi_mmr_root"); } MmrApi::Result MmrApiImpl::generateProof( const primitives::BlockHash &block, std::vector block_numbers, std::optional best_known_block_number) { - return executor_->callAt>( - block, "MmrApi_generate_proof", block_numbers, best_known_block_number); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "MmrApi_generate_proof", block_numbers, best_known_block_number); } MmrApi::Result MmrApiImpl::verifyProof( const primitives::BlockHash &block, const primitives::MmrLeaves &leaves, const primitives::MmrProof &proof) { - return executor_->callAt>( - block, "MmrApi_verify_proof", leaves, proof); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "MmrApi_verify_proof", leaves, proof); } MmrApi::Result MmrApiImpl::verifyProofStateless( @@ -41,7 +44,8 @@ namespace kagome::runtime { const common::Hash256 &mmr_root, const primitives::MmrLeaves &leaves, const primitives::MmrProof &proof) { - return executor_->callAt>( - block, "MmrApi_verify_proof_stateless", mmr_root, leaves, proof); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "MmrApi_verify_proof_stateless", mmr_root, leaves, proof); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/offchain_worker_api.cpp b/core/runtime/runtime_api/impl/offchain_worker_api.cpp index c9f9a913dd..ed2e80cdeb 100644 --- a/core/runtime/runtime_api/impl/offchain_worker_api.cpp +++ b/core/runtime/runtime_api/impl/offchain_worker_api.cpp @@ -51,8 +51,11 @@ namespace kagome::runtime { auto func = [block = std::move(block), header = std::move(header), executor = executor_] { - auto res = executor->callAt( - block, "OffchainWorkerApi_offchain_worker", header); + auto res = [&]() -> outcome::result { + OUTCOME_TRY(ctx, executor->ctx().ephemeralAt(block)); + return executor->call( + ctx, "OffchainWorkerApi_offchain_worker", header); + }(); if (res.has_error()) { auto log = log::createLogger("OffchainWorkerApi", "offchain"); diff --git a/core/runtime/runtime_api/impl/parachain_host.cpp b/core/runtime/runtime_api/impl/parachain_host.cpp index 53d4ae8a10..39a2edaf13 100644 --- a/core/runtime/runtime_api/impl/parachain_host.cpp +++ b/core/runtime/runtime_api/impl/parachain_host.cpp @@ -75,16 +75,18 @@ namespace kagome::runtime { const primitives::BlockHash &block, ParachainId id, OccupiedCoreAssumption assumption) { - return executor_->callAt>( - block, "ParachainHost_persisted_validation_data", id, assumption); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "ParachainHost_persisted_validation_data", id, assumption); } outcome::result ParachainHostImpl::check_validation_outputs( const primitives::BlockHash &block, ParachainId id, CandidateCommitments outputs) { - return executor_->callAt( - block, "ParachainHost_check_validation_outputs", id, outputs); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call( + ctx, "ParachainHost_check_validation_outputs", id, outputs); } outcome::result ParachainHostImpl::session_index_for_child( @@ -100,16 +102,18 @@ namespace kagome::runtime { ParachainHostImpl::validation_code(const primitives::BlockHash &block, ParachainId id, OccupiedCoreAssumption assumption) { - return executor_->callAt>( - block, "ParachainHost_validation_code", id, assumption); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "ParachainHost_validation_code", id, assumption); } outcome::result> ParachainHostImpl::validation_code_by_hash(const primitives::BlockHash &block, ValidationCodeHash hash) { - if (DISABLE_RUNTIME_LRU) { - return executor_->callAt>( - block, "ParachainHost_validation_code_by_hash", hash); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + if constexpr (DISABLE_RUNTIME_LRU) { + return executor_->call>( + ctx, "ParachainHost_validation_code_by_hash", hash); } if (auto r = validation_code_by_hash_.exclusiveAccess( [&](typename decltype(validation_code_by_hash_)::Type @@ -120,8 +124,8 @@ namespace kagome::runtime { return *r; } OUTCOME_TRY(code, - executor_->callAt>( - block, "ParachainHost_validation_code_by_hash", hash)); + executor_->call>( + ctx, "ParachainHost_validation_code_by_hash", hash)); if (code) { return validation_code_by_hash_.exclusiveAccess( [&](typename decltype(validation_code_by_hash_)::Type @@ -225,40 +229,43 @@ namespace kagome::runtime { outcome::result>> ParachainHostImpl::session_executor_params(const primitives::BlockHash &block, SessionIndex idx) { - return executor_->callAt>>( - block, "ParachainHost_session_executor_params", idx); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>>( + ctx, "ParachainHost_session_executor_params", idx); } outcome::result> ParachainHostImpl::on_chain_votes(const primitives::BlockHash &block) { - return executor_->callAt>( - block, "ParachainHost_on_chain_votes"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "ParachainHost_on_chain_votes"); } outcome::result>> ParachainHostImpl::disputes(const primitives::BlockHash &block) { - return executor_->callAt>>( - block, "ParachainHost_disputes"); // TODO ensure if it works + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>>( + ctx, "ParachainHost_disputes"); // TODO ensure if it works } outcome::result> ParachainHostImpl::pvfs_require_precheck(const primitives::BlockHash &block) { - return executor_->callAt>( - block, "ParachainHost_pvfs_require_precheck"); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call>( + ctx, "ParachainHost_pvfs_require_precheck"); } outcome::result ParachainHostImpl::submit_pvf_check_statement( const primitives::BlockHash &block, const parachain::PvfCheckStatement &statement, const parachain::Signature &signature) { - return executor_->callAt(block, - "ParachainHost_submit_pvf_check_statement", - statement, - signature); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); + return executor_->call( + ctx, "ParachainHost_submit_pvf_check_statement", statement, signature); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/session_keys_api.cpp b/core/runtime/runtime_api/impl/session_keys_api.cpp index acb5a6afd1..2d18b4d1aa 100644 --- a/core/runtime/runtime_api/impl/session_keys_api.cpp +++ b/core/runtime/runtime_api/impl/session_keys_api.cpp @@ -18,17 +18,19 @@ namespace kagome::runtime { outcome::result SessionKeysApiImpl::generate_session_keys( const primitives::BlockHash &block_hash, std::optional seed) { - return executor_->callAt>( - block_hash, "SessionKeys_generate_session_keys", std::move(seed)); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block_hash)); + return executor_->call>( + ctx, "SessionKeys_generate_session_keys", std::move(seed)); } outcome::result>> SessionKeysApiImpl::decode_session_keys( const primitives::BlockHash &block_hash, common::BufferView encoded) const { + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block_hash)); return executor_ - ->callAt>>( - block_hash, "SessionKeys_decode_session_keys", encoded); + ->call>>( + ctx, "SessionKeys_decode_session_keys", encoded); } } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/tagged_transaction_queue.cpp b/core/runtime/runtime_api/impl/tagged_transaction_queue.cpp index 65581e3fe4..34f7084b5b 100644 --- a/core/runtime/runtime_api/impl/tagged_transaction_queue.cpp +++ b/core/runtime/runtime_api/impl/tagged_transaction_queue.cpp @@ -25,9 +25,10 @@ namespace kagome::runtime { primitives::TransactionSource source, const primitives::Extrinsic &ext) { auto block = block_tree_.get()->bestBlock(); SL_TRACE(logger_, "Validate transaction called at block {}", block); + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block.hash)); OUTCOME_TRY(result, - executor_->callAt( - block.hash, + executor_->call( + ctx, "TaggedTransactionQueue_validate_transaction", source, ext, diff --git a/core/runtime/runtime_api/impl/transaction_payment_api.cpp b/core/runtime/runtime_api/impl/transaction_payment_api.cpp index 3cd8f9d5ec..db9d4c2ca8 100644 --- a/core/runtime/runtime_api/impl/transaction_payment_api.cpp +++ b/core/runtime/runtime_api/impl/transaction_payment_api.cpp @@ -50,15 +50,16 @@ namespace kagome::runtime { return Error::TRANSACTION_PAYMENT_API_NOT_FOUND; } auto api_version = res->second; + OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); if (api_version < 2) { return executor_ - ->callAt>( - block, "TransactionPaymentApi_query_info", ext.data, len); + ->call>( + ctx, "TransactionPaymentApi_query_info", ext.data, len); } OUTCOME_TRY( result, - executor_->callAt>( - block, "TransactionPaymentApi_query_info", ext.data, len)); + executor_->call>( + ctx, "TransactionPaymentApi_query_info", ext.data, len)); primitives::RuntimeDispatchInfo old_format_result; old_format_result.dispatch_class = result.dispatch_class; old_format_result.partial_fee = result.partial_fee; diff --git a/core/runtime/runtime_context.hpp b/core/runtime/runtime_context.hpp index c41a254234..e23015c8e2 100644 --- a/core/runtime/runtime_context.hpp +++ b/core/runtime/runtime_context.hpp @@ -28,6 +28,7 @@ namespace kagome::runtime { class ModuleFactory; class ModuleInstance; class ModuleRepository; + class Memory; class RuntimeContext { public: @@ -38,6 +39,8 @@ namespace kagome::runtime { RuntimeContext(RuntimeContext &&) = default; + ~RuntimeContext(); + // constructor for tests static RuntimeContext create_TEST( std::shared_ptr module_instance) { @@ -70,36 +73,37 @@ namespace kagome::runtime { virtual outcome::result fromBatch( std::shared_ptr module_instance, std::shared_ptr batch, - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; virtual outcome::result persistent( std::shared_ptr module_instance, const storage::trie::RootHash &state, std::optional> changes_tracker_opt, - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; virtual outcome::result persistentAt( const primitives::BlockHash &block_hash, std::optional> changes_tracker_opt, - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; virtual outcome::result ephemeral( std::shared_ptr module_instance, const storage::trie::RootHash &state, - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; virtual outcome::result ephemeralAt( - const primitives::BlockHash &block_hash, ContextParams params = {}) = 0; + const primitives::BlockHash &block_hash, + ContextParams params = {}) const = 0; virtual outcome::result ephemeralAt( const primitives::BlockHash &block_hash, const storage::trie::RootHash &state, - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; virtual outcome::result ephemeralAtGenesis( - ContextParams params = {}) = 0; + ContextParams params = {}) const = 0; }; class RuntimeContextFactoryImpl : public RuntimeContextFactory { @@ -113,37 +117,37 @@ namespace kagome::runtime { virtual outcome::result fromBatch( std::shared_ptr module_instance, std::shared_ptr batch, - ContextParams params = {}) override; + ContextParams params = {}) const override; virtual outcome::result persistent( std::shared_ptr module_instance, const storage::trie::RootHash &state, std::optional> changes_tracker_opt, - ContextParams params = {}) override; + ContextParams params = {}) const override; virtual outcome::result persistentAt( const primitives::BlockHash &block_hash, std::optional> - changes_tracker_opt, - ContextParams params = {}) override; + changes_tracker_opt = {}, + ContextParams params = {}) const override; virtual outcome::result ephemeral( std::shared_ptr module_instance, const storage::trie::RootHash &state, - ContextParams params = {}) override; + ContextParams params = {}) const override; virtual outcome::result ephemeralAt( const primitives::BlockHash &block_hash, - ContextParams params = {}) override; + ContextParams params = {}) const override; virtual outcome::result ephemeralAt( const primitives::BlockHash &block_hash, const storage::trie::RootHash &state, - ContextParams params = {}) override; + ContextParams params = {}) const override; virtual outcome::result ephemeralAtGenesis( - ContextParams params = {}) override; + ContextParams params = {}) const override; private: std::shared_ptr module_repo_; diff --git a/core/runtime/types.hpp b/core/runtime/types.hpp index 6aac0d259d..44d679582d 100644 --- a/core/runtime/types.hpp +++ b/core/runtime/types.hpp @@ -23,43 +23,42 @@ namespace kagome::runtime { Debug = 3, Trace = 4, }; - /** - * @brief type of wasm memory is 32 bit integer - */ - using WasmPointer = uint32_t; + + using WasmPointer = int32_t; + /** * @brief combination of pointer and size, where less significant part * represents wasm pointer, and most significant represents size */ - using WasmSpan = uint64_t; - /** - * @brief Size type is uint32_t because we are working in 32 bit address space - */ - using WasmSize = uint32_t; + using WasmSpan = int64_t; + /** - * @brief Enum value is uint32_t + * @brief Size type is int32_t because we are working in 32 bit address space */ - using WasmEnum = uint32_t; + using WasmSize = int32_t; + + using WasmEnum = int32_t; + /** - * @brief Offset type is uint32_t because we are working in 32 bit address + * @brief Offset type is int32_t because we are working in 32 bit address * space */ - using WasmOffset = uint32_t; + using WasmOffset = int32_t; using WasmI32 = int32_t; - using WasmU64 = uint64_t; + using WasmI64 = int64_t; struct MemoryLimits { - std::optional max_stack_size{}; - std::optional max_stack_values_num{}; - std::optional max_memory_pages_num{}; + std::optional max_stack_size{}; + std::optional max_stack_values_num{}; + std::optional max_memory_pages_num{}; }; struct MemoryConfig { - MemoryConfig(WasmSize heap_base, MemoryLimits limits = {}) + explicit MemoryConfig(uint32_t heap_base, MemoryLimits limits = {}) : heap_base{heap_base}, limits{std::move(limits)} {} - WasmSize heap_base; + uint32_t heap_base; MemoryLimits limits; }; diff --git a/core/runtime/wasm_edge/CMakeLists.txt b/core/runtime/wasm_edge/CMakeLists.txt new file mode 100644 index 0000000000..38bf8423c5 --- /dev/null +++ b/core/runtime/wasm_edge/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_library(runtime_wasm_edge module_factory_impl.cpp memory_impl.cpp) +target_link_libraries(runtime_wasm_edge "${WASM_EDGE_LIBRARY}" memory_allocator) +target_include_directories(runtime_wasm_edge PRIVATE "${WASMEDGE_ROOT}/include") diff --git a/core/runtime/wasm_edge/memory_impl.cpp b/core/runtime/wasm_edge/memory_impl.cpp new file mode 100644 index 0000000000..d33c95abe4 --- /dev/null +++ b/core/runtime/wasm_edge/memory_impl.cpp @@ -0,0 +1,85 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "runtime/wasm_edge/memory_impl.hpp" + +namespace kagome::runtime::wasm_edge { + + MemoryImpl::MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance, + const MemoryConfig &config) + : mem_instance_{std::move(mem_instance)}, + allocator_{MemoryAllocator{ + MemoryAllocator::MemoryHandle{ + .resize = [this](size_t new_size) { resize(new_size); }, + .getSize = [this]() -> size_t { return size(); }, + .storeSz = [this](WasmPointer p, uint32_t n) { store32(p, n); }, + .loadSz = [this](WasmPointer p) -> uint32_t { + return load32u(p); + }}, + config}} { + BOOST_ASSERT(mem_instance_ != nullptr); + SL_DEBUG(logger_, + "Created memory wrapper {} for internal instance {}", + fmt::ptr(this), + fmt::ptr(mem_instance_)); + } + + void MemoryImpl::resize(WasmSize new_size) { + if (new_size > size()) { + auto old_page_num = WasmEdge_MemoryInstanceGetPageSize(mem_instance_); + auto new_page_num = (new_size + kMemoryPageSize - 1) / kMemoryPageSize; + [[maybe_unused]] auto res = WasmEdge_MemoryInstanceGrowPage( + mem_instance_, new_page_num - old_page_num); + BOOST_ASSERT(WasmEdge_ResultOK(res)); + SL_DEBUG(logger_, + "Grow memory to {} pages ({} bytes) - {}", + new_page_num, + new_size, + WasmEdge_ResultGetMessage(res)); + } + } + + ExternalMemoryProviderImpl::ExternalMemoryProviderImpl( + WasmEdge_MemoryInstanceContext *wasmedge_memory) + : wasmedge_memory_{wasmedge_memory} { + BOOST_ASSERT(wasmedge_memory_); + } + + std::optional> + ExternalMemoryProviderImpl::getCurrentMemory() const { + if (current_memory_) { + return std::reference_wrapper(**current_memory_); + } + return std::nullopt; + } + + [[nodiscard]] outcome::result ExternalMemoryProviderImpl::resetMemory( + const MemoryConfig &config) { + current_memory_ = std::make_shared(wasmedge_memory_, config); + return outcome::success(); + } + + std::optional> + InternalMemoryProviderImpl::getCurrentMemory() const { + if (current_memory_) { + return std::reference_wrapper(**current_memory_); + } + return std::nullopt; + } + + [[nodiscard]] outcome::result InternalMemoryProviderImpl::resetMemory( + const MemoryConfig &config) { + if (wasmedge_memory_) { + current_memory_ = std::make_shared(wasmedge_memory_, config); + } + return outcome::success(); + } + + void InternalMemoryProviderImpl::setMemory( + WasmEdge_MemoryInstanceContext *wasmedge_memory) { + wasmedge_memory_ = wasmedge_memory; + } + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wasm_edge/memory_impl.hpp b/core/runtime/wasm_edge/memory_impl.hpp new file mode 100644 index 0000000000..575586269c --- /dev/null +++ b/core/runtime/wasm_edge/memory_impl.hpp @@ -0,0 +1,207 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "log/trace_macros.hpp" +#include "runtime/common/memory_allocator.hpp" +#include "runtime/memory.hpp" +#include "runtime/memory_provider.hpp" +#include "runtime/ptr_size.hpp" +#include "runtime/types.hpp" + +namespace kagome::runtime::wasm_edge { + + class MemoryImpl final : public Memory { + public: + MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance, + const MemoryConfig &config); + + /** + * @brief Return the size of the memory + */ + WasmSize size() const override { + return WasmEdge_MemoryInstanceGetPageSize(mem_instance_) + * kMemoryPageSize; + } + + /** + * Resizes memory to the given size + * @param new_size + */ + void resize(WasmSize new_size) override; + + WasmPointer allocate(WasmSize size) override { + return allocator_.allocate(size); + } + + std::optional deallocate(WasmPointer ptr) override { + return allocator_.deallocate(ptr); + } + + template + T loadInt(WasmPointer addr) const { + T data{}; + [[maybe_unused]] auto res = WasmEdge_MemoryInstanceGetData( + mem_instance_, reinterpret_cast(&data), addr, sizeof(T)); + BOOST_ASSERT(WasmEdge_ResultOK(res)); + if constexpr (std::is_integral_v) { + SL_TRACE_FUNC_CALL(logger_, data, addr); + } else { + SL_TRACE_FUNC_CALL(logger_, common::BufferView{data}, addr); + } + return data; + } + + int8_t load8s(WasmPointer addr) const override { + return loadInt(addr); + } + + uint8_t load8u(WasmPointer addr) const override { + return loadInt(addr); + } + + int16_t load16s(WasmPointer addr) const override { + return loadInt(addr); + } + + uint16_t load16u(WasmPointer addr) const override { + return loadInt(addr); + } + + int32_t load32s(WasmPointer addr) const override { + return loadInt(addr); + } + + uint32_t load32u(WasmPointer addr) const override { + return loadInt(addr); + } + + int64_t load64s(WasmPointer addr) const override { + return loadInt(addr); + } + + uint64_t load64u(WasmPointer addr) const override { + return loadInt(addr); + } + + std::array load128(WasmPointer addr) const override { + return loadInt>(addr); + } + + common::BufferView loadN(WasmPointer addr, WasmSize n) const override { + auto ptr = WasmEdge_MemoryInstanceGetPointer(mem_instance_, addr, n); + BOOST_ASSERT(ptr); + SL_TRACE_FUNC_CALL(logger_, fmt::ptr(ptr), addr, n); + return common::BufferView{ptr, static_cast(n)}; + } + + std::string loadStr(WasmPointer addr, WasmSize n) const override { + std::string res(n, ' '); + auto span = loadN(addr, n); + std::copy_n(span.begin(), n, res.begin()); + SL_TRACE_FUNC_CALL(logger_, res, addr, n); + return res; + } + + template + void storeInt(WasmPointer addr, T value) const { + [[maybe_unused]] auto res = WasmEdge_MemoryInstanceSetData( + mem_instance_, reinterpret_cast(&value), addr, sizeof(T)); + BOOST_ASSERT(WasmEdge_ResultOK(res)); + if constexpr (std::is_integral_v) { + SL_TRACE_FUNC_CALL( + logger_, WasmEdge_ResultGetMessage(res), addr, value); + } else { + SL_TRACE_FUNC_CALL(logger_, + WasmEdge_ResultGetMessage(res), + addr, + common::BufferView{value}); + } + } + + void store8(WasmPointer addr, int8_t value) override { + storeInt(addr, value); + } + + void store16(WasmPointer addr, int16_t value) override { + storeInt(addr, value); + } + + void store32(WasmPointer addr, int32_t value) override { + storeInt(addr, value); + } + + void store64(WasmPointer addr, int64_t value) override { + storeInt(addr, value); + } + + void store128(WasmPointer addr, + const std::array &value) override { + storeBuffer(addr, value); + } + + void storeBuffer(WasmPointer addr, common::BufferView value) override { + auto res = WasmEdge_MemoryInstanceSetData( + mem_instance_, value.data(), addr, value.size()); + if (!WasmEdge_ResultOK(res)) { + SL_ERROR(logger_, "{}", WasmEdge_ResultGetMessage(res)); + } + SL_TRACE_FUNC_CALL(logger_, WasmEdge_ResultGetMessage(res), addr); + } + + /** + * @brief allocates buffer in memory and copies value into memory + * @param value buffer to store + * @return full wasm pointer to allocated buffer + */ + WasmSpan storeBuffer(common::BufferView value) override { + auto ptr = allocate(value.size()); + storeBuffer(ptr, value); + return PtrSize{ptr, static_cast(value.size())}.combine(); + } + + private: + WasmEdge_MemoryInstanceContext *mem_instance_; + MemoryAllocator allocator_; + log::Logger logger_ = log::createLogger("Memory", "runtime"); + }; + + class ExternalMemoryProviderImpl final : public MemoryProvider { + public: + explicit ExternalMemoryProviderImpl( + WasmEdge_MemoryInstanceContext *wasmedge_memory); + + std::optional> getCurrentMemory() + const override; + + [[nodiscard]] outcome::result resetMemory( + const MemoryConfig &config) override; + + private: + std::optional> current_memory_; + WasmEdge_MemoryInstanceContext *wasmedge_memory_; + }; + + class InternalMemoryProviderImpl final : public MemoryProvider { + public: + explicit InternalMemoryProviderImpl() = default; + + std::optional> getCurrentMemory() + const override; + + [[nodiscard]] outcome::result resetMemory( + const MemoryConfig &config) override; + + void setMemory(WasmEdge_MemoryInstanceContext *wasmedge_memory); + + private: + std::optional> current_memory_; + WasmEdge_MemoryInstanceContext *wasmedge_memory_{}; + }; + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wasm_edge/module_factory_impl.cpp b/core/runtime/wasm_edge/module_factory_impl.cpp new file mode 100644 index 0000000000..5be907983d --- /dev/null +++ b/core/runtime/wasm_edge/module_factory_impl.cpp @@ -0,0 +1,438 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "module_factory_impl.hpp" + +#include + +#include +#include + +#include "crypto/hasher.hpp" +#include "host_api/host_api_factory.hpp" +#include "log/trace_macros.hpp" +#include "runtime/common/core_api_factory_impl.hpp" +#include "runtime/common/trie_storage_provider_impl.hpp" +#include "runtime/memory_provider.hpp" +#include "runtime/module.hpp" +#include "runtime/module_instance.hpp" +#include "runtime/runtime_context.hpp" +#include "runtime/wasm_edge/memory_impl.hpp" +#include "runtime/wasm_edge/register_host_api.hpp" +#include "runtime/wasm_edge/wrappers.hpp" + +namespace kagome::runtime::wasm_edge { + enum class Error { + INVALID_VALUE_TYPE = 1, + + }; +} + +OUTCOME_HPP_DECLARE_ERROR(kagome::runtime::wasm_edge, Error); + +OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime::wasm_edge, Error, e) { + using E = kagome::runtime::wasm_edge::Error; + switch (e) { + case E::INVALID_VALUE_TYPE: + return "Invalid value type"; + } + return "Unknown WasmEdge error"; +} + +namespace kagome::runtime::wasm_edge { + + static const auto kMemoryName = WasmEdge_StringCreateByCString("memory"); + + class WasmEdgeErrCategory final : public std::error_category { + public: + const char *name() const noexcept override { + return "WasmEdge"; + } + + std::string message(int code) const override { + auto res = WasmEdge_ResultGen(WasmEdge_ErrCategory_WASM, code); + return WasmEdge_ResultGetMessage(res); + } + }; + + WasmEdgeErrCategory wasm_edge_err_category; + + std::error_code make_error_code(WasmEdge_Result res) { + BOOST_ASSERT(WasmEdge_ResultGetCategory(res) == WasmEdge_ErrCategory_WASM); + return std::error_code{static_cast(WasmEdge_ResultGetCode(res)), + wasm_edge_err_category}; + } + +#define WasmEdge_UNWRAP(expr) \ + if (auto _wasm_edge_res = (expr); !WasmEdge_ResultOK(_wasm_edge_res)) { \ + return make_error_code(_wasm_edge_res); \ + } + +#define WasmEdge_UNWRAP_COMPILE_ERR(expr) \ + if (auto _wasm_edge_res = (expr); !WasmEdge_ResultOK(_wasm_edge_res)) { \ + return CompilationError( \ + fmt::format(#expr ": {}", WasmEdge_ResultGetMessage(_wasm_edge_res))); \ + } + + static outcome::result convertValue(WasmEdge_Value v) { + switch (v.Type) { + case WasmEdge_ValType_I32: + return WasmEdge_ValueGetI32(v); + case WasmEdge_ValType_I64: + return WasmEdge_ValueGetI64(v); + case WasmEdge_ValType_F32: + return WasmEdge_ValueGetF32(v); + case WasmEdge_ValType_F64: + return WasmEdge_ValueGetF64(v); + case WasmEdge_ValType_V128: + return Error::INVALID_VALUE_TYPE; + case WasmEdge_ValType_FuncRef: + return Error::INVALID_VALUE_TYPE; + case WasmEdge_ValType_ExternRef: + return Error::INVALID_VALUE_TYPE; + } + BOOST_UNREACHABLE_RETURN({}); + } + + class ModuleInstanceImpl : public ModuleInstance { + public: + explicit ModuleInstanceImpl( + std::shared_ptr module, + std::shared_ptr executor, + ModuleInstanceContext instance_ctx, + std::shared_ptr host_instance, + InstanceEnvironment env, + const common::Hash256 &code_hash) + : module_{module}, + instance_{std::move(instance_ctx)}, + host_instance_{host_instance}, + executor_{executor}, + env_{std::move(env)}, + code_hash_{} { + BOOST_ASSERT(module_ != nullptr); + BOOST_ASSERT(instance_ != nullptr); + BOOST_ASSERT(host_instance_ != nullptr); + BOOST_ASSERT(executor_ != nullptr); + } + + const common::Hash256 &getCodeHash() const override { + return code_hash_; + } + + std::shared_ptr getModule() const override { + return module_; + } + + outcome::result callExportFunction( + RuntimeContext &ctx, + std::string_view name, + common::BufferView encoded_args) const override { + PtrSize args_ptrsize{}; + if (!encoded_args.empty()) { + args_ptrsize = PtrSize{getEnvironment() + .memory_provider->getCurrentMemory() + .value() + .get() + .storeBuffer(encoded_args)}; + } + std::array params{WasmEdge_ValueGenI32(args_ptrsize.ptr), + WasmEdge_ValueGenI32(args_ptrsize.size)}; + std::array returns{WasmEdge_ValueGenI64(0)}; + String wasm_name = + WasmEdge_StringCreateByBuffer(name.data(), name.size()); + auto func = + WasmEdge_ModuleInstanceFindFunction(instance_.raw(), wasm_name.raw()); + if (!func) { + return RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND; + } + + SL_TRACE( + log_, + "Invoke env for {}:\n" + "\tmodule_instance: {}\n" + "\tinternal module_instance: {}\n" + "\thost module_instance: {}\n" + "\thost_api: {}\n" + "\tmemory_provider: {}\n" + "\tmemory: {}\n" + "\tstorage_provider: {}", + name, + fmt::ptr(this), + fmt::ptr(instance_.raw()), + fmt::ptr(host_instance_), + fmt::ptr(env_.host_api), + fmt::ptr(env_.memory_provider), + fmt::ptr(&env_.memory_provider->getCurrentMemory().value().get()), + fmt::ptr(env_.storage_provider)); + + auto res = WasmEdge_ExecutorInvoke(executor_->raw(), + func, + params.data(), + params.size(), + returns.data(), + 1); + WasmEdge_UNWRAP(res); + auto [ptr, size] = PtrSize{WasmEdge_ValueGetI64(returns[0])}; + auto result = getEnvironment() + .memory_provider->getCurrentMemory() + .value() + .get() + .loadN(ptr, size); + return result; + } + + outcome::result> getGlobal( + std::string_view name_) const override { + String name = WasmEdge_StringCreateByBuffer(name_.data(), name_.size()); + auto global = + WasmEdge_ModuleInstanceFindGlobal(instance_.raw(), name.raw()); + if (global == nullptr) { + return std::nullopt; + } + auto value = WasmEdge_GlobalInstanceGetValue(global); + OUTCOME_TRY(v, convertValue(value)); + return v; + } + + void forDataSegment(const DataSegmentProcessor &callback) const override { + uint32_t segments_num = + WasmEdge_ModuleInstanceListDataSegments(instance_.raw(), nullptr, 0); + std::vector segments(segments_num); + WasmEdge_ModuleInstanceListDataSegments( + instance_.raw(), segments.data(), segments.size()); + for (auto &segment : segments) { + callback(segment.Offset, std::span{segment.Data, segment.Length}); + } + } + + const InstanceEnvironment &getEnvironment() const override { + return env_; + } + + outcome::result resetEnvironment() override { + env_.host_api->reset(); + return outcome::success(); + } + + private: + std::shared_ptr module_; + ModuleInstanceContext instance_; + std::shared_ptr host_instance_; + std::shared_ptr executor_; + log::Logger log_ = log::createLogger("ModuleInstance", "runtime"); + InstanceEnvironment env_; + const common::Hash256 code_hash_; + }; + + class InstanceEnvironmentFactory { + public: + InstanceEnvironmentFactory( + std::shared_ptr core_factory, + std::shared_ptr host_api_factory, + std::shared_ptr storage, + std::shared_ptr serializer) + : core_factory_{core_factory}, + host_api_factory_{host_api_factory}, + storage_{storage}, + serializer_{serializer} {} + + InstanceEnvironment make(std::shared_ptr memory_provider) { + auto storage_provider = + std::make_shared(storage_, serializer_); + + return InstanceEnvironment{ + memory_provider, + storage_provider, + host_api_factory_->make( + core_factory_, memory_provider, storage_provider), + {}}; + } + + private: + std::shared_ptr core_factory_; + std::shared_ptr host_api_factory_; + std::shared_ptr storage_; + std::shared_ptr serializer_; + }; + + class ModuleImpl : public Module, + public std::enable_shared_from_this { + public: + explicit ModuleImpl(ASTModuleContext module, + std::shared_ptr executor, + std::shared_ptr env_factory, + const WasmEdge_MemoryTypeContext *memory_type, + common::Hash256 code_hash) + : env_factory_{std::move(env_factory)}, + executor_{std::move(executor)}, + memory_type_{memory_type}, + module_{std::move(module)}, + code_hash_{code_hash} { + BOOST_ASSERT(module_ != nullptr); + BOOST_ASSERT(executor_ != nullptr); + BOOST_ASSERT(env_factory_ != nullptr); + } + + outcome::result> instantiate() + const override { + StoreContext store = WasmEdge_StoreCreate(); + + auto host_instance = std::make_shared( + WasmEdge_ModuleInstanceCreate(WasmEdge_StringCreateByCString("env"))); + + std::shared_ptr memory_provider; + if (memory_type_) { + auto *mem_instance = WasmEdge_MemoryInstanceCreate(memory_type_); + auto limit = WasmEdge_MemoryTypeGetLimit(memory_type_); + SL_DEBUG(log_, + "Create memory instance, min: {}, max: {}", + limit.Min, + limit.Max); + WasmEdge_ModuleInstanceAddMemory( + host_instance->raw(), kMemoryName, mem_instance); + + mem_instance = WasmEdge_ModuleInstanceFindMemory(host_instance->raw(), + kMemoryName); + memory_provider = + std::make_shared(mem_instance); + } else { + memory_provider = std::make_shared(); + } + + InstanceEnvironment env = env_factory_->make(memory_provider); + + register_host_api(*env.host_api, host_instance->raw()); + WasmEdge_UNWRAP(WasmEdge_ExecutorRegisterImport( + executor_->raw(), store.raw(), host_instance->raw())); + + ModuleInstanceContext instance_ctx; + WasmEdge_UNWRAP(WasmEdge_ExecutorInstantiate( + executor_->raw(), &instance_ctx.raw(), store.raw(), module_.raw())); + + if (!memory_type_) { + auto memory_ctx = + WasmEdge_ModuleInstanceFindMemory(instance_ctx.raw(), kMemoryName); + BOOST_ASSERT(memory_ctx); + static_cast(memory_provider.get()) + ->setMemory(memory_ctx); + } + + return std::make_shared(shared_from_this(), + executor_, + std::move(instance_ctx), + host_instance, + std::move(env), + code_hash_); + } + + private: + std::shared_ptr env_factory_; + std::shared_ptr executor_; + log::Logger log_ = log::createLogger("Module", "runtime"); + const WasmEdge_MemoryTypeContext *memory_type_; + ASTModuleContext module_; + const common::Hash256 code_hash_; + }; + + ModuleFactoryImpl::ModuleFactoryImpl( + std::shared_ptr hasher, + std::shared_ptr host_api_factory, + std::shared_ptr storage, + std::shared_ptr serializer, + Config config) + : hasher_{hasher}, + host_api_factory_{host_api_factory}, + storage_{storage}, + serializer_{serializer}, + log_{log::createLogger("ModuleFactory", "runtime")}, + config_{config} { + BOOST_ASSERT(hasher_); + BOOST_ASSERT(host_api_factory_); + BOOST_ASSERT(storage_); + BOOST_ASSERT(serializer_); + } + + outcome::result, CompilationError> + ModuleFactoryImpl::make(common::BufferView code) const { + auto code_hash = hasher_->sha2_256(code); + + ConfigureContext configure_ctx = WasmEdge_ConfigureCreate(); + BOOST_ASSERT(configure_ctx.raw() != nullptr); // no known reasons to fail + + LoaderContext loader_ctx = WasmEdge_LoaderCreate(configure_ctx.raw()); + WasmEdge_ASTModuleContext *module_ctx; + + switch (config_.exec) { + case ExecType::Compiled: { + CompilerContext compiler = WasmEdge_CompilerCreate(configure_ctx.raw()); + std::string filename = fmt::format("{}/wasm_{}", + config_.compiled_module_dir.c_str(), + code_hash.toHex()); + std::error_code ec; + if (!std::filesystem::create_directories(config_.compiled_module_dir, + ec) + && ec) { + return CompilationError{fmt::format( + "Failed to create a dir for compiled modules: {}", ec.message())}; + } + if (!std::filesystem::exists(filename)) { + SL_INFO(log_, "Start compiling wasm module {}...", code_hash); + WasmEdge_UNWRAP_COMPILE_ERR(WasmEdge_CompilerCompileFromBuffer( + compiler.raw(), code.data(), code.size(), filename.c_str())); + SL_INFO(log_, "Compilation finished, saved at {}", filename); + } + WasmEdge_UNWRAP_COMPILE_ERR(WasmEdge_LoaderParseFromFile( + loader_ctx.raw(), &module_ctx, filename.c_str())); + break; + } + case ExecType::Interpreted: { + WasmEdge_UNWRAP_COMPILE_ERR(WasmEdge_LoaderParseFromBuffer( + loader_ctx.raw(), &module_ctx, code.data(), code.size())); + break; + } + default: + BOOST_UNREACHABLE_RETURN({}); + } + ASTModuleContext module = module_ctx; + + ValidatorContext validator = WasmEdge_ValidatorCreate(configure_ctx.raw()); + WasmEdge_UNWRAP_COMPILE_ERR( + WasmEdge_ValidatorValidate(validator.raw(), module.raw())); + + auto executor = std::make_shared( + WasmEdge_ExecutorCreate(nullptr, nullptr)); + + uint32_t imports_num = WasmEdge_ASTModuleListImportsLength(module.raw()); + std::vector imports; + imports.resize(imports_num); + WasmEdge_ASTModuleListImports( + module.raw(), + const_cast(imports.data()), + imports_num); + + const WasmEdge_MemoryTypeContext *import_memory_type{}; + using namespace std::string_view_literals; + for (auto &import : imports) { + if (WasmEdge_StringIsEqual(kMemoryName, + WasmEdge_ImportTypeGetExternalName(import))) { + import_memory_type = + WasmEdge_ImportTypeGetMemoryType(module.raw(), import); + break; + } + } + + auto core_api = std::make_shared(shared_from_this()); + + auto env_factory = std::make_shared( + core_api, host_api_factory_, storage_, serializer_); + + return std::shared_ptr{new ModuleImpl{std::move(module), + std::move(executor), + env_factory, + import_memory_type, + code_hash}}; + } + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wasm_edge/module_factory_impl.hpp b/core/runtime/wasm_edge/module_factory_impl.hpp new file mode 100644 index 0000000000..59f82ae618 --- /dev/null +++ b/core/runtime/wasm_edge/module_factory_impl.hpp @@ -0,0 +1,72 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include "log/logger.hpp" +#include "runtime/module_factory.hpp" + +namespace kagome::crypto { + class Hasher; +} + +namespace kagome::host_api { + class HostApiFactory; +} + +namespace kagome::runtime { + class TrieStorageProvider; +} + +namespace kagome::blockchain { + class BlockHeaderRepository; +} + +namespace kagome::storage::trie { + class TrieStorage; + class TrieSerializer; +} // namespace kagome::storage::trie + +namespace kagome::runtime::wasm_edge { + + class ModuleFactoryImpl + : public ModuleFactory, + public std::enable_shared_from_this { + public: + enum class ExecType { + Interpreted, + Compiled, + }; + struct Config { + Config() + : exec{ExecType::Compiled}, + compiled_module_dir{"/tmp/kagome/wasm-edge/"} {} + + ExecType exec; + std::filesystem::path compiled_module_dir; + }; + + explicit ModuleFactoryImpl( + std::shared_ptr hasher, + std::shared_ptr host_api_factory, + std::shared_ptr storage, + std::shared_ptr serializer, + Config config); + + outcome::result, CompilationError> make( + common::BufferView code) const override; + + private: + std::shared_ptr hasher_; + std::shared_ptr host_api_factory_; + std::shared_ptr storage_; + std::shared_ptr serializer_; + log::Logger log_; + Config config_; + }; + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wasm_edge/register_host_api.hpp b/core/runtime/wasm_edge/register_host_api.hpp new file mode 100644 index 0000000000..246ead0603 --- /dev/null +++ b/core/runtime/wasm_edge/register_host_api.hpp @@ -0,0 +1,286 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include + +#include "host_api/host_api.hpp" + +namespace kagome::runtime::wasm_edge { + + template + WasmEdge_ValType get_wasm_type() = delete; + + template <> + WasmEdge_ValType get_wasm_type() { + return WasmEdge_ValType_I32; + } + + template <> + WasmEdge_ValType get_wasm_type() { + return WasmEdge_ValType_I64; + } + + template <> + WasmEdge_ValType get_wasm_type() { + return WasmEdge_ValType_F32; + } + + template <> + WasmEdge_ValType get_wasm_type() { + return WasmEdge_ValType_F64; + } + + template + T get_wasm_value(WasmEdge_Value) = delete; + + template <> + int64_t get_wasm_value(WasmEdge_Value v) { + return WasmEdge_ValueGetI64(v); + } + + template <> + int32_t get_wasm_value(WasmEdge_Value v) { + return WasmEdge_ValueGetI32(v); + } + + template + using HostApiMethod = Ret (host_api::HostApi::*)(Args...); + + template + using ConstHostApiMethod = Ret (host_api::HostApi::*)(Args...) const; + + template + struct HostApiMethodTraits; + + template + struct HostApiMethodTraits> { + using Ret = Ret_; + using Args = std::tuple; + }; + + template + struct HostApiMethodTraits> { + using Ret = Ret_; + using Args = std::tuple; + }; + + template + decltype(auto) call_with_array(F f, + std::span array, + std::index_sequence) { + return f(get_wasm_value(array[Idxs])...); + } + + template + decltype(auto) call_with_array(F f, + std::span array, + std::tuple) { + return call_with_array( + f, array, std::make_index_sequence()); + } + + template + WasmEdge_Result host_method_wrapper( + void *current_host_api, + const WasmEdge_CallingFrameContext *call_frame_cxt, + const WasmEdge_Value *params, + WasmEdge_Value *returns) { + using Ret = typename HostApiMethodTraits::Ret; + using Args = typename HostApiMethodTraits::Args; + BOOST_ASSERT(current_host_api); + auto &host_api = *static_cast(current_host_api); + + try { + if constexpr (std::is_void_v) { + call_with_array( + [&host_api](auto... params) mutable { + std::invoke(Method, host_api, params...); + }, + std::span{params, std::tuple_size_v}, + Args{}); + } else { + Ret res = call_with_array( + [&host_api](auto... params) mutable -> Ret { + return std::invoke(Method, host_api, params...); + }, + std::span{params, std::tuple_size_v}, + Args{}); + returns[0].Value = res; + returns[0].Type = get_wasm_type(); + } + + } catch (std::runtime_error& e) { + auto log = log::createLogger("HostApi", "runtime"); + SL_ERROR(log, "Host API call failed with error: {}", e.what()); + return WasmEdge_Result_Terminate; + } + return WasmEdge_Result_Success; + } + + template + void register_method(WasmEdge_HostFunc_t cb, + WasmEdge_ModuleInstanceContext *module, + void *data, + std::string_view name) { + WasmEdge_ValType types[]{get_wasm_type()...}; + WasmEdge_ValType ret[1]; + WasmEdge_ValType *ret_ptr; + if constexpr (std::is_void_v) { + ret_ptr = nullptr; + } else { + ret[0] = get_wasm_type(); + ret_ptr = ret; + } + auto type = WasmEdge_FunctionTypeCreate( + types, sizeof...(Args), ret_ptr, ret_ptr == nullptr ? 0 : 1); + auto instance = WasmEdge_FunctionInstanceCreate(type, cb, data, 0); + WasmEdge_FunctionTypeDelete(type); + + WasmEdge_ModuleInstanceAddFunction( + module, + WasmEdge_StringCreateByBuffer(name.data(), name.size()), + instance); + } + + template + void register_host_method(WasmEdge_ModuleInstanceContext *module, + host_api::HostApi &host_api, + std::string_view name) { + WasmEdge_HostFunc_t cb = &host_method_wrapper; + register_method(cb, module, &host_api, name); + } + + WasmEdge_Result stub(void *data, + const WasmEdge_CallingFrameContext *, + const WasmEdge_Value *, + WasmEdge_Value *) { + static log::Logger logger = log::createLogger("WasmEdge", "runtime"); + SL_ERROR(logger, + "Attempt to call an unimplemented Host method '{}'", + reinterpret_cast(data)); + return WasmEdge_Result_Terminate; + }; + + template + void stub_host_method(WasmEdge_ModuleInstanceContext *module, + const char *name) { + register_method(stub, module, const_cast(name), name); + } + +#define REGISTER_HOST_METHOD(Ret, name, ...) \ + register_host_method<&host_api::HostApi::name, \ + Ret __VA_OPT__(, ) __VA_ARGS__>( \ + instance, host_api, #name); + +#define STUB_HOST_METHOD(Ret, name, ...) \ + stub_host_method(instance, #name); + + void register_host_api(host_api::HostApi &host_api, + WasmEdge_ModuleInstanceContext *instance) { + BOOST_ASSERT(instance); + // clang-format off + REGISTER_HOST_METHOD(void, ext_allocator_free_version_1, WasmPointer) + REGISTER_HOST_METHOD(void, ext_crypto_start_batch_verify_version_1) + REGISTER_HOST_METHOD(void, ext_default_child_storage_clear_version_1, WasmSpan, WasmSpan) + REGISTER_HOST_METHOD(void, ext_default_child_storage_clear_prefix_version_1, WasmSpan, WasmSpan) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_clear_prefix_version_2, int64_t, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_default_child_storage_set_version_1, int64_t, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_default_child_storage_storage_kill_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_storage_kill_version_3, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_logging_log_version_1, int32_t, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_misc_print_hex_version_1, int64_t) + REGISTER_HOST_METHOD(void, ext_misc_print_num_version_1, int64_t) + REGISTER_HOST_METHOD(void, ext_misc_print_utf8_version_1, int64_t) + STUB_HOST_METHOD(void, ext_sandbox_instance_teardown_version_1, int32_t) + STUB_HOST_METHOD(void, ext_sandbox_memory_teardown_version_1, int32_t) + REGISTER_HOST_METHOD(void, ext_storage_append_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_storage_clear_prefix_version_1, int64_t) + REGISTER_HOST_METHOD(void, ext_storage_clear_version_1, int64_t) + REGISTER_HOST_METHOD(void, ext_storage_commit_transaction_version_1) + REGISTER_HOST_METHOD(void, ext_storage_rollback_transaction_version_1) + REGISTER_HOST_METHOD(void, ext_storage_set_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(void, ext_storage_start_transaction_version_1) + REGISTER_HOST_METHOD(int32_t, ext_allocator_malloc_version_1, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_ed25519_generate_version_1, int32_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_ed25519_verify_version_1, int32_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_finish_batch_verify_version_1) + REGISTER_HOST_METHOD(int32_t, ext_crypto_sr25519_generate_version_1, int32_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_sr25519_verify_version_1, int32_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_sr25519_verify_version_2, int32_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_ecdsa_public_keys_version_1, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_ecdsa_sign_version_1, int32_t, int32_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_ecdsa_sign_prehashed_version_1, int32_t, int32_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_ecdsa_generate_version_1, int32_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_ecdsa_verify_version_1, int32_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_crypto_ecdsa_verify_prehashed_version_1, int32_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_default_child_storage_exists_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_blake2_128_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_blake2_256_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_keccak_256_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_sha2_256_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_twox_64_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_twox_128_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_hashing_twox_256_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_logging_max_level_version_1) + STUB_HOST_METHOD(int32_t, ext_sandbox_instantiate_version_1, int32_t, int64_t, int64_t, int32_t) + STUB_HOST_METHOD(int32_t, ext_sandbox_invoke_version_1, int32_t, int64_t, int64_t, int32_t, int32_t, int32_t) + STUB_HOST_METHOD(int32_t, ext_sandbox_memory_get_version_1, int32_t, int32_t, int32_t, int32_t) + STUB_HOST_METHOD(int32_t, ext_sandbox_memory_new_version_1, int32_t, int32_t) + STUB_HOST_METHOD(int32_t, ext_sandbox_memory_set_version_1, int32_t, int32_t, int32_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_storage_exists_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_trie_blake2_256_ordered_root_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_trie_blake2_256_ordered_root_version_2, int64_t, int32_t) + REGISTER_HOST_METHOD(int32_t, ext_trie_blake2_256_root_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_ed25519_public_keys_version_1, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_ed25519_sign_version_1, int32_t, int32_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_secp256k1_ecdsa_recover_compressed_version_1, int32_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_secp256k1_ecdsa_recover_compressed_version_2, int32_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_secp256k1_ecdsa_recover_version_1, int32_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_secp256k1_ecdsa_recover_version_2, int32_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_sr25519_public_keys_version_1, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_crypto_sr25519_sign_version_1, int32_t, int32_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_get_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_next_key_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_read_version_1, int64_t, int64_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_root_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_default_child_storage_root_version_2, int64_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_misc_runtime_version_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_changes_root_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_clear_prefix_version_2, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_get_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_next_key_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_read_version_1, int64_t, int64_t, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_storage_root_version_1) + REGISTER_HOST_METHOD(int64_t, ext_storage_root_version_2, int32_t) + + // -------------------------- Offchain extension --------------------------- + REGISTER_HOST_METHOD(int32_t, ext_offchain_is_validator_version_1) + REGISTER_HOST_METHOD(int64_t, ext_offchain_submit_transaction_version_1, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_network_state_version_1) + REGISTER_HOST_METHOD(int64_t, ext_offchain_timestamp_version_1) + REGISTER_HOST_METHOD(void , ext_offchain_sleep_until_version_1, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_offchain_random_seed_version_1) + REGISTER_HOST_METHOD(void , ext_offchain_local_storage_set_version_1, int32_t, int64_t, int64_t) + REGISTER_HOST_METHOD(void , ext_offchain_local_storage_clear_version_1, int32_t, int64_t) + REGISTER_HOST_METHOD(int32_t, ext_offchain_local_storage_compare_and_set_version_1, int32_t, int64_t, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_local_storage_get_version_1, int32_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_request_start_version_1, int64_t, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_request_add_header_version_1, int32_t, int64_t, int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_request_write_body_version_1, int32_t,int64_t,int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_response_wait_version_1,int64_t,int64_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_response_headers_version_1, int32_t) + REGISTER_HOST_METHOD(int64_t, ext_offchain_http_response_read_body_version_1, int32_t, int64_t, int64_t) + REGISTER_HOST_METHOD(void , ext_offchain_set_authorized_nodes_version_1, int64_t, int32_t) + REGISTER_HOST_METHOD(void , ext_offchain_index_set_version_1, int64_t, int64_t) + REGISTER_HOST_METHOD(void , ext_offchain_index_clear_version_1, int64_t) + + // clang-format on + } + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wasm_edge/wrappers.hpp b/core/runtime/wasm_edge/wrappers.hpp new file mode 100644 index 0000000000..9a75f81920 --- /dev/null +++ b/core/runtime/wasm_edge/wrappers.hpp @@ -0,0 +1,126 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace kagome::runtime::wasm_edge { + + std::atomic_int module_instance_count = 0; + std::atomic_int module_count = 0; + std::atomic_int memory_instance_count = 0; + std::atomic_int executor_count = 0; + std::atomic_int misc_count = 0; + + /** + * RAII wrapper for WasmEdge C API structures. + * @tparam T - WasmEdge type + * @tparam deleter - function that frees the object + * @tparam count - variable that counts instances of this object + */ + template + requires std::invocable + class Wrapper { + public: + Wrapper() : t{} { + count++; + } + + Wrapper(T t) : t{std::move(t)} { + count++; + } + + Wrapper(const Wrapper &) = delete; + Wrapper &operator=(const Wrapper &) = delete; + + Wrapper(Wrapper &&other) { + if (t) { + deleter(t); + } + t = other.t; + other.t = nullptr; + } + + Wrapper &operator=(Wrapper &&other) { + if (t) { + deleter(t); + } + t = other.t; + other.t = nullptr; + return *this; + } + + ~Wrapper() { + if constexpr (std::is_pointer_v) { + if (t) { + deleter(t); + count--; + } + } else { + deleter(t); + count--; + } + } + + T &raw() { + return t; + } + + const T &raw() const { + return t; + } + + T *operator->() { + return t; + } + + const T *operator->() const { + return t; + } + + const T *operator&() const { + return &t; + } + + T *operator&() { + return &t; + } + + bool operator==(std::nullptr_t) const { + return t == nullptr; + } + + T t = {}; + }; + + using ConfigureContext = + Wrapper; + using LoaderContext = + Wrapper; + using CompilerContext = + Wrapper; + using StatsContext = + Wrapper; + using FunctionTypeContext = + Wrapper; + using FunctionInstanceContext = Wrapper; + using ExecutorContext = Wrapper; + using StoreContext = Wrapper; + using ModuleInstanceContext = Wrapper; + using String = Wrapper; + using ASTModuleContext = Wrapper; + using MemoryInstanceContext = Wrapper; + using ValidatorContext = + Wrapper; + +} // namespace kagome::runtime::wasm_edge diff --git a/core/runtime/wavm/CMakeLists.txt b/core/runtime/wavm/CMakeLists.txt index 53957aa914..da72ff1d44 100644 --- a/core/runtime/wavm/CMakeLists.txt +++ b/core/runtime/wavm/CMakeLists.txt @@ -4,25 +4,8 @@ # SPDX-License-Identifier: Apache-2.0 # -add_library(compartment_wrapper compartment_wrapper.cpp) -target_link_libraries(compartment_wrapper - WAVM::libWAVM) -kagome_install(compartment_wrapper) - -llvm_map_components_to_libnames(LLVM_LIBS - core - ) - -add_library(wavm_memory memory_impl.cpp) -target_link_libraries(wavm_memory - ${LLVM_LIBS} - WAVM::libWAVM - memory_allocator - ) -kagome_install(wavm_memory) - add_library(runtime_wavm - core_api_factory_impl.cpp + compartment_wrapper.cpp intrinsics/intrinsic_functions.cpp intrinsics/intrinsic_module.cpp intrinsics/intrinsic_module_instance.cpp @@ -33,15 +16,18 @@ add_library(runtime_wavm module_params.cpp module_factory_impl.cpp module_instance.cpp + memory_impl.cpp wavm_external_memory_provider.cpp wavm_internal_memory_provider.cpp ) target_link_libraries(runtime_wavm - wavm_memory - constant_code_provider + ${LLVM_LIBS} + WAVM::libWAVM + constant_code_provider + core_api_factory executor Boost::boost - compartment_wrapper trie_storage_provider + memory_allocator ) kagome_install(runtime_wavm) diff --git a/core/runtime/wavm/core_api_factory_impl.cpp b/core/runtime/wavm/core_api_factory_impl.cpp index 86c75d906e..e69de29bb2 100644 --- a/core/runtime/wavm/core_api_factory_impl.cpp +++ b/core/runtime/wavm/core_api_factory_impl.cpp @@ -1,126 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "runtime/wavm/core_api_factory_impl.hpp" - -#include "runtime/common/constant_code_provider.hpp" -#include "runtime/common/runtime_properties_cache_impl.hpp" -#include "runtime/common/trie_storage_provider_impl.hpp" -#include "runtime/executor.hpp" -#include "runtime/module_repository.hpp" -#include "runtime/runtime_api/impl/core.hpp" -#include "runtime/runtime_context.hpp" -#include "runtime/wavm/compartment_wrapper.hpp" -#include "runtime/wavm/instance_environment_factory.hpp" -#include "runtime/wavm/intrinsics/intrinsic_functions.hpp" -#include "runtime/wavm/intrinsics/intrinsic_module.hpp" -#include "runtime/wavm/intrinsics/intrinsic_resolver_impl.hpp" -#include "runtime/wavm/module.hpp" -#include "runtime/wavm/module_instance.hpp" -#include "runtime/wavm/module_params.hpp" - -namespace kagome::runtime::wavm { - - class OneModuleRepository final : public ModuleRepository { - public: - OneModuleRepository( - std::shared_ptr compartment, - std::shared_ptr module_params, - std::shared_ptr intrinsic_module, - std::shared_ptr instance_env_factory, - common::BufferView code, - const common::Hash256 &code_hash, - std::shared_ptr last_compiled_module) - : instance_env_factory_{std::move(instance_env_factory)}, - compartment_{compartment}, - module_params_{std::move(module_params)}, - intrinsic_module_{std::move(intrinsic_module)}, - code_{code}, - last_compiled_module_{last_compiled_module} { - BOOST_ASSERT(instance_env_factory_); - BOOST_ASSERT(compartment_); - BOOST_ASSERT(intrinsic_module_); - BOOST_ASSERT(last_compiled_module_); - } - - outcome::result> getInstanceAt( - const primitives::BlockInfo &, - const storage::trie::RootHash &) override { - if (instance_ == nullptr) { - auto module_res = ModuleImpl::compileFrom(compartment_, - *module_params_, - intrinsic_module_, - instance_env_factory_, - code_, - code_hash_); - if (!module_res) { - return make_error_code(module_res.error()); - } - auto inst = module_res.value()->instantiate(); - last_compiled_module_->set(std::move(module_res.value())); - instance_ = std::move(inst); - } - return instance_; - } - - private: - std::shared_ptr instance_; - std::shared_ptr instance_env_factory_; - std::shared_ptr compartment_; - std::shared_ptr module_params_; - std::shared_ptr intrinsic_module_; - common::BufferView code_; - const common::Hash256 code_hash_; - std::shared_ptr last_compiled_module_; - }; - - CoreApiFactoryImpl::CoreApiFactoryImpl( - std::shared_ptr compartment, - std::shared_ptr module_params, - std::shared_ptr intrinsic_module, - std::shared_ptr storage, - std::shared_ptr block_header_repo, - std::shared_ptr instance_env_factory, - std::shared_ptr last_compiled_module, - std::shared_ptr cache) - : instance_env_factory_{std::move(instance_env_factory)}, - compartment_{compartment}, - module_params_{std::move(module_params)}, - intrinsic_module_{std::move(intrinsic_module)}, - storage_{std::move(storage)}, - block_header_repo_{std::move(block_header_repo)}, - last_compiled_module_{last_compiled_module}, - cache_(std::move(cache)) { - BOOST_ASSERT(compartment_); - BOOST_ASSERT(module_params_); - BOOST_ASSERT(intrinsic_module_); - BOOST_ASSERT(storage_); - BOOST_ASSERT(block_header_repo_); - BOOST_ASSERT(instance_env_factory_); - BOOST_ASSERT(last_compiled_module_); - BOOST_ASSERT(cache_); - } - - std::unique_ptr CoreApiFactoryImpl::make( - std::shared_ptr hasher, - const std::vector &runtime_code) const { - auto code_hash = hasher->sha2_256(runtime_code); - - auto ctx_factory = std::make_shared( - std::make_shared(compartment_, - module_params_, - intrinsic_module_, - instance_env_factory_, - runtime_code, - code_hash, - last_compiled_module_), - block_header_repo_); - auto executor = std::make_unique(ctx_factory, cache_); - return std::make_unique( - std::move(executor), ctx_factory, block_header_repo_, nullptr); - } - -} // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/core_api_factory_impl.hpp b/core/runtime/wavm/core_api_factory_impl.hpp deleted file mode 100644 index d516b46d83..0000000000 --- a/core/runtime/wavm/core_api_factory_impl.hpp +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "runtime/core_api_factory.hpp" - -#include - -namespace kagome::storage::trie { - class TrieStorage; -} - -namespace kagome::blockchain { - class BlockHeaderRepository; -} - -namespace kagome::runtime { - class Memory; - class ModuleRepository; - class SingleModuleCache; - class RuntimePropertiesCache; -} // namespace kagome::runtime - -namespace kagome::runtime::wavm { - - class IntrinsicModule; - class CompartmentWrapper; - class InstanceEnvironmentFactory; - struct ModuleParams; - - class CoreApiFactoryImpl final - : public runtime::CoreApiFactory, - public std::enable_shared_from_this { - public: - CoreApiFactoryImpl( - std::shared_ptr compartment, - std::shared_ptr module_params, - std::shared_ptr intrinsic_module, - std::shared_ptr storage, - std::shared_ptr block_header_repo, - std::shared_ptr instance_env_factory, - std::shared_ptr last_compiled_module, - std::shared_ptr cache); - - [[nodiscard]] std::unique_ptr make( - std::shared_ptr hasher, - const std::vector &runtime_code) const override; - - private: - std::shared_ptr instance_env_factory_; - std::shared_ptr compartment_; - std::shared_ptr module_params_; - std::shared_ptr intrinsic_module_; - std::shared_ptr storage_; - std::shared_ptr block_header_repo_; - std::shared_ptr last_compiled_module_; - std::shared_ptr cache_; - }; - -} // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/instance_environment_factory.cpp b/core/runtime/wavm/instance_environment_factory.cpp index d27ed8e9e2..5e5d940d8f 100644 --- a/core/runtime/wavm/instance_environment_factory.cpp +++ b/core/runtime/wavm/instance_environment_factory.cpp @@ -7,8 +7,8 @@ #include "runtime/wavm/instance_environment_factory.hpp" #include "host_api/host_api_factory.hpp" +#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" -#include "runtime/wavm/core_api_factory_impl.hpp" #include "runtime/wavm/intrinsics/intrinsic_functions.hpp" #include "runtime/wavm/intrinsics/intrinsic_module_instance.hpp" #include "runtime/wavm/module_params.hpp" @@ -20,31 +20,16 @@ namespace kagome::runtime::wavm { InstanceEnvironmentFactory::InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, - std::shared_ptr compartment, - std::shared_ptr module_params, - std::shared_ptr intrinsic_module, std::shared_ptr host_api_factory, - std::shared_ptr block_header_repo, - std::shared_ptr last_compiled_module, - std::shared_ptr cache) + std::shared_ptr module_factory) : storage_{std::move(storage)}, serializer_{std::move(serializer)}, - compartment_{std::move(compartment)}, - module_params_{std::move(module_params)}, - intrinsic_module_{std::move(intrinsic_module)}, host_api_factory_{std::move(host_api_factory)}, - block_header_repo_{std::move(block_header_repo)}, - last_compiled_module_{std::move(last_compiled_module)}, - cache_(std::move(cache)) { + module_factory_{module_factory} { BOOST_ASSERT(storage_ != nullptr); BOOST_ASSERT(serializer_ != nullptr); - BOOST_ASSERT(compartment_ != nullptr); - BOOST_ASSERT(module_params_ != nullptr); - BOOST_ASSERT(intrinsic_module_ != nullptr); BOOST_ASSERT(host_api_factory_ != nullptr); - BOOST_ASSERT(block_header_repo_ != nullptr); - BOOST_ASSERT(last_compiled_module_ != nullptr); - BOOST_ASSERT(cache_ != nullptr); + BOOST_ASSERT(module_factory_ != nullptr); } InstanceEnvironment InstanceEnvironmentFactory::make( @@ -53,15 +38,7 @@ namespace kagome::runtime::wavm { std::shared_ptr intrinsic_instance) const { auto new_storage_provider = std::make_shared(storage_, serializer_); - auto core_factory = - std::make_shared(compartment_, - module_params_, - intrinsic_module_, - storage_, - block_header_repo_, - shared_from_this(), - last_compiled_module_, - cache_); + auto core_factory = std::make_shared(module_factory_); std::shared_ptr memory_provider; switch (memory_origin) { diff --git a/core/runtime/wavm/instance_environment_factory.hpp b/core/runtime/wavm/instance_environment_factory.hpp index c531dc642e..0b2b19511c 100644 --- a/core/runtime/wavm/instance_environment_factory.hpp +++ b/core/runtime/wavm/instance_environment_factory.hpp @@ -27,15 +27,11 @@ namespace WAVM::Runtime { namespace kagome::runtime { class SingleModuleCache; - class RuntimePropertiesCache; + class ModuleFactory; } // namespace kagome::runtime namespace kagome::runtime::wavm { - class IntrinsicModule; class IntrinsicModuleInstance; - class IntrinsicResolver; - class CompartmentWrapper; - struct ModuleParams; class InstanceEnvironmentFactory final : public std::enable_shared_from_this { @@ -43,13 +39,8 @@ namespace kagome::runtime::wavm { InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, - std::shared_ptr compartment, - std::shared_ptr module_params, - std::shared_ptr intrinsic_module, std::shared_ptr host_api_factory, - std::shared_ptr block_header_repo, - std::shared_ptr last_compiled_module, - std::shared_ptr cache); + std::shared_ptr module_factory); enum class MemoryOrigin { EXTERNAL, INTERNAL }; [[nodiscard]] InstanceEnvironment make( @@ -60,13 +51,9 @@ namespace kagome::runtime::wavm { private: std::shared_ptr storage_; std::shared_ptr serializer_; - std::shared_ptr compartment_; - std::shared_ptr module_params_; - std::shared_ptr intrinsic_module_; std::shared_ptr host_api_factory_; - std::shared_ptr block_header_repo_; - std::shared_ptr last_compiled_module_; - std::shared_ptr cache_; + std::shared_ptr module_factory_; + }; } // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/module.cpp b/core/runtime/wavm/module.cpp index 0345dd0e42..2146ab766f 100644 --- a/core/runtime/wavm/module.cpp +++ b/core/runtime/wavm/module.cpp @@ -78,7 +78,8 @@ namespace kagome::runtime::wavm { BOOST_ASSERT(module_); } - std::shared_ptr ModuleImpl::instantiate() const { + outcome::result> ModuleImpl::instantiate() + const { #if defined(__GNUC__) and not defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" diff --git a/core/runtime/wavm/module.hpp b/core/runtime/wavm/module.hpp index 0b3aa78f7d..62ed823c5c 100644 --- a/core/runtime/wavm/module.hpp +++ b/core/runtime/wavm/module.hpp @@ -40,7 +40,7 @@ namespace kagome::runtime::wavm { common::BufferView code, const common::Hash256 &code_hash); - std::shared_ptr instantiate() + outcome::result> instantiate() const override; ModuleImpl(std::shared_ptr compartment, diff --git a/core/runtime/wavm/module_factory_impl.cpp b/core/runtime/wavm/module_factory_impl.cpp index e5de5d5003..8041b145b8 100644 --- a/core/runtime/wavm/module_factory_impl.cpp +++ b/core/runtime/wavm/module_factory_impl.cpp @@ -7,6 +7,7 @@ #include "runtime/wavm/module_factory_impl.hpp" #include "crypto/hasher.hpp" +#include "runtime/wavm/instance_environment_factory.hpp" #include "runtime/wavm/module.hpp" #include "runtime/wavm/module_cache.hpp" #include "runtime/wavm/module_params.hpp" @@ -16,18 +17,27 @@ namespace kagome::runtime::wavm { ModuleFactoryImpl::ModuleFactoryImpl( std::shared_ptr compartment, std::shared_ptr module_params, - std::shared_ptr env_factory, + std::shared_ptr host_api_factory, + std::shared_ptr storage, + std::shared_ptr serializer, std::shared_ptr intrinsic_module, + std::shared_ptr last_compiled_module, std::optional> module_cache, std::shared_ptr hasher) : compartment_{std::move(compartment)}, module_params_{std::move(module_params)}, - env_factory_{std::move(env_factory)}, + host_api_factory_{host_api_factory}, + storage_{storage}, + serializer_{serializer}, + last_compiled_module_{last_compiled_module}, intrinsic_module_{std::move(intrinsic_module)}, hasher_(std::move(hasher)) { BOOST_ASSERT(compartment_ != nullptr); BOOST_ASSERT(module_params_ != nullptr); - BOOST_ASSERT(env_factory_ != nullptr); + BOOST_ASSERT(host_api_factory_ != nullptr); + BOOST_ASSERT(storage_ != nullptr); + BOOST_ASSERT(serializer_ != nullptr); + BOOST_ASSERT(last_compiled_module_ != nullptr); BOOST_ASSERT(intrinsic_module_ != nullptr); BOOST_ASSERT(hasher_ != nullptr); @@ -38,11 +48,13 @@ namespace kagome::runtime::wavm { outcome::result, CompilationError> ModuleFactoryImpl::make(common::BufferView code) const { + auto env_factory = std::make_shared( + storage_, serializer_, host_api_factory_, shared_from_this()); OUTCOME_TRY(module, ModuleImpl::compileFrom(compartment_, *module_params_, intrinsic_module_, - env_factory_, + env_factory, code, hasher_->sha2_256(code))); return module; diff --git a/core/runtime/wavm/module_factory_impl.hpp b/core/runtime/wavm/module_factory_impl.hpp index 58b4681846..2e31cd86d1 100644 --- a/core/runtime/wavm/module_factory_impl.hpp +++ b/core/runtime/wavm/module_factory_impl.hpp @@ -13,25 +13,44 @@ namespace kagome::application { class AppConfiguration; } + namespace kagome::crypto { class Hasher; } +namespace kagome::host_api { + class HostApiFactory; +} + +namespace kagome::storage::trie { + class TrieStorage; + class TrieSerializer; +} // namespace kagome::storage::trie + +namespace kagome::runtime { + class SingleModuleCache; +} + namespace kagome::runtime::wavm { class CompartmentWrapper; - class InstanceEnvironmentFactory; + class IntrinsicModule; struct ModuleParams; struct ModuleCache; - class ModuleFactoryImpl final : public ModuleFactory { + class ModuleFactoryImpl final + : public ModuleFactory, + public std::enable_shared_from_this { public: ModuleFactoryImpl( std::shared_ptr compartment, std::shared_ptr module_params, - std::shared_ptr env_factory, + std::shared_ptr host_api_factory, + std::shared_ptr storage, + std::shared_ptr serializer, std::shared_ptr intrinsic_module, + std::shared_ptr last_compiled_module, std::optional> module_cache, std::shared_ptr hasher); @@ -41,7 +60,10 @@ namespace kagome::runtime::wavm { private: std::shared_ptr compartment_; std::shared_ptr module_params_; - std::shared_ptr env_factory_; + std::shared_ptr host_api_factory_; + std::shared_ptr storage_; + std::shared_ptr serializer_; + std::shared_ptr last_compiled_module_; std::shared_ptr intrinsic_module_; std::shared_ptr hasher_; }; diff --git a/core/runtime/wavm/module_instance.cpp b/core/runtime/wavm/module_instance.cpp index b4fcecface..025c7803c4 100644 --- a/core/runtime/wavm/module_instance.cpp +++ b/core/runtime/wavm/module_instance.cpp @@ -106,13 +106,17 @@ namespace kagome::runtime::wavm { return module_; } - outcome::result ModuleInstanceImpl::callExportFunction( - std::string_view name, common::BufferView encoded_args) const { + outcome::result ModuleInstanceImpl::callExportFunction( + kagome::runtime::RuntimeContext + &, // not used, but has to have been created before the call + std::string_view name, + common::BufferView encoded_args) const { auto memory = env_.memory_provider->getCurrentMemory().value(); PtrSize args_span{memory.get().storeBuffer(encoded_args)}; - auto res = [this, name, args_span]() -> outcome::result { + auto res = + [this, name, args_span, &memory]() -> outcome::result { WAVM::Runtime::GCPointer context = WAVM::Runtime::createContext(compartment_->getCompartment()); WAVM::Runtime::Function *function = WAVM::Runtime::asFunctionNullable( @@ -162,7 +166,9 @@ namespace kagome::runtime::wavm { untaggedInvokeArgs.data(), resultsDestination); }); - return PtrSize{untaggedInvokeResults[0].u64}; + auto [res_ptr, res_size] = PtrSize{untaggedInvokeResults[0].i64}; + return memory.get().loadN(res_ptr, res_size); + } catch (WAVM::Runtime::Exception *e) { const auto desc = WAVM::Runtime::describeException(e); logger_->error(desc); diff --git a/core/runtime/wavm/module_instance.hpp b/core/runtime/wavm/module_instance.hpp index e588ed2423..e057379038 100644 --- a/core/runtime/wavm/module_instance.hpp +++ b/core/runtime/wavm/module_instance.hpp @@ -50,8 +50,10 @@ namespace kagome::runtime::wavm { std::shared_ptr getModule() const override; - outcome::result callExportFunction( - std::string_view name, common::BufferView encoded_args) const override; + outcome::result callExportFunction( + kagome::runtime::RuntimeContext &, + std::string_view name, + common::BufferView encoded_args) const override; outcome::result> getGlobal( std::string_view name) const override; diff --git a/core/storage/rocksdb/rocksdb_spaces.cpp b/core/storage/rocksdb/rocksdb_spaces.cpp index 338aae1af8..e4eb18b498 100644 --- a/core/storage/rocksdb/rocksdb_spaces.cpp +++ b/core/storage/rocksdb/rocksdb_spaces.cpp @@ -20,6 +20,7 @@ namespace kagome::storage { "block_body", "justification", "trie_node", + "trie_value", "dispute_data", "beefy_justification", }; diff --git a/core/storage/spaces.hpp b/core/storage/spaces.hpp index 3f81a3be2e..286da4bcd1 100644 --- a/core/storage/spaces.hpp +++ b/core/storage/spaces.hpp @@ -20,6 +20,7 @@ namespace kagome::storage { kBlockBody, kJustification, kTrieNode, + kTrieValue, kDisputeData, kBeefyJustification, diff --git a/core/storage/trie/impl/trie_storage_backend_impl.cpp b/core/storage/trie/impl/trie_storage_backend_impl.cpp index 1640262cf9..b11c13313e 100644 --- a/core/storage/trie/impl/trie_storage_backend_impl.cpp +++ b/core/storage/trie/impl/trie_storage_backend_impl.cpp @@ -8,13 +8,14 @@ #include +#include "storage/spaces.hpp" #include "storage/trie/impl/trie_storage_backend_batch.hpp" namespace kagome::storage::trie { TrieStorageBackendImpl::TrieStorageBackendImpl( - std::shared_ptr storage) - : storage_{std::move(storage)} { + std::shared_ptr storage) + : storage_{storage->getSpace(Space::kTrieNode)} { BOOST_ASSERT(storage_ != nullptr); } diff --git a/core/storage/trie/impl/trie_storage_backend_impl.hpp b/core/storage/trie/impl/trie_storage_backend_impl.hpp index b46a806d7c..e90758f962 100644 --- a/core/storage/trie/impl/trie_storage_backend_impl.hpp +++ b/core/storage/trie/impl/trie_storage_backend_impl.hpp @@ -8,13 +8,14 @@ #include "common/buffer.hpp" #include "outcome/outcome.hpp" +#include "storage/spaced_storage.hpp" #include "storage/trie/trie_storage_backend.hpp" namespace kagome::storage::trie { class TrieStorageBackendImpl : public TrieStorageBackend { public: - TrieStorageBackendImpl(std::shared_ptr storage); + TrieStorageBackendImpl(std::shared_ptr storage); ~TrieStorageBackendImpl() override = default; diff --git a/core/storage/trie/serialization/trie_serializer_impl.cpp b/core/storage/trie/serialization/trie_serializer_impl.cpp index 15b68ab08f..fc546d78e5 100644 --- a/core/storage/trie/serialization/trie_serializer_impl.cpp +++ b/core/storage/trie/serialization/trie_serializer_impl.cpp @@ -17,13 +17,13 @@ namespace kagome::storage::trie { TrieSerializerImpl::TrieSerializerImpl( std::shared_ptr factory, std::shared_ptr codec, - std::shared_ptr backend) + std::shared_ptr node_backend) : trie_factory_{std::move(factory)}, codec_{std::move(codec)}, - backend_{std::move(backend)} { + node_backend_{std::move(node_backend)} { BOOST_ASSERT(trie_factory_ != nullptr); BOOST_ASSERT(codec_ != nullptr); - BOOST_ASSERT(backend_ != nullptr); + BOOST_ASSERT(node_backend_ != nullptr); } RootHash TrieSerializerImpl::getEmptyRootHash() const { @@ -65,7 +65,7 @@ namespace kagome::storage::trie { outcome::result TrieSerializerImpl::storeRootNode( TrieNode &node, StateVersion version) { - auto batch = backend_->batch(); + auto batch = node_backend_->batch(); BOOST_ASSERT(batch != nullptr); OUTCOME_TRY( @@ -114,7 +114,7 @@ namespace kagome::storage::trie { } BufferOrView enc; if (auto hash = db_key.asHash()) { - BOOST_OUTCOME_TRY(enc, backend_->get(*hash)); + BOOST_OUTCOME_TRY(enc, node_backend_->get(*hash)); if (on_node_loaded) { on_node_loaded(*hash, enc); } @@ -130,7 +130,7 @@ namespace kagome::storage::trie { outcome::result> TrieSerializerImpl::retrieveValue(const common::Hash256 &hash, const OnNodeLoaded &on_node_loaded) const { - OUTCOME_TRY(value, backend_->tryGet(hash)); + OUTCOME_TRY(value, node_backend_->tryGet(hash)); return common::map_optional(std::move(value), [&](common::BufferOrView &&value) { if (on_node_loaded) { diff --git a/core/storage/trie/serialization/trie_serializer_impl.hpp b/core/storage/trie/serialization/trie_serializer_impl.hpp index cb9bae1f41..3411c8fea7 100644 --- a/core/storage/trie/serialization/trie_serializer_impl.hpp +++ b/core/storage/trie/serialization/trie_serializer_impl.hpp @@ -24,7 +24,7 @@ namespace kagome::storage::trie { public: TrieSerializerImpl(std::shared_ptr factory, std::shared_ptr codec, - std::shared_ptr backend); + std::shared_ptr node_backend); ~TrieSerializerImpl() override = default; RootHash getEmptyRootHash() const override; @@ -66,6 +66,6 @@ namespace kagome::storage::trie { std::shared_ptr trie_factory_; std::shared_ptr codec_; - std::shared_ptr backend_; + std::shared_ptr node_backend_; }; } // namespace kagome::storage::trie diff --git a/core/storage/trie/trie_storage_backend.hpp b/core/storage/trie/trie_storage_backend.hpp index 2c57c10172..2a893ba1b8 100644 --- a/core/storage/trie/trie_storage_backend.hpp +++ b/core/storage/trie/trie_storage_backend.hpp @@ -15,7 +15,7 @@ namespace kagome::storage::trie { /** - * Adapter for key-value storages that allows to hide keyspace separation + * Adapter for the trie node storage that allows to hide keyspace separation * along with root hash storing logic from the trie db component */ class TrieStorageBackend : public BufferStorage { diff --git a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp index e3302f8fe2..b6ce610e36 100644 --- a/core/storage/trie_pruner/impl/trie_pruner_impl.cpp +++ b/core/storage/trie_pruner/impl/trie_pruner_impl.cpp @@ -8,13 +8,13 @@ #include +#include #include #include "application/app_configuration.hpp" #include "application/app_state_manager.hpp" #include "blockchain/block_tree.hpp" #include "crypto/hasher/hasher_impl.hpp" -#include "log/formatters/optional.hpp" #include "storage/database_error.hpp" #include "storage/predefined_keys.hpp" #include "storage/spaced_storage.hpp" @@ -60,20 +60,20 @@ namespace kagome::storage::trie_pruner { TriePrunerImpl::TriePrunerImpl( std::shared_ptr app_state_manager, - std::shared_ptr trie_storage, + std::shared_ptr node_storage, std::shared_ptr serializer, std::shared_ptr codec, std::shared_ptr storage, std::shared_ptr hasher, std::shared_ptr config) - : trie_storage_{trie_storage}, + : node_storage_{node_storage}, serializer_{serializer}, codec_{codec}, storage_{storage}, hasher_{hasher}, pruning_depth_{config->statePruningDepth()}, thorough_pruning_{config->enableThoroughPruning()} { - BOOST_ASSERT(trie_storage_ != nullptr); + BOOST_ASSERT(node_storage_ != nullptr); BOOST_ASSERT(serializer_ != nullptr); BOOST_ASSERT(codec_ != nullptr); BOOST_ASSERT(storage_ != nullptr); @@ -175,9 +175,9 @@ namespace kagome::storage::trie_pruner { outcome::result TriePrunerImpl::pruneFinalized( const primitives::BlockHeader &block) { std::unique_lock lock{mutex_}; - auto batch = trie_storage_->batch(); - OUTCOME_TRY(prune(*batch, block.state_root)); - OUTCOME_TRY(batch->commit()); + auto node_batch = node_storage_->batch(); + OUTCOME_TRY(prune(*node_batch, block.state_root)); + OUTCOME_TRY(node_batch->commit()); last_pruned_block_ = block.blockInfo(); OUTCOME_TRY(savePersistentState()); @@ -188,13 +188,15 @@ namespace kagome::storage::trie_pruner { const primitives::BlockHeader &block) { std::unique_lock lock{mutex_}; // should prune even when pruning depth is none - auto batch = trie_storage_->batch(); - OUTCOME_TRY(prune(*batch, block.state_root)); - OUTCOME_TRY(batch->commit()); + auto node_batch = node_storage_->batch(); + auto value_batch = node_storage_->batch(); + OUTCOME_TRY(prune(*node_batch, block.state_root)); + OUTCOME_TRY(node_batch->commit()); + OUTCOME_TRY(value_batch->commit()); return outcome::success(); } - outcome::result TriePrunerImpl::prune(BufferBatch &batch, + outcome::result TriePrunerImpl::prune(BufferBatch &node_batch, const trie::RootHash &root_hash) { auto trie_res = serializer_->retrieveTrie(root_hash, nullptr); if (trie_res.has_error() @@ -213,12 +215,12 @@ namespace kagome::storage::trie_pruner { return outcome::success(); } - OUTCOME_TRY( - forEachChildTrie(*trie, - [this, &batch](common::BufferView child_key, - const trie::RootHash &child_hash) { - return prune(batch, child_hash); - })); + OUTCOME_TRY(forEachChildTrie( + *trie, + [this, &node_batch](common::BufferView child_key, + const trie::RootHash &child_hash) { + return prune(node_batch, child_hash); + })); size_t nodes_removed = 0; size_t values_removed = 0; @@ -267,18 +269,18 @@ namespace kagome::storage::trie_pruner { && ref_count == 0) { nodes_removed++; ref_count_.erase(ref_count_it); - OUTCOME_TRY(batch.remove(hash)); + OUTCOME_TRY(node_batch.remove(hash)); auto hash_opt = node->getValue().hash; if (hash_opt.has_value()) { - auto &hash = *hash_opt; - auto value_ref_it = value_ref_count_.find(hash); + auto &value_hash = *hash_opt; + auto value_ref_it = value_ref_count_.find(value_hash); if (value_ref_it == value_ref_count_.end()) { values_unknown++; } else { auto &value_ref_count = value_ref_it->second; value_ref_count--; if (value_ref_count == 0) { - OUTCOME_TRY(batch.remove(hash)); + OUTCOME_TRY(node_batch.remove(value_hash)); value_ref_count_.erase(value_ref_it); values_removed++; } @@ -383,7 +385,7 @@ namespace kagome::storage::trie_pruner { queued_nodes.pop_back(); auto &ref_count = ref_count_[hash]; if (ref_count == 0 && !thorough_pruning_) { - OUTCOME_TRY(hash_is_in_storage, trie_storage_->contains(hash)); + OUTCOME_TRY(hash_is_in_storage, node_storage_->contains(hash)); if (hash_is_in_storage) { // the node is present in storage but pruner has not indexed it // because pruner has been initialized on a newer state @@ -404,7 +406,13 @@ namespace kagome::storage::trie_pruner { if (is_new_node_with_value) { auto value_hash_opt = encoder.getValueHash(*node, version); if (value_hash_opt) { - value_ref_count_[*value_hash_opt]++; + auto &value_ref_count = value_ref_count_[*value_hash_opt]; + OUTCOME_TRY(contains_value, + node_storage_->contains(*value_hash_opt)); + if (value_ref_count == 0 && contains_value && !thorough_pruning_) { + value_ref_count++; + } + value_ref_count++; referenced_values_num++; } } diff --git a/core/storage/trie_pruner/impl/trie_pruner_impl.hpp b/core/storage/trie_pruner/impl/trie_pruner_impl.hpp index 909add3752..9bf4d8642c 100644 --- a/core/storage/trie_pruner/impl/trie_pruner_impl.hpp +++ b/core/storage/trie_pruner/impl/trie_pruner_impl.hpp @@ -62,7 +62,7 @@ namespace kagome::storage::trie_pruner { TriePrunerImpl( std::shared_ptr app_state_manager, - std::shared_ptr trie_storage, + std::shared_ptr node_storage, std::shared_ptr serializer, std::shared_ptr codec, std::shared_ptr storage, @@ -121,7 +121,7 @@ namespace kagome::storage::trie_pruner { const primitives::BlockHeader &last_pruned_block, const blockchain::BlockTree &block_tree); - outcome::result prune(BufferBatch &batch, + outcome::result prune(BufferBatch &node_batch, const storage::trie::RootHash &state); outcome::result addNewStateWith( @@ -136,7 +136,7 @@ namespace kagome::storage::trie_pruner { std::unordered_set immortal_nodes_; std::optional last_pruned_block_; - std::shared_ptr trie_storage_; + std::shared_ptr node_storage_; std::shared_ptr serializer_; std::shared_ptr codec_; std::shared_ptr storage_; diff --git a/core/telemetry/CMakeLists.txt b/core/telemetry/CMakeLists.txt index 9c31bf0e81..c5f13448b3 100644 --- a/core/telemetry/CMakeLists.txt +++ b/core/telemetry/CMakeLists.txt @@ -22,5 +22,6 @@ target_link_libraries(telemetry RapidJSON::rapidjson p2p::p2p_asio_scheduler_backend p2p::p2p_multiaddress + p2p::p2p_peer_id ) kagome_install(telemetry) diff --git a/core/utils/kagome_db_editor.cpp b/core/utils/kagome_db_editor.cpp index 46cd22f547..f3071d5ebc 100644 --- a/core/utils/kagome_db_editor.cpp +++ b/core/utils/kagome_db_editor.cpp @@ -251,9 +251,8 @@ int db_editor_main(int argc, const char **argv) { return 0; } - auto trie_buffer_storage = storage->getSpace(storage::Space::kTrieNode); - auto trie_tracker = std::make_shared( - std::make_shared(trie_buffer_storage)); + auto trie_node_tracker = std::make_shared( + std::make_shared(storage)); auto injector = di::make_injector( di::bind.template to([](const auto &injector) { @@ -262,7 +261,7 @@ int db_editor_main(int argc, const char **argv) { injector.template create>(), injector.template create>()); }), - di::bind.template to(trie_tracker), + di::bind.template to(trie_node_tracker), di::bind.template to( std::shared_ptr(nullptr)), di::bind.template to(), @@ -396,34 +395,41 @@ int db_editor_main(int argc, const char **argv) { } } - auto db_cursor = trie_buffer_storage->cursor(); - auto db_batch = trie_buffer_storage->batch(); - auto res = check(db_cursor->seekFirst()); - int count = 0; - { - TicToc t2("Process DB.", log); - while (db_cursor->isValid() && db_cursor->key().has_value()) { - auto key = db_cursor->key().value(); - if (trie_tracker->tracked(key)) { - db_cursor->next().value(); - continue; - } - auto res2 = check(db_batch->remove(key)); - count++; - if (not(count % 10000000)) { - log->trace("{} keys were processed at the db.", count); - res2 = check(db_batch->commit()); - dynamic_cast(buffer_storage.get()) - ->compact(prefix, check(db_cursor->key()).value()); - db_cursor = buffer_storage->cursor(); - db_batch = buffer_storage->batch(); - res = check(db_cursor->seek(key)); + auto trie_node_storage = storage->getSpace(storage::Space::kTrieNode); + auto trie_value_storage = storage->getSpace(storage::Space::kTrieValue); + + auto track_trie_entries = [&log, &buffer_storage, &prefix](auto storage, + auto tracker) { + auto db_cursor = storage->cursor(); + auto db_batch = storage->batch(); + auto res = check(db_cursor->seekFirst()); + int count = 0; + { + TicToc t2("Process DB.", log); + while (db_cursor->isValid() && db_cursor->key().has_value()) { + auto key = db_cursor->key().value(); + if (tracker->tracked(key)) { + db_cursor->next().value(); + continue; + } + auto res2 = check(db_batch->remove(key)); + count++; + if (not(count % 10000000)) { + log->trace("{} keys were processed at the db.", count); + res2 = check(db_batch->commit()); + dynamic_cast(buffer_storage.get()) + ->compact(prefix, check(db_cursor->key()).value()); + db_cursor = buffer_storage->cursor(); + db_batch = buffer_storage->batch(); + res = check(db_cursor->seek(key)); + } + res2 = check(db_cursor->next()); } - res2 = check(db_cursor->next()); + std::ignore = check(db_batch->commit()); } - std::ignore = check(db_batch->commit()); - } - log->trace("{} keys were processed at the db.", ++count); + log->trace("{} keys were processed at the db.", ++count); + }; + track_trie_entries(trie_node_storage, trie_node_tracker); { TicToc t4("Compaction 1.", log); diff --git a/core/utils/storage_explorer.cpp b/core/utils/storage_explorer.cpp index cab9c393e8..7ef7d8d0fd 100644 --- a/core/utils/storage_explorer.cpp +++ b/core/utils/storage_explorer.cpp @@ -83,10 +83,10 @@ class Command { } template - [[noreturn]] void throwError(const char *fmt, Ts &&...ts) const { + [[noreturn]] void throwError(const char *fmt, const Ts &...ts) const { throw CommandExecutionError( name, - ::fmt::vformat(fmt, fmt::make_format_args(std::forward(ts)...))); + ::fmt::vformat(fmt, fmt::make_format_args(ts...))); } template @@ -104,8 +104,6 @@ class Command { class CommandParser { public: - using CommandFunctor = std::function; - void addCommand(std::unique_ptr cmd) { std::string name{cmd->getName()}; commands_.insert({name, std::move(cmd)}); @@ -115,6 +113,7 @@ class CommandParser { if (args.size() < 2) { std::cerr << "Unspecified command!\nAvailable commands are:\n"; printCommands(std::cerr); + return; } if (auto command = commands_.find(args[1]); command != commands_.cend()) { ArgumentList cmd_args{args.subspan(1)}; @@ -516,6 +515,35 @@ class SearchChainCommand : public Command { std::shared_ptr hasher; }; +class ChainInfoCommand final : public Command { + public: + ChainInfoCommand(std::shared_ptr block_tree) + : Command{"chain-info", "Print general info about the current chain. "}, + block_tree{block_tree} { + BOOST_ASSERT(block_tree); + } + + virtual void execute(std::ostream &out, const ArgumentList &args) override { + if (args.size() > 1) { + throwError("No arguments expected, {} arguments received", args.size()); + } + fmt::print(out, "Last finalized: {}\n", block_tree->getLastFinalized()); + fmt::print(out, "Best block: {}\n", block_tree->bestBlock()); + fmt::print(out, "Genesis block: {}\n", block_tree->getGenesisBlockHash()); + fmt::print(out, "Leaves:\n"); + for (auto &leaf : block_tree->getLeaves()) { + auto header_res = block_tree->getBlockHeader(leaf); + if (!header_res) { + throwError("Error loading block header: {}", header_res.error()); + } + fmt::print(out, "\t#{} - {}\n", header_res.value().number, leaf); + } + } + + private: + std::shared_ptr block_tree; +}; + int storage_explorer_main(int argc, const char **argv) { ArgumentList args(argv, argc); @@ -570,6 +598,7 @@ int storage_explorer_main(int argc, const char **argv) { parser.addCommand(std::make_unique(block_storage)); parser.addCommand(std::make_unique(block_storage)); parser.addCommand(std::make_unique(trie_storage)); + parser.addCommand(std::make_unique(block_tree)); parser.addCommand(std::make_unique( block_storage, trie_storage, authority_manager, hasher)); diff --git a/test/core/api/service/state/state_api_test.cpp b/test/core/api/service/state/state_api_test.cpp index 158331dc0d..7a1ee47a11 100644 --- a/test/core/api/service/state/state_api_test.cpp +++ b/test/core/api/service/state/state_api_test.cpp @@ -9,18 +9,19 @@ #include "api/service/state/impl/state_api_impl.hpp" #include "api/service/state/requests/get_metadata.hpp" #include "api/service/state/requests/subscribe_storage.hpp" -#include "core/storage/trie/polkadot_trie_cursor_dummy.hpp" #include "mock/core/api/service/api_service_mock.hpp" #include "mock/core/api/service/state/state_api_mock.hpp" #include "mock/core/blockchain/block_header_repository_mock.hpp" #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/runtime/core_mock.hpp" -#include "mock/core/runtime/executor_mock.hpp" #include "mock/core/runtime/metadata_mock.hpp" +#include "mock/core/runtime/runtime_context_factory_mock.hpp" #include "mock/core/storage/trie/trie_batches_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" #include "primitives/block_header.hpp" +#include "runtime/executor.hpp" #include "runtime/runtime_context.hpp" +#include "core/storage/trie/polkadot_trie_cursor_dummy.hpp" #include "testutil/lazy.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" @@ -35,7 +36,7 @@ using kagome::primitives::BlockHeader; using kagome::primitives::BlockInfo; using kagome::primitives::BlockNumber; using kagome::runtime::CoreMock; -using kagome::runtime::ExecutorMock; +using kagome::runtime::Executor; using kagome::runtime::MetadataMock; using kagome::storage::trie::TrieBatchMock; using kagome::storage::trie::TrieStorageMock; @@ -58,6 +59,8 @@ namespace kagome::api { class StateApiTest : public ::testing::Test { public: void SetUp() override { + executor_ = std::make_shared( + std::make_shared()); api_ = std::make_unique( block_header_repo_, storage_, @@ -79,7 +82,7 @@ namespace kagome::api { std::shared_ptr metadata_ = std::make_shared(); std::shared_ptr api_service_ = std::make_shared(); - std::shared_ptr executor_ = std::make_shared(); + std::shared_ptr executor_; std::unique_ptr api_{}; }; @@ -125,7 +128,8 @@ namespace kagome::api { auto runtime_core = std::make_shared(); auto metadata = std::make_shared(); - auto executor = std::make_shared(); + auto executor = std::make_shared( + std::make_shared()); api_ = std::make_shared( block_header_repo_, diff --git a/test/core/blockchain/block_storage_test.cpp b/test/core/blockchain/block_storage_test.cpp index 3587759345..6106374810 100644 --- a/test/core/blockchain/block_storage_test.cpp +++ b/test/core/blockchain/block_storage_test.cpp @@ -10,7 +10,7 @@ #include "blockchain/block_storage_error.hpp" #include "mock/core/crypto/hasher_mock.hpp" -#include "mock/core/storage/persistent_map_mock.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" #include "scale/scale.hpp" #include "storage/database_error.hpp" diff --git a/test/core/network/state_protocol_observer_test.cpp b/test/core/network/state_protocol_observer_test.cpp index 011c4022bf..f0cdf9e8fd 100644 --- a/test/core/network/state_protocol_observer_test.cpp +++ b/test/core/network/state_protocol_observer_test.cpp @@ -12,7 +12,7 @@ #include "mock/core/blockchain/block_header_repository_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "network/types/state_request.hpp" -#include "storage/in_memory/in_memory_storage.hpp" +#include "storage/in_memory/in_memory_spaced_storage.hpp" #include "storage/trie/impl/trie_storage_backend_impl.hpp" #include "storage/trie/impl/trie_storage_impl.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_factory_impl.hpp" @@ -39,14 +39,16 @@ using testing::_; using testing::Return; std::shared_ptr makeEmptyInMemoryTrie() { - auto backend = + auto spaced_storage = + std::make_shared(); + auto node_backend = std::make_shared( - std::make_shared()); + spaced_storage); auto trie_factory = std::make_shared(); auto codec = std::make_shared(); - auto serializer = - std::make_shared(trie_factory, codec, backend); + auto serializer = std::make_shared( + trie_factory, codec, node_backend); auto state_pruner = std::make_shared(); ON_CALL(*state_pruner, addNewState(testing::A(), _)) diff --git a/test/core/network/synchronizer_test.cpp b/test/core/network/synchronizer_test.cpp index d3e5304002..4169d458c7 100644 --- a/test/core/network/synchronizer_test.cpp +++ b/test/core/network/synchronizer_test.cpp @@ -19,7 +19,9 @@ #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/network/protocols/sync_protocol_mock.hpp" #include "mock/core/network/router_mock.hpp" -#include "mock/core/storage/persistent_map_mock.hpp" +#include "mock/core/runtime/module_factory_mock.hpp" +#include "mock/core/runtime/runtime_properties_cache_mock.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" #include "mock/core/storage/trie/trie_storage_backend_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" @@ -88,7 +90,7 @@ class SynchronizerTest block_tree, block_appender, block_executor, - trie_db, + trie_node_db, storage, state_pruner, router, @@ -108,7 +110,7 @@ class SynchronizerTest std::make_shared(); std::shared_ptr block_executor = std::make_shared(); - std::shared_ptr trie_db = + std::shared_ptr trie_node_db = std::make_shared(); std::shared_ptr storage = std::make_shared(); diff --git a/test/core/parachain/pvf_test.cpp b/test/core/parachain/pvf_test.cpp index e482658766..4426d1359e 100644 --- a/test/core/parachain/pvf_test.cpp +++ b/test/core/parachain/pvf_test.cpp @@ -14,15 +14,16 @@ #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/crypto/sr25519_provider_mock.hpp" #include "mock/core/host_api/host_api_mock.hpp" -#include "mock/core/runtime/executor_mock.hpp" #include "mock/core/runtime/memory_provider_mock.hpp" #include "mock/core/runtime/module_factory_mock.hpp" #include "mock/core/runtime/module_instance_mock.hpp" #include "mock/core/runtime/module_mock.hpp" #include "mock/core/runtime/parachain_host_mock.hpp" +#include "mock/core/runtime/runtime_context_factory_mock.hpp" #include "mock/core/runtime/runtime_properties_cache_mock.hpp" #include "mock/core/runtime/trie_storage_provider_mock.hpp" #include "parachain/types.hpp" +#include "runtime/executor.hpp" #include "testutil/literals.hpp" #include "testutil/outcome.hpp" #include "testutil/prepare_loggers.hpp" @@ -68,10 +69,7 @@ class PvfTest : public testing::Test { ctx_factory = std::make_shared(); auto cache = std::make_shared(); - auto executor = std::make_shared(ctx_factory, cache); - kagome::parachain::ValidationResult res; - ON_CALL(*executor, callWithCtx(_, "validate_block", _)) - .WillByDefault(Return(Buffer{scale::encode(res).value()})); + auto executor = std::make_shared(ctx_factory, cache); EXPECT_CALL(*parachain_api, check_validation_outputs(_, _, _)) .WillRepeatedly(Return(outcome::success(true))); @@ -105,11 +103,15 @@ class PvfTest : public testing::Test { auto module = std::make_shared(); auto instance = std::make_shared(); ON_CALL(*module, instantiate()).WillByDefault(Return(instance)); + auto res = scale::encode(kagome::parachain::ValidationResult{}).value(); + ON_CALL(*instance, callExportFunction(_, "validate_block", _)) + .WillByDefault(Return(outcome::success(res))); ON_CALL(*instance, getCodeHash()).WillByDefault(ReturnRef(code_hash)); ON_CALL(*ctx_factory, ephemeral(_, _, _)) .WillByDefault(Invoke([instance]() { return runtime::RuntimeContext::create_TEST(instance); })); + return module; } diff --git a/test/core/runtime/CMakeLists.txt b/test/core/runtime/CMakeLists.txt index a7fd08d09b..d95f9e613a 100644 --- a/test/core/runtime/CMakeLists.txt +++ b/test/core/runtime/CMakeLists.txt @@ -3,8 +3,10 @@ # All Rights Reserved # SPDX-License-Identifier: Apache-2.0 # - +if ("${WASM_COMPILER}" STREQUAL "WAVM") add_subdirectory(wavm) +endif() + add_subdirectory(binaryen) addtest(wasm_result_test diff --git a/test/core/runtime/binaryen/CMakeLists.txt b/test/core/runtime/binaryen/CMakeLists.txt index c1f97c812f..76986e9c5b 100644 --- a/test/core/runtime/binaryen/CMakeLists.txt +++ b/test/core/runtime/binaryen/CMakeLists.txt @@ -19,7 +19,6 @@ target_link_libraries(binaryen_runtime_test INTERFACE host_api host_api_factory core_api - binaryen_executor_factory binaryen_module_factory binaryen_wasm_memory_factory module_repository @@ -36,6 +35,7 @@ target_link_libraries(tagged_transaction_queue_test testutil_primitives_generator basic_code_provider core_api + core_api_factory logger_for_tests filesystem ) @@ -60,6 +60,7 @@ target_link_libraries(parachain_test parachain_host_api basic_code_provider core_api + core_api_factory filesystem hasher pbkdf2_provider @@ -75,6 +76,7 @@ target_link_libraries(metadata_test basic_code_provider storage core_api + core_api_factory logger_for_tests ) diff --git a/test/core/runtime/binaryen/binaryen_runtime_test.hpp b/test/core/runtime/binaryen/binaryen_runtime_test.hpp index 1f5303d87d..c6592562a6 100644 --- a/test/core/runtime/binaryen/binaryen_runtime_test.hpp +++ b/test/core/runtime/binaryen/binaryen_runtime_test.hpp @@ -11,10 +11,10 @@ #include "mock/core/storage/trie/trie_storage_mock.hpp" #include "runtime/binaryen/binaryen_memory_factory.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/binaryen/core_api_factory_impl.hpp" #include "runtime/binaryen/instance_environment_factory.hpp" #include "runtime/binaryen/memory_impl.hpp" #include "runtime/binaryen/module/module_factory_impl.hpp" +#include "runtime/common/core_api_factory_impl.hpp" class BinaryenRuntimeTest : public RuntimeTestBase { public: @@ -28,11 +28,7 @@ class BinaryenRuntimeTest : public RuntimeTestBase { auto instance_env_factory = std::make_shared( - trie_storage_, - serializer_, - host_api_factory_, - header_repo_, - cache_); + trie_storage_, serializer_, host_api_factory_); auto module_factory = std::make_shared( diff --git a/test/core/runtime/executor_test.cpp b/test/core/runtime/executor_test.cpp index dff6d5a604..56f73b41cf 100644 --- a/test/core/runtime/executor_test.cpp +++ b/test/core/runtime/executor_test.cpp @@ -83,13 +83,12 @@ class ExecutorTest : public testing::Test { enum class CallType { Persistent, Ephemeral }; - void prepareCall(const kagome::primitives::BlockInfo &blockchain_state, - const kagome::storage::trie::RootHash &storage_state, - CallType type, - const Buffer &encoded_args, - int res) { - static constexpr PtrSize RESULT_LOCATION{3, 4}; - + outcome::result prepareCall( + const kagome::primitives::BlockInfo &blockchain_state, + const kagome::storage::trie::RootHash &storage_state, + CallType type, + const Buffer &encoded_args, + int res) { EXPECT_CALL(*header_repo_, getBlockHeader(blockchain_state.hash)) .WillRepeatedly(Return(kagome::primitives::BlockHeader{ blockchain_state.number, // number @@ -102,20 +101,21 @@ class ExecutorTest : public testing::Test { auto module_instance = std::make_shared(); EXPECT_CALL(*module_instance, resetEnvironment()) .WillRepeatedly(Return(outcome::success())); - EXPECT_CALL(*module_instance, resetMemory(_)) - .WillRepeatedly(Return(outcome::success())); static const auto code_hash = "code_hash"_hash256; EXPECT_CALL(*module_instance, getCodeHash()) .WillRepeatedly(ReturnRef(code_hash)); + Buffer enc_res{scale::encode(res).value()}; EXPECT_CALL(*module_instance, - callExportFunction(std::string_view{"addTwo"}, _)) - .WillRepeatedly(Return(RESULT_LOCATION)); + callExportFunction(_, std::string_view{"addTwo"}, _)) + .WillRepeatedly(Return(enc_res)); auto memory_provider = std::make_shared(); EXPECT_CALL(*memory_provider, getCurrentMemory()) .WillRepeatedly(Return( std::optional>( *memory_))); + EXPECT_CALL(*memory_provider, resetMemory(_)) + .WillRepeatedly(Return(outcome::success())); auto storage_provider = std::make_shared(); @@ -126,6 +126,12 @@ class ExecutorTest : public testing::Test { EXPECT_CALL(*storage_provider, setToEphemeralAt(storage_state)) .WillOnce(Return(outcome::success())); } + auto batch = std::make_shared(); + EXPECT_CALL(*storage_provider, getCurrentBatch()).WillOnce(Return(batch)); + static const auto heappages = ":heappages"_buf; + EXPECT_CALL(*batch, tryGetMock(heappages.view())) + .WillOnce(Return(kagome::common::Buffer{})); + auto env = std::make_shared( memory_provider, storage_provider, nullptr, nullptr); EXPECT_CALL(*module_instance, getEnvironment()) @@ -133,12 +139,16 @@ class ExecutorTest : public testing::Test { [env]() -> const kagome::runtime::InstanceEnvironment & { return *env; })); + EXPECT_CALL(*module_instance, getGlobal("__heap_base")) + .WillRepeatedly(testing::Return(42)); EXPECT_CALL(*module_repo_, getInstanceAt(blockchain_state, storage_state)) .WillRepeatedly(testing::Return(module_instance)); - Buffer enc_res{scale::encode(res).value()}; - EXPECT_CALL(*memory_, loadN(RESULT_LOCATION.ptr, RESULT_LOCATION.size)) - .WillOnce(Return(enc_res)); + if (type == CallType::Persistent) { + return ctx_factory_->persistentAt(blockchain_state.hash); + } else { + return ctx_factory_->ephemeralAt(blockchain_state.hash, storage_state); + } } protected: @@ -157,50 +167,40 @@ TEST_F(ExecutorTest, LatestStateSwitchesCorrectly) { kagome::primitives::BlockInfo block_info3{44, "block_hash3"_hash256}; Buffer enc_args{scale::encode(2, 3).value()}; - prepareCall( - block_info1, "state_hash1"_hash256, CallType::Persistent, enc_args, 5); - auto ctx = ctx_factory_->persistentAt(block_info1.hash, std::nullopt).value(); - auto res = executor.decodedCallWithCtx(ctx, "addTwo", 2, 3).value(); + EXPECT_OUTCOME_TRUE(ctx1, + prepareCall(block_info1, + "state_hash1"_hash256, + CallType::Persistent, + enc_args, + 5)); + auto res = executor.call(ctx1, "addTwo", 2, 3).value(); EXPECT_EQ(res, 5); enc_args = scale::encode(7, 10).value(); - prepareCall( - block_info1, "state_hash2"_hash256, CallType::Ephemeral, enc_args, 17); - EXPECT_OUTCOME_TRUE( - res2, - executor.callAt( - block_info1.hash, "state_hash2"_hash256, "addTwo", 7, 10)); + EXPECT_OUTCOME_TRUE(ctx2, prepareCall( + block_info1, "state_hash2"_hash256, CallType::Ephemeral, enc_args, 17)); + EXPECT_OUTCOME_TRUE(res2, executor.call(ctx2, "addTwo", 7, 10)); ASSERT_EQ(res2, 17); enc_args = scale::encode(0, 0).value(); - prepareCall( - block_info1, "state_hash2"_hash256, CallType::Persistent, enc_args, 0); - auto ctx3 = - ctx_factory_->persistentAt(block_info1.hash, std::nullopt).value(); - EXPECT_EQ(executor.decodedCallWithCtx(ctx, "addTwo", 0, 0).value(), 0); + EXPECT_OUTCOME_TRUE(ctx3, prepareCall( + block_info1, "state_hash2"_hash256, CallType::Persistent, enc_args, 0)); + EXPECT_EQ(executor.call(ctx3, "addTwo", 0, 0).value(), 0); enc_args = scale::encode(7, 10).value(); - prepareCall( - block_info1, "state_hash3"_hash256, CallType::Ephemeral, enc_args, 17); - EXPECT_OUTCOME_TRUE( - res4, - executor.callAt( - block_info1.hash, "state_hash3"_hash256, "addTwo", 7, 10)); + EXPECT_OUTCOME_TRUE(ctx4, prepareCall( + block_info1, "state_hash3"_hash256, CallType::Ephemeral, enc_args, 17)); + EXPECT_OUTCOME_TRUE(res4, executor.call(ctx4, "addTwo", 7, 10)); ASSERT_EQ(res4, 17); enc_args = scale::encode(-5, 5).value(); - prepareCall( - block_info2, "state_hash4"_hash256, CallType::Persistent, enc_args, 0); - auto ctx5 = - ctx_factory_->persistentAt(block_info2.hash, std::nullopt).value(); - EXPECT_EQ(executor.decodedCallWithCtx(ctx5, "addTwo", -5, 5).value(), 0); + EXPECT_OUTCOME_TRUE(ctx5, prepareCall( + block_info2, "state_hash4"_hash256, CallType::Persistent, enc_args, 0)); + EXPECT_EQ(executor.call(ctx5, "addTwo", -5, 5).value(), 0); enc_args = scale::encode(7, 10).value(); - prepareCall( - block_info2, "state_hash5"_hash256, CallType::Ephemeral, enc_args, 17); - EXPECT_OUTCOME_TRUE( - res6, - executor.callAt( - block_info2.hash, "state_hash5"_hash256, "addTwo", 7, 10)); + EXPECT_OUTCOME_TRUE(ctx6, prepareCall( + block_info2, "state_hash5"_hash256, CallType::Ephemeral, enc_args, 17)); + EXPECT_OUTCOME_TRUE(res6, executor.call(ctx6, "addTwo", 7, 10)); ASSERT_EQ(res6, 17); } diff --git a/test/core/runtime/trie_storage_provider_test.cpp b/test/core/runtime/trie_storage_provider_test.cpp index 3cd7ef8a9d..e62c444171 100644 --- a/test/core/runtime/trie_storage_provider_test.cpp +++ b/test/core/runtime/trie_storage_provider_test.cpp @@ -11,7 +11,7 @@ #include "common/buffer.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "runtime/common/runtime_execution_error.hpp" -#include "storage/in_memory/in_memory_storage.hpp" +#include "storage/in_memory/in_memory_spaced_storage.hpp" #include "storage/trie/impl/trie_storage_backend_impl.hpp" #include "storage/trie/impl/trie_storage_impl.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_factory_impl.hpp" @@ -36,15 +36,14 @@ class TrieStorageProviderTest : public ::testing::Test { auto codec = std::make_shared(); - storage_ = std::make_shared(); + storage_ = std::make_shared(); - auto backend = - std::make_shared( - storage_); + auto node_backend = + std::make_shared(storage_); auto serializer = std::make_shared( - trie_factory, codec, backend); + trie_factory, codec, node_backend); auto state_pruner = std::make_shared(); @@ -62,7 +61,7 @@ class TrieStorageProviderTest : public ::testing::Test { } protected: - std::shared_ptr storage_; + std::shared_ptr storage_; std::shared_ptr storage_provider_; }; diff --git a/test/core/runtime/wavm/CMakeLists.txt b/test/core/runtime/wavm/CMakeLists.txt index 15538de647..a391f98832 100644 --- a/test/core/runtime/wavm/CMakeLists.txt +++ b/test/core/runtime/wavm/CMakeLists.txt @@ -18,6 +18,7 @@ target_link_libraries(wavm_runtime_test INTERFACE host_api host_api_factory core_api + core_api_factory runtime_wavm module_repository runtime_upgrade_tracker @@ -28,7 +29,6 @@ addtest(wavm_memory_test wasm_memory_test.cpp ) target_link_libraries(wavm_memory_test - wavm_memory logger_for_tests runtime_wavm ) diff --git a/test/core/runtime/wavm/core_integration_test.cpp b/test/core/runtime/wavm/core_integration_test.cpp index 0ea0d8b107..a4ef9b7a0b 100644 --- a/test/core/runtime/wavm/core_integration_test.cpp +++ b/test/core/runtime/wavm/core_integration_test.cpp @@ -43,23 +43,13 @@ class CoreTest : public WavmRuntimeTest { void SetUp() override { WavmRuntimeTest::SetUp(); - core_ = std::make_shared( - executor_, ctx_factory_, header_repo_, nullptr); + core_ = std::make_shared(executor_, header_repo_, nullptr); } protected: std::shared_ptr core_; }; -/** - * @given initialized core api - * @when version is invoked - * @then successful result is returned - */ -TEST_F(CoreTest, DISABLED_VersionTest) { - ASSERT_TRUE(core_->version()); -} - /** * @given initialized core api * @when execute_block is invoked diff --git a/test/core/runtime/wavm/wavm_module_init_test.cpp b/test/core/runtime/wavm/wavm_module_init_test.cpp index 34ae16daf3..5c9f33b02d 100644 --- a/test/core/runtime/wavm/wavm_module_init_test.cpp +++ b/test/core/runtime/wavm/wavm_module_init_test.cpp @@ -9,6 +9,7 @@ #include #include #include + #include "blockchain/impl/block_header_repository_impl.hpp" //header_repo #include "crypto/bip39/impl/bip39_provider_impl.hpp" //bip39_provider #include "crypto/crypto_store/crypto_store_impl.hpp" //crypto_store @@ -31,8 +32,8 @@ #include "runtime/wavm/intrinsics/intrinsic_module.hpp" // intrinsic_module #include "runtime/wavm/module_factory_impl.hpp" // module_factory #include "runtime/wavm/module_params.hpp" //module_params -#include "storage/in_memory/in_memory_storage.hpp" // storage -#include "storage/rocksdb/rocksdb.hpp" //database +#include "storage/in_memory/in_memory_spaced_storage.hpp" // storage +#include "storage/rocksdb/rocksdb.hpp" //database #include "storage/trie/impl/trie_storage_backend_impl.hpp" // storage_backend #include "storage/trie/impl/trie_storage_impl.hpp" // trie_storage #include "storage/trie/polkadot_trie/polkadot_trie_factory_impl.hpp" // trie_factory @@ -61,13 +62,13 @@ class WavmModuleInitTest : public ::testing::TestWithParam { auto trie_factory = std::make_shared(); auto codec = std::make_shared(); - auto storage = std::make_shared(); - auto storage_backend = + auto storage = std::make_shared(); + auto node_storage_backend = std::make_shared( storage); auto serializer = std::make_shared( - trie_factory, codec, storage_backend); + trie_factory, codec, node_storage_backend); auto state_pruner = std::make_shared(); std::shared_ptr trie_storage = @@ -137,26 +138,21 @@ class WavmModuleInitTest : public ::testing::TestWithParam { auto cache = std::make_shared(); - auto instance_env_factory = std::make_shared< - const kagome::runtime::wavm::InstanceEnvironmentFactory>( - trie_storage, - serializer, - compartment, - module_params, - intrinsic_module, - host_api_factory, - header_repo, - smc, - cache); - module_factory_ = std::make_shared( compartment, module_params, - instance_env_factory, + host_api_factory, + trie_storage, + serializer, intrinsic_module, + smc, std::nullopt, hasher); + + auto instance_env_factory = std::make_shared< + const kagome::runtime::wavm::InstanceEnvironmentFactory>( + trie_storage, serializer, host_api_factory, module_factory_); } std::shared_ptr module_factory_; @@ -172,16 +168,13 @@ TEST_P(WavmModuleInitTest, DISABLED_SingleModule) { EXPECT_OUTCOME_TRUE( runtime_context, kagome::runtime::RuntimeContextFactory::fromCode(*module_factory_, code)); - EXPECT_OUTCOME_TRUE( - memory_response, - runtime_context.module_instance->callExportFunction("Core_version", {})); + EXPECT_OUTCOME_TRUE(response, + runtime_context.module_instance->callExportFunction( + runtime_context, "Core_version", {})); auto memory = runtime_context.module_instance->getEnvironment() .memory_provider->getCurrentMemory(); GTEST_ASSERT_TRUE(memory.has_value()); - auto scale_resp = - memory->get().loadN(memory_response.ptr, memory_response.size); - EXPECT_OUTCOME_TRUE(cv, - scale::decode(scale_resp)); + EXPECT_OUTCOME_TRUE(cv, scale::decode(response)); SL_INFO(log_, "Module initialized - spec {}-{}, impl {}-{}", cv.spec_name, diff --git a/test/core/runtime/wavm/wavm_runtime_test.hpp b/test/core/runtime/wavm/wavm_runtime_test.hpp index bd00c9f632..7b4146e9e7 100644 --- a/test/core/runtime/wavm/wavm_runtime_test.hpp +++ b/test/core/runtime/wavm/wavm_runtime_test.hpp @@ -11,9 +11,10 @@ #include "crypto/hasher/hasher_impl.hpp" #include "mock/core/application/app_configuration_mock.hpp" #include "mock/core/storage/trie/trie_storage_mock.hpp" +#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/module.hpp" #include "runtime/wavm/compartment_wrapper.hpp" -#include "runtime/wavm/core_api_factory_impl.hpp" +#include "runtime/wavm/instance_environment_factory.hpp" #include "runtime/wavm/intrinsics/intrinsic_functions.hpp" #include "runtime/wavm/intrinsics/intrinsic_module.hpp" #include "runtime/wavm/intrinsics/intrinsic_resolver_impl.hpp" @@ -40,27 +41,26 @@ class WavmRuntimeTest : public RuntimeTestBase { resolver_ = std::make_shared( intrinsic_module_instance); - auto instance_env_factory = - std::make_shared( - trie_storage_, - serializer_, - compartment, - module_params, - intrinsic_module, - host_api_factory_, - header_repo_, - std::make_shared(), - cache_); - auto module_factory = std::make_shared( compartment, module_params, - instance_env_factory, + host_api_factory_, + trie_storage_, + serializer_, intrinsic_module, + std::make_shared(), std::nullopt, hasher_); + + auto instance_env_factory = + std::make_shared( + trie_storage_, + serializer_, + host_api_factory_, + module_factory); + return module_factory; } diff --git a/test/core/storage/changes_trie/changes_tracker_test.cpp b/test/core/storage/changes_trie/changes_tracker_test.cpp index 160d76e87f..f364c549ce 100644 --- a/test/core/storage/changes_trie/changes_tracker_test.cpp +++ b/test/core/storage/changes_trie/changes_tracker_test.cpp @@ -12,6 +12,7 @@ #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "primitives/event_types.hpp" #include "scale/scale.hpp" +#include "storage/in_memory/in_memory_spaced_storage.hpp" #include "storage/in_memory/in_memory_storage.hpp" #include "storage/trie/impl/persistent_trie_batch_impl.hpp" #include "storage/trie/impl/trie_storage_backend_impl.hpp" @@ -29,6 +30,7 @@ using kagome::primitives::BlockHash; using kagome::primitives::ExtrinsicIndex; using kagome::primitives::events::ChainSubscriptionEngine; using kagome::primitives::events::StorageSubscriptionEngine; +using kagome::storage::InMemorySpacedStorage; using kagome::storage::InMemoryStorage; using kagome::storage::changes_trie::ChangesTracker; using kagome::storage::changes_trie::StorageChangesTrackerImpl; @@ -53,10 +55,10 @@ TEST(ChangesTrieTest, IntegrationWithOverlay) { // GIVEN auto factory = std::make_shared(); auto codec = std::make_shared(); - auto backend = std::make_shared( - std::make_shared()); - auto serializer = - std::make_shared(factory, codec, backend); + auto in_memory_storage = std::make_shared(); + auto node_backend = std::make_shared(in_memory_storage); + auto serializer = std::make_shared( + factory, codec, node_backend); auto storage_subscription_engine = std::make_shared(); auto chain_subscription_engine = std::make_shared(); diff --git a/test/core/storage/trie/trie_storage/trie_batch_test.cpp b/test/core/storage/trie/trie_storage/trie_batch_test.cpp index 7c84ed6726..e3afc5fae2 100644 --- a/test/core/storage/trie/trie_storage/trie_batch_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_batch_test.cpp @@ -7,6 +7,7 @@ #include #include +#include "mock/core/storage/spaced_storage_mock.hpp" #include "mock/core/storage/trie_pruner/trie_pruner_mock.hpp" #include "storage/changes_trie/impl/storage_changes_tracker_impl.hpp" #include "storage/in_memory/in_memory_storage.hpp" @@ -29,6 +30,7 @@ using kagome::common::BufferView; using kagome::common::Hash256; using kagome::primitives::BlockHash; using kagome::storage::Space; +using kagome::storage::SpacedStorageMock; using kagome::storage::trie::StateVersion; using kagome::storage::trie_pruner::TriePrunerMock; using kagome::subscription::SubscriptionEngine; @@ -51,7 +53,7 @@ class TrieBatchTest : public test::BaseRocksDB_Test { auto serializer = std::make_shared( factory, codec, - std::make_shared(std::move(db_))); + std::make_shared(rocks_)); empty_hash = serializer->getEmptyRootHash(); @@ -189,7 +191,7 @@ TEST_F(TrieBatchTest, Replace) { * consistency */ TEST_F(TrieBatchTest, ConsistentOnFailure) { - auto db = std::make_unique(); + auto db = std::make_shared(); /** * First time the storage will function correctly, after which it will yield * an error @@ -202,10 +204,16 @@ TEST_F(TrieBatchTest, ConsistentOnFailure) { .After(expectation) .WillOnce(Return(PolkadotCodec::Error::UNKNOWN_NODE_TYPE)); + auto spaced_db = std::make_shared(); + ON_CALL(*spaced_db, getSpace(Space::kTrieNode)).WillByDefault(Return(db)); + ON_CALL(*spaced_db, getSpace(Space::kTrieValue)).WillByDefault(Return(db)); + auto factory = std::make_shared(); auto codec = std::make_shared(); auto serializer = std::make_shared( - factory, codec, std::make_shared(std::move(db))); + factory, + codec, + std::make_shared(spaced_db)); auto state_pruner = std::make_shared(); ON_CALL( *state_pruner, diff --git a/test/core/storage/trie/trie_storage/trie_storage_backend_test.cpp b/test/core/storage/trie/trie_storage/trie_storage_backend_test.cpp index e5fd26e568..9c9598cd4a 100644 --- a/test/core/storage/trie/trie_storage/trie_storage_backend_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_storage_backend_test.cpp @@ -8,7 +8,8 @@ #include -#include "mock/core/storage/persistent_map_mock.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" +#include "mock/core/storage/spaced_storage_mock.hpp" #include "mock/core/storage/write_batch_mock.hpp" #include "storage/trie/impl/trie_storage_backend_impl.hpp" #include "testutil/literals.hpp" @@ -17,6 +18,7 @@ using kagome::common::Buffer; using kagome::common::BufferView; using kagome::storage::BufferStorageMock; +using kagome::storage::SpacedStorageMock; using kagome::storage::face::WriteBatchMock; using kagome::storage::trie::TrieStorageBackendImpl; using testing::Invoke; @@ -24,9 +26,17 @@ using testing::Return; class TrieDbBackendTest : public testing::Test { public: + void SetUp() override { + ON_CALL(*spaced_storage, getSpace(kagome::storage::Space::kTrieNode)) + .WillByDefault(Return(storage)); + backend = std::make_unique(spaced_storage); + } + std::shared_ptr storage = std::make_shared(); - TrieStorageBackendImpl backend{storage}; + std::shared_ptr spaced_storage = + std::make_shared(); + std::unique_ptr backend; }; /** @@ -41,7 +51,7 @@ TEST_F(TrieDbBackendTest, Put) { .InternalExpectedAt( "_file_name_", 40, "*storage", "put(prefixed, \"123\"_buf)") .WillOnce(Return(outcome::success())); - EXPECT_OUTCOME_TRUE_1(backend.put("abc"_buf, "123"_buf)); + EXPECT_OUTCOME_TRUE_1(backend->put("abc"_buf, "123"_buf)); } /** @@ -52,7 +62,7 @@ TEST_F(TrieDbBackendTest, Put) { TEST_F(TrieDbBackendTest, Get) { auto key = "abc"_buf; EXPECT_CALL(*storage, getMock(BufferView{key})).WillOnce(Return("123"_buf)); - EXPECT_OUTCOME_TRUE_1(backend.get("abc"_buf)); + EXPECT_OUTCOME_TRUE_1(backend->get("abc"_buf)); } /** @@ -75,7 +85,7 @@ TEST_F(TrieDbBackendTest, Batch) { EXPECT_CALL(*storage, batch()) .WillOnce(Return(testing::ByMove(std::move(batch_mock)))); - auto batch = backend.batch(); + auto batch = backend->batch(); EXPECT_OUTCOME_TRUE_1(batch->put("abc"_buf, "123"_buf)); EXPECT_OUTCOME_TRUE_1(batch->put("def"_buf, "123"_buf)); EXPECT_OUTCOME_TRUE_1(batch->remove("abc"_buf)); diff --git a/test/core/storage/trie/trie_storage/trie_storage_test.cpp b/test/core/storage/trie/trie_storage/trie_storage_test.cpp index 7fe1a3cba0..c95ad31f51 100644 --- a/test/core/storage/trie/trie_storage/trie_storage_test.cpp +++ b/test/core/storage/trie/trie_storage/trie_storage_test.cpp @@ -57,10 +57,7 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { RocksDb::create("/tmp/kagome_rocksdb_persistency_test", options)); auto serializer = std::make_shared( - factory, - codec, - std::make_shared( - rocks_db->getSpace(Space::kDefault))); + factory, codec, std::make_shared(rocks_db)); auto state_pruner = std::make_shared(); ON_CALL(*state_pruner, @@ -85,10 +82,7 @@ TEST(TriePersistencyTest, CreateDestroyCreate) { EXPECT_OUTCOME_TRUE(new_rocks_db, RocksDb::create("/tmp/kagome_rocksdb_persistency_test")); auto serializer = std::make_shared( - factory, - codec, - std::make_shared( - new_rocks_db->getSpace(Space::kDefault))); + factory, codec, std::make_shared(new_rocks_db)); auto state_pruner = std::make_shared(); auto storage = TrieStorageImpl::createFromStorage(codec, serializer, state_pruner) diff --git a/test/core/storage/trie_pruner/trie_pruner_test.cpp b/test/core/storage/trie_pruner/trie_pruner_test.cpp index 2d9821d267..8cf192f5b3 100644 --- a/test/core/storage/trie_pruner/trie_pruner_test.cpp +++ b/test/core/storage/trie_pruner/trie_pruner_test.cpp @@ -13,7 +13,7 @@ #include "mock/core/application/app_configuration_mock.hpp" #include "mock/core/application/app_state_manager_mock.hpp" #include "mock/core/blockchain/block_tree_mock.hpp" -#include "mock/core/storage/persistent_map_mock.hpp" +#include "mock/core/storage/generic_storage_mock.hpp" #include "mock/core/storage/spaced_storage_mock.hpp" #include "mock/core/storage/trie/polkadot_trie_cursor_mock.h" #include "mock/core/storage/trie/serialization/codec_mock.hpp" @@ -168,7 +168,7 @@ class TriePrunerTest : public testing::Test { ON_CALL(*config_mock, statePruningDepth()).WillByDefault(Return(16)); ON_CALL(*config_mock, enableThoroughPruning()).WillByDefault(Return(true)); - trie_storage_mock.reset( + trie_node_storage_mock.reset( new testing::NiceMock()); persistent_storage_mock.reset( new testing::NiceMock); @@ -191,7 +191,7 @@ class TriePrunerTest : public testing::Test { pruner.reset(new TriePrunerImpl( std::make_shared(), - trie_storage_mock, + trie_node_storage_mock, serializer_mock, codec_mock, persistent_storage_mock, @@ -217,7 +217,7 @@ class TriePrunerTest : public testing::Test { pruner.reset(new TriePrunerImpl( std::make_shared(), - trie_storage_mock, + trie_node_storage_mock, serializer_mock, codec_mock, persistent_storage_mock, @@ -262,7 +262,7 @@ class TriePrunerTest : public testing::Test { std::unique_ptr pruner; std::shared_ptr serializer_mock; - std::shared_ptr trie_storage_mock; + std::shared_ptr trie_node_storage_mock; std::shared_ptr> persistent_storage_mock; std::shared_ptr codec_mock; std::shared_ptr hasher; @@ -352,7 +352,7 @@ TEST_F(TriePrunerTest, BasicScenario) { {{"_0"_hash256, makeTransparentNode({NODE, "_0"_hash256, {}})}, {"_5"_hash256, makeTransparentNode({NODE, "_5"_hash256, {}})}}})); - EXPECT_CALL(*trie_storage_mock, batch()).WillRepeatedly(Invoke([]() { + EXPECT_CALL(*trie_node_storage_mock, batch()).WillRepeatedly(Invoke([]() { auto batch = std::make_unique>(); EXPECT_CALL(*batch, remove(_)).WillRepeatedly(Return(outcome::success())); EXPECT_CALL(*batch, commit()).WillOnce(Return(outcome::success())); @@ -473,7 +473,7 @@ TEST_F(TriePrunerTest, RandomTree) { std::map node_storage; std::set inserted_keys; - EXPECT_CALL(*trie_storage_mock, get(_)) + EXPECT_CALL(*trie_node_storage_mock, get(_)) .WillRepeatedly( Invoke([&node_storage](auto &k) -> outcome::result { auto it = node_storage.find(k); @@ -483,7 +483,8 @@ TEST_F(TriePrunerTest, RandomTree) { return BufferOrView{it->second.view()}; })); - trie::TrieSerializerImpl serializer{trie_factory, codec, trie_storage_mock}; + trie::TrieSerializerImpl serializer{ + trie_factory, codec, trie_node_storage_mock}; std::vector> kv; std::mt19937 rand; rand.seed(42); @@ -504,18 +505,19 @@ TEST_F(TriePrunerTest, RandomTree) { })); for (unsigned i = 0; i < STATES_NUM; i++) { - EXPECT_CALL(*trie_storage_mock, batch()).WillOnce(Invoke([&node_storage]() { - auto batch_mock = - std::make_unique>(); - EXPECT_CALL(*batch_mock, put(_, _)) - .WillRepeatedly(Invoke([&node_storage](auto &k, auto &v) { - node_storage[k] = v; - return outcome::success(); - })); - EXPECT_CALL(*batch_mock, commit()) - .WillRepeatedly(Return(outcome::success())); - return batch_mock; - })); + EXPECT_CALL(*trie_node_storage_mock, batch()) + .WillOnce(Invoke([&node_storage]() { + auto batch_mock = + std::make_unique>(); + EXPECT_CALL(*batch_mock, put(_, _)) + .WillRepeatedly(Invoke([&node_storage](auto &k, auto &v) { + node_storage[k] = v; + return outcome::success(); + })); + EXPECT_CALL(*batch_mock, commit()) + .WillRepeatedly(Return(outcome::success())); + return batch_mock; + })); for (unsigned j = 0; j < INSERT_PER_STATE; j++) { auto k = randomBuffer(rand); @@ -553,7 +555,7 @@ TEST_F(TriePrunerTest, RandomTree) { roots.push_back(root); if (i >= 16) { - EXPECT_CALL(*trie_storage_mock, batch()) + EXPECT_CALL(*trie_node_storage_mock, batch()) .WillOnce(Invoke([&node_storage]() { auto batch = std::make_unique>(); @@ -574,16 +576,17 @@ TEST_F(TriePrunerTest, RandomTree) { } } for (unsigned i = STATES_NUM - 16; i < STATES_NUM; i++) { - EXPECT_CALL(*trie_storage_mock, batch()).WillOnce(Invoke([&node_storage]() { - auto batch = std::make_unique>(); - EXPECT_CALL(*batch, remove(_)) - .WillRepeatedly(Invoke([&node_storage](auto &k) { - node_storage.erase(k); - return outcome::success(); - })); - EXPECT_CALL(*batch, commit()).WillOnce(Return(outcome::success())); - return batch; - })); + EXPECT_CALL(*trie_node_storage_mock, batch()) + .WillOnce(Invoke([&node_storage]() { + auto batch = std::make_unique>(); + EXPECT_CALL(*batch, remove(_)) + .WillRepeatedly(Invoke([&node_storage](auto &k) { + node_storage.erase(k); + return outcome::success(); + })); + EXPECT_CALL(*batch, commit()).WillOnce(Return(outcome::success())); + return batch; + })); auto &root = roots[i]; BlockHeader header{.number = i, .state_root = root}; @@ -640,6 +643,9 @@ TEST_F(TriePrunerTest, RestoreStateFromGenesis) { ON_CALL(*block_tree, bestBlock()) .WillByDefault(Return(BlockInfo{6, hash_from_header(headers.at(6))})); + //ON_CALL(*block_tree, bestLeaf()) + // .WillByDefault(Return(BlockInfo{6, hash_from_header(headers.at(6))})); + auto mock_block = [&](unsigned int number) { auto str_number = std::to_string(number); @@ -704,7 +710,7 @@ TEST_F(TriePrunerTest, FastSyncScenario) { auto block_tree = std::make_shared>(); - ON_CALL(*trie_storage_mock, get(_)) + ON_CALL(*trie_node_storage_mock, get(_)) .WillByDefault(Invoke( [&](auto &key) -> outcome::result { if (node_storage.count(key) == 0) { @@ -713,7 +719,7 @@ TEST_F(TriePrunerTest, FastSyncScenario) { return kagome::common::BufferOrView{node_storage.at(key).view()}; })); - ON_CALL(*trie_storage_mock, batch()).WillByDefault(Invoke([&]() { + ON_CALL(*trie_node_storage_mock, batch()).WillByDefault(Invoke([&]() { auto batch = std::make_unique< testing::NiceMock>>(); ON_CALL(*batch, put(_, _)) @@ -741,7 +747,8 @@ TEST_F(TriePrunerTest, FastSyncScenario) { codec->encodeNode(*genesis_trie->getRoot(), trie::StateVersion::V0) .value()); - trie::TrieSerializerImpl serializer{trie_factory, codec, trie_storage_mock}; + trie::TrieSerializerImpl serializer{ + trie_factory, codec, trie_node_storage_mock}; ON_CALL(*serializer_mock, retrieveTrie(genesis_state_root, _)) .WillByDefault(Return(genesis_trie)); diff --git a/test/external-project-test/link_libraries.cmake b/test/external-project-test/link_libraries.cmake index 9acf933463..6827038ee6 100644 --- a/test/external-project-test/link_libraries.cmake +++ b/test/external-project-test/link_libraries.cmake @@ -8,7 +8,13 @@ function(external_project_link_libraries target prefix) set(targets app_config executor - runtime_wavm + binaryen_module_factory + binaryen_runtime_external_interface + binaryen_wasm_memory + binaryen_wasm_memory_factory + binaryen_wasm_module + binaryen_memory_provider + core_api_factory core_api storage trie_storage_provider diff --git a/test/external-project-test/src/main.cpp b/test/external-project-test/src/main.cpp index 1ba39b9b4c..4487bf3fae 100644 --- a/test/external-project-test/src/main.cpp +++ b/test/external-project-test/src/main.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include #include @@ -30,10 +32,6 @@ #include #include #include -#include -#include -#include -#include #include #include #include @@ -96,7 +94,6 @@ int main() { .value(); auto code_substitutes = chain_spec->codeSubstitutes(); - auto storage = std::make_shared(); auto config = std::make_shared( kagome::log::createLogger("AppConfiguration")); @@ -104,10 +101,10 @@ int main() { auto trie_factory = std::make_shared(); auto codec = std::make_shared(); - auto storage_backend = - std::make_shared(storage); + auto node_storage_backend = + std::make_shared(database); auto serializer = std::make_shared( - trie_factory, codec, storage_backend); + trie_factory, codec, node_storage_backend); auto app_state_manager = std::make_shared(); @@ -115,7 +112,7 @@ int main() { auto state_pruner = std::make_shared( app_state_manager, - storage_backend, + node_storage_backend, serializer, codec, database, @@ -154,13 +151,6 @@ int main() { auto code_provider = std::make_shared( trie_storage, runtime_upgrade_tracker, code_substitutes, chain_spec); - auto compartment = - std::make_shared( - "WAVM Compartment"); - auto module_params = std::make_shared(); - auto intrinsic_module = - std::make_shared( - compartment, module_params->intrinsicMemoryType); auto generator = std::make_shared(); @@ -214,24 +204,13 @@ int main() { auto smc = std::make_shared(); auto instance_env_factory = - std::make_shared( - trie_storage, - serializer, - compartment, - module_params, - intrinsic_module, - host_api_factory, - header_repo, - smc, - cache); + std::make_shared( + trie_storage, serializer, host_api_factory); + auto module_factory = - std::make_shared( - compartment, - module_params, - instance_env_factory, - intrinsic_module, - std::nullopt, - hasher); + std::make_shared( + instance_env_factory, trie_storage, hasher); + auto runtime_instances_pool = std::make_shared(module_factory); auto module_repo = std::make_shared( diff --git a/test/mock/core/host_api/host_api_mock.hpp b/test/mock/core/host_api/host_api_mock.hpp index 654d421f4d..0f4302a306 100644 --- a/test/mock/core/host_api/host_api_mock.hpp +++ b/test/mock/core/host_api/host_api_mock.hpp @@ -69,7 +69,7 @@ namespace kagome::host_api { MOCK_METHOD(void, ext_misc_print_num_version_1, - (uint64_t), + (int64_t), (const, override)); MOCK_METHOD(void, @@ -343,14 +343,14 @@ namespace kagome::host_api { (), (override)); - MOCK_METHOD(runtime::WasmU64, + MOCK_METHOD(runtime::WasmI64, ext_offchain_timestamp_version_1, (), (override)); MOCK_METHOD(void, ext_offchain_sleep_until_version_1, - (runtime::WasmU64), + (runtime::WasmI64), (override)); MOCK_METHOD(runtime::WasmPointer, @@ -476,7 +476,7 @@ namespace kagome::host_api { runtime::WasmOffset), (const, override)); - MOCK_METHOD(uint32_t, + MOCK_METHOD(int32_t, ext_default_child_storage_exists_version_1, (runtime::WasmSpan, runtime::WasmSpan), (const, override)); diff --git a/test/mock/core/runtime/core_api_factory_mock.hpp b/test/mock/core/runtime/core_api_factory_mock.hpp index 54f6732a07..ab638ddf54 100644 --- a/test/mock/core/runtime/core_api_factory_mock.hpp +++ b/test/mock/core/runtime/core_api_factory_mock.hpp @@ -17,7 +17,7 @@ namespace kagome::runtime { class CoreApiFactoryMock : public CoreApiFactory { public: - MOCK_METHOD(std::unique_ptr, + MOCK_METHOD(outcome::result>, make, (std::shared_ptr hasher, const std::vector &runtime_code), diff --git a/test/mock/core/runtime/core_mock.hpp b/test/mock/core/runtime/core_mock.hpp index 0c02310686..f0ae98082b 100644 --- a/test/mock/core/runtime/core_mock.hpp +++ b/test/mock/core/runtime/core_mock.hpp @@ -12,7 +12,7 @@ namespace kagome::runtime { - class CoreMock : public Core { + class CoreMock : public Core, public RestrictedCore { public: MOCK_METHOD(outcome::result, version, (), (override)); diff --git a/test/mock/core/runtime/executor_mock.hpp b/test/mock/core/runtime/executor_mock.hpp deleted file mode 100644 index 6e921f795a..0000000000 --- a/test/mock/core/runtime/executor_mock.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "runtime/executor.hpp" - -#include - -#include "mock/core/runtime/runtime_context_factory_mock.hpp" -#include "mock/core/runtime/runtime_properties_cache_mock.hpp" - -namespace kagome::runtime { - - class ExecutorMock : public Executor { - public: - ExecutorMock() - : Executor(std::make_shared(), - std::make_shared()) {} - - ExecutorMock(std::shared_ptr ctx_factory, - std::shared_ptr cache) - : Executor(ctx_factory, cache) {} - - MOCK_METHOD(outcome::result, - callWithCtx, - (RuntimeContext & ctx, - std::string_view name, - BufferView encoded_args), - (override)); - }; - -} // namespace kagome::runtime diff --git a/test/mock/core/runtime/module_instance_mock.hpp b/test/mock/core/runtime/module_instance_mock.hpp index f6086fc6bf..ee40f3935a 100644 --- a/test/mock/core/runtime/module_instance_mock.hpp +++ b/test/mock/core/runtime/module_instance_mock.hpp @@ -21,9 +21,11 @@ namespace kagome::runtime { (), (const, override)); - MOCK_METHOD(outcome::result, + MOCK_METHOD(outcome::result, callExportFunction, - (std::string_view name, common::BufferView args), + (RuntimeContext & ctx, + std::string_view name, + common::BufferView args), (const, override)); MOCK_METHOD(void, @@ -42,10 +44,5 @@ namespace kagome::runtime { (const, override)); MOCK_METHOD(outcome::result, resetEnvironment, (), (override)); - - MOCK_METHOD(outcome::result, - resetMemory, - (const MemoryLimits &limits), - (override)); }; } // namespace kagome::runtime diff --git a/test/mock/core/runtime/module_mock.hpp b/test/mock/core/runtime/module_mock.hpp index fa3ccb5e03..850a854339 100644 --- a/test/mock/core/runtime/module_mock.hpp +++ b/test/mock/core/runtime/module_mock.hpp @@ -14,7 +14,7 @@ namespace kagome::runtime { class ModuleMock : public Module { public: - MOCK_METHOD(std::shared_ptr, + MOCK_METHOD(outcome::result>, instantiate, (), (const)); diff --git a/test/mock/core/runtime/runtime_context_factory_mock.hpp b/test/mock/core/runtime/runtime_context_factory_mock.hpp index 8680c67435..f69cd1a4b2 100644 --- a/test/mock/core/runtime/runtime_context_factory_mock.hpp +++ b/test/mock/core/runtime/runtime_context_factory_mock.hpp @@ -19,7 +19,7 @@ namespace kagome::runtime { (std::shared_ptr module_instance, std::shared_ptr batch, ContextParams params), - (override)); + (const, override)); MOCK_METHOD( outcome::result, persistent, @@ -28,7 +28,7 @@ namespace kagome::runtime { std::optional> changes_tracker_opt, ContextParams params), - (override)); + (const, override)); MOCK_METHOD( outcome::result, persistentAt, @@ -36,27 +36,27 @@ namespace kagome::runtime { std::optional> changes_tracker_opt, ContextParams params), - (override)); + (const, override)); MOCK_METHOD(outcome::result, ephemeral, (std::shared_ptr module_instance, const storage::trie::RootHash &state, ContextParams params), - (override)); + (const, override)); MOCK_METHOD(outcome::result, ephemeralAt, (const primitives::BlockHash &block_hash, ContextParams params), - (override)); + (const, override)); MOCK_METHOD(outcome::result, ephemeralAt, (const primitives::BlockHash &block_hash, const storage::trie::RootHash &state, ContextParams params), - (override)); + (const, override)); MOCK_METHOD(outcome::result, ephemeralAtGenesis, (ContextParams params), - (override)); + (const, override)); }; } // namespace kagome::runtime diff --git a/test/mock/core/storage/persistent_map_mock.hpp b/test/mock/core/storage/generic_storage_mock.hpp similarity index 100% rename from test/mock/core/storage/persistent_map_mock.hpp rename to test/mock/core/storage/generic_storage_mock.hpp