diff --git a/README.md b/README.md index 703fb65cc2..ff0472b740 100644 --- a/README.md +++ b/README.md @@ -154,7 +154,7 @@ You will also be able to see your node on https://telemetry.polkadot.io/. If you Run `kagome --help` to explore other CLI flags. -#### Execute KAGOME validating node in development mode +#### Execute KAGOME block_validator node in development mode The easiest way to get started with KAGOME is to run it in development mode, which is a single node network: diff --git a/core/consensus/CMakeLists.txt b/core/consensus/CMakeLists.txt index 5b64b468af..858aca84a3 100644 --- a/core/consensus/CMakeLists.txt +++ b/core/consensus/CMakeLists.txt @@ -17,10 +17,8 @@ add_subdirectory(timeline) add_subdirectory(babe) add_subdirectory(grandpa) -add_library(consensus - validation/babe_block_validator.cpp -) -target_link_libraries(consensus +add_library(consensus INTERFACE) +target_link_libraries(consensus INTERFACE timeline babe grandpa diff --git a/core/consensus/README.md b/core/consensus/README.md index 654cd82d6b..58e3330597 100644 --- a/core/consensus/README.md +++ b/core/consensus/README.md @@ -11,4 +11,4 @@ This folder contains implementations for BABE and GRANDPA consensus in Kagome. * [authority](authority) – authority manager implementations. Needed to keep track of grandpa authorities updates * [babe](babe) - BABE block production algorithm implementation * [grandpa](grandpa) – GRANDPA block finalization algorithm implementation -* [validation](validation) – contains logic for validating incoming blocks +* [validation](validation) – contains logic for block_validator incoming blocks diff --git a/core/consensus/babe/CMakeLists.txt b/core/consensus/babe/CMakeLists.txt index 373d1605f9..97ddca5aba 100644 --- a/core/consensus/babe/CMakeLists.txt +++ b/core/consensus/babe/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(babe impl/babe.cpp impl/threshold_util.cpp impl/babe_lottery_impl.cpp + impl/babe_block_validator_impl.cpp impl/babe_config_repository_impl.cpp ) target_link_libraries(babe diff --git a/core/consensus/babe/babe_block_validator.hpp b/core/consensus/babe/babe_block_validator.hpp new file mode 100644 index 0000000000..b16d7d8504 --- /dev/null +++ b/core/consensus/babe/babe_block_validator.hpp @@ -0,0 +1,21 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "primitives/block.hpp" + +namespace kagome::consensus::babe { + + class BabeBlockValidator { + public: + virtual ~BabeBlockValidator() = default; + + virtual outcome::result validateHeader( + const primitives::BlockHeader &header) const = 0; + }; + +} // namespace kagome::consensus::babe diff --git a/core/consensus/babe/impl/babe.cpp b/core/consensus/babe/impl/babe.cpp index 6a34ace36c..fbd4ff65c7 100644 --- a/core/consensus/babe/impl/babe.cpp +++ b/core/consensus/babe/impl/babe.cpp @@ -15,6 +15,7 @@ #include "blockchain/block_tree.hpp" #include "consensus/babe/babe_config_repository.hpp" #include "consensus/babe/babe_lottery.hpp" +#include "consensus/babe/impl/babe_block_validator_impl.hpp" #include "consensus/babe/impl/babe_digests_util.hpp" #include "consensus/block_production_error.hpp" #include "consensus/timeline/backoff.hpp" @@ -70,6 +71,7 @@ namespace kagome::consensus::babe { std::shared_ptr lottery, std::shared_ptr hasher, std::shared_ptr sr25519_provider, + std::shared_ptr validating, std::shared_ptr bitfield_store, std::shared_ptr backing_store, std::shared_ptr dispute_coordinator, @@ -90,6 +92,7 @@ namespace kagome::consensus::babe { lottery_(std::move(lottery)), hasher_(std::move(hasher)), sr25519_provider_(std::move(sr25519_provider)), + validating_(std::move(validating)), bitfield_store_(std::move(bitfield_store)), backing_store_(std::move(backing_store)), dispute_coordinator_(std::move(dispute_coordinator)), @@ -108,6 +111,7 @@ namespace kagome::consensus::babe { BOOST_ASSERT(lottery_); BOOST_ASSERT(hasher_); BOOST_ASSERT(sr25519_provider_); + BOOST_ASSERT(validating_); BOOST_ASSERT(bitfield_store_); BOOST_ASSERT(backing_store_); BOOST_ASSERT(dispute_coordinator_); @@ -213,6 +217,11 @@ namespace kagome::consensus::babe { return processSlotLeadership(); } + outcome::result Babe::validateHeader( + const primitives::BlockHeader &block_header) const { + return validating_->validateHeader(block_header); + } + bool Babe::changeEpoch(EpochNumber epoch, const primitives::BlockInfo &block) const { return lottery_->changeEpoch(epoch, block); diff --git a/core/consensus/babe/impl/babe.hpp b/core/consensus/babe/impl/babe.hpp index c55b72fee5..106cf8a69e 100644 --- a/core/consensus/babe/impl/babe.hpp +++ b/core/consensus/babe/impl/babe.hpp @@ -44,6 +44,7 @@ namespace kagome::consensus { namespace kagome::consensus::babe { class BabeConfigRepository; + class BabeBlockValidator; class BabeLottery; } // namespace kagome::consensus::babe @@ -99,6 +100,7 @@ namespace kagome::consensus::babe { std::shared_ptr lottery, std::shared_ptr hasher, std::shared_ptr sr25519_provider, + std::shared_ptr validating, std::shared_ptr bitfield_store, std::shared_ptr backing_store, std::shared_ptr dispute_coordinator, @@ -121,6 +123,9 @@ namespace kagome::consensus::babe { outcome::result processSlot( SlotNumber slot, const primitives::BlockInfo &best_block) override; + outcome::result validateHeader( + const primitives::BlockHeader &block_header) const override; + private: bool changeEpoch(EpochNumber epoch, const primitives::BlockInfo &block) const override; @@ -153,6 +158,7 @@ namespace kagome::consensus::babe { std::shared_ptr lottery_; std::shared_ptr hasher_; std::shared_ptr sr25519_provider_; + std::shared_ptr validating_; std::shared_ptr bitfield_store_; std::shared_ptr backing_store_; std::shared_ptr dispute_coordinator_; diff --git a/core/consensus/validation/babe_block_validator.cpp b/core/consensus/babe/impl/babe_block_validator_impl.cpp similarity index 55% rename from core/consensus/validation/babe_block_validator.cpp rename to core/consensus/babe/impl/babe_block_validator_impl.cpp index 62a45c4771..851941384b 100644 --- a/core/consensus/validation/babe_block_validator.cpp +++ b/core/consensus/babe/impl/babe_block_validator_impl.cpp @@ -4,17 +4,30 @@ * SPDX-License-Identifier: Apache-2.0 */ -#include "consensus/validation/babe_block_validator.hpp" +#include "consensus/babe/impl/babe_block_validator_impl.hpp" +#include + +#include "consensus/babe/babe_config_repository.hpp" +#include "consensus/babe/babe_lottery.hpp" #include "consensus/babe/impl/babe_digests_util.hpp" -#include "consensus/babe/impl/prepare_transcript.hpp" +#include "consensus/babe/types/seal.hpp" +#include "consensus/timeline/impl/slot_leadership_error.hpp" +#include "consensus/timeline/slots_util.hpp" #include "crypto/sr25519_provider.hpp" #include "crypto/vrf_provider.hpp" +#include "metrics/histogram_timer.hpp" +#include "prepare_transcript.hpp" +#include "primitives/inherent_data.hpp" +#include "primitives/transcript.hpp" +#include "runtime/runtime_api/offchain_worker_api.hpp" +#include "storage/trie/serialization/ordered_trie_hash.hpp" +#include "threshold_util.hpp" OUTCOME_CPP_DEFINE_CATEGORY(kagome::consensus::babe, - BabeBlockValidator::ValidationError, + BabeBlockValidatorImpl::ValidationError, e) { - using E = kagome::consensus::babe::BabeBlockValidator::ValidationError; + using E = kagome::consensus::babe::BabeBlockValidatorImpl::ValidationError; switch (e) { case E::NO_AUTHORITIES: return "no authorities are provided for the validation"; @@ -31,28 +44,71 @@ OUTCOME_CPP_DEFINE_CATEGORY(kagome::consensus::babe, } namespace kagome::consensus::babe { - using common::Buffer; - BabeBlockValidator::BabeBlockValidator( - std::shared_ptr block_tree, - std::shared_ptr tx_queue, + BabeBlockValidatorImpl::BabeBlockValidatorImpl( + LazySPtr slots_util, + std::shared_ptr config_repo, std::shared_ptr hasher, - std::shared_ptr vrf_provider, - std::shared_ptr sr25519_provider) - : block_tree_{std::move(block_tree)}, - tx_queue_{std::move(tx_queue)}, - hasher_{std::move(hasher)}, - vrf_provider_{std::move(vrf_provider)}, - sr25519_provider_{std::move(sr25519_provider)}, - log_{log::createLogger("BlockValidator", "block_validator")} { - BOOST_ASSERT(block_tree_); - BOOST_ASSERT(tx_queue_); + std::shared_ptr sr25519_provider, + std::shared_ptr vrf_provider) + : log_(log::createLogger("BabeBlockValidatorImpl", "babe")), + slots_util_(std::move(slots_util)), + config_repo_(std::move(config_repo)), + hasher_(std::move(hasher)), + sr25519_provider_(std::move(sr25519_provider)), + vrf_provider_(std::move(vrf_provider)) { + BOOST_ASSERT(config_repo_); BOOST_ASSERT(hasher_); - BOOST_ASSERT(vrf_provider_); BOOST_ASSERT(sr25519_provider_); + BOOST_ASSERT(vrf_provider_); + } + + outcome::result BabeBlockValidatorImpl::validateHeader( + const primitives::BlockHeader &block_header) const { + OUTCOME_TRY(babe_header, babe::getBabeBlockHeader(block_header)); + + auto slot_number = babe_header.slot_number; + + OUTCOME_TRY(epoch_number, + slots_util_.get()->slotToEpoch(*block_header.parentInfo(), + slot_number)); + + SL_VERBOSE( + log_, + "Appending header of block {} ({} in slot {}, epoch {}, authority #{})", + block_header.blockInfo(), + to_string(babe_header.slotType()), + slot_number, + epoch_number, + babe_header.authority_index); + + OUTCOME_TRY(config_ptr, + config_repo_->config(*block_header.parentInfo(), epoch_number)); + auto &config = *config_ptr; + + SL_TRACE(log_, + "Actual epoch digest to apply block {} (slot {}, epoch {}). " + "Randomness: {}", + block_header.blockInfo(), + slot_number, + epoch_number, + config.randomness); + + auto threshold = calculateThreshold(config.leadership_rate, + config.authorities, + babe_header.authority_index); + + OUTCOME_TRY( + validateHeader(block_header, + epoch_number, + config.authorities[babe_header.authority_index].id, + threshold, + config)); + + return outcome::success(); } - outcome::result BabeBlockValidator::validateHeader( + outcome::result BabeBlockValidatorImpl::validateHeader( const primitives::BlockHeader &header, const EpochNumber epoch_number, const AuthorityId &authority_id, @@ -90,7 +146,7 @@ namespace kagome::consensus::babe { OUTCOME_TRY(seal, getSeal(header)); // signature in seal of the header must be valid - if (!verifySignature(header, babe_header, seal, authority_id)) { + if (!verifySignature(header, seal, authority_id)) { return ValidationError::INVALID_SIGNATURE; } @@ -108,9 +164,8 @@ namespace kagome::consensus::babe { return outcome::success(); } - bool BabeBlockValidator::verifySignature( + bool BabeBlockValidatorImpl::verifySignature( const primitives::BlockHeader &header, - const BabeBlockHeader &babe_header, const Seal &seal, const AuthorityId &public_key) const { primitives::UnsealedBlockHeaderReflection unsealed_header(header); @@ -125,12 +180,12 @@ namespace kagome::consensus::babe { return res && res.value(); } - bool BabeBlockValidator::verifyVRF(const BabeBlockHeader &babe_header, - const EpochNumber epoch_number, - const AuthorityId &public_key, - const Threshold &threshold, - const Randomness &randomness, - const bool checkThreshold) const { + bool BabeBlockValidatorImpl::verifyVRF(const BabeBlockHeader &babe_header, + const EpochNumber epoch_number, + const AuthorityId &public_key, + const Threshold &threshold, + const Randomness &randomness, + const bool checkThreshold) const { primitives::Transcript transcript; prepareTranscript( transcript, randomness, babe_header.slot_number, epoch_number); diff --git a/core/consensus/validation/babe_block_validator.hpp b/core/consensus/babe/impl/babe_block_validator_impl.hpp similarity index 58% rename from core/consensus/validation/babe_block_validator.hpp rename to core/consensus/babe/impl/babe_block_validator_impl.hpp index a5113fa7e5..24c44f4f0a 100644 --- a/core/consensus/validation/babe_block_validator.hpp +++ b/core/consensus/babe/impl/babe_block_validator_impl.hpp @@ -6,52 +6,48 @@ #pragma once -#include "consensus/validation/block_validator.hpp" +#include "consensus/babe/babe_block_validator.hpp" -#include - -#include "consensus/babe/types/authority.hpp" -#include "consensus/babe/types/seal.hpp" +#include "clock/clock.hpp" +#include "consensus/babe/types/babe_configuration.hpp" +#include "consensus/babe/types/slot_leadership.hpp" +#include "injector/lazy.hpp" #include "log/logger.hpp" +#include "metrics/metrics.hpp" +#include "primitives/block.hpp" +#include "primitives/event_types.hpp" +#include "telemetry/service.hpp" -namespace kagome::blockchain { - class BlockTree; +namespace kagome::consensus { + class SlotsUtil; } +namespace kagome::consensus::babe { + class BabeConfigRepository; + struct Seal; +} // namespace kagome::consensus::babe + namespace kagome::crypto { + class Hasher; class Sr25519Provider; class VRFProvider; - class Sr25519Provider; } // namespace kagome::crypto -namespace kagome::runtime { - class TaggedTransactionQueue; -} - namespace kagome::consensus::babe { - /** - * Validation of blocks in BABE system. Based on the algorithm described here: - * https://research.web3.foundation/en/latest/polkadot/BABE/Babe/#2-normal-phase - */ - class BabeBlockValidator : public BlockValidator { + class BabeBlockValidatorImpl + : public BabeBlockValidator, + public std::enable_shared_from_this { public: - ~BabeBlockValidator() override = default; - - /** - * Create an instance of BabeBlockValidator - * @param block_tree to be used by this instance - * @param tx_queue to validate the extrinsics - * @param hasher to take hashes - * @param vrf_provider for VRF-specific operations - * @param configuration Babe configuration from genesis - */ - BabeBlockValidator( - std::shared_ptr block_tree, - std::shared_ptr tx_queue, + BabeBlockValidatorImpl( + LazySPtr slots_util, + std::shared_ptr config_repo, std::shared_ptr hasher, - std::shared_ptr vrf_provider, - std::shared_ptr sr25519_provider); + std::shared_ptr sr25519_provider, + std::shared_ptr vrf_provider); + + outcome::result validateHeader( + const primitives::BlockHeader &block_header) const; enum class ValidationError { NO_AUTHORITIES = 1, @@ -61,25 +57,30 @@ namespace kagome::consensus::babe { SECONDARY_SLOT_ASSIGNMENTS_DISABLED }; + private: + /** + * Validate the block header + * @param block to be validated + * @param authority_id authority that sent this block + * @param threshold is vrf threshold for this epoch + * @param config is babe config for this epoch + * @return nothing or validation error + */ outcome::result validateHeader( - const primitives::BlockHeader &header, + const primitives::BlockHeader &block_header, const EpochNumber epoch_number, const babe::AuthorityId &authority_id, const Threshold &threshold, - const babe::BabeConfiguration &babe_config) const override; - - private: + const babe::BabeConfiguration &config) const; /** * Verify that block is signed by valid signature * @param header Header to be checked - * @param babe_header BabeBlockHeader corresponding to (fetched from) header * @param seal Seal corresponding to (fetched from) header * @param public_key public key that corresponds to the authority by * authority index * @return true if signature is valid, false otherwise */ bool verifySignature(const primitives::BlockHeader &header, - const BabeBlockHeader &babe_header, const Seal &seal, const AuthorityId &public_key) const; @@ -99,19 +100,16 @@ namespace kagome::consensus::babe { const Randomness &randomness, const bool checkThreshold) const; - std::shared_ptr block_tree_; - std::shared_ptr tx_queue_; + log::Logger log_; + + LazySPtr slots_util_; + std::shared_ptr config_repo_; std::shared_ptr hasher_; - std::shared_ptr vrf_provider_; std::shared_ptr sr25519_provider_; - - mutable std::unordered_map> - blocks_producers_; - - log::Logger log_; + std::shared_ptr vrf_provider_; }; + } // namespace kagome::consensus::babe OUTCOME_HPP_DECLARE_ERROR(kagome::consensus::babe, - BabeBlockValidator::ValidationError) + BabeBlockValidatorImpl::ValidationError) diff --git a/core/consensus/babe/types/equivocation_proof.hpp b/core/consensus/babe/types/equivocation_proof.hpp new file mode 100644 index 0000000000..c5779cbe15 --- /dev/null +++ b/core/consensus/babe/types/equivocation_proof.hpp @@ -0,0 +1,32 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "consensus/timeline/types.hpp" +#include "primitives/block_header.hpp" +#include "scale/tie.hpp" + +namespace kagome::consensus::babe { + + /// Represents an equivocation proof. An equivocation happens when a validator + /// produces more than one block on the same slot. The proof of equivocation + /// are the given distinct headers that were signed by the validator and which + /// include the slot number. + struct EquivocationProof { + SCALE_TIE(4); + + /// Returns the authority id of the equivocator. + AuthorityId offender; + /// The slot at which the equivocation happened. + SlotNumber slot; + /// The first header involved in the equivocation. + primitives::BlockHeader first_header; + /// The second header involved in the equivocation. + primitives::BlockHeader second_header; + }; + +} // namespace kagome::consensus::babe diff --git a/core/consensus/consensus_selector.hpp b/core/consensus/consensus_selector.hpp index b5f4a07e3a..732167b9ea 100644 --- a/core/consensus/consensus_selector.hpp +++ b/core/consensus/consensus_selector.hpp @@ -25,8 +25,14 @@ namespace kagome::consensus { virtual std::shared_ptr getProductionConsensus( const primitives::BlockInfo &parent_block) const = 0; + virtual std::shared_ptr getProductionConsensus( + const primitives::BlockHeader &block_header) const = 0; + virtual std::shared_ptr getFinalityConsensus( const primitives::BlockInfo &parent_block) const = 0; + + virtual std::shared_ptr getFinalityConsensus( + const primitives::BlockHeader &block_header) const = 0; }; } // namespace kagome::consensus diff --git a/core/consensus/production_consensus.hpp b/core/consensus/production_consensus.hpp index b0f6fc5b03..6ec7ae549b 100644 --- a/core/consensus/production_consensus.hpp +++ b/core/consensus/production_consensus.hpp @@ -43,6 +43,9 @@ namespace kagome::consensus { virtual outcome::result processSlot( SlotNumber slot, const primitives::BlockInfo &parent) = 0; + virtual outcome::result validateHeader( + const primitives::BlockHeader &block_header) const = 0; + protected: /// Changes epoch /// @param epoch epoch that switch to diff --git a/core/consensus/timeline/impl/block_appender_base.cpp b/core/consensus/timeline/impl/block_appender_base.cpp index a3d4d0cd49..4b489849d8 100644 --- a/core/consensus/timeline/impl/block_appender_base.cpp +++ b/core/consensus/timeline/impl/block_appender_base.cpp @@ -9,11 +9,10 @@ #include "blockchain/block_tree.hpp" #include "consensus/babe/babe_config_repository.hpp" #include "consensus/babe/impl/babe_digests_util.hpp" -#include "consensus/babe/impl/threshold_util.hpp" +#include "consensus/consensus_selector.hpp" #include "consensus/grandpa/environment.hpp" #include "consensus/grandpa/voting_round_error.hpp" #include "consensus/timeline/slots_util.hpp" -#include "consensus/validation/block_validator.hpp" namespace kagome::consensus { @@ -21,22 +20,21 @@ namespace kagome::consensus { std::shared_ptr block_tree, std::shared_ptr babe_config_repo, const EpochTimings &timings, - std::shared_ptr block_validator, std::shared_ptr grandpa_environment, LazySPtr slots_util, - std::shared_ptr hasher) + std::shared_ptr hasher, + LazySPtr consensus_selector) : block_tree_{std::move(block_tree)}, babe_config_repo_{std::move(babe_config_repo)}, timings_(timings), - block_validator_{std::move(block_validator)}, grandpa_environment_{std::move(grandpa_environment)}, slots_util_{std::move(slots_util)}, - hasher_{std::move(hasher)} { - BOOST_ASSERT(nullptr != block_tree_); - BOOST_ASSERT(nullptr != babe_config_repo_); - BOOST_ASSERT(nullptr != block_validator_); - BOOST_ASSERT(nullptr != grandpa_environment_); - BOOST_ASSERT(nullptr != hasher_); + hasher_{std::move(hasher)}, + consensus_selector_(std::move(consensus_selector)) { + BOOST_ASSERT(block_tree_ != nullptr); + BOOST_ASSERT(babe_config_repo_ != nullptr); + BOOST_ASSERT(grandpa_environment_ != nullptr); + BOOST_ASSERT(hasher_ != nullptr); postponed_justifications_ = std::make_shared< std::map>(); @@ -130,48 +128,10 @@ namespace kagome::consensus { outcome::result BlockAppenderBase::validateHeader( const primitives::Block &block) { - OUTCOME_TRY(babe_header, babe::getBabeBlockHeader(block.header)); - - auto slot_number = babe_header.slot_number; - - OUTCOME_TRY(epoch_number, - slots_util_.get()->slotToEpoch(*block.header.parentInfo(), - slot_number)); - - SL_VERBOSE( - logger_, - "Appending header of block {} ({} in slot {}, epoch {}, authority #{})", - block.header.blockInfo(), - to_string(babe_header.slotType()), - slot_number, - epoch_number, - babe_header.authority_index); - - OUTCOME_TRY( - babe_config_ptr, - babe_config_repo_->config(*block.header.parentInfo(), epoch_number)); - auto &babe_config = *babe_config_ptr; - - SL_TRACE(logger_, - "Actual epoch digest to apply block {} (slot {}, epoch {}). " - "Randomness: {}", - block.header.blockInfo(), - slot_number, - epoch_number, - babe_config.randomness); - - auto threshold = babe::calculateThreshold(babe_config.leadership_rate, - babe_config.authorities, - babe_header.authority_index); - - OUTCOME_TRY(block_validator_->validateHeader( - block.header, - epoch_number, - babe_config.authorities[babe_header.authority_index].id, - threshold, - babe_config)); - - return outcome::success(); + auto consensus = + consensus_selector_.get()->getProductionConsensus(block.header); + BOOST_ASSERT_MSG(consensus, "Must be returned at least fallback consensus"); + return consensus->validateHeader(block.header); } outcome::result BlockAppenderBase::getSlotInfo( diff --git a/core/consensus/timeline/impl/block_appender_base.hpp b/core/consensus/timeline/impl/block_appender_base.hpp index fe7744a2bb..1522cd588d 100644 --- a/core/consensus/timeline/impl/block_appender_base.hpp +++ b/core/consensus/timeline/impl/block_appender_base.hpp @@ -22,7 +22,8 @@ namespace kagome::crypto { namespace kagome::consensus { class SlotsUtil; -} + class ConsensusSelector; +} // namespace kagome::consensus namespace kagome::consensus::babe { class BabeConfigRepository; @@ -30,8 +31,6 @@ namespace kagome::consensus::babe { namespace kagome::consensus { - class BlockValidator; - /** * Common logic for adding a new block to the blockchain */ @@ -42,10 +41,10 @@ namespace kagome::consensus { std::shared_ptr block_tree, std::shared_ptr babe_config_repo, const EpochTimings &timings, - std::shared_ptr block_validator, std::shared_ptr grandpa_environment, LazySPtr slots_util, - std::shared_ptr hasher); + std::shared_ptr hasher, + LazySPtr consensus_selector); void applyJustifications( const primitives::BlockInfo &block_info, @@ -74,10 +73,10 @@ namespace kagome::consensus { std::shared_ptr block_tree_; std::shared_ptr babe_config_repo_; const EpochTimings &timings_; - std::shared_ptr block_validator_; std::shared_ptr grandpa_environment_; LazySPtr slots_util_; std::shared_ptr hasher_; + LazySPtr consensus_selector_; }; } // namespace kagome::consensus diff --git a/core/consensus/timeline/impl/block_executor_impl.cpp b/core/consensus/timeline/impl/block_executor_impl.cpp index e3b13da379..5f3e637e95 100644 --- a/core/consensus/timeline/impl/block_executor_impl.cpp +++ b/core/consensus/timeline/impl/block_executor_impl.cpp @@ -12,7 +12,6 @@ #include "consensus/babe/babe_config_repository.hpp" #include "consensus/timeline/impl/block_addition_error.hpp" #include "consensus/timeline/impl/block_appender_base.hpp" -#include "consensus/validation/block_validator.hpp" #include "metrics/histogram_timer.hpp" #include "runtime/runtime_api/core.hpp" #include "runtime/runtime_api/offchain_worker_api.hpp" diff --git a/core/consensus/timeline/impl/block_header_appender_impl.cpp b/core/consensus/timeline/impl/block_header_appender_impl.cpp index 44fe08d85e..d068b0da88 100644 --- a/core/consensus/timeline/impl/block_header_appender_impl.cpp +++ b/core/consensus/timeline/impl/block_header_appender_impl.cpp @@ -11,7 +11,6 @@ #include "consensus/babe/babe_config_repository.hpp" #include "consensus/timeline/impl/block_addition_error.hpp" #include "consensus/timeline/impl/block_appender_base.hpp" -#include "consensus/validation/block_validator.hpp" namespace kagome::consensus { diff --git a/core/consensus/timeline/impl/block_header_appender_impl.hpp b/core/consensus/timeline/impl/block_header_appender_impl.hpp index 740e13bb7c..fe03c97057 100644 --- a/core/consensus/timeline/impl/block_header_appender_impl.hpp +++ b/core/consensus/timeline/impl/block_header_appender_impl.hpp @@ -16,7 +16,7 @@ namespace kagome::blockchain { class BlockTree; -} // namespace kagome::blockchain +} namespace kagome::crypto { class Hasher; diff --git a/core/consensus/timeline/impl/consensus_selector_impl.cpp b/core/consensus/timeline/impl/consensus_selector_impl.cpp index bd9c4935f1..892b6f1118 100644 --- a/core/consensus/timeline/impl/consensus_selector_impl.cpp +++ b/core/consensus/timeline/impl/consensus_selector_impl.cpp @@ -70,6 +70,39 @@ namespace kagome::consensus { BOOST_UNREACHABLE_RETURN({}); } + std::shared_ptr + ConsensusSelectorImpl::getProductionConsensus( + const primitives::BlockHeader &header) const { + auto consensus_opt = pc_cache_.get(header.blockInfo()); + if (consensus_opt.has_value()) { + return consensus_opt.value(); + } + + [[unlikely]] if (header.number == 0) { + for (size_t i = 0; i < production_consensuses_.size(); ++i) { + const auto &consensus = production_consensuses_[i]; + if (consensus->isGenesisConsensus()) { + return consensus; + } + } + } + + for (size_t i = 0; i < production_consensuses_.size(); ++i) { + const auto &consensus = production_consensuses_[i]; + + // Try to fit consensus by getting slot + if (consensus->getSlot(header)) { + return consensus; + } + + // The last one is fallback + if (i == production_consensuses_.size() - 1) { + return consensus; + } + } + BOOST_UNREACHABLE_RETURN({}); + } + std::shared_ptr ConsensusSelectorImpl::getFinalityConsensus( const primitives::BlockInfo &parent_block) const { @@ -80,7 +113,7 @@ namespace kagome::consensus { for (size_t i = 0; i < finality_consensuses_.size(); ++i) { const auto &consensus = finality_consensuses_[i]; - if (i == finality_consensuses_.size() - 1) { // Last one is fallback + if (i == finality_consensuses_.size() - 1) { // The last one is fallback return fc_cache_.put(parent_block, consensus); } @@ -89,4 +122,23 @@ namespace kagome::consensus { BOOST_UNREACHABLE_RETURN({}); }; + std::shared_ptr + ConsensusSelectorImpl::getFinalityConsensus( + const primitives::BlockHeader &header) const { + auto consensus_opt = fc_cache_.get(header.blockInfo()); + if (consensus_opt.has_value()) { + return consensus_opt.value(); + } + + for (size_t i = 0; i < finality_consensuses_.size(); ++i) { + const auto &consensus = finality_consensuses_[i]; + if (i == finality_consensuses_.size() - 1) { // The last one is fallback + return consensus; + } + + // TODO: Code for trying to select another consensus + } + BOOST_UNREACHABLE_RETURN({}); + }; + } // namespace kagome::consensus diff --git a/core/consensus/timeline/impl/consensus_selector_impl.hpp b/core/consensus/timeline/impl/consensus_selector_impl.hpp index b30679793e..dce5358be6 100644 --- a/core/consensus/timeline/impl/consensus_selector_impl.hpp +++ b/core/consensus/timeline/impl/consensus_selector_impl.hpp @@ -27,9 +27,15 @@ namespace kagome::consensus { std::shared_ptr getProductionConsensus( const primitives::BlockInfo &parent_block) const override; + std::shared_ptr getProductionConsensus( + const primitives::BlockHeader &header) const override; + std::shared_ptr getFinalityConsensus( const primitives::BlockInfo &parent_block) const override; + std::shared_ptr getFinalityConsensus( + const primitives::BlockHeader &block_header) const override; + private: std::shared_ptr header_repo_; std::vector> production_consensuses_; diff --git a/core/consensus/validation/block_validator.hpp b/core/consensus/validation/block_validator.hpp deleted file mode 100644 index 3c0b4c42ce..0000000000 --- a/core/consensus/validation/block_validator.hpp +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "consensus/babe/types/babe_configuration.hpp" -#include "outcome/outcome.hpp" -#include "primitives/block.hpp" - -namespace kagome::consensus { - /** - * Validator of the blocks - */ - class BlockValidator { - public: - virtual ~BlockValidator() = default; - - /** - * Validate the block header - * @param block to be validated - * @param authority_id authority that sent this block - * @param threshold is vrf threshold for this epoch - * @param config is babe config for this epoch - * @return nothing or validation error - */ - virtual outcome::result validateHeader( - const primitives::BlockHeader &block_header, - const EpochNumber epoch_number, - const babe::AuthorityId &authority_id, - const Threshold &threshold, - const babe::BabeConfiguration &config) const = 0; - }; -} // namespace kagome::consensus diff --git a/core/injector/application_injector.cpp b/core/injector/application_injector.cpp index 2b9b12a13b..4224e8ee07 100644 --- a/core/injector/application_injector.cpp +++ b/core/injector/application_injector.cpp @@ -62,6 +62,7 @@ #include "common/fd_limit.hpp" #include "common/outcome_throw.hpp" #include "consensus/babe/impl/babe.hpp" +#include "consensus/babe/impl/babe_block_validator_impl.hpp" #include "consensus/babe/impl/babe_config_repository_impl.hpp" #include "consensus/babe/impl/babe_lottery_impl.hpp" #include "consensus/finality_consensus.hpp" @@ -75,7 +76,6 @@ #include "consensus/timeline/impl/consensus_selector_impl.hpp" #include "consensus/timeline/impl/slots_util_impl.hpp" #include "consensus/timeline/impl/timeline_impl.hpp" -#include "consensus/validation/babe_block_validator.hpp" #include "crypto/bip39/impl/bip39_provider_impl.hpp" #include "crypto/crypto_store/crypto_store_impl.hpp" #include "crypto/crypto_store/session_keys.hpp" @@ -692,7 +692,6 @@ namespace { di::bind.template to(), di::bind.template to(), di::bind.template to(), - di::bind.template to(), di::bind.template to(), di::bind.template to(), di::bind.template to(), @@ -821,6 +820,7 @@ namespace { di::bind.template to(), di::bind.template to(), di::bind.template to(), + di::bind.template to(), // user-defined overrides... std::forward(args)...); diff --git a/docs/source/tutorials/first_kagome_chain.md b/docs/source/tutorials/first_kagome_chain.md index eba52c0888..c21287d98b 100644 --- a/docs/source/tutorials/first_kagome_chain.md +++ b/docs/source/tutorials/first_kagome_chain.md @@ -10,7 +10,7 @@ In this tutorial you will learn how to execute Kagome-based Polkadot-host chain ### Prerequisites -1. Kagome validating node binary built as described [here](https://kagome.readthedocs.io/en/latest/overview/getting_started.html#build-application). +1. Kagome block_validator node binary built as described [here](https://kagome.readthedocs.io/en/latest/overview/getting_started.html#build-application). 2. For your convenience make sure you have this binary included into your path: ```bash diff --git a/docs/source/tutorials/private_network.md b/docs/source/tutorials/private_network.md index 825f4c723a..d77cde491e 100644 --- a/docs/source/tutorials/private_network.md +++ b/docs/source/tutorials/private_network.md @@ -14,9 +14,9 @@ First go to tutorial's folder: cd examples/network ``` -### Execute first validating node +### Execute first block_validator node -First we execute validating node in the similar way we did it during previous tutorial. This node will produce and finalize blocks. +First we execute block_validator node in the similar way we did it during previous tutorial. This node will produce and finalize blocks. To start with let's navigate into the node's folder: @@ -31,9 +31,9 @@ kagome \ --prometheus-port 11155 ``` -### Execute second validating node (node with authority) +### Execute second block_validator node (node with authority) -Now that validating node is up and running, second node can join the network by bootstrapping from the first node. Command will look very similar. +Now that block_validator node is up and running, second node can join the network by bootstrapping from the first node. Command will look very similar. ```shell script kagome \ @@ -73,7 +73,7 @@ kagome \ --prometheus-port 21155 ``` -Note that trie root is the same with validating nodes. When syncing node receives block announcement it first synchronizes missing blocks and then listens to the new blocks and finalization. +Note that trie root is the same with block_validator nodes. When syncing node receives block announcement it first synchronizes missing blocks and then listens to the new blocks and finalization. ### Send transaction diff --git a/test/core/blockchain/block_tree_test.cpp b/test/core/blockchain/block_tree_test.cpp index fe4f549c20..ecc00f5e5c 100644 --- a/test/core/blockchain/block_tree_test.cpp +++ b/test/core/blockchain/block_tree_test.cpp @@ -768,7 +768,10 @@ TEST_F(BlockTreeTest, GetBestChain_TwoChains) { auto T_hash = addHeaderToRepository(kFinalizedBlockInfo.hash, 43); auto A_hash = addHeaderToRepository(T_hash, 44); auto B_hash = addHeaderToRepository(A_hash, 45); + + [[maybe_unused]] // auto C1_hash = addHeaderToRepository(B_hash, 46); + auto C2_hash = addHeaderToRepository(B_hash, 46); auto D2_hash = addHeaderToRepository(C2_hash, 47); @@ -894,6 +897,7 @@ TEST_F(BlockTreeTest, GetBestBlock) { auto A_hash = addHeaderToRepository(T_hash, 44); auto B_hash = addHeaderToRepository(A_hash, 45); + [[maybe_unused]] // auto C1_hash = addHeaderToRepository(B_hash, 46); auto C2_hash = addHeaderToRepository(B_hash, 46); diff --git a/test/core/consensus/CMakeLists.txt b/test/core/consensus/CMakeLists.txt index ea778b5f27..741907dd00 100644 --- a/test/core/consensus/CMakeLists.txt +++ b/test/core/consensus/CMakeLists.txt @@ -5,6 +5,5 @@ # add_subdirectory(timeline) -add_subdirectory(validation) add_subdirectory(babe) add_subdirectory(grandpa) diff --git a/test/core/consensus/babe/CMakeLists.txt b/test/core/consensus/babe/CMakeLists.txt index 4208b872a8..b9638fe67d 100644 --- a/test/core/consensus/babe/CMakeLists.txt +++ b/test/core/consensus/babe/CMakeLists.txt @@ -26,6 +26,14 @@ target_link_libraries(babe_lottery_test logger_for_tests ) +addtest(babe_block_validating_test + babe_block_validator_test.cpp +) +target_link_libraries(babe_block_validating_test + consensus + logger_for_tests +) + addtest(threshold_util_test threshold_util_test.cpp ) diff --git a/test/core/consensus/babe/babe_block_validator_test.cpp b/test/core/consensus/babe/babe_block_validator_test.cpp new file mode 100644 index 0000000000..7708ca1fd6 --- /dev/null +++ b/test/core/consensus/babe/babe_block_validator_test.cpp @@ -0,0 +1,351 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "consensus/babe/impl/babe_block_validator_impl.hpp" +#include "consensus/babe/impl/babe_digests_util.hpp" +#include "consensus/babe/types/seal.hpp" +#include "mock/core/consensus/babe/babe_config_repository_mock.hpp" +#include "mock/core/consensus/timeline/slots_util_mock.hpp" +#include "mock/core/crypto/hasher_mock.hpp" +#include "mock/core/crypto/sr25519_provider_mock.hpp" +#include "mock/core/crypto/vrf_provider_mock.hpp" +#include "testutil/lazy.hpp" +#include "testutil/outcome.hpp" +#include "testutil/prepare_loggers.hpp" +#include "testutil/sr25519_utils.hpp" + +using kagome::common::Buffer; +using kagome::common::uint256_to_le_bytes; +using kagome::consensus::SlotNumber; +using kagome::consensus::SlotsUtil; +using kagome::consensus::SlotsUtilMock; +using kagome::consensus::Threshold; +using kagome::consensus::babe::Authorities; +using kagome::consensus::babe::Authority; +using kagome::consensus::babe::AuthorityId; +using kagome::consensus::babe::AuthorityIndex; +using kagome::consensus::babe::BabeBlockHeader; +using kagome::consensus::babe::BabeBlockValidatorImpl; +using kagome::consensus::babe::BabeConfigRepositoryMock; +using kagome::consensus::babe::BabeConfiguration; +using kagome::consensus::babe::DigestError; +using kagome::consensus::babe::Randomness; +using kagome::consensus::babe::SlotType; +using kagome::crypto::HasherMock; +using kagome::crypto::Sr25519Keypair; +using kagome::crypto::Sr25519ProviderMock; +using kagome::crypto::Sr25519PublicKey; +using kagome::crypto::Sr25519Signature; +using kagome::crypto::VRFPreOutput; +using kagome::crypto::VRFProof; +using kagome::crypto::VRFProviderMock; +using kagome::crypto::VRFVerifyOutput; +using kagome::primitives::Block; +using kagome::primitives::BlockBody; +using kagome::primitives::BlockHash; +using kagome::primitives::BlockHeader; +using kagome::primitives::ConsensusEngineId; +using kagome::primitives::Extrinsic; +using kagome::primitives::PreRuntime; +using Seal = kagome::consensus::babe::Seal; +using ValidatingError = + kagome::consensus::babe::BabeBlockValidatorImpl::ValidationError; + +using testing::_; +using testing::Invoke; +using testing::Mock; +using testing::Return; + +class BabeBlockValidatorTest : public testing::Test { + public: + const ConsensusEngineId kEngineId = + ConsensusEngineId::fromString("BABE").value(); + + static void SetUpTestCase() { + testutil::prepareLoggers(); + } + + void SetUp() override { + slots_util = std::make_shared(); + ON_CALL(*slots_util, slotToEpoch(_, _)).WillByDefault(Return(1)); + + config = std::make_shared(); + config->randomness.fill(0); + config->leadership_rate = {1, 4}; + + config_repo = std::make_shared(); + ON_CALL(*config_repo, config(_, _)).WillByDefault(Invoke([&] { + config->authorities = authorities; + return config; + })); + + hasher = std::make_shared(); + + sr25519_provider = std::make_shared(); + vrf_provider = std::make_shared(); + + block_validator = std::make_shared( + testutil::sptr_to_lazy(slots_util), + config_repo, + hasher, + sr25519_provider, + vrf_provider); + } + + std::shared_ptr slots_util; + std::shared_ptr our_keypair; + std::shared_ptr other_keypair; + std::shared_ptr config; + std::shared_ptr config_repo; + std::shared_ptr hasher; + std::shared_ptr sr25519_provider; + std::shared_ptr vrf_provider; + std::shared_ptr block_validator; + + // fields for block + BlockHash parent_hash_ = + BlockHash::fromString("c30ojfn4983u4093jv3894j3f034ojs3").value(); + + SlotNumber slot_number_ = 2; + VRFPreOutput vrf_value_ = {1, 2, 3, 4, 5}; + VRFProof vrf_proof_{}; + AuthorityIndex authority_index_ = {1}; + BabeBlockHeader babe_header_{SlotType::Primary, + authority_index_, + slot_number_, + {vrf_value_, vrf_proof_}}; + Buffer encoded_babe_header_{scale::encode(babe_header_).value()}; + + BlockHeader block_header_{123, // number + parent_hash_, // parent + {}, // state root + {}, // extrinsics root + {PreRuntime{{kEngineId, encoded_babe_header_}}}}; + Extrinsic ext_{Buffer{0x11, 0x22}}; + BlockBody block_body_{ext_}; + Block valid_block_{block_header_, block_body_}; + + Threshold threshold_ = 3820948573; + + BabeConfiguration config_{ + .leadership_rate = {3, 4}, + .authorities = {}, + .randomness = Randomness{uint256_to_le_bytes(475995757021)}, + .allowed_slots = {}, + }; + + Authorities authorities; + + /** + * Add a signature to the block + * @param block to seal + * @param block_hash - hash of the block to be sealed + * @return seal, which was produced, @and public key, which was generated + */ + std::pair sealBlock( + Block &block, const BlockHash &block_hash) const { + // generate a new keypair + Sr25519PublicKey public_key{}; + public_key.fill(8); + + Sr25519Signature sr25519_signature{}; + sr25519_signature.fill(0); + + // seal the block + Seal seal{sr25519_signature}; + Buffer encoded_seal{scale::encode(seal).value()}; + block.header.digest.push_back( + kagome::primitives::Seal{{kEngineId, encoded_seal}}); + + return {seal, public_key}; + } +}; + +/** + * @given block validator + * @when validating a valid block + * @then success + */ +TEST_F(BabeBlockValidatorTest, Success) { + // verifySignature + // get an encoded pre-seal part of the block's header + auto block_copy = valid_block_; + block_copy.header.digest.pop_back(); + auto encoded_block_copy = scale::encode(block_copy.header).value(); + BlockHash encoded_block_copy_hash{}; // not a real hash, but don't want to + // actually take it + std::copy(encoded_block_copy.begin(), + encoded_block_copy.begin() + BlockHash::size(), + encoded_block_copy_hash.begin()); + + auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); + + EXPECT_CALL(*hasher, blake2b_256(_)) + .WillOnce(Return(encoded_block_copy_hash)); + + auto authority = Authority{AuthorityId{pubkey}, 42}; + authorities.emplace_back(); + authorities.emplace_back(authority); + + EXPECT_CALL(*sr25519_provider, verify(_, _, pubkey)) + .WillOnce(Return(outcome::result(true))); + // verifyVRF + EXPECT_CALL(*vrf_provider, verifyTranscript(_, _, pubkey, _)) + .WillOnce(Return(VRFVerifyOutput{.is_valid = true, .is_less = true})); + + auto validate_res = block_validator->validateHeader(valid_block_.header); + ASSERT_TRUE(validate_res) << validate_res.error().message(); +} + +/** + * @given block validator + * @when validating block, which has less than two digests + * @then validation fails + */ +TEST_F(BabeBlockValidatorTest, LessDigestsThanNeeded) { + auto authority = Authority{{}, 42}; + authorities.emplace_back(authority); + + // for this test we can just not seal the block - it's the second digest + EXPECT_OUTCOME_FALSE(err, + block_validator->validateHeader(valid_block_.header)); + ASSERT_EQ(err, DigestError::REQUIRED_DIGESTS_NOT_FOUND); +} + +/** + * @given block validator + * @when validating block, which does not have a BabeHeader digest + * @then validation fails + */ +TEST_F(BabeBlockValidatorTest, NoBabeHeader) { + auto block_copy = valid_block_; + block_copy.header.digest.pop_back(); + auto encoded_block_copy = scale::encode(block_copy.header).value(); + BlockHash encoded_block_copy_hash{}; // not a real hash, but don't want to + // actually take it + std::copy(encoded_block_copy.begin(), + encoded_block_copy.begin() + BlockHash::size(), + encoded_block_copy_hash.begin()); + + // take BabeHeader out before sealing the block + valid_block_.header.digest.pop_back(); + + auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); + + authorities.emplace_back(); + authorities.emplace_back(pubkey, 42); + + EXPECT_OUTCOME_FALSE(err, + block_validator->validateHeader(valid_block_.header)); + ASSERT_EQ(err, DigestError::REQUIRED_DIGESTS_NOT_FOUND); +} + +/** + * @given block validator + * @when validating block with an invalid signature in the seal + * @then validation fails + */ +TEST_F(BabeBlockValidatorTest, SignatureVerificationFail) { + // GIVEN + auto block_copy = valid_block_; + block_copy.header.digest.pop_back(); + auto encoded_block_copy = scale::encode(block_copy.header).value(); + BlockHash encoded_block_copy_hash{}; + std::copy(encoded_block_copy.begin(), + encoded_block_copy.begin() + BlockHash::size(), + encoded_block_copy_hash.begin()); + + auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); + + authorities.emplace_back(); + authorities.emplace_back(pubkey, 42); + + EXPECT_CALL(*hasher, blake2b_256(_)) + .WillOnce(Return(encoded_block_copy_hash)); + + EXPECT_CALL(*sr25519_provider, verify(_, _, _)).WillOnce(Return(false)); + + // WHEN-THEN + EXPECT_OUTCOME_FALSE(err, + block_validator->validateHeader(valid_block_.header)); + ASSERT_EQ(err, ValidatingError::INVALID_SIGNATURE); +} + +/** + * @given block validator + * @when validating block with an invalid VRF proof + * @then validation fails + */ +TEST_F(BabeBlockValidatorTest, VRFFail) { + // GIVEN + auto block_copy = valid_block_; + block_copy.header.digest.pop_back(); + auto encoded_block_copy = scale::encode(block_copy.header).value(); + BlockHash encoded_block_copy_hash{}; + std::copy_n(encoded_block_copy.begin(), + BlockHash::size(), + encoded_block_copy_hash.begin()); + + auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); + + authorities.emplace_back(); + authorities.emplace_back(pubkey, 42); + + EXPECT_CALL(*hasher, blake2b_256(_)) + .WillOnce(Return(encoded_block_copy_hash)); + + EXPECT_CALL(*sr25519_provider, verify(_, _, pubkey)).WillOnce(Return(true)); + + // WHEN + EXPECT_CALL(*vrf_provider, verifyTranscript(_, _, pubkey, _)) + .WillOnce(Return(VRFVerifyOutput{.is_valid = false, .is_less = true})); + + // THEN + EXPECT_OUTCOME_FALSE(err, + block_validator->validateHeader(valid_block_.header)); + ASSERT_EQ(err, ValidatingError::INVALID_VRF); +} + +/** + * @given block validator + * @when validating block, which was produced by a non-slot-leader + * @then validation fails + */ +TEST_F(BabeBlockValidatorTest, ThresholdGreater) { + // GIVEN + auto block_copy = valid_block_; + block_copy.header.digest.pop_back(); + auto encoded_block_copy = scale::encode(block_copy.header).value(); + BlockHash encoded_block_copy_hash{}; + std::copy(encoded_block_copy.begin(), + encoded_block_copy.begin() + BlockHash::size(), + encoded_block_copy_hash.begin()); + + auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); + + EXPECT_CALL(*hasher, blake2b_256(_)) + .WillOnce(Return(encoded_block_copy_hash)); + + EXPECT_CALL(*sr25519_provider, verify(_, _, pubkey)) + .WillOnce(Return(outcome::result(true))); + + authorities.emplace_back(); + auto authority = Authority{{pubkey}, 42}; + authorities.emplace_back(authority); + + // WHEN + threshold_ = 0; + + EXPECT_CALL(*vrf_provider, verifyTranscript(_, _, pubkey, _)) + .WillOnce(Return(VRFVerifyOutput{.is_valid = true, .is_less = false})); + + // THEN + EXPECT_OUTCOME_FALSE(err, + block_validator->validateHeader(valid_block_.header)); + ASSERT_EQ(err, ValidatingError::INVALID_VRF); +} diff --git a/test/core/consensus/babe/babe_test.cpp b/test/core/consensus/babe/babe_test.cpp index 605d2010e8..032e04d0c3 100644 --- a/test/core/consensus/babe/babe_test.cpp +++ b/test/core/consensus/babe/babe_test.cpp @@ -17,12 +17,14 @@ #include "mock/core/authorship/proposer_mock.hpp" #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/clock/clock_mock.hpp" +#include "mock/core/consensus/babe/babe_block_validator_mock.hpp" #include "mock/core/consensus/babe/babe_config_repository_mock.hpp" -#include "mock/core/consensus/babe_lottery_mock.hpp" +#include "mock/core/consensus/babe/babe_lottery_mock.hpp" #include "mock/core/consensus/timeline/slots_util_mock.hpp" #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/crypto/session_keys_mock.hpp" #include "mock/core/crypto/sr25519_provider_mock.hpp" +#include "mock/core/crypto/vrf_provider_mock.hpp" #include "mock/core/dispute_coordinator/dispute_coordinator_mock.hpp" #include "mock/core/network/block_announce_transmitter_mock.hpp" #include "mock/core/parachain/backing_store_mock.hpp" @@ -45,6 +47,7 @@ using kagome::blockchain::BlockTreeMock; using kagome::clock::SystemClockMock; using kagome::common::Buffer; using kagome::common::BufferView; +using kagome::common::uint256_to_le_bytes; using kagome::consensus::BlockProductionError; using kagome::consensus::Duration; using kagome::consensus::EpochLength; @@ -54,31 +57,45 @@ using kagome::consensus::SlotLeadershipError; using kagome::consensus::SlotNumber; using kagome::consensus::SlotsUtil; using kagome::consensus::SlotsUtilMock; +using kagome::consensus::Threshold; using kagome::consensus::ValidatorStatus; +using kagome::consensus::babe::Authorities; using kagome::consensus::babe::Authority; +using kagome::consensus::babe::AuthorityId; +using kagome::consensus::babe::AuthorityIndex; using kagome::consensus::babe::Babe; using kagome::consensus::babe::BabeBlockHeader; +using kagome::consensus::babe::BabeBlockValidator; +using kagome::consensus::babe::BabeBlockValidatorMock; using kagome::consensus::babe::BabeConfigRepositoryMock; using kagome::consensus::babe::BabeConfiguration; using kagome::consensus::babe::BabeLotteryMock; using kagome::consensus::babe::DigestError; +using kagome::consensus::babe::Randomness; using kagome::consensus::babe::SlotLeadership; using kagome::consensus::babe::SlotType; using kagome::crypto::HasherMock; using kagome::crypto::SessionKeysMock; using kagome::crypto::Sr25519Keypair; using kagome::crypto::Sr25519ProviderMock; +using kagome::crypto::Sr25519PublicKey; using kagome::crypto::Sr25519Signature; using kagome::crypto::VRFOutput; +using kagome::crypto::VRFPreOutput; +using kagome::crypto::VRFProof; +using kagome::crypto::VRFProviderMock; +using kagome::crypto::VRFVerifyOutput; using kagome::dispute::DisputeCoordinatorMock; using kagome::dispute::MultiDisputeStatementSet; using kagome::network::BlockAnnounceTransmitterMock; using kagome::parachain::BackingStoreMock; using kagome::parachain::BitfieldStoreMock; using kagome::primitives::Block; +using kagome::primitives::BlockBody; using kagome::primitives::BlockHash; using kagome::primitives::BlockHeader; using kagome::primitives::BlockInfo; +using kagome::primitives::ConsensusEngineId; using kagome::primitives::Digest; using kagome::primitives::Extrinsic; using kagome::primitives::PreRuntime; @@ -170,6 +187,7 @@ class BabeTest : public testing::Test { .WillByDefault(Return(new_block_info.hash)); sr25519_provider = std::make_shared(); + block_validator = std::make_shared(); bitfield_store = std::make_shared(); backing_store = std::make_shared(); @@ -200,6 +218,7 @@ class BabeTest : public testing::Test { lottery, hasher, sr25519_provider, + block_validator, bitfield_store, backing_store, dispute_coordinator, @@ -222,6 +241,7 @@ class BabeTest : public testing::Test { std::shared_ptr lottery; std::shared_ptr hasher; std::shared_ptr sr25519_provider; + std::shared_ptr block_validator; std::shared_ptr bitfield_store; std::shared_ptr backing_store; std::shared_ptr dispute_coordinator; diff --git a/test/core/consensus/timeline/block_executor_test.cpp b/test/core/consensus/timeline/block_executor_test.cpp index c9da45ac62..5e7a1d897b 100644 --- a/test/core/consensus/timeline/block_executor_test.cpp +++ b/test/core/consensus/timeline/block_executor_test.cpp @@ -16,8 +16,9 @@ #include "mock/core/blockchain/block_tree_mock.hpp" #include "mock/core/consensus/babe/babe_config_repository_mock.hpp" #include "mock/core/consensus/grandpa/environment_mock.hpp" +#include "mock/core/consensus/production_consensus_mock.hpp" +#include "mock/core/consensus/timeline/consensus_selector_mock.hpp" #include "mock/core/consensus/timeline/slots_util_mock.hpp" -#include "mock/core/consensus/validation/block_validator_mock.hpp" #include "mock/core/crypto/hasher_mock.hpp" #include "mock/core/runtime/core_mock.hpp" #include "mock/core/runtime/offchain_worker_api_mock.hpp" @@ -36,9 +37,11 @@ using kagome::blockchain::BlockTreeMock; using kagome::common::Buffer; using kagome::consensus::BlockAppenderBase; using kagome::consensus::BlockExecutorImpl; -using kagome::consensus::BlockValidator; +using kagome::consensus::ConsensusSelector; +using kagome::consensus::ConsensusSelectorMock; using kagome::consensus::EpochNumber; using kagome::consensus::EpochTimings; +using kagome::consensus::ProductionConsensusMock; using kagome::consensus::SlotsUtil; using kagome::consensus::SlotsUtilMock; using BabeAuthority = kagome::consensus::babe::Authority; @@ -47,7 +50,6 @@ using BabeAuthorities = kagome::consensus::babe::Authorities; using kagome::consensus::babe::BabeBlockHeader; using kagome::consensus::babe::BabeConfigRepositoryMock; using kagome::consensus::babe::BabeConfiguration; -using kagome::consensus::babe::BlockValidatorMock; using GrandpaAuthority = kagome::consensus::grandpa::Authority; using GrandpaAuthorityId = kagome::consensus::grandpa::AuthorityId; using GrandpaAuthorities = kagome::consensus::grandpa::Authorities; @@ -140,7 +142,6 @@ class BlockExecutorTest : public testing::Test { ON_CALL(*babe_config_repo_, config(_, _)) .WillByDefault(Return(babe_config_)); - block_validator_ = std::make_shared(); grandpa_environment_ = std::make_shared(); tx_pool_ = std::make_shared(); hasher_ = std::make_shared(); @@ -148,6 +149,14 @@ class BlockExecutorTest : public testing::Test { slots_util_ = std::make_shared(); ON_CALL(*slots_util_, slotToEpoch(_, _)).WillByDefault(Return(1)); + production_consensus_ = std::make_shared(); + + consensus_selector_ = std::make_shared(); + ON_CALL(*consensus_selector_, getProductionConsensusByInfo(_)) + .WillByDefault(Return(production_consensus_)); + ON_CALL(*consensus_selector_, getProductionConsensusByHeader(_)) + .WillByDefault(Return(production_consensus_)); + offchain_worker_api_ = std::make_shared(); storage_sub_engine_ = std::make_shared< kagome::primitives::events::StorageSubscriptionEngine>(); @@ -158,10 +167,10 @@ class BlockExecutorTest : public testing::Test { block_tree_, babe_config_repo_, timings_, - block_validator_, grandpa_environment_, testutil::sptr_to_lazy(slots_util_), - hasher_); + hasher_, + testutil::sptr_to_lazy(consensus_selector_)); thread_pool_ = std::make_shared("test", 1); @@ -187,11 +196,12 @@ class BlockExecutorTest : public testing::Test { .slot_duration = 60ms, .epoch_length = 2, }; - std::shared_ptr block_validator_; std::shared_ptr grandpa_environment_; std::shared_ptr tx_pool_; std::shared_ptr hasher_; std::shared_ptr slots_util_; + std::shared_ptr consensus_selector_; + std::shared_ptr production_consensus_; std::shared_ptr offchain_worker_api_; kagome::primitives::events::StorageSubscriptionEnginePtr storage_sub_engine_; kagome::primitives::events::ChainSubscriptionEnginePtr chain_sub_engine_; @@ -246,14 +256,7 @@ TEST_F(BlockExecutorTest, JustificationFollowDigests) { testing::Return(kagome::blockchain::BlockTreeError::BODY_NOT_FOUND)); babe_config_->leadership_rate.second = 42; - EXPECT_CALL( - *block_validator_, - validateHeader(header, - 1, - "auth3"_babe_auth, - kagome::consensus::babe::calculateThreshold( - babe_config_->leadership_rate, babe_authorities, 0), - testing::Ref(*babe_config_))) + EXPECT_CALL(*production_consensus_, validateHeader(header)) .WillOnce(testing::Return(outcome::success())); EXPECT_CALL(*block_tree_, getBlockHeader(parent_hash)) .WillRepeatedly(testing::Return(kagome::primitives::BlockHeader{ diff --git a/test/core/consensus/timeline/slots_util_test.cpp b/test/core/consensus/timeline/slots_util_test.cpp index 70301054e1..05c4046ce0 100644 --- a/test/core/consensus/timeline/slots_util_test.cpp +++ b/test/core/consensus/timeline/slots_util_test.cpp @@ -59,7 +59,7 @@ class SlotsUtilTest : public testing::Test { consensus_selector = std::make_shared(); production_consensus = std::make_shared(); - EXPECT_CALL(*consensus_selector, getProductionConsensus(_)) + EXPECT_CALL(*consensus_selector, getProductionConsensusByInfo(_)) .WillRepeatedly(Return(production_consensus)); trie_storage = std::make_shared(); diff --git a/test/core/consensus/timeline/timeline_test.cpp b/test/core/consensus/timeline/timeline_test.cpp index 0c05bc94b3..c782e7b38b 100644 --- a/test/core/consensus/timeline/timeline_test.cpp +++ b/test/core/consensus/timeline/timeline_test.cpp @@ -141,7 +141,7 @@ class TimelineTest : public testing::Test { consensus_selector = std::make_shared(); production_consensus = std::make_shared(); - ON_CALL(*consensus_selector, getProductionConsensus(_)) + ON_CALL(*consensus_selector, getProductionConsensusByInfo(_)) .WillByDefault(Return(production_consensus)); ON_CALL(*production_consensus, getSlot(best_block_header)) .WillByDefault(Return(1)); diff --git a/test/core/consensus/validation/CMakeLists.txt b/test/core/consensus/validation/CMakeLists.txt deleted file mode 100644 index 5c68d3a7bd..0000000000 --- a/test/core/consensus/validation/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# -# Copyright Quadrivium LLC -# All Rights Reserved -# SPDX-License-Identifier: Apache-2.0 -# - -addtest(block_validator_test - block_validator_test.cpp - ) -target_link_libraries(block_validator_test - consensus - p2p::p2p_peer_id - testutil_primitives_generator - sr25519_provider - mp_utils - keccak - logger_for_tests - ) diff --git a/test/core/consensus/validation/block_validator_test.cpp b/test/core/consensus/validation/block_validator_test.cpp deleted file mode 100644 index b1b1d9039f..0000000000 --- a/test/core/consensus/validation/block_validator_test.cpp +++ /dev/null @@ -1,376 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#include - -#include "common/int_serialization.hpp" -#include "consensus/babe/impl/babe_digests_util.hpp" -#include "consensus/babe/types/authority.hpp" -#include "consensus/validation/babe_block_validator.hpp" -#include "mock/core/blockchain/block_tree_mock.hpp" -#include "mock/core/crypto/hasher_mock.hpp" -#include "mock/core/crypto/sr25519_provider_mock.hpp" -#include "mock/core/crypto/vrf_provider_mock.hpp" -#include "mock/core/runtime/tagged_transaction_queue_mock.hpp" -#include "mock/libp2p/crypto/random_generator_mock.hpp" -#include "scale/scale.hpp" -#include "testutil/outcome.hpp" -#include "testutil/prepare_loggers.hpp" -#include "testutil/primitives/mp_utils.hpp" - -using namespace kagome; -using namespace blockchain; -using namespace consensus; -using namespace babe; -using namespace runtime; -using namespace common; -using namespace crypto; -using namespace libp2p::crypto::random; - -using kagome::consensus::babe::Authority; -using kagome::consensus::babe::AuthorityIndex; -using kagome::primitives::Block; -using kagome::primitives::BlockBody; -using kagome::primitives::BlockHeader; -using kagome::primitives::ConsensusEngineId; -using kagome::primitives::Digest; -using kagome::primitives::Extrinsic; -using kagome::primitives::InvalidTransaction; -using kagome::primitives::PreRuntime; -using kagome::primitives::TransactionSource; -using kagome::primitives::TransactionValidity; -using kagome::primitives::ValidTransaction; - -using testing::_; -using testing::Return; -using testing::ReturnRef; - -using testutil::createHash256; - -namespace sr25519_constants = kagome::crypto::constants::sr25519; - -class BlockValidatorTest : public testing::Test { - public: - static void SetUpTestCase() { - testutil::prepareLoggers(); - } - - const ConsensusEngineId kEngineId = - primitives::ConsensusEngineId::fromString("BABE").value(); - /** - * Add a signature to the block - * @param block to seal - * @param block_hash - hash of the block to be sealed - * @return seal, which was produced, @and public key, which was generated - */ - std::pair sealBlock(Block &block, - Hash256 block_hash) const { - // generate a new keypair - Sr25519PublicKey public_key{}; - public_key.fill(8); - - Sr25519Signature sr25519_signature{}; - sr25519_signature.fill(0); - - // seal the block - Seal seal{sr25519_signature}; - common::Buffer encoded_seal{scale::encode(seal).value()}; - block.header.digest.push_back( - kagome::primitives::Seal{{kEngineId, encoded_seal}}); - - return {seal, public_key}; - } - - // fields for validator - std::shared_ptr tree_ = std::make_shared(); - std::shared_ptr tx_queue_ = - std::make_shared(); - std::shared_ptr hasher_ = std::make_shared(); - std::shared_ptr vrf_provider_ = - std::make_shared(); - std::shared_ptr sr25519_provider_ = - std::make_shared(); - - BabeBlockValidator validator_{ - tree_, tx_queue_, hasher_, vrf_provider_, sr25519_provider_}; - - // fields for block - Hash256 parent_hash_ = - Hash256::fromString("c30ojfn4983u4093jv3894j3f034ojs3").value(); - - SlotNumber slot_number_ = 2; - VRFPreOutput vrf_value_ = {1, 2, 3, 4, 5}; - VRFProof vrf_proof_{}; - AuthorityIndex authority_index_ = {1}; - BabeBlockHeader babe_header_{SlotType::Primary, - authority_index_, - slot_number_, - {vrf_value_, vrf_proof_}}; - Buffer encoded_babe_header_{scale::encode(babe_header_).value()}; - - BlockHeader block_header_{123, // number - parent_hash_, // parent - {}, // state root - {}, // extrinsics root - {PreRuntime{{kEngineId, encoded_babe_header_}}}}; - Extrinsic ext_{Buffer{0x11, 0x22}}; - BlockBody block_body_{ext_}; - Block valid_block_{block_header_, block_body_}; - - Threshold threshold_ = 3820948573; - Authorities authorities_; - - consensus::babe::BabeConfiguration config_{ - .leadership_rate = {3, 4}, - .authorities = {}, - .randomness = Randomness{uint256_to_le_bytes(475995757021)}, - .allowed_slots = {}, - }; -}; - -/** - * @given block validator - * @when validating a valid block - * @then success - */ -TEST_F(BlockValidatorTest, Success) { - // verifySignature - // get an encoded pre-seal part of the block's header - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; // not a real hash, but don't want to - // actually take it - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); - - EXPECT_CALL(*hasher_, blake2b_256(_)) - .WillOnce(Return(encoded_block_copy_hash)); - - auto authority = Authority{{pubkey}, 42}; - authorities_.emplace_back(); - authorities_.emplace_back(authority); - - EXPECT_CALL(*sr25519_provider_, verify(_, _, pubkey)) - .WillOnce(Return(outcome::result(true))); - // verifyVRF - EXPECT_CALL(*vrf_provider_, verifyTranscript(_, _, pubkey, _)) - .WillOnce(Return(VRFVerifyOutput{.is_valid = true, .is_less = true})); - - auto validate_res = validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_); - ASSERT_TRUE(validate_res) << validate_res.error().message(); -} - -/** - * @given block validator - * @when validating block, which has less than two digests - * @then validation fails - */ -TEST_F(BlockValidatorTest, LessDigestsThanNeeded) { - auto authority = Authority{{}, 42}; - authorities_.emplace_back(authority); - - // for this test we can just not seal the block - it's the second digest - EXPECT_OUTCOME_FALSE( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_)); - ASSERT_EQ(err, consensus::babe::DigestError::REQUIRED_DIGESTS_NOT_FOUND); -} - -/** - * @given block validator - * @when validating block, which does not have a BabeHeader digest - * @then validation fails - */ -TEST_F(BlockValidatorTest, NoBabeHeader) { - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; // not a real hash, but don't want to - // actually take it - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - // take BabeHeader out before sealing the block - valid_block_.header.digest.pop_back(); - - auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); - - auto authority = Authority{{pubkey}, 42}; - authorities_.emplace_back(); - authorities_.emplace_back(authority); - - EXPECT_OUTCOME_FALSE( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_)); - ASSERT_EQ(err, consensus::babe::DigestError::REQUIRED_DIGESTS_NOT_FOUND); -} - -/** - * @given block validator - * @when validating block, which was produced by authority, which we don't know - * about - * @then validation fails - */ -TEST_F(BlockValidatorTest, NoAuthority) { - // GIVEN - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - const auto &[seal, public_key] = - sealBlock(valid_block_, encoded_block_copy_hash); - - EXPECT_CALL(*hasher_, blake2b_256(_)) - .WillOnce(Return(encoded_block_copy_hash)); - - // WHEN - // only one authority even though we want at least two - auto authority = Authority{{}, 42}; - - EXPECT_CALL( - *sr25519_provider_, - verify(seal.signature, _, kagome::crypto::Sr25519PublicKey{authority.id})) - .WillOnce(Return(false)); - - // THEN - EXPECT_OUTCOME_ERROR( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_), - BabeBlockValidator::ValidationError::INVALID_SIGNATURE); -} - -/** - * @given block validator - * @when validating block with an invalid signature in the seal - * @then validation fails - */ -TEST_F(BlockValidatorTest, SignatureVerificationFail) { - // GIVEN - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); - - EXPECT_CALL(*hasher_, blake2b_256(_)) - .WillOnce(Return(encoded_block_copy_hash)); - - EXPECT_CALL(*sr25519_provider_, verify(_, _, pubkey)) - .WillOnce(Return(outcome::result(false))); - - authorities_.emplace_back(); - auto authority = Authority{{pubkey}, 42}; - authorities_.emplace_back(authority); - - // WHEN - // mutate seal of the block to make signature invalid - boost::get(valid_block_.header.digest[1]) - .data[10]++; - - // THEN - EXPECT_OUTCOME_FALSE( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_)); - ASSERT_EQ(err, BabeBlockValidator::ValidationError::INVALID_SIGNATURE); -} - -/** - * @given block validator - * @when validating block with an invalid VRF proof - * @then validation fails - */ -TEST_F(BlockValidatorTest, VRFFail) { - // GIVEN - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); - - EXPECT_CALL(*hasher_, blake2b_256(_)) - .WillOnce(Return(encoded_block_copy_hash)); - - EXPECT_CALL(*sr25519_provider_, verify(_, _, pubkey)) - .WillOnce(Return(outcome::result(true))); - - authorities_.emplace_back(); - auto authority = Authority{{pubkey}, 42}; - authorities_.emplace_back(authority); - - // WHEN - EXPECT_CALL(*vrf_provider_, verifyTranscript(_, _, pubkey, _)) - .WillOnce(Return(VRFVerifyOutput{.is_valid = false, .is_less = true})); - - // THEN - EXPECT_OUTCOME_FALSE( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_)); - ASSERT_EQ(err, BabeBlockValidator::ValidationError::INVALID_VRF); -} - -/** - * @given block validator - * @when validating block, which was produced by a non-slot-leader - * @then validation fails - */ -TEST_F(BlockValidatorTest, ThresholdGreater) { - // GIVEN - auto block_copy = valid_block_; - block_copy.header.digest.pop_back(); - auto encoded_block_copy = scale::encode(block_copy.header).value(); - Hash256 encoded_block_copy_hash{}; - std::copy(encoded_block_copy.begin(), - encoded_block_copy.begin() + Hash256::size(), - encoded_block_copy_hash.begin()); - - auto [seal, pubkey] = sealBlock(valid_block_, encoded_block_copy_hash); - - EXPECT_CALL(*hasher_, blake2b_256(_)) - .WillOnce(Return(encoded_block_copy_hash)); - - EXPECT_CALL(*sr25519_provider_, verify(_, _, pubkey)) - .WillOnce(Return(outcome::result(true))); - - authorities_.emplace_back(); - auto authority = Authority{{pubkey}, 42}; - authorities_.emplace_back(authority); - - // WHEN - threshold_ = 0; - - EXPECT_CALL(*vrf_provider_, verifyTranscript(_, _, pubkey, _)) - .WillOnce(Return(VRFVerifyOutput{.is_valid = true, .is_less = false})); - - // THEN - EXPECT_OUTCOME_FALSE( - err, - validator_.validateHeader( - valid_block_.header, 0ull, authority.id, threshold_, config_)); - ASSERT_EQ(err, BabeBlockValidator::ValidationError::INVALID_VRF); -} diff --git a/test/mock/core/consensus/babe/babe_block_validator_mock.hpp b/test/mock/core/consensus/babe/babe_block_validator_mock.hpp new file mode 100644 index 0000000000..b75aef7b75 --- /dev/null +++ b/test/mock/core/consensus/babe/babe_block_validator_mock.hpp @@ -0,0 +1,23 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "consensus/babe/babe_block_validator.hpp" + +#include + +namespace kagome::consensus::babe { + + class BabeBlockValidatorMock : public BabeBlockValidator { + public: + MOCK_METHOD(outcome::result, + validateHeader, + (const primitives::BlockHeader &block_header), + (const, override)); + }; + +} // namespace kagome::consensus::babe diff --git a/test/mock/core/consensus/babe_lottery_mock.hpp b/test/mock/core/consensus/babe/babe_lottery_mock.hpp similarity index 100% rename from test/mock/core/consensus/babe_lottery_mock.hpp rename to test/mock/core/consensus/babe/babe_lottery_mock.hpp diff --git a/test/mock/core/consensus/production_consensus_mock.hpp b/test/mock/core/consensus/production_consensus_mock.hpp index ba03eaa5ca..73a3467126 100644 --- a/test/mock/core/consensus/production_consensus_mock.hpp +++ b/test/mock/core/consensus/production_consensus_mock.hpp @@ -31,12 +31,12 @@ namespace kagome::consensus { MOCK_METHOD(bool, changeEpoch, - (EpochNumber epoch, const primitives::BlockInfo &block), + (EpochNumber, const primitives::BlockInfo &), (const, override)); MOCK_METHOD(bool, checkSlotLeadership, - (const primitives::BlockInfo &block, SlotNumber slot), + (const primitives::BlockInfo &, SlotNumber), (override)); MOCK_METHOD(outcome::result, @@ -46,7 +46,12 @@ namespace kagome::consensus { MOCK_METHOD(outcome::result, makeSeal, - (const primitives::Block &block), + (const primitives::Block &), + (const, override)); + + MOCK_METHOD(outcome::result, + validateHeader, + (const primitives::BlockHeader &), (const, override)); }; } // namespace kagome::consensus diff --git a/test/mock/core/consensus/timeline/consensus_selector_mock.hpp b/test/mock/core/consensus/timeline/consensus_selector_mock.hpp index 86eedc8f38..535424a86d 100644 --- a/test/mock/core/consensus/timeline/consensus_selector_mock.hpp +++ b/test/mock/core/consensus/timeline/consensus_selector_mock.hpp @@ -15,14 +15,32 @@ namespace kagome::consensus { class ConsensusSelectorMock final : public ConsensusSelector { public: MOCK_METHOD(std::shared_ptr, - getProductionConsensus, + getProductionConsensusByInfo, (const primitives::BlockInfo &), - (const, override)); + (const)); + std::shared_ptr getProductionConsensus( + const primitives::BlockInfo &info) const override { + return getProductionConsensusByInfo(info); + } + + MOCK_METHOD(std::shared_ptr, + getProductionConsensusByHeader, + (const primitives::BlockHeader &), + (const)); + std::shared_ptr getProductionConsensus( + const primitives::BlockHeader &header) const override { + return getProductionConsensusByHeader(header); + } MOCK_METHOD(std::shared_ptr, getFinalityConsensus, (const primitives::BlockInfo &), (const, override)); + + MOCK_METHOD(std::shared_ptr, + getFinalityConsensus, + (const primitives::BlockHeader &), + (const, override)); }; } // namespace kagome::consensus diff --git a/test/mock/core/consensus/validation/block_validator_mock.hpp b/test/mock/core/consensus/validation/block_validator_mock.hpp deleted file mode 100644 index 979ce76ee6..0000000000 --- a/test/mock/core/consensus/validation/block_validator_mock.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Copyright Quadrivium LLC - * All Rights Reserved - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include "consensus/validation/babe_block_validator.hpp" - -#include - -namespace kagome::consensus::babe { - - class BlockValidatorMock : public BlockValidator { - public: - MOCK_METHOD(outcome::result, - validateBlock, - (const primitives::Block &, - const AuthorityId &, - const Threshold &, - const Randomness &), - (const)); - - MOCK_METHOD(outcome::result, - validateHeader, - (const primitives::BlockHeader &, - const EpochNumber, - const AuthorityId &, - const Threshold &, - const BabeConfiguration &), - (const, override)); - }; - -} // namespace kagome::consensus::babe