From 9b3af6b06e518c7d5156159b4bb409a9c0ec44b1 Mon Sep 17 00:00:00 2001 From: turuslan Date: Fri, 10 May 2024 17:00:47 +0500 Subject: [PATCH 1/4] historical votes Signed-off-by: turuslan --- core/consensus/grandpa/grandpa.hpp | 3 +- core/consensus/grandpa/historical_votes.hpp | 45 ++++++++++++ core/consensus/grandpa/impl/grandpa_impl.cpp | 71 +++++++------------ core/consensus/grandpa/impl/grandpa_impl.hpp | 18 ++--- .../grandpa/impl/voting_round_impl.cpp | 45 ++++++++---- .../grandpa/impl/voting_round_impl.hpp | 43 +++++++++-- core/storage/predefined_keys.hpp | 3 +- .../voting_round/voting_round_test.cpp | 26 ++++++- .../core/consensus/grandpa/grandpa_mock.hpp | 5 ++ 9 files changed, 181 insertions(+), 78 deletions(-) create mode 100644 core/consensus/grandpa/historical_votes.hpp diff --git a/core/consensus/grandpa/grandpa.hpp b/core/consensus/grandpa/grandpa.hpp index ff59ee1695..db182f1374 100644 --- a/core/consensus/grandpa/grandpa.hpp +++ b/core/consensus/grandpa/grandpa.hpp @@ -8,6 +8,7 @@ #include "consensus/finality_consensus.hpp" #include "consensus/grandpa/common.hpp" +#include "consensus/grandpa/historical_votes.hpp" #include @@ -21,7 +22,7 @@ namespace kagome::consensus::grandpa { * Interface for launching new grandpa rounds. See more details in * kagome::consensus::grandpa::GrandpaImpl */ - class Grandpa : public FinalityConsensus { + class Grandpa : public FinalityConsensus, public SaveHistoricalVotes { public: virtual ~Grandpa() = default; diff --git a/core/consensus/grandpa/historical_votes.hpp b/core/consensus/grandpa/historical_votes.hpp new file mode 100644 index 0000000000..6f15304c20 --- /dev/null +++ b/core/consensus/grandpa/historical_votes.hpp @@ -0,0 +1,45 @@ +/** + * Copyright Quadrivium LLC + * All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "consensus/grandpa/structs.hpp" + +namespace kagome::consensus::grandpa { + /** + * Historical votes seen in a round. + * https://github.com/paritytech/finality-grandpa/blob/8c45a664c05657f0c71057158d3ba555ba7d20de/src/lib.rs#L544 + */ + struct HistoricalVotes { + SCALE_TIE(3); + + std::vector seen; + std::optional prevote_idx, precommit_idx; + + /** + * Set the number of messages seen before prevoting. + */ + void set_prevoted_index() { + prevote_idx = seen.size(); + } + + /** + * Set the number of messages seen before precommiting. + */ + void set_precommitted_index() { + precommit_idx = seen.size(); + } + }; + + class SaveHistoricalVotes { + public: + virtual ~SaveHistoricalVotes() = default; + + virtual void saveHistoricalVotes(AuthoritySetId set, + RoundNumber round, + const HistoricalVotes &votes) = 0; + }; +} // namespace kagome::consensus::grandpa diff --git a/core/consensus/grandpa/impl/grandpa_impl.cpp b/core/consensus/grandpa/impl/grandpa_impl.cpp index 924f79eade..f92cae4080 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.cpp +++ b/core/consensus/grandpa/impl/grandpa_impl.cpp @@ -66,6 +66,13 @@ namespace kagome::consensus::grandpa { // https://github.com/paritytech/polkadot/pull/6217 constexpr std::chrono::milliseconds kGossipDuration{1000}; + inline auto historicalVotesKey(AuthoritySetId set, RoundNumber round) { + auto key = storage::kGrandpaHistoricalVotesPrefix; + key.putUint64(set); + key.putUint64(round); + return key; + } + GrandpaImpl::GrandpaImpl( std::shared_ptr app_state_manager, std::shared_ptr hasher, @@ -125,12 +132,6 @@ namespace kagome::consensus::grandpa { } bool GrandpaImpl::tryStart() { - if (auto r = db_->get(storage::kGrandpaVotesKey)) { - if (auto r2 = scale::decode(r.value())) { - cached_votes_ = std::move(r2.value()); - } - } - // Obtain last completed round auto round_state_res = getLastCompletedRound(); if (not round_state_res.has_value()) { @@ -211,24 +212,19 @@ namespace kagome::consensus::grandpa { auto vote_crypto_provider = std::make_shared( keypair, crypto_provider_, round_state.round_number, config.voters); - auto save_cached_votes = [weak{weak_from_this()}]() { - if (auto self = weak.lock()) { - self->saveCachedVotes(); - } - }; auto new_round = std::make_shared( shared_from_this(), std::move(config), hasher_, environment_, - std::move(save_cached_votes), + historicalVotes(authority_set.id, round_state.round_number), std::move(vote_crypto_provider), std::make_shared(), // Prevote tracker std::make_shared(), // Precommit tracker std::move(vote_graph), scheduler_, round_state); - applyCachedVotes(*new_round); + new_round->applyHistoricalVotes(); new_round->end(); // it is okay, because we do not want to actually execute // this round @@ -277,24 +273,19 @@ namespace kagome::consensus::grandpa { auto vote_crypto_provider = std::make_shared( keypair, crypto_provider_, new_round_number, config.voters); - auto save_cached_votes = [weak{weak_from_this()}]() { - if (auto self = weak.lock()) { - self->saveCachedVotes(); - } - }; auto new_round = std::make_shared( shared_from_this(), std::move(config), hasher_, environment_, - std::move(save_cached_votes), + historicalVotes(authority_set->id, new_round_number), std::move(vote_crypto_provider), std::make_shared(), // Prevote tracker std::make_shared(), // Precommit tracker std::move(vote_graph), scheduler_, round); - applyCachedVotes(*new_round); + new_round->applyHistoricalVotes(); return new_round; } @@ -1192,7 +1183,7 @@ namespace kagome::consensus::grandpa { GrandpaConfig{voters, justification.round_number, {}, {}}, hasher_, environment_, - nullptr, + HistoricalVotes{}, std::make_shared( nullptr, crypto_provider_, justification.round_number, voters), std::make_shared(), @@ -1424,36 +1415,24 @@ namespace kagome::consensus::grandpa { retain_if(waiting_blocks_, f); } - void GrandpaImpl::saveCachedVotes() { - CachedVotes rounds; - for (auto round = current_round_; round; - round = round->getPreviousRound()) { - rounds.emplace_back(CachedRound{ - round->voterSetId(), - round->roundNumber(), - round->votes(), - }); + void GrandpaImpl::saveHistoricalVotes(AuthoritySetId set, + RoundNumber round, + const HistoricalVotes &votes) { + if (votes.seen.empty()) { + return; } std::ignore = - db_->put(storage::kGrandpaVotesKey, scale::encode(rounds).value()); + db_->put(historicalVotesKey(set, round), scale::encode(votes).value()); } - void GrandpaImpl::applyCachedVotes(VotingRound &round) { - auto it = std::find_if( - cached_votes_.begin(), cached_votes_.end(), [&](const CachedRound &c) { - return c.set == round.voterSetId() and c.round == round.roundNumber(); - }); - if (it == cached_votes_.end()) { - return; - } - VotingRoundUpdate update{round}; - for (auto &vote : it->votes.first) { - update.vote(vote); - } - for (auto &vote : it->votes.second) { - update.vote(vote); + HistoricalVotes GrandpaImpl::historicalVotes(AuthoritySetId set, + RoundNumber round) const { + if (auto r = db_->get(historicalVotesKey(set, round))) { + if (auto r2 = scale::decode(r.value())) { + return std::move(r2.value()); + } } - update.update(); + return {}; } void GrandpaImpl::setTimerFallback() { diff --git a/core/consensus/grandpa/impl/grandpa_impl.hpp b/core/consensus/grandpa/impl/grandpa_impl.hpp index 7f3632209d..880cee93ec 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.hpp +++ b/core/consensus/grandpa/impl/grandpa_impl.hpp @@ -11,6 +11,7 @@ #include +#include "consensus/grandpa/historical_votes.hpp" #include "consensus/grandpa/impl/votes_cache.hpp" #include "consensus/grandpa/voting_round.hpp" #include "injector/lazy.hpp" @@ -253,6 +254,10 @@ namespace kagome::consensus::grandpa { */ void updateNextRound(RoundNumber round_number) override; + void saveHistoricalVotes(AuthoritySetId set, + RoundNumber round, + const HistoricalVotes &votes) override; + private: struct WaitingBlock { libp2p::peer::PeerId peer; @@ -319,8 +324,8 @@ namespace kagome::consensus::grandpa { void onHead(const primitives::BlockInfo &block); void pruneWaitingBlocks(); - void saveCachedVotes(); - void applyCachedVotes(VotingRound &round); + HistoricalVotes historicalVotes(AuthoritySetId set, + RoundNumber round) const; void setTimerFallback(); @@ -357,15 +362,6 @@ namespace kagome::consensus::grandpa { std::vector waiting_blocks_; - struct CachedRound { - SCALE_TIE(3); - AuthoritySetId set; - RoundNumber round; - VotingRound::Votes votes; - }; - using CachedVotes = std::vector; - CachedVotes cached_votes_; - // Metrics metrics::RegistryPtr metrics_registry_ = metrics::createRegistry(); metrics::Gauge *metric_highest_round_; diff --git a/core/consensus/grandpa/impl/voting_round_impl.cpp b/core/consensus/grandpa/impl/voting_round_impl.cpp index a5c7de22cb..3e800fda34 100644 --- a/core/consensus/grandpa/impl/voting_round_impl.cpp +++ b/core/consensus/grandpa/impl/voting_round_impl.cpp @@ -56,7 +56,7 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, std::shared_ptr env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, std::shared_ptr vote_crypto_provider, std::shared_ptr prevotes, std::shared_ptr precommits, @@ -69,7 +69,7 @@ namespace kagome::consensus::grandpa { grandpa_(grandpa), hasher_{std::move(hasher)}, env_{std::move(env)}, - save_cached_votes_{std::move(save_cached_votes)}, + historical_votes_{std::move(historical_votes)}, vote_crypto_provider_{std::move(vote_crypto_provider)}, graph_{std::move(vote_graph)}, scheduler_{std::move(scheduler)}, @@ -105,7 +105,7 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -116,7 +116,7 @@ namespace kagome::consensus::grandpa { config, std::move(hasher), env, - std::move(save_cached_votes), + std::move(historical_votes), vote_crypto_provider, prevotes, precommits, @@ -134,7 +134,7 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -145,7 +145,7 @@ namespace kagome::consensus::grandpa { config, std::move(hasher), env, - std::move(save_cached_votes), + std::move(historical_votes), vote_crypto_provider, prevotes, precommits, @@ -188,6 +188,10 @@ namespace kagome::consensus::grandpa { } } + VotingRoundImpl::~VotingRoundImpl() { + saveHistoricalVotes(); + } + bool VotingRoundImpl::hasKeypair() const { return id_.has_value(); } @@ -555,11 +559,10 @@ namespace kagome::consensus::grandpa { return; } auto &signed_prevote = signed_prevote_opt.value(); + set_prevoted_index(); if (onPrevote({}, signed_prevote, Propagation::NEEDLESS)) { update(false, true, false); - if (save_cached_votes_) { - save_cached_votes_(); - } + saveHistoricalVotes(); } env_->onVoted(round_number_, voter_set_->id(), signed_prevote); } @@ -611,11 +614,10 @@ namespace kagome::consensus::grandpa { return; } auto &signed_precommit = signed_precommit_opt.value(); + set_precommitted_index(); if (onPrecommit({}, signed_precommit, Propagation::NEEDLESS)) { update(false, false, true); - if (save_cached_votes_) { - save_cached_votes_(); - } + saveHistoricalVotes(); } env_->onVoted(round_number_, voter_set_->id(), signed_precommit); } @@ -628,6 +630,8 @@ namespace kagome::consensus::grandpa { SL_DEBUG( logger_, "Round #{}: Finalizing on block {}", round_number_, block); + saveHistoricalVotes(); + GrandpaJustification justification{ .round_number = round_number_, .block_info = block, @@ -1184,6 +1188,7 @@ namespace kagome::consensus::grandpa { result.error()); return result.as_failure(); } + historical_votes_.seen.emplace_back(vote); return outcome::success(); } case VoteTracker::PushResult::DUPLICATED: { @@ -1204,6 +1209,7 @@ namespace kagome::consensus::grandpa { std::ignore = env_->reportEquivocation(*this, equivocation); + historical_votes_.seen.emplace_back(vote); return VotingRoundError::EQUIVOCATED_VOTE; } default: @@ -1610,4 +1616,19 @@ namespace kagome::consensus::grandpa { VotingRound::Votes VotingRoundImpl::votes() const { return {prevotes_->getMessages(), precommits_->getMessages()}; } + + void VotingRoundImpl::applyHistoricalVotes() { + VotingRoundUpdate update{*this}; + for (auto &vote : historical_votes_.seen) { + update.vote(vote); + } + update.update(); + } + + void VotingRoundImpl::saveHistoricalVotes() { + if (auto grandpa = grandpa_.lock()) { + grandpa->saveHistoricalVotes( + voter_set_->id(), round_number_, historical_votes_); + } + } } // namespace kagome::consensus::grandpa diff --git a/core/consensus/grandpa/impl/voting_round_impl.hpp b/core/consensus/grandpa/impl/voting_round_impl.hpp index d7093fc084..26f34723e0 100644 --- a/core/consensus/grandpa/impl/voting_round_impl.hpp +++ b/core/consensus/grandpa/impl/voting_round_impl.hpp @@ -10,6 +10,7 @@ #include +#include "consensus/grandpa/historical_votes.hpp" #include "log/logger.hpp" namespace kagome::consensus::grandpa { @@ -32,13 +33,11 @@ namespace kagome::consensus::grandpa { VotingRoundImpl() : round_number_{}, duration_{} {} public: - using SaveCachedVotes = std::function; - VotingRoundImpl(const std::shared_ptr &grandpa, const GrandpaConfig &config, std::shared_ptr hasher, std::shared_ptr env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, std::shared_ptr vote_crypto_provider, std::shared_ptr prevotes, std::shared_ptr precommits, @@ -50,7 +49,7 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -63,7 +62,7 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - SaveCachedVotes save_cached_votes, + HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -71,6 +70,8 @@ namespace kagome::consensus::grandpa { const std::shared_ptr &scheduler, const std::shared_ptr &previous_round); + ~VotingRoundImpl() override; + enum class Stage { // Initial stage, round is just created INIT, @@ -253,6 +254,34 @@ namespace kagome::consensus::grandpa { return prevote_ghost_; } + /** + * Set the number of prevotes and precommits received at the moment + * of prevoting. It should be called inmediatly after prevoting. + */ + void set_prevoted_index() { + historical_votes_.set_prevoted_index(); + } + + /** + * Set the number of prevotes and precommits received at the moment + * of precommiting. It should be called inmediatly after precommiting. + */ + void set_precommitted_index() { + historical_votes_.set_precommitted_index(); + } + + /** + * Return all votes for the round (prevotes and precommits), sorted by + * imported order and indicating the indices where we voted. At most two + * prevotes and two precommits per voter are present, further equivocations + * are not stored (as they are redundant). + */ + auto &historical_votes() const { + return historical_votes_; + } + + void applyHistoricalVotes(); + private: /// Check if peer \param id is primary bool isPrimary(const Id &id) const; @@ -301,6 +330,8 @@ namespace kagome::consensus::grandpa { void sendPrecommit(const Precommit &precommit); void pending(); + void saveHistoricalVotes(); + std::shared_ptr voter_set_; const RoundNumber round_number_; std::shared_ptr previous_round_; @@ -314,7 +345,7 @@ namespace kagome::consensus::grandpa { std::weak_ptr grandpa_; std::shared_ptr hasher_; std::shared_ptr env_; - SaveCachedVotes save_cached_votes_; + HistoricalVotes historical_votes_; std::shared_ptr vote_crypto_provider_; std::shared_ptr graph_; std::shared_ptr scheduler_; diff --git a/core/storage/predefined_keys.hpp b/core/storage/predefined_keys.hpp index d837ef2f41..5967ec210e 100644 --- a/core/storage/predefined_keys.hpp +++ b/core/storage/predefined_keys.hpp @@ -49,7 +49,8 @@ namespace kagome::storage { inline const common::Buffer kAuthorityManagerImplIndexerPrefix = ":kagome:AuthorityManagerImpl:Indexer:"_buf; - inline const common::Buffer kGrandpaVotesKey = ":kagome:Grandpa:votes"_buf; + inline const common::Buffer kGrandpaHistoricalVotesPrefix = + ":kagome:Grandpa:historical_votes:"_buf; inline const common::Buffer kRecentDisputeLookupKey = "recent_disputes"_buf; diff --git a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp index a6648755d0..fa5d7bed07 100644 --- a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp +++ b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp @@ -27,6 +27,7 @@ using namespace kagome::consensus::grandpa; using kagome::consensus::grandpa::Authority; using kagome::consensus::grandpa::AuthoritySet; using kagome::consensus::grandpa::Equivocation; +using kagome::consensus::grandpa::HistoricalVotes; using kagome::crypto::Ed25519Keypair; using kagome::crypto::Ed25519Signature; using kagome::crypto::HasherMock; @@ -124,6 +125,9 @@ class VotingRoundTest : public testing::Test, .id = kAlice}; env_ = std::make_shared(); + EXPECT_CALL(*env_, getAncestry("C"_H, "EA"_H)) + .WillRepeatedly( + Return(std::vector{"EA"_H, "E"_H, "D"_H, "C"_H})); EXPECT_CALL(*env_, getAncestry("C"_H, "FC"_H)) .WillRepeatedly(Return(std::vector{ "FC"_H, "FB"_H, "FA"_H, "F"_H, "E"_H, "D"_H, "C"_H})); @@ -168,7 +172,7 @@ class VotingRoundTest : public testing::Test, config, hasher_, env_, - nullptr, + HistoricalVotes{}, vote_crypto_provider_, prevotes_, precommits_, @@ -362,6 +366,26 @@ TEST_F(VotingRoundTest, EquivocateDoesNotDoubleCount) { ASSERT_EQ(round_->prevoteGhost(), (BlockInfo{7, "FA"_H})); } +TEST_F(VotingRoundTest, HistoricalVotesWorks) { + auto alice1 = preparePrevote(kAlice, kAliceSignature, Prevote{9, "FC"_H}); + auto alice2 = preparePrevote(kAlice, kAliceSignature, Prevote{9, "EC"_H}); + auto bob1 = preparePrevote(kBob, kBobSignature, Prevote{7, "EA"_H}); + auto bob2 = preparePrecommit(kBob, kBobSignature, Precommit{7, "EA"_H}); + + EXPECT_CALL(*env_, reportEquivocation(_, _)) + .WillOnce(Return(outcome::success())); + round_->onPrevote({}, alice1, Propagation::NEEDLESS); + round_->set_prevoted_index(); + round_->onPrevote({}, bob1, Propagation::NEEDLESS); + round_->onPrecommit({}, bob2, Propagation::NEEDLESS); + round_->onPrevote({}, alice2, Propagation::NEEDLESS); + round_->set_precommitted_index(); + round_->update(false, true, true); + + HistoricalVotes expected{{alice1, bob1, bob2, alice2}, 1, 4}; + EXPECT_EQ(round_->historical_votes(), expected); +} + /** * @given Network of: * Alice with weight 4, diff --git a/test/mock/core/consensus/grandpa/grandpa_mock.hpp b/test/mock/core/consensus/grandpa/grandpa_mock.hpp index 10f507aeca..7cfc9777b1 100644 --- a/test/mock/core/consensus/grandpa/grandpa_mock.hpp +++ b/test/mock/core/consensus/grandpa/grandpa_mock.hpp @@ -69,6 +69,11 @@ namespace kagome::consensus::grandpa { (override)); MOCK_METHOD(void, updateNextRound, (RoundNumber round_number), (override)); + + MOCK_METHOD(void, + saveHistoricalVotes, + (AuthoritySetId, RoundNumber, const HistoricalVotes &), + (override)); }; } // namespace kagome::consensus::grandpa From 95653c8f3cc822f17985d080543643b96ae31e56 Mon Sep 17 00:00:00 2001 From: turuslan Date: Sat, 11 May 2024 08:14:59 +0500 Subject: [PATCH 2/4] pr comment Signed-off-by: turuslan --- core/consensus/grandpa/impl/grandpa_impl.cpp | 5 +++++ .../consensus/grandpa/voting_round/voting_round_test.cpp | 1 + 2 files changed, 6 insertions(+) diff --git a/core/consensus/grandpa/impl/grandpa_impl.cpp b/core/consensus/grandpa/impl/grandpa_impl.cpp index f92cae4080..5cb6605dde 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.cpp +++ b/core/consensus/grandpa/impl/grandpa_impl.cpp @@ -1430,6 +1430,11 @@ namespace kagome::consensus::grandpa { if (auto r = db_->get(historicalVotesKey(set, round))) { if (auto r2 = scale::decode(r.value())) { return std::move(r2.value()); + } else { + SL_ERROR(logger_, + "historicalVotes(set={}, round={}): decode error", + set, + round); } } return {}; diff --git a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp index fa5d7bed07..fb1ba91e5b 100644 --- a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp +++ b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp @@ -366,6 +366,7 @@ TEST_F(VotingRoundTest, EquivocateDoesNotDoubleCount) { ASSERT_EQ(round_->prevoteGhost(), (BlockInfo{7, "FA"_H})); } +// https://github.com/paritytech/finality-grandpa/blob/8c45a664c05657f0c71057158d3ba555ba7d20de/src/round.rs#L844 TEST_F(VotingRoundTest, HistoricalVotesWorks) { auto alice1 = preparePrevote(kAlice, kAliceSignature, Prevote{9, "FC"_H}); auto alice2 = preparePrevote(kAlice, kAliceSignature, Prevote{9, "EC"_H}); From 15a4c8b2994ca11643c917ebdb38902c24c62b83 Mon Sep 17 00:00:00 2001 From: turuslan Date: Tue, 28 May 2024 15:48:10 +0500 Subject: [PATCH 3/4] rewrite historical votes implementation Signed-off-by: turuslan --- core/consensus/grandpa/historical_votes.hpp | 7 +- core/consensus/grandpa/impl/grandpa_impl.cpp | 97 ++++++++++++++----- core/consensus/grandpa/impl/grandpa_impl.hpp | 23 +++-- .../grandpa/impl/voting_round_impl.cpp | 41 ++------ .../grandpa/impl/voting_round_impl.hpp | 37 ------- core/utils/lru.hpp | 10 +- .../voting_round/voting_round_test.cpp | 18 ++-- .../core/consensus/grandpa/grandpa_mock.hpp | 4 +- 8 files changed, 127 insertions(+), 110 deletions(-) diff --git a/core/consensus/grandpa/historical_votes.hpp b/core/consensus/grandpa/historical_votes.hpp index 6f15304c20..970006d964 100644 --- a/core/consensus/grandpa/historical_votes.hpp +++ b/core/consensus/grandpa/historical_votes.hpp @@ -38,8 +38,9 @@ namespace kagome::consensus::grandpa { public: virtual ~SaveHistoricalVotes() = default; - virtual void saveHistoricalVotes(AuthoritySetId set, - RoundNumber round, - const HistoricalVotes &votes) = 0; + virtual void saveHistoricalVote(AuthoritySetId set, + RoundNumber round, + const SignedMessage &vote, + bool set_index) = 0; }; } // namespace kagome::consensus::grandpa diff --git a/core/consensus/grandpa/impl/grandpa_impl.cpp b/core/consensus/grandpa/impl/grandpa_impl.cpp index 9c34b4ecbe..20df907b2a 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.cpp +++ b/core/consensus/grandpa/impl/grandpa_impl.cpp @@ -217,14 +217,13 @@ namespace kagome::consensus::grandpa { std::move(config), hasher_, environment_, - historicalVotes(authority_set.id, round_state.round_number), std::move(vote_crypto_provider), std::make_shared(), // Prevote tracker std::make_shared(), // Precommit tracker std::move(vote_graph), scheduler_, round_state); - new_round->applyHistoricalVotes(); + applyHistoricalVotes(*new_round); new_round->end(); // it is okay, because we do not want to actually execute // this round @@ -278,14 +277,13 @@ namespace kagome::consensus::grandpa { std::move(config), hasher_, environment_, - historicalVotes(authority_set->id, new_round_number), std::move(vote_crypto_provider), std::make_shared(), // Prevote tracker std::make_shared(), // Precommit tracker std::move(vote_graph), scheduler_, round); - new_round->applyHistoricalVotes(); + applyHistoricalVotes(*new_round); return new_round; } @@ -1183,7 +1181,6 @@ namespace kagome::consensus::grandpa { GrandpaConfig{voters, justification.round_number, {}, {}}, hasher_, environment_, - HistoricalVotes{}, std::make_shared( nullptr, crypto_provider_, justification.round_number, voters), std::make_shared(), @@ -1415,29 +1412,85 @@ namespace kagome::consensus::grandpa { retain_if(waiting_blocks_, f); } - void GrandpaImpl::saveHistoricalVotes(AuthoritySetId set, - RoundNumber round, - const HistoricalVotes &votes) { - if (votes.seen.empty()) { + void GrandpaImpl::saveHistoricalVote(AuthoritySetId set, + RoundNumber round, + const SignedMessage &vote, + bool set_index) { + REINVOKE(*grandpa_pool_handler_, + saveHistoricalVote, + set, + round, + vote, + set_index); + auto &[votes, dirty] = historicalVotes(set, round); + if (std::find(votes.seen.begin(), votes.seen.end(), vote) + != votes.seen.end()) { + return; + } + if (set_index) { + auto *index = vote.is() ? &votes.prevote_idx + : vote.is() ? &votes.precommit_idx + : nullptr; + if (index and not *index) { + *index = votes.seen.size(); + } + } + votes.seen.emplace_back(vote); + dirty = true; + if (writing_historical_votes_) { return; } - std::ignore = - db_->put(historicalVotesKey(set, round), scale::encode(votes).value()); + writing_historical_votes_ = true; + grandpa_pool_handler_->execute([weak_self{weak_from_this()}] { + auto self = weak_self.lock(); + if (not self) { + return; + } + self->writeHistoricalVotes(); + }); } - HistoricalVotes GrandpaImpl::historicalVotes(AuthoritySetId set, - RoundNumber round) const { - if (auto r = db_->get(historicalVotesKey(set, round))) { - if (auto r2 = scale::decode(r.value())) { - return std::move(r2.value()); - } else { - SL_ERROR(logger_, - "historicalVotes(set={}, round={}): decode error", - set, - round); + void GrandpaImpl::writeHistoricalVotes() { + writing_historical_votes_ = false; + historical_votes_.forEach( + [&](const HistoricalVotesKey &key, HistoricalVotesDirty &cache) { + if (not cache.second) { + return; + } + cache.second = false; + std::ignore = db_->put(historicalVotesKey(key.first, key.second), + scale::encode(cache.first).value()); + }); + } + + GrandpaImpl::HistoricalVotesDirty &GrandpaImpl::historicalVotes( + AuthoritySetId set, RoundNumber round) { + auto key = std::make_pair(set, round); + auto cache = historical_votes_.get(key); + if (not cache) { + cache = historical_votes_.put(key, {{}, false}); + if (auto r = db_->get(historicalVotesKey(set, round))) { + if (auto r2 = scale::decode(r.value())) { + cache->get().first = std::move(r2.value()); + } else { + SL_ERROR(logger_, + "historicalVotes(set={}, round={}): decode error", + set, + round); + } } } - return {}; + return cache->get(); + } + + void GrandpaImpl::applyHistoricalVotes(VotingRound &round) { + auto &votes = + historicalVotes(round.voterSetId(), round.roundNumber()).first; + VotingRoundUpdate update{round}; + for (auto &vote : votes.seen) { + update.vote(vote); + } + update.update(); } void GrandpaImpl::setTimerFallback() { diff --git a/core/consensus/grandpa/impl/grandpa_impl.hpp b/core/consensus/grandpa/impl/grandpa_impl.hpp index 880cee93ec..2ea439d46b 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.hpp +++ b/core/consensus/grandpa/impl/grandpa_impl.hpp @@ -19,7 +19,7 @@ #include "metrics/metrics.hpp" #include "primitives/event_types.hpp" #include "storage/spaced_storage.hpp" -#include "utils/safe_object.hpp" +#include "utils/lru.hpp" namespace kagome { class PoolHandler; @@ -254,9 +254,10 @@ namespace kagome::consensus::grandpa { */ void updateNextRound(RoundNumber round_number) override; - void saveHistoricalVotes(AuthoritySetId set, - RoundNumber round, - const HistoricalVotes &votes) override; + void saveHistoricalVote(AuthoritySetId set, + RoundNumber round, + const SignedMessage &vote, + bool set_index) override; private: struct WaitingBlock { @@ -324,8 +325,11 @@ namespace kagome::consensus::grandpa { void onHead(const primitives::BlockInfo &block); void pruneWaitingBlocks(); - HistoricalVotes historicalVotes(AuthoritySetId set, - RoundNumber round) const; + void writeHistoricalVotes(); + using HistoricalVotesDirty = std::pair; + HistoricalVotesDirty &historicalVotes(AuthoritySetId set, + RoundNumber round); + void applyHistoricalVotes(VotingRound &round); void setTimerFallback(); @@ -362,6 +366,13 @@ namespace kagome::consensus::grandpa { std::vector waiting_blocks_; + using HistoricalVotesKey = std::pair; + Lru> + historical_votes_{5}; + bool writing_historical_votes_ = false; + // Metrics metrics::RegistryPtr metrics_registry_ = metrics::createRegistry(); metrics::Gauge *metric_highest_round_; diff --git a/core/consensus/grandpa/impl/voting_round_impl.cpp b/core/consensus/grandpa/impl/voting_round_impl.cpp index fc45e3bb1d..9344a67102 100644 --- a/core/consensus/grandpa/impl/voting_round_impl.cpp +++ b/core/consensus/grandpa/impl/voting_round_impl.cpp @@ -14,6 +14,7 @@ #include "consensus/grandpa/grandpa.hpp" #include "consensus/grandpa/grandpa_config.hpp" #include "consensus/grandpa/grandpa_context.hpp" +#include "consensus/grandpa/historical_votes.hpp" #include "consensus/grandpa/vote_crypto_provider.hpp" #include "consensus/grandpa/vote_graph.hpp" #include "consensus/grandpa/vote_tracker.hpp" @@ -56,7 +57,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, std::shared_ptr env, - HistoricalVotes historical_votes, std::shared_ptr vote_crypto_provider, std::shared_ptr prevotes, std::shared_ptr precommits, @@ -69,7 +69,6 @@ namespace kagome::consensus::grandpa { grandpa_(grandpa), hasher_{std::move(hasher)}, env_{std::move(env)}, - historical_votes_{std::move(historical_votes)}, vote_crypto_provider_{std::move(vote_crypto_provider)}, graph_{std::move(vote_graph)}, scheduler_{std::move(scheduler)}, @@ -105,7 +104,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -116,7 +114,6 @@ namespace kagome::consensus::grandpa { config, std::move(hasher), env, - std::move(historical_votes), vote_crypto_provider, prevotes, precommits, @@ -134,7 +131,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -145,7 +141,6 @@ namespace kagome::consensus::grandpa { config, std::move(hasher), env, - std::move(historical_votes), vote_crypto_provider, prevotes, precommits, @@ -188,10 +183,6 @@ namespace kagome::consensus::grandpa { } } - VotingRoundImpl::~VotingRoundImpl() { - saveHistoricalVotes(); - } - bool VotingRoundImpl::hasKeypair() const { return id_.has_value(); } @@ -559,10 +550,8 @@ namespace kagome::consensus::grandpa { return; } auto &signed_prevote = signed_prevote_opt.value(); - set_prevoted_index(); if (onPrevote({}, signed_prevote, Propagation::NEEDLESS)) { update(false, true, false); - saveHistoricalVotes(); } env_->onVoted(round_number_, voter_set_->id(), signed_prevote); } @@ -614,10 +603,8 @@ namespace kagome::consensus::grandpa { return; } auto &signed_precommit = signed_precommit_opt.value(); - set_precommitted_index(); if (onPrecommit({}, signed_precommit, Propagation::NEEDLESS)) { update(false, false, true); - saveHistoricalVotes(); } env_->onVoted(round_number_, voter_set_->id(), signed_precommit); } @@ -630,8 +617,6 @@ namespace kagome::consensus::grandpa { SL_DEBUG( logger_, "Round #{}: Finalizing on block {}", round_number_, block); - saveHistoricalVotes(); - GrandpaJustification justification{ .round_number = round_number_, .block_info = block, @@ -1123,6 +1108,12 @@ namespace kagome::consensus::grandpa { template outcome::result VotingRoundImpl::onSigned( OptRef grandpa_context, const SignedMessage &vote) { + auto save_historical_vote = [&] { + if (auto grandpa = grandpa_.lock()) { + grandpa->saveHistoricalVote( + voter_set_->id(), round_number_, vote, vote.id == id_); + } + }; BOOST_ASSERT(vote.is()); // Check if a voter is contained in a current voter set @@ -1186,7 +1177,7 @@ namespace kagome::consensus::grandpa { result.error()); return result.as_failure(); } - historical_votes_.seen.emplace_back(vote); + save_historical_vote(); return outcome::success(); } case VoteTracker::PushResult::DUPLICATED: { @@ -1207,7 +1198,7 @@ namespace kagome::consensus::grandpa { std::ignore = env_->reportEquivocation(*this, equivocation); - historical_votes_.seen.emplace_back(vote); + save_historical_vote(); return VotingRoundError::EQUIVOCATED_VOTE; } default: @@ -1615,18 +1606,4 @@ namespace kagome::consensus::grandpa { return {prevotes_->getMessages(), precommits_->getMessages()}; } - void VotingRoundImpl::applyHistoricalVotes() { - VotingRoundUpdate update{*this}; - for (auto &vote : historical_votes_.seen) { - update.vote(vote); - } - update.update(); - } - - void VotingRoundImpl::saveHistoricalVotes() { - if (auto grandpa = grandpa_.lock()) { - grandpa->saveHistoricalVotes( - voter_set_->id(), round_number_, historical_votes_); - } - } } // namespace kagome::consensus::grandpa diff --git a/core/consensus/grandpa/impl/voting_round_impl.hpp b/core/consensus/grandpa/impl/voting_round_impl.hpp index 26f34723e0..1f2fde063e 100644 --- a/core/consensus/grandpa/impl/voting_round_impl.hpp +++ b/core/consensus/grandpa/impl/voting_round_impl.hpp @@ -10,7 +10,6 @@ #include -#include "consensus/grandpa/historical_votes.hpp" #include "log/logger.hpp" namespace kagome::consensus::grandpa { @@ -37,7 +36,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, std::shared_ptr env, - HistoricalVotes historical_votes, std::shared_ptr vote_crypto_provider, std::shared_ptr prevotes, std::shared_ptr precommits, @@ -49,7 +47,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -62,7 +59,6 @@ namespace kagome::consensus::grandpa { const GrandpaConfig &config, std::shared_ptr hasher, const std::shared_ptr &env, - HistoricalVotes historical_votes, const std::shared_ptr &vote_crypto_provider, const std::shared_ptr &prevotes, const std::shared_ptr &precommits, @@ -70,8 +66,6 @@ namespace kagome::consensus::grandpa { const std::shared_ptr &scheduler, const std::shared_ptr &previous_round); - ~VotingRoundImpl() override; - enum class Stage { // Initial stage, round is just created INIT, @@ -254,34 +248,6 @@ namespace kagome::consensus::grandpa { return prevote_ghost_; } - /** - * Set the number of prevotes and precommits received at the moment - * of prevoting. It should be called inmediatly after prevoting. - */ - void set_prevoted_index() { - historical_votes_.set_prevoted_index(); - } - - /** - * Set the number of prevotes and precommits received at the moment - * of precommiting. It should be called inmediatly after precommiting. - */ - void set_precommitted_index() { - historical_votes_.set_precommitted_index(); - } - - /** - * Return all votes for the round (prevotes and precommits), sorted by - * imported order and indicating the indices where we voted. At most two - * prevotes and two precommits per voter are present, further equivocations - * are not stored (as they are redundant). - */ - auto &historical_votes() const { - return historical_votes_; - } - - void applyHistoricalVotes(); - private: /// Check if peer \param id is primary bool isPrimary(const Id &id) const; @@ -330,8 +296,6 @@ namespace kagome::consensus::grandpa { void sendPrecommit(const Precommit &precommit); void pending(); - void saveHistoricalVotes(); - std::shared_ptr voter_set_; const RoundNumber round_number_; std::shared_ptr previous_round_; @@ -345,7 +309,6 @@ namespace kagome::consensus::grandpa { std::weak_ptr grandpa_; std::shared_ptr hasher_; std::shared_ptr env_; - HistoricalVotes historical_votes_; std::shared_ptr vote_crypto_provider_; std::shared_ptr graph_; std::shared_ptr scheduler_; diff --git a/core/utils/lru.hpp b/core/utils/lru.hpp index bb6c82a058..db5a4cae0c 100644 --- a/core/utils/lru.hpp +++ b/core/utils/lru.hpp @@ -13,12 +13,12 @@ namespace kagome { /** * `std::unordered_map` with LRU. */ - template + template > class Lru { public: struct Item; // TODO(turuslan): remove unique_ptr after deprecating gcc 10 - using Map = std::unordered_map>; + using Map = std::unordered_map, H>; using It = typename Map::iterator; struct Item { V v; @@ -64,6 +64,12 @@ namespace kagome { map_.erase(it); } + void forEach(const auto &f) { + for (auto &p : map_) { + f(p.first, p.second->v); + } + } + template void erase_if(const F &f) { for (auto it = map_.begin(); it != map_.end();) { diff --git a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp index fb1ba91e5b..c2ecf91e1a 100644 --- a/test/core/consensus/grandpa/voting_round/voting_round_test.cpp +++ b/test/core/consensus/grandpa/voting_round/voting_round_test.cpp @@ -172,7 +172,6 @@ class VotingRoundTest : public testing::Test, config, hasher_, env_, - HistoricalVotes{}, vote_crypto_provider_, prevotes_, precommits_, @@ -375,16 +374,23 @@ TEST_F(VotingRoundTest, HistoricalVotesWorks) { EXPECT_CALL(*env_, reportEquivocation(_, _)) .WillOnce(Return(outcome::success())); + EXPECT_CALL(*grandpa_, + saveHistoricalVote( + round_->voterSetId(), round_->roundNumber(), alice1, true)); round_->onPrevote({}, alice1, Propagation::NEEDLESS); - round_->set_prevoted_index(); + EXPECT_CALL(*grandpa_, + saveHistoricalVote( + round_->voterSetId(), round_->roundNumber(), bob1, false)); round_->onPrevote({}, bob1, Propagation::NEEDLESS); + EXPECT_CALL(*grandpa_, + saveHistoricalVote( + round_->voterSetId(), round_->roundNumber(), bob2, false)); round_->onPrecommit({}, bob2, Propagation::NEEDLESS); + EXPECT_CALL(*grandpa_, + saveHistoricalVote( + round_->voterSetId(), round_->roundNumber(), alice2, true)); round_->onPrevote({}, alice2, Propagation::NEEDLESS); - round_->set_precommitted_index(); round_->update(false, true, true); - - HistoricalVotes expected{{alice1, bob1, bob2, alice2}, 1, 4}; - EXPECT_EQ(round_->historical_votes(), expected); } /** diff --git a/test/mock/core/consensus/grandpa/grandpa_mock.hpp b/test/mock/core/consensus/grandpa/grandpa_mock.hpp index 7cfc9777b1..d981b5fee8 100644 --- a/test/mock/core/consensus/grandpa/grandpa_mock.hpp +++ b/test/mock/core/consensus/grandpa/grandpa_mock.hpp @@ -71,8 +71,8 @@ namespace kagome::consensus::grandpa { MOCK_METHOD(void, updateNextRound, (RoundNumber round_number), (override)); MOCK_METHOD(void, - saveHistoricalVotes, - (AuthoritySetId, RoundNumber, const HistoricalVotes &), + saveHistoricalVote, + (AuthoritySetId, RoundNumber, const SignedMessage &, bool), (override)); }; From 97d75deaba9fba6e9480eef07055021ea0162ee0 Mon Sep 17 00:00:00 2001 From: turuslan Date: Thu, 30 May 2024 16:58:52 +0500 Subject: [PATCH 4/4] remove unused, comments Signed-off-by: turuslan --- core/consensus/grandpa/historical_votes.hpp | 17 +--- core/consensus/grandpa/impl/grandpa_impl.hpp | 97 ++------------------ 2 files changed, 9 insertions(+), 105 deletions(-) diff --git a/core/consensus/grandpa/historical_votes.hpp b/core/consensus/grandpa/historical_votes.hpp index 970006d964..1f535ae6b9 100644 --- a/core/consensus/grandpa/historical_votes.hpp +++ b/core/consensus/grandpa/historical_votes.hpp @@ -18,26 +18,15 @@ namespace kagome::consensus::grandpa { std::vector seen; std::optional prevote_idx, precommit_idx; - - /** - * Set the number of messages seen before prevoting. - */ - void set_prevoted_index() { - prevote_idx = seen.size(); - } - - /** - * Set the number of messages seen before precommiting. - */ - void set_precommitted_index() { - precommit_idx = seen.size(); - } }; class SaveHistoricalVotes { public: virtual ~SaveHistoricalVotes() = default; + /** + * Called from `VotingRoundImpl` to `GrandpaImpl` to save historical vote. + */ virtual void saveHistoricalVote(AuthoritySetId set, RoundNumber round, const SignedMessage &vote, diff --git a/core/consensus/grandpa/impl/grandpa_impl.hpp b/core/consensus/grandpa/impl/grandpa_impl.hpp index 2ea439d46b..4e4815c18f 100644 --- a/core/consensus/grandpa/impl/grandpa_impl.hpp +++ b/core/consensus/grandpa/impl/grandpa_impl.hpp @@ -136,124 +136,39 @@ namespace kagome::consensus::grandpa { */ void stop(); - /** - * Processes grandpa neighbour message - * Neighbour message is ignored if voter set ids between our and receiving - * peer do not match. Otherwsie if our peer is behind by grandpa rounds by - * more than GrandpaImpl::kCatchUpThreshold, then catch request is sent to - * the peer that sent us a message (see GrandpaImpl::onCatchUpRequest()). - * - * Otherwise our peer will send back the response containing known votes for - * the round in msg (if any) - * @param peer_id id of the peer that sent the message - * @param msg received grandpa neighbour message - */ + // NeighborObserver void onNeighborMessage(const libp2p::peer::PeerId &peer_id, std::optional &&info_opt, network::GrandpaNeighborMessage &&msg) override; - // Catch-up methods - - /** - * Catch up request processing according to - * [spec](https://w3f.github.io/polkadot-spec/develop/sect-block-finalization.html#algo-process-catchup-request) - * - * We check voter set ids between ours and remote peer match. Then we check - * politeness of request and send response containing state for requested - * round - * @param peer_id id of the peer that sent catch up request - * @param msg network message containing catch up request - */ + // CatchUpObserver void onCatchUpRequest(const libp2p::peer::PeerId &peer_id, std::optional &&info, network::CatchUpRequest &&msg) override; - - /** - * Catch up response processing according to - * [spec](https://w3f.github.io/polkadot-spec/develop/sect-block-finalization.html#algo-process-catchup-response) - * - * Response is ignored if remote peer's voter set id does not match ours - * peer id Response is ignored if message contains info about round in the - * past (earlier than our current round) If message is received from the - * future round we create round from the round state information in - * response, check round for completeness and execute the round following - * the round from response - * @param peer_id id of remote peer that sent catch up response - * @param msg message containing catch up response - */ void onCatchUpResponse(const libp2p::peer::PeerId &peer_id, network::CatchUpResponse &&msg) override; - // Voting methods - - /** - * Processing of vote messages - * - * Vote messages are ignored if they are not sent politely - * Otherwise, we check if we have the round that corresponds to the received - * message and process it by this round - * @param peer_id id of remote peer - * @param msg vote message that could be either primary propose, prevote, or - * precommit message - */ + // RoundObserver void onVoteMessage(const libp2p::peer::PeerId &peer_id, std::optional &&info_opt, network::VoteMessage &&msg) override; - - /** - * Processing of commit message - * - * We check commit message for politeness and send it to - * GrandpaImpl::applyJustification() - * - * @param peer_id id of remote peer - * @param msg message containing commit message with justification - */ void onCommitMessage(const libp2p::peer::PeerId &peer_id, network::FullCommitMessage &&msg) override; - /** - * Check justification votes signatures, ancestry and threshold. - */ + // JustificationObserver outcome::result verifyJustification( const GrandpaJustification &justification, const AuthoritySet &authorities) override; - - /** - * Selects round that corresponds for justification, checks justification, - * finalizes corresponding block and stores justification in storage - * - * If there is no corresponding round, it will be created - * @param justification justification containing precommit votes and - * signatures for block info - * @return nothing or an error - */ void applyJustification(const GrandpaJustification &justification, ApplyJustificationCb &&callback) override; - void reload() override; - // Round processing method - - /** - * Creates and executes round that follows the round with provided - * round_number - * - * Also this method removes old round, so that only 3 rounds in total are - * stored at any moment - * @param round previous round from which new one is created and executed - */ + // Grandpa void tryExecuteNextRound( const std::shared_ptr &round) override; - - /** - * Selects round next to provided one and updates it by checking if - * prevote_ghost, estimate and finalized block were updated. - * @see VotingRound::update() - * @param round_number the round after which we select the round for update - */ void updateNextRound(RoundNumber round_number) override; + // SaveHistoricalVotes void saveHistoricalVote(AuthoritySetId set, RoundNumber round, const SignedMessage &vote,