Skip to content

Commit 82e3c0f

Browse files
authored
pvf precheck (#1705)
Signed-off-by: turuslan <[email protected]>
1 parent f0132ae commit 82e3c0f

File tree

11 files changed

+243
-10
lines changed

11 files changed

+243
-10
lines changed

core/parachain/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_library(validator_parachain
1111
availability/recovery/recovery_impl.cpp
1212
availability/store/store_impl.cpp
1313
backing/store_impl.cpp
14+
pvf/precheck.cpp
1415
pvf/pvf_impl.cpp
1516
validator/impl/parachain_observer_impl.cpp
1617
validator/impl/parachain_processor.cpp

core/parachain/availability/bitfield/signer.cpp

+3-9
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,6 @@
1111
namespace kagome::parachain {
1212
constexpr std::chrono::milliseconds kDelay{1500};
1313

14-
namespace {
15-
inline auto log() {
16-
return log::createLogger("BitfieldSigner");
17-
}
18-
} // namespace
19-
2014
BitfieldSigner::BitfieldSigner(
2115
std::shared_ptr<crypto::Hasher> hasher,
2216
std::shared_ptr<ValidatorSignerFactory> signer_factory,
@@ -52,7 +46,7 @@ namespace kagome::parachain {
5246
boost::get<primitives::events::HeadsEventParams>(event))
5347
.value()));
5448
if (r.has_error()) {
55-
SL_DEBUG(log(), "onBlock error {}", r.error());
49+
SL_DEBUG(self->logger_, "onBlock error {}", r.error());
5650
}
5751
}
5852
});
@@ -66,7 +60,7 @@ namespace kagome::parachain {
6660

6761
outcome::result<void> BitfieldSigner::sign(const ValidatorSigner &signer,
6862
const Candidates &candidates) {
69-
BlockHash const &relay_parent = signer.relayParent();
63+
const BlockHash &relay_parent = signer.relayParent();
7064
scale::BitVec bitfield;
7165
bitfield.bits.reserve(candidates.size());
7266
for (auto &candidate : candidates) {
@@ -125,7 +119,7 @@ namespace kagome::parachain {
125119
if (auto self = weak.lock()) {
126120
auto r = self->sign(signer, candidates);
127121
if (r.has_error()) {
128-
SL_WARN(log(), "sign error {}", r.error());
122+
SL_WARN(self->logger_, "sign error {}", r.error());
129123
}
130124
}
131125
},

core/parachain/pvf/precheck.cpp

+107
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
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

core/parachain/pvf/precheck.hpp

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Copyright Soramitsu Co., Ltd. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
#ifndef KAGOME_PARACHAIN_PVF_PRECHECK_HPP
7+
#define KAGOME_PARACHAIN_PVF_PRECHECK_HPP
8+
9+
#include <unordered_set>
10+
11+
#include "crypto/hasher.hpp"
12+
#include "log/logger.hpp"
13+
#include "parachain/validator/signer.hpp"
14+
#include "primitives/event_types.hpp"
15+
#include "runtime/runtime_api/parachain_host.hpp"
16+
#include "utils/thread_pool.hpp"
17+
18+
namespace kagome::offchain {
19+
class OffchainWorkerFactory;
20+
class OffchainWorkerPool;
21+
} // namespace kagome::offchain
22+
23+
namespace kagome::runtime {
24+
class Executor;
25+
class ModuleFactory;
26+
} // namespace kagome::runtime
27+
28+
namespace kagome::parachain {
29+
/// Signs pvf check statement for every new head.
30+
class PvfPrecheck : public std::enable_shared_from_this<PvfPrecheck> {
31+
public:
32+
using BroadcastCallback = std::function<void(
33+
const primitives::BlockHash &, const network::SignedBitfield &)>;
34+
using Candidates = std::vector<std::optional<network::CandidateHash>>;
35+
36+
PvfPrecheck(
37+
std::shared_ptr<crypto::Hasher> hasher,
38+
std::shared_ptr<ValidatorSignerFactory> signer_factory,
39+
std::shared_ptr<runtime::ParachainHost> parachain_api,
40+
std::shared_ptr<runtime::ModuleFactory> module_factory,
41+
std::shared_ptr<runtime::Executor> executor,
42+
std::shared_ptr<offchain::OffchainWorkerFactory>
43+
offchain_worker_factory,
44+
std::shared_ptr<offchain::OffchainWorkerPool> offchain_worker_pool);
45+
46+
/// Subscribes to new heads.
47+
void start(std::shared_ptr<primitives::events::ChainSubscriptionEngine>
48+
chain_sub_engine);
49+
50+
private:
51+
using BlockHash = primitives::BlockHash;
52+
53+
outcome::result<void> onBlock(const BlockHash &block_hash,
54+
const primitives::BlockHeader &header);
55+
56+
std::shared_ptr<crypto::Hasher> hasher_;
57+
std::shared_ptr<ValidatorSignerFactory> signer_factory_;
58+
std::shared_ptr<runtime::ParachainHost> parachain_api_;
59+
std::shared_ptr<runtime::ModuleFactory> module_factory_;
60+
std::shared_ptr<runtime::Executor> executor_;
61+
std::shared_ptr<offchain::OffchainWorkerFactory> offchain_worker_factory_;
62+
std::shared_ptr<offchain::OffchainWorkerPool> offchain_worker_pool_;
63+
std::shared_ptr<primitives::events::ChainEventSubscriber> chain_sub_;
64+
std::unordered_set<ValidationCodeHash> seen_;
65+
ThreadPool thread_{"PvfPrecheck", 1};
66+
log::Logger logger_ = log::createLogger("PvfPrecheck", "parachain");
67+
};
68+
} // namespace kagome::parachain
69+
70+
#endif // KAGOME_PARACHAIN_PVF_PRECHECK_HPP

core/parachain/types.hpp

+14-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ namespace kagome::parachain {
6969
using IndexedAndSigned = kagome::crypto::Sr25519Signed<Indexed<T>>;
7070

7171
template <typename T>
72-
[[maybe_unused]] inline T const &getPayload(IndexedAndSigned<T> const &t) {
72+
[[maybe_unused]] inline const T &getPayload(const IndexedAndSigned<T> &t) {
7373
return t.payload.payload;
7474
}
7575

@@ -78,6 +78,19 @@ namespace kagome::parachain {
7878
return t.payload.payload;
7979
}
8080

81+
struct PvfCheckStatement {
82+
SCALE_TIE(4);
83+
84+
bool accept;
85+
ValidationCodeHash subject;
86+
SessionIndex session_index;
87+
ValidatorIndex validator_index;
88+
89+
auto signable() {
90+
constexpr std::array<uint8_t, 4> kMagic{'V', 'C', 'P', 'C'};
91+
return scale::encode(std::make_tuple(kMagic, *this)).value();
92+
}
93+
};
8194
} // namespace kagome::parachain
8295

8396
#endif // KAGOME_PARACHAIN_PRIMITIVES_HPP

core/parachain/validator/impl/parachain_processor.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ namespace kagome::parachain {
6767
std::shared_ptr<network::PeerView> peer_view,
6868
std::shared_ptr<ThreadPool> thread_pool,
6969
std::shared_ptr<parachain::BitfieldSigner> bitfield_signer,
70+
std::shared_ptr<parachain::PvfPrecheck> pvf_precheck,
7071
std::shared_ptr<parachain::BitfieldStore> bitfield_store,
7172
std::shared_ptr<parachain::BackingStore> backing_store,
7273
std::shared_ptr<parachain::Pvf> pvf,
@@ -87,6 +88,7 @@ namespace kagome::parachain {
8788
pvf_(std::move(pvf)),
8889
signer_factory_(std::move(signer_factory)),
8990
bitfield_signer_(std::move(bitfield_signer)),
91+
pvf_precheck_(std::move(pvf_precheck)),
9092
bitfield_store_(std::move(bitfield_store)),
9193
backing_store_(std::move(backing_store)),
9294
av_store_(std::move(av_store)),
@@ -147,6 +149,8 @@ namespace kagome::parachain {
147149
if (not was_synchronized) {
148150
self->bitfield_signer_->start(
149151
self->peer_view_->intoChainEventsEngine());
152+
self->pvf_precheck_->start(
153+
self->peer_view_->intoChainEventsEngine());
150154
was_synchronized = true;
151155
}
152156
}

core/parachain/validator/parachain_processor.hpp

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "parachain/availability/bitfield/signer.hpp"
3131
#include "parachain/availability/store/store.hpp"
3232
#include "parachain/backing/store.hpp"
33+
#include "parachain/pvf/precheck.hpp"
3334
#include "parachain/pvf/pvf.hpp"
3435
#include "parachain/validator/signer.hpp"
3536
#include "primitives/common.hpp"
@@ -84,6 +85,7 @@ namespace kagome::parachain {
8485
std::shared_ptr<network::PeerView> peer_view,
8586
std::shared_ptr<ThreadPool> thread_pool,
8687
std::shared_ptr<parachain::BitfieldSigner> bitfield_signer,
88+
std::shared_ptr<parachain::PvfPrecheck> pvf_precheck,
8789
std::shared_ptr<parachain::BitfieldStore> bitfield_store,
8890
std::shared_ptr<parachain::BackingStore> backing_store,
8991
std::shared_ptr<parachain::Pvf> pvf,
@@ -417,6 +419,7 @@ namespace kagome::parachain {
417419
std::shared_ptr<parachain::Pvf> pvf_;
418420
std::shared_ptr<parachain::ValidatorSignerFactory> signer_factory_;
419421
std::shared_ptr<parachain::BitfieldSigner> bitfield_signer_;
422+
std::shared_ptr<parachain::PvfPrecheck> pvf_precheck_;
420423
std::shared_ptr<parachain::BitfieldStore> bitfield_store_;
421424
std::shared_ptr<parachain::BackingStore> backing_store_;
422425
std::shared_ptr<parachain::AvailabilityStore> av_store_;

core/parachain/validator/signer.hpp

+4
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ namespace kagome::parachain {
7474
};
7575
}
7676

77+
outcome::result<Signature> signRaw(common::BufferView data) const {
78+
return sr25519_provider_->sign(*keypair_, data);
79+
}
80+
7781
SessionIndex getSessionIndex() const;
7882

7983
/// Get validator index.

core/runtime/runtime_api/impl/parachain_host.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,19 @@ namespace kagome::runtime {
131131
block, "ParachainHost_inbound_hrmp_channels_contents", id);
132132
}
133133

134+
outcome::result<std::vector<ValidationCodeHash>>
135+
ParachainHostImpl::pvfs_require_precheck(const primitives::BlockHash &block) {
136+
return executor_->callAt<std::vector<ValidationCodeHash>>(
137+
block, "ParachainHost_pvfs_require_precheck");
138+
}
139+
140+
outcome::result<void> ParachainHostImpl::submit_pvf_check_statement(
141+
const primitives::BlockHash &block,
142+
const parachain::PvfCheckStatement &statement,
143+
const parachain::Signature &signature) {
144+
return executor_->callAt<void>(block,
145+
"ParachainHost_submit_pvf_check_statement",
146+
statement,
147+
signature);
148+
}
134149
} // namespace kagome::runtime

core/runtime/runtime_api/impl/parachain_host.hpp

+8
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,14 @@ namespace kagome::runtime {
7676
inbound_hrmp_channels_contents(const primitives::BlockHash &block,
7777
ParachainId id) override;
7878

79+
outcome::result<std::vector<ValidationCodeHash>> pvfs_require_precheck(
80+
const primitives::BlockHash &block) override;
81+
82+
outcome::result<void> submit_pvf_check_statement(
83+
const primitives::BlockHash &block,
84+
const parachain::PvfCheckStatement &statement,
85+
const parachain::Signature &signature) override;
86+
7987
private:
8088
std::shared_ptr<Executor> executor_;
8189
};

core/runtime/runtime_api/parachain_host.hpp

+14
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ namespace kagome::runtime {
180180
std::map<ParachainId, std::vector<InboundHrmpMessage>>>
181181
inbound_hrmp_channels_contents(const primitives::BlockHash &block,
182182
ParachainId id) = 0;
183+
184+
/**
185+
* @return list of pvf requiring precheck
186+
*/
187+
virtual outcome::result<std::vector<ValidationCodeHash>>
188+
pvfs_require_precheck(const primitives::BlockHash &block) = 0;
189+
190+
/**
191+
* @return submit pvf check statement
192+
*/
193+
virtual outcome::result<void> submit_pvf_check_statement(
194+
const primitives::BlockHash &block,
195+
const parachain::PvfCheckStatement &statement,
196+
const parachain::Signature &signature) = 0;
183197
};
184198

185199
} // namespace kagome::runtime

0 commit comments

Comments
 (0)