diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index fd5785e310..cae7704f2d 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -3962,6 +3962,8 @@ struct controller_impl { // is actually valid as it simply is used as a network message for this data. const auto& final_on_strong_qc_block_ref = claimed->core.get_block_reference(claimed->core.final_on_strong_qc_block_num); set_if_irreversible_block_id(final_on_strong_qc_block_ref.block_id); + // Update finalizer safety information based on vote evidence + my_finalizers.maybe_update_fsi(claimed, received_qc); } } diff --git a/libraries/chain/finality/finalizer.cpp b/libraries/chain/finality/finalizer.cpp index 35100dd805..0c06e96c16 100644 --- a/libraries/chain/finality/finalizer.cpp +++ b/libraries/chain/finality/finalizer.cpp @@ -85,6 +85,19 @@ finalizer::vote_result finalizer::decide_vote(const block_state_ptr& bsp) { return res; } +// ---------------------------------------------------------------------------------------- +// finalizer has voted strong on bsp, update finalizer safety info if more recent than the current lock +bool finalizer::maybe_update_fsi(const block_state_ptr& bsp) { + auto& final_on_strong_qc_block_ref = bsp->core.get_block_reference(bsp->core.final_on_strong_qc_block_num); + if (final_on_strong_qc_block_ref.timestamp > fsi.lock.timestamp) { + fsi.lock = { final_on_strong_qc_block_ref.block_id, final_on_strong_qc_block_ref.timestamp }; + fsi.last_vote = { bsp->id(), bsp->timestamp() }; + fsi.last_vote_range_start = bsp->core.latest_qc_block_timestamp(); + return true; + } + return false; +} + // ---------------------------------------------------------------------------------------- vote_message_ptr finalizer::maybe_vote(const bls_public_key& pub_key, const block_state_ptr& bsp, @@ -105,6 +118,41 @@ vote_message_ptr finalizer::maybe_vote(const bls_public_key& pub_key, } // ---------------------------------------------------------------------------------------- +inline bool has_voted_strong(const std::vector& active_finalizers, const valid_quorum_certificate& qc, const bls_public_key& key) { + assert(qc.is_strong()); + auto it = std::find_if(active_finalizers.begin(), + active_finalizers.end(), + [&](const auto& finalizer) { return finalizer.public_key == key; }); + + if (it != active_finalizers.end()) { + auto index = std::distance(active_finalizers.begin(), it); + assert(qc.strong_votes); + return qc.strong_votes->test(index); + } + return false; +} + +void my_finalizers_t::maybe_update_fsi(const block_state_ptr& bsp, const valid_quorum_certificate& received_qc) { + if (finalizers.empty()) + return; + + assert(bsp->active_finalizer_policy); + + // see comment on possible optimization in maybe_vote + std::lock_guard g(mtx); + + bool updated = false; + for (auto& f : finalizers) { + if (has_voted_strong(bsp->active_finalizer_policy->finalizers, received_qc, f.first)) { + updated |= f.second.maybe_update_fsi(bsp); + } + } + + if (updated) { + save_finalizer_safety_info(); + } +} + void my_finalizers_t::save_finalizer_safety_info() const { if (!persist_file.is_open()) { diff --git a/libraries/chain/include/eosio/chain/finality/finalizer.hpp b/libraries/chain/include/eosio/chain/finality/finalizer.hpp index eb965b2ddc..81a9a36d2d 100644 --- a/libraries/chain/include/eosio/chain/finality/finalizer.hpp +++ b/libraries/chain/include/eosio/chain/finality/finalizer.hpp @@ -63,6 +63,8 @@ namespace eosio::chain { vote_result decide_vote(const block_state_ptr& bsp); vote_message_ptr maybe_vote(const bls_public_key& pub_key, const block_state_ptr& bsp, const digest_type& digest); + // finalizer has voted strong, update fsi if it does not already contain vote or better + bool maybe_update_fsi(const block_state_ptr& bsp); }; // ---------------------------------------------------------------------------------------- @@ -119,6 +121,8 @@ namespace eosio::chain { } } + void maybe_update_fsi(const block_state_ptr& bsp, const valid_quorum_certificate& received_qc); + size_t size() const { return finalizers.size(); } // doesn't change, thread safe bool empty() const { return finalizers.empty(); } // doesn't change, thread safe