From 53c9c4ecc7375fba8f038f02474c4ef902334679 Mon Sep 17 00:00:00 2001 From: Ruslan Tushov Date: Wed, 22 Nov 2023 12:44:49 +0300 Subject: [PATCH 01/16] babe deadlock (#1872) Signed-off-by: turuslan --- .../babe/impl/babe_config_repository_impl.cpp | 78 +++++++++++-------- .../babe/impl/babe_config_repository_impl.hpp | 17 ++-- core/consensus/timeline/types.hpp | 4 +- core/utils/safe_object.hpp | 15 ++++ 4 files changed, 74 insertions(+), 40 deletions(-) diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index 2c360bbe47..2bfa2f1218 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -82,9 +82,11 @@ namespace kagome::consensus::babe { BOOST_ASSERT(header_repo_ != nullptr); BOOST_ASSERT(babe_api_ != nullptr); - if (auto r = indexer_.init(); not r) { - logger_->error("Indexer::init error: {}", r.error()); - } + SAFE_UNIQUE(indexer_) { + if (auto r = indexer_.init(); not r) { + logger_->error("Indexer::init error: {}", r.error()); + } + }; app_state_manager.takeControl(*this); } @@ -104,27 +106,29 @@ namespace kagome::consensus::babe { return false; } - std::unique_lock lock{indexer_mutex_}; auto finalized = block_tree_->getLastFinalized(); auto finalized_header = block_tree_->getBlockHeader(finalized.hash).value(); - if (finalized.number - indexer_.last_finalized_indexed_.number - > kMaxUnindexedBlocksNum - and trie_storage_->getEphemeralBatchAt(finalized_header.state_root)) { - warp(lock, finalized); - } + SAFE_UNIQUE(indexer_) { + if (finalized.number - indexer_.last_finalized_indexed_.number + > kMaxUnindexedBlocksNum + and trie_storage_->getEphemeralBatchAt(finalized_header.state_root)) { + warp(indexer_, finalized); + } - if (!timings_) { - auto genesis_res = config({block_tree_->getGenesisBlockHash(), 0}, false); - if (genesis_res.has_value()) { - auto &genesis = genesis_res.value(); - timings_.init(genesis->slot_duration, genesis->epoch_length); - SL_DEBUG(logger_, - "Timing was initialized: slot is {}ms, epoch is {} slots", - timings_.slot_duration.count(), - timings_.epoch_length); + if (!timings_) { + auto genesis_res = + config(indexer_, {block_tree_->getGenesisBlockHash(), 0}, false); + if (genesis_res.has_value()) { + auto &genesis = genesis_res.value(); + timings_.init(genesis->slot_duration, genesis->epoch_length); + SL_DEBUG(logger_, + "Timing was initialized: slot is {}ms, epoch is {} slots", + timings_.slot_duration.count(), + timings_.epoch_length); + } } - } + }; [[maybe_unused]] bool active_ = false; @@ -133,7 +137,10 @@ namespace kagome::consensus::babe { auto consensus = consensus_selector_.get()->getProductionConsensus(best); if (std::dynamic_pointer_cast(consensus)) { active_ = true; - if (auto res = config(best, true); not res and not config_warp_sync_) { + auto res = SAFE_UNIQUE(indexer_) { + return config(indexer_, best, true); + }; + if (not res and not config_warp_sync_) { SL_ERROR(logger_, "get config at best {} error: {}", best, res.error()); auto best_header = block_tree_->getBlockHeader(best.hash).value(); if (not trie_storage_->getEphemeralBatchAt(best_header.state_root)) { @@ -146,8 +153,10 @@ namespace kagome::consensus::babe { chain_sub_.onFinalize([weak{weak_from_this()}]() { if (auto self = weak.lock()) { - std::unique_lock lock{self->indexer_mutex_}; - self->indexer_.finalize(); + auto &indexer_ = self->indexer_; + SAFE_UNIQUE(indexer_) { + indexer_.finalize(); + }; } }); @@ -165,8 +174,9 @@ namespace kagome::consensus::babe { slots_util_.get()->slotToEpoch(parent_info, parent_slot)); epoch_changed = epoch_number != parent_epoch; } - std::unique_lock lock{indexer_mutex_}; - return config(parent_info, epoch_changed); + return SAFE_UNIQUE(indexer_) { + return config(indexer_, parent_info, epoch_changed); + }; } outcome::result BabeConfigRepositoryImpl::getFirstBlockSlotNumber( @@ -209,18 +219,20 @@ namespace kagome::consensus::babe { return slot1.value(); } - void BabeConfigRepositoryImpl::warp(std::unique_lock &lock, + void BabeConfigRepositoryImpl::warp(Indexer &indexer_, const primitives::BlockInfo &block) { indexer_.put(block, {}, true); } void BabeConfigRepositoryImpl::warp(const primitives::BlockInfo &block) { - std::unique_lock lock{indexer_mutex_}; - warp(lock, block); + SAFE_UNIQUE(indexer_) { + warp(indexer_, block); + }; } outcome::result> - BabeConfigRepositoryImpl::config(const primitives::BlockInfo &block, + BabeConfigRepositoryImpl::config(Indexer &indexer_, + const primitives::BlockInfo &block, bool next_epoch) const { auto descent = indexer_.descend(block); outcome::result cb_res = outcome::success(); @@ -262,7 +274,7 @@ namespace kagome::consensus::babe { OUTCOME_TRY(header, block_tree_->getBlockHeader(info.hash)); if (HasBabeConsensusDigest digests{header}) { if (not prev_state) { - BOOST_OUTCOME_TRY(prev_state, loadPrev(prev)); + BOOST_OUTCOME_TRY(prev_state, loadPrev(indexer_, prev)); } auto state = applyDigests(getConfig(*prev_state), digests); BabeIndexedValue value{ @@ -294,10 +306,10 @@ namespace kagome::consensus::babe { return *r->second.value->state; } if (next_epoch) { - OUTCOME_TRY(load(r->first, r->second)); + OUTCOME_TRY(load(indexer_, r->first, r->second)); return *r->second.value->next_state; } - return loadPrev(r->second.prev); + return loadPrev(indexer_, r->second.prev); } std::shared_ptr BabeConfigRepositoryImpl::applyDigests( @@ -320,6 +332,7 @@ namespace kagome::consensus::babe { } outcome::result BabeConfigRepositoryImpl::load( + Indexer &indexer_, const primitives::BlockInfo &block, blockchain::Indexed &item) const { if (not item.value->next_state) { @@ -339,6 +352,7 @@ namespace kagome::consensus::babe { outcome::result> BabeConfigRepositoryImpl::loadPrev( + Indexer &indexer_, const std::optional &prev) const { if (not prev) { return Error::PREVIOUS_NOT_FOUND; @@ -350,7 +364,7 @@ namespace kagome::consensus::babe { if (not r->value) { return Error::PREVIOUS_NOT_FOUND; } - OUTCOME_TRY(load(*prev, *r)); + OUTCOME_TRY(load(indexer_, *prev, *r)); return *r->value->next_state; } } // namespace kagome::consensus::babe diff --git a/core/consensus/babe/impl/babe_config_repository_impl.hpp b/core/consensus/babe/impl/babe_config_repository_impl.hpp index bbcbc559d0..ec1d2bc1b9 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.hpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.hpp @@ -8,8 +8,6 @@ #include "consensus/babe/babe_config_repository.hpp" -#include - #include "blockchain/indexer.hpp" #include "consensus/babe/has_babe_consensus_digest.hpp" #include "consensus/babe/types/scheduled_change.hpp" @@ -18,6 +16,7 @@ #include "primitives/block_data.hpp" #include "primitives/event_types.hpp" #include "storage/spaced_storage.hpp" +#include "utils/safe_object.hpp" namespace kagome::application { class AppStateManager; @@ -98,32 +97,36 @@ namespace kagome::consensus::babe { void warp(const primitives::BlockInfo &block) override; private: + using Indexer = blockchain::Indexer; + outcome::result getFirstBlockSlotNumber( const primitives::BlockInfo &parent_info) const; outcome::result> config( - const primitives::BlockInfo &block, bool next_epoch) const; + Indexer &indexer_, + const primitives::BlockInfo &block, + bool next_epoch) const; std::shared_ptr applyDigests( const NextConfigDataV1 &config, const HasBabeConsensusDigest &digests) const; outcome::result load( + Indexer &indexer_, const primitives::BlockInfo &block, blockchain::Indexed &item) const; outcome::result> loadPrev( + Indexer &indexer_, const std::optional &prev) const; - void warp(std::unique_lock &lock, - const primitives::BlockInfo &block); + void warp(Indexer &indexer_, const primitives::BlockInfo &block); std::shared_ptr persistent_storage_; bool config_warp_sync_; EpochTimings &timings_; std::shared_ptr block_tree_; - mutable std::mutex indexer_mutex_; - mutable blockchain::Indexer indexer_; + mutable SafeObject indexer_; std::shared_ptr header_repo_; LazySPtr consensus_selector_; std::shared_ptr babe_api_; diff --git a/core/consensus/timeline/types.hpp b/core/consensus/timeline/types.hpp index e417a5bc70..1e78686784 100644 --- a/core/consensus/timeline/types.hpp +++ b/core/consensus/timeline/types.hpp @@ -56,7 +56,9 @@ namespace kagome::consensus { *this); } - auto operator<=>(const SlotDuration &) const = default; + auto operator<=>(const SlotDuration &r) const { + return count() <=> r.count(); + } friend ::scale::ScaleEncoderStream &operator<<( ::scale::ScaleEncoderStream &s, const SlotDuration &duration) { diff --git a/core/utils/safe_object.hpp b/core/utils/safe_object.hpp index b4aa4107da..84b73e4f7e 100644 --- a/core/utils/safe_object.hpp +++ b/core/utils/safe_object.hpp @@ -10,6 +10,14 @@ #include #include #include +#include + +#define SAFE_UNIQUE_CAPTURE(x, ...) \ + x ^= __VA_ARGS__(typename std::remove_cvref_t::Type & x) +#define SAFE_SHARED_CAPTURE(x, ...) \ + x |= __VA_ARGS__(const typename std::remove_cvref_t::Type &x) +#define SAFE_UNIQUE(x) SAFE_UNIQUE_CAPTURE(x, [&]) +#define SAFE_SHARED(x) SAFE_SHARED_CAPTURE(x, [&]) // clang-format off /** @@ -55,6 +63,13 @@ struct SafeObject { return std::forward(f)(t_); } + auto operator^=(auto &&f) { + return exclusiveAccess(std::forward(f)); + } + auto operator|=(auto &&f) const { + return sharedAccess(std::forward(f)); + } + T &unsafeGet() { return t_; } From e9ece4cc4a8db88cacda028742202e621a54b137 Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Wed, 22 Nov 2023 12:58:58 +0300 Subject: [PATCH 02/16] Feature/stream hasher (#1799) * blake2b stream hasher Signed-off-by: iceseer --------- Signed-off-by: iceseer --- core/crypto/hasher/blake2b_stream_hasher.hpp | 43 +++++++++ core/crypto/murmur2.hpp | 92 +++++++++++++++++++ core/crypto/type_hasher.hpp | 78 ++++++++++++++++ core/network/types/collator_messages.hpp | 15 +-- .../approval/approval_distribution.cpp | 91 +++++++++--------- .../approval/approval_distribution.hpp | 32 ++++--- .../availability/recovery/recovery.hpp | 6 +- .../availability/recovery/recovery_impl.cpp | 5 +- .../availability/recovery/recovery_impl.hpp | 2 +- core/scale/kagome_scale.hpp | 18 ++++ core/utils/stringify.hpp | 9 ++ 11 files changed, 318 insertions(+), 73 deletions(-) create mode 100644 core/crypto/hasher/blake2b_stream_hasher.hpp create mode 100644 core/crypto/murmur2.hpp create mode 100644 core/crypto/type_hasher.hpp create mode 100644 core/utils/stringify.hpp diff --git a/core/crypto/hasher/blake2b_stream_hasher.hpp b/core/crypto/hasher/blake2b_stream_hasher.hpp new file mode 100644 index 0000000000..c4d599e2d3 --- /dev/null +++ b/core/crypto/hasher/blake2b_stream_hasher.hpp @@ -0,0 +1,43 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "crypto/blake2/blake2b.h" + +namespace kagome::crypto { + + template + struct Blake2b_StreamHasher final { + static constexpr size_t kOutlen = Outlen; + + static_assert((Outlen & (Outlen - 1)) == 0, "Outlen is pow 2"); + Blake2b_StreamHasher() { + initialized_ = (0 == blake2b_init(&ctx_, Outlen, nullptr, 0ull)); + } + + bool update(std::span buffer) { + if (!initialized_) { + return false; + } + blake2b_update(&ctx_, buffer.data(), buffer.size()); + return true; + } + + bool get_final(common::Blob &out) { + if (!initialized_) { + return false; + } + blake2b_final(&ctx_, out.data()); + return true; + } + + private: + blake2b_ctx ctx_; + bool initialized_{false}; + }; +} // namespace kagome::crypto diff --git a/core/crypto/murmur2.hpp b/core/crypto/murmur2.hpp new file mode 100644 index 0000000000..5e63e0fc70 --- /dev/null +++ b/core/crypto/murmur2.hpp @@ -0,0 +1,92 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace kagome::crypto { + + class CompileTimeHasher { + static constexpr /* h */ uint32_t __init__(uint32_t len) { + return 0 ^ len; + } + + template + static constexpr uint32_t __load__(__T &data, uint32_t offset) { + return data[offset + 0] | (data[offset + 1] << 8) + | (data[offset + 2] << 16) | (data[offset + 3] << 24); + } + + static constexpr uint32_t __mul__(uint32_t val1, uint32_t val2) { + return val1 * val2; + } + + static constexpr uint32_t __sl__(uint32_t value, uint32_t count) { + return (value << count); + } + + static constexpr uint32_t __sr__(uint32_t value, uint32_t count) { + return (value >> count); + } + + static constexpr uint32_t __xor__(uint32_t h, uint32_t k) { + return h ^ k; + } + + static constexpr uint32_t __xor_with_sr__(uint32_t k, uint32_t r) { + return __xor__(k, __sr__(k, r)); + } + + template + static constexpr /* h */ uint32_t __proc__(__Type &data, + uint32_t len, + uint32_t offset, + uint32_t h, + uint32_t m, + uint32_t r) { + return len >= 4 ? __proc__( + data, + len - 4, + offset + 4, + __xor__(__mul__(h, m), + __mul__(__xor_with_sr__( + __mul__(__load__(data, offset), m), r), + m)), + m, + r) + : len == 3 ? __proc__(data, + len - 1, + offset, + __xor__(h, __sl__(data[offset + 2], 16)), + m, + r) + : len == 2 ? __proc__(data, + len - 1, + offset, + __xor__(h, __sl__(data[offset + 1], 8)), + m, + r) + : len == 1 ? __proc__( + data, len - 1, offset, __xor__(h, data[offset]) * m, m, r) + : __xor__(__mul__(__xor_with_sr__(h, 13), m), + __sr__(__mul__(__xor_with_sr__(h, 13), m), 15)); + } + + public: + template + static constexpr uint32_t murmur2(__Type &data, uint32_t len) { + return __proc__(data, len, 0, __init__(len), 0x5bd1e995, 24); + } + }; + +} // namespace kagome::crypto + +#ifndef CompileTime_MURMUR2 +#define CompileTime_MURMUR2(x) \ + ::kagome::crypto::CompileTimeHasher::murmur2(x, \ + (sizeof(x) / sizeof(x[0])) - 1) +#endif // CompileTime_MURMUR2 diff --git a/core/crypto/type_hasher.hpp b/core/crypto/type_hasher.hpp new file mode 100644 index 0000000000..76a9058252 --- /dev/null +++ b/core/crypto/type_hasher.hpp @@ -0,0 +1,78 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include "crypto/hasher/blake2b_stream_hasher.hpp" +#include "scale/kagome_scale.hpp" + +namespace kagome::crypto { + + template + inline void hashTypes(H &hasher, common::Blob &out, T &&...t) { + kagome::scale::encode( + [&](const uint8_t *const val, size_t count) { + hasher.update({val, count}); + }, + std::forward(t)...); + + hasher.get_final(out); + } + + template + struct Hashed { + static_assert(N == 8 || N == 16 || N == 32 || N == 64, + "Unexpected hash size"); + using Type = std::decay_t; + using HashType = common::Blob; + + public: + template + Hashed(Args &&...args) : type_{std::forward(args)...} {} + + Hashed(const Hashed &c) = default; + Hashed(Hashed &&c) = default; + + Hashed &operator=(const Hashed &c) = default; + Hashed &operator=(Hashed &&c) = default; + + const Type &get() const { + return type_; + } + + const HashType *operator->() { + return &type_; + } + + Type &get_mut() { + opt_hash_ = std::nullopt; + return type_; + } + + const HashType &getHash() const { + if (!opt_hash_) { + HashType h; + StreamHasherT hasher_{}; + hashTypes(hasher_, h, type_); + opt_hash_ = std::move(h); + } + return *opt_hash_; + } + + private: + T type_; + mutable std::optional opt_hash_{}; + }; + + template + inline Hashed> create256Blake( + Args &&...args) { + return Hashed>{std::forward(args)...}; + } + +} // namespace kagome::crypto diff --git a/core/network/types/collator_messages.hpp b/core/network/types/collator_messages.hpp index f860435929..f1c1157468 100644 --- a/core/network/types/collator_messages.hpp +++ b/core/network/types/collator_messages.hpp @@ -111,11 +111,11 @@ namespace kagome::network { common::Buffer signable() const { return common::Buffer{ - scale::encode(relay_parent, - para_id, - persisted_data_hash, - pov_hash, - validation_code_hash) + ::scale::encode(relay_parent, + para_id, + persisted_data_hash, + pov_hash, + validation_code_hash) .value(), }; } @@ -518,11 +518,6 @@ namespace kagome::network { ViewUpdate /// view update message >; - inline CandidateHash candidateHash(const crypto::Hasher &hasher, - const CandidateReceipt &receipt) { - return hasher.blake2b_256(scale::encode(receipt).value()); - } - inline CandidateHash candidateHash(const crypto::Hasher &hasher, const CommittedCandidateReceipt &receipt) { auto commitments_hash = diff --git a/core/parachain/approval/approval_distribution.cpp b/core/parachain/approval/approval_distribution.cpp index 868f7ab395..5f6b6c95dc 100644 --- a/core/parachain/approval/approval_distribution.cpp +++ b/core/parachain/approval/approval_distribution.cpp @@ -669,7 +669,8 @@ namespace kagome::parachain { const auto &[validator_ix, assignments_key] = *founded_key; std::vector lc; - for (const auto &[candidate_hash, _, core_ix, group_ix] : leaving_cores) { + for (const auto &[hashed_candidate_receipt, core_ix, group_ix] : + leaving_cores) { if (isInBackingGroup(config.validator_groups, validator_ix, group_ix)) { continue; } @@ -914,11 +915,10 @@ namespace kagome::parachain { for (auto &candidate : candidates) { if (auto obj{boost::get(&candidate)}) { - included.emplace_back( - std::make_tuple(candidateHash(*hasher_, obj->candidate_receipt), - std::move(obj->candidate_receipt), - obj->core_index, - obj->group_index)); + included.emplace_back(std::make_tuple( + HashedCandidateReceipt{std::move(obj->candidate_receipt)}, + obj->core_index, + obj->group_index)); } } return included; @@ -943,7 +943,7 @@ namespace kagome::parachain { entries.reserve(block_info.included_candidates.size()); candidates.reserve(block_info.included_candidates.size()); - for (const auto &[candidateHash, candidateReceipt, coreIndex, groupIndex] : + for (const auto &[hashed_candidate_receipt, coreIndex, groupIndex] : block_info.included_candidates) { std::optional> assignment{}; if (auto assignment_it = block_info.assignments.find(coreIndex); @@ -951,16 +951,17 @@ namespace kagome::parachain { assignment = assignment_it->second; } - auto candidate_entry = - storedCandidateEntries().get_or_create(candidateHash, - candidateReceipt, - block_info.session_index, - block_info.n_validators); + auto candidate_entry = storedCandidateEntries().get_or_create( + hashed_candidate_receipt.getHash(), + hashed_candidate_receipt.get(), + block_info.session_index, + block_info.n_validators); candidate_entry.get().block_assignments.insert_or_assign( block_hash, ApprovalEntry(groupIndex, assignment, block_info.n_validators)); - entries.emplace_back(candidateHash, candidate_entry.get()); - candidates.emplace_back(coreIndex, candidateHash); + entries.emplace_back(hashed_candidate_receipt.getHash(), + candidate_entry.get()); + candidates.emplace_back(coreIndex, hashed_candidate_receipt.getHash()); } // Update the child index for the parent. @@ -1035,7 +1036,7 @@ namespace kagome::parachain { approved_bitfield.bits.end(), num_candidates, false); for (size_t ix = 0; ix < imported_block.included_candidates.size(); ++ix) { - const auto &[_0, _1, _2, backing_group] = + const auto &[_0, _1, backing_group] = imported_block.included_candidates[ix]; const size_t backing_group_size = ((backing_group < session_info->validator_groups.size()) @@ -1070,8 +1071,9 @@ namespace kagome::parachain { imported_block)); std::vector candidates; - for (const auto &[hash, _0, _1, _2] : imported_block.included_candidates) { - candidates.emplace_back(hash); + for (const auto &[hashed_candidate_receipt, _1, _2] : + imported_block.included_candidates) { + candidates.emplace_back(hashed_candidate_receipt.getHash()); } runNewBlocks( @@ -1269,19 +1271,17 @@ namespace kagome::parachain { void ApprovalDistribution::launch_approval( const RelayHash &relay_block_hash, - const CandidateHash &candidate_hash, SessionIndex session_index, - const network::CandidateReceipt &candidate_receipt, + const HashedCandidateReceipt &hashed_candidate, ValidatorIndex validator_index, Hash block_hash, GroupIndex backing_group) { auto on_recover_complete = [wself{weak_from_this()}, - candidate_receipt, + hashed_candidate{hashed_candidate}, block_hash, session_index, validator_index, - candidate_hash, relay_block_hash]( std::optional> &&opt_result) mutable { @@ -1290,12 +1290,13 @@ namespace kagome::parachain { 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, - candidate_hash, + hashed_candidate.getHash(), relay_block_hash); return; } @@ -1306,16 +1307,16 @@ namespace kagome::parachain { "candidate hash={}, relay block hash={})", opt_result->error(), session_index, - candidate_hash, + hashed_candidate.getHash(), relay_block_hash); self->dispute_coordinator_.get()->issueLocalStatement( - session_index, candidate_hash, candidate_receipt, false); + session_index, + hashed_candidate.getHash(), + hashed_candidate.get(), + false); return; } auto &available_data = opt_result->value(); - [[maybe_unused]] auto const para_id = - candidate_receipt.descriptor.para_id; - auto result = self->parachain_host_->validation_code_by_hash( block_hash, candidate_receipt.descriptor.validation_code_hash); if (result.has_error() || !result.value()) { @@ -1332,7 +1333,7 @@ namespace kagome::parachain { self->logger_->info( "Make exhaustive validation. Candidate hash {}, validator index " "{}, block hash {}", - candidate_hash, + hashed_candidate.getHash(), validator_index, block_hash); @@ -1345,7 +1346,7 @@ namespace kagome::parachain { validation_code); self->approvals_cache_.exclusiveAccess([&](auto &approvals_cache) { - if (auto it = approvals_cache.find(candidate_hash); + if (auto it = approvals_cache.find(hashed_candidate.getHash()); it != approvals_cache.end()) { ApprovalCache &ac = it->second; ac.approval_result = outcome; @@ -1353,14 +1354,17 @@ namespace kagome::parachain { }); if (ApprovalOutcome::Approved == outcome) { self->issue_approval( - candidate_hash, validator_index, relay_block_hash); + hashed_candidate.getHash(), validator_index, relay_block_hash); } else if (ApprovalOutcome::Failed == outcome) { self->dispute_coordinator_.get()->issueLocalStatement( - session_index, candidate_hash, candidate_receipt, false); + session_index, + hashed_candidate.getHash(), + candidate_receipt, + false); } }; - recovery_->recover(candidate_receipt, + recovery_->recover(hashed_candidate, session_index, backing_group, std::move(on_recover_complete)); @@ -1482,7 +1486,7 @@ namespace kagome::parachain { "Imported assignment. (validator={}, candidate hash={}, para id={})", assignment.validator, assigned_candidate_hash, - candidate_entry.candidate.descriptor.para_id); + candidate_entry.candidate.get().descriptor.para_id); res = AssignmentCheckResult::Accepted; } @@ -1578,7 +1582,7 @@ namespace kagome::parachain { approval.payload.ix, pubkey, approved_candidate_hash, - candidate_entry.candidate.descriptor.para_id); + candidate_entry.candidate.get().descriptor.para_id); advance_approval_state(block_entry, approved_candidate_hash, candidate_entry, @@ -2382,13 +2386,12 @@ namespace kagome::parachain { } void ApprovalDistribution::runLaunchApproval( - const CandidateHash &candidate_hash, const approval::IndirectAssignmentCert &indirect_cert, DelayTranche assignment_tranche, const RelayHash &relay_block_hash, CandidateIndex candidate_index, SessionIndex session, - const network::CandidateReceipt &candidate, + const HashedCandidateReceipt &hashed_candidate, GroupIndex backing_group) { /// TODO(iceseer): don't launch approval work if the node is syncing. const auto &block_hash = indirect_cert.block_hash; @@ -2412,13 +2415,13 @@ namespace kagome::parachain { std::optional approval_state = approvals_cache_.exclusiveAccess( [&](auto &approvals_cache) -> std::optional { - if (auto it = approvals_cache.find(candidate_hash); + if (auto it = approvals_cache.find(hashed_candidate.getHash()); it != approvals_cache.end()) { it->second.blocks_.insert(relay_block_hash); return it->second.approval_result; } approvals_cache.emplace( - candidate_hash, + hashed_candidate.getHash(), ApprovalCache{ .blocks_ = {relay_block_hash}, .approval_result = ApprovalOutcome::Failed, @@ -2428,14 +2431,13 @@ namespace kagome::parachain { if (!approval_state) { launch_approval(relay_block_hash, - candidate_hash, session, - candidate, + hashed_candidate, validator_index, block_hash, backing_group); } else if (*approval_state == ApprovalOutcome::Approved) { - issue_approval(candidate_hash, validator_index, block_hash); + issue_approval(hashed_candidate.getHash(), validator_index, block_hash); } } @@ -2762,7 +2764,7 @@ namespace kagome::parachain { const auto should_trigger = shouldTriggerAssignment( approval_entry, candidate_entry, tta, tranche_now); const auto backing_group = approval_entry.backing_group; - const auto &candidate_receipt = candidate_entry.candidate; + const auto &candidate_receipt = candidate_entry.candidate.get(); ApprovalEntry::MaybeCert maybe_cert{}; if (should_trigger) { @@ -2791,13 +2793,12 @@ namespace kagome::parachain { candidate_receipt.descriptor.para_id, block_hash); - runLaunchApproval(candidate_hash, - indirect_cert, + runLaunchApproval(indirect_cert, tranche, block_hash, CandidateIndex(*i), block_entry.session, - candidate_receipt, + candidate_entry.candidate, backing_group); } } diff --git a/core/parachain/approval/approval_distribution.hpp b/core/parachain/approval/approval_distribution.hpp index ff46d5976a..5224a7c402 100644 --- a/core/parachain/approval/approval_distribution.hpp +++ b/core/parachain/approval/approval_distribution.hpp @@ -23,6 +23,7 @@ #include "consensus/timeline/types.hpp" #include "crypto/crypto_store/key_file_storage.hpp" #include "crypto/crypto_store/session_keys.hpp" +#include "crypto/type_hasher.hpp" #include "dispute_coordinator/dispute_coordinator.hpp" #include "injector/lazy.hpp" #include "network/peer_view.hpp" @@ -83,6 +84,9 @@ namespace kagome::parachain { bool triggered; /// Whether the assignment has been triggered already. }; + using HashedCandidateReceipt = + crypto::Hashed; + /// Metadata regarding a specific tranche of assignments for a specific /// candidate. struct TrancheEntry { @@ -201,20 +205,26 @@ namespace kagome::parachain { }; struct CandidateEntry { - network::CandidateReceipt candidate; + HashedCandidateReceipt candidate; SessionIndex session; // Assignments are based on blocks, so we need to track assignments // separately based on the block we are looking at. std::unordered_map block_assignments; scale::BitVec approvals; - CandidateEntry(const network::CandidateReceipt &receipt, + CandidateEntry(const HashedCandidateReceipt &hashed_receipt, SessionIndex session_index, size_t approvals_size) - : candidate(receipt), session(session_index) { + : candidate(hashed_receipt), session(session_index) { approvals.bits.insert(approvals.bits.end(), approvals_size, false); } + CandidateEntry(const network::CandidateReceipt &receipt, + SessionIndex session_index, + size_t approvals_size) + : CandidateEntry( + HashedCandidateReceipt{receipt}, session_index, approvals_size) {} + std::optional> approval_entry( const network::RelayHash &relay_hash) { if (auto it = block_assignments.find(relay_hash); @@ -254,8 +264,9 @@ namespace kagome::parachain { return true; }; - return candidate == c.candidate && session == c.session - && approvals == c.approvals && block_assignments_eq(); + return candidate.getHash() == c.candidate.getHash() + && session == c.session && approvals == c.approvals + && block_assignments_eq(); } }; @@ -283,10 +294,7 @@ namespace kagome::parachain { bool prepare(); using CandidateIncludedList = - std::vector>; + std::vector>; using AssignmentsList = std::unordered_map; static AssignmentsList compute_assignments( @@ -606,9 +614,8 @@ namespace kagome::parachain { const CandidateHash &candidate_hash); void launch_approval(const RelayHash &relay_block_hash, - const CandidateHash &candidate_hash, SessionIndex session_index, - const network::CandidateReceipt &candidate, + const HashedCandidateReceipt &hashed_receipt, ValidatorIndex validator_index, Hash block_hash, GroupIndex backing_group); @@ -618,13 +625,12 @@ namespace kagome::parachain { const RelayHash &block_hash); void runLaunchApproval( - const CandidateHash &candidate_hash, const approval::IndirectAssignmentCert &indirect_cert, DelayTranche assignment_tranche, const RelayHash &relay_block_hash, CandidateIndex candidate_index, SessionIndex session, - const network::CandidateReceipt &candidate, + const HashedCandidateReceipt &hashed_candidate, GroupIndex backing_group); void runNewBlocks(approval::BlockApprovalMeta &&approval_meta, diff --git a/core/parachain/availability/recovery/recovery.hpp b/core/parachain/availability/recovery/recovery.hpp index 125466d3bc..2834199d6f 100644 --- a/core/parachain/availability/recovery/recovery.hpp +++ b/core/parachain/availability/recovery/recovery.hpp @@ -6,6 +6,7 @@ #pragma once +#include "crypto/type_hasher.hpp" #include "runtime/runtime_api/parachain_host_types.hpp" namespace kagome::parachain { @@ -18,12 +19,13 @@ namespace kagome::parachain { using AvailableData = runtime::AvailableData; using Cb = std::function>)>; - using CandidateReceipt = network::CandidateReceipt; + using HashedCandidateReceipt = + crypto::Hashed; virtual ~Recovery() = default; virtual void remove(const CandidateHash &candidate) = 0; - virtual void recover(CandidateReceipt receipt, + virtual void recover(const HashedCandidateReceipt &hashed_receipt, SessionIndex session_index, std::optional backing_group, Cb cb) = 0; diff --git a/core/parachain/availability/recovery/recovery_impl.cpp b/core/parachain/availability/recovery/recovery_impl.cpp index 637c3de630..7a699d7472 100644 --- a/core/parachain/availability/recovery/recovery_impl.cpp +++ b/core/parachain/availability/recovery/recovery_impl.cpp @@ -32,12 +32,13 @@ namespace kagome::parachain { cached_.erase(candidate); } - void RecoveryImpl::recover(CandidateReceipt receipt, + void RecoveryImpl::recover(const HashedCandidateReceipt &hashed_receipt, SessionIndex session_index, std::optional backing_group, Cb cb) { std::unique_lock lock{mutex_}; - auto candidate_hash = candidateHash(*hasher_, receipt); + const auto &receipt = hashed_receipt.get(); + const auto &candidate_hash = hashed_receipt.getHash(); if (auto it = cached_.find(candidate_hash); it != cached_.end()) { auto r = it->second; lock.unlock(); diff --git a/core/parachain/availability/recovery/recovery_impl.hpp b/core/parachain/availability/recovery/recovery_impl.hpp index 97dee77627..89caae4503 100644 --- a/core/parachain/availability/recovery/recovery_impl.hpp +++ b/core/parachain/availability/recovery/recovery_impl.hpp @@ -29,7 +29,7 @@ namespace kagome::parachain { std::shared_ptr query_audi, std::shared_ptr router); - void recover(CandidateReceipt receipt, + void recover(const HashedCandidateReceipt &hashed_receipt, SessionIndex session_index, std::optional backing_group, Cb cb) override; diff --git a/core/scale/kagome_scale.hpp b/core/scale/kagome_scale.hpp index 0bdbc36c2a..ea9652633d 100644 --- a/core/scale/kagome_scale.hpp +++ b/core/scale/kagome_scale.hpp @@ -6,18 +6,28 @@ #ifndef KAGOME_KAGOME_SCALE_HPP #define KAGOME_KAGOME_SCALE_HPP +#include #include #include "common/blob.hpp" #include "consensus/babe/types/babe_block_header.hpp" #include "consensus/babe/types/seal.hpp" #include "network/types/blocks_response.hpp" +#include "network/types/roles.hpp" #include "primitives/block_header.hpp" #include "primitives/block_id.hpp" #include "primitives/justification.hpp" #include "scale/encode_append.hpp" +#include "scale/libp2p_types.hpp" namespace kagome::scale { using CompactInteger = ::scale::CompactInteger; + using BitVec = ::scale::BitVec; + using ScaleDecoderStream = ::scale::ScaleDecoderStream; + using ScaleEncoderStream = ::scale::ScaleEncoderStream; + using PeerInfoSerializable = ::scale::PeerInfoSerializable; + using DecodeError = ::scale::DecodeError; + + using ::scale::decode; template constexpr void encode(const F &func, const primitives::BlockHeader &bh); @@ -35,6 +45,9 @@ namespace kagome::scale { template constexpr void encode(const F &func, const common::SLBuffer &c); + template + constexpr void encode(const F &func, const network::Roles &c); + template constexpr void encode(const F &func, const primitives::Other &c); @@ -139,6 +152,11 @@ namespace kagome::scale { putByte(func, c.v.data(), c.v.size()); } + template + constexpr void encode(const F &func, const network::Roles &c) { + encode(func, c.value); + } + } // namespace kagome::scale #endif // KAGOME_KAGOME_SCALE_HPP diff --git a/core/utils/stringify.hpp b/core/utils/stringify.hpp new file mode 100644 index 0000000000..dcd6e2fcb7 --- /dev/null +++ b/core/utils/stringify.hpp @@ -0,0 +1,9 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ +#pragma once + +#define XSTRINGIFY(s) STRINGIFY(s) +#define STRINGIFY(s) #s From 96bb9032d880efc835d9cc95de25ef552b46a6fa Mon Sep 17 00:00:00 2001 From: Dmitriy Khaustov Date: Wed, 22 Nov 2023 13:28:57 +0300 Subject: [PATCH 03/16] Fix: babe secondary slot-leadership (#1879) * fix: babe secondary slot leadership * fix: babe lottery log Signed-off-by: Dmitriy Khaustov aka xDimon --- core/consensus/babe/impl/babe.cpp | 18 ++++++++++-------- core/consensus/babe/impl/babe_lottery_impl.cpp | 2 +- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/core/consensus/babe/impl/babe.cpp b/core/consensus/babe/impl/babe.cpp index fbd4ff65c7..ada3df1066 100644 --- a/core/consensus/babe/impl/babe.cpp +++ b/core/consensus/babe/impl/babe.cpp @@ -189,31 +189,33 @@ namespace kagome::consensus::babe { "Probably authority list has changed."); } } else { - SL_VERBOSE(log_, "Node is active validator in epoch {}", epoch_); + SL_VERBOSE(log_, "Node is active validator in epoch {}", epoch); } } if (not is_active_validator_) { - SL_TRACE(log_, "Node is not active validator in epoch {}", epoch_); + SL_TRACE(log_, "Node is not active validator in epoch {}", epoch); return SlotLeadershipError::NO_VALIDATOR; } if (not checkSlotLeadership(best_block, slot)) { SL_TRACE( - log_, "Node is not slot leader in slot {} epoch {}", slot_, epoch_); + log_, "Node is not slot leader in slot {} epoch {}", slot, epoch); return SlotLeadershipError::NO_SLOT_LEADER; } + SL_DEBUG(log_, + "Node is leader in current slot {} epoch {}; Authority {}", + slot, + epoch, + slot_leadership_.keypair->public_key); + + // Init context parent_ = best_block; slot_timestamp_ = slot_timestamp; slot_ = slot; epoch_ = epoch; - SL_DEBUG(log_, - "Node is leader in current slot {} epoch {}; Authority {}", - slot_, - epoch_, - slot_leadership_.keypair->public_key); return processSlotLeadership(); } diff --git a/core/consensus/babe/impl/babe_lottery_impl.cpp b/core/consensus/babe/impl/babe_lottery_impl.cpp index 2f1e909481..d1bf56db3b 100644 --- a/core/consensus/babe/impl/babe_lottery_impl.cpp +++ b/core/consensus/babe/impl/babe_lottery_impl.cpp @@ -95,7 +95,7 @@ namespace kagome::consensus::babe { } // Index of secondary leader - auto auth_index_of_leader = le_bytes_to_uint64(hasher_->blake2b_64( + auto auth_index_of_leader = be_bytes_to_uint256(hasher_->blake2b_256( scale::encode(randomness_, slot).value())) % auth_number_; From 6189b219b1d1ecfbbf3daaad5f0e624358e74141 Mon Sep 17 00:00:00 2001 From: Ruslan Tushov Date: Wed, 22 Nov 2023 15:23:39 +0300 Subject: [PATCH 04/16] wavm stub (#1876) Signed-off-by: turuslan --- .../wavm/intrinsics/intrinsic_functions.cpp | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/core/runtime/wavm/intrinsics/intrinsic_functions.cpp b/core/runtime/wavm/intrinsics/intrinsic_functions.cpp index 417cd96f91..6eb9c49b6b 100644 --- a/core/runtime/wavm/intrinsics/intrinsic_functions.cpp +++ b/core/runtime/wavm/intrinsics/intrinsic_functions.cpp @@ -45,7 +45,7 @@ namespace kagome::runtime::wavm { Result cName(WAVM::Runtime::ContextRuntimeData *contextRuntimeData, \ ##__VA_ARGS__) { \ logger->warn("Unimplemented Host API function " #cName " was called"); \ - return Result(); \ + throw std::runtime_error{#cName " not implemented"}; \ } WAVM_DEFINE_INTRINSIC_FUNCTION(void, @@ -743,6 +743,11 @@ namespace kagome::runtime::wavm { WAVM::I32, WAVM::I32) + WAVM_DEFINE_INTRINSIC_FUNCTION_STUB(void, + ext_transaction_index_renew_version_1, + WAVM::I32, + WAVM::I32) + WAVM_DEFINE_INTRINSIC_FUNCTION_STUB( void, ext_benchmarking_add_to_whitelist_version_1, WAVM::I64) @@ -776,6 +781,15 @@ namespace kagome::runtime::wavm { return peekHostApi()->ext_trie_blake2_256_root_version_1(values_data); } + WAVM_DEFINE_INTRINSIC_FUNCTION_STUB( + WAVM::I32, + ext_trie_blake2_256_verify_proof_version_2, + WAVM::I32, + WAVM::I64, + WAVM::I64, + WAVM::I64, + WAVM::I32) + void registerHostApiMethods(IntrinsicModule &module) { if (logger == nullptr) { logger = log::createLogger("Host API wrappers", "wavm"); @@ -843,6 +857,7 @@ namespace kagome::runtime::wavm { REGISTER_HOST_INTRINSIC(I32, ext_trie_blake2_256_ordered_root_version_1, I64) REGISTER_HOST_INTRINSIC(I32, ext_trie_blake2_256_ordered_root_version_2, I64, I32) REGISTER_HOST_INTRINSIC(I32, ext_trie_blake2_256_root_version_1, I64) + REGISTER_HOST_INTRINSIC(I32, ext_trie_blake2_256_verify_proof_version_2,I32, I64, I64, I64, I32) REGISTER_HOST_INTRINSIC(I64, ext_crypto_ed25519_public_keys_version_1, I32) REGISTER_HOST_INTRINSIC(I64, ext_crypto_ed25519_sign_version_1, I32, I32, I64) REGISTER_HOST_INTRINSIC(I64, ext_crypto_secp256k1_ecdsa_recover_compressed_version_1, I32, I32) @@ -892,6 +907,7 @@ namespace kagome::runtime::wavm { REGISTER_HOST_INTRINSIC( , ext_evm_ext_runtime_event_version_1, I64) REGISTER_HOST_INTRINSIC(I64, ext_evm_ext_step_event_filter_version_1) REGISTER_HOST_INTRINSIC( , ext_transaction_index_index_version_1, I32, I32, I32) + REGISTER_HOST_INTRINSIC( , ext_transaction_index_renew_version_1, I32, I32) REGISTER_HOST_INTRINSIC( , ext_benchmarking_add_to_whitelist_version_1, I64) REGISTER_HOST_INTRINSIC( , ext_benchmarking_commit_db_version_1) From 52ebab671a711f4f0bc7c9735016a2dc4f69255f Mon Sep 17 00:00:00 2001 From: Alexander Lednev <57529355+iceseer@users.noreply.github.com> Date: Wed, 22 Nov 2023 18:14:22 +0300 Subject: [PATCH 05/16] build fix (#1880) * build fix Signed-off-by: iceseer * fix assignments test Signed-off-by: iceseer --------- Signed-off-by: iceseer --- core/crypto/hasher/blake2b_stream_hasher.hpp | 1 + .../approval/approval_distribution.hpp | 4 +- .../availability/recovery/recovery.hpp | 4 +- test/core/parachain/assignments.cpp | 54 +++++++------------ 4 files changed, 25 insertions(+), 38 deletions(-) diff --git a/core/crypto/hasher/blake2b_stream_hasher.hpp b/core/crypto/hasher/blake2b_stream_hasher.hpp index c4d599e2d3..3e7e49af04 100644 --- a/core/crypto/hasher/blake2b_stream_hasher.hpp +++ b/core/crypto/hasher/blake2b_stream_hasher.hpp @@ -7,6 +7,7 @@ #pragma once #include +#include "common/blob.hpp" #include "crypto/blake2/blake2b.h" namespace kagome::crypto { diff --git a/core/parachain/approval/approval_distribution.hpp b/core/parachain/approval/approval_distribution.hpp index 5224a7c402..a38e853801 100644 --- a/core/parachain/approval/approval_distribution.hpp +++ b/core/parachain/approval/approval_distribution.hpp @@ -84,8 +84,8 @@ namespace kagome::parachain { bool triggered; /// Whether the assignment has been triggered already. }; - using HashedCandidateReceipt = - crypto::Hashed; + using HashedCandidateReceipt = crypto:: + Hashed>; /// Metadata regarding a specific tranche of assignments for a specific /// candidate. diff --git a/core/parachain/availability/recovery/recovery.hpp b/core/parachain/availability/recovery/recovery.hpp index 2834199d6f..c67e0b52d7 100644 --- a/core/parachain/availability/recovery/recovery.hpp +++ b/core/parachain/availability/recovery/recovery.hpp @@ -19,8 +19,8 @@ namespace kagome::parachain { using AvailableData = runtime::AvailableData; using Cb = std::function>)>; - using HashedCandidateReceipt = - crypto::Hashed; + using HashedCandidateReceipt = crypto:: + Hashed>; virtual ~Recovery() = default; diff --git a/test/core/parachain/assignments.cpp b/test/core/parachain/assignments.cpp index 015ba09ac6..46c0edb0f4 100644 --- a/test/core/parachain/assignments.cpp +++ b/test/core/parachain/assignments.cpp @@ -125,15 +125,6 @@ TEST_F(AssignmentsTest, assign_to_nonzero_core) { auto asgn_keys = assignment_keys_plus_random(cs, {"//Alice", "//Bob", "//Charlie"}, 0ull); - auto c_a = - kagome::common::Hash256::fromHexWithPrefix( - "0x0000000000000000000000000000000000000000000000000000000000000000") - .value(); - auto c_b = - kagome::common::Hash256::fromHexWithPrefix( - "0x0101010101010101010101010101010101010101010101010101010101010101") - .value(); - ::RelayVRFStory vrf_story; ::memset(vrf_story.data, 42, sizeof(vrf_story.data)); @@ -152,14 +143,16 @@ TEST_F(AssignmentsTest, assign_to_nonzero_core) { si.n_delay_tranches = 40; kagome::parachain::ApprovalDistribution::CandidateIncludedList leaving_cores = - {std::make_tuple(c_a, - kagome::network::CandidateReceipt{}, - (kagome::parachain::CoreIndex)0, - (kagome::parachain::GroupIndex)0), - std::make_tuple(c_b, - kagome::network::CandidateReceipt{}, - (kagome::parachain::CoreIndex)1, - (kagome::parachain::GroupIndex)1)}; + {std::make_tuple( + kagome::parachain::ApprovalDistribution::HashedCandidateReceipt{ + kagome::network::CandidateReceipt{}}, + (kagome::parachain::CoreIndex)0, + (kagome::parachain::GroupIndex)0), + std::make_tuple( + kagome::parachain::ApprovalDistribution::HashedCandidateReceipt{ + kagome::network::CandidateReceipt{}}, + (kagome::parachain::CoreIndex)1, + (kagome::parachain::GroupIndex)1)}; auto assignments = kagome::parachain::ApprovalDistribution::compute_assignments( cs, si, vrf_story, leaving_cores); @@ -203,15 +196,6 @@ TEST_F(AssignmentsTest, assignments_produced_for_non_backing) { auto asgn_keys = assignment_keys_plus_random(cs, {"//Alice", "//Bob", "//Charlie"}, 0ull); - auto c_a = - kagome::common::Hash256::fromHexWithPrefix( - "0x0000000000000000000000000000000000000000000000000000000000000000") - .value(); - auto c_b = - kagome::common::Hash256::fromHexWithPrefix( - "0x0101010101010101010101010101010101010101010101010101010101010101") - .value(); - ::RelayVRFStory vrf_story; ::memset(vrf_story.data, 42, sizeof(vrf_story.data)); @@ -230,14 +214,16 @@ TEST_F(AssignmentsTest, assignments_produced_for_non_backing) { si.n_delay_tranches = 40; kagome::parachain::ApprovalDistribution::CandidateIncludedList leaving_cores = - {std::make_tuple(c_a, - kagome::network::CandidateReceipt{}, - (kagome::parachain::CoreIndex)0, - (kagome::parachain::GroupIndex)1), - std::make_tuple(c_b, - kagome::network::CandidateReceipt{}, - (kagome::parachain::CoreIndex)1, - (kagome::parachain::GroupIndex)0)}; + {std::make_tuple( + kagome::parachain::ApprovalDistribution::HashedCandidateReceipt{ + kagome::network::CandidateReceipt{}}, + (kagome::parachain::CoreIndex)0, + (kagome::parachain::GroupIndex)1), + std::make_tuple( + kagome::parachain::ApprovalDistribution::HashedCandidateReceipt{ + kagome::network::CandidateReceipt{}}, + (kagome::parachain::CoreIndex)1, + (kagome::parachain::GroupIndex)0)}; auto assignments = kagome::parachain::ApprovalDistribution::compute_assignments( cs, si, vrf_story, leaving_cores); From 62b9a92b02f3586df7c034df9e1782eacff92432 Mon Sep 17 00:00:00 2001 From: Harrm Date: Fri, 24 Nov 2023 11:35:00 +0300 Subject: [PATCH 06/16] Parachain instance cache warmup (#1851) * Added precompilation of parachain modules -- at node startup, parachain validation codes are pulled from the state of the last finalized block and compiled into wasm modules, which are stored in the RuntimeInstancesPool, a cache used by pvf executor. * Add test for multithreaded module compilation * Disable parachain precompilation for non-validating nodes * Make pools_mtx non-shared * build fix Signed-off-by: iceseer * Fix errors from master --------- Signed-off-by: iceseer Co-authored-by: iceseer Co-authored-by: Alexander Lednev <57529355+iceseer@users.noreply.github.com> --- cmake/clang-format.cmake | 3 + cmake/toolchain/compiler/gcc-13.cmake | 34 ++++ cmake/toolchain/gcc-13_cxx20.cmake | 2 + core/application/app_configuration.hpp | 2 + core/application/app_state_manager.hpp | 51 +++-- .../impl/app_configuration_impl.cpp | 15 ++ .../impl/app_configuration_impl.hpp | 11 + core/blockchain/impl/block_tree_impl.cpp | 4 +- core/blockchain/indexer.hpp | 2 +- core/common/empty.hpp | 6 +- core/common/monadic_utils.hpp | 9 +- core/common/visitor.hpp | 2 +- core/consensus/CMakeLists.txt | 1 - core/consensus/babe/CMakeLists.txt | 1 + .../babe/impl/babe_config_repository_impl.cpp | 2 +- core/consensus/grandpa/CMakeLists.txt | 16 +- .../grandpa/impl/authority_manager_impl.cpp | 2 +- core/consensus/timeline/CMakeLists.txt | 3 + core/host_api/impl/storage_extension.cpp | 4 +- core/injector/application_injector.cpp | 15 +- core/log/configurator.cpp | 2 + core/network/CMakeLists.txt | 1 + core/network/beefy/beefy.cpp | 4 +- .../helpers/scale_message_read_writer.hpp | 2 +- core/network/protobuf/CMakeLists.txt | 2 + core/network/types/collator_messages.hpp | 4 +- core/parachain/CMakeLists.txt | 1 + .../availability/bitfield/signer.cpp | 2 +- .../availability/bitfield/store_impl.cpp | 2 +- core/parachain/availability/chunks.hpp | 2 +- core/parachain/pvf/module_precompiler.cpp | 189 ++++++++++++++++++ core/parachain/pvf/module_precompiler.hpp | 59 ++++++ core/parachain/pvf/pvf_impl.cpp | 47 ++++- core/parachain/pvf/pvf_impl.hpp | 32 ++- core/parachain/types.hpp | 2 +- .../validator/impl/parachain_processor.cpp | 2 +- .../binaryen/core_api_factory_impl.cpp | 8 +- .../binaryen/module/module_factory_impl.cpp | 2 +- .../binaryen/module/module_factory_impl.hpp | 2 +- core/runtime/binaryen/module/module_impl.cpp | 16 +- core/runtime/binaryen/module/module_impl.hpp | 7 +- .../binaryen/module/module_instance_impl.cpp | 4 +- core/runtime/common/CMakeLists.txt | 5 +- .../runtime/common/module_repository_impl.cpp | 22 +- core/runtime/common/runtime_context.cpp | 7 +- .../common/runtime_execution_error.cpp | 17 ++ .../common/runtime_execution_error.hpp | 22 ++ .../runtime/common/runtime_instances_pool.cpp | 107 ++++++---- .../runtime/common/runtime_instances_pool.hpp | 54 +++-- .../common/runtime_transaction_error.cpp | 18 -- .../common/runtime_transaction_error.hpp | 23 --- .../common/trie_storage_provider_impl.cpp | 6 +- .../common/trie_storage_provider_impl.hpp | 2 +- .../common/uncompress_code_if_needed.cpp | 2 +- .../common/uncompress_code_if_needed.hpp | 10 +- core/runtime/module.hpp | 3 +- core/runtime/module_factory.hpp | 20 +- core/runtime/module_repository.hpp | 1 + core/runtime/runtime_api/impl/beefy.cpp | 4 +- .../runtime_api/impl/parachain_host.cpp | 1 + .../runtime_api/parachain_host_types.hpp | 8 +- core/runtime/types.hpp | 9 + core/runtime/wavm/core_api_factory_impl.cpp | 19 +- core/runtime/wavm/module.cpp | 16 +- core/runtime/wavm/module.hpp | 17 +- core/runtime/wavm/module_factory_impl.cpp | 18 +- core/runtime/wavm/module_factory_impl.hpp | 2 +- core/runtime/wavm/module_instance.cpp | 4 +- core/scale/CMakeLists.txt | 2 + core/scale/std_variant.hpp | 43 ++++ core/telemetry/CMakeLists.txt | 2 +- core/utils/thread_pool.hpp | 11 +- .../babe/babe_block_validator_test.cpp | 6 +- test/core/consensus/timeline/CMakeLists.txt | 1 + test/core/parachain/pvf_test.cpp | 45 +++-- test/core/runtime/CMakeLists.txt | 4 + test/core/runtime/instance_pool_test.cpp | 80 ++++++++ test/core/runtime/runtime_test_base.hpp | 4 +- .../runtime/trie_storage_provider_test.cpp | 8 +- test/core/runtime/wavm/CMakeLists.txt | 1 + test/external-project-test/src/main.cpp | 2 +- .../application/app_configuration_mock.hpp | 7 + .../mock/core/runtime/module_factory_mock.hpp | 2 +- test/mock/core/runtime/module_mock.hpp | 2 +- 84 files changed, 924 insertions(+), 290 deletions(-) create mode 100644 cmake/toolchain/compiler/gcc-13.cmake create mode 100644 cmake/toolchain/gcc-13_cxx20.cmake create mode 100644 core/parachain/pvf/module_precompiler.cpp create mode 100644 core/parachain/pvf/module_precompiler.hpp create mode 100644 core/runtime/common/runtime_execution_error.cpp create mode 100644 core/runtime/common/runtime_execution_error.hpp delete mode 100644 core/runtime/common/runtime_transaction_error.cpp delete mode 100644 core/runtime/common/runtime_transaction_error.hpp create mode 100644 core/scale/std_variant.hpp create mode 100644 test/core/runtime/instance_pool_test.cpp diff --git a/cmake/clang-format.cmake b/cmake/clang-format.cmake index 7daf973673..b02f7b3a3e 100644 --- a/cmake/clang-format.cmake +++ b/cmake/clang-format.cmake @@ -22,6 +22,9 @@ if(NOT CLANG_FORMAT_BIN) OUTPUT_VARIABLE DEFAULT_CLANG_FORMAT_VERSION ) math(EXPR DEFAULT_CLANG_FORMAT_VERSION "0 + 0${DEFAULT_CLANG_FORMAT_VERSION}") + if (${DEFAULT_CLANG_FORMAT_VERSION} GREATER ${RECOMMENDED_CLANG_FORMAT_VERSION}) + return() + endif() # Try to find newest version foreach(CLANG_FORMAT_VERSION RANGE ${RECOMMENDED_CLANG_FORMAT_VERSION} ${DEFAULT_CLANG_FORMAT_VERSION} -1) find_program(CLANG_FORMAT_BIN_ NAMES clang-format-${CLANG_FORMAT_VERSION}) diff --git a/cmake/toolchain/compiler/gcc-13.cmake b/cmake/toolchain/compiler/gcc-13.cmake new file mode 100644 index 0000000000..8dcbbd4eaf --- /dev/null +++ b/cmake/toolchain/compiler/gcc-13.cmake @@ -0,0 +1,34 @@ +if(DEFINED POLLY_COMPILER_GCC_13_CMAKE_) + return() +else() + set(POLLY_COMPILER_GCC_13_CMAKE_ 1) +endif() + +find_program(CMAKE_C_COMPILER gcc-13) +find_program(CMAKE_CXX_COMPILER g++-13) + +if(NOT CMAKE_C_COMPILER) + fatal_error("gcc-13 not found") +endif() + +if(NOT CMAKE_CXX_COMPILER) + fatal_error("g++-13 not found") +endif() + +set( + CMAKE_C_COMPILER + "${CMAKE_C_COMPILER}" + CACHE + STRING + "C compiler" + FORCE +) + +set( + CMAKE_CXX_COMPILER + "${CMAKE_CXX_COMPILER}" + CACHE + STRING + "C++ compiler" + FORCE +) diff --git a/cmake/toolchain/gcc-13_cxx20.cmake b/cmake/toolchain/gcc-13_cxx20.cmake new file mode 100644 index 0000000000..63abe45f68 --- /dev/null +++ b/cmake/toolchain/gcc-13_cxx20.cmake @@ -0,0 +1,2 @@ +include(${CMAKE_CURRENT_LIST_DIR}/compiler/gcc-13.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/cxx20.cmake) diff --git a/core/application/app_configuration.hpp b/core/application/app_configuration.hpp index 52972802c4..77a7d89adc 100644 --- a/core/application/app_configuration.hpp +++ b/core/application/app_configuration.hpp @@ -234,6 +234,8 @@ namespace kagome::application { virtual bool purgeWavmCache() const = 0; virtual uint32_t parachainRuntimeInstanceCacheSize() const = 0; + virtual uint32_t parachainPrecompilationThreadNum() const = 0; + virtual bool shouldPrecompileParachainModules() const = 0; enum class OffchainWorkerMode { WhenValidating, Always, Never }; /** diff --git a/core/application/app_state_manager.hpp b/core/application/app_state_manager.hpp index ce4b877754..78f9475a85 100644 --- a/core/application/app_state_manager.hpp +++ b/core/application/app_state_manager.hpp @@ -10,6 +10,26 @@ namespace kagome::application { + // concepts that check if an object has a method that is called by app state + // manager. Deliberately avoid checking that the method returns bool, + // because if there's a method with an appropriate name, and it doesn't return + // bool, we want it to be a compile error instead of silently ignoring it + // because the concept is not satisfied. + template + concept AppStateInjectable = requires(T& t) { t.inject(); }; + template + concept AppStatePreparable = requires(T& t) { t.prepare(); }; + template + concept AppStateStartable = requires(T& t) { t.start(); }; + template + concept AppStateStoppable = requires(T& t) { t.stop(); }; + + // if an object is registered with AppStateManager but has no method + // that is called by AppStateManager, there's probably something wrong + template + concept AppStateControllable = AppStatePreparable || AppStateInjectable + || AppStateStoppable || AppStateStartable; + class AppStateManager : public std::enable_shared_from_this { public: using OnInject = std::function; @@ -55,45 +75,24 @@ namespace kagome::application { */ virtual void atShutdown(OnShutdown &&cb) = 0; - private: - template - struct HasMethodInject : std::false_type {}; - template - struct HasMethodInject : std::true_type {}; - - template - struct HasMethodPrepare : std::false_type {}; - template - struct HasMethodPrepare : std::true_type {}; - - template - struct HasMethodStart : std::false_type {}; - template - struct HasMethodStart : std::true_type {}; - - template - struct HasMethodStop : std::false_type {}; - template - struct HasMethodStop : std::true_type {}; - public: /** * @brief Registration special methods (if any) of object as handlers * for stages of application life-cycle * @param entity is registered entity */ - template + template void takeControl(Controlled &entity) { - if constexpr (HasMethodInject::value) { + if constexpr (AppStateInjectable) { atInject([&entity]() -> bool { return entity.inject(); }); } - if constexpr (HasMethodPrepare::value) { + if constexpr (AppStatePreparable) { atPrepare([&entity]() -> bool { return entity.prepare(); }); } - if constexpr (HasMethodStart::value) { + if constexpr (AppStateStartable) { atLaunch([&entity]() -> bool { return entity.start(); }); } - if constexpr (HasMethodStop::value) { + if constexpr (AppStateStoppable) { atShutdown([&entity]() -> void { return entity.stop(); }); } } diff --git a/core/application/impl/app_configuration_impl.cpp b/core/application/impl/app_configuration_impl.cpp index 995d414a5f..d1b103b187 100644 --- a/core/application/impl/app_configuration_impl.cpp +++ b/core/application/impl/app_configuration_impl.cpp @@ -845,6 +845,10 @@ namespace kagome::application { ("parachain-runtime-instance-cache-size", po::value()->default_value(def_parachain_runtime_instance_cache_size), "Number of parachain runtime instances to keep cached") + ("no-precompile-parachain-modules", po::bool_switch(), "Don't precompile parachain runtime modules at node startup") + ("parachain-precompilation-thread-num", + po::value()->default_value(parachain_precompilation_thread_num_), + "Number of threads that precompile parachain runtime modules at node startup") ; po::options_description benchmark_desc("Benchmark options"); benchmark_desc.add_options() @@ -1402,6 +1406,17 @@ namespace kagome::application { parachain_runtime_instance_cache_size_ = *arg; } + if (!find_argument(vm, "validator") + || find_argument(vm, "no-precompile-parachain-modules")) { + should_precompile_parachain_modules_ = false; + } + + if (auto arg = + find_argument(vm, "parachain-precompilation-thread-num"); + arg.has_value()) { + parachain_precompilation_thread_num_ = *arg; + } + bool offchain_worker_value_error = false; find_argument( vm, diff --git a/core/application/impl/app_configuration_impl.hpp b/core/application/impl/app_configuration_impl.hpp index 374644b0e1..b13f2984ca 100644 --- a/core/application/impl/app_configuration_impl.hpp +++ b/core/application/impl/app_configuration_impl.hpp @@ -18,6 +18,7 @@ namespace rapidjson { #include #include #include +#include #include "log/logger.hpp" @@ -169,6 +170,13 @@ namespace kagome::application { uint32_t parachainRuntimeInstanceCacheSize() const override { return parachain_runtime_instance_cache_size_; } + uint32_t parachainPrecompilationThreadNum() const override { + return parachain_precompilation_thread_num_; + } + + bool shouldPrecompileParachainModules() const override { + return should_precompile_parachain_modules_; + } OffchainWorkerMode offchainWorkerMode() const override { return offchain_worker_mode_; @@ -362,6 +370,9 @@ namespace kagome::application { std::optional benchmark_config_; AllowUnsafeRpc allow_unsafe_rpc_ = AllowUnsafeRpc::kAuto; uint32_t parachain_runtime_instance_cache_size_ = 100; + uint32_t parachain_precompilation_thread_num_ = + std::thread::hardware_concurrency() / 2; + bool should_precompile_parachain_modules_{true}; }; } // namespace kagome::application diff --git a/core/blockchain/impl/block_tree_impl.cpp b/core/blockchain/impl/block_tree_impl.cpp index ce41b3375e..8cea23b1f9 100644 --- a/core/blockchain/impl/block_tree_impl.cpp +++ b/core/blockchain/impl/block_tree_impl.cpp @@ -1044,10 +1044,10 @@ namespace kagome::blockchain { auto header_res = p.header_repo_->getBlockHeader(hash); if (header_res.has_error()) { if (chain.empty()) { - log_->error("cannot retrieve block with hash {}: {}", + log_->error("Cannot retrieve block with hash {}: {}", hash, header_res.error()); - return BlockTreeError::HEADER_NOT_FOUND; + return header_res.error(); } break; } diff --git a/core/blockchain/indexer.hpp b/core/blockchain/indexer.hpp index fce4beb398..428b047c77 100644 --- a/core/blockchain/indexer.hpp +++ b/core/blockchain/indexer.hpp @@ -121,7 +121,7 @@ namespace kagome::blockchain { return outcome::success(); } - Descent descend(const primitives::BlockInfo &from) const { + Descent startDescentFrom(const primitives::BlockInfo &from) const { return {block_tree_, from}; } diff --git a/core/common/empty.hpp b/core/common/empty.hpp index ca9acd03eb..35d92055f5 100644 --- a/core/common/empty.hpp +++ b/core/common/empty.hpp @@ -11,19 +11,19 @@ namespace kagome { /// Special zero-size-type for some things - /// (e.g., unsupported, experimental or empty). + /// (e.g. unsupported, experimental or empty). struct Empty { inline constexpr bool operator==(const Empty &) const { return true; } template - friend inline auto &operator<<(Stream &s, const Empty &) { + friend inline Stream &operator<<(Stream &s, const Empty &) { return s; } template - friend inline auto &operator>>(Stream &s, const Empty &) { + friend inline Stream &operator>>(Stream &s, const Empty &) { return s; } }; diff --git a/core/common/monadic_utils.hpp b/core/common/monadic_utils.hpp index 92635f3702..bfe42e4e75 100644 --- a/core/common/monadic_utils.hpp +++ b/core/common/monadic_utils.hpp @@ -48,11 +48,12 @@ namespace kagome::common { * error. */ template > - outcome::result map_result(const outcome::result &res, const F &f) { + outcome::result map_result(const outcome::result &res, const F &f) { if (res.has_value()) { - return outcome::result{f(res.value())}; + return outcome::result{f(res.value())}; } return res.as_failure(); } @@ -63,7 +64,9 @@ namespace kagome::common { * outcome::result contains a value. Otherwise, just returns the contained * error. */ - template > + template F, + typename R = std::invoke_result_t> outcome::result map_result(outcome::result &&res, const F &f) { if (res.has_value()) { return outcome::result{f(std::move(res.value()))}; diff --git a/core/common/visitor.hpp b/core/common/visitor.hpp index e8f8249f22..6edefebdf6 100644 --- a/core/common/visitor.hpp +++ b/core/common/visitor.hpp @@ -155,4 +155,4 @@ namespace kagome { constexpr decltype(auto) match_in_place(T &&t, Fs &&...fs) { return match(std::forward(t), make_visitor(std::forward(fs)...)); } -} // namespace kagome +} // namespace kagome::common diff --git a/core/consensus/CMakeLists.txt b/core/consensus/CMakeLists.txt index 858aca84a3..0f7daca745 100644 --- a/core/consensus/CMakeLists.txt +++ b/core/consensus/CMakeLists.txt @@ -24,4 +24,3 @@ target_link_libraries(consensus INTERFACE grandpa ) kagome_install(consensus) -kagome_clear_objects(consensus) diff --git a/core/consensus/babe/CMakeLists.txt b/core/consensus/babe/CMakeLists.txt index 97ddca5aba..ab1377fab7 100644 --- a/core/consensus/babe/CMakeLists.txt +++ b/core/consensus/babe/CMakeLists.txt @@ -16,3 +16,4 @@ target_link_libraries(babe consensus_common ) kagome_install(babe) +kagome_clear_objects(babe) diff --git a/core/consensus/babe/impl/babe_config_repository_impl.cpp b/core/consensus/babe/impl/babe_config_repository_impl.cpp index 2bfa2f1218..8d35475133 100644 --- a/core/consensus/babe/impl/babe_config_repository_impl.cpp +++ b/core/consensus/babe/impl/babe_config_repository_impl.cpp @@ -234,7 +234,7 @@ namespace kagome::consensus::babe { BabeConfigRepositoryImpl::config(Indexer &indexer_, const primitives::BlockInfo &block, bool next_epoch) const { - auto descent = indexer_.descend(block); + auto descent = indexer_.startDescentFrom(block); outcome::result cb_res = outcome::success(); auto cb = [&](std::optional prev, size_t i_first, diff --git a/core/consensus/grandpa/CMakeLists.txt b/core/consensus/grandpa/CMakeLists.txt index 80535c917d..72ec996b5b 100644 --- a/core/consensus/grandpa/CMakeLists.txt +++ b/core/consensus/grandpa/CMakeLists.txt @@ -19,22 +19,8 @@ add_library(grandpa voting_round_error.cpp ) target_link_libraries(grandpa -# mp_utils -# ed25519_types -# Boost::boost -# schnorrkel_crust::schnorrkel_crust -# transaction_pool_error -# hasher -# vrf_provider logger -# scale::scale -# primitives -# blob -# outcome -# p2p::p2p_peer_id -# storage metrics -# blockchain -# telemetry ) kagome_install(grandpa) +kagome_clear_objects(grandpa) diff --git a/core/consensus/grandpa/impl/authority_manager_impl.cpp b/core/consensus/grandpa/impl/authority_manager_impl.cpp index b9ad3d3ee5..27c0c703d2 100644 --- a/core/consensus/grandpa/impl/authority_manager_impl.cpp +++ b/core/consensus/grandpa/impl/authority_manager_impl.cpp @@ -85,7 +85,7 @@ namespace kagome::consensus::grandpa { outcome::result> AuthorityManagerImpl::authoritiesOutcome(const primitives::BlockInfo &block, bool next) const { - auto descent = indexer_.descend(block); + auto descent = indexer_.startDescentFrom(block); outcome::result cb_res = outcome::success(); auto cb = [&](std::optional prev, size_t i_first, diff --git a/core/consensus/timeline/CMakeLists.txt b/core/consensus/timeline/CMakeLists.txt index fde6ddf127..75a5f7aec5 100644 --- a/core/consensus/timeline/CMakeLists.txt +++ b/core/consensus/timeline/CMakeLists.txt @@ -17,5 +17,8 @@ add_library(timeline ) target_link_libraries(timeline logger + telemetry + network ) kagome_install(timeline) +kagome_clear_objects(timeline) diff --git a/core/host_api/impl/storage_extension.cpp b/core/host_api/impl/storage_extension.cpp index 96e5a2e8b1..4eaf0ddb85 100644 --- a/core/host_api/impl/storage_extension.cpp +++ b/core/host_api/impl/storage_extension.cpp @@ -13,7 +13,7 @@ #include "host_api/impl/storage_util.hpp" #include "log/formatters/optional.hpp" #include "log/trace_macros.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/memory_provider.hpp" #include "runtime/ptr_size.hpp" #include "runtime/trie_storage_provider.hpp" @@ -42,7 +42,7 @@ namespace kagome::host_api { if (auto res = storage_provider_->rollbackTransaction(); res.has_error()) { if (res.error() - != runtime::RuntimeTransactionError::NO_TRANSACTIONS_WERE_STARTED) { + != runtime::RuntimeExecutionError::NO_TRANSACTIONS_WERE_STARTED) { logger_->error(res.error()); } break; diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 4224e8ee07..5d71c60832 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -8,7 +8,7 @@ #define BOOST_DI_CFG_DIAGNOSTICS_LEVEL 2 #define BOOST_DI_CFG_CTOR_LIMIT_SIZE \ - 32 // TODO(Harrm): check how it influences on compilation time + 24 // TODO(Harrm): check how it influences on compilation time #include #include @@ -128,6 +128,7 @@ #include "parachain/availability/recovery/recovery_impl.hpp" #include "parachain/availability/store/store_impl.hpp" #include "parachain/backing/store_impl.hpp" +#include "parachain/pvf/module_precompiler.hpp" #include "parachain/pvf/pvf_impl.hpp" #include "parachain/validator/impl/parachain_observer_impl.hpp" #include "parachain/validator/parachain_processor.hpp" @@ -462,7 +463,10 @@ namespace { makeWavmInjector(method), makeBinaryenInjector(method), bind_by_lambda([](const auto &injector) { - return std::make_shared(); + auto module_factory = + injector.template create>(); + return std::make_shared( + module_factory); }), di::bind.template to(), bind_by_lambda([method](const auto &injector) { @@ -555,6 +559,12 @@ namespace { libp2p::protocol::PingConfig ping_config{}; host_api::OffchainExtensionConfig offchain_ext_config{ config->isOffchainIndexingEnabled()}; + parachain::PvfImpl::Config pvf_config{ + .precompile_modules = config->shouldPrecompileParachainModules(), + .runtime_instance_cache_size = + config->parachainRuntimeInstanceCacheSize(), + .precompile_threads_num = config->parachainPrecompilationThreadNum(), + }; // clang-format off return di:: @@ -566,6 +576,7 @@ namespace { useConfig(tp_pool_limits), useConfig(ping_config), useConfig(offchain_ext_config), + useConfig(pvf_config), // inherit host injector libp2p::injector::makeHostInjector( diff --git a/core/log/configurator.cpp b/core/log/configurator.cpp index c67fb83335..e0e0055147 100644 --- a/core/log/configurator.cpp +++ b/core/log/configurator.cpp @@ -64,6 +64,8 @@ namespace kagome::log { children: - name: voting_round - name: parachain + children: + - name: pvf_executor - name: dispute - name: runtime children: diff --git a/core/network/CMakeLists.txt b/core/network/CMakeLists.txt index aa86a644f3..9f789cf101 100644 --- a/core/network/CMakeLists.txt +++ b/core/network/CMakeLists.txt @@ -58,3 +58,4 @@ target_link_libraries(network p2p::p2p_loopback_stream ) kagome_clear_objects(network) +kagome_install(network) \ No newline at end of file diff --git a/core/network/beefy/beefy.cpp b/core/network/beefy/beefy.cpp index 7327620b40..cac0a1c620 100644 --- a/core/network/beefy/beefy.cpp +++ b/core/network/beefy/beefy.cpp @@ -16,7 +16,7 @@ #include "crypto/crypto_store/session_keys.hpp" #include "metrics/histogram_timer.hpp" #include "network/beefy/protocol.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/runtime_api/beefy.hpp" #include "storage/spaced_storage.hpp" #include "utils/block_number_key.hpp" @@ -232,7 +232,7 @@ namespace kagome::network { // bug: beefy pallet doesn't produce digest with first validators OUTCOME_TRY(validators, beefy_api_->validatorSet(info.hash)); if (not validators) { - return runtime::RuntimeTransactionError::EXPORT_FUNCTION_NOT_FOUND; + return runtime::RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND; } return std::make_pair(info.number, std::move(*validators)); } diff --git a/core/network/helpers/scale_message_read_writer.hpp b/core/network/helpers/scale_message_read_writer.hpp index 8432a580e8..d605d81848 100644 --- a/core/network/helpers/scale_message_read_writer.hpp +++ b/core/network/helpers/scale_message_read_writer.hpp @@ -66,7 +66,7 @@ namespace kagome::network { template void write(const MsgType &msg, libp2p::basic::Writer::WriteCallbackFunc cb) const { - auto encoded_msg_res = scale::encode(msg); + auto encoded_msg_res = ::scale::encode(msg); if (!encoded_msg_res) { return cb(encoded_msg_res.error()); } diff --git a/core/network/protobuf/CMakeLists.txt b/core/network/protobuf/CMakeLists.txt index 044af64474..b173323e98 100644 --- a/core/network/protobuf/CMakeLists.txt +++ b/core/network/protobuf/CMakeLists.txt @@ -13,6 +13,7 @@ target_include_directories(node_api_proto PUBLIC # required for compiling proto targets $ ) +kagome_install(node_api_proto) add_proto_library(light_api_proto light.v1.proto @@ -23,3 +24,4 @@ target_include_directories(light_api_proto PUBLIC # required for compiling proto targets $ ) +kagome_install(light_api_proto) diff --git a/core/network/types/collator_messages.hpp b/core/network/types/collator_messages.hpp index f1c1157468..112450dbb3 100644 --- a/core/network/types/collator_messages.hpp +++ b/core/network/types/collator_messages.hpp @@ -132,7 +132,7 @@ namespace kagome::network { const Hash &hash(const crypto::Hasher &hasher) const { if (not hash_.has_value()) { hash_.emplace(hasher.blake2b_256( - scale::encode(std::tie(descriptor, commitments_hash)).value())); + ::scale::encode(std::tie(descriptor, commitments_hash)).value())); } return hash_.value(); } @@ -523,7 +523,7 @@ namespace kagome::network { auto commitments_hash = hasher.blake2b_256(scale::encode(receipt.commitments).value()); return hasher.blake2b_256( - scale::encode(std::tie(receipt.descriptor, commitments_hash)).value()); + ::scale::encode(std::tie(receipt.descriptor, commitments_hash)).value()); } inline CandidateHash candidateHash(const crypto::Hasher &hasher, diff --git a/core/parachain/CMakeLists.txt b/core/parachain/CMakeLists.txt index a4ff265a18..8cb7897d5d 100644 --- a/core/parachain/CMakeLists.txt +++ b/core/parachain/CMakeLists.txt @@ -14,6 +14,7 @@ add_library(validator_parachain backing/store_impl.cpp pvf/precheck.cpp pvf/pvf_impl.cpp + pvf/module_precompiler.cpp validator/impl/parachain_observer_impl.cpp validator/impl/parachain_processor.cpp validator/signer.cpp diff --git a/core/parachain/availability/bitfield/signer.cpp b/core/parachain/availability/bitfield/signer.cpp index 84616a60b2..d21ed65d60 100644 --- a/core/parachain/availability/bitfield/signer.cpp +++ b/core/parachain/availability/bitfield/signer.cpp @@ -104,7 +104,7 @@ namespace kagome::parachain { parachain_api_->session_info(relay_parent, signer->getSessionIndex())); candidates.reserve(cores.size()); for (auto &core : cores) { - if (auto occupied = boost::get(&core)) { + if (auto occupied = std::get_if(&core)) { candidates.emplace_back(occupied->candidate_hash); fetch_->fetch(signer->validatorIndex(), *occupied, *session); } else { diff --git a/core/parachain/availability/bitfield/store_impl.cpp b/core/parachain/availability/bitfield/store_impl.cpp index 2d136839ea..270c5a9e6d 100644 --- a/core/parachain/availability/bitfield/store_impl.cpp +++ b/core/parachain/availability/bitfield/store_impl.cpp @@ -59,7 +59,7 @@ namespace kagome::parachain { bool skip = false; for (size_t ix = 0; ix < cores.size(); ++ix) { auto &core = cores[ix]; - if (is_type(core)) { + if (std::holds_alternative(core)) { continue; } diff --git a/core/parachain/availability/chunks.hpp b/core/parachain/availability/chunks.hpp index f6c0230ef2..6e4b6838e1 100644 --- a/core/parachain/availability/chunks.hpp +++ b/core/parachain/availability/chunks.hpp @@ -23,7 +23,7 @@ namespace kagome::parachain { inline outcome::result> toChunks( size_t validators, const runtime::AvailableData &data) { - OUTCOME_TRY(message, scale::encode(data)); + OUTCOME_TRY(message, ::scale::encode(data)); auto create_result = ec_cpp::create(validators); if (ec_cpp::resultHasError(create_result)) { diff --git a/core/parachain/pvf/module_precompiler.cpp b/core/parachain/pvf/module_precompiler.cpp new file mode 100644 index 0000000000..88769c8d8f --- /dev/null +++ b/core/parachain/pvf/module_precompiler.cpp @@ -0,0 +1,189 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "parachain/pvf/module_precompiler.hpp" + +#include +#include +#include + +#include "runtime/common/runtime_execution_error.hpp" +#include "runtime/common/runtime_instances_pool.hpp" +#include "runtime/runtime_api/parachain_host.hpp" +#include "runtime/runtime_api/parachain_host_types.hpp" + +namespace kagome::parachain { + + struct ParachainCore { + runtime::CoreState state; + }; + + ModulePrecompiler::ModulePrecompiler( + const kagome::parachain::ModulePrecompiler::Config &config, + std::shared_ptr parachain_api, + std::shared_ptr runtime_cache, + std::shared_ptr hasher) + : config_{config}, + parachain_api_{parachain_api}, + runtime_cache_{runtime_cache}, + hasher_{hasher} { + if (getThreadsNum() > std::thread::hardware_concurrency() - 1) { + SL_WARN( + log_, + "The number of threads assigned for parachain runtime module " + "pre-compilation is greater than (the number of hardware cores - 1). " + "This is most likely inefficient."); + } + } + + struct ModulePrecompiler::PrecompilationStats { + const size_t total_count{}; + std::atomic_int occupied_precompiled_count{}; + std::atomic_int scheduled_precompiled_count{}; + std::atomic_int total_code_size{}; + }; + + std::optional get_para_id(runtime::CoreState core) { + return visit_in_place( + core, + [](const runtime::OccupiedCore &core) mutable + -> std::optional { + return core.candidate_descriptor.para_id; + }, + [](const runtime::ScheduledCore &core) mutable + -> std::optional { return core.para_id; }, + [](runtime::FreeCore) -> std::optional { + return std::nullopt; + }); + } + + outcome::result ModulePrecompiler::precompileModulesAt( + const primitives::BlockHash &last_finalized) { + auto cores_res = parachain_api_->availability_cores(last_finalized); + if (cores_res.has_error() + && cores_res.error() + == runtime::RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND) { + SL_WARN(log_, + "Failed to warm up PVF executor runtime module cache, since " + "ParachainHost API is not present in the runtime at block {}", + last_finalized); + return outcome::success(); + } + OUTCOME_TRY(cores, cores_res); + SL_DEBUG(log_, + "Warming up PVF executor runtime instance cache at block {}", + last_finalized); + PrecompilationStats stats{ + .total_count = cores.size(), + }; + auto start = std::chrono::steady_clock::now(); + + std::mutex cores_queue_mutex; + std::vector threads; + for (size_t i = 0; i < config_.precompile_threads_num; i++) { + auto compilation_worker = + [self = shared_from_this(), + &cores_queue_mutex, + &cores, + &stats, + &last_finalized]() mutable -> outcome::result { + while (true) { + runtime::CoreState core; + { + std::scoped_lock l{cores_queue_mutex}; + if (cores.empty()) { + break; + } + core = cores.back(); + cores.pop_back(); + } + auto res = self->precompileModulesForCore( + stats, last_finalized, ParachainCore{core}); + if (!res) { + using namespace std::string_literals; + auto id = get_para_id(core); + SL_ERROR(self->log_, + "Failed to precompile parachain module for {} parachain " + "core: {}", + id ? std::to_string(*id) : "empty"s, + res.error()); + } + } + return outcome::success(); + }; + threads.emplace_back(compilation_worker); + } + + for (auto &t : threads) { + t.join(); + } + + auto end = std::chrono::steady_clock::now(); + double time_taken = + static_cast( + std::chrono::duration_cast(end - start) + .count()) + / 1e3; + SL_VERBOSE(log_, + "Precompiled runtime instances for {} occupied parachain " + "cores and {} scheduled parachain cores. Total code size is " + "{}, time taken is {}s", + stats.occupied_precompiled_count.load(), + stats.scheduled_precompiled_count.load(), + stats.total_code_size.load(), + time_taken); + return outcome::success(); + } + + outcome::result ModulePrecompiler::precompileModulesForCore( + PrecompilationStats &stats, + const primitives::BlockHash &last_finalized, + const ParachainCore &_core) { + auto &core = _core.state; + if (std::holds_alternative(core)) { + return outcome::success(); + + } else if (std::holds_alternative(core)) { + SL_TRACE(log_, "Precompile for occupied availability core"); + stats.occupied_precompiled_count++; + } else if (std::holds_alternative(core)) { + SL_TRACE(log_, "Precompile for scheduled availability core"); + stats.scheduled_precompiled_count++; + } + // since we eliminated empty core option earlier + auto para_id = get_para_id(core).value(); + OUTCOME_TRY(code_opt, + parachain_api_->validation_code( + last_finalized, + para_id, + runtime::OccupiedCoreAssumption::Included)); + if (!code_opt) { + SL_WARN(log_, + "No validation code found for parachain {} with 'included' " + "occupied assumption", + para_id); + return outcome::success(); + } + auto &code = *code_opt; + auto hash = hasher_->blake2b_256(code); + SL_DEBUG(log_, + "Validation code for parachain {} has size {} and hash {}", + para_id, + code.size(), + hash); + stats.total_code_size += code.size(); + OUTCOME_TRY(runtime_cache_->instantiateFromCode(hash, code)); + SL_DEBUG(log_, + "Instantiated runtime instance with code hash {} for parachain " + "{}, {} left", + hash, + para_id, + stats.total_count - stats.occupied_precompiled_count + - stats.scheduled_precompiled_count); + + return outcome::success(); + } + +} // namespace kagome::parachain diff --git a/core/parachain/pvf/module_precompiler.hpp b/core/parachain/pvf/module_precompiler.hpp new file mode 100644 index 0000000000..57e7b7e6c6 --- /dev/null +++ b/core/parachain/pvf/module_precompiler.hpp @@ -0,0 +1,59 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "log/logger.hpp" +#include "outcome/outcome.hpp" +#include "primitives/block_id.hpp" + +namespace kagome::crypto { + class Hasher; +} + +namespace kagome::runtime { + class ParachainHost; + class RuntimeInstancesPool; +} // namespace kagome::runtime + +namespace kagome::parachain { + struct ParachainCore; + + class ModulePrecompiler + : public std::enable_shared_from_this { + public: + struct Config { + unsigned precompile_threads_num; + }; + + ModulePrecompiler( + const Config &config, + std::shared_ptr parachain_api, + std::shared_ptr runtime_cache, + std::shared_ptr hasher); + + outcome::result precompileModulesAt( + const primitives::BlockHash &last_finalized); + + size_t getThreadsNum() const { + return config_.precompile_threads_num; + } + + private: + struct PrecompilationStats; + + outcome::result precompileModulesForCore( + PrecompilationStats &stats, + const primitives::BlockHash &last_finalized, + const ParachainCore &core); + + Config config_; + std::shared_ptr parachain_api_; + std::shared_ptr runtime_cache_; + std::shared_ptr hasher_; + + log::Logger log_ = log::createLogger("ModulePrecompiler", "pvf_executor"); + }; +} // namespace kagome::parachain diff --git a/core/parachain/pvf/pvf_impl.cpp b/core/parachain/pvf/pvf_impl.cpp index 4055f48a16..b1c326d2cf 100644 --- a/core/parachain/pvf/pvf_impl.cpp +++ b/core/parachain/pvf/pvf_impl.cpp @@ -7,7 +7,12 @@ #include "parachain/pvf/pvf_impl.hpp" #include "application/app_configuration.hpp" +#include "application/app_state_manager.hpp" +#include "blockchain/block_tree.hpp" +#include "common/visitor.hpp" #include "metrics/histogram_timer.hpp" +#include "parachain/pvf/module_precompiler.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/common/runtime_instances_pool.hpp" #include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/executor.hpp" @@ -15,6 +20,7 @@ #include "runtime/module_factory.hpp" #include "runtime/module_repository.hpp" #include "runtime/runtime_code_provider.hpp" +#include "scale/std_variant.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::parachain, PvfError, e) { using kagome::parachain::PvfError; @@ -108,26 +114,50 @@ namespace kagome::parachain { }; PvfImpl::PvfImpl( + const Config &config, std::shared_ptr hasher, std::shared_ptr module_factory, std::shared_ptr runtime_properties_cache, - std::shared_ptr - block_header_repository, + std::shared_ptr block_tree, std::shared_ptr sr25519_provider, std::shared_ptr parachain_api, std::shared_ptr executor, std::shared_ptr ctx_factory, - std::shared_ptr config) - : hasher_{std::move(hasher)}, + std::shared_ptr state_manager) + : config_{config}, + hasher_{std::move(hasher)}, runtime_properties_cache_{std::move(runtime_properties_cache)}, - block_header_repository_{std::move(block_header_repository)}, + block_tree_{std::move(block_tree)}, sr25519_provider_{std::move(sr25519_provider)}, parachain_api_{std::move(parachain_api)}, executor_{std::move(executor)}, ctx_factory_{std::move(ctx_factory)}, - log_{log::createLogger("Pvf")}, + log_{log::createLogger("PVF Executor", "pvf_executor")}, runtime_cache_{std::make_shared( - module_factory, config->parachainRuntimeInstanceCacheSize())} {} + module_factory, config.runtime_instance_cache_size)}, + precompiler_{std::make_shared( + ModulePrecompiler::Config{config_.precompile_threads_num}, + parachain_api_, + runtime_cache_, + hasher_)} { + state_manager->takeControl(*this); + } + + bool PvfImpl::prepare() { + if (config_.precompile_modules) { + std::thread t{[self = shared_from_this()]() { + auto res = self->precompiler_->precompileModulesAt( + self->block_tree_->getLastFinalized().hash); + if (!res) { + SL_ERROR(self->log_, + "Parachain module precompilation failed: {}", + res.error()); + } + }}; + t.detach(); + } + return true; + } outcome::result PvfImpl::pvfValidate( const PersistedValidationData &data, @@ -228,7 +258,8 @@ namespace kagome::parachain { const common::Hash256 &code_hash, const ParachainRuntime &code_zstd, const ValidationParams ¶ms) const { - OUTCOME_TRY(instance, runtime_cache_->instantiate(code_hash, code_zstd)); + OUTCOME_TRY(instance, + runtime_cache_->instantiateFromCode(code_hash, code_zstd)); runtime::RuntimeContext::ContextParams executor_params{}; auto &parent_hash = receipt.descriptor.relay_parent; diff --git a/core/parachain/pvf/pvf_impl.hpp b/core/parachain/pvf/pvf_impl.hpp index 9facdeb84c..027b774134 100644 --- a/core/parachain/pvf/pvf_impl.hpp +++ b/core/parachain/pvf/pvf_impl.hpp @@ -8,7 +8,6 @@ #include "parachain/pvf/pvf.hpp" -#include "blockchain/block_header_repository.hpp" #include "crypto/sr25519_provider.hpp" #include "log/logger.hpp" #include "runtime/runtime_api/parachain_host.hpp" @@ -16,6 +15,11 @@ namespace kagome::application { class AppConfiguration; + class AppStateManager; +} // namespace kagome::application + +namespace kagome::blockchain { + class BlockTree; } namespace kagome::runtime { @@ -43,6 +47,8 @@ namespace kagome::parachain { OUTCOME_HPP_DECLARE_ERROR(kagome::parachain, PvfError) namespace kagome::parachain { + class ModulePrecompiler; + struct ValidationParams; struct ValidationResult { @@ -56,19 +62,27 @@ namespace kagome::parachain { BlockNumber hrmp_watermark; }; - class PvfImpl : public Pvf { + class PvfImpl : public Pvf, public std::enable_shared_from_this { public: - PvfImpl(std::shared_ptr hasher, + struct Config { + bool precompile_modules; + size_t runtime_instance_cache_size{16}; + unsigned precompile_threads_num{1}; + }; + + PvfImpl(const Config &config, + std::shared_ptr hasher, std::shared_ptr module_factory, std::shared_ptr runtime_properties_cache, - std::shared_ptr - block_header_repository, + std::shared_ptr block_tree, std::shared_ptr sr25519_provider, std::shared_ptr parachain_api, std::shared_ptr executor, std::shared_ptr ctx_factory, - std::shared_ptr config); + std::shared_ptr app_state_manager); + + bool prepare(); outcome::result pvfSync(const CandidateReceipt &receipt, const ParachainBlock &pov) const override; @@ -84,17 +98,20 @@ namespace kagome::parachain { outcome::result> findData(const CandidateDescriptor &descriptor) const; + outcome::result callWasm( const CandidateReceipt &receipt, const common::Hash256 &code_hash, const ParachainRuntime &code_zstd, const ValidationParams ¶ms) const; + outcome::result fromOutputs( const CandidateReceipt &receipt, ValidationResult &&result) const; + Config config_; std::shared_ptr hasher_; std::shared_ptr runtime_properties_cache_; - std::shared_ptr block_header_repository_; + std::shared_ptr block_tree_; std::shared_ptr sr25519_provider_; std::shared_ptr parachain_api_; std::shared_ptr executor_; @@ -102,5 +119,6 @@ namespace kagome::parachain { log::Logger log_; std::shared_ptr runtime_cache_; + std::shared_ptr precompiler_; }; } // namespace kagome::parachain diff --git a/core/parachain/types.hpp b/core/parachain/types.hpp index cd13dd2a64..90fc897378 100644 --- a/core/parachain/types.hpp +++ b/core/parachain/types.hpp @@ -88,7 +88,7 @@ namespace kagome::parachain { auto signable() { constexpr std::array kMagic{'V', 'C', 'P', 'C'}; - return scale::encode(std::make_tuple(kMagic, *this)).value(); + return ::scale::encode(std::make_tuple(kMagic, *this)).value(); } }; } // namespace kagome::parachain diff --git a/core/parachain/validator/impl/parachain_processor.cpp b/core/parachain/validator/impl/parachain_processor.cpp index 5cb5b11e32..9baf31de50 100644 --- a/core/parachain/validator/impl/parachain_processor.cpp +++ b/core/parachain/validator/impl/parachain_processor.cpp @@ -335,7 +335,7 @@ namespace kagome::parachain { core_index < static_cast(cores.size()); ++core_index) { if (const auto *scheduled = - boost::get(&cores[core_index])) { + std::get_if(&cores[core_index])) { const auto group_index = group_rotation_info.groupForCore(core_index, n_cores); if (group_index < validator_groups.size()) { diff --git a/core/runtime/binaryen/core_api_factory_impl.cpp b/core/runtime/binaryen/core_api_factory_impl.cpp index 02d3e98a45..71ea9908a3 100644 --- a/core/runtime/binaryen/core_api_factory_impl.cpp +++ b/core/runtime/binaryen/core_api_factory_impl.cpp @@ -34,10 +34,10 @@ namespace kagome::runtime::binaryen { const primitives::BlockInfo &, const storage::trie::RootHash &) override { if (instance_ == nullptr) { - OUTCOME_TRY( - module, - ModuleImpl::createFromCode(code_, env_factory_, code_hash_)); - OUTCOME_TRY(inst, module->instantiate()); + auto module_res = + ModuleImpl::createFromCode(code_, env_factory_, code_hash_); + if (!module_res) return make_error_code(module_res.error()); + auto inst = module_res.value()->instantiate(); instance_ = std::move(inst); } return instance_; diff --git a/core/runtime/binaryen/module/module_factory_impl.cpp b/core/runtime/binaryen/module/module_factory_impl.cpp index d7c240d18f..9e8724550d 100644 --- a/core/runtime/binaryen/module/module_factory_impl.cpp +++ b/core/runtime/binaryen/module/module_factory_impl.cpp @@ -27,7 +27,7 @@ namespace kagome::runtime::binaryen { BOOST_ASSERT(storage_ != nullptr); } - outcome::result> ModuleFactoryImpl::make( + outcome::result, CompilationError> ModuleFactoryImpl::make( common::BufferView code) const { std::vector code_vec{code.begin(), code.end()}; OUTCOME_TRY(module, diff --git a/core/runtime/binaryen/module/module_factory_impl.hpp b/core/runtime/binaryen/module/module_factory_impl.hpp index 523c853a2e..a60d4ea4f4 100644 --- a/core/runtime/binaryen/module/module_factory_impl.hpp +++ b/core/runtime/binaryen/module/module_factory_impl.hpp @@ -38,7 +38,7 @@ namespace kagome::runtime::binaryen { std::shared_ptr storage, std::shared_ptr hasher); - outcome::result> make( + outcome::result, CompilationError> make( common::BufferView code) const override; private: diff --git a/core/runtime/binaryen/module/module_impl.cpp b/core/runtime/binaryen/module/module_impl.cpp index 463b79134f..c272090b4d 100644 --- a/core/runtime/binaryen/module/module_impl.cpp +++ b/core/runtime/binaryen/module/module_impl.cpp @@ -44,7 +44,8 @@ namespace kagome::runtime::binaryen { BOOST_ASSERT(env_factory_ != nullptr); } - outcome::result> ModuleImpl::createFromCode( + outcome::result, CompilationError> + ModuleImpl::createFromCode( const std::vector &code, std::shared_ptr env_factory, const common::Hash256 &code_hash) { @@ -52,7 +53,8 @@ namespace kagome::runtime::binaryen { // that nolint suppresses false positive in a library function // NOLINTNEXTLINE(clang-analyzer-core.NonNullParamChecker) if (code.empty()) { - return Error::EMPTY_STATE_CODE; + return CompilationError{ + "Empty WASM code supplied to binaryen's ModuleImpl::createFromCode"}; } auto module = std::make_unique(); @@ -67,8 +69,11 @@ namespace kagome::runtime::binaryen { } catch (wasm::ParseException &e) { std::ostringstream msg; e.dump(msg); - log->error(msg.str()); - return Error::INVALID_STATE_CODE; + log->warn(msg.str()); + return CompilationError{ + fmt::format("Invalid WASM code supplied to binaryen's " + "ModuleImpl::createFromCode: {}", + msg.str())}; } } @@ -78,8 +83,7 @@ namespace kagome::runtime::binaryen { std::move(module), std::move(env_factory), code_hash); } - outcome::result> ModuleImpl::instantiate() - const { + std::shared_ptr ModuleImpl::instantiate() const { 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 4826c33d4b..d478acca97 100644 --- a/core/runtime/binaryen/module/module_impl.hpp +++ b/core/runtime/binaryen/module/module_impl.hpp @@ -10,6 +10,7 @@ #include "common/buffer.hpp" #include "runtime/binaryen/module/module_instance_impl.hpp" +#include "runtime/module_factory.hpp" #include "runtime/trie_storage_provider.hpp" namespace wasm { @@ -43,13 +44,13 @@ namespace kagome::runtime::binaryen { ~ModuleImpl() override = default; - static outcome::result> createFromCode( + static outcome::result, CompilationError> + createFromCode( const std::vector &code, std::shared_ptr env_factory_, const common::Hash256 &code_hash); - outcome::result> instantiate() - const override; + std::shared_ptr instantiate() const override; ModuleImpl(std::unique_ptr &&module, std::shared_ptr env_factory, diff --git a/core/runtime/binaryen/module/module_instance_impl.cpp b/core/runtime/binaryen/module/module_instance_impl.cpp index 9dd93d6ae1..00009885df 100644 --- a/core/runtime/binaryen/module/module_instance_impl.cpp +++ b/core/runtime/binaryen/module/module_instance_impl.cpp @@ -13,7 +13,7 @@ #include "runtime/binaryen/memory_impl.hpp" #include "runtime/binaryen/module/module_impl.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/memory_provider.hpp" #include @@ -110,7 +110,7 @@ namespace kagome::runtime::binaryen { module_instance_->wasm.getExportOrNull(wasm::Name{name.data()}); nullptr == res) { SL_DEBUG(logger_, "The requested function {} not found", name); - return RuntimeTransactionError::EXPORT_FUNCTION_NOT_FOUND; + return RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND; } try { diff --git a/core/runtime/common/CMakeLists.txt b/core/runtime/common/CMakeLists.txt index 94746f315e..9f54a91901 100644 --- a/core/runtime/common/CMakeLists.txt +++ b/core/runtime/common/CMakeLists.txt @@ -31,7 +31,7 @@ target_link_libraries(uncompress_if_needed kagome_install(uncompress_if_needed) add_library(runtime_transaction_error - runtime_transaction_error.cpp + runtime_execution_error.cpp ) target_link_libraries(runtime_transaction_error outcome @@ -62,6 +62,9 @@ add_library(module_repository runtime_instances_pool.cpp) target_link_libraries(module_repository outcome + uncompress_if_needed + blob + executor ) kagome_install(module_repository) diff --git a/core/runtime/common/module_repository_impl.cpp b/core/runtime/common/module_repository_impl.cpp index 077d16a491..bedbb56a2d 100644 --- a/core/runtime/common/module_repository_impl.cpp +++ b/core/runtime/common/module_repository_impl.cpp @@ -15,6 +15,15 @@ #include "runtime/runtime_code_provider.hpp" #include "runtime/runtime_upgrade_tracker.hpp" +OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, Error, e) { + using E = kagome::runtime::Error; + switch (e) { + case E::COMPILATION_FAILED: + return "Runtime module compilation failed"; + } + return "Unknown module repository error"; +} + namespace kagome::runtime { using kagome::primitives::ThreadNumber; using soralog::util::getThreadNumber; @@ -63,13 +72,18 @@ namespace kagome::runtime { if (not code.has_value()) { return code.as_failure(); } - OUTCOME_TRY(new_module, module_factory_->make(code.value())); - runtime_instances_pool_->putModule(state, std::move(new_module)); + auto new_module_res = module_factory_->make(code.value()); + if (!new_module_res) { + return make_error_code(new_module_res.error()); + } + runtime_instances_pool_->putModule(state, + std::move(new_module_res.value())); } } - // Try acquire instance (instantiate if needed) - OUTCOME_TRY(runtime_instance, runtime_instances_pool_->tryAcquire(state)); + // Try to acquire an instance (instantiate if needed) + OUTCOME_TRY(runtime_instance, + runtime_instances_pool_->instantiateFromState(state)); KAGOME_PROFILE_END(module_retrieval) return runtime_instance; diff --git a/core/runtime/common/runtime_context.cpp b/core/runtime/common/runtime_context.cpp index 30fa8eb99c..a1f31d71ae 100644 --- a/core/runtime/common/runtime_context.cpp +++ b/core/runtime/common/runtime_context.cpp @@ -37,8 +37,11 @@ namespace kagome::runtime { ContextParams params) { common::Buffer code; OUTCOME_TRY(runtime::uncompressCodeIfNeeded(code_zstd, code)); - OUTCOME_TRY(runtime_module, module_factory.make(code)); - OUTCOME_TRY(instance, runtime_module->instantiate()); + auto runtime_module_res = module_factory.make(code); + if (!runtime_module_res) { + return Error::COMPILATION_FAILED; + } + auto instance = runtime_module_res.value()->instantiate(); runtime::RuntimeContext ctx{ instance, }; diff --git a/core/runtime/common/runtime_execution_error.cpp b/core/runtime/common/runtime_execution_error.cpp new file mode 100644 index 0000000000..cc7b7d8645 --- /dev/null +++ b/core/runtime/common/runtime_execution_error.cpp @@ -0,0 +1,17 @@ +/** +* Copyright Quadrivium LLC All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0 +*/ + +#include "runtime/common/runtime_execution_error.hpp" + +OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, RuntimeExecutionError, e) { + using E = kagome::runtime::RuntimeExecutionError; + switch (e) { + case E::NO_TRANSACTIONS_WERE_STARTED: + return "No storage transactions were started"; + case E::EXPORT_FUNCTION_NOT_FOUND: + return "Export function not found"; + } + return "Unknown RuntimeExecutionError"; +} diff --git a/core/runtime/common/runtime_execution_error.hpp b/core/runtime/common/runtime_execution_error.hpp new file mode 100644 index 0000000000..7e438bfee6 --- /dev/null +++ b/core/runtime/common/runtime_execution_error.hpp @@ -0,0 +1,22 @@ +/** +* Copyright Quadrivium LLC All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0 +*/ + +#pragma once + +#include + +namespace kagome::runtime { + + /** + * @brief RuntimeExecutionError enum provides error codes for storage + * transactions mechanism + */ + enum class RuntimeExecutionError { // 0 is reserved for success + NO_TRANSACTIONS_WERE_STARTED = 1, + EXPORT_FUNCTION_NOT_FOUND + }; +} // namespace kagome::runtime + +OUTCOME_HPP_DECLARE_ERROR(kagome::runtime, RuntimeExecutionError); diff --git a/core/runtime/common/runtime_instances_pool.cpp b/core/runtime/common/runtime_instances_pool.cpp index 2a47d024ad..0eeac1f2b1 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 "common/monadic_utils.hpp" #include "runtime/common/uncompress_code_if_needed.hpp" #include "runtime/instance_environment.hpp" #include "runtime/module.hpp" @@ -21,14 +22,12 @@ namespace kagome::runtime { class BorrowedInstance : public ModuleInstance { public: BorrowedInstance(std::weak_ptr pool, - const RuntimeInstancesPool::RootHash &state, + const common::Hash256 &hash, std::shared_ptr instance) - : pool_{std::move(pool)}, - state_{state}, - instance_{std::move(instance)} {} + : pool_{std::move(pool)}, hash_{hash}, instance_{std::move(instance)} {} ~BorrowedInstance() { if (auto pool = pool_.lock()) { - pool->release(state_, std::move(instance_)); + pool->release(hash_, std::move(instance_)); } } @@ -64,52 +63,91 @@ namespace kagome::runtime { private: std::weak_ptr pool_; - RuntimeInstancesPool::RootHash state_; + common::Hash256 hash_; // either trie hash or code hash std::shared_ptr instance_; }; - RuntimeInstancesPool::RuntimeInstancesPool() : pools_{MODULES_CACHE_SIZE} {} - RuntimeInstancesPool::RuntimeInstancesPool( std::shared_ptr module_factory, size_t capacity) : module_factory_{std::move(module_factory)}, pools_{capacity} {} - outcome::result> - RuntimeInstancesPool::instantiate(const RootHash &code_hash, - common::BufferView code_zstd) { - std::unique_lock lock{mt_}; - auto entry = pools_.get(code_hash); - if (not entry) { + outcome::result, CompilationError> + RuntimeInstancesPool::instantiateFromCode(const CodeHash &code_hash, + common::BufferView code_zstd) { + std::unique_lock lock{pools_mtx_}; + auto pool_opt = pools_.get(code_hash); + + if (!pool_opt) { lock.unlock(); - common::Buffer code; - OUTCOME_TRY(uncompressCodeIfNeeded(code_zstd, code)); - OUTCOME_TRY(module, module_factory_->make(code)); - lock.lock(); - entry = pools_.get(code_hash); - if (not entry) { - entry = pools_.put(code_hash, {std::move(module), {}}); + if (auto future = getFutureCompiledModule(code_hash)) { + lock.lock(); + pool_opt = pools_.get(code_hash); + } else { + OUTCOME_TRY(module, tryCompileModule(code_hash, code_zstd)); + BOOST_ASSERT(module != nullptr); + lock.lock(); + pool_opt = std::ref(pools_.put(code_hash, InstancePool{module, {}})); } } - OUTCOME_TRY(instance, entry->get().instantiate(lock)); + auto instance = pool_opt->get().instantiate(lock); return std::make_shared( weak_from_this(), code_hash, std::move(instance)); } + std::optional> + RuntimeInstancesPool::getFutureCompiledModule( + const CodeHash &code_hash) const { + std::unique_lock l{compiling_modules_mtx_}; + auto iter = compiling_modules_.find(code_hash); + if (iter == compiling_modules_.end()) { + return std::nullopt; + } + auto future = iter->second; + l.unlock(); + return future; + } + + RuntimeInstancesPool::CompilationResult + RuntimeInstancesPool::tryCompileModule(const CodeHash &code_hash, + common::BufferView code_zstd) { + std::unique_lock l{compiling_modules_mtx_}; + std::promise promise; + auto [iter, inserted] = + compiling_modules_.insert({code_hash, promise.get_future()}); + BOOST_ASSERT(inserted); + l.unlock(); + + common::Buffer code; + CompilationResult res{nullptr}; + if (!uncompressCodeIfNeeded(code_zstd, code)) { + res = CompilationError{"Failed to uncompress code"}; + } else { + res = common::map_result(module_factory_->make(code), [](auto &&module) { + return std::shared_ptr(module); + }); + } + promise.set_value(res); + + l.lock(); + compiling_modules_.erase(iter); + return res; + } + outcome::result> - RuntimeInstancesPool::tryAcquire( - const RuntimeInstancesPool::RootHash &state) { - std::unique_lock lock{mt_}; + RuntimeInstancesPool::instantiateFromState( + const RuntimeInstancesPool::TrieHash &state) { + std::unique_lock lock{pools_mtx_}; auto entry = pools_.get(state); BOOST_ASSERT(entry); - OUTCOME_TRY(instance, entry->get().instantiate(lock)); + auto instance = entry->get().instantiate(lock); return std::make_shared( weak_from_this(), state, std::move(instance)); } void RuntimeInstancesPool::release( - const RuntimeInstancesPool::RootHash &state, + const RuntimeInstancesPool::TrieHash &state, std::shared_ptr &&instance) { - std::lock_guard guard{mt_}; + std::unique_lock guard{pools_mtx_}; auto entry = pools_.get(state); if (not entry) { entry = pools_.put(state, {instance->getModule(), {}}); @@ -118,8 +156,8 @@ namespace kagome::runtime { } std::optional> RuntimeInstancesPool::getModule( - const RuntimeInstancesPool::RootHash &state) { - std::lock_guard guard{mt_}; + const RuntimeInstancesPool::TrieHash &state) { + std::unique_lock guard{pools_mtx_}; if (auto entry = pools_.get(state)) { return entry->get().module; } @@ -127,16 +165,17 @@ namespace kagome::runtime { } void RuntimeInstancesPool::putModule( - const RuntimeInstancesPool::RootHash &state, + const RuntimeInstancesPool::TrieHash &state, std::shared_ptr module) { - std::lock_guard guard{mt_}; + std::unique_lock guard{pools_mtx_}; if (not pools_.get(state)) { pools_.put(state, {std::move(module), {}}); } } - outcome::result> - RuntimeInstancesPool::Entry::instantiate(std::unique_lock &lock) { + std::shared_ptr + RuntimeInstancesPool::InstancePool::instantiate( + std::unique_lock &lock) { if (instances.empty()) { auto copy = module; lock.unlock(); diff --git a/core/runtime/common/runtime_instances_pool.hpp b/core/runtime/common/runtime_instances_pool.hpp index 84dc970598..6e1a2077e4 100644 --- a/core/runtime/common/runtime_instances_pool.hpp +++ b/core/runtime/common/runtime_instances_pool.hpp @@ -8,13 +8,15 @@ #include "runtime/module_repository.hpp" +#include #include -#include +#include +#include +#include "runtime/module_factory.hpp" #include "utils/lru.hpp" namespace kagome::runtime { - class ModuleFactory; /** * @brief Pool of runtime instances - per state. Encapsulates modules cache. @@ -22,18 +24,18 @@ namespace kagome::runtime { */ class RuntimeInstancesPool final : public std::enable_shared_from_this { - using ModuleInstancePool = std::stack>; + static constexpr size_t DEFAULT_MODULES_CACHE_SIZE = 2; public: - using RootHash = storage::trie::RootHash; - - RuntimeInstancesPool(); + using TrieHash = storage::trie::RootHash; + using CodeHash = storage::trie::RootHash; RuntimeInstancesPool(std::shared_ptr module_factory, - size_t capacity); + size_t capacity = DEFAULT_MODULES_CACHE_SIZE); - outcome::result> instantiate( - const RootHash &code_hash, common::BufferView code_zstd); + outcome::result, CompilationError> + instantiateFromCode(const CodeHash &code_hash, + common::BufferView code_zstd); /** * @brief Instantiate new or reuse existing ModuleInstance for the provided @@ -44,8 +46,8 @@ namespace kagome::runtime { * @return pointer to the acquired ModuleInstance if success. Error * otherwise. */ - outcome::result> tryAcquire( - const RootHash &state); + outcome::result> instantiateFromState( + const TrieHash &state); /** * @brief Releases the module instance (returns it to the pool) * @@ -53,7 +55,7 @@ namespace kagome::runtime { * module code we are releasing an instance of. * @param instance - instance to be released. */ - void release(const RootHash &state, + void release(const TrieHash &state, std::shared_ptr &&instance); /** @@ -63,30 +65,42 @@ namespace kagome::runtime { * @return Module if any, nullopt otherwise */ std::optional> getModule( - const RootHash &state); + const TrieHash &state); /** * @brief Puts new module into internal cache * - * @param state - runtime block, by its root hash + * @param state - storage hash of the block containing the code of the + * module * @param module - new module pointer */ - void putModule(const RootHash &state, std::shared_ptr module); + void putModule(const TrieHash &state, std::shared_ptr module); private: - struct Entry { + struct InstancePool { std::shared_ptr module; std::vector> instances; - outcome::result> instantiate( + std::shared_ptr instantiate( std::unique_lock &lock); }; + using CompilationResult = + outcome::result, CompilationError>; + CompilationResult tryCompileModule(const CodeHash &code_hash, + common::BufferView code_zstd); + + std::optional> getFutureCompiledModule( + const CodeHash &code_hash) const; + std::shared_ptr module_factory_; - std::mutex mt_; - static constexpr size_t MODULES_CACHE_SIZE = 2; - Lru pools_; + std::mutex pools_mtx_; + Lru pools_; + + mutable std::mutex compiling_modules_mtx_; + std::unordered_map> + compiling_modules_; }; } // namespace kagome::runtime diff --git a/core/runtime/common/runtime_transaction_error.cpp b/core/runtime/common/runtime_transaction_error.cpp deleted file mode 100644 index a8dac28cb9..0000000000 --- a/core/runtime/common/runtime_transaction_error.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include "runtime/common/runtime_transaction_error.hpp" - -OUTCOME_CPP_DEFINE_CATEGORY(kagome::runtime, RuntimeTransactionError, e) { - using E = kagome::runtime::RuntimeTransactionError; - switch (e) { - case E::NO_TRANSACTIONS_WERE_STARTED: - return "no transactions were started"; - case E::EXPORT_FUNCTION_NOT_FOUND: - return "Export function not found"; - } - return "unknown TransactionError"; -} diff --git a/core/runtime/common/runtime_transaction_error.hpp b/core/runtime/common/runtime_transaction_error.hpp deleted file mode 100644 index 54c6fee41c..0000000000 --- a/core/runtime/common/runtime_transaction_error.hpp +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -namespace kagome::runtime { - - /** - * @brief RuntimeTransactionError enum provides error codes for storage - * transactions mechanism - */ - enum class RuntimeTransactionError { // 0 is reserved for success - NO_TRANSACTIONS_WERE_STARTED = 1, - EXPORT_FUNCTION_NOT_FOUND, - }; -} // namespace kagome::runtime - -OUTCOME_HPP_DECLARE_ERROR(kagome::runtime, RuntimeTransactionError); diff --git a/core/runtime/common/trie_storage_provider_impl.cpp b/core/runtime/common/trie_storage_provider_impl.cpp index cd3415e526..1d95c0a2b4 100644 --- a/core/runtime/common/trie_storage_provider_impl.cpp +++ b/core/runtime/common/trie_storage_provider_impl.cpp @@ -6,7 +6,7 @@ #include "runtime/common/trie_storage_provider_impl.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "storage/trie/impl/topper_trie_batch_impl.hpp" #include "storage/trie/trie_batches.hpp" @@ -177,7 +177,7 @@ namespace kagome::runtime { outcome::result TrieStorageProviderImpl::rollbackTransaction() { if (transaction_stack_.empty()) { - return RuntimeTransactionError::NO_TRANSACTIONS_WERE_STARTED; + return RuntimeExecutionError::NO_TRANSACTIONS_WERE_STARTED; } SL_TRACE(logger_, @@ -189,7 +189,7 @@ namespace kagome::runtime { outcome::result TrieStorageProviderImpl::commitTransaction() { if (transaction_stack_.empty()) { - return RuntimeTransactionError::NO_TRANSACTIONS_WERE_STARTED; + return RuntimeExecutionError::NO_TRANSACTIONS_WERE_STARTED; } OUTCOME_TRY(transaction_stack_.back().main_batch->writeBack()); diff --git a/core/runtime/common/trie_storage_provider_impl.hpp b/core/runtime/common/trie_storage_provider_impl.hpp index f20713bb76..a7a7f89edb 100644 --- a/core/runtime/common/trie_storage_provider_impl.hpp +++ b/core/runtime/common/trie_storage_provider_impl.hpp @@ -13,7 +13,7 @@ #include "common/buffer.hpp" #include "log/logger.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "storage/trie/serialization/trie_serializer.hpp" #include "storage/trie/trie_storage.hpp" diff --git a/core/runtime/common/uncompress_code_if_needed.cpp b/core/runtime/common/uncompress_code_if_needed.cpp index c3c2e8b0bd..d9baa53a46 100644 --- a/core/runtime/common/uncompress_code_if_needed.cpp +++ b/core/runtime/common/uncompress_code_if_needed.cpp @@ -31,7 +31,7 @@ namespace kagome::runtime { // https://github.com/paritytech/substrate/blob/polkadot-v0.9.8/primitives/maybe-compressed-blob/src/lib.rs#L35 constexpr size_t kCodeBlobBombLimit = 50 * 1024 * 1024; - outcome::result uncompressCodeIfNeeded(common::BufferView buf, + outcome::result uncompressCodeIfNeeded(common::BufferView buf, common::Buffer &res) { if (startsWith(buf, kZstdPrefix)) { auto zstd = buf.subspan(std::size(kZstdPrefix)); diff --git a/core/runtime/common/uncompress_code_if_needed.hpp b/core/runtime/common/uncompress_code_if_needed.hpp index 6c1666dd1a..ef88c757ef 100644 --- a/core/runtime/common/uncompress_code_if_needed.hpp +++ b/core/runtime/common/uncompress_code_if_needed.hpp @@ -14,8 +14,14 @@ namespace kagome::runtime { BOMB_SIZE_REACHED, }; - outcome::result uncompressCodeIfNeeded(common::BufferView buf, - common::Buffer &res); + outcome::result uncompressCodeIfNeeded( + common::BufferView buf, common::Buffer &res); } // namespace kagome::runtime OUTCOME_HPP_DECLARE_ERROR(kagome::runtime, UncompressError); + +namespace kagome::runtime { + inline auto format_as(UncompressError e) { + return make_error_code(e); + } +} diff --git a/core/runtime/module.hpp b/core/runtime/module.hpp index 77cc6ec9bb..10444a8467 100644 --- a/core/runtime/module.hpp +++ b/core/runtime/module.hpp @@ -25,8 +25,7 @@ namespace kagome::runtime { public: virtual ~Module() = default; - virtual outcome::result> instantiate() - const = 0; + virtual std::shared_ptr instantiate() const = 0; }; /** diff --git a/core/runtime/module_factory.hpp b/core/runtime/module_factory.hpp index 5425d36378..dc920ff413 100644 --- a/core/runtime/module_factory.hpp +++ b/core/runtime/module_factory.hpp @@ -10,17 +10,35 @@ #include "outcome/outcome.hpp" #include "runtime/instance_environment.hpp" +#include "runtime/types.hpp" #include "storage/trie/types.hpp" namespace kagome::runtime { class Module; + struct CompilationError : std::runtime_error { + CompilationError(const std::string& message) + : std::runtime_error(message.c_str()) {} + + std::string_view message() const { + return what(); + } + }; + + inline std::error_code make_error_code(CompilationError) { + return Error::COMPILATION_FAILED; + } + + inline void outcome_throw_as_system_error_with_payload(CompilationError e) { + throw e; + } + class ModuleFactory { public: virtual ~ModuleFactory() = default; - virtual outcome::result> make( + virtual outcome::result, CompilationError> make( common::BufferView code) const = 0; }; diff --git a/core/runtime/module_repository.hpp b/core/runtime/module_repository.hpp index e76fb5b8b2..1c6c1f050c 100644 --- a/core/runtime/module_repository.hpp +++ b/core/runtime/module_repository.hpp @@ -28,6 +28,7 @@ namespace kagome::runtime { */ class ModuleRepository { public: + virtual ~ModuleRepository() = default; /** diff --git a/core/runtime/runtime_api/impl/beefy.cpp b/core/runtime/runtime_api/impl/beefy.cpp index f50b415d6c..6c10c61bc2 100644 --- a/core/runtime/runtime_api/impl/beefy.cpp +++ b/core/runtime/runtime_api/impl/beefy.cpp @@ -6,7 +6,7 @@ #include "runtime/runtime_api/impl/beefy.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/executor.hpp" namespace kagome::runtime { @@ -22,7 +22,7 @@ namespace kagome::runtime { if (r) { return std::move(r.value()); } - if (r.error() == RuntimeTransactionError::EXPORT_FUNCTION_NOT_FOUND) { + if (r.error() == RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND) { return std::nullopt; } return r.error(); diff --git a/core/runtime/runtime_api/impl/parachain_host.cpp b/core/runtime/runtime_api/impl/parachain_host.cpp index 5f7ea6979a..53d4ae8a10 100644 --- a/core/runtime/runtime_api/impl/parachain_host.cpp +++ b/core/runtime/runtime_api/impl/parachain_host.cpp @@ -9,6 +9,7 @@ #include "common/blob.hpp" #include "runtime/executor.hpp" #include "runtime/runtime_api/impl/parachain_host_types_serde.hpp" +#include "scale/std_variant.hpp" namespace kagome::runtime { diff --git a/core/runtime/runtime_api/parachain_host_types.hpp b/core/runtime/runtime_api/parachain_host_types.hpp index b9f244d3bc..a70695a9f9 100644 --- a/core/runtime/runtime_api/parachain_host_types.hpp +++ b/core/runtime/runtime_api/parachain_host_types.hpp @@ -121,11 +121,13 @@ namespace kagome::runtime { } }; + using FreeCore = Empty; + using ValidatorGroupsAndDescriptor = std::tuple, GroupDescriptor>; - using CoreState = boost::variant>; // 2 + using CoreState = std::variant; // 2 enum class OccupiedCoreAssumption : uint8_t { Included, // 0 TimedOut, // 1 diff --git a/core/runtime/types.hpp b/core/runtime/types.hpp index 32f636ca6d..6aac0d259d 100644 --- a/core/runtime/types.hpp +++ b/core/runtime/types.hpp @@ -10,6 +10,8 @@ #include #include +#include "outcome/outcome.hpp" + namespace kagome::runtime { /** * @brief type of wasm log levels @@ -71,4 +73,11 @@ namespace kagome::runtime { return {minor_part, major_part}; } + + enum class Error { + COMPILATION_FAILED = 1, + }; + } // namespace kagome::runtime + +OUTCOME_HPP_DECLARE_ERROR(kagome::runtime, Error); diff --git a/core/runtime/wavm/core_api_factory_impl.cpp b/core/runtime/wavm/core_api_factory_impl.cpp index 2b34fd8260..86c75d906e 100644 --- a/core/runtime/wavm/core_api_factory_impl.cpp +++ b/core/runtime/wavm/core_api_factory_impl.cpp @@ -50,14 +50,17 @@ namespace kagome::runtime::wavm { const primitives::BlockInfo &, const storage::trie::RootHash &) override { if (instance_ == nullptr) { - auto module = ModuleImpl::compileFrom(compartment_, - *module_params_, - intrinsic_module_, - instance_env_factory_, - code_, - code_hash_); - OUTCOME_TRY(inst, module->instantiate()); - last_compiled_module_->set(std::move(module)); + auto module_res = ModuleImpl::compileFrom(compartment_, + *module_params_, + intrinsic_module_, + instance_env_factory_, + code_, + code_hash_); + if (!module_res) { + return make_error_code(module_res.error()); + } + auto inst = module_res.value()->instantiate(); + last_compiled_module_->set(std::move(module_res.value())); instance_ = std::move(inst); } return instance_; diff --git a/core/runtime/wavm/module.cpp b/core/runtime/wavm/module.cpp index 1f3dda225c..0345dd0e42 100644 --- a/core/runtime/wavm/module.cpp +++ b/core/runtime/wavm/module.cpp @@ -21,7 +21,8 @@ namespace kagome::runtime::wavm { - std::shared_ptr ModuleImpl::compileFrom( + outcome::result, CompilationError> + ModuleImpl::compileFrom( std::shared_ptr compartment, ModuleParams &module_params, std::shared_ptr intrinsic_module, @@ -35,13 +36,13 @@ namespace kagome::runtime::wavm { featureSpec.extendedNameSection = true; log::Logger logger = log::createLogger("WAVM Module", "wavm"); logger->info( - "Compiling WebAssembly module for Runtime (going to take a few dozens " - "of seconds)"); + "Compiling WebAssembly module with code hash {} (going to " + "take a few dozens of seconds)", + code_hash); if (!WAVM::Runtime::loadBinaryModule( code.data(), code.size(), module, featureSpec, &loadError)) { - logger->critical("Error loading WAVM binary module: {}", - loadError.message); - return nullptr; + logger->warn("Error loading WAVM binary module: {}", loadError.message); + return CompilationError{std::move(loadError.message)}; } auto &imports = WAVM::Runtime::getModuleIR(module).memories.imports; @@ -77,8 +78,7 @@ namespace kagome::runtime::wavm { BOOST_ASSERT(module_); } - outcome::result> ModuleImpl::instantiate() - const { + std::shared_ptr ModuleImpl::instantiate() const { #if defined(__GNUC__) and not defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdangling-reference" diff --git a/core/runtime/wavm/module.hpp b/core/runtime/wavm/module.hpp index 70c172d3a3..0b3aa78f7d 100644 --- a/core/runtime/wavm/module.hpp +++ b/core/runtime/wavm/module.hpp @@ -12,6 +12,7 @@ #include "common/blob.hpp" #include "log/logger.hpp" +#include "runtime/module_factory.hpp" namespace WAVM::Runtime { struct Compartment; @@ -31,15 +32,15 @@ namespace kagome::runtime::wavm { class ModuleImpl final : public runtime::Module, public std::enable_shared_from_this { public: - static std::shared_ptr compileFrom( - std::shared_ptr compartment, - ModuleParams &module_params, - std::shared_ptr intrinsic_module, - std::shared_ptr env_factory, - common::BufferView code, - const common::Hash256 &code_hash); + static outcome::result, CompilationError> + compileFrom(std::shared_ptr compartment, + ModuleParams &module_params, + std::shared_ptr intrinsic_module, + std::shared_ptr env_factory, + common::BufferView code, + const common::Hash256 &code_hash); - outcome::result> instantiate() + std::shared_ptr instantiate() const override; ModuleImpl(std::shared_ptr compartment, diff --git a/core/runtime/wavm/module_factory_impl.cpp b/core/runtime/wavm/module_factory_impl.cpp index af8ab6350d..e5de5d5003 100644 --- a/core/runtime/wavm/module_factory_impl.cpp +++ b/core/runtime/wavm/module_factory_impl.cpp @@ -36,14 +36,16 @@ namespace kagome::runtime::wavm { } } - outcome::result> ModuleFactoryImpl::make( - common::BufferView code) const { - return ModuleImpl::compileFrom(compartment_, - *module_params_, - intrinsic_module_, - env_factory_, - code, - hasher_->sha2_256(code)); + outcome::result, CompilationError> + ModuleFactoryImpl::make(common::BufferView code) const { + OUTCOME_TRY(module, + ModuleImpl::compileFrom(compartment_, + *module_params_, + intrinsic_module_, + env_factory_, + code, + hasher_->sha2_256(code))); + 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 04934d0f2b..58b4681846 100644 --- a/core/runtime/wavm/module_factory_impl.hpp +++ b/core/runtime/wavm/module_factory_impl.hpp @@ -35,7 +35,7 @@ namespace kagome::runtime::wavm { std::optional> module_cache, std::shared_ptr hasher); - outcome::result> make( + outcome::result, CompilationError> make( common::BufferView code) const override; private: diff --git a/core/runtime/wavm/module_instance.cpp b/core/runtime/wavm/module_instance.cpp index 2e7c760ab0..b4fcecface 100644 --- a/core/runtime/wavm/module_instance.cpp +++ b/core/runtime/wavm/module_instance.cpp @@ -13,7 +13,7 @@ #include "host_api/host_api.hpp" #include "log/profiling_logger.hpp" -#include "runtime/common/runtime_transaction_error.hpp" +#include "runtime/common/runtime_execution_error.hpp" #include "runtime/memory_provider.hpp" #include "runtime/module_repository.hpp" #include "runtime/trie_storage_provider.hpp" @@ -119,7 +119,7 @@ namespace kagome::runtime::wavm { WAVM::Runtime::getInstanceExport(instance_, name.data())); if (!function) { SL_DEBUG(logger_, "The requested function {} not found", name); - return RuntimeTransactionError::EXPORT_FUNCTION_NOT_FOUND; + return RuntimeExecutionError::EXPORT_FUNCTION_NOT_FOUND; } const WAVM::IR::FunctionType functionType = WAVM::Runtime::getFunctionType(function); diff --git a/core/scale/CMakeLists.txt b/core/scale/CMakeLists.txt index 528e18e2bb..8a87402a4f 100644 --- a/core/scale/CMakeLists.txt +++ b/core/scale/CMakeLists.txt @@ -12,3 +12,5 @@ target_link_libraries(scale_libp2p_types scale::scale p2p::p2p ) + +kagome_install(scale_libp2p_types) diff --git a/core/scale/std_variant.hpp b/core/scale/std_variant.hpp new file mode 100644 index 0000000000..bea6949ea7 --- /dev/null +++ b/core/scale/std_variant.hpp @@ -0,0 +1,43 @@ +/** + * Copyright Quadrivium LLC All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +#include + +namespace scale { + + template + requires Stream::is_encoder_stream + Stream &operator<<(Stream &stream, const std::variant &variant) { + stream << static_cast(variant.index()); + std::visit([&stream](const auto &v) { stream << v; }, variant); + return stream; + } + + template + requires Stream::is_decoder_stream + constexpr auto make_decoder() { + return [](Stream &stream, std::variant &variant) { + variant.template emplace