From 8f4628dd65b30993874081233e929b031ab59fa8 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 24 Jan 2024 10:57:46 -0500 Subject: [PATCH 1/4] accumulate the weight in vote calculation --- libraries/chain/block_state.cpp | 4 +- libraries/chain/hotstuff/hotstuff.cpp | 29 ++--- .../chain/hotstuff/test/hotstuff_tools.cpp | 14 +-- .../eosio/chain/hotstuff/finalizer_policy.hpp | 9 ++ .../include/eosio/chain/hotstuff/hotstuff.hpp | 18 +-- unittests/block_state_tests.cpp | 103 +++++++++++++++++- 6 files changed, 133 insertions(+), 44 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 397cfc8889..3d58c2a1f5 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -11,7 +11,7 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , block(std::move(b)) , strong_digest(compute_finalizer_digest()) , weak_digest(compute_finalizer_digest()) - , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->threshold) + , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->finalizer_weights(), prev.active_finalizer_policy->threshold) {} block_state::block_state(const block_header_state& bhs, deque&& trx_metas, @@ -20,7 +20,7 @@ block_state::block_state(const block_header_state& bhs, deque(signed_block_header{bhs.header})) // [greg todo] do we need signatures? , strong_digest(compute_finalizer_digest()) , weak_digest(compute_finalizer_digest()) - , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->threshold) + , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->finalizer_weights(), bhs.active_finalizer_policy->threshold) , pub_keys_recovered(true) // probably not needed , cached_trxs(std::move(trx_metas)) { diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index 531f2a33a8..4af1cbe9d6 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -45,22 +45,15 @@ pending_quorum_certificate::pending_quorum_certificate() : _mtx(std::make_unique()) { } -pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, size_t quorum) +pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, std::vector&& weights, uint64_t quorum) : _num_finalizers(num_finalizers) , _quorum(quorum) + , _finalizer_weights(std::move(weights)) , _mtx(std::make_unique()) { _weak_votes.resize(num_finalizers); _strong_votes.resize(num_finalizers); } -pending_quorum_certificate::pending_quorum_certificate(const fc::sha256& proposal_id, - const digest_type& proposal_digest, size_t num_finalizers, - size_t quorum) - : pending_quorum_certificate(num_finalizers, quorum) { - _proposal_id = proposal_id; - _proposal_digest.assign(proposal_digest.data(), proposal_digest.data() + 32); -} - bool pending_quorum_certificate::is_quorum_met() const { std::lock_guard g(*_mtx); return _state == state_t::weak_achieved || _state == state_t::weak_final || _state == state_t::strong; @@ -84,21 +77,20 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector& pro assert(index < _num_finalizers); if (!_strong_votes.add_vote(proposal_digest, index, pubkey, sig)) return false; - size_t weak = num_weak(); - size_t strong = num_strong(); + _strong_accumulated_weight += _finalizer_weights[index]; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (strong >= _quorum) { + if (_strong_accumulated_weight >= _quorum) { assert(_state != state_t::restricted); _state = state_t::strong; - } else if (weak + strong >= _quorum) + } else if (_weak_accumulated_weight + _strong_accumulated_weight >= _quorum) _state = (_state == state_t::restricted) ? state_t::weak_final : state_t::weak_achieved; break; case state_t::weak_achieved: - if (strong >= _quorum) + if (_strong_accumulated_weight >= _quorum) _state = state_t::strong; break; @@ -116,16 +108,15 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector& propo assert(index < _num_finalizers); if (!_weak_votes.add_vote(proposal_digest, index, pubkey, sig)) return false; - size_t weak = num_weak(); - size_t strong = num_strong(); + _weak_accumulated_weight += _finalizer_weights[index]; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (weak + strong >= _quorum) + if (_weak_accumulated_weight + _strong_accumulated_weight >= _quorum) _state = state_t::weak_achieved; - if (weak > (_num_finalizers - _quorum)) { + if (_weak_accumulated_weight > (_num_finalizers - _quorum)) { if (_state == state_t::weak_achieved) _state = state_t::weak_final; else if (_state == state_t::unrestricted) @@ -134,7 +125,7 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector& propo break; case state_t::weak_achieved: - if (weak >= (_num_finalizers - _quorum)) + if (_weak_accumulated_weight >= (_num_finalizers - _quorum)) _state = state_t::weak_final; break; diff --git a/libraries/chain/hotstuff/test/hotstuff_tools.cpp b/libraries/chain/hotstuff/test/hotstuff_tools.cpp index a5b62ffda0..12f890a67b 100644 --- a/libraries/chain/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/chain/hotstuff/test/hotstuff_tools.cpp @@ -108,7 +108,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { }; { - pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 + pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add one weak vote @@ -131,7 +131,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 + pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a weak vote @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 + pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a strong vote @@ -165,7 +165,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2 + pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 // add a weak vote // --------------- @@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2 + pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 // add a weak vote // --------------- @@ -217,7 +217,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2 + pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 // add a weak vote // --------------- @@ -243,7 +243,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { } { - pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2 + pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 // add a weak vote // --------------- diff --git a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp index 89c75d1994..61100121cd 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp @@ -9,6 +9,15 @@ namespace eosio::chain { uint32_t generation = 0; ///< sequentially incrementing version number uint64_t threshold = 0; ///< vote weight threshold to finalize blocks std::vector finalizers; ///< Instant Finality voter set + + std::vector finalizer_weights() const { + auto n = finalizers.size(); + std::vector weights(n); + for( size_t i = 0; i < n; ++i ) { + weights[i] = finalizers[i].weight; + } + return weights; + } }; using finalizer_policy_ptr = std::shared_ptr; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 9573637ad5..bd29bb317a 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -185,18 +185,13 @@ namespace eosio::chain { pending_quorum_certificate(); - explicit pending_quorum_certificate(size_t num_finalizers, size_t quorum); - - explicit pending_quorum_certificate(const fc::sha256& proposal_id, - const digest_type& proposal_digest, - size_t num_finalizers, - size_t quorum); + explicit pending_quorum_certificate(size_t num_finalizers, std::vector&& weights, uint64_t quorum); // thread safe bool is_quorum_met() const; // thread safe - void reset(const fc::sha256& proposal_id, const digest_type& proposal_digest, size_t num_finalizers, size_t quorum); + void reset(const fc::sha256& proposal_id, const digest_type& proposal_digest, size_t num_finalizers, uint64_t quorum); // thread safe std::pair add_vote(bool strong, @@ -223,15 +218,14 @@ namespace eosio::chain { std::vector _proposal_digest; state_t _state { state_t::unrestricted }; size_t _num_finalizers {0}; - size_t _quorum {0}; + std::vector _finalizer_weights; // weight of each finalizer + uint64_t _strong_accumulated_weight {0}; // accumulated weight of strong votes far + uint64_t _weak_accumulated_weight {0}; // accumulated weight of weak votes so far + uint64_t _quorum {0}; std::unique_ptr _mtx; // protect both _strong_votes and _weak_votes votes_t _weak_votes; votes_t _strong_votes; - // num_weak and num_strong are protected by mutex by add_vote - size_t num_weak() const { return _weak_votes.count(); } - size_t num_strong() const { return _strong_votes.count(); } - // called by add_vote, already protected by mutex bool add_strong_vote(const std::vector& proposal_digest, size_t index, diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index 350b577aef..6875b55833 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -42,7 +42,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; bsp->weak_digest = weak_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), threshold }; for (size_t i = 0; i < num_finalizers; ++i) { bool strong = (i % 2 == 0); // alternate strong and weak @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; vote_message vote {block_id, true, public_key[0], private_key[1].sign(strong_digest_data) }; BOOST_REQUIRE(!bsp->aggregate_vote(vote).first); @@ -66,7 +66,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; vote_message vote {block_id, true, public_key[0], private_key[0].sign(strong_digest_data) }; BOOST_REQUIRE(bsp->aggregate_vote(vote).first); @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; bls_private_key new_private_key{ "PVT_BLS_warwI76e+pPX9wLFZKPFagngeFM8bm6J8D5w0iiHpxW7PiId" }; bls_public_key new_public_key{ new_private_key.get_public_key() }; @@ -87,4 +87,99 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { } } FC_LOG_AND_RETHROW(); +void do_quorum_test(const std::vector& weights, + uint64_t threshold, + bool strong, + const std::vector& to_vote, + bool expected_quorum) { + using namespace eosio::chain; + using namespace fc::crypto::blslib; + + digest_type block_id(fc::sha256("0000000000000000000000000000001")); + digest_type strong_digest(fc::sha256("0000000000000000000000000000002")); + std::vector strong_digest_data(strong_digest.data(), strong_digest.data() + strong_digest.data_size()); + digest_type weak_digest(fc::sha256("0000000000000000000000000000003")); + std::vector weak_digest_data(weak_digest.data(), weak_digest.data() + weak_digest.data_size()); + + // initialize a set of private keys + std::vector private_key { + bls_private_key("PVT_BLS_r4ZpChd87ooyzl6MIkw23k7PRX8xptp7TczLJHCIIW88h/hS"), + bls_private_key("PVT_BLS_/l7xzXANaB+GrlTsbZEuTiSOiWTtpBoog+TZnirxUUSaAfCo"), + bls_private_key("PVT_BLS_3FoY73Q/gED3ejyg8cvnGqHrMmx4cLKwh/e0sbcsCxpCeqn3"), + }; + const size_t num_finalizers = private_key.size(); + + // construct finalizers + std::vector public_key(num_finalizers); + std::vector finalizers(num_finalizers); + for (size_t i = 0; i < num_finalizers; ++i) { + public_key[i] = private_key[i].get_public_key(); + finalizers[i] = finalizer_authority{ "test", weights[i], public_key[i] }; + } + + block_state_ptr bsp = std::make_shared(); + constexpr uint32_t generation = 1; + bsp->active_finalizer_policy = std::make_shared( generation, threshold, finalizers ); + bsp->strong_digest = strong_digest; + bsp->weak_digest = weak_digest; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), threshold }; + + for (size_t i = 0; i < num_finalizers; ++i) { + if( to_vote[i] ) { + auto sig = strong ? private_key[i].sign(strong_digest_data) : private_key[i].sign(weak_digest_data); + vote_message vote{ block_id, strong, public_key[i], sig }; + BOOST_REQUIRE(bsp->aggregate_vote(vote).first); + } + } + + BOOST_REQUIRE_EQUAL(bsp->pending_qc.is_quorum_met(), expected_quorum); +} + +BOOST_AUTO_TEST_CASE(quorum_test) try { + std::vector weights{1, 3, 5}; + constexpr uint64_t threshold = 4; + + { // 1 strong vote, quorum not met + constexpr bool strong = true; + std::vector to_vote{true, false, false}; // finalizer 0 voting + constexpr bool expected_quorum_met = false; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } + + { // 2 strong votes, quorum met + constexpr bool strong = true; + std::vector to_vote{true, true, false}; // finalizers 0 and 1 voting + constexpr bool expected_quorum_met = true; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } + + { // 1 strong vote, quorum met + constexpr bool strong = true; + std::vector to_vote{false, false, true}; // finalizer 2 voting + constexpr bool expected_quorum_met = true; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } + + { // 1 weak vote, quorum not met + constexpr bool strong = false; + std::vector to_vote{true, false, false}; // finalizer 0 voting + constexpr bool expected_quorum_met = false; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } + + { // 2 weak votes, quorum met + constexpr bool strong = false; + std::vector to_vote{true, true, false}; // finalizers 0 and 1 voting + constexpr bool expected_quorum_met = true; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } + + { // 1 weak vote, quorum met + constexpr bool strong = false; + std::vector to_vote{false, false, true}; // finalizer 2 voting + constexpr bool expected_quorum_met = true; + do_quorum_test( weights, threshold, strong, to_vote, expected_quorum_met ); + } +} FC_LOG_AND_RETHROW(); + BOOST_AUTO_TEST_SUITE_END() From 70f4cef5ead7a6399730ac8ca2bbbb08b38cf883 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Wed, 24 Jan 2024 14:21:13 -0500 Subject: [PATCH 2/4] fix a merge conflict --- unittests/block_state_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index c8e4039244..9ff05207fe 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; bsp->weak_digest = weak_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), threshold }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; for (size_t i = 0; i < num_finalizers; ++i) { bool strong = (i % 2 == 0); // alternate strong and weak From f5bb5da79ba157d5399f8da39194aaa0e8425e43 Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Mon, 29 Jan 2024 20:57:44 -0500 Subject: [PATCH 3/4] rework accumalating vote weight --- libraries/chain/block_state.cpp | 7 +- libraries/chain/hotstuff/hotstuff.cpp | 50 +++++------- .../hotstuff/test/finality_misc_tests.cpp | 76 +++++++++++-------- .../eosio/chain/hotstuff/finalizer_policy.hpp | 9 ++- .../include/eosio/chain/hotstuff/hotstuff.hpp | 25 +++--- unittests/block_state_tests.cpp | 10 +-- 6 files changed, 91 insertions(+), 86 deletions(-) diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index f44f881cd5..7cd5aca8cd 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -13,7 +13,7 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con , block(std::move(b)) , strong_digest(compute_finalizer_digest()) , weak_digest(compute_finalizer_digest()) - , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->finalizer_weights(), prev.active_finalizer_policy->threshold) + , pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->threshold, prev.active_finalizer_policy->max_weak_sum_before_weak_final()) {} block_state::block_state(const block_header_state& bhs, deque&& trx_metas, @@ -22,7 +22,7 @@ block_state::block_state(const block_header_state& bhs, deque(signed_block_header{bhs.header})) // [greg todo] do we need signatures? , strong_digest(compute_finalizer_digest()) , weak_digest(compute_finalizer_digest()) - , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->finalizer_weights(), bhs.active_finalizer_policy->threshold) + , pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) , pub_keys_recovered(true) // probably not needed , cached_trxs(std::move(trx_metas)) { @@ -81,7 +81,8 @@ std::pair> block_state::aggregate_vote(const vote_ std::vector{digest.data(), digest.data() + digest.data_size()}, index, vote.finalizer_key, - vote.sig); + vote.sig, + finalizers[index].weight); return {valid, strong ? core.final_on_strong_qc_block_num : std::optional{}}; } else { wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index 4af1cbe9d6..e2f361ff1e 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -45,11 +45,10 @@ pending_quorum_certificate::pending_quorum_certificate() : _mtx(std::make_unique()) { } -pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, std::vector&& weights, uint64_t quorum) - : _num_finalizers(num_finalizers) +pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, uint64_t quorum, uint64_t max_weak_sum_before_weak_final) + : _mtx(std::make_unique()) , _quorum(quorum) - , _finalizer_weights(std::move(weights)) - , _mtx(std::make_unique()) { + , _max_weak_sum_before_weak_final(max_weak_sum_before_weak_final) { _weak_votes.resize(num_finalizers); _strong_votes.resize(num_finalizers); } @@ -59,38 +58,26 @@ bool pending_quorum_certificate::is_quorum_met() const { return _state == state_t::weak_achieved || _state == state_t::weak_final || _state == state_t::strong; } -void pending_quorum_certificate::reset(const fc::sha256& proposal_id, const digest_type& proposal_digest, - size_t num_finalizers, size_t quorum) { - std::lock_guard g(*_mtx); - _proposal_id = proposal_id; - _proposal_digest.assign(proposal_digest.data(), proposal_digest.data() + 32); - _quorum = quorum; - _strong_votes.reset(num_finalizers); - _weak_votes.reset(num_finalizers); - _num_finalizers = num_finalizers; - _state = state_t::unrestricted; -} - // called by add_vote, already protected by mutex bool pending_quorum_certificate::add_strong_vote(const std::vector& proposal_digest, size_t index, - const bls_public_key& pubkey, const bls_signature& sig) { - assert(index < _num_finalizers); + const bls_public_key& pubkey, const bls_signature& sig, + uint64_t weight) { if (!_strong_votes.add_vote(proposal_digest, index, pubkey, sig)) return false; - _strong_accumulated_weight += _finalizer_weights[index]; + _strong_sum += weight; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (_strong_accumulated_weight >= _quorum) { + if (_strong_sum >= _quorum) { assert(_state != state_t::restricted); _state = state_t::strong; - } else if (_weak_accumulated_weight + _strong_accumulated_weight >= _quorum) + } else if (_weak_sum + _strong_sum >= _quorum) _state = (_state == state_t::restricted) ? state_t::weak_final : state_t::weak_achieved; break; case state_t::weak_achieved: - if (_strong_accumulated_weight >= _quorum) + if (_strong_sum >= _quorum) _state = state_t::strong; break; @@ -104,19 +91,19 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector& pro // called by add_vote, already protected by mutex bool pending_quorum_certificate::add_weak_vote(const std::vector& proposal_digest, size_t index, - const bls_public_key& pubkey, const bls_signature& sig) { - assert(index < _num_finalizers); + const bls_public_key& pubkey, const bls_signature& sig, + uint64_t weight) { if (!_weak_votes.add_vote(proposal_digest, index, pubkey, sig)) return false; - _weak_accumulated_weight += _finalizer_weights[index]; + _weak_sum += weight; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (_weak_accumulated_weight + _strong_accumulated_weight >= _quorum) + if (_weak_sum + _strong_sum >= _quorum) _state = state_t::weak_achieved; - if (_weak_accumulated_weight > (_num_finalizers - _quorum)) { + if (_weak_sum > _max_weak_sum_before_weak_final) { if (_state == state_t::weak_achieved) _state = state_t::weak_final; else if (_state == state_t::unrestricted) @@ -125,7 +112,7 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector& propo break; case state_t::weak_achieved: - if (_weak_accumulated_weight >= (_num_finalizers - _quorum)) + if (_weak_sum >= _max_weak_sum_before_weak_final) _state = state_t::weak_final; break; @@ -139,10 +126,11 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector& propo // thread safe, std::pair pending_quorum_certificate::add_vote(bool strong, const std::vector& proposal_digest, size_t index, - const bls_public_key& pubkey, const bls_signature& sig) { + const bls_public_key& pubkey, const bls_signature& sig, + uint64_t weight) { std::lock_guard g(*_mtx); - bool valid = strong ? add_strong_vote(proposal_digest, index, pubkey, sig) - : add_weak_vote(proposal_digest, index, pubkey, sig); + bool valid = strong ? add_strong_vote(proposal_digest, index, pubkey, sig, weight) + : add_weak_vote(proposal_digest, index, pubkey, sig, weight); return {valid, _state == state_t::strong}; } diff --git a/libraries/chain/hotstuff/test/finality_misc_tests.cpp b/libraries/chain/hotstuff/test/finality_misc_tests.cpp index 785a685eb3..9f1726f07b 100644 --- a/libraries/chain/hotstuff/test/finality_misc_tests.cpp +++ b/libraries/chain/hotstuff/test/finality_misc_tests.cpp @@ -50,83 +50,93 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { for (const auto& k : sk) pubkey.push_back(k.get_public_key()); - auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index) { - return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest)).first; + auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { + return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest), weight).first; }; - auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index) { - return qc.add_vote(true, digest, index, pubkey[index], sk[index].sign(digest)).first; + auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index, uint64_t weight) { + return qc.add_vote(true, digest, index, pubkey[index], sk[index].sign(digest), weight).first; }; + constexpr uint64_t weight = 1; + { - pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 + constexpr uint64_t quorum = 1; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(2, quorum, max_weak_sum_before_weak_final); // 2 finalizers BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add one weak vote // ----------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); // add duplicate weak vote // ----------------------- - bool ok = weak_vote(qc, digest, 0); + bool ok = weak_vote(qc, digest, 0, weight); BOOST_CHECK(!ok); // vote was a duplicate BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); // add another weak vote // --------------------- - weak_vote(qc, digest, 1); + weak_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); } { - pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 + constexpr uint64_t quorum = 1; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(2, quorum, max_weak_sum_before_weak_final); // 2 finalizers BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a weak vote // --------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); // add a strong vote // ----------------- - strong_vote(qc, digest, 1); + strong_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); } { - pending_quorum_certificate qc(2, {1, 1}, 1); // 2 finalizers, quorum = 1 + constexpr uint64_t quorum = 1; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(2, quorum, max_weak_sum_before_weak_final); // 2 finalizers, weight_sum_minus_quorum = 1 BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a strong vote // ----------------- - strong_vote(qc, digest, 1); + strong_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); // add a strong vote // ----------------- - strong_vote(qc, digest, 1); + strong_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); } { - pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 + constexpr uint64_t quorum = 2; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(3, quorum, max_weak_sum_before_weak_final); // 3 finalizers // add a weak vote // --------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a strong vote // ----------------- - strong_vote(qc, digest, 1); + strong_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); @@ -135,24 +145,26 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a weak vote // --------------- - weak_vote(qc2, digest, 2); + weak_vote(qc2, digest, 2, weight); BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final); BOOST_CHECK(qc2.is_quorum_met()); } } { - pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 + constexpr uint64_t quorum = 2; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(3, quorum, max_weak_sum_before_weak_final); // 3 finalizers, quorum = 2 // add a weak vote // --------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a strong vote // ----------------- - strong_vote(qc, digest, 1); + strong_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); @@ -161,24 +173,26 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a strong vote // ----------------- - strong_vote(qc2, digest, 2); + strong_vote(qc2, digest, 2, weight); BOOST_CHECK_EQUAL(qc2.state(), state_t::strong); BOOST_CHECK(qc2.is_quorum_met()); } } { - pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 + constexpr uint64_t quorum = 2; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(3, quorum, max_weak_sum_before_weak_final); // 3 finalizers, quorum = 2 // add a weak vote // --------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a weak vote // --------------- - weak_vote(qc, digest, 1); + weak_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); BOOST_CHECK(qc.is_quorum_met()); @@ -187,24 +201,26 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a weak vote // --------------- - weak_vote(qc2, digest, 2); + weak_vote(qc2, digest, 2, weight); BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final); BOOST_CHECK(qc2.is_quorum_met()); } } { - pending_quorum_certificate qc(3, {1, 1, 1}, 2); // 3 finalizers, quorum = 2 + constexpr uint64_t quorum = 2; + constexpr uint64_t max_weak_sum_before_weak_final = 1; + pending_quorum_certificate qc(3, quorum, max_weak_sum_before_weak_final); // 3 finalizers, quorum = 2 // add a weak vote // --------------- - weak_vote(qc, digest, 0); + weak_vote(qc, digest, 0, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a weak vote // --------------- - weak_vote(qc, digest, 1); + weak_vote(qc, digest, 1, weight); BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); BOOST_CHECK(qc.is_quorum_met()); @@ -213,7 +229,7 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a strong vote // ----------------- - strong_vote(qc2, digest, 2); + strong_vote(qc2, digest, 2, weight); BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final); BOOST_CHECK(qc2.is_quorum_met()); } diff --git a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp index 61100121cd..0de3074cc8 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp @@ -10,13 +10,14 @@ namespace eosio::chain { uint64_t threshold = 0; ///< vote weight threshold to finalize blocks std::vector finalizers; ///< Instant Finality voter set - std::vector finalizer_weights() const { + // max accumulated weak weight before becoming weak_final + uint64_t max_weak_sum_before_weak_final() const { auto n = finalizers.size(); - std::vector weights(n); + uint64_t sum = 0; for( size_t i = 0; i < n; ++i ) { - weights[i] = finalizers[i].weight; + sum += finalizers[i].weight; } - return weights; + return (sum - threshold); } }; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index bd29bb317a..01f6d2933c 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -185,20 +185,18 @@ namespace eosio::chain { pending_quorum_certificate(); - explicit pending_quorum_certificate(size_t num_finalizers, std::vector&& weights, uint64_t quorum); + explicit pending_quorum_certificate(size_t num_finalizers, uint64_t quorum, uint64_t max_weak_sum_before_weak_final); // thread safe bool is_quorum_met() const; - // thread safe - void reset(const fc::sha256& proposal_id, const digest_type& proposal_digest, size_t num_finalizers, uint64_t quorum); - // thread safe std::pair add_vote(bool strong, const std::vector&proposal_digest, size_t index, const bls_public_key&pubkey, - const bls_signature&sig); + const bls_signature&sig, + uint64_t weight); state_t state() const { std::lock_guard g(*_mtx); return _state; }; valid_quorum_certificate to_valid_quorum_certificate() const; @@ -216,13 +214,12 @@ namespace eosio::chain { friend class qc_chain; fc::sha256 _proposal_id; // only used in to_msg(). Remove eventually std::vector _proposal_digest; - state_t _state { state_t::unrestricted }; - size_t _num_finalizers {0}; - std::vector _finalizer_weights; // weight of each finalizer - uint64_t _strong_accumulated_weight {0}; // accumulated weight of strong votes far - uint64_t _weak_accumulated_weight {0}; // accumulated weight of weak votes so far + std::unique_ptr _mtx; uint64_t _quorum {0}; - std::unique_ptr _mtx; // protect both _strong_votes and _weak_votes + uint64_t _max_weak_sum_before_weak_final {0}; // max weak sum before becoming weak_final + state_t _state { state_t::unrestricted }; + uint64_t _strong_sum {0}; // accumulated sum of strong votes so far + uint64_t _weak_sum {0}; // accumulated sum of weak votes so far votes_t _weak_votes; votes_t _strong_votes; @@ -230,13 +227,15 @@ namespace eosio::chain { bool add_strong_vote(const std::vector& proposal_digest, size_t index, const bls_public_key& pubkey, - const bls_signature& sig); + const bls_signature& sig, + uint64_t weight); // called by add_vote, already protected by mutex bool add_weak_vote(const std::vector& proposal_digest, size_t index, const bls_public_key& pubkey, - const bls_signature& sig); + const bls_signature& sig, + uint64_t weight); }; } //eosio::chain diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index aa86713db7..0b4bfa488e 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -43,7 +43,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; bsp->weak_digest = weak_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; for (size_t i = 0; i < num_finalizers; ++i) { bool strong = (i % 2 == 0); // alternate strong and weak @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; vote_message vote {block_id, true, public_key[0], private_key[1].sign(strong_digest_data) }; BOOST_REQUIRE(!bsp->aggregate_vote(vote).first); @@ -67,7 +67,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; vote_message vote {block_id, true, public_key[0], private_key[0].sign(strong_digest_data) }; BOOST_REQUIRE(bsp->aggregate_vote(vote).first); @@ -78,7 +78,7 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { block_state_ptr bsp = std::make_shared(); bsp->active_finalizer_policy = std::make_shared( 10, 15, finalizers ); bsp->strong_digest = strong_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), 1 }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, 1, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; bls_private_key new_private_key{ "PVT_BLS_warwI76e+pPX9wLFZKPFagngeFM8bm6J8D5w0iiHpxW7PiId" }; bls_public_key new_public_key{ new_private_key.get_public_key() }; @@ -123,7 +123,7 @@ void do_quorum_test(const std::vector& weights, bsp->active_finalizer_policy = std::make_shared( generation, threshold, finalizers ); bsp->strong_digest = strong_digest; bsp->weak_digest = weak_digest; - bsp->pending_qc = pending_quorum_certificate{ num_finalizers, bsp->active_finalizer_policy->finalizer_weights(), threshold }; + bsp->pending_qc = pending_quorum_certificate{ num_finalizers, threshold, bsp->active_finalizer_policy->max_weak_sum_before_weak_final() }; for (size_t i = 0; i < num_finalizers; ++i) { if( to_vote[i] ) { From 7ada54858e7d46d26fbd129bf860328d5bccba0f Mon Sep 17 00:00:00 2001 From: Lin Huang Date: Tue, 30 Jan 2024 09:56:51 -0500 Subject: [PATCH 4/4] use std::accumulate to sum; correct a space problem --- .../include/eosio/chain/hotstuff/finalizer_policy.hpp | 11 ++++++----- unittests/block_state_tests.cpp | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp index 0de3074cc8..60a26a755d 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp @@ -12,11 +12,12 @@ namespace eosio::chain { // max accumulated weak weight before becoming weak_final uint64_t max_weak_sum_before_weak_final() const { - auto n = finalizers.size(); - uint64_t sum = 0; - for( size_t i = 0; i < n; ++i ) { - sum += finalizers[i].weight; - } + uint64_t sum = std::accumulate( finalizers.begin(), finalizers.end(), 0, + [](uint64_t acc, const finalizer_authority& f) { + return acc + f.weight; + } + ); + return (sum - threshold); } }; diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp index 0b4bfa488e..eb1295e41a 100644 --- a/unittests/block_state_tests.cpp +++ b/unittests/block_state_tests.cpp @@ -191,7 +191,7 @@ BOOST_AUTO_TEST_CASE(verify_qc_test) try { digest_type strong_digest(fc::sha256("0000000000000000000000000000002")); std::vector strong_digest_data(strong_digest.data(), strong_digest.data() + strong_digest.data_size()); digest_type weak_digest(fc::sha256("0000000000000000000000000000003")); - std::vector weak_digest_data(weak_digest.data(), weak_digest.data() + weak_digest. data_size()); + std::vector weak_digest_data(weak_digest.data(), weak_digest.data() + weak_digest.data_size()); // initialize a set of private keys std::vector private_key {