8 #include <unordered_set> 20 auto convertToPrimaryPropose(T &&vote) {
25 auto convertToPrevote(T &&vote) {
26 return Prevote(vote.number, vote.hash);
30 auto convertToPrecommit(T &&vote) {
35 auto convertToBlockInfo(T &&vote) {
40 auto toMilliseconds(D &&duration) {
41 return std::chrono::duration_cast<std::chrono::milliseconds>(duration);
46 const std::shared_ptr<Grandpa> &grandpa,
48 std::shared_ptr<authority::AuthorityManager> authority_manager,
49 std::shared_ptr<Environment>
env,
50 std::shared_ptr<VoteCryptoProvider> vote_crypto_provider,
51 std::shared_ptr<VoteTracker> prevotes,
52 std::shared_ptr<VoteTracker> precommits,
53 std::shared_ptr<VoteGraph> vote_graph,
54 std::shared_ptr<Clock> clock,
55 std::shared_ptr<libp2p::basic::Scheduler> scheduler)
56 : voter_set_{std::move(config.
voters)},
64 graph_{std::move(vote_graph)},
68 BOOST_ASSERT(not
grandpa_.expired());
73 BOOST_ASSERT(
env_ !=
nullptr);
74 BOOST_ASSERT(
graph_ !=
nullptr);
78 auto faulty = (
voter_set_->totalWeight() - 1) / 3;
89 "Round #{}: Created with voter set #{}",
95 const std::shared_ptr<Grandpa> &grandpa,
97 const std::shared_ptr<authority::AuthorityManager> authority_manager,
98 const std::shared_ptr<Environment> &
env,
99 const std::shared_ptr<VoteCryptoProvider> &vote_crypto_provider,
100 const std::shared_ptr<VoteTracker> &prevotes,
101 const std::shared_ptr<VoteTracker> &precommits,
102 const std::shared_ptr<VoteGraph> &vote_graph,
103 const std::shared_ptr<Clock> &clock,
104 const std::shared_ptr<libp2p::basic::Scheduler> &scheduler,
105 const std::shared_ptr<VotingRound> &previous_round)
110 vote_crypto_provider,
116 BOOST_ASSERT(previous_round !=
nullptr);
120 previous_round->lastFinalizedBlock());
124 const std::shared_ptr<Grandpa> &grandpa,
126 const std::shared_ptr<authority::AuthorityManager> authority_manager,
127 const std::shared_ptr<Environment> &
env,
128 const std::shared_ptr<VoteCryptoProvider> &vote_crypto_provider,
129 const std::shared_ptr<VoteTracker> &prevotes,
130 const std::shared_ptr<VoteTracker> &precommits,
131 const std::shared_ptr<VoteGraph> &vote_graph,
132 const std::shared_ptr<Clock> &clock,
133 const std::shared_ptr<libp2p::basic::Scheduler> &scheduler,
139 vote_crypto_provider,
148 bool is_prevotes_changed =
false;
149 bool is_precommits_changed =
false;
152 auto apply = [&](
const auto &vote) {
156 if (VotingRoundImpl::onPrevote(vote, Propagation::NEEDLESS)) {
157 is_prevotes_changed = true;
162 is_precommits_changed =
true;
168 for (
auto &vote_variant : round_state.
votes) {
178 if (is_prevotes_changed or is_precommits_changed) {
190 auto possible_to_finalize = [&](
const VoteWeight &weight) {
199 std::move(possible_to_finalize));
278 "Round #{}: Time of prevote stage is out",
343 "Round #{}: Time of precommit stage is out",
397 const bool is_ready_to_end =
404 if (is_ready_to_end) {
406 "Round #{}: Conditions for final stage are satisfied already",
416 const bool is_ready_to_end =
423 if (is_ready_to_end) {
425 "Round #{}: Conditions for final stage are met",
476 "Primary proposal must be once for a round");
480 sendProposal(convertToPrimaryPropose(primary_vote_.value()));
485 "Round #{}: Sending primary proposal of block {}",
489 auto signed_primary_proposal_opt =
492 if (not signed_primary_proposal_opt.has_value()) {
494 "Round #{}: Primary proposal was not sent: Can't sign message",
497 const auto &signed_primary_proposal = signed_primary_proposal_opt.value();
502 logger_->error(
"Round #{}: Primary proposal was not sent: {}",
504 res.error().message());
521 const auto best_final_candicate = previous_round_->bestFinalCandidate();
524 const auto best_chain =
525 env_->bestChainContaining(best_final_candicate.hash,
voter_set_->id());
526 const auto best_prevote_candidate =
527 best_chain.has_value() ? convertToBlockInfo(best_chain.value())
537 if (best_prevote_candidate.number >= primary.number
538 and primary.number > best_final_candicate.number) {
551 "Round #{}: Sending prevote for block {}",
556 if (not signed_prevote_opt.has_value()) {
557 logger_->error(
"Round #{}: Prevote was not sent: Can't sign message",
561 auto &signed_prevote = signed_prevote_opt.value();
565 logger_->error(
"Round #{}: Prevote was not sent: {}",
567 res.error().message());
586 const auto &prevote_ghost =
589 auto last_round_estimate = previous_round_->bestFinalCandidate();
593 const bool should_precommit =
594 prevote_ghost == last_round_estimate
595 or
env_->isEqualOrDescendOf(last_round_estimate.hash,
598 if (should_precommit) {
608 "Round #{}: Sending precommit for block {}",
613 if (not signed_precommit_opt.has_value()) {
614 logger_->error(
"Round #{}: Precommit was not sent: Can't sign message",
618 auto &signed_precommit = signed_precommit_opt.value();
622 logger_->error(
"Round #{}: Precommit was not sent: {}",
624 res.error().message());
642 if (res.has_error()) {
644 "Round #{}: Finalizing on block {} is failed: {}",
647 res.error().message());
663 "Round #{}: Sending commit message for block {}",
667 auto committed =
env_->onCommitted(
670 logger_->error(
"Round #{}: Commit message was not sent: {}",
672 committed.error().message());
679 return voter_set_->voterId(index) == outcome::success(
id);
688 "Round #{}: Finalisation of round is received for block {}",
692 bool is_prevotes_changed =
false;
693 bool is_precommits_changed =
false;
695 for (
auto &vote : justification.
items) {
699 if (VotingRoundImpl::onPrevote(vote, Propagation::NEEDLESS)) {
700 is_prevotes_changed = true;
705 is_precommits_changed =
true;
711 if (is_prevotes_changed or is_precommits_changed) {
719 auto possible_to_finalize = [&](
const VoteWeight &weight) {
734 if (not
env_->isEqualOrDescendOf(block_info.hash,
742 return finalized.as_failure();
747 return outcome::success();
752 size_t total_weight = 0;
755 std::unordered_map<Id, BlockHash> validators;
756 std::unordered_set<Id> equivocators;
758 for (
const auto &signed_precommit : justification.
items) {
760 if (
auto index =
voter_set_->voterIndex(signed_precommit.id);
771 "Round #{}: Precommit signed by {} was rejected: invalid signature",
773 signed_precommit.id);
781 if (
auto [it, success] = validators.emplace(
782 signed_precommit.id, signed_precommit.getBlockHash());
785 auto weight_opt =
voter_set_->voterWeight(signed_precommit.id);
787 SL_DEBUG(
logger_,
"Voter {} is not in the current voter set", signed_precommit.id.toHex());
790 if (
env_->hasAncestry(vote.
hash, signed_precommit.getBlockHash())) {
791 total_weight += weight_opt.value();
794 "Vote does not have ancestry with target block: " 797 signed_precommit.getBlockHash());
800 }
else if (equivocators.emplace(signed_precommit.id).second) {
802 if (
env_->hasAncestry(vote.
hash, it->second)) {
803 auto weight =
voter_set_->voterWeight(signed_precommit.id).value();
804 total_weight -= weight;
808 "Vote does not have ancestry with target block: " 811 signed_precommit.getBlockHash());
818 "Round #{}: Received third precommit of caught equivocator from {}",
820 signed_precommit.id);
825 if (total_weight < threshold) {
827 "Round #{}: Received justification does not have super-majority: " 828 "total_weight={} < threshold={}",
835 return outcome::success();
853 "Round #{}: Round not finalized yet: not completable",
857 "Round #{}: Round not finalized yet: not finalizable",
861 "Round #{}: Round not finalized yet: unknown reason",
878 "Round #{}: Proposal signed by {} was rejected: " 879 "voter is not primary",
886 const auto &ctx = ctx_opt.value();
887 ctx->checked_signature_counter++;
893 "Round #{}: Proposal signed by {} was rejected: " 899 const auto &ctx = ctx_opt.value();
900 ctx->invalid_signature_counter++;
909 const auto &ctx = ctx_opt.value();
910 ctx->unknown_voter_counter++;
915 "Round #{}: Proposal signed by {} was accepted for block {}",
925 if (res.has_value() and not res.value()) {
927 auto ctx = ctx_opt.value();
939 logger_->error(
"Round #{}: Primary proposal was not propagated: {}",
941 res.error().message());
949 const auto &ctx = ctx_opt.value();
950 ctx->checked_signature_counter++;
956 "Round #{}: Prevote signed by {} was rejected: invalid signature",
961 const auto &ctx = ctx_opt.value();
962 ctx->invalid_signature_counter++;
968 if (
auto result = onSigned<Prevote>(prevote); result.has_failure()) {
978 const auto &ctx = ctx_opt.value();
979 ctx->unknown_voter_counter++;
983 logger_->warn(
"Round #{}: Prevote signed by {} was rejected: {}",
986 result.error().message());
992 "Round #{}: Prevote signed by {} was accepted for block {}",
997 if (
id_ == prevote.
id) {
1008 logger_->error(
"Round #{}: Prevote was not propagated: {}",
1010 res.error().message());
1020 const auto &ctx = ctx_opt.value();
1021 ctx->checked_signature_counter++;
1027 "Round #{}: Precommit signed by {} was rejected: " 1028 "invalid signature",
1033 const auto &ctx = ctx_opt.value();
1034 ctx->invalid_signature_counter++;
1040 if (
auto result = onSigned<Precommit>(precommit); result.has_failure()) {
1049 logger_->warn(
"Round #{}: Precommit signed by {} was rejected: {}",
1052 result.error().message());
1058 "Round #{}: Precommit signed by {} was accepted for block {}",
1063 if (
id_ == precommit.
id) {
1075 logger_->error(
"Round #{}: Precommit was not propagated: {}",
1077 res.error().message());
1087 bool need_to_update_grandpa_ghost =
1088 is_previous_round_changed or is_prevotes_changed;
1090 bool need_to_update_estimate =
1091 is_precommits_changed or need_to_update_grandpa_ghost;
1093 if (need_to_update_grandpa_ghost) {
1095 need_to_update_estimate =
true;
1099 if (need_to_update_estimate) {
1103 if (
auto grandpa =
grandpa_.lock()) {
1109 bool can_start_next_round;
1120 can_start_next_round =
true;
1124 can_start_next_round = can_start_next_round and
completable_;
1129 if (can_start_next_round) {
1131 [grandpa_wp = std::move(
grandpa_), round_wp = weak_from_this()] {
1132 if (
auto grandpa = grandpa_wp.lock()) {
1133 if (
auto round = round_wp.lock()) {
1134 grandpa->tryExecuteNextRound(round);
1141 template <
typename T>
1143 BOOST_ASSERT(vote.
is<T>());
1146 auto index_and_weight_opt =
voter_set_->indexAndWeight(vote.
id);
1147 if (!index_and_weight_opt) {
1148 SL_DEBUG(
logger_,
"Voter {} is not in the current voter set", vote.
id.toHex());
1151 const auto &[index, weight] = index_and_weight_opt.value();
1153 auto [type, type_str_, equivocators, tracker] =
1156 std::vector<bool> &,
1158 if constexpr (std::is_same_v<T, Prevote>) {
1162 if constexpr (std::is_same_v<T, Precommit>) {
1169 auto &type_str = type_str_;
1172 if (equivocators[index]) {
1181 auto push_res = tracker.push(vote, weight);
1185 if (result.has_error()) {
1186 tracker.unpush(vote, weight);
1187 auto log_lvl = log::Level::WARN;
1191 == outcome::failure(
1194 auto &ctx = ctx_opt.value();
1196 log_lvl = log::Level::DEBUG;
1201 "{} from {} for block {} was not inserted with error: {}",
1205 result.error().message());
1206 return result.as_failure();
1208 return outcome::success();
1214 equivocators[index] =
true;
1219 BOOST_UNREACHABLE_RETURN({});
1223 template outcome::result<void> VotingRoundImpl::onSigned<Prevote>(
1226 template outcome::result<void> VotingRoundImpl::onSigned<Precommit>(
1232 "Round #{}: updateGrandpaGhost->false " 1233 "(total prevote weight={} < threshold={})",
1243 auto possible_to_prevote = [
this](
const VoteWeight &weight) {
1249 auto new_prevote_ghost =
1252 if (new_prevote_ghost.has_value()) {
1259 "Round #{}: updateGrandpaGhost->true " 1260 "(prevote ghost was changed to block {})",
1265 "Round #{}: updateGrandpaGhost->false " 1266 "(prevote ghost was not changed)",
1273 "Round #{}: updateGrandpaGhost->false (no new prevote ghost)",
1281 "Round #{}: updateEstimate->false " 1282 "(total prevote weight={} < threshold={})",
1292 const auto &prevote_ghost = prevote_ghost_.value();
1297 auto possible_to_finalize = [&](
const VoteWeight &weight) {
1298 return weight.total(
1317 const auto current_equivocations = std::accumulate(
1321 [
this, index = 0ul](
size_t sum,
auto isEquivocator)
mutable {
1322 if (not isEquivocator) {
1326 return sum +
voter_set_->voterWeight(index++).value();
1329 const auto additional_equivocations =
1330 tolerated_equivocations - current_equivocations;
1332 const auto remaining_commit_votes =
1339 auto possible_to_precommit = [&](
const VoteWeight &weight) {
1341 auto precommited_for = weight.total(
1346 auto possible_equivocations =
1347 std::min<size_t>(
precommits_->getTotalWeight() - precommited_for,
1348 additional_equivocations);
1353 auto full_possible_weight =
1354 precommited_for + remaining_commit_votes + possible_equivocations;
1372 "Round #{}: updateEstimate->false: pc weight not enough => " 1373 "estimate=pv_ghost",
1384 "Round #{}: updateEstimate: no estimate => completable=false",
1387 const auto &estimate =
estimate_.value();
1389 if (estimate != prevote_ghost) {
1393 "Round #{}: updateEstimate: estimate!=pv_ghost => completable=true",
1396 auto ghost_opt =
graph_->findGhost(
1399 if (not ghost_opt.has_value()) {
1402 "Round #{}: updateEstimate: no pc_ghost => completable=true",
1405 const auto &ghost = ghost_opt.value();
1411 if (ghost == estimate) {
1414 "Round #{}: updateEstimate: estimate==pc_ghost => " 1420 "Round #{}: updateEstimate: estimate!=pc_ghost => " 1421 "completable=false",
1447 env_->bestChainContaining(best_final_candidate.hash,
voter_set_->id());
1448 auto best_prevote_candidate = best_chain.has_value()
1449 ? convertToBlockInfo(best_chain.value())
1459 if (best_prevote_candidate.number >= primary.number
1460 and primary.number > best_final_candidate.number) {
1466 return prevote_.value();
1480 round_state.votes.reserve(round_state.votes.size() + precommits.size());
1481 std::move(precommits.begin(),
1483 std::back_inserter(round_state.votes));
1489 const BlockInfo &estimate,
const std::vector<VoteVariant> &votes)
const {
1490 auto result = std::accumulate(
1493 std::vector<SignedPrevote>(),
1494 [
this, &estimate](
auto &prevotes,
const auto &voting_variant) {
1497 [
this, &prevotes, &estimate](
1500 if (env_->isEqualOrDescendOf(estimate.hash,
1501 voting_message.getBlockHash())) {
1503 static_cast<const SignedPrevote &>(voting_message));
1508 &equivocatory_voting_message) {
1509 prevotes.push_back(static_cast<const SignedPrevote &>(
1510 equivocatory_voting_message.first));
1511 prevotes.push_back(static_cast<const SignedPrevote &>(
1512 equivocatory_voting_message.second));
1520 const BlockInfo &estimate,
const std::vector<VoteVariant> &votes)
const {
1521 auto result = std::accumulate(
1524 std::vector<SignedPrecommit>(),
1525 [
this, &estimate](
auto &precommits,
const auto &voting_variant) {
1528 [
this, &precommits, &estimate](
1531 if (env_->isEqualOrDescendOf(estimate.hash,
1532 voting_message.getBlockHash())) {
1533 precommits.push_back(
1534 static_cast<const SignedPrecommit &>(voting_message));
1539 &equivocatory_voting_message) {
1540 precommits.push_back(static_cast<const SignedPrecommit &>(
1541 equivocatory_voting_message.first));
1542 precommits.push_back(static_cast<const SignedPrecommit &>(
1543 equivocatory_voting_message.second));
1552 const auto &finalized_block =
finalized_.value();
1555 auto prevote_justification =
1558 auto precommit_justification =
1561 auto result =
env_->onCatchUpRespond(peer_id,
1564 std::move(prevote_justification),
1565 std::move(precommit_justification),
1568 logger_->warn(
"Catch-Up-Response was not sent: {}",
1569 result.error().message());
1574 auto res =
env_->onNeighborMessageSent(
1578 if (res.has_error()) {
1579 logger_->warn(
"Neighbor message was not sent: {}", res.error().message());
1588 std::function<void(std::shared_ptr<VotingRound>)> resend =
1589 [&](std::shared_ptr<VotingRound> round_arg)
mutable {
1591 BOOST_ASSERT_MSG(round !=
nullptr,
"Expected single implementation");
1592 if (
auto prev_round = round->getPreviousRound()) {
1593 resend(std::move(prev_round));
1595 auto r = round->roundNumber();
1596 auto s = round->voterSetId();
1598 SL_DEBUG(
logger_,
"Round #{}: resend votes", r);
1599 for (
const auto &graph : {round->prevotes_, round->precommits_}) {
1600 for (
const auto &vote_variant : graph->getMessages()) {
1604 std::ignore =
env_->onVoted(r, s, vote);
1607 std::ignore =
env_->onVoted(r, s, pair.first);
1608 std::ignore =
env_->onVoted(r, s, pair.second);
1614 SL_DEBUG(
logger_,
"Resend votes of recent rounds");
1615 resend(shared_from_this());
RoundNumber roundNumber() const override
VoterSetId voterSetId() const override
libp2p::basic::Scheduler::Handle pending_timer_handle_
const RoundNumber round_number_
std::shared_ptr< libp2p::basic::Scheduler > scheduler_
std::chrono::milliseconds start_time_
void sendPrevote(const Prevote &prevote)
std::shared_ptr< VotingRound > previous_round_
void doPrecommit() override
Calculate precommit and broadcast signed precommit message.
libp2p::basic::Scheduler::Handle stage_timer_handle_
std::optional< BlockInfo > precommit_
void sendProposal(const PrimaryPropose &primary_proposal)
primitives::detail::BlockInfoT< struct PrimaryProposeTag > PrimaryPropose
void startPrecommitStage()
std::optional< BlockInfo > primary_vote_
bool completable() const override
std::vector< SignedPrecommit > items
std::shared_ptr< VoteTracker > precommits_
primitives::detail::BlockInfoT< struct PrecommitTag > Precommit
primitives::BlockInfo BlockInfo
BlockInfo bestFinalCandidate() override
std::vector< VoteVariant > votes
static const wasm::Name env
const std::chrono::milliseconds pending_interval_
outcome::result< void > validatePrecommitJustification(const BlockInfo &vote, const GrandpaJustification &justification) const
crypto::Ed25519PublicKey Id
MovableRoundState state() const override
std::function< void()> on_complete_handler_
std::shared_ptr< VoterSet > voters
Current round's authorities.
const std::optional< Id > id_
libp2p::peer::PeerId PeerId
std::optional< BlockInfo > prevote_
BlockInfo last_finalized_block_
outcome::result< void > onSigned(const SignedMessage &vote)
Triggered when we receive {.
static std::optional< std::shared_ptr< GrandpaContext > > get()
void sendPrecommit(const Precommit &precommit)
void sendNeighborMessage()
void update(IsPreviousRoundChanged is_previous_round_changed, IsPrevotesChanged is_prevotes_changed, IsPrecommitsChanged is_precommits_changed) override
std::vector< bool > prevote_equivocators_
BlockHash getBlockHash() const
outcome::result< void > applyJustification(const BlockInfo &block_info, const GrandpaJustification &justification) override
std::shared_ptr< authority::AuthorityManager > authority_manager_
bool updateGrandpaGhost()
std::shared_ptr< VoteTracker > prevotes_
std::optional< BlockInfo > finalized_
void doPrevote() override
Calculate prevote and broadcast signed prevote message.
void doCatchUpResponse(const libp2p::peer::PeerId &peer_id) override
std::pair< SignedMessage, SignedMessage > EquivocatorySignedMessage
BlockInfo getBlockInfo() const
BlockInfo bestPrevoteCandidate() override
std::string toHex() const noexcept
std::vector< bool > precommit_equivocators_
Stores the current state of the round.
void doFinalize() override
Collect and save justifications finalizing this round.
bool onPrevote(const SignedMessage &prevote, Propagation propagation) override
std::shared_ptr< VoterSet > voter_set_
void doProposal() override
bool isPrimary(const Id &id) const
Check if peer.
BlockInfo last_finalized_block
std::vector< SignedPrevote > getPrevoteJustification(const BlockInfo &estimate, const std::vector< VoteVariant > &votes) const
std::optional< BlockInfo > prevote_ghost_
void onProposal(const SignedMessage &proposal, Propagation propagation) override
std::optional< BlockInfo > estimate_
bool onPrecommit(const SignedMessage &precommit, Propagation propagation) override
std::shared_ptr< VoteGraph > graph_
primitives::detail::BlockInfoT< struct PrevoteTag > Prevote
std::shared_ptr< Environment > env_
void doCommit() override
Broadcast commit message.
void attemptToFinalizeRound() override
std::weak_ptr< Grandpa > grandpa_
std::shared_ptr< VoteCryptoProvider > vote_crypto_provider_
std::vector< SignedPrecommit > getPrecommitJustification(const BlockInfo &precommits, const std::vector< VoteVariant > &votes) const