|
| 1 | +/** |
| 2 | + * Copyright Soramitsu Co., Ltd. All Rights Reserved. |
| 3 | + * SPDX-License-Identifier: Apache-2.0 |
| 4 | + */ |
| 5 | + |
| 6 | +#include "parachain/pvf/precheck.hpp" |
| 7 | + |
| 8 | +#include "offchain/offchain_worker_factory.hpp" |
| 9 | +#include "offchain/offchain_worker_pool.hpp" |
| 10 | +#include "runtime/common/uncompress_code_if_needed.hpp" |
| 11 | +#include "runtime/module.hpp" |
| 12 | +#include "runtime/module_factory.hpp" |
| 13 | + |
| 14 | +namespace kagome::parachain { |
| 15 | + PvfPrecheck::PvfPrecheck( |
| 16 | + std::shared_ptr<crypto::Hasher> hasher, |
| 17 | + std::shared_ptr<ValidatorSignerFactory> signer_factory, |
| 18 | + std::shared_ptr<runtime::ParachainHost> parachain_api, |
| 19 | + std::shared_ptr<runtime::ModuleFactory> module_factory, |
| 20 | + std::shared_ptr<runtime::Executor> executor, |
| 21 | + std::shared_ptr<offchain::OffchainWorkerFactory> offchain_worker_factory, |
| 22 | + std::shared_ptr<offchain::OffchainWorkerPool> offchain_worker_pool) |
| 23 | + : hasher_{std::move(hasher)}, |
| 24 | + signer_factory_{std::move(signer_factory)}, |
| 25 | + parachain_api_{std::move(parachain_api)}, |
| 26 | + module_factory_{std::move(module_factory)}, |
| 27 | + executor_{std::move(executor)}, |
| 28 | + offchain_worker_factory_{std::move(offchain_worker_factory)}, |
| 29 | + offchain_worker_pool_{std::move(offchain_worker_pool)} {} |
| 30 | + |
| 31 | + void PvfPrecheck::start( |
| 32 | + std::shared_ptr<primitives::events::ChainSubscriptionEngine> |
| 33 | + chain_sub_engine) { |
| 34 | + chain_sub_ = std::make_shared<primitives::events::ChainEventSubscriber>( |
| 35 | + chain_sub_engine); |
| 36 | + chain_sub_->subscribe(chain_sub_->generateSubscriptionSetId(), |
| 37 | + primitives::events::ChainEventType::kNewHeads); |
| 38 | + chain_sub_->setCallback( |
| 39 | + [weak = weak_from_this()]( |
| 40 | + subscription::SubscriptionSetId, |
| 41 | + auto &&, |
| 42 | + primitives::events::ChainEventType, |
| 43 | + const primitives::events::ChainEventParams &event) { |
| 44 | + if (auto self = weak.lock()) { |
| 45 | + self->thread_.io_context()->post( |
| 46 | + [weak, |
| 47 | + header{boost::get<primitives::events::HeadsEventParams>(event) |
| 48 | + .get()}] { |
| 49 | + if (auto self = weak.lock()) { |
| 50 | + auto block_hash = self->hasher_->blake2b_256( |
| 51 | + scale::encode(header).value()); |
| 52 | + auto r = self->onBlock(block_hash, header); |
| 53 | + if (r.has_error()) { |
| 54 | + SL_DEBUG(self->logger_, "onBlock error {}", r.error()); |
| 55 | + } |
| 56 | + } |
| 57 | + }); |
| 58 | + } |
| 59 | + }); |
| 60 | + } |
| 61 | + |
| 62 | + outcome::result<void> PvfPrecheck::onBlock( |
| 63 | + const BlockHash &block_hash, const primitives::BlockHeader &header) { |
| 64 | + OUTCOME_TRY(signer, signer_factory_->at(block_hash)); |
| 65 | + if (not signer.has_value()) { |
| 66 | + return outcome::success(); |
| 67 | + } |
| 68 | + OUTCOME_TRY(need, parachain_api_->pvfs_require_precheck(block_hash)); |
| 69 | + for (auto &code_hash : need) { |
| 70 | + if (not seen_.emplace(code_hash).second) { |
| 71 | + continue; |
| 72 | + } |
| 73 | + auto code_zstd_res = |
| 74 | + parachain_api_->validation_code_by_hash(block_hash, code_hash); |
| 75 | + if (not code_zstd_res or not code_zstd_res.value()) { |
| 76 | + seen_.erase(code_hash); |
| 77 | + continue; |
| 78 | + } |
| 79 | + auto &code_zstd = *code_zstd_res.value(); |
| 80 | + ParachainRuntime code; |
| 81 | + auto res = [&]() -> outcome::result<void> { |
| 82 | + OUTCOME_TRY(runtime::uncompressCodeIfNeeded(code_zstd, code)); |
| 83 | + OUTCOME_TRY(module_factory_->make(code)); |
| 84 | + return outcome::success(); |
| 85 | + }(); |
| 86 | + if (res) { |
| 87 | + SL_VERBOSE(logger_, "approve {}", code_hash); |
| 88 | + } else { |
| 89 | + SL_WARN(logger_, "reject {}: {}", code_hash, res.error()); |
| 90 | + } |
| 91 | + PvfCheckStatement statement{ |
| 92 | + res.has_value(), |
| 93 | + code_hash, |
| 94 | + signer->getSessionIndex(), |
| 95 | + signer->validatorIndex(), |
| 96 | + }; |
| 97 | + OUTCOME_TRY(signature, signer->signRaw(statement.signable())); |
| 98 | + offchain_worker_pool_->addWorker( |
| 99 | + offchain_worker_factory_->make(executor_, header)); |
| 100 | + auto remove = |
| 101 | + gsl::finally([&] { offchain_worker_pool_->removeWorker(); }); |
| 102 | + OUTCOME_TRY(parachain_api_->submit_pvf_check_statement( |
| 103 | + block_hash, statement, signature)); |
| 104 | + } |
| 105 | + return outcome::success(); |
| 106 | + } |
| 107 | +} // namespace kagome::parachain |
0 commit comments