diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index ddb53e5cee..a1cfd65670 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -57,6 +57,28 @@ void block_state::set_trxs_metas( deque&& trxs_metas, cached_trxs = std::move( trxs_metas ); } +// Called from net threads +bool block_state::aggregate_vote(const hs_vote_message& vote) { + const auto& finalizers = active_finalizer_policy->finalizers; + auto it = std::find_if(finalizers.begin(), + finalizers.end(), + [&](const auto& finalizer) { return finalizer.public_key == vote.finalizer_key; }); + + if (it != finalizers.end()) { + auto index = std::distance(finalizers.begin(), it); + const digest_type& digest = vote.strong ? strong_digest : weak_digest; + return pending_qc.add_vote(vote.strong, +#warning TODO change to use std::span if possible + std::vector{digest.data(), digest.data() + digest.data_size()}, + index, + vote.finalizer_key, + vote.sig); + } else { + wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) ); + return false; + } +} + std::optional block_state::get_best_qc() const { auto block_number = block_num(); @@ -69,9 +91,8 @@ std::optional block_state::get_best_qc() const { } } -#warning TODO valid_quorum_certificate constructor can assert. Implement an extract method in pending_quorum_certificate for this. // extract valid QC from pending_qc - valid_quorum_certificate valid_qc_from_pending(pending_qc); + valid_quorum_certificate valid_qc_from_pending = pending_qc.to_valid_quorum_certificate(); // if valid_qc does not have value, consider valid_qc_from_pending only if( !valid_qc ) { diff --git a/libraries/chain/controller.cpp b/libraries/chain/controller.cpp index a8cce174bb..83c7777f61 100644 --- a/libraries/chain/controller.cpp +++ b/libraries/chain/controller.cpp @@ -301,6 +301,20 @@ struct block_data_t { }, v); } + // called from net thread + bool aggregate_vote(const hs_vote_message& vote) { + return std::visit( + overloaded{[](const block_data_legacy_t&) { + // We can be late in switching to Instant Finality + // and receive votes from those already having switched. + return false; }, + [&](const block_data_new_t& bd) { + auto bsp = bd.fork_db.get_block(vote.proposal_id); + return bsp && bsp->aggregate_vote(vote); } + }, + v); + } + signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const { return std::visit([&](const auto& bd) -> signed_block_ptr { auto bsp = bd.fork_db.search_on_branch(bd.fork_db.head()->id(), block_num); @@ -4194,8 +4208,8 @@ void controller::get_finalizer_state( finalizer_state& fs ) const { } // called from net threads -void controller::notify_hs_message( const uint32_t connection_id, const hs_message& msg ) { - my->pacemaker->on_hs_msg(connection_id, msg); +bool controller::process_vote_message( const hs_vote_message& vote ) { + return my->block_data.aggregate_vote(vote); }; const producer_authority_schedule& controller::active_producers()const { diff --git a/libraries/chain/hotstuff/hotstuff.cpp b/libraries/chain/hotstuff/hotstuff.cpp index c9a498c9ad..200331842e 100644 --- a/libraries/chain/hotstuff/hotstuff.cpp +++ b/libraries/chain/hotstuff/hotstuff.cpp @@ -22,10 +22,13 @@ inline std::vector bitset_to_vector(const hs_bitset& bs) { bool pending_quorum_certificate::votes_t::add_vote(const std::vector& proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& new_sig) { - if (_bitset[index]) + if (_bitset[index]) { return false; // shouldn't be already present - if (!fc::crypto::blslib::verify(pubkey, proposal_digest, new_sig)) + } + if (!fc::crypto::blslib::verify(pubkey, proposal_digest, new_sig)) { + wlog( "signature from finalizer ${i} cannot be verified", ("i", index) ); return false; + } _bitset.set(index); _sig = fc::crypto::blslib::aggregate({_sig, new_sig}); // works even if _sig is default initialized (fp2::zero()) return true; @@ -38,9 +41,14 @@ void pending_quorum_certificate::votes_t::reset(size_t num_finalizers) { _sig = bls_signature(); } +pending_quorum_certificate::pending_quorum_certificate() + : _mtx(std::make_unique()) { +} + pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, size_t quorum) : _num_finalizers(num_finalizers) - , _quorum(quorum) { + , _quorum(quorum) + , _mtx(std::make_unique()) { _weak_votes.resize(num_finalizers); _strong_votes.resize(num_finalizers); } @@ -54,11 +62,13 @@ pending_quorum_certificate::pending_quorum_certificate(const fc::sha256& propos } 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; @@ -68,6 +78,7 @@ void pending_quorum_certificate::reset(const fc::sha256& proposal_id, const dige _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); @@ -99,6 +110,7 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector& pro return true; } +// 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); @@ -134,12 +146,35 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector& propo return true; } +// thread safe bool pending_quorum_certificate::add_vote(bool strong, const std::vector& proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& sig) { + std::lock_guard g(*_mtx); return strong ? add_strong_vote(proposal_digest, index, pubkey, sig) : add_weak_vote(proposal_digest, index, pubkey, sig); } +// thread safe +valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate() const { + std::lock_guard g(*_mtx); + + valid_quorum_certificate valid_qc; + + valid_qc._proposal_id = _proposal_id; + valid_qc._proposal_digest = _proposal_digest; + if( _state == state_t::strong ) { + valid_qc._strong_votes = _strong_votes._bitset; + valid_qc._sig = _strong_votes._sig; + } else if (is_quorum_met()) { + valid_qc._strong_votes = _strong_votes._bitset; + valid_qc._weak_votes = _weak_votes._bitset; + valid_qc._sig = fc::crypto::blslib::aggregate({_strong_votes._sig, _weak_votes._sig}); + } else + assert(0); // this should be called only when we have a valid qc. + + return valid_qc; +} + // ================== begin compatibility functions ======================= // these are present just to make the tests still work. will be removed. // these assume *only* strong votes. @@ -155,21 +190,6 @@ std::string pending_quorum_certificate::get_votes_string() const { } // ================== end compatibility functions ======================= - -valid_quorum_certificate::valid_quorum_certificate(const pending_quorum_certificate& qc) - : _proposal_id(qc._proposal_id) - , _proposal_digest(qc._proposal_digest) { - if (qc._state == pending_quorum_certificate::state_t::strong) { - _strong_votes = qc._strong_votes._bitset; - _sig = qc._strong_votes._sig; - } else if (qc.is_quorum_met()) { - _strong_votes = qc._strong_votes._bitset; - _weak_votes = qc._weak_votes._bitset; - _sig = fc::crypto::blslib::aggregate({qc._strong_votes._sig, qc._weak_votes._sig}); - } else - assert(0); // this should be called only when we have a valid qc. -} - valid_quorum_certificate::valid_quorum_certificate( const fc::sha256& proposal_id, const std::vector& proposal_digest, const std::vector& strong_votes, // bitset encoding, following canonical order diff --git a/libraries/chain/hotstuff/qc_chain.cpp b/libraries/chain/hotstuff/qc_chain.cpp index 2336fb7c75..8315901810 100644 --- a/libraries/chain/hotstuff/qc_chain.cpp +++ b/libraries/chain/hotstuff/qc_chain.cpp @@ -372,7 +372,7 @@ namespace eosio::chain { ("id", _id)); //fc_tlog(_logger, " === update_high_qc : _current_qc ==="); - update_high_qc(_current_qc); + update_high_qc(_current_qc.to_valid_quorum_certificate()); fc_dlog(_logger, " === ${id} quorum met on #${block_num} ${phase_counter} ${proposal_id} ", ("block_num", p->block_num()) ("phase_counter", p->phase_counter) diff --git a/libraries/chain/hotstuff/test/hotstuff_tools.cpp b/libraries/chain/hotstuff/test/hotstuff_tools.cpp index 49326990ec..1ecb2bbec4 100644 --- a/libraries/chain/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/chain/hotstuff/test/hotstuff_tools.cpp @@ -100,67 +100,67 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { pubkey.push_back(k.get_public_key()); auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index) { - return qc.add_weak_vote(digest, index, pubkey[index], sk[index].sign(digest)); + return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest)); }; auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector& digest, size_t index) { - return qc.add_strong_vote(digest, index, pubkey[index], sk[index].sign(digest)); + return qc.add_vote(true, digest, index, pubkey[index], sk[index].sign(digest)); }; { pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 - BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add one weak vote // ----------------- weak_vote(qc, digest, 0); - BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved); + 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); BOOST_CHECK(!ok); // vote was a duplicate - BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); // add another weak vote // --------------------- weak_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::weak_final); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); } { pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 - BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a weak vote // --------------- weak_vote(qc, digest, 0); - BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); // add a strong vote // ----------------- strong_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::strong); + BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); } { pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1 - BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); // add a strong vote // ----------------- strong_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::strong); + BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); // add a strong vote // ----------------- strong_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::strong); + BOOST_CHECK_EQUAL(qc.state(), state_t::strong); BOOST_CHECK(qc.is_quorum_met()); } @@ -170,32 +170,48 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a weak vote // --------------- weak_vote(qc, digest, 0); - BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a strong vote // ----------------- strong_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); BOOST_CHECK(qc.is_quorum_met()); { - pending_quorum_certificate qc2(qc); + pending_quorum_certificate qc2(std::move(qc)); // add a weak vote // --------------- weak_vote(qc2, digest, 2); - BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final); + 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 + + // add a weak vote + // --------------- + weak_vote(qc, digest, 0); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); + BOOST_CHECK(!qc.is_quorum_met()); + + // add a strong vote + // ----------------- + strong_vote(qc, digest, 1); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved); + BOOST_CHECK(qc.is_quorum_met()); { - pending_quorum_certificate qc2(qc); + pending_quorum_certificate qc2(std::move(qc)); // add a strong vote // ----------------- strong_vote(qc2, digest, 2); - BOOST_CHECK_EQUAL(qc2._state, state_t::strong); + BOOST_CHECK_EQUAL(qc2.state(), state_t::strong); BOOST_CHECK(qc2.is_quorum_met()); } } @@ -206,32 +222,48 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try { // add a weak vote // --------------- weak_vote(qc, digest, 0); - BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); BOOST_CHECK(!qc.is_quorum_met()); // add a weak vote // --------------- weak_vote(qc, digest, 1); - BOOST_CHECK_EQUAL(qc._state, state_t::weak_final); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); BOOST_CHECK(qc.is_quorum_met()); { - pending_quorum_certificate qc2(qc); + pending_quorum_certificate qc2(std::move(qc)); // add a weak vote // --------------- weak_vote(qc2, digest, 2); - BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final); + 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 + + // add a weak vote + // --------------- + weak_vote(qc, digest, 0); + BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted); + BOOST_CHECK(!qc.is_quorum_met()); + + // add a weak vote + // --------------- + weak_vote(qc, digest, 1); + BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final); + BOOST_CHECK(qc.is_quorum_met()); { - pending_quorum_certificate qc2(qc); + pending_quorum_certificate qc2(std::move(qc)); // add a strong vote // ----------------- strong_vote(qc2, digest, 2); - BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final); + BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final); BOOST_CHECK(qc2.is_quorum_met()); } } diff --git a/libraries/chain/include/eosio/chain/block_state.hpp b/libraries/chain/include/eosio/chain/block_state.hpp index aeb8298d9c..844b3b9b35 100644 --- a/libraries/chain/include/eosio/chain/block_state.hpp +++ b/libraries/chain/include/eosio/chain/block_state.hpp @@ -17,7 +17,7 @@ struct block_state : public block_header_state { // block_header_state provi digest_type weak_digest; // finalizer_digest (weak, cached so we can quickly validate votes) pending_quorum_certificate pending_qc; // where we accumulate votes we receive std::optional valid_qc; // best qc received from the network inside block extension - + // ------ data members caching information available elsewhere ---------------------- bool pub_keys_recovered = false; deque cached_trxs; @@ -32,12 +32,13 @@ struct block_state : public block_header_state { // block_header_state provi void set_valid(bool b) { validated = b; } uint32_t irreversible_blocknum() const { return 0; } // [greg todo] equivalent of dpos_irreversible_blocknum std::optional get_best_qc() const; - + protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; } bool is_pub_keys_recovered() const { return pub_keys_recovered; } deque extract_trxs_metas(); void set_trxs_metas(deque&& trxs_metas, bool keys_recovered); const deque& trxs_metas() const { return cached_trxs; } + bool aggregate_vote(const hs_vote_message& vote); // aggregate vote into pending_qc using bhs_t = block_header_state; using bhsp_t = block_header_state_ptr; diff --git a/libraries/chain/include/eosio/chain/controller.hpp b/libraries/chain/include/eosio/chain/controller.hpp index 4b726a3a2d..df01872a5b 100644 --- a/libraries/chain/include/eosio/chain/controller.hpp +++ b/libraries/chain/include/eosio/chain/controller.hpp @@ -330,7 +330,7 @@ namespace eosio::chain { void set_proposed_finalizers( const finalizer_policy& fin_set ); void get_finalizer_state( finalizer_state& fs ) const; // called from net threads - void notify_hs_message( const uint32_t connection_id, const hs_message& msg ); + bool process_vote_message( const hs_vote_message& msg ); bool light_validation_allowed() const; bool skip_auth_check()const; diff --git a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp index 88bf7eb530..1d1b6e57bb 100644 --- a/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff/hotstuff.hpp @@ -7,6 +7,8 @@ #include +#include + namespace eosio::chain { using hs_bitset = boost::dynamic_bitset; @@ -120,6 +122,43 @@ namespace eosio::chain { using bls_signature = fc::crypto::blslib::bls_signature; using bls_private_key = fc::crypto::blslib::bls_private_key; + // -------------------- valid_quorum_certificate ------------------------------------------------- + class valid_quorum_certificate { + public: + valid_quorum_certificate(const fc::sha256& proposal_id, + const std::vector& proposal_digest, + const std::vector& strong_votes, //bitset encoding, following canonical order + const std::vector& weak_votes, //bitset encoding, following canonical order + const bls_signature& sig); + + valid_quorum_certificate() = default; + valid_quorum_certificate(const valid_quorum_certificate&) = default; + + bool is_weak() const { return !!_weak_votes; } + bool is_strong() const { return !_weak_votes; } + + // ================== begin compatibility functions ======================= + // these are present just to make the tests still work. will be removed. + // these assume *only* strong votes. + quorum_certificate_message to_msg() const; + const fc::sha256& get_proposal_id() const { return _proposal_id; } + // ================== end compatibility functions ======================= + + friend struct fc::reflector; + fc::sha256 _proposal_id; // [todo] remove + std::vector _proposal_digest; // [todo] remove + std::optional _strong_votes; + std::optional _weak_votes; + bls_signature _sig; + }; + + // -------------------- quorum_certificate ------------------------------------------------------- + struct quorum_certificate { + uint32_t block_height; + valid_quorum_certificate qc; + }; + + // -------------------- pending_quorum_certificate ------------------------------------------------- class pending_quorum_certificate { public: @@ -144,7 +183,7 @@ namespace eosio::chain { void reset(size_t num_finalizers); }; - pending_quorum_certificate() = default; + pending_quorum_certificate(); explicit pending_quorum_certificate(size_t num_finalizers, size_t quorum); @@ -153,29 +192,22 @@ namespace eosio::chain { size_t num_finalizers, size_t quorum); - size_t num_weak() const { return _weak_votes.count(); } - size_t num_strong() const { return _strong_votes.count(); } - + // 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); - bool add_strong_vote(const std::vector& proposal_digest, - size_t index, - const bls_public_key& pubkey, - const bls_signature& sig); - - bool add_weak_vote(const std::vector& proposal_digest, - size_t index, - const bls_public_key& pubkey, - const bls_signature& sig); - + // thread safe bool add_vote(bool strong, const std::vector& proposal_digest, size_t index, const bls_public_key& pubkey, const bls_signature& sig); + state_t state() const { std::lock_guard g(*_mtx); return _state; }; + valid_quorum_certificate to_valid_quorum_certificate() const; + // ================== begin compatibility functions ======================= // these are present just to make the tests still work. will be removed. // these assume *only* strong votes. @@ -184,54 +216,34 @@ namespace eosio::chain { std::string get_votes_string() const; // ================== end compatibility functions ======================= + private: friend struct fc::reflector; + 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}; size_t _quorum {0}; + std::unique_ptr _mtx; // protect both _strong_votes and _weak_votes votes_t _weak_votes; votes_t _strong_votes; - }; - - // -------------------- valid_quorum_certificate ------------------------------------------------- - class valid_quorum_certificate { - public: - valid_quorum_certificate(const pending_quorum_certificate& qc); - - valid_quorum_certificate(const fc::sha256& proposal_id, - const std::vector& proposal_digest, - const std::vector& strong_votes, //bitset encoding, following canonical order - const std::vector& weak_votes, //bitset encoding, following canonical order - const bls_signature& sig); - - valid_quorum_certificate() = default; - valid_quorum_certificate(const valid_quorum_certificate&) = default; - - bool is_weak() const { return !!_weak_votes; } - bool is_strong() const { return !_weak_votes; } - // ================== begin compatibility functions ======================= - // these are present just to make the tests still work. will be removed. - // these assume *only* strong votes. - quorum_certificate_message to_msg() const; - const fc::sha256& get_proposal_id() const { return _proposal_id; } - // ================== end compatibility functions ======================= + // 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(); } - friend struct fc::reflector; - fc::sha256 _proposal_id; // [todo] remove - std::vector _proposal_digest; // [todo] remove - std::optional _strong_votes; - std::optional _weak_votes; - bls_signature _sig; - }; + // called by add_vote, already protected by mutex + bool add_strong_vote(const std::vector& proposal_digest, + size_t index, + const bls_public_key& pubkey, + const bls_signature& sig); - // -------------------- quorum_certificate ------------------------------------------------------- - struct quorum_certificate { - uint32_t block_height; - valid_quorum_certificate qc; + // 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); }; - } //eosio::chain diff --git a/plugins/net_plugin/net_plugin.cpp b/plugins/net_plugin/net_plugin.cpp index 7452479497..9665397071 100644 --- a/plugins/net_plugin/net_plugin.cpp +++ b/plugins/net_plugin/net_plugin.cpp @@ -1095,7 +1095,7 @@ namespace eosio { void handle_message( const block_id_type& id, signed_block_ptr ptr ); void handle_message( const packed_transaction& msg ) = delete; // packed_transaction_ptr overload used instead void handle_message( packed_transaction_ptr trx ); - void handle_message( const chain::hs_message& msg ); + void handle_message( const chain::hs_vote_message& msg ); // returns calculated number of blocks combined latency uint32_t calc_block_latency(); @@ -1177,9 +1177,9 @@ namespace eosio { c->handle_message( msg ); } - void operator()( const chain::hs_message& msg ) const { + void operator()( const chain::hs_vote_message& msg ) const { // continue call to handle_message on connection strand - peer_dlog( c, "handle hs_message" ); + peer_dlog( c, "handle hs_vote_message" ); c->handle_message( msg ); } }; @@ -3673,10 +3673,14 @@ namespace eosio { } } - void connection::handle_message( const chain::hs_message& msg ) { - peer_dlog(this, "received hs: ${msg}", ("msg", msg)); + void connection::handle_message( const chain::hs_vote_message& msg ) { + peer_dlog(this, "received vote: ${msg}", ("msg", msg)); controller& cc = my_impl->chain_plug->chain(); - cc.notify_hs_message(connection_id, msg); + if( cc.process_vote_message(msg) ) { +#warning TDDO remove hs_message + hs_message hs_msg{msg}; + my_impl->bcast_hs_message(connection_id, hs_msg); + } } size_t calc_trx_size( const packed_transaction_ptr& trx ) { diff --git a/unittests/block_state_tests.cpp b/unittests/block_state_tests.cpp new file mode 100644 index 0000000000..28ad30258c --- /dev/null +++ b/unittests/block_state_tests.cpp @@ -0,0 +1,90 @@ +#include + +#include +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(block_state_tests) + +BOOST_AUTO_TEST_CASE(aggregate_vote_test) try { + 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()); + + const size_t num_finalizers = 3; + + // 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"), + }; + + // 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", 1, public_key[i] }; + } + + { // all finalizers can aggregate votes + block_state_ptr bsp = std::make_shared(); + 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 }; + + for (size_t i = 0; i < num_finalizers; ++i) { + bool strong = (i % 2 == 0); // alternate strong and weak + auto sig = strong ? private_key[i].sign(strong_digest_data) : private_key[i].sign(weak_digest_data); + hs_vote_message vote{ block_id, strong, public_key[i], sig }; + BOOST_REQUIRE(bsp->aggregate_vote(vote)); + } + } + + { // public and private keys mismatched + 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 }; + + hs_vote_message vote {block_id, true, public_key[0], private_key[1].sign(strong_digest_data) }; + BOOST_REQUIRE(!bsp->aggregate_vote(vote)); + } + + { // duplicate votes + 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 }; + + hs_vote_message vote {block_id, true, public_key[0], private_key[0].sign(strong_digest_data) }; + BOOST_REQUIRE(bsp->aggregate_vote(vote)); + BOOST_REQUIRE(!bsp->aggregate_vote(vote)); + } + + { // public key does not exit in finalizer set + 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 }; + + bls_private_key new_private_key{ "PVT_BLS_warwI76e+pPX9wLFZKPFagngeFM8bm6J8D5w0iiHpxW7PiId" }; + bls_public_key new_public_key{ new_private_key.get_public_key() }; + + hs_vote_message vote {block_id, true, new_public_key, private_key[0].sign(strong_digest_data) }; + BOOST_REQUIRE(!bsp->aggregate_vote(vote)); + } +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_SUITE_END()