Skip to content

Commit

Permalink
Merge branch 'master' into enchancement/small-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsa authored Nov 28, 2024
2 parents 435cbd0 + f016055 commit ce058ed
Show file tree
Hide file tree
Showing 27 changed files with 513 additions and 138 deletions.
2 changes: 0 additions & 2 deletions core/network/peer_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ namespace kagome::network {
template <>
struct std::hash<kagome::network::FetchedCollation> {
size_t operator()(const kagome::network::FetchedCollation &value) const {
using CollatorId = kagome::parachain::CollatorId;
using CandidateHash = kagome::parachain::CandidateHash;
using RelayHash = kagome::parachain::RelayHash;
using ParachainId = kagome::parachain::ParachainId;
Expand All @@ -108,7 +107,6 @@ struct std::hash<kagome::network::FetchedCollation> {
boost::hash_combine(result, std::hash<ParachainId>()(value.para_id));
boost::hash_combine(result,
std::hash<CandidateHash>()(value.candidate_hash));
boost::hash_combine(result, std::hash<CollatorId>()(value.collator_id));

return result;
}
Expand Down
7 changes: 2 additions & 5 deletions core/network/types/collator_messages_vstaging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -447,16 +447,12 @@ namespace kagome::network {
};

struct FetchedCollation {
SCALE_TIE(4);

/// Candidate's relay parent.
RelayHash relay_parent;
/// Parachain id.
ParachainId para_id;
/// Candidate hash.
CandidateHash candidate_hash;
/// Id of the collator the collation was fetched from.
CollatorId collator_id;

static FetchedCollation from(const network::CandidateReceipt &receipt,
const crypto::Hasher &hasher) {
Expand All @@ -465,9 +461,10 @@ namespace kagome::network {
.relay_parent = descriptor.relay_parent,
.para_id = descriptor.para_id,
.candidate_hash = receipt.hash(hasher),
.collator_id = descriptor.collator_id,
};
}

bool operator==(const FetchedCollation &) const = default;
};

/**
Expand Down
12 changes: 3 additions & 9 deletions core/parachain/approval/approval_distribution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,15 +1115,9 @@ namespace kagome::parachain {
}

bool enable_v2_assignments = false;
if (auto r = parachain_host_->node_features(block_hash, session_index);
r.has_value()) {
if (r.value()
&& r.value()->bits.size() > runtime::ParachainHost::NodeFeatureIndex::
EnableAssignmentsV2) {
enable_v2_assignments =
r.value()->bits
[runtime::ParachainHost::NodeFeatureIndex::EnableAssignmentsV2];
}
if (auto r = parachain_host_->node_features(block_hash); r.has_value()) {
enable_v2_assignments =
r.value().has(runtime::NodeFeatures::EnableAssignmentsV2);
}

approval::UnsafeVRFOutput unsafe_vrf{
Expand Down
21 changes: 3 additions & 18 deletions core/parachain/availability/availability_chunk_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,31 +44,16 @@ namespace kagome::parachain {
}

inline bool availability_chunk_mapping_is_enabled(
std::optional<runtime::ParachainHost::NodeFeatures> node_features) {
// none if node_features is not defined
[[unlikely]] if (not node_features.has_value()) { //
return false;
}

const auto &features = node_features.value();

static const auto feature =
runtime::ParachainHost::NodeFeatureIndex::AvailabilityChunkMapping;

// none if needed feature is out of bound
[[unlikely]] if (feature >= features.bits.size()) { //
return false;
}

return features.bits[feature];
const runtime::NodeFeatures &node_features) {
return node_features.has(runtime::NodeFeatures::AvailabilityChunkMapping);
}

/// Compute the per-validator availability chunk index.
/// WARNING: THIS FUNCTION IS CRITICAL TO PARACHAIN CONSENSUS.
/// Any modification to the output of the function needs to be coordinated via
/// the runtime. It's best to use minimal/no external dependencies.
inline outcome::result<ChunkIndex> availability_chunk_index(
std::optional<runtime::ParachainHost::NodeFeatures> node_features,
const runtime::NodeFeatures &node_features,
size_t n_validators,
CoreIndex core_index,
ValidatorIndex validator_index) {
Expand Down
4 changes: 1 addition & 3 deletions core/parachain/availability/bitfield/signer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,7 @@ namespace kagome::parachain {
OUTCOME_TRY(
session,
parachain_api_->session_info(relay_parent, signer->getSessionIndex()));
OUTCOME_TRY(
node_features,
parachain_api_->node_features(relay_parent, signer->getSessionIndex()));
OUTCOME_TRY(node_features, parachain_api_->node_features(relay_parent));
candidates.reserve(cores.size());
for (CoreIndex core_index = 0; core_index < cores.size(); ++core_index) {
auto &core = cores[core_index];
Expand Down
3 changes: 1 addition & 2 deletions core/parachain/availability/recovery/recovery_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,7 @@ namespace kagome::parachain {
cb(_min.error());
return;
}
auto _node_features =
parachain_api_->node_features(block.hash, session_index);
auto _node_features = parachain_api_->node_features(block.hash);
if (_node_features.has_error()) {
lock.unlock();
cb(_node_features.error());
Expand Down
169 changes: 169 additions & 0 deletions core/parachain/candidate_descriptor_v2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <boost/endian/conversion.hpp>

#include "crypto/sr25519_provider.hpp"
#include "parachain/pvf/pvf_error.hpp"
#include "parachain/transpose_claim_queue.hpp"
#include "parachain/types.hpp"
#include "parachain/ump_signal.hpp"

namespace kagome::network {
// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L44
/// The default claim queue offset to be used if it's not
/// configured/accessible in the parachain
/// runtime
constexpr uint8_t kDefaultClaimQueueOffset = 0;

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L532-L534
inline bool isV1(const CandidateDescriptor &descriptor) {
constexpr auto is_zero = [](BufferView xs) {
return static_cast<size_t>(std::ranges::count(xs, 0)) == xs.size();
};
return not is_zero(std::span{descriptor.reserved_1}.subspan(7))
or not is_zero(descriptor.reserved_2);
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L532-L537
inline bool isV2(const CandidateDescriptor &descriptor) {
return not isV1(descriptor) and descriptor.reserved_1[0] == 0;
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L179
/// Check the signature of the collator within this descriptor.
inline outcome::result<void> checkSignature(
const crypto::Sr25519Provider &sr25519,
const CandidateDescriptor &descriptor) {
if (not isV1(descriptor)) {
return outcome::success();
}
OUTCOME_TRY(
r,
sr25519.verify(crypto::Sr25519Signature{descriptor.reserved_2},
descriptor.signable(),
crypto::Sr25519PublicKey{descriptor.reserved_1}));
if (not r) {
return PvfError::SIGNATURE;
}
return outcome::success();
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L580
/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
inline std::optional<parachain::CoreIndex> coreIndex(
const CandidateDescriptor &descriptor) {
if (isV1(descriptor)) {
return std::nullopt;
}
return boost::endian::load_little_u16(&descriptor.reserved_1[1]);
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L589
/// Returns the `core_index` of `V2` candidate descriptors, `None` otherwise.
inline std::optional<parachain::SessionIndex> sessionIndex(
const CandidateDescriptor &descriptor) {
if (isV1(descriptor)) {
return std::nullopt;
}
return boost::endian::load_little_u32(&descriptor.reserved_1[3]);
}

enum class CheckCoreIndexError : uint8_t {
InvalidCoreIndex,
NoAssignment,
UnknownVersion,
InvalidSession,
};
Q_ENUM_ERROR_CODE(CheckCoreIndexError) {
using E = decltype(e);
switch (e) {
case E::InvalidCoreIndex:
return "The specified core index is invalid";
case E::NoAssignment:
return "The parachain is not assigned to any core at specified claim "
"queue offset";
case E::UnknownVersion:
return "Unknown internal version";
case E::InvalidSession:
return "Invalid session";
}
abort();
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/primitives/src/vstaging/mod.rs#L602
/// Checks if descriptor core index is equal to the committed core index.
/// Input `cores_per_para` is a claim queue snapshot at the candidate's relay
/// parent, stored as a mapping between `ParaId` and the cores assigned per
/// depth.
inline outcome::result<void> checkCoreIndex(
const CommittedCandidateReceipt &receipt,
const TransposedClaimQueue &claims) {
if (isV1(receipt.descriptor)) {
return outcome::success();
}
if (not isV2(receipt.descriptor)) {
return CheckCoreIndexError::UnknownVersion;
}
OUTCOME_TRY(selector, coreSelector(receipt.commitments));
auto offset =
selector ? selector->claim_queue_offset : kDefaultClaimQueueOffset;
auto it1 = claims.find(receipt.descriptor.para_id);
if (it1 == claims.end()) {
return CheckCoreIndexError::NoAssignment;
}
auto it2 = it1->second.find(offset);
if (it2 == it1->second.end()) {
return CheckCoreIndexError::NoAssignment;
}
auto &assigned_cores = it2->second;
if (assigned_cores.empty()) {
return CheckCoreIndexError::NoAssignment;
}
auto core = coreIndex(receipt.descriptor).value();
if (not selector and assigned_cores.size() > 1) {
if (not assigned_cores.contains(core)) {
return CheckCoreIndexError::InvalidCoreIndex;
}
return outcome::success();
}
auto expected_core =
*std::next(assigned_cores.begin(),
selector ? static_cast<ptrdiff_t>(selector->core_selector
% assigned_cores.size())
: 0);
if (core != expected_core) {
return CheckCoreIndexError::InvalidCoreIndex;
}
return outcome::success();
}

// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/node/network/collator-protocol/src/validator_side/mod.rs#L2114
inline outcome::result<void> descriptorVersionSanityCheck(
const CandidateDescriptor &descriptor,
bool v2_receipts,
CoreIndex expected_core,
SessionIndex expected_session) {
if (isV1(descriptor)) {
return outcome::success();
}
if (not isV2(descriptor)) {
return CheckCoreIndexError::UnknownVersion;
}
if (not v2_receipts) {
return CheckCoreIndexError::UnknownVersion;
}
if (coreIndex(descriptor) != expected_core) {
return CheckCoreIndexError::InvalidCoreIndex;
}
if (sessionIndex(descriptor) != expected_session) {
return CheckCoreIndexError::InvalidSession;
}
return outcome::success();
}
} // namespace kagome::network
30 changes: 30 additions & 0 deletions core/parachain/pvf/pvf_error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <cstdint>

#include <qtils/enum_error_code.hpp>

namespace kagome::parachain {
enum class PvfError : uint8_t {
// NO_DATA conflicted with <netdb.h>
NO_PERSISTED_DATA = 1,
POV_SIZE,
POV_HASH,
CODE_HASH,
SIGNATURE,
HEAD_HASH,
COMMITMENTS_HASH,
OUTPUTS,
PERSISTED_DATA_HASH,
NO_CODE,
COMPILATION_ERROR,
};
} // namespace kagome::parachain

OUTCOME_HPP_DECLARE_ERROR(kagome::parachain, PvfError)
39 changes: 32 additions & 7 deletions core/parachain/pvf/pvf_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
#include "common/visitor.hpp"
#include "log/profiling_logger.hpp"
#include "metrics/histogram_timer.hpp"
#include "parachain/candidate_descriptor_v2.hpp"
#include "parachain/pvf/module_precompiler.hpp"
#include "parachain/pvf/pool.hpp"
#include "parachain/pvf/pvf_error.hpp"
#include "parachain/pvf/pvf_thread_pool.hpp"
#include "parachain/pvf/pvf_worker_types.hpp"
#include "parachain/pvf/session_params.hpp"
Expand Down Expand Up @@ -222,6 +224,18 @@ namespace kagome::parachain {
code_zstd,
timeout_kind,
std::move(cb));
// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/node/core/candidate-validation/src/lib.rs#L763-L782
auto session = sessionIndex(receipt.descriptor);
if (session and timeout_kind == runtime::PvfExecTimeoutKind::Backing) {
CB_TRY(auto expected_session,
parachain_api_->session_index_for_child(
receipt.descriptor.relay_parent));
if (sessionIndex(receipt.descriptor) != expected_session) {
cb(network::CheckCoreIndexError::InvalidSession);
return;
}
}

CB_TRY(auto pov_encoded, scale::encode(pov));
if (pov_encoded.size() > data.max_pov_size) {
return cb(PvfError::POV_SIZE);
Expand All @@ -234,13 +248,7 @@ namespace kagome::parachain {
if (code_hash != receipt.descriptor.validation_code_hash) {
return cb(PvfError::CODE_HASH);
}
CB_TRY(auto signature_valid,
sr25519_provider_->verify(receipt.descriptor.signature,
receipt.descriptor.signable(),
receipt.descriptor.collator_id));
if (!signature_valid) {
return cb(PvfError::SIGNATURE);
}
CB_TRYV(checkSignature(*sr25519_provider_, receipt.descriptor));

auto timer = metric_pvf_execution_time.timer();
ValidationParams params;
Expand All @@ -257,6 +265,7 @@ namespace kagome::parachain {
libp2p::SharedFn{[weak_self{weak_from_this()},
data,
receipt,
timeout_kind,
cb{std::move(cb)},
timer{std::move(timer)}](
outcome::result<ValidationResult> r) {
Expand All @@ -267,6 +276,22 @@ namespace kagome::parachain {
CB_TRY(auto result, std::move(r));
CB_TRY(auto commitments,
self->fromOutputs(receipt, std::move(result)));
// https://github.com/paritytech/polkadot-sdk/blob/1e3b8e1639c1cf784eabf0a9afcab1f3987e0ca4/polkadot/node/core/candidate-validation/src/lib.rs#L915-L951
if (timeout_kind == runtime::PvfExecTimeoutKind::Backing
and coreIndex(receipt.descriptor)) {
CB_TRY(auto claims,
self->parachain_api_->claim_queue(
receipt.descriptor.relay_parent));
if (not claims) {
claims.emplace();
}
CB_TRYV(network::checkCoreIndex(
{
.descriptor = receipt.descriptor,
.commitments = commitments,
},
transposeClaimQueue(*claims)));
}
cb(std::make_pair(std::move(commitments), data));
}});
}
Expand Down
Loading

0 comments on commit ce058ed

Please sign in to comment.