From 1ef95219494d85c5d1b4c139e87fd072e9cfaa64 Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov Date: Tue, 25 Jun 2024 17:49:30 +0300 Subject: [PATCH 1/7] Feature: don't disconnect privileged peers (#2057) * feature: don't disconnect privileged peers Signed-off-by: Dmitriy Khaustov aka xDimon --- core/host_api/impl/host_api_impl.cpp | 6 +- core/injector/application_injector.cpp | 2 +- core/network/impl/peer_manager_impl.cpp | 18 ++- core/network/impl/peer_manager_impl.hpp | 12 +- .../approval/approval_distribution.cpp | 108 +++++++++--------- core/parachain/backing/grid_tracker.cpp | 3 +- core/runtime/common/memory_allocator.cpp | 3 +- core/runtime/runtime_api/impl/babe_api.cpp | 1 - core/scale/std_variant.hpp | 3 +- test/core/parachain/cluster_test.cpp | 2 +- 10 files changed, 87 insertions(+), 71 deletions(-) diff --git a/core/host_api/impl/host_api_impl.cpp b/core/host_api/impl/host_api_impl.cpp index 2f960f726d..f5b8ce882f 100644 --- a/core/host_api/impl/host_api_impl.cpp +++ b/core/host_api/impl/host_api_impl.cpp @@ -19,10 +19,8 @@ #include "runtime/trie_storage_provider.hpp" #include "storage/predefined_keys.hpp" -#define FFI \ - Ffi ffi { \ - memory_provider_->getCurrentMemory().value().get() \ - } +#define FFI \ + Ffi ffi { memory_provider_->getCurrentMemory().value().get() } namespace kagome::host_api { /** diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 2fab17d2b1..2b190a84e1 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -927,7 +927,7 @@ namespace kagome::injector { KagomeNodeInjector::KagomeNodeInjector( sptr app_config) : pimpl_{std::make_unique( - makeKagomeNodeInjector(app_config))} {} + makeKagomeNodeInjector(app_config))} {} sptr KagomeNodeInjector::injectAppConfig() { return pimpl_->injector_ diff --git a/core/network/impl/peer_manager_impl.cpp b/core/network/impl/peer_manager_impl.cpp index befa16adb5..27e0dcbe73 100644 --- a/core/network/impl/peer_manager_impl.cpp +++ b/core/network/impl/peer_manager_impl.cpp @@ -13,6 +13,7 @@ #include #include +#include "authority_discovery/query/query.hpp" #include "common/main_thread_pool.hpp" #include "network/impl/protocols/beefy_protocol_impl.hpp" #include "network/impl/protocols/grandpa_protocol.hpp" @@ -87,7 +88,8 @@ namespace kagome::network { std::shared_ptr storage, std::shared_ptr hasher, std::shared_ptr reputation_repository, - std::shared_ptr peer_view) + std::shared_ptr peer_view, + std::shared_ptr authority_discovery) : log_{log::createLogger("PeerManager", "network")}, host_(host), main_pool_handler_{poolHandlerReadyMake( @@ -104,7 +106,8 @@ namespace kagome::network { storage_{storage->getSpace(storage::Space::kDefault)}, hasher_{std::move(hasher)}, reputation_repository_{std::move(reputation_repository)}, - peer_view_{std::move(peer_view)} { + peer_view_{std::move(peer_view)}, + authority_discovery_{std::move(authority_discovery)} { BOOST_ASSERT(identify_ != nullptr); BOOST_ASSERT(kademlia_ != nullptr); BOOST_ASSERT(scheduler_ != nullptr); @@ -115,6 +118,7 @@ namespace kagome::network { BOOST_ASSERT(peer_view_); BOOST_ASSERT(reputation_repository_ != nullptr); BOOST_ASSERT(peer_view_ != nullptr); + BOOST_ASSERT(authority_discovery_ != nullptr); // Register metrics registry_->registerGaugeFamily(syncPeerMetricName, @@ -309,7 +313,6 @@ namespace kagome::network { clearClosedPingingConnections(); - // disconnect from peers with negative reputation using PriorityType = int32_t; using ItemType = std::pair; @@ -324,6 +327,14 @@ namespace kagome::network { std::chrono::duration_cast(peer_ttl).count(); for (const auto &[peer_id, desc] : active_peers_) { + // Skip peer having immunity + // TODO(xDimon): it's validators now. + // Probably is needed to limit them by common core + auto authority_id_opt = authority_discovery_->get(peer_id); + if (authority_id_opt.has_value()) { + continue; + } + const uint64_t last_activity_ms = std::chrono::time_point_cast( desc.time_point) @@ -335,6 +346,7 @@ namespace kagome::network { [[maybe_unused]] bool activity_timeout = last_activity_ms + idle_ms < now_ms; + // disconnect from peers with negative reputation const auto peer_reputation = reputation_repository_->reputation(peer_id); if (peer_reputation < kDisconnectReputation) { peers_list.push_back( diff --git a/core/network/impl/peer_manager_impl.hpp b/core/network/impl/peer_manager_impl.hpp index 60cab57c4d..63fa631590 100644 --- a/core/network/impl/peer_manager_impl.hpp +++ b/core/network/impl/peer_manager_impl.hpp @@ -44,6 +44,10 @@ namespace kagome { class PoolHandlerReady; } // namespace kagome +namespace kagome::authority_discovery { + class Query; +} + namespace kagome::network { enum class PeerType { PEER_TYPE_IN = 0, PEER_TYPE_OUT }; @@ -76,7 +80,8 @@ namespace kagome::network { std::shared_ptr storage, std::shared_ptr hasher, std::shared_ptr reputation_repository, - std::shared_ptr peer_view); + std::shared_ptr peer_view, + std::shared_ptr authority_discovery); /** @see poolHandlerReadyMake */ bool tryStart(); @@ -204,6 +209,8 @@ namespace kagome::network { std::shared_ptr storage_; std::shared_ptr hasher_; std::shared_ptr reputation_repository_; + std::shared_ptr peer_view_; + std::shared_ptr authority_discovery_; libp2p::event::Handle add_peer_handle_; libp2p::event::Handle peer_disconnected_handler_; @@ -222,9 +229,6 @@ namespace kagome::network { metrics::RegistryPtr registry_ = metrics::createRegistry(); metrics::Gauge *sync_peer_num_; metrics::Gauge *peers_count_metric_; - - // parachain - std::shared_ptr peer_view_; }; } // namespace kagome::network diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 8935acd602..888395358d 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -1224,63 +1224,63 @@ namespace kagome::parachain { ValidatorIndex validator_index, Hash block_hash, GroupIndex backing_group) { - auto on_recover_complete = [wself{weak_from_this()}, - hashed_candidate{hashed_candidate}, - block_hash, - session_index, - validator_index, - relay_block_hash]( - std::optional< - outcome::result> - &&opt_result) mutable { - auto self = wself.lock(); - if (!self) { - return; - } + auto on_recover_complete = + [wself{weak_from_this()}, + hashed_candidate{hashed_candidate}, + block_hash, + session_index, + validator_index, + relay_block_hash]( + std::optional> + &&opt_result) mutable { + auto self = wself.lock(); + if (!self) { + return; + } - const auto &candidate_receipt = hashed_candidate.get(); - if (!opt_result) { // Unavailable - self->logger_->warn( - "No available parachain data.(session index={}, candidate " - "hash={}, relay block hash={})", - session_index, - hashed_candidate.getHash(), - relay_block_hash); - return; - } + const auto &candidate_receipt = hashed_candidate.get(); + if (!opt_result) { // Unavailable + self->logger_->warn( + "No available parachain data.(session index={}, candidate " + "hash={}, relay block hash={})", + session_index, + hashed_candidate.getHash(), + relay_block_hash); + return; + } - if (opt_result->has_error()) { - self->logger_->warn( - "Parachain data recovery failed.(error={}, session index={}, " - "candidate hash={}, relay block hash={})", - opt_result->error(), - session_index, - hashed_candidate.getHash(), - relay_block_hash); - self->dispute_coordinator_.get()->issueLocalStatement( - session_index, - hashed_candidate.getHash(), - hashed_candidate.get(), - false); - return; - } - auto &available_data = opt_result->value(); - auto result = self->parachain_host_->validation_code_by_hash( - block_hash, candidate_receipt.descriptor.validation_code_hash); - if (result.has_error() || !result.value()) { - self->logger_->warn( - "Approval state is failed. Block hash {}, session index {}, " - "validator index {}, relay parent {}", - block_hash, - session_index, - validator_index, - candidate_receipt.descriptor.relay_parent); - return; /// ApprovalState::failed - } + if (opt_result->has_error()) { + self->logger_->warn( + "Parachain data recovery failed.(error={}, session index={}, " + "candidate hash={}, relay block hash={})", + opt_result->error(), + session_index, + hashed_candidate.getHash(), + relay_block_hash); + self->dispute_coordinator_.get()->issueLocalStatement( + session_index, + hashed_candidate.getHash(), + hashed_candidate.get(), + false); + return; + } + auto &available_data = opt_result->value(); + auto result = self->parachain_host_->validation_code_by_hash( + block_hash, candidate_receipt.descriptor.validation_code_hash); + if (result.has_error() || !result.value()) { + self->logger_->warn( + "Approval state is failed. Block hash {}, session index {}, " + "validator index {}, relay parent {}", + block_hash, + session_index, + validator_index, + candidate_receipt.descriptor.relay_parent); + return; /// ApprovalState::failed + } - self->logger_->info( - "Make exhaustive validation. Candidate hash {}, validator index " - "{}, block hash {}", + self->logger_->info( + "Make exhaustive validation. Candidate hash {}, validator index " + "{}, block hash {}", hashed_candidate.getHash(), validator_index, block_hash); diff --git a/core/parachain/backing/grid_tracker.cpp b/core/parachain/backing/grid_tracker.cpp index 8b0712462a..6545a888b4 100644 --- a/core/parachain/backing/grid_tracker.cpp +++ b/core/parachain/backing/grid_tracker.cpp @@ -208,7 +208,8 @@ namespace kagome::parachain::grid { received[v].candidate_statement_filter(candidate_hash); if (!statement_filter) { throw std::runtime_error( - "unconfirmed is only populated by validators who have sent manifest"); + "unconfirmed is only populated by validators who have sent " + "manifest"); } c.manifest_received_from(v, *statement_filter); } diff --git a/core/runtime/common/memory_allocator.cpp b/core/runtime/common/memory_allocator.cpp index 4f45177783..15f9929168 100644 --- a/core/runtime/common/memory_allocator.cpp +++ b/core/runtime/common/memory_allocator.cpp @@ -67,7 +67,8 @@ namespace kagome::runtime { auto pages = sizeToPages(next_offset); if (pages > max_memory_pages_num_) { throw std::runtime_error{ - "Memory resize failed, because maximum number of pages is reached."}; + "Memory resize failed, because maximum number of pages is " + "reached."}; } pages = std::max(pages, 2 * sizeToPages(memory_->size())); pages = std::min(pages, max_memory_pages_num_); diff --git a/core/runtime/runtime_api/impl/babe_api.cpp b/core/runtime/runtime_api/impl/babe_api.cpp index 7db4449ae2..2a1b2c6eaa 100644 --- a/core/runtime/runtime_api/impl/babe_api.cpp +++ b/core/runtime/runtime_api/impl/babe_api.cpp @@ -53,7 +53,6 @@ namespace kagome::runtime { key_owner_proof); } - outcome::result> BabeApiImpl::disabled_validators(const primitives::BlockHash &block) { OUTCOME_TRY(ctx, executor_->ctx().ephemeralAt(block)); diff --git a/core/scale/std_variant.hpp b/core/scale/std_variant.hpp index 1572c7ae06..1d3a481df7 100644 --- a/core/scale/std_variant.hpp +++ b/core/scale/std_variant.hpp @@ -33,7 +33,8 @@ namespace scale { Stream &operator>>(Stream &stream, std::variant &variant) { uint8_t index; stream >> index; - using Decoder = void (*)(Stream &stream, std::variant &variant); + using Decoder = + void (*)(Stream & stream, std::variant & variant); constexpr Decoder decoders[]{ make_decoder()...}; decoders[index](stream, variant); diff --git a/test/core/parachain/cluster_test.cpp b/test/core/parachain/cluster_test.cpp index 1a643c3327..7a2c9981d3 100644 --- a/test/core/parachain/cluster_test.cpp +++ b/test/core/parachain/cluster_test.cpp @@ -2,7 +2,7 @@ * Copyright Quadrivium LLC * All Rights Reserved * SPDX-License-Identifier: Apache-2.0 -*/ + */ #include "parachain/backing/cluster.hpp" From 81c606e8100aa3c728bf601c96dea90ccde17c26 Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov Date: Wed, 26 Jun 2024 12:01:19 +0300 Subject: [PATCH 2/7] Feature: don't disconnect peer if still needed (#2138) * feature: don't disconnect peer if still needed Signed-off-by: Dmitriy Khaustov aka xDimon --- core/network/impl/peer_manager_impl.cpp | 17 ++++++----------- core/network/impl/peer_manager_impl.hpp | 8 +------- 2 files changed, 7 insertions(+), 18 deletions(-) diff --git a/core/network/impl/peer_manager_impl.cpp b/core/network/impl/peer_manager_impl.cpp index 27e0dcbe73..e66681e365 100644 --- a/core/network/impl/peer_manager_impl.cpp +++ b/core/network/impl/peer_manager_impl.cpp @@ -13,7 +13,6 @@ #include #include -#include "authority_discovery/query/query.hpp" #include "common/main_thread_pool.hpp" #include "network/impl/protocols/beefy_protocol_impl.hpp" #include "network/impl/protocols/grandpa_protocol.hpp" @@ -88,8 +87,7 @@ namespace kagome::network { std::shared_ptr storage, std::shared_ptr hasher, std::shared_ptr reputation_repository, - std::shared_ptr peer_view, - std::shared_ptr authority_discovery) + std::shared_ptr peer_view) : log_{log::createLogger("PeerManager", "network")}, host_(host), main_pool_handler_{poolHandlerReadyMake( @@ -106,8 +104,7 @@ namespace kagome::network { storage_{storage->getSpace(storage::Space::kDefault)}, hasher_{std::move(hasher)}, reputation_repository_{std::move(reputation_repository)}, - peer_view_{std::move(peer_view)}, - authority_discovery_{std::move(authority_discovery)} { + peer_view_{std::move(peer_view)} { BOOST_ASSERT(identify_ != nullptr); BOOST_ASSERT(kademlia_ != nullptr); BOOST_ASSERT(scheduler_ != nullptr); @@ -118,7 +115,6 @@ namespace kagome::network { BOOST_ASSERT(peer_view_); BOOST_ASSERT(reputation_repository_ != nullptr); BOOST_ASSERT(peer_view_ != nullptr); - BOOST_ASSERT(authority_discovery_ != nullptr); // Register metrics registry_->registerGaugeFamily(syncPeerMetricName, @@ -328,11 +324,10 @@ namespace kagome::network { for (const auto &[peer_id, desc] : active_peers_) { // Skip peer having immunity - // TODO(xDimon): it's validators now. - // Probably is needed to limit them by common core - auto authority_id_opt = authority_discovery_->get(peer_id); - if (authority_id_opt.has_value()) { - continue; + if (auto it = peer_states_.find(peer_id); it != peer_states_.end()) { + if (not it->second.can_be_disconnected()) { + continue; + } } const uint64_t last_activity_ms = diff --git a/core/network/impl/peer_manager_impl.hpp b/core/network/impl/peer_manager_impl.hpp index 63fa631590..04a1151a1e 100644 --- a/core/network/impl/peer_manager_impl.hpp +++ b/core/network/impl/peer_manager_impl.hpp @@ -42,10 +42,6 @@ namespace kagome { class PoolHandlerReady; -} // namespace kagome - -namespace kagome::authority_discovery { - class Query; } namespace kagome::network { @@ -80,8 +76,7 @@ namespace kagome::network { std::shared_ptr storage, std::shared_ptr hasher, std::shared_ptr reputation_repository, - std::shared_ptr peer_view, - std::shared_ptr authority_discovery); + std::shared_ptr peer_view); /** @see poolHandlerReadyMake */ bool tryStart(); @@ -210,7 +205,6 @@ namespace kagome::network { std::shared_ptr hasher_; std::shared_ptr reputation_repository_; std::shared_ptr peer_view_; - std::shared_ptr authority_discovery_; libp2p::event::Handle add_peer_handle_; libp2p::event::Handle peer_disconnected_handler_; From 8ca1e3f605dcb714ea2de215516b66a528c209bf Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov Date: Wed, 26 Jun 2024 12:37:46 +0300 Subject: [PATCH 3/7] Feature: avoid connection with low reputation peers (#2054) * feature: avoid connection with low reputation peers Signed-off-by: Dmitriy Khaustov aka xDimon --- core/network/impl/peer_manager_impl.cpp | 30 ++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/core/network/impl/peer_manager_impl.cpp b/core/network/impl/peer_manager_impl.cpp index e66681e365..ccbc9517cc 100644 --- a/core/network/impl/peer_manager_impl.cpp +++ b/core/network/impl/peer_manager_impl.cpp @@ -26,8 +26,11 @@ namespace { constexpr const char *syncPeerMetricName = "kagome_sync_peers"; constexpr const char *kPeersCountMetricName = "kagome_sub_libp2p_peers_count"; - /// Reputation change for a node when we get disconnected from it. + /// Reputation value for a node when we get disconnected from it. static constexpr int32_t kDisconnectReputation = -256; + /// Reputation change for a node when we get disconnected from it. + static constexpr int32_t kMinReputationForInnerConnection = -128; + static constexpr int32_t kMinReputationForOuterConnection = -128; } // namespace OUTCOME_CPP_DEFINE_CATEGORY(kagome::network, PeerManagerImpl::Error, e) { @@ -421,6 +424,18 @@ namespace kagome::network { return; } + // Don't establish connection to bad (negative reputation) peers + const auto peer_reputation = reputation_repository_->reputation(peer_id); + if (peer_reputation < kMinReputationForOuterConnection) { + SL_DEBUG(log_, + "Attempt to establish connection to peer {} skipped: " + "peer has low ({}) reputation", + peer_id, + peer_reputation); + connecting_peers_.erase(peer_id); + return; + } + auto peer_info = host_.getPeerRepository().getPeerInfo(peer_id); if (peer_info.addresses.empty()) { SL_DEBUG(log_, "Not found addresses for peer {}", peer_id); @@ -799,6 +814,19 @@ namespace kagome::network { } } + // Don't accept connection from bad (negative reputation) peers + const auto peer_reputation = reputation_repository_->reputation(peer_id); + if (peer_reputation < kMinReputationForInnerConnection) { + SL_DEBUG(log_, + "New connection from peer {} was dropped: " + "peer has low ({}) reputation", + peer_id, + peer_reputation); + connecting_peers_.erase(peer_id); + disconnectFromPeer(peer_id); + return; + } + PeerInfo peer_info{.id = peer_id, .addresses = {}}; openBlockAnnounceProtocol( peer_info, From 8b3eeccdc13c0ef4f178600c7aac82a4e88f43fd Mon Sep 17 00:00:00 2001 From: Ruslan Tushov Date: Tue, 2 Jul 2024 18:15:20 +0500 Subject: [PATCH 4/7] binaryen memory size getter (#2143) Signed-off-by: turuslan --- cmake/Hunter/config.cmake | 9 --------- cmake/Hunter/hunter-gate-url.cmake | 4 ++-- core/runtime/binaryen/memory_impl.cpp | 4 +--- core/runtime/binaryen/memory_impl.hpp | 2 +- core/runtime/binaryen/module/module_impl.cpp | 2 -- core/runtime/binaryen/module/module_impl.hpp | 1 - .../binaryen/runtime_external_interface.cpp | 3 +-- .../binaryen/runtime_external_interface.hpp | 14 +++++++++++--- 8 files changed, 16 insertions(+), 23 deletions(-) diff --git a/cmake/Hunter/config.cmake b/cmake/Hunter/config.cmake index 5a2adef3e3..1c9244f25c 100644 --- a/cmake/Hunter/config.cmake +++ b/cmake/Hunter/config.cmake @@ -74,15 +74,6 @@ if ("${WASM_COMPILER}" STREQUAL "WAVM") ) endif () -# 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/e6a2fea157bde503f07f28444b350512374cf5bf.zip - SHA1 301f8b1775904179cb552c12be237b4aa076981e - ) -endif () - hunter_config( libsecp256k1 VERSION 0.4.1-qdrvm1 diff --git a/cmake/Hunter/hunter-gate-url.cmake b/cmake/Hunter/hunter-gate-url.cmake index 0893b53568..8bc0cb16cf 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.25.3-qdrvm16.zip - SHA1 990ea05207260b3757ce051e354cc163e910a211 + URL https://github.com/qdrvm/hunter/archive/refs/tags/v0.25.3-qdrvm18.zip + SHA1 22d842b448f84a39392d7835f4046da34f8dcd70 LOCAL ) diff --git a/core/runtime/binaryen/memory_impl.cpp b/core/runtime/binaryen/memory_impl.cpp index 8c9cd8b6ef..2f9091c811 100644 --- a/core/runtime/binaryen/memory_impl.cpp +++ b/core/runtime/binaryen/memory_impl.cpp @@ -16,9 +16,7 @@ namespace kagome::runtime::binaryen { MemoryImpl::MemoryImpl(RuntimeExternalInterface::InternalMemory *memory, const MemoryConfig &config) : memory_{memory}, - logger_{log::createLogger("Binaryen Memory", "binaryen")} { - memory_->resize(kInitialMemorySize); - } + logger_{log::createLogger("Binaryen Memory", "binaryen")} {} std::optional MemoryImpl::pagesMax() const { return memory_->pagesMax(); diff --git a/core/runtime/binaryen/memory_impl.hpp b/core/runtime/binaryen/memory_impl.hpp index 624674756b..f94c32eb7a 100644 --- a/core/runtime/binaryen/memory_impl.hpp +++ b/core/runtime/binaryen/memory_impl.hpp @@ -52,7 +52,7 @@ namespace kagome::runtime::binaryen { * deallocated_ pointers fixup */ if (new_size >= size()) { - memory_->resize(sizeToPages(new_size) * kMemoryPageSize); + memory_->pagesResize(sizeToPages(new_size)); } } diff --git a/core/runtime/binaryen/module/module_impl.cpp b/core/runtime/binaryen/module/module_impl.cpp index 3ac2ba9ffa..95843a9a31 100644 --- a/core/runtime/binaryen/module/module_impl.cpp +++ b/core/runtime/binaryen/module/module_impl.cpp @@ -81,8 +81,6 @@ namespace kagome::runtime::binaryen { } } - module->memory.initial = kDefaultHeappages; - return std::make_shared( std::move(module), module_factory, env_factory, code_hash); } diff --git a/core/runtime/binaryen/module/module_impl.hpp b/core/runtime/binaryen/module/module_impl.hpp index 0e25751cb0..08d36afadc 100644 --- a/core/runtime/binaryen/module/module_impl.hpp +++ b/core/runtime/binaryen/module/module_impl.hpp @@ -33,7 +33,6 @@ namespace kagome::runtime::binaryen { class ModuleImpl final : public runtime::Module, public std::enable_shared_from_this { public: - static constexpr auto kDefaultHeappages = 1024; enum class Error { EMPTY_STATE_CODE = 1, INVALID_STATE_CODE }; ModuleImpl(ModuleImpl &&) = default; diff --git a/core/runtime/binaryen/runtime_external_interface.cpp b/core/runtime/binaryen/runtime_external_interface.cpp index f42f1121fd..31b4357ac3 100644 --- a/core/runtime/binaryen/runtime_external_interface.cpp +++ b/core/runtime/binaryen/runtime_external_interface.cpp @@ -141,7 +141,6 @@ namespace kagome::runtime::binaryen { std::shared_ptr host_api) : host_api_{std::move(host_api)}, logger_{log::createLogger("RuntimeExternalInterface", "binaryen")} { - memory.resize(kInitialMemorySize); BOOST_ASSERT(host_api_); registerMethods(); } @@ -195,7 +194,7 @@ namespace kagome::runtime::binaryen { void RuntimeExternalInterface::init(wasm::Module &wasm, wasm::ModuleInstance &instance) { - memory.resize(wasm.memory.initial * wasm::Memory::kPageSize); + memory.pagesResize(wasm.memory.initial); if (wasm.memory.hasMax()) { memory.pages_max = wasm.memory.max; } diff --git a/core/runtime/binaryen/runtime_external_interface.hpp b/core/runtime/binaryen/runtime_external_interface.hpp index 345d1e07e0..5ed49b3162 100644 --- a/core/runtime/binaryen/runtime_external_interface.hpp +++ b/core/runtime/binaryen/runtime_external_interface.hpp @@ -52,7 +52,11 @@ namespace kagome::runtime::binaryen { public: Memory() = default; - void resize(size_t newSize) { + uint32_t pages() { + return memory.size() / ::wasm::Memory::kPageSize; + } + void pagesResize(size_t newPages) { + size_t newSize = newPages * ::wasm::Memory::kPageSize; // Ensure the smallest allocation is large enough that most allocators // will provide page-aligned storage. This hopefully allows the // interpreter's memory to be as aligned as the memory being simulated, @@ -171,8 +175,12 @@ namespace kagome::runtime::binaryen { memory.set>(addr, value); } - void growMemory(wasm::Address /*oldSize*/, wasm::Address newSize) override { - memory.resize(newSize); + uint32_t memoryPages() override { + return memory.pages(); + } + + void memoryPagesGrow(uint32_t /*oldPages*/, uint32_t newPages) override { + memory.pagesResize(newPages); } [[noreturn]] void trap(const char *why) override { From 837de1a6300ea46448f6505703eb9efdb09677e2 Mon Sep 17 00:00:00 2001 From: zerg-su <65297783+zerg-su@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:58:03 +0300 Subject: [PATCH 5/7] zombie builder docker -> bookworm (#2147) Co-authored-by: Kirill Azovtsev --- zombienet/docker/builder.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zombienet/docker/builder.Dockerfile b/zombienet/docker/builder.Dockerfile index 6932635f38..c4192892c4 100644 --- a/zombienet/docker/builder.Dockerfile +++ b/zombienet/docker/builder.Dockerfile @@ -2,7 +2,7 @@ ARG POLKADOT_RELEASE_GLOBAL ARG POLKADOT_RELEASE_GLOBAL_NUMERIC ARG ZOMBIENET_RELEASE -FROM node:18-bullseye-slim as zombie-builder +FROM node:bookworm-slim as zombie-builder USER root RUN apt-get update && apt update && \ From 9b12f3a7f8917c2f10205e8b83c049d807a0dd9f Mon Sep 17 00:00:00 2001 From: Ruslan Tushov Date: Wed, 3 Jul 2024 19:31:55 +0500 Subject: [PATCH 6/7] read_embedded_version (#2141) Signed-off-by: turuslan --- core/injector/calculate_genesis_state.hpp | 21 +++++---- core/primitives/version.cpp | 32 +++++++++++++ core/primitives/version.hpp | 37 +++++++-------- .../binaryen/core_api_factory_impl.cpp | 0 core/runtime/common/CMakeLists.txt | 2 + core/runtime/common/core_api_factory_impl.cpp | 19 ++++++++ .../runtime/common/module_repository_impl.cpp | 42 ++++++++++++----- .../runtime/common/module_repository_impl.hpp | 15 +++++++ .../common/uncompress_code_if_needed.hpp | 7 +++ core/runtime/module_repository.hpp | 7 +++ core/runtime/runtime_api/impl/core.cpp | 7 +++ core/runtime/runtime_api/impl/core.hpp | 2 + core/runtime/wabt/CMakeLists.txt | 2 + core/runtime/wabt/version.cpp | 45 +++++++++++++++++++ core/runtime/wabt/version.hpp | 21 +++++++++ core/runtime/wavm/core_api_factory_impl.cpp | 0 test/core/runtime/runtime_test_base.hpp | 1 + .../runtime/wavm/core_integration_test.cpp | 3 +- test/external-project-test/src/main.cpp | 1 + .../core/runtime/module_repository_mock.hpp | 5 +++ 20 files changed, 228 insertions(+), 41 deletions(-) delete mode 100644 core/runtime/binaryen/core_api_factory_impl.cpp create mode 100644 core/runtime/wabt/version.cpp create mode 100644 core/runtime/wabt/version.hpp delete mode 100644 core/runtime/wavm/core_api_factory_impl.cpp diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index 4e010b0543..e2e8ca9515 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -7,8 +7,10 @@ #pragma once #include "application/chain_spec.hpp" +#include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/heap_alloc_strategy_heappages.hpp" #include "runtime/runtime_api/impl/core.hpp" +#include "runtime/wabt/version.hpp" #include "storage/predefined_keys.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_impl.hpp" #include "storage/trie/serialization/trie_serializer.hpp" @@ -30,14 +32,17 @@ namespace kagome::injector { }; auto top_trie = trie_from(chain_spec.getGenesisTopSection()); OUTCOME_TRY(code, top_trie->get(storage::kRuntimeCodeKey)); - - runtime::MemoryLimits config; - BOOST_OUTCOME_TRY(config.heap_alloc_strategy, - heapAllocStrategyHeappagesDefault(*top_trie)); - OUTCOME_TRY( - runtime_version, - runtime::callCoreVersion(module_factory, code, config, runtime_cache)); - auto version = storage::trie::StateVersion{runtime_version.state_version}; + BOOST_OUTCOME_TRY(code, runtime::uncompressCodeIfNeeded(code)); + OUTCOME_TRY(runtime_version, runtime::readEmbeddedVersion(code)); + if (not runtime_version) { + runtime::MemoryLimits config; + BOOST_OUTCOME_TRY(config.heap_alloc_strategy, + heapAllocStrategyHeappagesDefault(*top_trie)); + BOOST_OUTCOME_TRY(runtime_version, + runtime::callCoreVersion( + module_factory, code, config, runtime_cache)); + } + auto version = storage::trie::StateVersion{runtime_version->state_version}; std::vector> child_tries; for (auto &[child, kv] : chain_spec.getGenesisChildrenDefaultSection()) { child_tries.emplace_back(trie_from(kv)); diff --git a/core/primitives/version.cpp b/core/primitives/version.cpp index 069f9d8ee3..68c8cdaa53 100644 --- a/core/primitives/version.cpp +++ b/core/primitives/version.cpp @@ -34,3 +34,35 @@ namespace kagome::primitives::detail { } } // namespace kagome::primitives::detail + +namespace kagome::primitives { + outcome::result Version::decode( + scale::ScaleDecoderStream &s, std::optional core_version) { + Version v; + auto t = std::tie(v.spec_name, + v.impl_name, + v.authoring_version, + v.spec_version, + v.impl_version, + v.apis); + OUTCOME_TRY(scale::decode(s, t)); + + if (not core_version) { + core_version = detail::coreVersionFromApis(v.apis); + } + // old Kusama runtimes do not contain transaction_version and + // state_version + // https://github.com/paritytech/substrate/blob/1b3ddae9dec6e7653b5d6ef0179df1af831f46f0/primitives/version/src/lib.rs#L238 + if (core_version and *core_version >= 3) { + OUTCOME_TRY(scale::decode(s, v.transaction_version)); + } else { + v.transaction_version = 1; + } + if (core_version and *core_version >= 4) { + OUTCOME_TRY(scale::decode(s, v.state_version)); + } else { + v.state_version = 0; + } + return v; + } +} // namespace kagome::primitives diff --git a/core/primitives/version.hpp b/core/primitives/version.hpp index 3c9e335100..f6f0c0cfee 100644 --- a/core/primitives/version.hpp +++ b/core/primitives/version.hpp @@ -91,9 +91,20 @@ namespace kagome::primitives { and state_version == rhs.state_version; } - bool operator!=(const Version &rhs) const { - return !operator==(rhs); - } + /** + * `Decode` while giving a "version hint" + * There exists multiple versions of [`RuntimeVersion`] and they are + * versioned using the `Core` runtime api: + * - `Core` version < 3 is a runtime version without a transaction version + * and state version. + * - `Core` version 3 is a runtime version without a state version. + * - `Core` version 4 is the latest runtime version. + * `core_version` hint is used by `readEmbeddedVersion`, because + * `Version.apis` is stored separately from other `Version` fields. + * https://github.com/paritytech/polkadot-sdk/blob/aaf0443591b134a0da217d575161872796e75059/substrate/primitives/version/src/lib.rs#L242 + */ + static outcome::result decode( + scale::ScaleDecoderStream &s, std::optional core_version); }; namespace detail { @@ -130,24 +141,8 @@ namespace kagome::primitives { template > Stream &operator>>(Stream &s, Version &v) { - s >> v.spec_name >> v.impl_name >> v.authoring_version >> v.spec_version - >> v.impl_version >> v.apis; - - auto core_version = detail::coreVersionFromApis(v.apis); - // old Kusama runtimes do not contain transaction_version and state_version - // https://github.com/paritytech/substrate/blob/1b3ddae9dec6e7653b5d6ef0179df1af831f46f0/primitives/version/src/lib.rs#L238 - if (core_version.has_value() and core_version.value() >= 3 - and s.hasMore(sizeof(v.transaction_version))) { - s >> v.transaction_version; - } else { - v.transaction_version = 1; - } - if (core_version.has_value() and core_version.value() >= 4 - and s.hasMore(sizeof(v.state_version))) { - s >> v.state_version; - } else { - v.state_version = 0; - } + // `.value()` may throw, `scale::decode` will catch that + v = Version::decode(s, std::nullopt).value(); return s; } } // namespace kagome::primitives diff --git a/core/runtime/binaryen/core_api_factory_impl.cpp b/core/runtime/binaryen/core_api_factory_impl.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/core/runtime/common/CMakeLists.txt b/core/runtime/common/CMakeLists.txt index 34a13ebc69..3cacfb64f4 100644 --- a/core/runtime/common/CMakeLists.txt +++ b/core/runtime/common/CMakeLists.txt @@ -98,5 +98,7 @@ add_library(core_api_factory core_api_factory_impl.cpp) target_link_libraries(core_api_factory core_api outcome + uncompress_if_needed + wasm_instrument ) 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 index 085659d956..b049781a33 100644 --- a/core/runtime/common/core_api_factory_impl.cpp +++ b/core/runtime/common/core_api_factory_impl.cpp @@ -8,11 +8,25 @@ #include "runtime/common/runtime_properties_cache_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" +#include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/module_repository.hpp" #include "runtime/runtime_api/impl/core.hpp" #include "runtime/runtime_context.hpp" +#include "runtime/wabt/version.hpp" namespace kagome::runtime { + using primitives::Version; + class GetVersion : public RestrictedCore { + public: + GetVersion(const Version &version) : version_{version} {} + + outcome::result version() { + return version_; + } + + private: + Version version_; + }; CoreApiFactoryImpl::CoreApiFactoryImpl( std::shared_ptr module_factory) @@ -23,6 +37,11 @@ namespace kagome::runtime { outcome::result> CoreApiFactoryImpl::make( std::shared_ptr hasher, const std::vector &runtime_code) const { + OUTCOME_TRY(code, uncompressCodeIfNeeded(runtime_code)); + OUTCOME_TRY(version, readEmbeddedVersion(code)); + if (version) { + return std::make_unique(*version); + } OUTCOME_TRY( ctx, RuntimeContextFactory::fromCode(*module_factory_, runtime_code, {})); diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index 89369c11e1..570715018a 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -6,8 +6,10 @@ #include "runtime/common/module_repository_impl.hpp" +#include "blockchain/block_header_repository.hpp" #include "log/profiling_logger.hpp" #include "runtime/common/runtime_instances_pool.hpp" +#include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/heap_alloc_strategy_heappages.hpp" #include "runtime/instance_environment.hpp" #include "runtime/module.hpp" @@ -15,6 +17,7 @@ #include "runtime/module_instance.hpp" #include "runtime/runtime_code_provider.hpp" #include "runtime/runtime_upgrade_tracker.hpp" +#include "runtime/wabt/version.hpp" #include "storage/trie/trie_storage.hpp" namespace kagome::runtime { @@ -24,12 +27,15 @@ namespace kagome::runtime { ModuleRepositoryImpl::ModuleRepositoryImpl( std::shared_ptr runtime_instances_pool, std::shared_ptr hasher, + std::shared_ptr + block_header_repository, std::shared_ptr runtime_upgrade_tracker, std::shared_ptr trie_storage, std::shared_ptr module_factory, std::shared_ptr code_provider) : runtime_instances_pool_{std::move(runtime_instances_pool)}, hasher_{std::move(hasher)}, + block_header_repository_{std::move(block_header_repository)}, runtime_upgrade_tracker_{std::move(runtime_upgrade_tracker)}, trie_storage_{std::move(trie_storage)}, module_factory_{std::move(module_factory)}, @@ -45,6 +51,22 @@ namespace kagome::runtime { ModuleRepositoryImpl::getInstanceAt( const primitives::BlockInfo &block, const storage::trie::RootHash &storage_state) { + OUTCOME_TRY(item, codeAt(block, storage_state)); + return runtime_instances_pool_->instantiateFromCode( + item.hash, *item.code, {item.config}); + } + + outcome::result> + ModuleRepositoryImpl::embeddedVersion( + const primitives::BlockHash &block_hash) { + OUTCOME_TRY(header, block_header_repository_->getBlockHeader(block_hash)); + OUTCOME_TRY(item, codeAt(header.blockInfo(), header.state_root)); + return item.version; + } + + outcome::result ModuleRepositoryImpl::codeAt( + const primitives::BlockInfo &block, + const storage::trie::RootHash &storage_state) { KAGOME_PROFILE_START(code_retrieval) OUTCOME_TRY(state, runtime_upgrade_tracker_->getLastCodeUpdateState(block)); KAGOME_PROFILE_END(code_retrieval) @@ -55,12 +77,15 @@ namespace kagome::runtime { if (auto r = cache_.get(state)) { item = r->get(); } else { - auto code = code_provider_->getCodeAt(state); - if (not code.has_value()) { - code = code_provider_->getCodeAt(storage_state); + auto code_res = code_provider_->getCodeAt(state); + if (not code_res) { + code_res = code_provider_->getCodeAt(storage_state); } - BOOST_OUTCOME_TRY(item.code, std::move(code)); - item.hash = hasher_->blake2b_256(*item.code); + auto &code_zstd = *code_res.value(); + item.hash = hasher_->blake2b_256(code_zstd); + OUTCOME_TRY(code, uncompressCodeIfNeeded(code_zstd)); + item.code = std::make_shared(code); + BOOST_OUTCOME_TRY(item.version, readEmbeddedVersion(code)); OUTCOME_TRY(batch, trie_storage_->getEphemeralBatchAt(storage_state)); BOOST_OUTCOME_TRY(item.config.heap_alloc_strategy, heapAllocStrategyHeappagesDefault(*batch)); @@ -68,11 +93,6 @@ namespace kagome::runtime { } return outcome::success(); }); - OUTCOME_TRY(runtime_instance, - runtime_instances_pool_->instantiateFromCode( - item.hash, *item.code, {item.config})); - KAGOME_PROFILE_END(module_retrieval) - - return runtime_instance; + return item; } } // namespace kagome::runtime diff --git a/core/runtime/common/module_repository_impl.hpp b/core/runtime/common/module_repository_impl.hpp index d663314e3a..3872b316ec 100644 --- a/core/runtime/common/module_repository_impl.hpp +++ b/core/runtime/common/module_repository_impl.hpp @@ -16,6 +16,10 @@ #include "utils/lru.hpp" #include "utils/safe_object.hpp" +namespace kagome::blockchain { + class BlockHeaderRepository; +} // namespace kagome::blockchain + namespace kagome::crypto { class Hasher; } // namespace kagome::crypto @@ -34,6 +38,8 @@ namespace kagome::runtime { ModuleRepositoryImpl( std::shared_ptr runtime_instances_pool, std::shared_ptr hasher, + std::shared_ptr + block_header_repository, std::shared_ptr runtime_upgrade_tracker, std::shared_ptr trie_storage, std::shared_ptr module_factory, @@ -43,14 +49,23 @@ namespace kagome::runtime { const primitives::BlockInfo &block, const storage::trie::RootHash &state) override; + outcome::result> embeddedVersion( + const primitives::BlockHash &block_hash) override; + private: struct Item { common::Hash256 hash; std::shared_ptr code; + std::optional version; MemoryLimits config; }; + + outcome::result codeAt(const primitives::BlockInfo &block, + const storage::trie::RootHash &storage_state); + std::shared_ptr runtime_instances_pool_; std::shared_ptr hasher_; + std::shared_ptr block_header_repository_; std::shared_ptr runtime_upgrade_tracker_; std::shared_ptr trie_storage_; std::shared_ptr module_factory_; diff --git a/core/runtime/common/uncompress_code_if_needed.hpp b/core/runtime/common/uncompress_code_if_needed.hpp index 3e46085261..338cd9a6d1 100644 --- a/core/runtime/common/uncompress_code_if_needed.hpp +++ b/core/runtime/common/uncompress_code_if_needed.hpp @@ -27,4 +27,11 @@ namespace kagome::runtime { UncompressOutcome uncompressCodeIfNeeded(common::BufferView buf, common::Buffer &res); + + inline UncompressOutcome uncompressCodeIfNeeded( + BufferView data_zstd) { + Buffer data; + OUTCOME_TRY(uncompressCodeIfNeeded(data_zstd, data)); + return data; + } } // namespace kagome::runtime diff --git a/core/runtime/module_repository.hpp b/core/runtime/module_repository.hpp index e76fb5b8b2..31d97c441d 100644 --- a/core/runtime/module_repository.hpp +++ b/core/runtime/module_repository.hpp @@ -13,6 +13,7 @@ #include "host_api/host_api.hpp" #include "outcome/outcome.hpp" #include "primitives/block_data.hpp" +#include "primitives/version.hpp" namespace kagome::runtime { @@ -41,6 +42,12 @@ namespace kagome::runtime { virtual outcome::result> getInstanceAt( const primitives::BlockInfo &block, const storage::trie::RootHash &state_hash) = 0; + + /** + * Return cached `readEmbeddedVersion` result. + */ + virtual outcome::result> embeddedVersion( + const primitives::BlockHash &block_hash) = 0; }; } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/core.cpp b/core/runtime/runtime_api/impl/core.cpp index 7671df1146..7211ab70ad 100644 --- a/core/runtime/runtime_api/impl/core.cpp +++ b/core/runtime/runtime_api/impl/core.cpp @@ -10,6 +10,7 @@ #include "log/logger.hpp" #include "runtime/executor.hpp" #include "runtime/module_instance.hpp" +#include "runtime/module_repository.hpp" #include "runtime/runtime_properties_cache.hpp" namespace kagome::runtime { @@ -44,9 +45,11 @@ namespace kagome::runtime { CoreImpl::CoreImpl( std::shared_ptr executor, + std::shared_ptr module_repository, std::shared_ptr header_repo, std::shared_ptr runtime_upgrade_tracker) : executor_{std::move(executor)}, + module_repository_{std::move(module_repository)}, header_repo_{std::move(header_repo)}, runtime_upgrade_tracker_{std::move(runtime_upgrade_tracker)} { BOOST_ASSERT(executor_ != nullptr); @@ -56,6 +59,10 @@ namespace kagome::runtime { outcome::result CoreImpl::version( const primitives::BlockHash &block) { + OUTCOME_TRY(version, module_repository_->embeddedVersion(block)); + if (version) { + return *version; + } return version_.call(*header_repo_, *runtime_upgrade_tracker_, *executor_, diff --git a/core/runtime/runtime_api/impl/core.hpp b/core/runtime/runtime_api/impl/core.hpp index 20cb844cb5..780c060ee5 100644 --- a/core/runtime/runtime_api/impl/core.hpp +++ b/core/runtime/runtime_api/impl/core.hpp @@ -35,6 +35,7 @@ namespace kagome::runtime { public: CoreImpl( std::shared_ptr executor, + std::shared_ptr module_repository, std::shared_ptr header_repo, std::shared_ptr runtime_upgrade_tracker); @@ -55,6 +56,7 @@ namespace kagome::runtime { private: std::shared_ptr executor_; + std::shared_ptr module_repository_; std::shared_ptr header_repo_; std::shared_ptr runtime_upgrade_tracker_; diff --git a/core/runtime/wabt/CMakeLists.txt b/core/runtime/wabt/CMakeLists.txt index 2e5a248393..7b4f5cfd9c 100644 --- a/core/runtime/wabt/CMakeLists.txt +++ b/core/runtime/wabt/CMakeLists.txt @@ -7,10 +7,12 @@ add_library(wasm_instrument instrument.cpp stack_limiter.cpp + version.cpp ) target_link_libraries(wasm_instrument logger outcome + primitives wabt::wabt ) kagome_install(wasm_instrument) diff --git a/core/runtime/wabt/version.cpp b/core/runtime/wabt/version.cpp new file mode 100644 index 0000000000..37ed002750 --- /dev/null +++ b/core/runtime/wabt/version.cpp @@ -0,0 +1,45 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "runtime/wabt/version.hpp" + +#include "runtime/wabt/util.hpp" + +namespace kagome::runtime { + outcome::result> readEmbeddedVersion( + BufferView wasm) { + OUTCOME_TRY(module, wabtDecode(wasm)); + auto custom_section_contents = [&](std::string_view name) { + auto it = std::find_if( + module.customs.begin(), + module.customs.end(), + [&](const wabt::Custom §ion) { return section.name == name; }); + return it != module.customs.end() + ? std::make_optional(BufferView{it->data}) + : std::nullopt; + }; + auto version_section = custom_section_contents("runtime_version"); + if (not version_section) { + return std::nullopt; + } + std::optional apis; + std::optional core_version; + if (auto apis_section = custom_section_contents("runtime_apis")) { + apis.emplace(); + scale::ScaleDecoderStream s{*apis_section}; + while (s.hasMore(1)) { + OUTCOME_TRY(scale::decode(s, apis->emplace_back())); + } + core_version = primitives::detail::coreVersionFromApis(*apis); + } + scale::ScaleDecoderStream s{*version_section}; + OUTCOME_TRY(version, primitives::Version::decode(s, core_version)); + if (apis) { + version.apis = std::move(*apis); + } + return version; + } +} // namespace kagome::runtime diff --git a/core/runtime/wabt/version.hpp b/core/runtime/wabt/version.hpp new file mode 100644 index 0000000000..faca2d9386 --- /dev/null +++ b/core/runtime/wabt/version.hpp @@ -0,0 +1,21 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "primitives/version.hpp" + +namespace kagome::runtime { + /** + * Take the runtime blob and scan it for the custom wasm sections containing + * the version information and construct the `RuntimeVersion` from them. + * If there are no such sections, it returns `None`. If there is an error + * during decoding those sections, `Err` will be returned. + * https://github.com/paritytech/polkadot-sdk/blob/929a273ae1ba647628c4ba6e2f8737e58b596d6a/substrate/client/executor/src/wasm_runtime.rs#L355 + */ + outcome::result> readEmbeddedVersion( + BufferView wasm); +} // namespace kagome::runtime diff --git a/core/runtime/wavm/core_api_factory_impl.cpp b/core/runtime/wavm/core_api_factory_impl.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/test/core/runtime/runtime_test_base.hpp b/test/core/runtime/runtime_test_base.hpp index db4437aa3d..47a7737158 100644 --- a/test/core/runtime/runtime_test_base.hpp +++ b/test/core/runtime/runtime_test_base.hpp @@ -171,6 +171,7 @@ class RuntimeTestBase : public ::testing::Test { std::make_shared( module_factory, std::make_shared()), hasher_, + header_repo_, upgrade_tracker, trie_storage_, module_factory, diff --git a/test/core/runtime/wavm/core_integration_test.cpp b/test/core/runtime/wavm/core_integration_test.cpp index a4ef9b7a0b..c49c5983e3 100644 --- a/test/core/runtime/wavm/core_integration_test.cpp +++ b/test/core/runtime/wavm/core_integration_test.cpp @@ -43,7 +43,8 @@ class CoreTest : public WavmRuntimeTest { void SetUp() override { WavmRuntimeTest::SetUp(); - core_ = std::make_shared(executor_, header_repo_, nullptr); + core_ = + std::make_shared(executor_, nullptr, header_repo_, nullptr); } protected: diff --git a/test/external-project-test/src/main.cpp b/test/external-project-test/src/main.cpp index 9615470cd5..7428d62b20 100644 --- a/test/external-project-test/src/main.cpp +++ b/test/external-project-test/src/main.cpp @@ -200,6 +200,7 @@ int main() { auto module_repo = std::make_shared( runtime_instances_pool, hasher, + header_repo, runtime_upgrade_tracker, trie_storage, module_factory, diff --git a/test/mock/core/runtime/module_repository_mock.hpp b/test/mock/core/runtime/module_repository_mock.hpp index f33efd6d75..0d6df6cfc4 100644 --- a/test/mock/core/runtime/module_repository_mock.hpp +++ b/test/mock/core/runtime/module_repository_mock.hpp @@ -21,6 +21,11 @@ namespace kagome::runtime { (const primitives::BlockInfo &block, const storage::trie::RootHash &state_root), (override)); + + MOCK_METHOD(outcome::result>, + embeddedVersion, + (const primitives::BlockHash &), + (override)); }; } // namespace kagome::runtime From 9e2aaf80253ab8a17bbc5f6462714f9311f4347f Mon Sep 17 00:00:00 2001 From: Ruslan Tushov Date: Thu, 4 Jul 2024 18:33:11 +0500 Subject: [PATCH 7/7] explicit compile (#2140) * explicit compile * Reduce kagome validators in 1st test Signed-off-by: turuslan Co-authored-by: kamilsa --- .../impl/app_configuration_impl.cpp | 28 ++- core/application/modes/precompile_wasm.cpp | 17 +- core/application/modes/precompile_wasm.hpp | 15 +- .../timeline/impl/block_executor_impl.cpp | 4 +- .../timeline/impl/block_executor_impl.hpp | 3 +- .../consensus/timeline/impl/timeline_impl.cpp | 11 +- .../consensus/timeline/impl/timeline_impl.hpp | 10 ++ core/crypto/key_store/key_file_storage.cpp | 43 +---- core/crypto/key_store/key_file_storage.hpp | 2 - core/filesystem/directories.hpp | 22 --- core/host_api/impl/host_api_impl.cpp | 1 + core/host_api/impl/misc_extension.cpp | 19 +-- core/host_api/impl/misc_extension.hpp | 3 + core/injector/application_injector.cpp | 27 +-- core/injector/calculate_genesis_state.hpp | 24 ++- core/injector/get_peer_keypair.hpp | 7 +- core/log/formatters/filepath.hpp | 27 +-- .../approval/approval_distribution.cpp | 8 +- core/parachain/pvf/kagome_pvf_worker.cpp | 26 ++- .../pvf/kagome_pvf_worker_injector.hpp | 32 +--- core/parachain/pvf/module_precompiler.cpp | 2 +- core/parachain/pvf/pool.cpp | 37 ++-- core/parachain/pvf/pool.hpp | 11 ++ core/parachain/pvf/precheck.cpp | 3 +- core/parachain/pvf/pvf_impl.cpp | 30 +--- core/parachain/pvf/pvf_worker_types.hpp | 6 +- core/parachain/pvf/secure_mode.hpp | 4 + core/parachain/pvf/secure_mode_precheck.cpp | 15 +- .../binaryen/binaryen_memory_factory.cpp | 5 +- .../binaryen/binaryen_memory_factory.hpp | 3 +- .../binaryen/binaryen_memory_provider.cpp | 2 +- .../binaryen/instance_environment_factory.cpp | 9 +- .../binaryen/instance_environment_factory.hpp | 10 +- core/runtime/binaryen/memory_impl.cpp | 3 +- core/runtime/binaryen/memory_impl.hpp | 3 +- .../binaryen/module/module_factory_impl.cpp | 28 ++- .../binaryen/module/module_factory_impl.hpp | 12 +- core/runtime/binaryen/module/module_impl.cpp | 13 +- core/runtime/binaryen/module/module_impl.hpp | 14 +- .../binaryen/runtime_external_interface.cpp | 6 +- core/runtime/common/core_api_factory_impl.cpp | 37 ++-- core/runtime/common/core_api_factory_impl.hpp | 20 ++- core/runtime/common/memory_allocator.cpp | 3 - core/runtime/common/module_instance.cpp | 14 +- .../runtime/common/module_repository_impl.cpp | 2 +- core/runtime/common/runtime_context.cpp | 61 +++---- .../runtime/common/runtime_instances_pool.cpp | 91 ++++++---- .../runtime/common/runtime_instances_pool.hpp | 21 ++- core/runtime/common/storage_code_provider.cpp | 16 +- core/runtime/common/storage_code_provider.hpp | 2 - core/runtime/core_api_factory.hpp | 16 +- core/runtime/memory.hpp | 5 - core/runtime/module_factory.hpp | 29 +++- core/runtime/module_instance.hpp | 4 +- core/runtime/runtime_api/impl/core.cpp | 21 --- core/runtime/runtime_api/impl/core.hpp | 7 - core/runtime/runtime_context.hpp | 54 ++---- core/runtime/runtime_instances_pool.hpp | 4 +- core/runtime/types.hpp | 4 +- core/runtime/wasm_edge/memory_impl.cpp | 8 +- core/runtime/wasm_edge/memory_impl.hpp | 3 +- .../runtime/wasm_edge/module_factory_impl.cpp | 91 +++++----- .../runtime/wasm_edge/module_factory_impl.hpp | 20 ++- core/runtime/wavm/CMakeLists.txt | 1 - .../wavm/instance_environment_factory.cpp | 10 +- .../wavm/instance_environment_factory.hpp | 6 +- core/runtime/wavm/memory_impl.cpp | 4 +- core/runtime/wavm/memory_impl.hpp | 7 +- core/runtime/wavm/module_cache.cpp | 60 ------- core/runtime/wavm/module_cache.hpp | 42 ----- core/runtime/wavm/module_factory_impl.cpp | 77 +++++++-- core/runtime/wavm/module_factory_impl.hpp | 19 ++- .../wavm/wavm_external_memory_provider.cpp | 4 +- .../wavm/wavm_internal_memory_provider.cpp | 2 +- core/storage/rocksdb/rocksdb.cpp | 6 +- core/utils/kagome_db_editor.cpp | 6 - core/utils/mkdirs.hpp | 25 +++ core/utils/read_file.hpp | 14 +- core/utils/write_file.hpp | 45 +++++ .../core/consensus/timeline/timeline_test.cpp | 9 + test/core/host_api/misc_extension_test.cpp | 7 +- test/core/parachain/pvf_test.cpp | 39 +++-- .../binaryen/binaryen_runtime_test.hpp | 2 +- .../runtime/binaryen/wasm_memory_test.cpp | 73 +------- test/core/runtime/instance_pool_test.cpp | 57 ++++--- test/core/runtime/runtime_test_base.hpp | 42 +++-- test/core/runtime/wavm/CMakeLists.txt | 1 + .../runtime/wavm/core_integration_test.cpp | 4 +- test/core/runtime/wavm/wasm_memory_test.cpp | 7 +- .../runtime/wavm/wavm_module_init_test.cpp | 159 ++---------------- test/core/runtime/wavm/wavm_runtime_test.hpp | 7 +- test/external-project-test/src/main.cpp | 21 ++- .../binaryen_wasm_memory_factory_mock.hpp | 3 +- .../core/runtime/core_api_factory_mock.hpp | 3 +- .../mock/core/runtime/module_factory_mock.hpp | 12 +- .../core/runtime/module_instance_mock.hpp | 2 + .../runtime/runtime_context_factory_mock.hpp | 19 +-- .../functional/0001-parachains-pvf.toml | 10 +- .../functional/0001-parachains-pvf.zndsl | 16 +- 99 files changed, 825 insertions(+), 1074 deletions(-) delete mode 100644 core/filesystem/directories.hpp delete mode 100644 core/runtime/wavm/module_cache.cpp delete mode 100644 core/runtime/wavm/module_cache.hpp create mode 100644 core/utils/mkdirs.hpp create mode 100644 core/utils/write_file.hpp diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index e7deeaa327..a406b4890c 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -8,7 +8,6 @@ #include #include -#include #include #include #include @@ -31,9 +30,10 @@ #include "common/hexutil.hpp" #include "common/uri.hpp" #include "filesystem/common.hpp" -#include "filesystem/directories.hpp" #include "log/formatters/filepath.hpp" +#include "utils/mkdirs.hpp" #include "utils/read_file.hpp" +#include "utils/write_file.hpp" namespace { namespace fs = kagome::filesystem; @@ -507,7 +507,7 @@ namespace kagome::application { return false; } - if (base_path_.empty() or !fs::createDirectoryRecursive(base_path_)) { + if (not mkdirs(base_path_)) { SL_ERROR(logger_, "Base path {} does not exist, " "please specify a valid path with -d option", @@ -988,13 +988,10 @@ namespace kagome::application { } if (not kagome::filesystem::exists(chain_spec_path_)) { - kagome::filesystem::create_directories( - chain_spec_path_.parent_path()); + mkdirs(chain_spec_path_.parent_path()).value(); - std::ofstream ofs; - ofs.open(chain_spec_path_.native(), std::ios::ate); - ofs << kagome::assets::embedded_chainspec; - ofs.close(); + writeFile(chain_spec_path_, kagome::assets::embedded_chainspec) + .value(); auto chain_spec = ChainSpecImpl::loadFrom(chain_spec_path_.native()); auto path = keystorePath(chain_spec.value()->id()); @@ -1015,12 +1012,10 @@ namespace kagome::application { auto ma_res = chain_spec.value()->bootNodes()[0]; listen_addresses_.emplace_back(ma_res); - kagome::filesystem::create_directories(path); + mkdirs(path).value(); for (auto key_descr : kagome::assets::embedded_keys) { - ofs.open((path / key_descr.first).native(), std::ios::ate); - ofs << key_descr.second; - ofs.close(); + writeFile(path / key_descr.first, key_descr.second).value(); } } @@ -1476,13 +1471,12 @@ namespace kagome::application { } } { - std::error_code ec; - kagome::filesystem::create_directories(runtimeCacheDirPath(), ec); - if (ec) { + auto r = mkdirs(runtimeCacheDirPath()); + if (not r) { SL_ERROR(logger_, "Failed to create runtime cache dir {}: {}", runtimeCacheDirPath(), - ec); + r.error()); return false; } } diff --git a/core/application/modes/precompile_wasm.cpp b/core/application/modes/precompile_wasm.cpp index f1f38bc10e..c2dc7455a2 100644 --- a/core/application/modes/precompile_wasm.cpp +++ b/core/application/modes/precompile_wasm.cpp @@ -8,10 +8,9 @@ #include "blockchain/block_tree.hpp" #include "common/bytestr.hpp" +#include "parachain/pvf/pool.hpp" #include "parachain/pvf/session_params.hpp" -#include "runtime/common/uncompress_code_if_needed.hpp" -#include "runtime/module_factory.hpp" -#include "runtime/wabt/instrument.hpp" +#include "runtime/common/runtime_instances_pool.hpp" #include "utils/read_file.hpp" namespace kagome::application::mode { @@ -19,11 +18,13 @@ namespace kagome::application::mode { const application::AppConfiguration &app_config, std::shared_ptr block_tree, std::shared_ptr parachain_api, - std::shared_ptr module_factory) + std::shared_ptr hasher, + std::shared_ptr module_factory) : log_{log::createLogger("PrecompileWasm")}, config_{*app_config.precompileWasm()}, block_tree_{std::move(block_tree)}, parachain_api_{std::move(parachain_api)}, + hasher_{std::move(hasher)}, module_factory_{std::move(module_factory)} {} int PrecompileWasmMode::run() const { @@ -55,13 +56,11 @@ namespace kagome::application::mode { if (text.starts_with("0x")) { BOOST_OUTCOME_TRY(bytes, common::unhexWith0x(text)); } - Buffer code; - OUTCOME_TRY(runtime::uncompressCodeIfNeeded(bytes, code)); + // https://github.com/paritytech/polkadot-sdk/blob/b4ae5b01da280f754ccc00b94314a30b658182a1/polkadot/parachain/src/primitives.rs#L74-L81 + auto code_hash = hasher_->blake2b_256(bytes); OUTCOME_TRY(config, parachain::sessionParams(*parachain_api_, block.hash)); - BOOST_OUTCOME_TRY( - code, runtime::prepareBlobForCompilation(code, config.memory_limits)); - OUTCOME_TRY(module_factory_->make(code)); + OUTCOME_TRY(module_factory_->precompile(code_hash, bytes, config)); } return outcome::success(); } diff --git a/core/application/modes/precompile_wasm.hpp b/core/application/modes/precompile_wasm.hpp index 6108c4505a..c626b3c2a8 100644 --- a/core/application/modes/precompile_wasm.hpp +++ b/core/application/modes/precompile_wasm.hpp @@ -14,8 +14,15 @@ namespace kagome::blockchain { class BlockTree; } // namespace kagome::blockchain +namespace kagome::crypto { + class Hasher; +} // namespace kagome::crypto + +namespace kagome::parachain { + class PvfPool; +} // namespace kagome::parachain + namespace kagome::runtime { - class ModuleFactory; class ParachainHost; } // namespace kagome::runtime @@ -28,7 +35,8 @@ namespace kagome::application::mode { PrecompileWasmMode(const application::AppConfiguration &app_config, std::shared_ptr block_tree, std::shared_ptr parachain_api, - std::shared_ptr module_factory); + std::shared_ptr hasher, + std::shared_ptr module_factory); int run() const override; @@ -39,6 +47,7 @@ namespace kagome::application::mode { application::PrecompileWasmConfig config_; std::shared_ptr block_tree_; std::shared_ptr parachain_api_; - std::shared_ptr module_factory_; + std::shared_ptr hasher_; + std::shared_ptr module_factory_; }; } // namespace kagome::application::mode diff --git a/core/consensus/timeline/impl/block_executor_impl.cpp b/core/consensus/timeline/impl/block_executor_impl.cpp index 003cb881f4..ff116613fb 100644 --- a/core/consensus/timeline/impl/block_executor_impl.cpp +++ b/core/consensus/timeline/impl/block_executor_impl.cpp @@ -20,6 +20,7 @@ #include "storage/changes_trie/impl/storage_changes_tracker_impl.hpp" #include "transaction_pool/transaction_pool.hpp" #include "transaction_pool/transaction_pool_error.hpp" +#include "utils/pool_handler_ready_make.hpp" namespace kagome::consensus { @@ -43,7 +44,8 @@ namespace kagome::consensus { std::unique_ptr appender) : block_tree_{std::move(block_tree)}, main_pool_handler_{main_thread_pool.handler(app_state_manager)}, - worker_pool_handler_{worker_thread_pool.handler(app_state_manager)}, + worker_pool_handler_{ + poolHandlerReadyMake(app_state_manager, worker_thread_pool)}, core_{std::move(core)}, tx_pool_{std::move(tx_pool)}, hasher_{std::move(hasher)}, diff --git a/core/consensus/timeline/impl/block_executor_impl.hpp b/core/consensus/timeline/impl/block_executor_impl.hpp index 7ea4f9b0c3..1f8df60f3c 100644 --- a/core/consensus/timeline/impl/block_executor_impl.hpp +++ b/core/consensus/timeline/impl/block_executor_impl.hpp @@ -18,6 +18,7 @@ namespace kagome { class PoolHandler; + class PoolHandlerReady; } // namespace kagome namespace kagome::application { @@ -86,7 +87,7 @@ namespace kagome::consensus { private: std::shared_ptr block_tree_; std::shared_ptr main_pool_handler_; - std::shared_ptr worker_pool_handler_; + std::shared_ptr worker_pool_handler_; std::shared_ptr core_; std::shared_ptr tx_pool_; std::shared_ptr hasher_; diff --git a/core/consensus/timeline/impl/timeline_impl.cpp b/core/consensus/timeline/impl/timeline_impl.cpp index daf4fd564c..cbc52a92ca 100644 --- a/core/consensus/timeline/impl/timeline_impl.cpp +++ b/core/consensus/timeline/impl/timeline_impl.cpp @@ -10,6 +10,7 @@ #include "application/app_state_manager.hpp" #include "blockchain/block_tree.hpp" #include "clock/impl/clock_impl.hpp" +#include "common/main_thread_pool.hpp" #include "consensus/consensus_selector.hpp" #include "consensus/grandpa/justification_observer.hpp" #include "consensus/timeline/impl/slot_leadership_error.hpp" @@ -37,6 +38,7 @@ namespace kagome::consensus { const application::AppConfiguration &app_config, std::shared_ptr app_state_manager, const clock::SystemClock &clock, + common::MainThreadPool &main_thread_pool, std::shared_ptr slots_util, std::shared_ptr block_tree, std::shared_ptr consensus_selector, @@ -57,6 +59,7 @@ namespace kagome::consensus { : log_(log::createLogger("Timeline", "timeline")), app_state_manager_(std::move(app_state_manager)), clock_(clock), + main_pool_handler_{main_thread_pool.handlerStarted()}, slots_util_(std::move(slots_util)), block_tree_(std::move(block_tree)), consensus_selector_(std::move(consensus_selector)), @@ -272,7 +275,13 @@ namespace kagome::consensus { if (validator_status == ValidatorStatus::SingleValidator) { SL_INFO(log_, "Starting single validating node."); - onSynchronized(); + main_pool_handler_->execute([weak_self{weak_from_this()}] { + auto self = weak_self.lock(); + if (not self) { + return; + } + self->onSynchronized(); + }); return true; } } diff --git a/core/consensus/timeline/impl/timeline_impl.hpp b/core/consensus/timeline/impl/timeline_impl.hpp index 3eadb29799..e7421d9d09 100644 --- a/core/consensus/timeline/impl/timeline_impl.hpp +++ b/core/consensus/timeline/impl/timeline_impl.hpp @@ -18,6 +18,10 @@ #include "primitives/event_types.hpp" #include "telemetry/service.hpp" +namespace kagome { + class PoolHandler; +} // namespace kagome + namespace kagome::application { class AppStateManager; class AppConfiguration; @@ -27,6 +31,10 @@ namespace kagome::blockchain { class BlockTree; } +namespace kagome::common { + class MainThreadPool; +} // namespace kagome::common + namespace kagome::consensus { class SlotsUtil; class ConsensusSelector; @@ -71,6 +79,7 @@ namespace kagome::consensus { const application::AppConfiguration &app_config, std::shared_ptr app_state_manager, const clock::SystemClock &clock, + common::MainThreadPool &main_thread_pool, std::shared_ptr slots_util, std::shared_ptr block_tree, std::shared_ptr consensus_selector, @@ -138,6 +147,7 @@ namespace kagome::consensus { std::shared_ptr app_state_manager_; const clock::SystemClock &clock_; + std::shared_ptr main_pool_handler_; std::shared_ptr slots_util_; std::shared_ptr block_tree_; std::shared_ptr consensus_selector_; diff --git a/core/crypto/key_store/key_file_storage.cpp b/core/crypto/key_store/key_file_storage.cpp index 23252e166a..996cfad008 100644 --- a/core/crypto/key_store/key_file_storage.cpp +++ b/core/crypto/key_store/key_file_storage.cpp @@ -6,13 +6,13 @@ #include "crypto/key_store/key_file_storage.hpp" -#include - #include "common/hexutil.hpp" #include "crypto/key_store/key_type.hpp" #include "filesystem/common.hpp" #include "utils/json_unquote.hpp" +#include "utils/mkdirs.hpp" #include "utils/read_file.hpp" +#include "utils/write_file.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::crypto, KeyFileStorage::Error, e) { using E = kagome::crypto::KeyFileStorage::Error; @@ -29,10 +29,6 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::crypto, KeyFileStorage::Error, e) { return "specified key file is invalid"; case E::INCONSISTENT_KEYFILE: return "key file is inconsistent, public key != derived public key"; - case E::KEYS_PATH_IS_NOT_DIRECTORY: - return "specified key storage directory path is not a directory"; - case E::FAILED_CREATE_KEYS_DIRECTORY: - return "failed to create key storage directory"; } return "unknown KeyFileStorage error"; } @@ -80,44 +76,13 @@ namespace kagome::crypto { } outcome::result KeyFileStorage::initialize() { - std::error_code ec{}; - bool does_exist = filesystem::exists(keystore_path_, ec); - if (ec and ec != std::errc::no_such_file_or_directory) { - logger_->error("Error initializing key storage: {}", ec); - return outcome::failure(ec); - } - if (does_exist) { - // check whether specified path is a directory - if (not filesystem::is_directory(keystore_path_, ec)) { - return Error::KEYS_PATH_IS_NOT_DIRECTORY; - } - if (ec) { - logger_->error("Error scanning key storage: {}", ec); - return outcome::failure(ec); - } - } else { - // try create directory - if (not filesystem::create_directories(keystore_path_, ec)) { - return Error::FAILED_CREATE_KEYS_DIRECTORY; - } - if (ec) { - logger_->error("Error creating keystore dir: {}", ec); - return outcome::failure(ec); - } - } - + OUTCOME_TRY(mkdirs(keystore_path_)); return outcome::success(); } outcome::result KeyFileStorage::saveKeyHexAtPath( common::BufferView private_key, const KeyFileStorage::Path &path) const { - std::ofstream file; - file.open(path.native(), std::ios::out | std::ios::trunc); - if (!file.is_open()) { - return Error::FAILED_OPEN_FILE; - } - auto hex = common::hex_lower_0x(private_key); - file << hex; + OUTCOME_TRY(writeFile(path, common::hex_lower_0x(private_key))); SL_TRACE(logger_, "Saving key to {}", path.native()); return outcome::success(); } diff --git a/core/crypto/key_store/key_file_storage.hpp b/core/crypto/key_store/key_file_storage.hpp index 3d1c67226a..a159de1684 100644 --- a/core/crypto/key_store/key_file_storage.hpp +++ b/core/crypto/key_store/key_file_storage.hpp @@ -30,8 +30,6 @@ namespace kagome::crypto { FILE_DOESNT_EXIST, INVALID_FILE_FORMAT, INCONSISTENT_KEYFILE, - KEYS_PATH_IS_NOT_DIRECTORY, - FAILED_CREATE_KEYS_DIRECTORY }; using Buffer = common::Buffer; diff --git a/core/filesystem/directories.hpp b/core/filesystem/directories.hpp deleted file mode 100644 index aae677661d..0000000000 --- a/core/filesystem/directories.hpp +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "filesystem/common.hpp" - -namespace kagome::filesystem { - - /** - * Creates directories recursively - * @param path full path or relative path to directory to be created - * @return true if path exists, otherwise false - */ - inline bool createDirectoryRecursive(const path &path) { - return exists(path) || create_directories(path); - } - -} // namespace kagome::filesystem diff --git a/core/host_api/impl/host_api_impl.cpp b/core/host_api/impl/host_api_impl.cpp index f5b8ce882f..3d6ceeeb96 100644 --- a/core/host_api/impl/host_api_impl.cpp +++ b/core/host_api/impl/host_api_impl.cpp @@ -101,6 +101,7 @@ namespace kagome::host_api { misc_ext_{DEFAULT_CHAIN_ID, hasher, memory_provider_, + storage_provider_, std::move(core_provider)}, storage_ext_(storage_provider_, memory_provider_, hasher), child_storage_ext_(storage_provider_, memory_provider_), diff --git a/core/host_api/impl/misc_extension.cpp b/core/host_api/impl/misc_extension.cpp index f37bb2afe0..6b792d0253 100644 --- a/core/host_api/impl/misc_extension.cpp +++ b/core/host_api/impl/misc_extension.cpp @@ -8,12 +8,8 @@ #include "log/trace_macros.hpp" #include "primitives/version.hpp" -#include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/core_api_factory.hpp" #include "runtime/memory_provider.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" @@ -23,13 +19,13 @@ namespace kagome::host_api { uint64_t chain_id, std::shared_ptr hasher, std::shared_ptr memory_provider, + std::shared_ptr storage_provider, std::shared_ptr core_factory) : hasher_{std::move(hasher)}, memory_provider_{std::move(memory_provider)}, core_factory_{std::move(core_factory)}, logger_{log::createLogger("MiscExtension", "misc_extension")} { BOOST_ASSERT(hasher_); - BOOST_ASSERT(core_factory_); BOOST_ASSERT(memory_provider_); } @@ -39,20 +35,9 @@ namespace kagome::host_api { auto &memory = memory_provider_->getCurrentMemory()->get(); auto code = memory.loadN(ptr, len); - common::Buffer uncompressed_code; - auto uncompress_res = - runtime::uncompressCodeIfNeeded(code, uncompressed_code); - static const auto kErrorRes = scale::encode>(std::nullopt).value(); - - if (uncompress_res.has_error()) { - SL_ERROR(logger_, "Error decompressing code: {}", uncompress_res.error()); - return memory.storeBuffer(kErrorRes); - } - - auto core_api = - core_factory_->make(hasher_, uncompressed_code.asVector()).value(); + auto core_api = core_factory_->make(code, storage_provider_).value(); auto version_res = core_api->version(); SL_TRACE_FUNC_CALL(logger_, version_res.has_value(), data); diff --git a/core/host_api/impl/misc_extension.hpp b/core/host_api/impl/misc_extension.hpp index 94ec3780b2..fca954e5ab 100644 --- a/core/host_api/impl/misc_extension.hpp +++ b/core/host_api/impl/misc_extension.hpp @@ -18,6 +18,7 @@ namespace kagome::runtime { class CoreApiFactory; class MemoryProvider; + class TrieStorageProvider; } // namespace kagome::runtime namespace kagome::crypto { @@ -35,6 +36,7 @@ namespace kagome::host_api { uint64_t chain_id, std::shared_ptr hasher, std::shared_ptr memory_provider, + std::shared_ptr storage_provider, std::shared_ptr core_provider); ~MiscExtension() = default; @@ -51,6 +53,7 @@ namespace kagome::host_api { private: std::shared_ptr hasher_; std::shared_ptr memory_provider_; + std::shared_ptr storage_provider_; std::shared_ptr core_factory_; log::Logger logger_; }; diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 2b190a84e1..e623ddf033 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -202,7 +202,6 @@ #include "runtime/wavm/intrinsics/intrinsic_module_instance.hpp" #include "runtime/wavm/intrinsics/intrinsic_resolver_impl.hpp" #include "runtime/wavm/module.hpp" -#include "runtime/wavm/module_cache.hpp" #include "runtime/wavm/module_factory_impl.hpp" #endif @@ -390,26 +389,6 @@ namespace { .template create>(); return module->instantiate(); }), - bind_by_lambda([](const auto - &injector) { - std::optional> - module_cache_opt; - auto &app_config = - injector.template create(); - 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>(), - module_cache_opt, - injector.template create>()); - }), di::bind.template to(), #endif std::forward(args)...); @@ -605,7 +584,6 @@ namespace { Compile ? runtime::wasm_edge::ModuleFactoryImpl::ExecType::Compiled : runtime::wasm_edge::ModuleFactoryImpl::ExecType::Interpreted, - config->runtimeCacheDirPath(), }; #endif @@ -727,12 +705,13 @@ namespace { return get_rocks_db(config, chain_spec); }), bind_by_lambda([](const auto &injector) { - auto module_factory = injector.template create>(); auto root_res = injector::calculate_genesis_state( injector .template create(), - *module_factory, + injector + .template create(), + injector.template create(), injector .template create(), injector.template create< diff --git a/core/injector/calculate_genesis_state.hpp b/core/injector/calculate_genesis_state.hpp index e2e8ca9515..91c190b262 100644 --- a/core/injector/calculate_genesis_state.hpp +++ b/core/injector/calculate_genesis_state.hpp @@ -10,6 +10,7 @@ #include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/heap_alloc_strategy_heappages.hpp" #include "runtime/runtime_api/impl/core.hpp" +#include "runtime/runtime_instances_pool.hpp" #include "runtime/wabt/version.hpp" #include "storage/predefined_keys.hpp" #include "storage/trie/polkadot_trie/polkadot_trie_impl.hpp" @@ -20,7 +21,8 @@ namespace kagome::injector { inline outcome::result calculate_genesis_state( const application::ChainSpec &chain_spec, - const runtime::ModuleFactory &module_factory, + const crypto::Hasher &hasher, + runtime::RuntimeInstancesPool &module_factory, storage::trie::TrieSerializer &trie_serializer, std::shared_ptr runtime_cache) { auto trie_from = [](const application::GenesisRawData &kv) { @@ -32,15 +34,29 @@ namespace kagome::injector { }; auto top_trie = trie_from(chain_spec.getGenesisTopSection()); OUTCOME_TRY(code, top_trie->get(storage::kRuntimeCodeKey)); + + auto code_hash = hasher.blake2b_256(code); BOOST_OUTCOME_TRY(code, runtime::uncompressCodeIfNeeded(code)); OUTCOME_TRY(runtime_version, runtime::readEmbeddedVersion(code)); if (not runtime_version) { runtime::MemoryLimits config; BOOST_OUTCOME_TRY(config.heap_alloc_strategy, heapAllocStrategyHeappagesDefault(*top_trie)); - BOOST_OUTCOME_TRY(runtime_version, - runtime::callCoreVersion( - module_factory, code, config, runtime_cache)); + OUTCOME_TRY(instance, + module_factory.instantiateFromCode( + code_hash, + [&] { return std::make_shared(code); }, + {config})); + OUTCOME_TRY(ctx, runtime::RuntimeContextFactory::stateless(instance)); + BOOST_OUTCOME_TRY( + runtime_version, + runtime_cache->getVersion( + ctx.module_instance->getCodeHash(), + [&]() -> outcome::result { + return ctx.module_instance + ->callAndDecodeExportFunction( + ctx, "Core_version"); + })); } auto version = storage::trie::StateVersion{runtime_version->state_version}; std::vector> child_tries; diff --git a/core/injector/get_peer_keypair.hpp b/core/injector/get_peer_keypair.hpp index 4cb79e638d..7b300f0cb4 100644 --- a/core/injector/get_peer_keypair.hpp +++ b/core/injector/get_peer_keypair.hpp @@ -6,15 +6,13 @@ #pragma once -#include - #include "application/app_configuration.hpp" #include "application/chain_spec.hpp" -#include "common/bytestr.hpp" #include "common/outcome_throw.hpp" #include "crypto/ed25519_provider.hpp" #include "crypto/key_store.hpp" #include "crypto/random_generator.hpp" +#include "utils/write_file.hpp" namespace kagome::injector { inline std::shared_ptr get_peer_keypair( @@ -80,8 +78,7 @@ namespace kagome::injector { auto generated_keypair = crypto_provider.generateKeypair(seed, {}).value(); auto save = app_config.shouldSaveNodeKey(); if (save) { - std::ofstream file{path.c_str()}; - file.write(byte2str(seed.unsafeBytes()).data(), seed.size()); + writeFile(path, seed.unsafeBytes()).value(); } auto key_pair = std::make_shared( diff --git a/core/log/formatters/filepath.hpp b/core/log/formatters/filepath.hpp index 48e39b0a13..d03c6355e2 100644 --- a/core/log/formatters/filepath.hpp +++ b/core/log/formatters/filepath.hpp @@ -10,28 +10,9 @@ #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::filesystem::path &path, FormatContext &ctx) const - -> decltype(ctx.out()) { - // ctx.out() is an output iterator to write to. - - auto native_path(path.native()); - return std::copy(std::begin(native_path), std::end(native_path), ctx.out()); +struct fmt::formatter + : fmt::formatter { + auto format(const std::filesystem::path &path, format_context &ctx) { + return fmt::formatter::format(path.native(), ctx); } }; diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 888395358d..aea4e6468f 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -2067,7 +2067,7 @@ namespace kagome::parachain { const approval::IndirectAssignmentCert &indirect_cert, CandidateIndex candidate_index, std::unordered_set &&peers) { - REINVOKE(*approval_thread_handler_, + REINVOKE(*main_pool_handler_, runDistributeAssignment, indirect_cert, candidate_index, @@ -2187,10 +2187,8 @@ namespace kagome::parachain { void ApprovalDistribution::runDistributeApproval( const network::IndirectSignedApprovalVote &vote, std::unordered_set &&peers) { - REINVOKE(*approval_thread_handler_, - runDistributeApproval, - vote, - std::move(peers)); + REINVOKE( + *main_pool_handler_, runDistributeApproval, vote, std::move(peers)); logger_->info( "Sending an approval to peers. (block={}, index={}, num peers={})", diff --git a/core/parachain/pvf/kagome_pvf_worker.cpp b/core/parachain/pvf/kagome_pvf_worker.cpp index 24a1a2c5f7..1278fb8958 100644 --- a/core/parachain/pvf/kagome_pvf_worker.cpp +++ b/core/parachain/pvf/kagome_pvf_worker.cpp @@ -43,6 +43,7 @@ #include "runtime/binaryen/module/module_factory_impl.hpp" #include "runtime/module_instance.hpp" #include "runtime/runtime_context.hpp" +#include "utils/mkdirs.hpp" // rust reference: polkadot-sdk/polkadot/node/core/pvf/execute-worker/src/lib.rs @@ -73,14 +74,7 @@ namespace kagome::parachain { // This should not be called in a multi-threaded context. `unshare(2)`: // "CLONE_NEWUSER requires that the calling process is not threaded." SecureModeOutcome changeRoot(const std::filesystem::path &worker_dir) { - std::error_code ec; - std::filesystem::create_directories(worker_dir, ec); - if (ec) { - return SecureModeError{ - fmt::format("Failed to create worker directory {}: {}", - worker_dir.c_str(), - ec.message())}; - } + OUTCOME_TRY(mkdirs(worker_dir)); EXPECT_NON_NEG(unshare, CLONE_NEWUSER | CLONE_NEWNS); EXPECT_NON_NEG(mount, nullptr, "/", nullptr, MS_REC | MS_PRIVATE, nullptr); @@ -274,21 +268,23 @@ namespace kagome::parachain { OUTCOME_TRY(input, decodeInput()); kagome::log::tuneLoggingSystem(input.log_params); - SL_VERBOSE(logger, "Cache directory: {}", input.cache_dir); + SL_VERBOSE(logger, "Compiled path: {}", input.path_compiled); #ifdef __linux__ if (!input.force_disable_secure_mode) { + std::filesystem::path path_compiled{input.path_compiled}; SL_VERBOSE(logger, "Attempting to enable secure validator mode..."); - if (auto res = changeRoot(input.cache_dir); !res) { + if (auto res = changeRoot(path_compiled.parent_path()); !res) { SL_ERROR(logger, "Failed to enable secure validator mode (change root): {}", res.error()); return std::errc::not_supported; } - input.cache_dir = "/"; + path_compiled = "/" / path_compiled.filename(); + input.path_compiled = path_compiled.native(); - if (auto res = enableLandlock(input.cache_dir); !res) { + if (auto res = enableLandlock(path_compiled.parent_path()); !res) { SL_ERROR(logger, "Failed to enable secure validator mode (landlock): {}", res.error()); @@ -308,9 +304,9 @@ namespace kagome::parachain { #endif auto injector = pvf_worker_injector(input); OUTCOME_TRY(factory, createModuleFactory(injector, input.engine)); - OUTCOME_TRY(ctx, - runtime::RuntimeContextFactory::fromCode( - *factory, input.runtime_code, input.runtime_params)); + OUTCOME_TRY(module, factory->loadCompiled(input.path_compiled)); + OUTCOME_TRY(instance, module->instantiate()); + OUTCOME_TRY(ctx, runtime::RuntimeContextFactory::stateless(instance)); OUTCOME_TRY(result, ctx.module_instance->callExportFunction( ctx, input.function, input.params)); diff --git a/core/parachain/pvf/kagome_pvf_worker_injector.hpp b/core/parachain/pvf/kagome_pvf_worker_injector.hpp index d538b2f1f6..edefb3fb5e 100644 --- a/core/parachain/pvf/kagome_pvf_worker_injector.hpp +++ b/core/parachain/pvf/kagome_pvf_worker_injector.hpp @@ -21,6 +21,7 @@ #include "parachain/pvf/pvf_worker_types.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/runtime_properties_cache_impl.hpp" #include "runtime/memory_provider.hpp" #include "runtime/module.hpp" @@ -32,7 +33,6 @@ #include "runtime/wavm/instance_environment_factory.hpp" #include "runtime/wavm/intrinsics/intrinsic_functions.hpp" #include "runtime/wavm/intrinsics/intrinsic_module.hpp" -#include "runtime/wavm/module_cache.hpp" #include "runtime/wavm/module_factory_impl.hpp" #include "runtime/wavm/module_params.hpp" #endif @@ -86,8 +86,10 @@ namespace kagome::parachain { bind_null(), bind_null(), bind_null(), + di::bind.to(), di::bind.to(), di::bind.to(), + bind_null(), bind_null() #if KAGOME_WASM_COMPILER_WAVM == 1 @@ -106,40 +108,18 @@ namespace kagome::parachain { runtime::wavm::registerHostApiMethods(*module); return module; }), - - bind_by_lambda([cache_dir = - input.cache_dir]( - const auto - &injector) { - kagome::filesystem::path path_cache_dir(cache_dir); - auto module_cache = std::make_shared( - injector.template create>(), path_cache_dir); - return std::make_shared( - injector - .template create>(), - injector.template create>(), - injector.template create>(), - injector.template create>(), - injector.template create>(), - injector.template create>(), - module_cache, - injector.template create>()); - }), - bind_by_lambda([](const auto &injector) { - return injector - .template create>(); - }) + di::bind() + .to() #endif #if KAGOME_WASM_COMPILER_WASM_EDGE == 1 , bind_by_lambda( - [engine = input.engine, &input](const auto &injector) { + [engine = input.engine](const auto &injector) { using E = runtime::wasm_edge::ModuleFactoryImpl::ExecType; runtime::wasm_edge::ModuleFactoryImpl::Config config{ engine == RuntimeEngine::kWasmEdgeCompiled ? E::Compiled : E::Interpreted, - input.cache_dir, }; return std::make_shared(config); }), diff --git a/core/parachain/pvf/module_precompiler.cpp b/core/parachain/pvf/module_precompiler.cpp index 4a9fcd871c..83b2ebfd7e 100644 --- a/core/parachain/pvf/module_precompiler.cpp +++ b/core/parachain/pvf/module_precompiler.cpp @@ -180,7 +180,7 @@ namespace kagome::parachain { hash); stats.total_code_size += code.size(); - OUTCOME_TRY(pvf_pool_->pool()->precompile(hash, code, executor_params)); + OUTCOME_TRY(pvf_pool_->precompile(hash, code, executor_params)); SL_DEBUG(log_, "Instantiated runtime instance with code hash {} for parachain " "{}, {} left", diff --git a/core/parachain/pvf/pool.cpp b/core/parachain/pvf/pool.cpp index 17e23d17b8..81f40243e1 100644 --- a/core/parachain/pvf/pool.cpp +++ b/core/parachain/pvf/pool.cpp @@ -9,6 +9,7 @@ #include "application/app_configuration.hpp" #include "metrics/histogram_timer.hpp" #include "runtime/common/runtime_instances_pool.hpp" +#include "runtime/common/uncompress_code_if_needed.hpp" namespace kagome::parachain { inline auto &metric_pvf_preparation_time() { @@ -34,24 +35,36 @@ namespace kagome::parachain { return metric; } - struct PvfPoolWrapper : runtime::ModuleFactory { - PvfPoolWrapper(std::shared_ptr inner) - : inner_{std::move(inner)} {} - - runtime::CompilationOutcome> make( - common::BufferView code) const override { - auto timer = metric_pvf_preparation_time().timer(); - return inner_->make(code); - } - - std::shared_ptr inner_; + metrics::HistogramHelper metric_code_size{ + "kagome_parachain_candidate_validation_code_size", + "The size of the decompressed WASM validation blob used for checking a " + "candidate", + metrics::exponentialBuckets(16384, 2, 10), }; PvfPool::PvfPool(const application::AppConfiguration &app_config, std::shared_ptr module_factory, std::shared_ptr instrument) : pool_{std::make_shared( - std::make_shared(std::move(module_factory)), + app_config, + std::move(module_factory), std::move(instrument), app_config.parachainRuntimeInstanceCacheSize())} {} + + outcome::result PvfPool::precompile( + const Hash256 &code_hash, + BufferView code_zstd, + const runtime::RuntimeContext::ContextParams &config) const { + auto make_timer = [] { return metric_pvf_preparation_time().timer(); }; + decltype(make_timer()) timer; + return pool_->precompile( + code_hash, + [&]() mutable -> runtime::RuntimeCodeProvider::Result { + timer.emplace(make_timer().value()); + OUTCOME_TRY(code, runtime::uncompressCodeIfNeeded(code_zstd)); + metric_code_size.observe(code.size()); + return std::make_shared(code); + }, + config); + } } // namespace kagome::parachain diff --git a/core/parachain/pvf/pool.hpp b/core/parachain/pvf/pool.hpp index 517e772175..f5b0e84090 100644 --- a/core/parachain/pvf/pool.hpp +++ b/core/parachain/pvf/pool.hpp @@ -8,6 +8,8 @@ #include +#include "runtime/runtime_context.hpp" + namespace kagome::application { class AppConfiguration; } // namespace kagome::application @@ -32,6 +34,15 @@ namespace kagome::parachain { return pool_; } + /** + * Measures `kagome_parachain_candidate_validation_code_size` and + * `kagome_pvf_preparation_time` metrics. + */ + outcome::result precompile( + const Hash256 &code_hash, + BufferView code_zstd, + const runtime::RuntimeContext::ContextParams &config) const; + private: std::shared_ptr pool_; }; diff --git a/core/parachain/pvf/precheck.cpp b/core/parachain/pvf/precheck.cpp index cf96a26d8e..e1f26683c1 100644 --- a/core/parachain/pvf/precheck.cpp +++ b/core/parachain/pvf/precheck.cpp @@ -100,8 +100,7 @@ namespace kagome::parachain { auto &code_zstd = *code_zstd_res.value(); auto res = [&]() -> outcome::result { OUTCOME_TRY(config, sessionParams(*parachain_api_, block.hash)); - OUTCOME_TRY( - pvf_pool_->pool()->precompile(code_hash, code_zstd, config)); + OUTCOME_TRY(pvf_pool_->precompile(code_hash, code_zstd, config)); return outcome::success(); }(); if (res) { diff --git a/core/parachain/pvf/pvf_impl.cpp b/core/parachain/pvf/pvf_impl.cpp index 9b15e226be..808f3bc73c 100644 --- a/core/parachain/pvf/pvf_impl.cpp +++ b/core/parachain/pvf/pvf_impl.cpp @@ -100,13 +100,6 @@ namespace kagome::parachain { }, }; - metrics::HistogramHelper metric_code_size{ - "kagome_parachain_candidate_validation_code_size", - "The size of the decompressed WASM validation blob used for checking a " - "candidate", - metrics::exponentialBuckets(16384, 2, 10), - }; - RuntimeEngine pvf_runtime_engine( const application::AppConfiguration &app_conf) { bool interpreted = @@ -245,9 +238,6 @@ namespace kagome::parachain { } auto timer = metric_pvf_execution_time.timer(); - ParachainRuntime code; - CB_TRYV(runtime::uncompressCodeIfNeeded(code_zstd, code)); - metric_code_size.observe(code.size()); ValidationParams params; params.parent_head = data.parent_head; CB_TRYV(runtime::uncompressCodeIfNeeded(pov.payload, @@ -256,7 +246,7 @@ namespace kagome::parachain { params.relay_parent_storage_root = data.relay_parent_storage_root; callWasm(receipt, code_hash, - code, + code_zstd, params, libp2p::SharedFn{[weak_self{weak_from_this()}, data, @@ -329,25 +319,21 @@ namespace kagome::parachain { sessionParams(*parachain_api_, receipt.descriptor.relay_parent)); constexpr auto name = "validate_block"; + CB_TRYV(pvf_pool_->precompile(code_hash, code_zstd, executor_params)); if (not app_configuration_->usePvfSubprocess()) { - CB_TRY(auto instance, - pvf_pool_->pool()->instantiateFromCode( - code_hash, code_zstd, executor_params)); - CB_TRY(auto ctx, - ctx_factory_->ephemeral( - instance, storage::trie::kEmptyRootHash, executor_params)); + CB_TRY( + auto instance, + pvf_pool_->pool()->instantiateFromCode( + code_hash, [&] { return PvfError::NO_CODE; }, executor_params)); + CB_TRY(auto ctx, ctx_factory_->stateless(instance)); return cb(executor_->call(ctx, name, params)); } - CB_TRYV( - pvf_pool_->pool()->precompile(code_hash, code_zstd, executor_params)); PvfWorkerInput input{ pvf_runtime_engine(*app_configuration_), - code_zstd, + pvf_pool_->pool()->cachePath(code_hash, executor_params), name, common::Buffer{scale::encode(params).value()}, - executor_params, - app_configuration_->runtimeCacheDirPath(), app_configuration_->log(), app_configuration_->disableSecureMode(), }; diff --git a/core/parachain/pvf/pvf_worker_types.hpp b/core/parachain/pvf/pvf_worker_types.hpp index 7ff1de23d2..e549f2458a 100644 --- a/core/parachain/pvf/pvf_worker_types.hpp +++ b/core/parachain/pvf/pvf_worker_types.hpp @@ -23,14 +23,12 @@ namespace kagome::parachain { }; struct PvfWorkerInput { - SCALE_TIE(8); + SCALE_TIE(6); RuntimeEngine engine; - common::Buffer runtime_code; + std::string path_compiled; std::string function; common::Buffer params; - runtime::RuntimeContextFactory::ContextParams runtime_params; - std::string cache_dir; std::vector log_params; bool force_disable_secure_mode; }; diff --git a/core/parachain/pvf/secure_mode.hpp b/core/parachain/pvf/secure_mode.hpp index 8eaf2122a1..617370360f 100644 --- a/core/parachain/pvf/secure_mode.hpp +++ b/core/parachain/pvf/secure_mode.hpp @@ -12,6 +12,10 @@ namespace kagome::parachain { struct SecureModeError { + SecureModeError(std::string message) : message_{std::move(message)} {} + + SecureModeError(const std::error_code &ec) : message_{ec.message()} {} + std::string_view message() const { return message_; } diff --git a/core/parachain/pvf/secure_mode_precheck.cpp b/core/parachain/pvf/secure_mode_precheck.cpp index a44917ba04..841cd3fbca 100644 --- a/core/parachain/pvf/secure_mode_precheck.cpp +++ b/core/parachain/pvf/secure_mode_precheck.cpp @@ -14,11 +14,13 @@ #include #include #include +#include #include #include #include #include +#include "common/buffer.hpp" #include "common/buffer_view.hpp" #include "log/configurator.hpp" #include "log/logger.hpp" @@ -78,20 +80,15 @@ namespace kagome::parachain { exePath().c_str(), {"check-secure-mode", cache_dir.c_str()}, process_v2::process_stdio{{}, pipe, {}}}; - std::vector output; - + Buffer output; boost::system::error_code ec; - boost::asio::read(pipe, boost::asio::dynamic_buffer(output), ec); + boost::asio::read(pipe, libp2p::asioBuffer(output), ec); if (process.wait() != 0) { return SecureModeError{"Secure mode check failed"}; } - auto res = scale::decode( - common::BufferView{qtils::str2byte(output.data()), output.size()}); - if (!res) { - return SecureModeError{res.error().message()}; - } - return res.value(); + OUTCOME_TRY(res, scale::decode(output)); + return res; } int secureModeCheckMain(int argc, const char **argv) { diff --git a/core/runtime/binaryen/binaryen_memory_factory.cpp b/core/runtime/binaryen/binaryen_memory_factory.cpp index 5804f8e007..a8060b11f7 100644 --- a/core/runtime/binaryen/binaryen_memory_factory.cpp +++ b/core/runtime/binaryen/binaryen_memory_factory.cpp @@ -11,9 +11,8 @@ namespace kagome::runtime::binaryen { std::unique_ptr BinaryenMemoryFactory::make( - RuntimeExternalInterface::InternalMemory *memory, - const MemoryConfig &config) const { - return std::make_unique(memory, config); + RuntimeExternalInterface::InternalMemory *memory) const { + return std::make_unique(memory); } } // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/binaryen_memory_factory.hpp b/core/runtime/binaryen/binaryen_memory_factory.hpp index 6139a3af02..13dd9d8d88 100644 --- a/core/runtime/binaryen/binaryen_memory_factory.hpp +++ b/core/runtime/binaryen/binaryen_memory_factory.hpp @@ -16,8 +16,7 @@ namespace kagome::runtime::binaryen { virtual ~BinaryenMemoryFactory() = default; virtual std::unique_ptr make( - RuntimeExternalInterface::InternalMemory *memory, - const MemoryConfig &config) const; + RuntimeExternalInterface::InternalMemory *memory) const; }; } // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/binaryen_memory_provider.cpp b/core/runtime/binaryen/binaryen_memory_provider.cpp index 6ce63ae064..9087ef255b 100644 --- a/core/runtime/binaryen/binaryen_memory_provider.cpp +++ b/core/runtime/binaryen/binaryen_memory_provider.cpp @@ -41,7 +41,7 @@ namespace kagome::runtime::binaryen { auto rei = external_interface_.lock(); BOOST_ASSERT(rei != nullptr); if (rei) { - std::shared_ptr handle = memory_factory_->make(rei->getMemory(), config); + std::shared_ptr handle = memory_factory_->make(rei->getMemory()); memory_ = std::make_shared( handle, std::make_unique(handle, config)); return outcome::success(); diff --git a/core/runtime/binaryen/instance_environment_factory.cpp b/core/runtime/binaryen/instance_environment_factory.cpp index 23bd7d4bf3..cb3d84cf1e 100644 --- a/core/runtime/binaryen/instance_environment_factory.cpp +++ b/core/runtime/binaryen/instance_environment_factory.cpp @@ -8,7 +8,6 @@ #include "host_api/host_api_factory.hpp" #include "runtime/binaryen/binaryen_memory_provider.hpp" -#include "runtime/common/core_api_factory_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" namespace kagome::runtime::binaryen { @@ -16,23 +15,23 @@ namespace kagome::runtime::binaryen { InstanceEnvironmentFactory::InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, + std::shared_ptr core_factory, std::shared_ptr host_api_factory) : storage_{std::move(storage)}, serializer_{std::move(serializer)}, + core_factory_{std::move(core_factory)}, host_api_factory_{std::move(host_api_factory)} { BOOST_ASSERT(host_api_factory_); } - BinaryenInstanceEnvironment InstanceEnvironmentFactory::make( - std::shared_ptr module_factory) const { + BinaryenInstanceEnvironment InstanceEnvironmentFactory::make() 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(module_factory); auto host_api = std::shared_ptr(host_api_factory_->make( - core_factory, new_memory_provider, new_storage_provider)); + core_factory_, new_memory_provider, new_storage_provider)); auto rei = std::make_shared(host_api); new_memory_provider->setExternalInterface(rei); return BinaryenInstanceEnvironment{ diff --git a/core/runtime/binaryen/instance_environment_factory.hpp b/core/runtime/binaryen/instance_environment_factory.hpp index e9c9ef527a..2d81c39280 100644 --- a/core/runtime/binaryen/instance_environment_factory.hpp +++ b/core/runtime/binaryen/instance_environment_factory.hpp @@ -18,7 +18,7 @@ namespace kagome::host_api { } namespace kagome::runtime { - class ModuleFactory; + class CoreApiFactory; } namespace kagome::runtime::binaryen { @@ -30,20 +30,20 @@ namespace kagome::runtime::binaryen { std::shared_ptr rei; }; - class InstanceEnvironmentFactory final - : public std::enable_shared_from_this { + class InstanceEnvironmentFactory final { public: InstanceEnvironmentFactory( std::shared_ptr storage, std::shared_ptr serializer, + std::shared_ptr core_factory, std::shared_ptr host_api_factory); - [[nodiscard]] BinaryenInstanceEnvironment make( - std::shared_ptr module_factory) const; + [[nodiscard]] BinaryenInstanceEnvironment make() const; private: std::shared_ptr storage_; std::shared_ptr serializer_; + std::shared_ptr core_factory_; std::shared_ptr host_api_factory_; }; diff --git a/core/runtime/binaryen/memory_impl.cpp b/core/runtime/binaryen/memory_impl.cpp index 2f9091c811..75383f424c 100644 --- a/core/runtime/binaryen/memory_impl.cpp +++ b/core/runtime/binaryen/memory_impl.cpp @@ -13,8 +13,7 @@ namespace kagome::runtime::binaryen { - MemoryImpl::MemoryImpl(RuntimeExternalInterface::InternalMemory *memory, - const MemoryConfig &config) + MemoryImpl::MemoryImpl(RuntimeExternalInterface::InternalMemory *memory) : memory_{memory}, logger_{log::createLogger("Binaryen Memory", "binaryen")} {} diff --git a/core/runtime/binaryen/memory_impl.hpp b/core/runtime/binaryen/memory_impl.hpp index f94c32eb7a..795cc36554 100644 --- a/core/runtime/binaryen/memory_impl.hpp +++ b/core/runtime/binaryen/memory_impl.hpp @@ -38,8 +38,7 @@ namespace kagome::runtime::binaryen { */ class MemoryImpl final : public MemoryHandle { public: - MemoryImpl(RuntimeExternalInterface::InternalMemory *memory, - const MemoryConfig &config); + MemoryImpl(RuntimeExternalInterface::InternalMemory *memory); MemoryImpl(const MemoryImpl ©) = delete; MemoryImpl &operator=(const MemoryImpl ©) = delete; MemoryImpl(MemoryImpl &&move) = delete; diff --git a/core/runtime/binaryen/module/module_factory_impl.cpp b/core/runtime/binaryen/module/module_factory_impl.cpp index a2656ead59..78bb129da9 100644 --- a/core/runtime/binaryen/module/module_factory_impl.cpp +++ b/core/runtime/binaryen/module/module_factory_impl.cpp @@ -14,6 +14,8 @@ #include "runtime/binaryen/module/module_impl.hpp" #include "runtime/common/core_api_factory_impl.hpp" #include "runtime/common/trie_storage_provider_impl.hpp" +#include "utils/read_file.hpp" +#include "utils/write_file.hpp" namespace kagome::runtime::binaryen { @@ -25,15 +27,25 @@ namespace kagome::runtime::binaryen { storage_{std::move(storage)}, hasher_(std::move(hasher)) {} - CompilationOutcome> ModuleFactoryImpl::make( - common::BufferView code) const { - std::vector code_vec{code.begin(), code.end()}; + std::optional ModuleFactoryImpl::compilerType() const { + return std::nullopt; + } + + CompilationOutcome ModuleFactoryImpl::compile( + std::filesystem::path path_compiled, BufferView code) const { + OUTCOME_TRY(writeFileTmp(path_compiled, code)); + return outcome::success(); + } + + CompilationOutcome> ModuleFactoryImpl::loadCompiled( + std::filesystem::path path_compiled) const { + Buffer code; + if (not readFile(code, path_compiled)) { + return CompilationError{"read file failed"}; + } OUTCOME_TRY(module, - ModuleImpl::createFromCode(code_vec, - env_factory_, - shared_from_this(), - hasher_->sha2_256(code))); + ModuleImpl::createFromCode( + code, env_factory_, hasher_->blake2b_256(code))); return module; } - } // namespace kagome::runtime::binaryen diff --git a/core/runtime/binaryen/module/module_factory_impl.hpp b/core/runtime/binaryen/module/module_factory_impl.hpp index d270a0b1e4..47c81bbda0 100644 --- a/core/runtime/binaryen/module/module_factory_impl.hpp +++ b/core/runtime/binaryen/module/module_factory_impl.hpp @@ -32,16 +32,18 @@ namespace kagome::runtime::binaryen { class InstanceEnvironmentFactory; - class ModuleFactoryImpl final - : public ModuleFactory, - public std::enable_shared_from_this { + class ModuleFactoryImpl final : public ModuleFactory { public: ModuleFactoryImpl(std::shared_ptr env_factory, std::shared_ptr storage, std::shared_ptr hasher); - CompilationOutcome> make( - common::BufferView code) const override; + // ModuleFactory + std::optional compilerType() const override; + CompilationOutcome compile(std::filesystem::path path_compiled, + BufferView code) const override; + CompilationOutcome> loadCompiled( + std::filesystem::path path_compiled) const override; private: std::shared_ptr env_factory_; diff --git a/core/runtime/binaryen/module/module_impl.cpp b/core/runtime/binaryen/module/module_impl.cpp index 95843a9a31..edef612d83 100644 --- a/core/runtime/binaryen/module/module_impl.cpp +++ b/core/runtime/binaryen/module/module_impl.cpp @@ -14,7 +14,6 @@ #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" @@ -36,22 +35,18 @@ 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) - : module_factory_{std::move(module_factory)}, - env_factory_{std::move(env_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); } CompilationOutcome> ModuleImpl::createFromCode( - const std::vector &code, + const Buffer &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 @@ -82,12 +77,12 @@ namespace kagome::runtime::binaryen { } return std::make_shared( - std::move(module), module_factory, env_factory, code_hash); + std::move(module), env_factory, code_hash); } outcome::result> ModuleImpl::instantiate() const { - auto env = env_factory_->make(module_factory_); + auto env = env_factory_->make(); 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 08d36afadc..57eb04e526 100644 --- a/core/runtime/binaryen/module/module_impl.hpp +++ b/core/runtime/binaryen/module/module_impl.hpp @@ -13,15 +13,6 @@ #include "runtime/module_factory.hpp" #include "runtime/trie_storage_provider.hpp" -namespace wasm { - using namespace ::wasm; // NOLINT(google-build-using-namespace) - class Module; -} // namespace wasm - -namespace kagome::runtime { - class ModuleFactory; -}; - namespace kagome::runtime::binaryen { class InstanceEnvironmentFactory; @@ -44,21 +35,18 @@ namespace kagome::runtime::binaryen { ~ModuleImpl() override = default; static CompilationOutcome> createFromCode( - const std::vector &code, + const Buffer &code, std::shared_ptr env_factory, - std::shared_ptr module_factory, const common::Hash256 &code_hash); 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/runtime_external_interface.cpp b/core/runtime/binaryen/runtime_external_interface.cpp index 31b4357ac3..24059eef06 100644 --- a/core/runtime/binaryen/runtime_external_interface.cpp +++ b/core/runtime/binaryen/runtime_external_interface.cpp @@ -209,9 +209,9 @@ namespace kagome::runtime::binaryen { > wasm.memory.initial * wasm::Memory::kPageSize) { trap("invalid offset when initializing memory"); } - for (size_t i = 0; i != segment.data.size(); ++i) { - memory.set(offset + i, segment.data[i]); - } + memcpy(memory.view(offset, segment.data.size()).data(), + segment.data.data(), + segment.data.size()); } table.resize(wasm.table.initial); diff --git a/core/runtime/common/core_api_factory_impl.cpp b/core/runtime/common/core_api_factory_impl.cpp index b049781a33..fc8ff41f15 100644 --- a/core/runtime/common/core_api_factory_impl.cpp +++ b/core/runtime/common/core_api_factory_impl.cpp @@ -6,12 +6,12 @@ #include "core_api_factory_impl.hpp" -#include "runtime/common/runtime_properties_cache_impl.hpp" -#include "runtime/common/trie_storage_provider_impl.hpp" #include "runtime/common/uncompress_code_if_needed.hpp" -#include "runtime/module_repository.hpp" +#include "runtime/heap_alloc_strategy_heappages.hpp" #include "runtime/runtime_api/impl/core.hpp" #include "runtime/runtime_context.hpp" +#include "runtime/runtime_instances_pool.hpp" +#include "runtime/trie_storage_provider.hpp" #include "runtime/wabt/version.hpp" namespace kagome::runtime { @@ -29,22 +29,33 @@ namespace kagome::runtime { }; CoreApiFactoryImpl::CoreApiFactoryImpl( - std::shared_ptr module_factory) - : module_factory_{module_factory} { - BOOST_ASSERT(module_factory_); - } + std::shared_ptr hasher, + LazySPtr module_factory) + : hasher_{std::move(hasher)}, + module_factory_{std::move(module_factory)} {} outcome::result> CoreApiFactoryImpl::make( - std::shared_ptr hasher, - const std::vector &runtime_code) const { - OUTCOME_TRY(code, uncompressCodeIfNeeded(runtime_code)); + BufferView code_zstd, + std::shared_ptr storage_provider) const { + auto code_hash = hasher_->blake2b_256(code_zstd); + OUTCOME_TRY(code, uncompressCodeIfNeeded(code_zstd)); OUTCOME_TRY(version, readEmbeddedVersion(code)); if (version) { return std::make_unique(*version); } - OUTCOME_TRY( - ctx, - RuntimeContextFactory::fromCode(*module_factory_, runtime_code, {})); + if (not module_factory_.get()) { + return std::errc::not_supported; + } + MemoryLimits config; + BOOST_OUTCOME_TRY(config.heap_alloc_strategy, + heapAllocStrategyHeappagesDefault( + *storage_provider->getCurrentBatch())); + OUTCOME_TRY(instance, + module_factory_.get()->instantiateFromCode( + code_hash, + [&] { return std::make_shared(code); }, + {config})); + OUTCOME_TRY(ctx, RuntimeContextFactory::stateless(instance)); return std::make_unique(std::move(ctx)); } diff --git a/core/runtime/common/core_api_factory_impl.hpp b/core/runtime/common/core_api_factory_impl.hpp index d89141bdea..5d6462175a 100644 --- a/core/runtime/common/core_api_factory_impl.hpp +++ b/core/runtime/common/core_api_factory_impl.hpp @@ -10,6 +10,12 @@ #include +#include "injector/lazy.hpp" + +namespace kagome::crypto { + class Hasher; +} // namespace kagome::crypto + namespace kagome::storage::trie { class TrieStorage; } @@ -19,7 +25,7 @@ namespace kagome::blockchain { } namespace kagome::runtime { - class ModuleFactory; + class RuntimeInstancesPool; } // namespace kagome::runtime namespace kagome::runtime { @@ -28,16 +34,16 @@ namespace kagome::runtime { : public runtime::CoreApiFactory, public std::enable_shared_from_this { public: - explicit CoreApiFactoryImpl( - std::shared_ptr module_factory); - ~CoreApiFactoryImpl() = default; + explicit CoreApiFactoryImpl(std::shared_ptr hasher, + LazySPtr module_factory); outcome::result> make( - std::shared_ptr hasher, - const std::vector &runtime_code) const override; + BufferView code, + std::shared_ptr storage_provider) const override; private: - std::shared_ptr module_factory_; + std::shared_ptr hasher_; + LazySPtr module_factory_; }; } // namespace kagome::runtime diff --git a/core/runtime/common/memory_allocator.cpp b/core/runtime/common/memory_allocator.cpp index 15f9929168..c787c79c3f 100644 --- a/core/runtime/common/memory_allocator.cpp +++ b/core/runtime/common/memory_allocator.cpp @@ -17,9 +17,6 @@ namespace kagome::runtime { static_assert(roundUpAlign(kDefaultHeapBase) == kDefaultHeapBase, "Heap base must be aligned"); - static_assert(kDefaultHeapBase < kInitialMemorySize, - "Heap base must be in memory"); - constexpr auto kPoisoned{"the allocator has been poisoned"}; static uint64_t read_u64(const MemoryHandle &memory, WasmPointer ptr) { diff --git a/core/runtime/common/module_instance.cpp b/core/runtime/common/module_instance.cpp index 250f9606ff..4f4d00f178 100644 --- a/core/runtime/common/module_instance.cpp +++ b/core/runtime/common/module_instance.cpp @@ -26,9 +26,7 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, ModuleInstance::Error, e) { } namespace kagome::runtime { - using namespace kagome::common::literals; - outcome::result ModuleInstance::resetMemory( - const MemoryLimits &limits) { + outcome::result ModuleInstance::resetMemory() { static auto log = log::createLogger("RuntimeEnvironmentFactory", "runtime"); OUTCOME_TRY(opt_heap_base, getGlobal("__heap_base")); @@ -40,7 +38,7 @@ namespace kagome::runtime { uint32_t heap_base = boost::get(*opt_heap_base); auto &memory_provider = getEnvironment().memory_provider; OUTCOME_TRY(const_cast(*memory_provider) - .resetMemory(MemoryConfig{heap_base, limits})); + .resetMemory(MemoryConfig{heap_base})); auto &memory = memory_provider->getCurrentMemory()->get(); size_t max_data_segment_end = 0; @@ -67,4 +65,12 @@ namespace kagome::runtime { return outcome::success(); } + + outcome::result ModuleInstance::stateless() { + getEnvironment() + .storage_provider->setToEphemeralAt(storage::trie::kEmptyRootHash) + .value(); + OUTCOME_TRY(resetMemory()); + return outcome::success(); + } } // namespace kagome::runtime diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index 570715018a..15e83c71d4 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -53,7 +53,7 @@ namespace kagome::runtime { const storage::trie::RootHash &storage_state) { OUTCOME_TRY(item, codeAt(block, storage_state)); return runtime_instances_pool_->instantiateFromCode( - item.hash, *item.code, {item.config}); + item.hash, [&] { return item.code; }, {item.config}); } outcome::result> diff --git a/core/runtime/common/runtime_context.cpp b/core/runtime/common/runtime_context.cpp index 187ccb077d..24cd13a0ce 100644 --- a/core/runtime/common/runtime_context.cpp +++ b/core/runtime/common/runtime_context.cpp @@ -48,38 +48,20 @@ namespace kagome::runtime { : module_repo_{std::move(module_repo)}, header_repo_{std::move(header_repo)} {} - outcome::result RuntimeContextFactory::fromCode( - const runtime::ModuleFactory &module_factory, - common::BufferView code_zstd, - ContextParams params) { - common::Buffer code; - OUTCOME_TRY(runtime::uncompressCodeIfNeeded(code_zstd, code)); - BOOST_OUTCOME_TRY(code, - prepareBlobForCompilation(code, params.memory_limits)); - auto runtime_module_res = module_factory.make(code); - if (!runtime_module_res) { - return Error::COMPILATION_FAILED; - } - OUTCOME_TRY(instance, runtime_module_res.value()->instantiate()); - runtime::RuntimeContext ctx{ - instance, - }; - instance->getEnvironment() - .storage_provider->setToEphemeralAt(storage::trie::kEmptyRootHash) - .value(); - OUTCOME_TRY(instance->resetMemory(params.memory_limits)); - return ctx; + outcome::result RuntimeContextFactory::stateless( + std::shared_ptr instance) { + OUTCOME_TRY(instance->stateless()); + return RuntimeContext{instance}; } outcome::result RuntimeContextFactoryImpl::fromBatch( std::shared_ptr instance, - std::shared_ptr batch, - ContextParams params) const { + std::shared_ptr batch) const { runtime::RuntimeContext ctx{ instance, }; instance->getEnvironment().storage_provider->setTo(batch); - OUTCOME_TRY(instance->resetMemory(params.memory_limits)); + OUTCOME_TRY(instance->resetMemory()); return ctx; } @@ -87,80 +69,75 @@ namespace kagome::runtime { std::shared_ptr instance, const storage::trie::RootHash &state, std::optional> - changes_tracker_opt, - ContextParams params) const { + changes_tracker_opt) const { runtime::RuntimeContext ctx{ instance, }; OUTCOME_TRY(instance->getEnvironment().storage_provider->setToPersistentAt( state, changes_tracker_opt)); - OUTCOME_TRY(instance->resetMemory(params.memory_limits)); + OUTCOME_TRY(instance->resetMemory()); return ctx; } outcome::result RuntimeContextFactoryImpl::ephemeral( std::shared_ptr instance, - const storage::trie::RootHash &state, - ContextParams params) const { + const storage::trie::RootHash &state) const { runtime::RuntimeContext ctx{ instance, }; OUTCOME_TRY( instance->getEnvironment().storage_provider->setToEphemeralAt(state)); - OUTCOME_TRY(instance->resetMemory(params.memory_limits)); + OUTCOME_TRY(instance->resetMemory()); return ctx; } - outcome::result RuntimeContextFactoryImpl::ephemeralAtGenesis( - ContextParams params) const { + outcome::result + RuntimeContextFactoryImpl::ephemeralAtGenesis() const { OUTCOME_TRY(genesis_hash, header_repo_->getHashByNumber(0)); OUTCOME_TRY(genesis_header, header_repo_->getBlockHeader(genesis_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({genesis_hash, 0}, genesis_header.state_root)); - OUTCOME_TRY(ctx, ephemeral(instance, genesis_header.state_root, params)); + OUTCOME_TRY(ctx, ephemeral(instance, genesis_header.state_root)); return ctx; } outcome::result RuntimeContextFactoryImpl::persistentAt( const primitives::BlockHash &block_hash, - TrieChangesTrackerOpt changes_tracker, - ContextParams params) const { + TrieChangesTrackerOpt changes_tracker) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, header.state_root)); - OUTCOME_TRY( - ctx, persistent(instance, header.state_root, changes_tracker, params)); + OUTCOME_TRY(ctx, persistent(instance, header.state_root, changes_tracker)); return ctx; } outcome::result RuntimeContextFactoryImpl::ephemeralAt( - const primitives::BlockHash &block_hash, ContextParams params) const { + const primitives::BlockHash &block_hash) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, header.state_root)); - OUTCOME_TRY(ctx, ephemeral(instance, header.state_root, params)); + OUTCOME_TRY(ctx, ephemeral(instance, header.state_root)); return ctx; } outcome::result RuntimeContextFactoryImpl::ephemeralAt( const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state_hash, - ContextParams params) const { + const storage::trie::RootHash &state_hash) const { OUTCOME_TRY(header, header_repo_->getBlockHeader(block_hash)); OUTCOME_TRY(instance, module_repo_->getInstanceAt({block_hash, header.number}, header.state_root)); - OUTCOME_TRY(ctx, ephemeral(instance, state_hash, params)); + OUTCOME_TRY(ctx, ephemeral(instance, state_hash)); return ctx; } diff --git a/core/runtime/common/runtime_instances_pool.cpp b/core/runtime/common/runtime_instances_pool.cpp index 4357519a5b..680cf37391 100644 --- a/core/runtime/common/runtime_instances_pool.cpp +++ b/core/runtime/common/runtime_instances_pool.cpp @@ -6,6 +6,7 @@ #include "runtime/common/runtime_instances_pool.hpp" +#include "application/app_configuration.hpp" #include "common/monadic_utils.hpp" #include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/instance_environment.hpp" @@ -68,6 +69,10 @@ namespace kagome::runtime { return instance_->resetEnvironment(); } + outcome::result stateless() override { + return instance_->stateless(); + } + private: std::weak_ptr pool_; common::Hash256 hash_; @@ -76,10 +81,12 @@ namespace kagome::runtime { }; RuntimeInstancesPoolImpl::RuntimeInstancesPoolImpl( + const application::AppConfiguration &app_config, std::shared_ptr module_factory, std::shared_ptr instrument, size_t capacity) - : module_factory_{std::move(module_factory)}, + : cache_dir_{app_config.runtimeCacheDirPath()}, + module_factory_{std::move(module_factory)}, instrument_{std::move(instrument)}, pools_{capacity} { BOOST_ASSERT(module_factory_); @@ -88,22 +95,52 @@ namespace kagome::runtime { outcome::result> RuntimeInstancesPoolImpl::instantiateFromCode( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) { std::unique_lock lock{pools_mtx_}; - OUTCOME_TRY(module, getModule(lock, code_hash, code_zstd, config)); + OUTCOME_TRY(module, getModule(lock, code_hash, get_code, config)); OUTCOME_TRY(instance, module.get().instantiate(lock)); BOOST_ASSERT(shared_from_this()); return std::make_shared( weak_from_this(), code_hash, config, std::move(instance)); } + std::filesystem::path RuntimeInstancesPoolImpl::cachePath( + const CodeHash &code_hash, + const RuntimeContext::ContextParams &config) const { + std::string name; + auto to = std::back_inserter(name); + if (auto type = module_factory_->compilerType()) { + fmt::format_to(to, "{}_", *type); + } else { + name.append("wasm_"); + } + fmt::format_to(to, "{}_s", code_hash.toHex()); + if (auto &stack = config.memory_limits.max_stack_values_num) { + fmt::format_to(to, "{}", *stack); + } + if (auto v = boost::get( + &config.memory_limits.heap_alloc_strategy)) { + name.append("_d"); + if (auto &max = v->maximum_pages) { + fmt::format_to(to, "{}", *max); + } + } else { + fmt::format_to(to, + "_s{}", + boost::get( + config.memory_limits.heap_alloc_strategy) + .extra_pages); + } + return cache_dir_ / name; + } + outcome::result RuntimeInstancesPoolImpl::precompile( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) { std::unique_lock lock{pools_mtx_}; - OUTCOME_TRY(getModule(lock, code_hash, code_zstd, config)); + OUTCOME_TRY(getModule(lock, code_hash, get_code, config)); return outcome::success(); } @@ -112,14 +149,14 @@ namespace kagome::runtime { RuntimeInstancesPoolImpl::getModule( std::unique_lock &lock, const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) { Key key{code_hash, config}; auto pool_opt = pools_.get(key); if (!pool_opt) { lock.unlock(); - OUTCOME_TRY(module, tryCompileModule(code_hash, code_zstd, config)); + OUTCOME_TRY(module, tryCompileModule(code_hash, get_code, config)); lock.lock(); pool_opt = pools_.get(key); if (!pool_opt) { @@ -133,7 +170,7 @@ namespace kagome::runtime { RuntimeInstancesPoolImpl::CompilationResult RuntimeInstancesPoolImpl::tryCompileModule( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) { std::unique_lock l{compiling_modules_mtx_}; Key key{code_hash, config}; @@ -149,30 +186,26 @@ namespace kagome::runtime { BOOST_ASSERT(is_inserted); BOOST_ASSERT(iter != compiling_modules_.end()); l.unlock(); - - common::Buffer code; - std::optional res; - if (!uncompressCodeIfNeeded(code_zstd, code)) { - res = CompilationError{"Failed to uncompress code"}; - } else { - auto instr_res = instrument_->instrument(code, config.memory_limits); - if (!instr_res) { - res = CompilationError{fmt::format("Failed to inject stack limiter: {}", - instr_res.error().msg)}; - } else { - code = std::move(instr_res.value()); - } - if (!res) { - res = - common::map_result(module_factory_->make(code), [](auto &&module) { - return std::shared_ptr(module); - }); + auto path = cachePath(code_hash, config); + auto res = [&]() -> CompilationResult { + std::error_code ec; + if (not std::filesystem::exists(path, ec)) { + if (ec) { + return ec; + } + OUTCOME_TRY(code_zstd, get_code()); + OUTCOME_TRY(code, uncompressCodeIfNeeded(*code_zstd)); + BOOST_OUTCOME_TRY(code, + instrument_->instrument(code, config.memory_limits)); + OUTCOME_TRY(module_factory_->compile(path, code)); } - } + OUTCOME_TRY(module, module_factory_->loadCompiled(path)); + return module; + }(); l.lock(); compiling_modules_.erase(iter); - promise.set_value(*res); - return *res; + promise.set_value(res); + return res; } void RuntimeInstancesPoolImpl::release( diff --git a/core/runtime/common/runtime_instances_pool.hpp b/core/runtime/common/runtime_instances_pool.hpp index 6a9aeaa24e..293ea0b3ac 100644 --- a/core/runtime/common/runtime_instances_pool.hpp +++ b/core/runtime/common/runtime_instances_pool.hpp @@ -17,6 +17,10 @@ #include "runtime/module_factory.hpp" #include "utils/lru.hpp" +namespace kagome::application { + class AppConfiguration; +} // namespace kagome::application + namespace kagome::runtime { class InstrumentWasm; @@ -28,13 +32,14 @@ namespace kagome::runtime { public std::enable_shared_from_this { public: explicit RuntimeInstancesPoolImpl( + const application::AppConfiguration &app_config, std::shared_ptr module_factory, std::shared_ptr instrument, size_t capacity = DEFAULT_MODULES_CACHE_SIZE); outcome::result> instantiateFromCode( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) override; /** @@ -48,9 +53,13 @@ namespace kagome::runtime { const RuntimeContext::ContextParams &config, std::shared_ptr &&instance); + std::filesystem::path cachePath( + const CodeHash &code_hash, + const RuntimeContext::ContextParams &config) const; + outcome::result precompile( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config); private: @@ -66,15 +75,16 @@ namespace kagome::runtime { outcome::result> getModule( std::unique_lock &lock, const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config); using CompilationResult = CompilationOutcome>; CompilationResult tryCompileModule( const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config); + std::filesystem::path cache_dir_; std::shared_ptr module_factory_; std::shared_ptr instrument_; @@ -90,6 +100,7 @@ namespace kagome::runtime { template <> struct boost::di::ctor_traits { - BOOST_DI_INJECT_TRAITS(std::shared_ptr, + BOOST_DI_INJECT_TRAITS(const kagome::application::AppConfiguration &, + std::shared_ptr, std::shared_ptr); }; diff --git a/core/runtime/common/storage_code_provider.cpp b/core/runtime/common/storage_code_provider.cpp index 28b1055e87..4eaf7027ce 100644 --- a/core/runtime/common/storage_code_provider.cpp +++ b/core/runtime/common/storage_code_provider.cpp @@ -8,8 +8,6 @@ #include -#include "log/logger.hpp" -#include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/runtime_upgrade_tracker.hpp" #include "storage/predefined_keys.hpp" #include "storage/trie/trie_storage.hpp" @@ -42,24 +40,14 @@ namespace kagome::runtime { OUTCOME_TRY( code, chain_spec_->fetchCodeSubstituteByBlockInfo(block_info.value())); - common::Buffer code2; - OUTCOME_TRY(uncompressCodeIfNeeded(code, code2)); - return std::make_shared(std::move(code2)); + return std::make_shared(std::move(code)); } } OUTCOME_TRY(batch, storage_->getEphemeralBatchAt(state)); - OUTCOME_TRY(code, setCodeFromBatch(*batch.get())); + OUTCOME_TRY(code, batch->get(storage::kRuntimeCodeKey)); cached_code_ = std::make_shared(std::move(code)); last_state_root_ = state; } return cached_code_; } - - outcome::result StorageCodeProvider::setCodeFromBatch( - const storage::trie::TrieBatch &batch) const { - OUTCOME_TRY(code, batch.get(storage::kRuntimeCodeKey)); - common::Buffer uncompressed; - OUTCOME_TRY(uncompressCodeIfNeeded(code, uncompressed)); - return uncompressed; - } } // namespace kagome::runtime diff --git a/core/runtime/common/storage_code_provider.hpp b/core/runtime/common/storage_code_provider.hpp index c7416d244f..e709c8fd9e 100644 --- a/core/runtime/common/storage_code_provider.hpp +++ b/core/runtime/common/storage_code_provider.hpp @@ -34,8 +34,6 @@ namespace kagome::runtime { Result getCodeAt(const storage::trie::RootHash &state) const override; private: - outcome::result setCodeFromBatch( - const storage::trie::TrieBatch &batch) const; std::shared_ptr storage_; std::shared_ptr runtime_upgrade_tracker_; std::shared_ptr known_code_substitutes_; diff --git a/core/runtime/core_api_factory.hpp b/core/runtime/core_api_factory.hpp index d078bac816..7a18295091 100644 --- a/core/runtime/core_api_factory.hpp +++ b/core/runtime/core_api_factory.hpp @@ -7,21 +7,13 @@ #pragma once #include -#include +#include "common/buffer_view.hpp" #include "outcome/outcome.hpp" -namespace kagome::host_api { - class HostApiFactory; -} - -namespace kagome::crypto { - class Hasher; -} - namespace kagome::runtime { - class RestrictedCore; + class TrieStorageProvider; /** * A factory for Core API, used where an isolated runtime environment @@ -32,8 +24,8 @@ namespace kagome::runtime { virtual ~CoreApiFactory() = default; [[nodiscard]] virtual outcome::result> make( - std::shared_ptr hasher, - const std::vector &runtime_code) const = 0; + BufferView code, + std::shared_ptr storage_provider) const = 0; }; } // namespace kagome::runtime diff --git a/core/runtime/memory.hpp b/core/runtime/memory.hpp index 99c8ccc52f..5c41ef09e2 100644 --- a/core/runtime/memory.hpp +++ b/core/runtime/memory.hpp @@ -17,11 +17,6 @@ namespace kagome::runtime { using BytesOut = std::span; - inline constexpr size_t kInitialMemorySize = []() { - using kagome::common::literals::operator""_MB; - return 2_MB; - }(); - // according to $3.1.2.1 in the Polkadot Host Spec // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances inline constexpr size_t kMemoryPageSize = []() { diff --git a/core/runtime/module_factory.hpp b/core/runtime/module_factory.hpp index 1cefa4f29c..e6849fc5f2 100644 --- a/core/runtime/module_factory.hpp +++ b/core/runtime/module_factory.hpp @@ -6,10 +6,11 @@ #pragma once +#include + +#include "common/buffer_view.hpp" #include "outcome/custom.hpp" -#include "runtime/instance_environment.hpp" #include "runtime/types.hpp" -#include "storage/trie/types.hpp" namespace kagome::runtime { @@ -19,6 +20,9 @@ namespace kagome::runtime { CompilationError(const std::string &message) : std::runtime_error(message.c_str()) {} + CompilationError(const std::error_code &ec) + : CompilationError{ec.message()} {} + std::string_view message() const { return what(); } @@ -38,8 +42,25 @@ namespace kagome::runtime { public: virtual ~ModuleFactory() = default; - virtual CompilationOutcome> make( - common::BufferView code) const = 0; + /** + * Used as part of filename to separate files of different incompatible + * compilers. + * `std::nullopt` means `path_compiled` stores raw WASM code for + * interpretation. + */ + virtual std::optional compilerType() const = 0; + + /** + * Compile `wasm` code to `path_compiled`. + */ + virtual CompilationOutcome compile( + std::filesystem::path path_compiled, BufferView wasm) const = 0; + + /** + * Load compiled code from `path_compiled`. + */ + virtual CompilationOutcome> loadCompiled( + std::filesystem::path path_compiled) const = 0; }; } // namespace kagome::runtime diff --git a/core/runtime/module_instance.hpp b/core/runtime/module_instance.hpp index 2fe1e3830f..c0b16e2b45 100644 --- a/core/runtime/module_instance.hpp +++ b/core/runtime/module_instance.hpp @@ -121,7 +121,9 @@ namespace kagome::runtime { virtual const InstanceEnvironment &getEnvironment() const = 0; virtual outcome::result resetEnvironment() = 0; - outcome::result resetMemory(const MemoryLimits &config); + outcome::result resetMemory(); + + virtual outcome::result stateless(); }; } // namespace kagome::runtime diff --git a/core/runtime/runtime_api/impl/core.cpp b/core/runtime/runtime_api/impl/core.cpp index 7211ab70ad..b2e0dc80bd 100644 --- a/core/runtime/runtime_api/impl/core.cpp +++ b/core/runtime/runtime_api/impl/core.cpp @@ -11,29 +11,8 @@ #include "runtime/executor.hpp" #include "runtime/module_instance.hpp" #include "runtime/module_repository.hpp" -#include "runtime/runtime_properties_cache.hpp" namespace kagome::runtime { - - outcome::result callCoreVersion( - const ModuleFactory &module_factory, - common::BufferView code, - const MemoryLimits &config, - const std::shared_ptr &runtime_properties_cache) { - OUTCOME_TRY(ctx, - runtime::RuntimeContextFactory::fromCode( - module_factory, code, {config})); - 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( - "Core_version", raw_res); - }); - } - RestrictedCoreImpl::RestrictedCoreImpl(RuntimeContext ctx) : ctx_{std::move(ctx)} {} diff --git a/core/runtime/runtime_api/impl/core.hpp b/core/runtime/runtime_api/impl/core.hpp index 780c060ee5..e58bc4bab7 100644 --- a/core/runtime/runtime_api/impl/core.hpp +++ b/core/runtime/runtime_api/impl/core.hpp @@ -13,13 +13,6 @@ namespace kagome::runtime { class Executor; - class RuntimePropertiesCache; - - outcome::result callCoreVersion( - const ModuleFactory &module_factory, - common::BufferView code, - const MemoryLimits &config, - const std::shared_ptr &runtime_properties_cache); class RestrictedCoreImpl final : public RestrictedCore { public: diff --git a/core/runtime/runtime_context.hpp b/core/runtime/runtime_context.hpp index 5879bb491d..18fb33551e 100644 --- a/core/runtime/runtime_context.hpp +++ b/core/runtime/runtime_context.hpp @@ -41,12 +41,6 @@ namespace kagome::runtime { ~RuntimeContext(); - // constructor for tests - static RuntimeContext create_TEST( - std::shared_ptr module_instance) { - return RuntimeContext{module_instance}; - } - // https://github.com/paritytech/polkadot-sdk/blob/e16ef0861f576dd260487d78b57949b18795ed77/polkadot/primitives/src/v6/executor_params.rs#L32 static constexpr size_t DEFAULT_STACK_MAX = 65536; @@ -69,45 +63,36 @@ namespace kagome::runtime { virtual ~RuntimeContextFactory() = default; - static outcome::result fromCode( - const runtime::ModuleFactory &module_factory, - common::BufferView code_zstd, - ContextParams params); + static outcome::result stateless( + std::shared_ptr instance); virtual outcome::result fromBatch( std::shared_ptr module_instance, - std::shared_ptr batch, - ContextParams params = {}) const = 0; + std::shared_ptr batch) const = 0; virtual outcome::result persistent( std::shared_ptr module_instance, const storage::trie::RootHash &state, std::optional> - changes_tracker_opt, - ContextParams params = {}) const = 0; + changes_tracker_opt) const = 0; virtual outcome::result persistentAt( const primitives::BlockHash &block_hash, std::optional> - changes_tracker_opt, - ContextParams params = {}) const = 0; + changes_tracker_opt) const = 0; virtual outcome::result ephemeral( std::shared_ptr module_instance, - const storage::trie::RootHash &state, - ContextParams params = {}) const = 0; + const storage::trie::RootHash &state) const = 0; virtual outcome::result ephemeralAt( - const primitives::BlockHash &block_hash, - ContextParams params = {}) const = 0; + const primitives::BlockHash &block_hash) const = 0; virtual outcome::result ephemeralAt( const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state, - ContextParams params = {}) const = 0; + const storage::trie::RootHash &state) const = 0; - virtual outcome::result ephemeralAtGenesis( - ContextParams params = {}) const = 0; + virtual outcome::result ephemeralAtGenesis() const = 0; }; class RuntimeContextFactoryImpl : public RuntimeContextFactory { @@ -120,38 +105,31 @@ namespace kagome::runtime { virtual outcome::result fromBatch( std::shared_ptr module_instance, - std::shared_ptr batch, - ContextParams params = {}) const override; + std::shared_ptr batch) const override; virtual outcome::result persistent( std::shared_ptr module_instance, const storage::trie::RootHash &state, std::optional> - changes_tracker_opt, - ContextParams params = {}) const override; + changes_tracker_opt) const override; virtual outcome::result persistentAt( const primitives::BlockHash &block_hash, std::optional> - changes_tracker_opt = {}, - ContextParams params = {}) const override; + changes_tracker_opt = {}) const override; virtual outcome::result ephemeral( std::shared_ptr module_instance, - const storage::trie::RootHash &state, - ContextParams params = {}) const override; + const storage::trie::RootHash &state) const override; virtual outcome::result ephemeralAt( - const primitives::BlockHash &block_hash, - ContextParams params = {}) const override; + const primitives::BlockHash &block_hash) const override; virtual outcome::result ephemeralAt( const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state, - ContextParams params = {}) const override; + const storage::trie::RootHash &state) const override; - virtual outcome::result ephemeralAtGenesis( - ContextParams params = {}) const override; + virtual outcome::result ephemeralAtGenesis() const override; private: std::shared_ptr module_repo_; diff --git a/core/runtime/runtime_instances_pool.hpp b/core/runtime/runtime_instances_pool.hpp index 4d922e1ae8..0b5e24f75b 100644 --- a/core/runtime/runtime_instances_pool.hpp +++ b/core/runtime/runtime_instances_pool.hpp @@ -7,6 +7,7 @@ #pragma once #include "runtime/module_instance.hpp" +#include "runtime/runtime_code_provider.hpp" #include "runtime/runtime_context.hpp" #include "storage/trie/types.hpp" @@ -20,12 +21,13 @@ namespace kagome::runtime { static constexpr size_t DEFAULT_MODULES_CACHE_SIZE = 2; using CodeHash = storage::trie::RootHash; + using GetCode = std::function; virtual ~RuntimeInstancesPool() = default; virtual outcome::result> instantiateFromCode(const CodeHash &code_hash, - common::BufferView code_zstd, + const GetCode &get_code, const RuntimeContext::ContextParams &config) = 0; }; diff --git a/core/runtime/types.hpp b/core/runtime/types.hpp index 34d222bd8d..0c4859f588 100644 --- a/core/runtime/types.hpp +++ b/core/runtime/types.hpp @@ -59,11 +59,9 @@ namespace kagome::runtime { }; struct MemoryConfig { - explicit MemoryConfig(uint32_t heap_base, MemoryLimits limits = {}) - : heap_base{heap_base}, limits{std::move(limits)} {} + explicit MemoryConfig(uint32_t heap_base) : heap_base{heap_base} {} uint32_t heap_base; - MemoryLimits limits; }; /** diff --git a/core/runtime/wasm_edge/memory_impl.cpp b/core/runtime/wasm_edge/memory_impl.cpp index 37048a61fb..77f7b599b2 100644 --- a/core/runtime/wasm_edge/memory_impl.cpp +++ b/core/runtime/wasm_edge/memory_impl.cpp @@ -13,11 +13,9 @@ namespace kagome::runtime::wasm_edge { - MemoryImpl::MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance, - const MemoryConfig &config) + MemoryImpl::MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance) : mem_instance_{mem_instance} { BOOST_ASSERT(mem_instance_ != nullptr); - resize(kInitialMemorySize); SL_DEBUG(logger_, "Created memory wrapper {} for internal instance {}", fmt::ptr(this), @@ -82,7 +80,7 @@ namespace kagome::runtime::wasm_edge { [[nodiscard]] outcome::result ExternalMemoryProviderImpl::resetMemory( const MemoryConfig &config) { - auto handle = std::make_shared(wasmedge_memory_, config); + auto handle = std::make_shared(wasmedge_memory_); auto allocator = std::make_unique(handle, config); current_memory_ = std::make_shared(handle, std::move(allocator)); return outcome::success(); @@ -99,7 +97,7 @@ namespace kagome::runtime::wasm_edge { [[nodiscard]] outcome::result InternalMemoryProviderImpl::resetMemory( const MemoryConfig &config) { if (wasmedge_memory_) { - auto handle = std::make_shared(wasmedge_memory_, config); + auto handle = std::make_shared(wasmedge_memory_); auto allocator = std::make_unique(handle, config); current_memory_ = std::make_shared(handle, std::move(allocator)); } diff --git a/core/runtime/wasm_edge/memory_impl.hpp b/core/runtime/wasm_edge/memory_impl.hpp index fecea9ae72..8cfcdf7d6f 100644 --- a/core/runtime/wasm_edge/memory_impl.hpp +++ b/core/runtime/wasm_edge/memory_impl.hpp @@ -18,8 +18,7 @@ namespace kagome::runtime::wasm_edge { class MemoryImpl final : public MemoryHandle { public: - MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance, - const MemoryConfig &config); + MemoryImpl(WasmEdge_MemoryInstanceContext *mem_instance); /** * @brief Return the size of the memory diff --git a/core/runtime/wasm_edge/module_factory_impl.cpp b/core/runtime/wasm_edge/module_factory_impl.cpp index 2e9074c945..ca2efac3f8 100644 --- a/core/runtime/wasm_edge/module_factory_impl.cpp +++ b/core/runtime/wasm_edge/module_factory_impl.cpp @@ -10,10 +10,11 @@ #include #include +#include "application/app_configuration.hpp" #include "crypto/hasher.hpp" #include "host_api/host_api_factory.hpp" +#include "log/formatters/filepath.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" @@ -22,6 +23,9 @@ #include "runtime/wasm_edge/memory_impl.hpp" #include "runtime/wasm_edge/register_host_api.hpp" #include "runtime/wasm_edge/wrappers.hpp" +#include "utils/mkdirs.hpp" +#include "utils/read_file.hpp" +#include "utils/write_file.hpp" namespace kagome::runtime::wasm_edge { enum class Error { @@ -96,6 +100,14 @@ namespace kagome::runtime::wasm_edge { BOOST_UNREACHABLE_RETURN({}); } + inline CompilationOutcome configureCtx() { + ConfigureContext ctx{WasmEdge_ConfigureCreate()}; + if (ctx.raw() == nullptr) { + return CompilationError{"WasmEdge_ConfigureCreate returned nullptr"}; + } + return ctx; + } + class ModuleInstanceImpl : public ModuleInstance { public: explicit ModuleInstanceImpl( @@ -348,60 +360,55 @@ namespace kagome::runtime::wasm_edge { std::shared_ptr host_api_factory, std::shared_ptr storage, std::shared_ptr serializer, + std::shared_ptr core_factory, Config config) : hasher_{hasher}, host_api_factory_{host_api_factory}, storage_{storage}, serializer_{serializer}, + core_factory_{std::move(core_factory)}, log_{log::createLogger("ModuleFactory", "runtime")}, config_{config} { BOOST_ASSERT(hasher_); BOOST_ASSERT(host_api_factory_); } - CompilationOutcome> ModuleFactoryImpl::make( - common::BufferView code) const { - auto code_hash = hasher_->sha2_256(code); + std::optional ModuleFactoryImpl::compilerType() const { + if (config_.exec == ExecType::Interpreted) { + return std::nullopt; + } + return "wasmedge"; + } - ConfigureContext configure_ctx = WasmEdge_ConfigureCreate(); - BOOST_ASSERT(configure_ctx.raw() != nullptr); // no known reasons to fail + CompilationOutcome ModuleFactoryImpl::compile( + std::filesystem::path path_compiled, BufferView code) const { + if (config_.exec == ExecType::Interpreted) { + OUTCOME_TRY(writeFileTmp(path_compiled, code)); + return outcome::success(); + } + OUTCOME_TRY(configure_ctx, configureCtx()); + WasmEdge_ConfigureCompilerSetOptimizationLevel( + configure_ctx.raw(), WasmEdge_CompilerOptimizationLevel_O3); + CompilerContext compiler = WasmEdge_CompilerCreate(configure_ctx.raw()); + SL_INFO(log_, "Start compiling wasm module {}", path_compiled); + WasmEdge_UNWRAP_COMPILE_ERR(WasmEdge_CompilerCompileFromBuffer( + compiler.raw(), code.data(), code.size(), path_compiled.c_str())); + SL_INFO(log_, "Compilation finished, saved at {}", path_compiled); + return outcome::success(); + } + CompilationOutcome> ModuleFactoryImpl::loadCompiled( + std::filesystem::path path_compiled) const { + Buffer code; + if (not readFile(code, path_compiled)) { + return CompilationError{"read file failed"}; + } + auto code_hash = hasher_->blake2b_256(code); + OUTCOME_TRY(configure_ctx, configureCtx()); LoaderContext loader_ctx = WasmEdge_LoaderCreate(configure_ctx.raw()); WasmEdge_ASTModuleContext *module_ctx; - - switch (config_.exec) { - case ExecType::Compiled: { - WasmEdge_ConfigureCompilerSetOptimizationLevel( - configure_ctx.raw(), WasmEdge_CompilerOptimizationLevel_O3); - 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)}; - } - 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({}); - } + WasmEdge_UNWRAP_COMPILE_ERR(WasmEdge_LoaderParseFromFile( + loader_ctx.raw(), &module_ctx, path_compiled.c_str())); ASTModuleContext module = module_ctx; ValidatorContext validator = WasmEdge_ValidatorCreate(configure_ctx.raw()); @@ -430,10 +437,8 @@ namespace kagome::runtime::wasm_edge { } } - auto core_api = std::make_shared(shared_from_this()); - auto env_factory = std::make_shared( - core_api, host_api_factory_, storage_, serializer_); + core_factory_, host_api_factory_, storage_, serializer_); return std::shared_ptr{new ModuleImpl{std::move(module), std::move(executor), diff --git a/core/runtime/wasm_edge/module_factory_impl.hpp b/core/runtime/wasm_edge/module_factory_impl.hpp index d480fafec6..044e88fa25 100644 --- a/core/runtime/wasm_edge/module_factory_impl.hpp +++ b/core/runtime/wasm_edge/module_factory_impl.hpp @@ -19,8 +19,9 @@ namespace kagome::host_api { } namespace kagome::runtime { + class CoreApiFactory; class TrieStorageProvider; -} +} // namespace kagome::runtime namespace kagome::blockchain { class BlockHeaderRepository; @@ -33,20 +34,16 @@ namespace kagome::storage::trie { namespace kagome::runtime::wasm_edge { - class ModuleFactoryImpl - : public ModuleFactory, - public std::enable_shared_from_this { + class ModuleFactoryImpl : public ModuleFactory { public: enum class ExecType { Interpreted, Compiled, }; struct Config { - Config(ExecType exec, const std::filesystem::path &compiled_module_dir) - : exec{exec}, compiled_module_dir{compiled_module_dir} {} + Config(ExecType exec) : exec{exec} {} ExecType exec; - std::filesystem::path compiled_module_dir; }; explicit ModuleFactoryImpl( @@ -54,16 +51,21 @@ namespace kagome::runtime::wasm_edge { std::shared_ptr host_api_factory, std::shared_ptr storage, std::shared_ptr serializer, + std::shared_ptr core_factory, Config config); - CompilationOutcome> make( - common::BufferView code) const override; + std::optional compilerType() const override; + CompilationOutcome compile(std::filesystem::path path_compiled, + BufferView code) const override; + CompilationOutcome> loadCompiled( + std::filesystem::path path_compiled) const override; private: std::shared_ptr hasher_; std::shared_ptr host_api_factory_; std::shared_ptr storage_; std::shared_ptr serializer_; + std::shared_ptr core_factory_; log::Logger log_; Config config_; }; diff --git a/core/runtime/wavm/CMakeLists.txt b/core/runtime/wavm/CMakeLists.txt index 726d2f1fc3..3409b89d91 100644 --- a/core/runtime/wavm/CMakeLists.txt +++ b/core/runtime/wavm/CMakeLists.txt @@ -12,7 +12,6 @@ add_library(runtime_wavm intrinsics/intrinsic_resolver_impl.cpp instance_environment_factory.cpp module.cpp - module_cache.cpp module_params.cpp module_factory_impl.cpp module_instance.cpp diff --git a/core/runtime/wavm/instance_environment_factory.cpp b/core/runtime/wavm/instance_environment_factory.cpp index d841aa3866..6c21e634ca 100644 --- a/core/runtime/wavm/instance_environment_factory.cpp +++ b/core/runtime/wavm/instance_environment_factory.cpp @@ -7,7 +7,6 @@ #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/intrinsics/intrinsic_functions.hpp" #include "runtime/wavm/intrinsics/intrinsic_module_instance.hpp" @@ -21,13 +20,11 @@ namespace kagome::runtime::wavm { std::shared_ptr storage, std::shared_ptr serializer, std::shared_ptr host_api_factory, - std::shared_ptr module_factory) + std::shared_ptr core_factory) : storage_{std::move(storage)}, serializer_{std::move(serializer)}, host_api_factory_{std::move(host_api_factory)}, - module_factory_{module_factory} { - BOOST_ASSERT(module_factory_ != nullptr); - } + core_factory_{core_factory} {} InstanceEnvironment InstanceEnvironmentFactory::make( MemoryOrigin memory_origin, @@ -35,7 +32,6 @@ 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(module_factory_); std::shared_ptr memory_provider; switch (memory_origin) { @@ -49,7 +45,7 @@ namespace kagome::runtime::wavm { } break; } auto host_api = std::shared_ptr(host_api_factory_->make( - core_factory, memory_provider, new_storage_provider)); + core_factory_, memory_provider, new_storage_provider)); return InstanceEnvironment{std::move(memory_provider), std::move(new_storage_provider), diff --git a/core/runtime/wavm/instance_environment_factory.hpp b/core/runtime/wavm/instance_environment_factory.hpp index bec26db93d..3b5f8bb46f 100644 --- a/core/runtime/wavm/instance_environment_factory.hpp +++ b/core/runtime/wavm/instance_environment_factory.hpp @@ -26,7 +26,7 @@ namespace WAVM::Runtime { } namespace kagome::runtime { - class ModuleFactory; + class CoreApiFactory; } // namespace kagome::runtime namespace kagome::runtime::wavm { @@ -39,7 +39,7 @@ namespace kagome::runtime::wavm { std::shared_ptr storage, std::shared_ptr serializer, std::shared_ptr host_api_factory, - std::shared_ptr module_factory); + std::shared_ptr core_factory); enum class MemoryOrigin { EXTERNAL, INTERNAL }; [[nodiscard]] InstanceEnvironment make( @@ -51,7 +51,7 @@ namespace kagome::runtime::wavm { std::shared_ptr storage_; std::shared_ptr serializer_; std::shared_ptr host_api_factory_; - std::shared_ptr module_factory_; + std::shared_ptr core_factory_; }; } // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/memory_impl.cpp b/core/runtime/wavm/memory_impl.cpp index 06a23e60d9..d06e9e2bc7 100644 --- a/core/runtime/wavm/memory_impl.cpp +++ b/core/runtime/wavm/memory_impl.cpp @@ -13,11 +13,9 @@ namespace kagome::runtime::wavm { - MemoryImpl::MemoryImpl(WAVM::Runtime::Memory *memory, - const MemoryConfig &config) + MemoryImpl::MemoryImpl(WAVM::Runtime::Memory *memory) : memory_{memory}, logger_{log::createLogger("WAVM Memory", "wavm")} { BOOST_ASSERT(memory_); - resize(kInitialMemorySize); } std::optional MemoryImpl::pagesMax() const { diff --git a/core/runtime/wavm/memory_impl.hpp b/core/runtime/wavm/memory_impl.hpp index 88d5dbd77b..d98cbb588c 100644 --- a/core/runtime/wavm/memory_impl.hpp +++ b/core/runtime/wavm/memory_impl.hpp @@ -19,17 +19,12 @@ #include "runtime/types.hpp" #include "runtime/wavm/intrinsics/intrinsic_functions.hpp" -namespace kagome::runtime { - class MemoryAllocator; - struct MemoryConfig; -} // namespace kagome::runtime - namespace kagome::runtime::wavm { static_assert(kMemoryPageSize == WAVM::IR::numBytesPerPage); class MemoryImpl final : public kagome::runtime::MemoryHandle { public: - MemoryImpl(WAVM::Runtime::Memory *memory, const MemoryConfig &config); + MemoryImpl(WAVM::Runtime::Memory *memory); MemoryImpl(const MemoryImpl ©) = delete; MemoryImpl &operator=(const MemoryImpl ©) = delete; MemoryImpl(MemoryImpl &&move) = delete; diff --git a/core/runtime/wavm/module_cache.cpp b/core/runtime/wavm/module_cache.cpp deleted file mode 100644 index 0464707c63..0000000000 --- a/core/runtime/wavm/module_cache.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "runtime/wavm/module_cache.hpp" - -#include -#include - -#include "crypto/hasher.hpp" -#include "log/formatters/filepath.hpp" -#include "utils/read_file.hpp" - -namespace kagome::runtime::wavm { - ModuleCache::ModuleCache(std::shared_ptr hasher, - fs::path cache_dir) - : cache_dir_{std::move(cache_dir)}, - hasher_{std::move(hasher)}, - logger_{log::createLogger("WAVM Module Cache", "runtime_cache")} { - BOOST_ASSERT(hasher_ != nullptr); - } - - std::vector ModuleCache::getCachedObject( - const WAVM::U8 *wasmBytes, - WAVM::Uptr numWASMBytes, - std::function()> &&compileThunk) { - auto runtime_hash = hasher_->twox_64({wasmBytes, numWASMBytes}).toHex(); - auto filepath = cache_dir_ / runtime_hash; - if (!exists(filepath) and !exists(cache_dir_) - and !fs::createDirectoryRecursive(cache_dir_)) { - SL_ERROR( - logger_, "Failed to create runtimes cache directory {}", cache_dir_); - } - - std ::vector module; - if (readFile(module, filepath.string())) { - SL_VERBOSE(logger_, "WAVM runtime cache hit: {}", filepath); - } else { - module = compileThunk(); - if (auto file = - std::ofstream{filepath.c_str(), std::ios::out | std::ios::binary}; - file.is_open()) { - file.write(reinterpret_cast(module.data()), module.size()); - file.close(); - if (not file.fail()) { - SL_VERBOSE(logger_, "Saved WAVM runtime to cache: {}", filepath); - } else { - module.clear(); - SL_ERROR(logger_, "Error writing module to cache: {}", filepath); - } - } else { - SL_ERROR(logger_, "Failed to cache WAVM runtime: {}", filepath); - } - } - - return module; - } -} // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/module_cache.hpp b/core/runtime/wavm/module_cache.hpp deleted file mode 100644 index 5cc3ba40bc..0000000000 --- a/core/runtime/wavm/module_cache.hpp +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "application/app_configuration.hpp" - -#include -#include "filesystem/directories.hpp" -#include "log/logger.hpp" - -namespace kagome::crypto { - class Hasher; -} - -namespace kagome::runtime::wavm { - namespace fs = kagome::filesystem; - - /** - * WAVM runtime cache. Attempts to fetch precompiled module from fs and saves - * compiled module upon cache miss. - * - */ - struct ModuleCache : public WAVM::Runtime::ObjectCacheInterface { - public: - ModuleCache(std::shared_ptr hasher, fs::path cache_dir); - - std::vector getCachedObject( - const WAVM::U8 *wasmBytes, - WAVM::Uptr numWASMBytes, - std::function()> &&compileThunk) override; - - private: - fs::path cache_dir_; - std::shared_ptr hasher_; - log::Logger logger_; - }; - -} // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/module_factory_impl.cpp b/core/runtime/wavm/module_factory_impl.cpp index 6b4e7e3ffd..1488950899 100644 --- a/core/runtime/wavm/module_factory_impl.cpp +++ b/core/runtime/wavm/module_factory_impl.cpp @@ -6,26 +6,57 @@ #include "runtime/wavm/module_factory_impl.hpp" +#include +#include +#include +#include +#include + +#include "common/buffer.hpp" +#include "common/span_adl.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" +#include "utils/read_file.hpp" +#include "utils/write_file.hpp" namespace kagome::runtime::wavm { + struct Compiled { + SCALE_TIE(2); + + Buffer wasm, compiled; + }; + + static thread_local std::shared_ptr loading; + + struct ObjectCache : WAVM::Runtime::ObjectCacheInterface { + std::vector getCachedObject( + const WAVM::U8 *ptr, + WAVM::Uptr size, + std::function()> &&get) override { + std::span input{ptr, size}; + // wasm code was already compiled, other calls are trampolines + if (loading and SpanAdl{input} == loading->wasm) { + return loading->compiled; + } + return get(); + } + }; ModuleFactoryImpl::ModuleFactoryImpl( std::shared_ptr compartment, std::shared_ptr module_params, std::shared_ptr host_api_factory, + std::shared_ptr core_factory, std::shared_ptr storage, std::shared_ptr serializer, std::shared_ptr intrinsic_module, - std::optional> module_cache, std::shared_ptr hasher) : compartment_{std::move(compartment)}, module_params_{std::move(module_params)}, host_api_factory_{host_api_factory}, + core_factory_{std::move(core_factory)}, storage_{storage}, serializer_{serializer}, intrinsic_module_{std::move(intrinsic_module)}, @@ -36,23 +67,49 @@ namespace kagome::runtime::wavm { BOOST_ASSERT(intrinsic_module_ != nullptr); BOOST_ASSERT(hasher_ != nullptr); - if (module_cache.has_value()) { - WAVM::Runtime::setGlobalObjectCache(std::move(module_cache.value())); + [[maybe_unused]] static auto init = [] { + WAVM::Runtime::setGlobalObjectCache(std::make_shared()); + return 0; + }(); + } + + std::optional ModuleFactoryImpl::compilerType() const { + return "wavm"; + } + + CompilationOutcome ModuleFactoryImpl::compile( + std::filesystem::path path_compiled, BufferView code) const { + WAVM::IR::Module ir; + WAVM::WASM::LoadError error; + if (not WAVM::WASM::loadBinaryModule( + code.data(), code.size(), ir, &error)) { + return CompilationError{std::move(error.message)}; } + auto compiled = + WAVM::LLVMJIT::compileModule(ir, WAVM::LLVMJIT::getHostTargetSpec()); + auto raw = + scale::encode(Compiled{code, Buffer{std::move(compiled)}}).value(); + OUTCOME_TRY(writeFileTmp(path_compiled, raw)); + return outcome::success(); } - CompilationOutcome> ModuleFactoryImpl::make( - common::BufferView code) const { + CompilationOutcome> ModuleFactoryImpl::loadCompiled( + std::filesystem::path path_compiled) const { + Buffer file; + if (not readFile(file, path_compiled)) { + return CompilationError{"read file failed"}; + } + BOOST_OUTCOME_TRY(loading, scale::decode>(file)); + libp2p::common::FinalAction clear = [] { loading.reset(); }; auto env_factory = std::make_shared( - storage_, serializer_, host_api_factory_, shared_from_this()); + storage_, serializer_, host_api_factory_, core_factory_); OUTCOME_TRY(module, ModuleImpl::compileFrom(compartment_, *module_params_, intrinsic_module_, env_factory, - code, - hasher_->sha2_256(code))); + loading->wasm, + hasher_->blake2b_256(loading->wasm))); return module; } - } // namespace kagome::runtime::wavm diff --git a/core/runtime/wavm/module_factory_impl.hpp b/core/runtime/wavm/module_factory_impl.hpp index 16ac2cb198..0435953cfc 100644 --- a/core/runtime/wavm/module_factory_impl.hpp +++ b/core/runtime/wavm/module_factory_impl.hpp @@ -22,6 +22,10 @@ namespace kagome::host_api { class HostApiFactory; } +namespace kagome::runtime { + class CoreApiFactory; +} // namespace kagome::runtime + namespace kagome::storage::trie { class TrieStorage; class TrieSerializer; @@ -33,29 +37,30 @@ namespace kagome::runtime::wavm { class IntrinsicModule; struct ModuleParams; - struct ModuleCache; - class ModuleFactoryImpl final - : public ModuleFactory, - public std::enable_shared_from_this { + class ModuleFactoryImpl final : public ModuleFactory { public: ModuleFactoryImpl( std::shared_ptr compartment, std::shared_ptr module_params, std::shared_ptr host_api_factory, + std::shared_ptr core_factory, std::shared_ptr storage, std::shared_ptr serializer, std::shared_ptr intrinsic_module, - std::optional> module_cache, std::shared_ptr hasher); - CompilationOutcome> make( - common::BufferView code) const override; + std::optional compilerType() const override; + CompilationOutcome compile(std::filesystem::path path_compiled, + BufferView code) const override; + CompilationOutcome> loadCompiled( + std::filesystem::path path_compiled) const override; private: std::shared_ptr compartment_; std::shared_ptr module_params_; std::shared_ptr host_api_factory_; + std::shared_ptr core_factory_; std::shared_ptr storage_; std::shared_ptr serializer_; std::shared_ptr intrinsic_module_; diff --git a/core/runtime/wavm/wavm_external_memory_provider.cpp b/core/runtime/wavm/wavm_external_memory_provider.cpp index 09674c070d..e2acfb57a9 100644 --- a/core/runtime/wavm/wavm_external_memory_provider.cpp +++ b/core/runtime/wavm/wavm_external_memory_provider.cpp @@ -28,8 +28,8 @@ namespace kagome::runtime::wavm { outcome::result WavmExternalMemoryProvider::resetMemory( const MemoryConfig &config) { - auto handle = std::make_shared( - intrinsic_module_->getExportedMemory(), config); + auto handle = + std::make_shared(intrinsic_module_->getExportedMemory()); current_memory_ = std::make_shared( handle, std::make_unique(handle, config)); diff --git a/core/runtime/wavm/wavm_internal_memory_provider.cpp b/core/runtime/wavm/wavm_internal_memory_provider.cpp index 9820f690b6..0a3313d078 100644 --- a/core/runtime/wavm/wavm_internal_memory_provider.cpp +++ b/core/runtime/wavm/wavm_internal_memory_provider.cpp @@ -28,7 +28,7 @@ namespace kagome::runtime::wavm { outcome::result WavmInternalMemoryProvider::resetMemory( const MemoryConfig &config) { - auto handle = std::make_shared(memory_, config); + auto handle = std::make_shared(memory_); auto allocator = std::make_unique(handle, config); current_memory_ = std::make_unique(handle, std::move(allocator)); return outcome::success(); diff --git a/core/storage/rocksdb/rocksdb.cpp b/core/storage/rocksdb/rocksdb.cpp index df2d65f773..067ded4107 100644 --- a/core/storage/rocksdb/rocksdb.cpp +++ b/core/storage/rocksdb/rocksdb.cpp @@ -10,12 +10,12 @@ #include "filesystem/common.hpp" -#include "filesystem/directories.hpp" #include "storage/database_error.hpp" #include "storage/rocksdb/rocksdb_batch.hpp" #include "storage/rocksdb/rocksdb_cursor.hpp" #include "storage/rocksdb/rocksdb_spaces.hpp" #include "storage/rocksdb/rocksdb_util.hpp" +#include "utils/mkdirs.hpp" namespace kagome::storage { namespace fs = filesystem; @@ -36,9 +36,7 @@ namespace kagome::storage { rocksdb::Options options, uint32_t memory_budget_mib, bool prevent_destruction) { - if (!filesystem::createDirectoryRecursive(path)) { - return DatabaseError::DB_PATH_NOT_CREATED; - } + OUTCOME_TRY(mkdirs(path)); auto log = log::createLogger("RocksDB", "storage"); auto absolute_path = fs::absolute(path); diff --git a/core/utils/kagome_db_editor.cpp b/core/utils/kagome_db_editor.cpp index f5a1afdee7..9beb3caaf2 100644 --- a/core/utils/kagome_db_editor.cpp +++ b/core/utils/kagome_db_editor.cpp @@ -4,12 +4,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include -#include -#include -#include - #if defined(BACKWARD_HAS_BACKTRACE) #include #endif diff --git a/core/utils/mkdirs.hpp b/core/utils/mkdirs.hpp new file mode 100644 index 0000000000..14fab98426 --- /dev/null +++ b/core/utils/mkdirs.hpp @@ -0,0 +1,25 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +namespace kagome { + /** + * Fix ambiguity of `create_directories` returning `bool` which doesn't + * indicate error. + */ + inline outcome::result mkdirs(const std::filesystem::path &path) { + std::error_code ec; + std::filesystem::create_directories(path, ec); + if (ec) { + return ec; + } + return outcome::success(); + } +} // namespace kagome diff --git a/core/utils/read_file.hpp b/core/utils/read_file.hpp index 1ae540bf30..3549f36b93 100644 --- a/core/utils/read_file.hpp +++ b/core/utils/read_file.hpp @@ -9,8 +9,6 @@ #include #include -#include "common/buffer.hpp" - namespace kagome { template @@ -19,11 +17,13 @@ namespace kagome { template concept ByteContainer = requires(T t, std::streampos pos) { - { t.data() } -> StandardLayoutPointer; - { t.size() } -> std::convertible_to; - { t.resize(pos) }; - { t.clear() }; - }; + { t.data() } -> StandardLayoutPointer; + { + t.size() + } -> std::convertible_to; + { t.resize(pos) }; + { t.clear() }; + }; template bool readFile(Out &out, const std::filesystem::path &path) { diff --git a/core/utils/write_file.hpp b/core/utils/write_file.hpp new file mode 100644 index 0000000000..220172e5c3 --- /dev/null +++ b/core/utils/write_file.hpp @@ -0,0 +1,45 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include + +namespace kagome { + inline outcome::result writeFile(const std::filesystem::path &path, + std::string_view data) { + std::ofstream file{path, std::ios::binary}; + if (file and file.write(data.data(), data.size()) and file.flush()) { + return outcome::success(); + } + return std::errc{errno}; + } + + inline outcome::result writeFile(const std::filesystem::path &path, + qtils::BytesIn data) { + return writeFile(path, qtils::byte2str(data)); + } + + outcome::result writeFileTmp(const std::filesystem::path &path, + auto &&data) { + boost::system::error_code ec1; + auto tmp = + boost::filesystem::unique_path(path.native() + ".%%%%", ec1).native(); + if (ec1) { + return ec1; + } + OUTCOME_TRY(writeFile(tmp, data)); + std::error_code ec2; + std::filesystem::rename(tmp, path, ec2); + if (ec2) { + return ec2; + } + return outcome::success(); + } +} // namespace kagome diff --git a/test/core/consensus/timeline/timeline_test.cpp b/test/core/consensus/timeline/timeline_test.cpp index 14553abaa9..fa2e1296b0 100644 --- a/test/core/consensus/timeline/timeline_test.cpp +++ b/test/core/consensus/timeline/timeline_test.cpp @@ -8,6 +8,7 @@ #include +#include "common/main_thread_pool.hpp" #include "consensus/babe/types/babe_block_header.hpp" #include "consensus/babe/types/seal.hpp" #include "consensus/babe/types/slot_type.hpp" @@ -32,11 +33,13 @@ #include "testutil/literals.hpp" #include "testutil/prepare_loggers.hpp" +using kagome::TestThreadPool; using kagome::application::AppConfigurationMock; using kagome::application::AppStateManagerMock; using kagome::blockchain::BlockTreeMock; using kagome::clock::SystemClockMock; using kagome::common::Buffer; +using kagome::common::MainThreadPool; using kagome::consensus::ConsensusSelectorMock; using kagome::consensus::Duration; using kagome::consensus::EpochLength; @@ -161,10 +164,12 @@ class TimelineTest : public testing::Test { chain_sub_engine = std::make_shared(); state_sub_engine = std::make_shared(); + MainThreadPool main_thread{TestThreadPool{io}}; timeline = std::make_shared( app_config, app_state_manager, clock, + main_thread, slots_util, block_tree, consensus_selector, @@ -182,6 +187,8 @@ class TimelineTest : public testing::Test { core_api); } + std::shared_ptr io = + std::make_shared(); AppConfigurationMock app_config; std::shared_ptr app_state_manager; SystemClockMock clock; @@ -267,6 +274,8 @@ TEST_F(TimelineTest, SingleValidator) { timeline->start(); + io->run_one(); + EXPECT_TRUE(timeline->wasSynchronized()); EXPECT_EQ(timeline->getCurrentState(), SyncState::SYNCHRONIZED); diff --git a/test/core/host_api/misc_extension_test.cpp b/test/core/host_api/misc_extension_test.cpp index e574428eb3..ac2718ccb7 100644 --- a/test/core/host_api/misc_extension_test.cpp +++ b/test/core/host_api/misc_extension_test.cpp @@ -59,7 +59,12 @@ TEST_F(MiscExtensionTest, CoreVersion) { })); kagome::host_api::MiscExtension m{ - 42, std::make_shared(), memory_provider, core_factory}; + 42, + std::make_shared(), + memory_provider, + nullptr, + core_factory, + }; ASSERT_EQ(memory[m.ext_misc_runtime_version_version_1(memory["test"_v])], v1_enc); } diff --git a/test/core/parachain/pvf_test.cpp b/test/core/parachain/pvf_test.cpp index 9cea2e3556..4744cd5cb4 100644 --- a/test/core/parachain/pvf_test.cpp +++ b/test/core/parachain/pvf_test.cpp @@ -119,23 +119,24 @@ class PvfTest : public testing::Test { auto mockModule(uint8_t code_i) { Buffer code{code_i}; auto code_hash = hasher_->blake2b_256(code); - EXPECT_CALL(*module_factory_, make(MatchSpan(code))) - .WillRepeatedly([=, this] { - auto module = std::make_shared(); - ON_CALL(*module, instantiate()).WillByDefault([=, this] { - auto instance = std::make_shared(); - ON_CALL(*instance, callExportFunction(_, "validate_block", _)) - .WillByDefault( - Return(Buffer{scale::encode(ValidationResult{}).value()})); - ON_CALL(*instance, getCodeHash()).WillByDefault(Return(code_hash)); - ON_CALL(*ctx_factory, ephemeral(_, _, _)) - .WillByDefault(Invoke([instance]() { - return runtime::RuntimeContext::create_TEST(instance); - })); - return instance; - }); - return module; - }); + EXPECT_CALL(*module_factory_, compilerType()) + .WillRepeatedly(Return(std::nullopt)); + EXPECT_CALL(*module_factory_, compile(_, MatchSpan(code))) + .WillRepeatedly(Return(outcome::success())); + EXPECT_CALL(*module_factory_, loadCompiled(_)).WillRepeatedly([=] { + auto module = std::make_shared(); + ON_CALL(*module, instantiate()).WillByDefault([=] { + auto instance = std::make_shared(); + ON_CALL(*instance, callExportFunction(_, "validate_block", _)) + .WillByDefault( + Return(Buffer{scale::encode(ValidationResult{}).value()})); + ON_CALL(*instance, getCodeHash()).WillByDefault(Return(code_hash)); + EXPECT_CALL(*instance, stateless()) + .WillRepeatedly(Return(outcome::success())); + return instance; + }); + return module; + }); return [=, this](ParachainId para) { Pvf::PersistedValidationData pvd; pvd.max_pov_size = 1; @@ -173,11 +174,9 @@ class PvfTest : public testing::Test { TEST_F(PvfTest, InputEncodeDecode) { kagome::parachain::PvfWorkerInput input{ .engine = kagome::parachain::RuntimeEngine::kWasmEdgeInterpreted, - .runtime_code = {1, 2, 3, 4}, + .path_compiled = "/tmp/compiled", .function = "test", .params = {1, 2, 3, 4}, - .runtime_params{.memory_limits = {}}, - .cache_dir = "/tmp/kagome_pvf_test", .log_params = {}, }; ASSERT_OUTCOME_SUCCESS(buf, scale::encode(input)); diff --git a/test/core/runtime/binaryen/binaryen_runtime_test.hpp b/test/core/runtime/binaryen/binaryen_runtime_test.hpp index c6592562a6..c78fbe4d29 100644 --- a/test/core/runtime/binaryen/binaryen_runtime_test.hpp +++ b/test/core/runtime/binaryen/binaryen_runtime_test.hpp @@ -28,7 +28,7 @@ class BinaryenRuntimeTest : public RuntimeTestBase { auto instance_env_factory = std::make_shared( - trie_storage_, serializer_, host_api_factory_); + trie_storage_, serializer_, nullptr, host_api_factory_); auto module_factory = std::make_shared( diff --git a/test/core/runtime/binaryen/wasm_memory_test.cpp b/test/core/runtime/binaryen/wasm_memory_test.cpp index 38d8369e0b..9bf967c710 100644 --- a/test/core/runtime/binaryen/wasm_memory_test.cpp +++ b/test/core/runtime/binaryen/wasm_memory_test.cpp @@ -20,7 +20,6 @@ using namespace kagome; using common::literals::operator""_MB; using runtime::kDefaultHeapBase; -using runtime::kInitialMemorySize; using runtime::MemoryAllocator; using runtime::binaryen::MemoryImpl; @@ -37,23 +36,14 @@ class BinaryenMemoryHeapTest : public ::testing::Test { rei_ = std::make_unique(host_api); - runtime::MemoryConfig config{kDefaultHeapBase, {}}; - auto handle = std::make_shared(rei_->getMemory(), config); + runtime::MemoryConfig config{kDefaultHeapBase}; + auto handle = std::make_shared(rei_->getMemory()); auto allocator = std::make_unique(handle, config); allocator_ = allocator.get(); memory_ = std::make_unique(handle, std::move(allocator)); } - void TearDown() override { - memory_.reset(); - rei_.reset(); - } - - static constexpr uint32_t memory_size_ = kInitialMemorySize; - static constexpr uint32_t memory_page_limit_ = - 512_MB / runtime::kMemoryPageSize; - std::unique_ptr rei_; std::unique_ptr memory_; runtime::MemoryAllocatorImpl *allocator_; @@ -69,38 +59,6 @@ TEST_F(BinaryenMemoryHeapTest, Return0WhenSize0) { ASSERT_EQ(allocator_->getAllocatedChunkSize(ptr), 8); } -/** - * @given memory of size memory_size_ - * @when trying to allocate memory of size bigger than memory_size_ but less - * than max memory size - * @then -1 is not returned by allocate method indicating that memory was - * allocated - */ -TEST_F(BinaryenMemoryHeapTest, AllocatedMoreThanMemorySize) { - const auto allocated_memory = memory_size_ + 1; - ASSERT_NE(memory_->allocate(allocated_memory), -1); -} - -/** - * @given memory of size memory_size_ that is fully allocated - * @when trying to allocate memory of size bigger than - * (kMaxMemorySize-memory_size_) - * @then -1 is not returned by allocate method indicating that memory was not - * allocated - */ -TEST_F(BinaryenMemoryHeapTest, AllocatedTooBigMemoryFailed) { - // fully allocate memory - auto ptr1 = memory_->allocate(memory_size_); - // check that ptr1 is not -1, thus memory was allocated - ASSERT_NE(ptr1, -1); - - // The memory size that can be allocated is within interval (0, kMaxMemorySize - // - memory_size_]. Trying to allocate more - auto big_memory_size = - runtime::kMemoryPageSize * memory_page_limit_ - memory_size_ + 1; - EXPECT_ANY_THROW(memory_->allocate(big_memory_size)); -} - /** * @given memory with allocated memory chunk * @when this memory is deallocated @@ -116,33 +74,6 @@ TEST_F(BinaryenMemoryHeapTest, DeallocateExisingMemoryChunk) { memory_->deallocate(ptr1); } -/** - * @given memory with two memory chunk filling entire memory - * @when first memory chunk of size size1 is deallocated @and new memory chunk - * of the same size is trying to be allocated on that memory - * @then it is allocated on the place of the first memory chunk - */ -TEST_F(BinaryenMemoryHeapTest, AllocateAfterDeallocate) { - auto available_memory_size = kInitialMemorySize - kDefaultHeapBase; - - // two memory sizes totalling to the total memory size - const size_t size1 = available_memory_size / 3 + 1; - const size_t size2 = available_memory_size / 3 + 1; - - // allocate two memory chunks with total size equal to the memory size - auto pointer_of_first_allocation = memory_->allocate(size1); - memory_->allocate(size2); - - // deallocate first memory chunk - memory_->deallocate(pointer_of_first_allocation); - - // allocate new memory chunk - auto pointer_of_repeated_allocation = memory_->allocate(size1); - // expected that it will be allocated on the same place as the first memory - // chunk that was deallocated - ASSERT_EQ(pointer_of_first_allocation, pointer_of_repeated_allocation); -} - /** * @given full memory with different sized memory chunks * @when deallocate chunks in various ways: in order, reversed, single chunk diff --git a/test/core/runtime/instance_pool_test.cpp b/test/core/runtime/instance_pool_test.cpp index a584573a9b..4b9f5ddd4c 100644 --- a/test/core/runtime/instance_pool_test.cpp +++ b/test/core/runtime/instance_pool_test.cpp @@ -15,11 +15,13 @@ #include "runtime/common/runtime_instances_pool.hpp" +#include "mock/core/application/app_configuration_mock.hpp" #include "mock/core/runtime/instrument_wasm.hpp" #include "mock/core/runtime/module_factory_mock.hpp" #include "mock/core/runtime/module_instance_mock.hpp" #include "mock/core/runtime/module_mock.hpp" +using kagome::application::AppConfigurationMock; using kagome::common::Buffer; using kagome::runtime::DontInstrumentWasm; using kagome::runtime::ModuleFactoryMock; @@ -28,6 +30,7 @@ using kagome::runtime::ModuleMock; using kagome::runtime::RuntimeContext; using kagome::runtime::RuntimeInstancesPool; using kagome::runtime::RuntimeInstancesPoolImpl; +using testing::_; using testing::Return; RuntimeInstancesPool::CodeHash make_code_hash(int i) { @@ -44,33 +47,43 @@ TEST(InstancePoolTest, HeavilyMultithreadedCompilation) { auto module_instance_mock = std::make_shared(); auto module_mock = std::make_shared(); - ON_CALL(*module_mock, instantiate()) - .WillByDefault(testing::Return(module_instance_mock)); - - std::atomic_int times_make_called{}; + EXPECT_CALL(*module_mock, instantiate()) + .WillRepeatedly(testing::Return(module_instance_mock)); auto module_factory = std::make_shared(); - const Buffer code = "runtime_code"_buf; - ON_CALL(*module_factory, make(code.view())) - .WillByDefault(testing::Invoke([module_mock, ×_make_called](auto) { - std::this_thread::sleep_for(1s); - times_make_called++; - return module_mock; - })); + auto code = std::make_shared("runtime_code"_buf); static constexpr int THREAD_NUM = 100; static constexpr int POOL_SIZE = 10; + AppConfigurationMock app_config; + EXPECT_CALL(app_config, runtimeCacheDirPath()).WillRepeatedly(Return("/tmp")); auto pool = std::make_shared( - module_factory, std::make_shared(), POOL_SIZE); + app_config, + module_factory, + std::make_shared(), + POOL_SIZE); + + EXPECT_CALL(*module_factory, compilerType()) + .WillRepeatedly(Return(std::nullopt)); + EXPECT_CALL(*module_factory, compile(_, _)) + .Times(POOL_SIZE) + .WillRepeatedly([&] { + std::this_thread::sleep_for(1s); + return outcome::success(); + }); + EXPECT_CALL(*module_factory, loadCompiled(_)) + .Times(POOL_SIZE) + .WillRepeatedly([&] { + std::this_thread::sleep_for(1s); + return module_mock; + }); std::vector threads; for (int i = 0; i < THREAD_NUM; i++) { threads.emplace_back([&pool, &code, i]() { - ASSERT_OUTCOME_SUCCESS_TRY( - pool->instantiateFromCode(make_code_hash(i % POOL_SIZE), - code, - RuntimeContext::ContextParams{{{}, {}}})); + ASSERT_OUTCOME_SUCCESS_TRY(pool->instantiateFromCode( + make_code_hash(i % POOL_SIZE), [&] { return code; }, {})); }); } @@ -78,15 +91,13 @@ TEST(InstancePoolTest, HeavilyMultithreadedCompilation) { t.join(); } - // check that 'make' was only called 5 times - ASSERT_EQ(times_make_called.load(), POOL_SIZE); + testing::Mock::VerifyAndClearExpectations(pool.get()); // check that all POOL_SIZE instances are in cache for (int i = 0; i < POOL_SIZE; i++) { - ASSERT_OUTCOME_SUCCESS_TRY( - pool->instantiateFromCode(make_code_hash(i), - code.view(), - RuntimeContext::ContextParams{{{}, {}}})); + ASSERT_OUTCOME_SUCCESS_TRY(pool->instantiateFromCode( + make_code_hash(i), + []() -> decltype(code) { throw std::logic_error{"already compiled"}; }, + {})); } - ASSERT_EQ(times_make_called.load(), POOL_SIZE); } diff --git a/test/core/runtime/runtime_test_base.hpp b/test/core/runtime/runtime_test_base.hpp index 47a7737158..cb73722fa2 100644 --- a/test/core/runtime/runtime_test_base.hpp +++ b/test/core/runtime/runtime_test_base.hpp @@ -52,13 +52,15 @@ #include "testutil/outcome.hpp" #include "testutil/runtime/common/basic_code_provider.hpp" +using kagome::application::AppConfigurationMock; +using kagome::runtime::RuntimeInstancesPoolImpl; using testing::_; using testing::Invoke; using testing::Return; using namespace kagome; -class RuntimeTestBase : public ::testing::Test { +class RuntimeTestBaseImpl { public: using Buffer = common::Buffer; using Block = primitives::Block; @@ -135,7 +137,7 @@ class RuntimeTestBase : public ::testing::Test { virtual std::shared_ptr createModuleFactory() = 0; - void SetUp() override { + void SetUpImpl() { initStorage(); trie_storage_ = std::make_shared(); serializer_ = std::make_shared(); @@ -167,15 +169,23 @@ class RuntimeTestBase : public ::testing::Test { std::make_shared()) .value(); - auto module_repo = std::make_shared( - std::make_shared( - module_factory, std::make_shared()), - hasher_, - header_repo_, - upgrade_tracker, - trie_storage_, + auto wasm_cache_dir = + filesystem::temp_directory_path() / "runtime_test_base_cache"; + std::filesystem::create_directories(wasm_cache_dir); + EXPECT_CALL(app_config_, runtimeCacheDirPath()) + .WillOnce(Return(wasm_cache_dir)); + instance_pool_ = std::make_shared( + app_config_, module_factory, - wasm_provider_); + std::make_shared()); + auto module_repo = + std::make_shared(instance_pool_, + hasher_, + header_repo_, + upgrade_tracker, + trie_storage_, + module_factory, + wasm_provider_); ctx_factory_ = std::make_shared( module_repo, header_repo_); @@ -203,8 +213,7 @@ class RuntimeTestBase : public ::testing::Test { template void prepareStorageBatchExpectations(BatchMock &batch) { - ON_CALL(batch, tryGetMock(_)) - .WillByDefault(testing::Return(common::Buffer{})); + ON_CALL(batch, tryGetMock(_)).WillByDefault(testing::Return(std::nullopt)); ON_CALL(batch, put(_, _)) .WillByDefault(testing::Return(outcome::success())); ON_CALL(batch, remove(_)) @@ -254,6 +263,7 @@ class RuntimeTestBase : public ::testing::Test { } protected: + AppConfigurationMock app_config_; std::shared_ptr> header_repo_; std::shared_ptr wasm_provider_; @@ -266,4 +276,12 @@ class RuntimeTestBase : public ::testing::Test { std::shared_ptr offchain_worker_pool_; std::shared_ptr hasher_; std::shared_ptr host_api_factory_; + std::shared_ptr instance_pool_; +}; + +class RuntimeTestBase : public ::testing::Test, public RuntimeTestBaseImpl { + public: + void SetUp() override { + SetUpImpl(); + } }; diff --git a/test/core/runtime/wavm/CMakeLists.txt b/test/core/runtime/wavm/CMakeLists.txt index 0dc9449fbf..0d36515fb4 100644 --- a/test/core/runtime/wavm/CMakeLists.txt +++ b/test/core/runtime/wavm/CMakeLists.txt @@ -59,4 +59,5 @@ target_link_libraries(wavm_module_init_test runtime_properties_cache logger_for_tests module_repository + wavm_runtime_test ) diff --git a/test/core/runtime/wavm/core_integration_test.cpp b/test/core/runtime/wavm/core_integration_test.cpp index c49c5983e3..3466b7afad 100644 --- a/test/core/runtime/wavm/core_integration_test.cpp +++ b/test/core/runtime/wavm/core_integration_test.cpp @@ -34,14 +34,14 @@ using ::testing::Return; namespace fs = kagome::filesystem; -class CoreTest : public WavmRuntimeTest { +class CoreTest : public ::testing::Test, public WavmRuntimeTest { public: static void SetUpTestCase() { testutil::prepareLoggers(); } void SetUp() override { - WavmRuntimeTest::SetUp(); + SetUpImpl(); core_ = std::make_shared(executor_, nullptr, header_repo_, nullptr); diff --git a/test/core/runtime/wavm/wasm_memory_test.cpp b/test/core/runtime/wavm/wasm_memory_test.cpp index 058f2585f2..732413bcdd 100644 --- a/test/core/runtime/wavm/wasm_memory_test.cpp +++ b/test/core/runtime/wavm/wasm_memory_test.cpp @@ -17,7 +17,6 @@ using kagome::common::literals::operator""_MB; using kagome::runtime::kDefaultHeapBase; -using kagome::runtime::kInitialMemorySize; using kagome::runtime::Memory; using kagome::runtime::MemoryAllocator; using kagome::runtime::MemoryConfig; @@ -51,15 +50,13 @@ class WavmMemoryHeapTest : public ::testing::Test { WAVM::IR::FunctionType{}); instance_ = intr_module->instantiate(); - MemoryConfig config{kDefaultHeapBase, {}}; - auto handle = - std::make_shared(instance_->getExportedMemory(), config); + MemoryConfig config{kDefaultHeapBase}; + auto handle = std::make_shared(instance_->getExportedMemory()); auto allocator = std::make_unique(handle, config); memory_ = std::make_unique(handle, std::move(allocator)); } - static const uint32_t memory_size_ = kInitialMemorySize; static const uint32_t memory_page_limit_ = 512_MB; std::unique_ptr memory_; diff --git a/test/core/runtime/wavm/wavm_module_init_test.cpp b/test/core/runtime/wavm/wavm_module_init_test.cpp index 53232982b8..4566440f2c 100644 --- a/test/core/runtime/wavm/wavm_module_init_test.cpp +++ b/test/core/runtime/wavm/wavm_module_init_test.cpp @@ -4,163 +4,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include -#include - #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/ecdsa/ecdsa_provider_impl.hpp" //ecdsa_provider -#include "crypto/ed25519/ed25519_provider_impl.hpp" //ed25519_provider -#include "crypto/hasher/hasher_impl.hpp" //hasher -#include "crypto/key_store/key_store_impl.hpp" //crypto_store -#include "crypto/pbkdf2/impl/pbkdf2_provider_impl.hpp" //pbkdf2_provider -#include "crypto/secp256k1/secp256k1_provider_impl.hpp" //secp256k1_provider -#include "crypto/sr25519/sr25519_provider_impl.hpp" //sr25519_provider -#include "host_api/impl/host_api_factory_impl.hpp" // host_api_factory -#include "mock/core/application/app_state_manager_mock.hpp" -#include "offchain/impl/offchain_persistent_storage.hpp" //offchain_persistent_store -#include "offchain/impl/offchain_worker_pool_impl.hpp" //offchain_worker_pool -#include "runtime/common/runtime_properties_cache_impl.hpp" // cache -#include "runtime/executor.hpp" -#include "runtime/memory_provider.hpp" -#include "runtime/runtime_context.hpp" -#include "runtime/wavm/compartment_wrapper.hpp" // compartment -#include "runtime/wavm/instance_environment_factory.hpp" // instance_env_factory -#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_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 -#include "storage/trie/serialization/polkadot_codec.hpp" //codec -#include "storage/trie/serialization/trie_serializer_impl.hpp" // serializer -#include "storage/trie_pruner/impl/dummy_pruner.hpp" // trie_pruner -#include "testutil/outcome.hpp" -#include "testutil/prepare_loggers.hpp" -#include "testutil/runtime/common/basic_code_provider.hpp" +#include "core/runtime/wavm/wavm_runtime_test.hpp" #include "core/runtime/wavm/runtime_paths.hpp" +#include "testutil/prepare_loggers.hpp" -class WavmModuleInitTest : public ::testing::TestWithParam { +class WavmModuleInitTest : public ::testing::TestWithParam, + public WavmRuntimeTest { public: static void SetUpTestCase() { testutil::prepareLoggers(); } void SetUp() override { - std::filesystem::path base_path{std::filesystem::temp_directory_path() - / "wasm_module_init_test"}; - - auto compartment = - std::make_shared( - "WAVM Compartment"); - auto module_params = - std::make_shared(); - - auto trie_factory = - std::make_shared(); - auto codec = std::make_shared(); - auto storage = std::make_shared(); - auto node_storage_backend = - std::make_shared( - storage); - auto serializer = - std::make_shared( - trie_factory, codec, node_storage_backend); - auto state_pruner = - std::make_shared(); - std::shared_ptr trie_storage = - kagome::storage::trie::TrieStorageImpl::createEmpty( - trie_factory, codec, serializer, state_pruner) - .value(); - auto intrinsic_module = - std::make_shared( - compartment, module_params->intrinsicMemoryType); - - using namespace kagome::crypto; - auto hasher = std::make_shared(); - auto csprng = - std::make_shared(); - auto ecdsa_provider = std::make_shared(hasher); - auto ed25519_provider = std::make_shared(hasher); - auto sr25519_provider = std::make_shared(); - - auto secp256k1_provider = std::make_shared(); - auto pbkdf2_provider = std::make_shared(); - auto bip39_provider = - std::make_shared(std::move(pbkdf2_provider), hasher); - std::shared_ptr key_file_storage = - kagome::crypto::KeyFileStorage::createAt(base_path).value(); - KeyStore::Config config{base_path}; - auto key_store = std::make_shared( - std::make_unique>( - std::move(sr25519_provider), - bip39_provider, - csprng, - key_file_storage), - std::make_unique>( - ed25519_provider, bip39_provider, csprng, key_file_storage), - std::make_unique>( - std::move(ecdsa_provider), - bip39_provider, - csprng, - key_file_storage), - ed25519_provider, - std::make_shared(), - config); - - rocksdb::Options db_options{}; - db_options.create_if_missing = true; - std::shared_ptr database = - kagome::storage::RocksDb::create(base_path / "db", db_options).value(); - - auto header_repo = - std::make_shared( - database, hasher); - auto offchain_persistent_storage = - std::make_shared( - database); - auto offchain_worker_pool = - std::make_shared(); - - auto host_api_factory = - std::make_shared( - kagome::host_api::OffchainExtensionConfig{}, - sr25519_provider, - ecdsa_provider, - ed25519_provider, - secp256k1_provider, - hasher, - key_store, - offchain_persistent_storage, - offchain_worker_pool); - - auto cache = - std::make_shared(); - - module_factory_ = - std::make_shared( - compartment, - module_params, - host_api_factory, - trie_storage, - serializer, - intrinsic_module, - std::nullopt, - hasher); - - auto instance_env_factory = std::make_shared< - const kagome::runtime::wavm::InstanceEnvironmentFactory>( - trie_storage, serializer, host_api_factory, module_factory_); + SetUpImpl(); } - std::shared_ptr module_factory_; kagome::log::Logger log_ = kagome::log::createLogger("Test"); }; @@ -170,9 +31,13 @@ TEST_P(WavmModuleInitTest, DISABLED_SingleModule) { auto code_provider = std::make_shared( std::string(kBasePath) + std::string(wasm)); EXPECT_OUTCOME_TRUE(code, code_provider->getCodeAt({})); - EXPECT_OUTCOME_TRUE(runtime_context, - kagome::runtime::RuntimeContextFactory::fromCode( - *module_factory_, *code, {})); + auto code_hash = hasher_->blake2b_256(*code); + auto instance = + instance_pool_->instantiateFromCode(code_hash, [&] { return code; }, {}) + .value(); + EXPECT_OUTCOME_TRUE( + runtime_context, + kagome::runtime::RuntimeContextFactory::stateless(instance)); EXPECT_OUTCOME_TRUE(response, runtime_context.module_instance->callExportFunction( runtime_context, "Core_version", {})); diff --git a/test/core/runtime/wavm/wavm_runtime_test.hpp b/test/core/runtime/wavm/wavm_runtime_test.hpp index 8cd61ae8c6..a81d415abe 100644 --- a/test/core/runtime/wavm/wavm_runtime_test.hpp +++ b/test/core/runtime/wavm/wavm_runtime_test.hpp @@ -18,12 +18,11 @@ #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_cache.hpp" #include "runtime/wavm/module_factory_impl.hpp" #include "runtime/wavm/module_params.hpp" #include "runtime/wavm/wavm_external_memory_provider.hpp" -class WavmRuntimeTest : public RuntimeTestBase { +class WavmRuntimeTest : public RuntimeTestBaseImpl { public: std::shared_ptr createModuleFactory() override { @@ -46,15 +45,15 @@ class WavmRuntimeTest : public RuntimeTestBase { compartment, module_params, host_api_factory_, + nullptr, trie_storage_, serializer_, intrinsic_module, - std::nullopt, hasher_); auto instance_env_factory = std::make_shared( - trie_storage_, serializer_, host_api_factory_, module_factory); + trie_storage_, serializer_, host_api_factory_, nullptr); return module_factory; } diff --git a/test/external-project-test/src/main.cpp b/test/external-project-test/src/main.cpp index 7428d62b20..36d84617ca 100644 --- a/test/external-project-test/src/main.cpp +++ b/test/external-project-test/src/main.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ int main() { } kagome::log::setLoggingSystem(logging_system); + kagome::application::AppConfigurationImpl app_config; + rocksdb::Options db_options{}; db_options.create_if_missing = true; std::shared_ptr database = @@ -186,17 +189,29 @@ int main() { auto cache = std::make_shared(); + std::shared_ptr + runtime_instances_pool; + auto injector = boost::di::make_injector( + boost::di::bind().to( + [&](const auto &) { return runtime_instances_pool; })); + auto core_factory = std::make_shared( + hasher, + injector + .create>()); + auto instance_env_factory = std::make_shared( - trie_storage, serializer, host_api_factory); + trie_storage, serializer, core_factory, host_api_factory); auto module_factory = std::make_shared( instance_env_factory, trie_storage, hasher); - auto runtime_instances_pool = + runtime_instances_pool = std::make_shared( - module_factory, std::make_shared()); + app_config, + module_factory, + std::make_shared()); auto module_repo = std::make_shared( runtime_instances_pool, hasher, diff --git a/test/mock/core/runtime/binaryen_wasm_memory_factory_mock.hpp b/test/mock/core/runtime/binaryen_wasm_memory_factory_mock.hpp index de10a66f67..d4c6d3b6d3 100644 --- a/test/mock/core/runtime/binaryen_wasm_memory_factory_mock.hpp +++ b/test/mock/core/runtime/binaryen_wasm_memory_factory_mock.hpp @@ -18,8 +18,7 @@ namespace kagome::runtime::binaryen { MOCK_METHOD(std::unique_ptr, make, - (RuntimeExternalInterface::InternalMemory * memory, - const MemoryConfig &config), + (RuntimeExternalInterface::InternalMemory * memory), (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 ab638ddf54..b24d6b929c 100644 --- a/test/mock/core/runtime/core_api_factory_mock.hpp +++ b/test/mock/core/runtime/core_api_factory_mock.hpp @@ -19,8 +19,7 @@ namespace kagome::runtime { public: MOCK_METHOD(outcome::result>, make, - (std::shared_ptr hasher, - const std::vector &runtime_code), + (BufferView, std::shared_ptr), (const, override)); }; diff --git a/test/mock/core/runtime/module_factory_mock.hpp b/test/mock/core/runtime/module_factory_mock.hpp index 70a6cdb793..5eae958f4e 100644 --- a/test/mock/core/runtime/module_factory_mock.hpp +++ b/test/mock/core/runtime/module_factory_mock.hpp @@ -16,9 +16,17 @@ namespace kagome::runtime { class ModuleFactoryMock final : public ModuleFactory { public: + MOCK_METHOD(std::optional, + compilerType, + (), + (const, override)); + MOCK_METHOD((CompilationOutcome), + compile, + (std::filesystem::path, BufferView), + (const, override)); MOCK_METHOD((CompilationOutcome>), - make, - (common::BufferView), + loadCompiled, + (std::filesystem::path), (const, 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 c6f6f22f04..2c65f4b135 100644 --- a/test/mock/core/runtime/module_instance_mock.hpp +++ b/test/mock/core/runtime/module_instance_mock.hpp @@ -44,5 +44,7 @@ namespace kagome::runtime { (const, override)); MOCK_METHOD(outcome::result, resetEnvironment, (), (override)); + + MOCK_METHOD(outcome::result, stateless, (), (override)); }; } // namespace kagome::runtime diff --git a/test/mock/core/runtime/runtime_context_factory_mock.hpp b/test/mock/core/runtime/runtime_context_factory_mock.hpp index f69cd1a4b2..67bda591aa 100644 --- a/test/mock/core/runtime/runtime_context_factory_mock.hpp +++ b/test/mock/core/runtime/runtime_context_factory_mock.hpp @@ -17,8 +17,7 @@ namespace kagome::runtime { MOCK_METHOD(outcome::result, fromBatch, (std::shared_ptr module_instance, - std::shared_ptr batch, - ContextParams params), + std::shared_ptr batch), (const, override)); MOCK_METHOD( outcome::result, @@ -26,36 +25,32 @@ namespace kagome::runtime { (std::shared_ptr module_instance, const storage::trie::RootHash &state, std::optional> - changes_tracker_opt, - ContextParams params), + changes_tracker_opt), (const, override)); MOCK_METHOD( outcome::result, persistentAt, (const primitives::BlockHash &block_hash, std::optional> - changes_tracker_opt, - ContextParams params), + changes_tracker_opt), (const, override)); MOCK_METHOD(outcome::result, ephemeral, (std::shared_ptr module_instance, - const storage::trie::RootHash &state, - ContextParams params), + const storage::trie::RootHash &state), (const, override)); MOCK_METHOD(outcome::result, ephemeralAt, - (const primitives::BlockHash &block_hash, ContextParams params), + (const primitives::BlockHash &block_hash), (const, override)); MOCK_METHOD(outcome::result, ephemeralAt, (const primitives::BlockHash &block_hash, - const storage::trie::RootHash &state, - ContextParams params), + const storage::trie::RootHash &state), (const, override)); MOCK_METHOD(outcome::result, ephemeralAtGenesis, - (ContextParams params), + (), (const, override)); }; diff --git a/zombienet/polkadot/functional/0001-parachains-pvf.toml b/zombienet/polkadot/functional/0001-parachains-pvf.toml index 63eae2aa95..1ff353a556 100644 --- a/zombienet/polkadot/functional/0001-parachains-pvf.toml +++ b/zombienet/polkadot/functional/0001-parachains-pvf.toml @@ -26,15 +26,11 @@ requests = { memory = "2G", cpu = "1" } [[relaychain.nodes]] name = "ferdie" - command = "kagome" - prometheus_prefix = "kagome" - args = [ "--ferdie", "--wasm-execution Compiled", "-lparachain=debug", "-lruntime=debug"] + args = [ "--ferdie", "-lparachain=debug,runtime=debug"] [[relaychain.nodes]] name = "eve" - command = "kagome" - prometheus_prefix = "kagome" - args = [ "--eve", "--wasm-execution Compiled", "-lparachain=debug", "-lruntime=debug"] + args = [ "--eve", "-lparachain=debug,runtime=debug"] [[relaychain.nodes]] name = "one" @@ -46,7 +42,7 @@ requests = { memory = "2G", cpu = "1" } name = "two" command = "kagome" prometheus_prefix = "kagome" - args = [ "--two", "--wasm-execution Compiled", "-lparachain=debug", "-lruntime=debug"] + args = [ "--two", "--wasm-execution Compiled", "-lparachain=trace", "-lruntime=debug"] [[parachains]] id = 2000 diff --git a/zombienet/polkadot/functional/0001-parachains-pvf.zndsl b/zombienet/polkadot/functional/0001-parachains-pvf.zndsl index 8d77229cf1..a3279602ae 100644 --- a/zombienet/polkadot/functional/0001-parachains-pvf.zndsl +++ b/zombienet/polkadot/functional/0001-parachains-pvf.zndsl @@ -40,8 +40,8 @@ alice: reports histogram polkadot_pvf_preparation_time has at least 1 samples in bob: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds charlie: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds dave: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds -ferdie: reports histogram kagome_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds -eve: reports histogram kagome_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds +ferdie: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds +eve: reports histogram polkadot_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds one: reports histogram kagome_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds two: reports histogram kagome_pvf_preparation_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2", "3", "10"] within 10 seconds @@ -50,8 +50,8 @@ alice: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets bob: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds charlie: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds dave: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds -ferdie: reports histogram kagome_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds -eve: reports histogram kagome_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds +ferdie: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds +eve: reports histogram polkadot_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds one: reports histogram kagome_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds two: reports histogram kagome_pvf_preparation_time has 0 samples in buckets ["20", "30", "60", "120", "+Inf"] within 10 seconds @@ -68,8 +68,8 @@ alice: reports histogram polkadot_pvf_execution_time has at least 1 samples in b bob: reports histogram polkadot_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds charlie: reports histogram polkadot_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds dave: reports histogram polkadot_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds -ferdie: reports histogram kagome_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds -eve: reports histogram kagome_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds +ferdie: reports histogram polkadot_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds +eve: reports histogram polkadot_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds one: reports histogram kagome_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds two: reports histogram kagome_pvf_execution_time has at least 1 samples in buckets ["0.1", "0.5", "1", "2"] within 10 seconds @@ -78,7 +78,7 @@ alice: reports histogram polkadot_pvf_execution_time has 0 samples in buckets [" bob: reports histogram polkadot_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds charlie: reports histogram polkadot_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds dave: reports histogram polkadot_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds -ferdie: reports histogram kagome_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds -eve: reports histogram kagome_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds +ferdie: reports histogram polkadot_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds +eve: reports histogram polkadot_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds one: reports histogram kagome_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds two: reports histogram kagome_pvf_execution_time has 0 samples in buckets ["3", "4", "5", "6", "+Inf"] within 10 seconds