diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index 83bdec97dc..74865cabcc 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->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<transaction_metadata_ptr>&& trx_metas, @@ -22,7 +22,7 @@ block_state::block_state(const block_header_state& bhs, deque<transaction_metada , block(std::make_shared<signed_block>(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->threshold, bhs.active_finalizer_policy->max_weak_sum_before_weak_final()) , pub_keys_recovered(true) // probably not needed , cached_trxs(std::move(trx_metas)) { @@ -83,7 +83,8 @@ std::pair<bool, std::optional<uint32_t>> block_state::aggregate_vote(const vote_ std::vector<uint8_t>{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<uint32_t>{}}; } 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 531f2a33a8..e2f361ff1e 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -45,60 +45,39 @@ pending_quorum_certificate::pending_quorum_certificate() : _mtx(std::make_unique<std::mutex>()) { } -pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, size_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<std::mutex>()) , _quorum(quorum) - , _mtx(std::make_unique<std::mutex>()) { + , _max_weak_sum_before_weak_final(max_weak_sum_before_weak_final) { _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; } -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<uint8_t>& 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; - size_t weak = num_weak(); - size_t strong = num_strong(); + _strong_sum += weight; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (strong >= _quorum) { + if (_strong_sum >= _quorum) { assert(_state != state_t::restricted); _state = state_t::strong; - } else if (weak + strong >= _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 >= _quorum) + if (_strong_sum >= _quorum) _state = state_t::strong; break; @@ -112,20 +91,19 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector<uint8_t>& pro // called by add_vote, already protected by mutex bool pending_quorum_certificate::add_weak_vote(const std::vector<uint8_t>& 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; - size_t weak = num_weak(); - size_t strong = num_strong(); + _weak_sum += weight; switch (_state) { case state_t::unrestricted: case state_t::restricted: - if (weak + strong >= _quorum) + if (_weak_sum + _strong_sum >= _quorum) _state = state_t::weak_achieved; - if (weak > (_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) @@ -134,7 +112,7 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector<uint8_t>& propo break; case state_t::weak_achieved: - if (weak >= (_num_finalizers - _quorum)) + if (_weak_sum >= _max_weak_sum_before_weak_final) _state = state_t::weak_final; break; @@ -148,10 +126,11 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector<uint8_t>& propo // thread safe, <valid, strong> std::pair<bool, bool> pending_quorum_certificate::add_vote(bool strong, const std::vector<uint8_t>& 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 592f225013..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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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<uint8_t>& 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); // 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); // 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); // 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, 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, 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, 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, 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 89c75d1994..60a26a755d 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/finalizer_policy.hpp @@ -9,6 +9,17 @@ namespace eosio::chain { uint32_t generation = 0; ///< sequentially incrementing version number uint64_t threshold = 0; ///< vote weight threshold to finalize blocks std::vector<finalizer_authority> finalizers; ///< Instant Finality voter set + + // max accumulated weak weight before becoming weak_final + uint64_t max_weak_sum_before_weak_final() const { + uint64_t sum = std::accumulate( finalizers.begin(), finalizers.end(), 0, + [](uint64_t acc, const finalizer_authority& f) { + return acc + f.weight; + } + ); + + return (sum - threshold); + } }; using finalizer_policy_ptr = std::shared_ptr<finalizer_policy>; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 1e58b5e5f6..7f3b21e653 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -185,25 +185,18 @@ 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, 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, size_t quorum); - // thread safe std::pair<bool, bool> add_vote(bool strong, const std::vector<uint8_t>&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; @@ -221,28 +214,28 @@ namespace eosio::chain { friend class qc_chain; fc::sha256 _proposal_id; // only used in to_msg(). Remove eventually std::vector<uint8_t> _proposal_digest; + std::unique_ptr<std::mutex> _mtx; + uint64_t _quorum {0}; + uint64_t _max_weak_sum_before_weak_final {0}; // max weak sum before becoming weak_final state_t _state { state_t::unrestricted }; - size_t _num_finalizers {0}; - size_t _quorum {0}; - std::unique_ptr<std::mutex> _mtx; // protect both _strong_votes and _weak_votes + 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; - // 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<uint8_t>& 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<uint8_t>& 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 8d962ffc22..eb1295e41a 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<finalizer_policy>( 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, 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<block_state>(); bsp->active_finalizer_policy = std::make_shared<finalizer_policy>( 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, 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<block_state>(); bsp->active_finalizer_policy = std::make_shared<finalizer_policy>( 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, 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<block_state>(); bsp->active_finalizer_policy = std::make_shared<finalizer_policy>( 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, 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() }; @@ -88,6 +88,101 @@ BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { } } FC_LOG_AND_RETHROW(); +void do_quorum_test(const std::vector<uint64_t>& weights, + uint64_t threshold, + bool strong, + const std::vector<bool>& 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<uint8_t> strong_digest_data(strong_digest.data(), strong_digest.data() + strong_digest.data_size()); + digest_type weak_digest(fc::sha256("0000000000000000000000000000003")); + std::vector<uint8_t> weak_digest_data(weak_digest.data(), weak_digest.data() + weak_digest.data_size()); + + // initialize a set of private keys + std::vector<bls_private_key> 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<bls_public_key> public_key(num_finalizers); + std::vector<finalizer_authority> 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<block_state>(); + constexpr uint32_t generation = 1; + bsp->active_finalizer_policy = std::make_shared<finalizer_policy>( generation, threshold, finalizers ); + bsp->strong_digest = strong_digest; + bsp->weak_digest = weak_digest; + 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] ) { + 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<uint64_t> weights{1, 3, 5}; + constexpr uint64_t threshold = 4; + + { // 1 strong vote, quorum not met + constexpr bool strong = true; + std::vector<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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<bool> 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_CASE(verify_qc_test) try { using namespace eosio::chain; using namespace fc::crypto::blslib; @@ -105,7 +200,7 @@ BOOST_AUTO_TEST_CASE(verify_qc_test) try { bls_private_key("PVT_BLS_3FoY73Q/gED3ejyg8cvnGqHrMmx4cLKwh/e0sbcsCxpCeqn3") }; auto num_finalizers = private_key.size(); - + // construct finalizers, with weight 1, 2, 3 respectively std::vector<bls_public_key> public_key(num_finalizers); std::vector<finalizer_authority> finalizers(num_finalizers); @@ -114,7 +209,7 @@ BOOST_AUTO_TEST_CASE(verify_qc_test) try { uint64_t weight = i + 1; finalizers[i] = finalizer_authority{ "test", weight, public_key[i] }; } - + // consturct a test bsp block_state_ptr bsp = std::make_shared<block_state>(); constexpr uint32_t generation = 1;