Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rfc 103, candidate core and session #2282

Merged
merged 22 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
159 changes: 159 additions & 0 deletions core/parachain/candidate_descriptor_v2.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/**
* 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 {
turuslan marked this conversation as resolved.
Show resolved Hide resolved
/// The default claim queue offset to be used if it's not
/// configured/accessible in the parachain
/// runtime
constexpr uint8_t kDefaultClaimQueueOffset = 0;

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);
}

inline bool isV2(const CandidateDescriptor &descriptor) {
return not isV1(descriptor) and descriptor.reserved_1[0] == 0;
}

/// 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();
}

/// 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]);
}

/// 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 {
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();
}

/// 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 ? selector->core_selector % assigned_cores.size() : 0));
if (core != expected_core) {
return CheckCoreIndexError::InvalidCoreIndex;
}
return outcome::success();
}

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
28 changes: 28 additions & 0 deletions core/parachain/pvf/pvf_error.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/

#pragma once

#include <qtils/enum_error_code.hpp>

namespace kagome::parachain {
enum class PvfError {
// 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)
33 changes: 26 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 @@ -234,13 +236,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 +253,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 +264,28 @@ namespace kagome::parachain {
CB_TRY(auto result, std::move(r));
CB_TRY(auto commitments,
self->fromOutputs(receipt, std::move(result)));
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_TRY(auto expected_session,
self->parachain_api_->session_index_for_child(
receipt.descriptor.relay_parent));
if (sessionIndex(receipt.descriptor) != expected_session) {
cb(network::CheckCoreIndexError::InvalidSession);
return;
}
}
cb(std::make_pair(std::move(commitments), data));
}});
}
Expand Down
19 changes: 0 additions & 19 deletions core/parachain/pvf/pvf_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,6 @@ namespace kagome::runtime {
class RuntimeInstancesPool;
} // namespace kagome::runtime

namespace kagome::parachain {
enum class PvfError {
// 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)

namespace kagome::parachain {
class PvfPool;
class PvfThreadPool;
Expand Down
Loading
Loading