diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 27fb8491f6..8d31a4a317 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -10,7 +10,7 @@ namespace eosio::chain { - using hs_bitset = boost::dynamic_bitset; + using hs_bitset = boost::dynamic_bitset; using bls_key_map_t = std::map; inline uint64_t compute_height(uint32_t block_height, uint32_t phase_counter) { @@ -30,7 +30,7 @@ namespace eosio::chain { struct hs_vote_message { fc::sha256 proposal_id; //vote on proposal - name finalizer; + fc::crypto::blslib::bls_public_key finalizer_key; fc::crypto::blslib::bls_signature sig; }; @@ -91,7 +91,7 @@ namespace eosio::chain { // // @ignore quorum_met FC_REFLECT(eosio::chain::quorum_certificate_message, (proposal_id)(active_finalizers)(active_agg_sig)); FC_REFLECT(eosio::chain::extended_schedule, (producer_schedule)(bls_pub_keys)); -FC_REFLECT(eosio::chain::hs_vote_message, (proposal_id)(finalizer)(sig)); +FC_REFLECT(eosio::chain::hs_vote_message, (proposal_id)(finalizer_key)(sig)); FC_REFLECT(eosio::chain::hs_proposal_message, (proposal_id)(block_id)(parent_id)(final_on_qc)(justify)(phase_counter)); FC_REFLECT(eosio::chain::hs_new_block_message, (block_id)(justify)); FC_REFLECT(eosio::chain::hs_new_view_message, (high_qc)); diff --git a/libraries/hotstuff/chain_pacemaker.cpp b/libraries/hotstuff/chain_pacemaker.cpp index e034d9b1fc..c270801b8c 100644 --- a/libraries/hotstuff/chain_pacemaker.cpp +++ b/libraries/hotstuff/chain_pacemaker.cpp @@ -106,7 +106,7 @@ namespace eosio { namespace hotstuff { bls_key_map_t finalizer_keys, fc::logger& logger) : _chain(chain), - _qc_chain("default"_n, this, std::move(my_producers), std::move(finalizer_keys), logger), + _qc_chain("default", this, std::move(my_producers), std::move(finalizer_keys), logger), _logger(logger) { _accepted_block_connection = chain->accepted_block.connect( [this]( const block_state_ptr& blk ) { @@ -266,23 +266,8 @@ namespace eosio { namespace hotstuff { return n; } - std::vector chain_pacemaker::get_finalizers() { - -#warning FIXME: Use _active_finalizer_set in pacemaker/qc_chain. - // _active_finalizer_set should be used - - std::unique_lock g( _chain_state_mutex ); - block_state_ptr hbs = _head_block_state; - g.unlock(); - - // Old code: get eosio::name from the producer schedule - const std::vector& pa_list = hbs->active_schedule.producers; - std::vector pn_list; - pn_list.reserve(pa_list.size()); - std::transform(pa_list.begin(), pa_list.end(), - std::back_inserter(pn_list), - [](const producer_authority& p) { return p.producer_name; }); - return pn_list; + const finalizer_set& chain_pacemaker::get_finalizer_set(){ + return _active_finalizer_set; } block_id_type chain_pacemaker::get_current_block_id() { @@ -303,24 +288,25 @@ namespace eosio { namespace hotstuff { prof.core_out(); } - void chain_pacemaker::send_hs_proposal_msg(const hs_proposal_message& msg, name id, const std::optional& exclude_peer) { + void chain_pacemaker::send_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer) { bcast_hs_message(exclude_peer, msg); } - void chain_pacemaker::send_hs_vote_msg(const hs_vote_message& msg, name id, const std::optional& exclude_peer) { + void chain_pacemaker::send_hs_vote_msg(const hs_vote_message& msg, const std::string& id, const std::optional& exclude_peer) { bcast_hs_message(exclude_peer, msg); } - void chain_pacemaker::send_hs_new_block_msg(const hs_new_block_message& msg, name id, const std::optional& exclude_peer) { + void chain_pacemaker::send_hs_new_block_msg(const hs_new_block_message& msg, const std::string& id, const std::optional& exclude_peer) { bcast_hs_message(exclude_peer, msg); } - void chain_pacemaker::send_hs_new_view_msg(const hs_new_view_message& msg, name id, const std::optional& exclude_peer) { + void chain_pacemaker::send_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer) { bcast_hs_message(exclude_peer, msg); } void chain_pacemaker::send_hs_message_warning(const uint32_t sender_peer, const chain::hs_message_warning code) { warn_hs_message(sender_peer, code); + } // called from net threads diff --git a/libraries/hotstuff/include/eosio/hotstuff/base_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/base_pacemaker.hpp index 98339ef58b..222c6deabc 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/base_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/base_pacemaker.hpp @@ -4,6 +4,10 @@ #include #include +#include + +#include + #include namespace eosio::hotstuff { @@ -27,15 +31,18 @@ namespace eosio::hotstuff { virtual chain::name get_proposer() = 0; virtual chain::name get_leader() = 0; virtual chain::name get_next_leader() = 0; - virtual std::vector get_finalizers() = 0; + virtual const eosio::chain::finalizer_set& get_finalizer_set() = 0; + //outbound communications; 'id' is the producer name (can be ignored if/when irrelevant to the implementer) - virtual void send_hs_proposal_msg(const chain::hs_proposal_message& msg, chain::name id, const std::optional& exclude_peer = std::nullopt) = 0; - virtual void send_hs_vote_msg(const chain::hs_vote_message& msg, chain::name id, const std::optional& exclude_peer = std::nullopt) = 0; - virtual void send_hs_new_view_msg(const chain::hs_new_view_message& msg, chain::name id, const std::optional& exclude_peer = std::nullopt) = 0; - virtual void send_hs_new_block_msg(const chain::hs_new_block_message& msg, chain::name id, const std::optional& exclude_peer = std::nullopt) = 0; + + virtual void send_hs_proposal_msg(const chain::hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; + virtual void send_hs_vote_msg(const chain::hs_vote_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; + virtual void send_hs_new_view_msg(const chain::hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; + virtual void send_hs_new_block_msg(const chain::hs_new_block_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; virtual void send_hs_message_warning(const uint32_t sender_peer, const chain::hs_message_warning code) = 0; + }; } // namespace eosio::hotstuff diff --git a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp index 64bff0dd07..4cc274c828 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp @@ -3,8 +3,6 @@ #include #include -#include - #include #include @@ -38,16 +36,16 @@ namespace eosio::hotstuff { name get_proposer(); name get_leader() ; name get_next_leader() ; - std::vector get_finalizers(); + const finalizer_set& get_finalizer_set(); block_id_type get_current_block_id(); uint32_t get_quorum_threshold(); - void send_hs_proposal_msg(const hs_proposal_message& msg, name id, const std::optional& exclude_peer); - void send_hs_vote_msg(const hs_vote_message& msg, name id, const std::optional& exclude_peer); - void send_hs_new_view_msg(const hs_new_view_message& msg, name id, const std::optional& exclude_peer); - void send_hs_new_block_msg(const hs_new_block_message& msg, name id, const std::optional& exclude_peer); + void send_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_vote_msg(const hs_vote_message& msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_new_block_msg(const hs_new_block_message& msg, const std::string& id, const std::optional& exclude_peer); void send_hs_message_warning(const uint32_t sender_peer, const chain::hs_message_warning code); diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index 6169b1efa5..5f388cdc47 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -5,6 +5,9 @@ #include #include +#include +#include + #include #include @@ -18,6 +21,7 @@ #include + #include #include @@ -36,10 +40,11 @@ namespace eosio::hotstuff { active_finalizers.resize(finalizer_size); } - explicit quorum_certificate(const quorum_certificate_message& msg) + explicit quorum_certificate(const quorum_certificate_message& msg, size_t finalizer_count) : proposal_id(msg.proposal_id) , active_finalizers(msg.active_finalizers.cbegin(), msg.active_finalizers.cend()) , active_agg_sig(msg.active_agg_sig) { + active_finalizers.resize(finalizer_count); } quorum_certificate_message to_msg() const { @@ -95,14 +100,14 @@ namespace eosio::hotstuff { qc_chain() = delete; - qc_chain(name id, base_pacemaker* pacemaker, + qc_chain(std::string id, base_pacemaker* pacemaker, std::set my_producers, chain::bls_key_map_t finalizer_keys, fc::logger& logger); uint64_t get_state_version() const { return _state_version; } // no lock required - name get_id_i() const { return _id; } // only for testing + const std::string& get_id_i() const { return _id; } // so far, only ever relevant in a test environment and for logging (no sync) // Calls to the following methods should be thread-synchronized externally: @@ -124,18 +129,17 @@ namespace eosio::hotstuff { uint32_t positive_bits_count(const hs_bitset& finalizers); - hs_bitset update_bitset(const hs_bitset& finalizer_set, name finalizer); + hs_bitset update_bitset(const hs_bitset& finalizer_set, const fc::crypto::blslib::bls_public_key& finalizer_key); //get digest to sign from proposal data digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc); void reset_qc(const fc::sha256& proposal_id); - //evaluate quorum for a proposal - bool evaluate_quorum(const extended_schedule& es, const hs_bitset& finalizers, const fc::crypto::blslib::bls_signature& agg_sig, const hs_proposal_message& proposal); + bool evaluate_quorum(const hs_bitset& finalizers, const fc::crypto::blslib::bls_signature& agg_sig, const hs_proposal_message& proposal); //evaluate quorum for a proposal - //check if quorum has been met over a proposal - bool is_quorum_met(const quorum_certificate& qc, const extended_schedule& schedule, const hs_proposal_message& proposal); + // qc.quorum_met has to be updated by the caller (if it wants to) based on the return value of this method + bool is_quorum_met(const quorum_certificate& qc, const hs_proposal_message& proposal); //check if quorum has been met over a proposal hs_proposal_message new_proposal_candidate(const block_id_type& block_id, uint8_t phase_counter); hs_new_block_message new_block_candidate(const block_id_type& block_id); @@ -150,7 +154,7 @@ namespace eosio::hotstuff { void process_new_view(const std::optional& connection_id, const hs_new_view_message& msg); void process_new_block(const std::optional& connection_id, const hs_new_block_message& msg); - hs_vote_message sign_proposal(const hs_proposal_message& proposal, name finalizer); + hs_vote_message sign_proposal(const hs_proposal_message& proposal, const fc::crypto::blslib::bls_private_key& finalizer_priv_key); //verify that a proposal descends from another bool extends(const fc::sha256& descendant, const fc::sha256& ancestor); @@ -180,15 +184,6 @@ namespace eosio::hotstuff { void gc_proposals(uint64_t cutoff); -#warning remove. bls12-381 key used for testing purposes - //todo : remove. bls12-381 key used for testing purposes - std::vector _seed = - { 0, 50, 6, 244, 24, 199, 1, 25, 52, 88, 192, - 19, 18, 12, 89, 6, 220, 18, 102, 58, 209, 82, - 12, 62, 89, 110, 182, 9, 44, 20, 254, 22 }; - - fc::crypto::blslib::bls_private_key _private_key = fc::crypto::blslib::bls_private_key(_seed); - enum msg_type { new_view = 1, new_block = 2, @@ -206,11 +201,10 @@ namespace eosio::hotstuff { quorum_certificate _high_qc; quorum_certificate _current_qc; uint32_t _v_height = 0; - eosio::chain::extended_schedule _schedule; base_pacemaker* _pacemaker = nullptr; std::set _my_producers; chain::bls_key_map_t _my_finalizer_keys; - name _id; + std::string _id; mutable std::atomic _state_version = 1; diff --git a/libraries/hotstuff/include/eosio/hotstuff/test_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/test_pacemaker.hpp index 8efbcf4d65..b093b14c84 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/test_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/test_pacemaker.hpp @@ -2,12 +2,15 @@ #include #include +//#include + namespace eosio { namespace hotstuff { class test_pacemaker : public base_pacemaker { public: - using hotstuff_message = std::pair>; + using hotstuff_message = std::pair>; + enum hotstuff_message_index { hs_proposal = 0, hs_vote = 1, @@ -18,7 +21,7 @@ namespace eosio { namespace hotstuff { //class-specific functions - bool is_qc_chain_active(const name & qcc_name) { return _qcc_deactivated.find(qcc_name) == _qcc_deactivated.end(); } + bool is_qc_chain_active(const name& qcc_name) { return _qcc_deactivated.find(qcc_name) == _qcc_deactivated.end(); } void set_proposer(name proposer); @@ -26,7 +29,7 @@ namespace eosio { namespace hotstuff { void set_next_leader(name next_leader); - void set_finalizers(const std::vector& finalizers); + void set_finalizer_set(const eosio::chain::finalizer_set& finalizer_set); void set_current_block_id(block_id_type id); @@ -34,11 +37,11 @@ namespace eosio { namespace hotstuff { void add_message_to_queue(const hotstuff_message& msg); - void connect(const std::vector& nodes); + void connect(const std::vector& nodes); - void disconnect(const std::vector& nodes); + void disconnect(const std::vector& nodes); - bool is_connected(name node1, name node2); + bool is_connected(std::string node1, std::string node2); void pipe(const std::vector& messages); @@ -56,26 +59,26 @@ namespace eosio { namespace hotstuff { void beat(); - void on_hs_vote_msg(const hs_vote_message & msg, name id); //confirmation msg event handler - void on_hs_proposal_msg(const hs_proposal_message & msg, name id); //consensus msg event handler - void on_hs_new_view_msg(const hs_new_view_message & msg, name id); //new view msg event handler - void on_hs_new_block_msg(const hs_new_block_message & msg, name id); //new block msg event handler + void on_hs_vote_msg(const hs_vote_message & msg, const std::string& id); //confirmation msg event handler + void on_hs_proposal_msg(const hs_proposal_message & msg, const std::string& id); //consensus msg event handler + void on_hs_new_view_msg(const hs_new_view_message & msg, const std::string& id); //new view msg event handler + void on_hs_new_block_msg(const hs_new_block_message & msg, const std::string& id); //new block msg event handler //base_pacemaker interface functions name get_proposer(); name get_leader(); name get_next_leader(); - std::vector get_finalizers(); + const finalizer_set& get_finalizer_set(); block_id_type get_current_block_id(); uint32_t get_quorum_threshold(); - void send_hs_proposal_msg(const hs_proposal_message & msg, name id, const std::optional& exclude_peer); - void send_hs_vote_msg(const hs_vote_message & msg, name id, const std::optional& exclude_peer); - void send_hs_new_block_msg(const hs_new_block_message & msg, name id, const std::optional& exclude_peer); - void send_hs_new_view_msg(const hs_new_view_message & msg, name id, const std::optional& exclude_peer); + void send_hs_proposal_msg(const hs_proposal_message & msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_vote_msg(const hs_vote_message & msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_new_block_msg(const hs_new_block_message & msg, const std::string& id, const std::optional& exclude_peer); + void send_hs_new_view_msg(const hs_new_view_message & msg, const std::string& id, const std::optional& exclude_peer); void send_hs_message_warning(const uint32_t sender_peer, const chain::hs_message_warning code); @@ -92,17 +95,16 @@ namespace eosio { namespace hotstuff { // network topology: key (node name) is connected to all nodes in the mapped set. // double mapping, so if _net[a] yields b, then _net[b] yields a. // this is a filter; messages to self won't happen even if _net[x] yields x. - map> _net; + map> _net; name _proposer; name _leader; name _next_leader; - std::vector _finalizers; + finalizer_set _finalizer_set; block_id_type _current_block_id; - std::vector _unique_replicas; #warning calculate from schedule uint32_t _quorum_threshold = 15; //todo : calculate from schedule }; diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 99018fb36b..4f6162bdbf 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -64,7 +64,6 @@ namespace eosio::hotstuff { fs.v_height = _v_height; fs.high_qc = _high_qc.to_msg(); fs.current_qc = _current_qc.to_msg(); - fs.schedule = _schedule; #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE ps_height_iterator psh_it = _proposal_stores_by_height.begin(); while (psh_it != _proposal_stores_by_height.end()) { @@ -91,23 +90,25 @@ namespace eosio::hotstuff { return finalizers.count(); // the number of bits in this bitset that are set. } - hs_bitset qc_chain::update_bitset(const hs_bitset& finalizer_set, name finalizer ) { + hs_bitset qc_chain::update_bitset(const hs_bitset& finalizer_set, const fc::crypto::blslib::bls_public_key& finalizer_key ) { hs_bitset b(finalizer_set ); - vector finalizers = _pacemaker->get_finalizers(); + + const auto& finalizers = _pacemaker->get_finalizer_set().finalizers; + for (size_t i = 0; i < finalizers.size();i++) { - if (finalizers[i] == finalizer) { + if (finalizers[i].public_key == finalizer_key) { b.set(i); fc_tlog(_logger, " === finalizer found ${finalizer} new value : ${value}", - ("finalizer", finalizer)("value", [&](){ std::string r; boost::to_string(b, r); return r; }())); + ("value", [&](){ std::string r; boost::to_string(b, r); return r; }())); return b; } } - fc_tlog(_logger, " *** finalizer not found ${finalizer}", - ("finalizer", finalizer)); - throw std::runtime_error("qc_chain internal error: finalizer not found"); + fc_tlog(_logger, " *** finalizer_key not found ${finalizer_key}", + ("finalizer_key", finalizer_key)); + throw std::runtime_error("qc_chain internal error: finalizer_key not found"); } digest_type qc_chain::get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ @@ -182,46 +183,25 @@ namespace eosio::hotstuff { return b; } - bool qc_chain::evaluate_quorum(const extended_schedule& es, const hs_bitset& finalizers, const fc::crypto::blslib::bls_signature& agg_sig, const hs_proposal_message& proposal) { - - + bool qc_chain::evaluate_quorum(const hs_bitset& finalizers, const fc::crypto::blslib::bls_signature& agg_sig, const hs_proposal_message& proposal) { if (positive_bits_count(finalizers) < _pacemaker->get_quorum_threshold()){ return false; } - - fc::crypto::blslib::bls_public_key agg_key; - - bool first = true; - for (hs_bitset::size_type i = 0; i < finalizers.size(); ++i) { - if (finalizers[i]){ - //adding finalizer's key to the aggregate pub key - if (first) { - first = false; - agg_key = _private_key.get_public_key(); - } else { - agg_key = fc::crypto::blslib::aggregate({agg_key, _private_key.get_public_key()}); - } - } - } -#warning fix todo - // **************************************************************************************************** - // FIXME/TODO: I removed this since it doesn't seem to be doing anything at the moment - // **************************************************************************************************** - // - //fc::crypto::blslib::bls_signature justification_agg_sig; - // - //if (proposal.justify.proposal_id != NULL_PROPOSAL_ID) justification_agg_sig = proposal.justify.active_agg_sig; + const auto& c_finalizers = _pacemaker->get_finalizer_set().finalizers; + std::vector keys; + keys.reserve(finalizers.size()); + for (hs_bitset::size_type i = 0; i < finalizers.size(); ++i) + if (finalizers[i]) + keys.push_back(c_finalizers[i].public_key); + fc::crypto::blslib::bls_public_key agg_key = fc::crypto::blslib::aggregate(keys); digest_type digest = get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); - std::vector h = std::vector(digest.data(), digest.data() + 32); - bool ok = fc::crypto::blslib::verify(agg_key, h, agg_sig); - return ok; } - bool qc_chain::is_quorum_met(const quorum_certificate& qc, const extended_schedule& schedule, const hs_proposal_message& proposal) { + bool qc_chain::is_quorum_met(const quorum_certificate& qc, const hs_proposal_message& proposal) { if (qc.is_quorum_met()) { return true; //skip evaluation if we've already verified quorum was met @@ -230,19 +210,19 @@ namespace eosio::hotstuff { fc_tlog(_logger, " === qc : ${qc}", ("qc", qc.to_msg())); // If the caller wants to update the quorum_met flag on its "qc" object, it will have to do so // based on the return value of this method, since "qc" here is const. - return evaluate_quorum(schedule, qc.get_active_finalizers(), qc.get_active_agg_sig(), proposal); + return evaluate_quorum(qc.get_active_finalizers(), qc.get_active_agg_sig(), proposal); } } - - qc_chain::qc_chain(name id, base_pacemaker* pacemaker, + qc_chain::qc_chain(std::string id, + base_pacemaker* pacemaker, std::set my_producers, bls_key_map_t finalizer_keys, fc::logger& logger) : _pacemaker(pacemaker), _my_producers(std::move(my_producers)), _my_finalizer_keys(std::move(finalizer_keys)), - _id(id), + _id(std::move(id)), _logger(logger) { _high_qc.reset({}, 21); // TODO: use active schedule size @@ -266,34 +246,28 @@ namespace eosio::hotstuff { } bool qc_chain::am_i_finalizer(){ - std::vector finalizers = _pacemaker->get_finalizers(); - auto mf_itr = _my_producers.begin(); - while(mf_itr!=_my_producers.end()){ - name n = *mf_itr; - auto prod_itr = std::find_if(finalizers.begin(), finalizers.end(), [&](const auto& f){ return f == n; }); - if (prod_itr!=finalizers.end()) return true; - mf_itr++; - } - return false; + + const auto& finalizers = _pacemaker->get_finalizer_set().finalizers; + return !_my_finalizer_keys.empty() && + std::any_of(finalizers.begin(), finalizers.end(), [&](const auto& fa) { return _my_finalizer_keys.contains(fa.public_key); }); + } - hs_vote_message qc_chain::sign_proposal(const hs_proposal_message & proposal, name finalizer){ + hs_vote_message qc_chain::sign_proposal(const hs_proposal_message& proposal, const fc::crypto::blslib::bls_private_key& finalizer_priv_key){ _v_height = proposal.get_height(); digest_type digest = get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); std::vector h = std::vector(digest.data(), digest.data() + 32); -#warning use appropriate private key for each producer - fc::crypto::blslib::bls_signature sig = _private_key.sign(h); //FIXME/TODO: use appropriate private key for each producer - hs_vote_message v_msg = {proposal.proposal_id, finalizer, sig}; + fc::crypto::blslib::bls_signature sig = finalizer_priv_key.sign(h); + + hs_vote_message v_msg = {proposal.proposal_id, finalizer_priv_key.get_public_key(), sig}; return v_msg; } void qc_chain::process_proposal(const std::optional& connection_id, const hs_proposal_message& proposal){ - //auto start = fc::time_point::now(); - if (!proposal.justify.proposal_id.empty()) { const hs_proposal_message *jp = get_proposal( proposal.justify.proposal_id ); @@ -308,7 +282,6 @@ namespace eosio::hotstuff { if (p != nullptr) { fc_elog(_logger, " *** ${id} proposal received twice : ${proposal_id}", ("id",_id)("proposal_id", proposal.proposal_id)); - if (p->justify.proposal_id != proposal.justify.proposal_id) { fc_elog(_logger, " *** ${id} two identical proposals (${proposal_id}) have different justifications : ${justify_1} vs ${justify_2}", @@ -340,7 +313,6 @@ namespace eosio::hotstuff { { const hs_proposal_message & existing_proposal = *hgt_itr; #endif - fc_elog(_logger, " *** ${id} received a different proposal at the same height (${block_num}, ${phase_counter})", ("id",_id) ("block_num", existing_proposal.block_num()) @@ -379,20 +351,16 @@ namespace eosio::hotstuff { std::vector msgs; - if (signature_required){ - - //iterate over all my finalizers and sign / broadcast for each that is in the schedule - std::vector finalizers = _pacemaker->get_finalizers(); - - auto mf_itr = _my_producers.begin(); + if (signature_required && !_my_finalizer_keys.empty()){ + //iterate over all my finalizer keys and sign / broadcast for each that is in the schedule + const auto& finalizers = _pacemaker->get_finalizer_set().finalizers; - while(mf_itr!=_my_producers.end()){ + for (const auto& i : finalizers) { + auto mfk_itr = _my_finalizer_keys.find(i.public_key); - auto prod_itr = std::find(finalizers.begin(), finalizers.end(), *mf_itr); + if (mfk_itr!=_my_finalizer_keys.end()) { - if (prod_itr!=finalizers.end()) { - - hs_vote_message v_msg = sign_proposal(proposal, *prod_itr); + hs_vote_message v_msg = sign_proposal(proposal, mfk_itr->second); fc_tlog(_logger, " === ${id} signed proposal : block_num ${block_num} phase ${phase_counter} : proposal_id ${proposal_id}", ("id", _id) @@ -400,15 +368,11 @@ namespace eosio::hotstuff { ("phase_counter", proposal.phase_counter) ("proposal_id", proposal.proposal_id)); - //send_hs_vote_msg(v_msg); msgs.push_back(v_msg); - - }; - - mf_itr++; + } } - } + } else fc_tlog(_logger, " === ${id} skipping signature on proposal : block_num ${block_num} phase ${phase_counter} : proposal_id ${proposal_id}", ("id", _id) ("block_num", proposal.block_num()) @@ -439,8 +403,8 @@ namespace eosio::hotstuff { if (!am_leader) return; - fc_tlog(_logger, " === Process vote from ${finalizer} : current bitset ${value}" , - ("finalizer", vote.finalizer)("value", _current_qc.get_active_finalizers_string())); + fc_tlog(_logger, " === Process vote from ${finalizer_key} : current bitset ${value}" , + ("finalizer_key", vote.finalizer_key)("value", _current_qc.get_active_finalizers_string())); // only leader need to take action on votes if (vote.proposal_id != _current_qc.get_proposal_id()) { send_hs_message_warning(connection_id, hs_message_warning::discarded); // example; to be tuned to actual need @@ -464,9 +428,10 @@ namespace eosio::hotstuff { const hs_bitset& finalizer_set = _current_qc.get_active_finalizers(); // if a finalizer has already aggregated a vote signature for the current QC, just discard this vote - const vector& finalizers = _pacemaker->get_finalizers(); + + const auto& finalizers = _pacemaker->get_finalizer_set().finalizers; for (size_t i=0; i& connection_id, const hs_new_view_message& msg){ fc_tlog(_logger, " === ${id} process_new_view === ${qc}", ("qc", msg.high_qc)("id", _id)); auto increment_version = fc::make_scoped_exit([this]() { ++_state_version; }); - if (!update_high_qc(quorum_certificate{msg.high_qc})) { + if (!update_high_qc(quorum_certificate{msg.high_qc, 21})) { // TODO: use active schedule size increment_version.cancel(); } } @@ -708,7 +672,7 @@ namespace eosio::hotstuff { return false; if (new_high_qc_prop->get_height() > old_high_qc_prop->get_height() - && is_quorum_met(high_qc, _schedule, *new_high_qc_prop)) + && is_quorum_met(high_qc, *new_high_qc_prop)) { // "The caller does not need this updated on their high_qc structure" -- g //high_qc.quorum_met = true; @@ -889,7 +853,7 @@ namespace eosio::hotstuff { EOS_ASSERT( b_lock != nullptr || _b_lock.empty(), chain_exception, "expected hs_proposal ${id} not found", ("id", _b_lock) ); //fc_tlog(_logger, " === update_high_qc : proposal.justify ==="); - update_high_qc(quorum_certificate{proposal.justify}); + update_high_qc(quorum_certificate{proposal.justify, 21}); // TODO: use active schedule size if (chain_length<1){ fc_dlog(_logger, " === ${id} qc chain length is 0", ("id", _id)); diff --git a/libraries/hotstuff/test/test_hotstuff.cpp b/libraries/hotstuff/test/test_hotstuff.cpp index d128cbf0a0..40a6a1f2df 100644 --- a/libraries/hotstuff/test/test_hotstuff.cpp +++ b/libraries/hotstuff/test/test_hotstuff.cpp @@ -10,7 +10,11 @@ #include #include +#include +#include +#include +#include #include using namespace eosio::hotstuff; @@ -35,56 +39,71 @@ std::vector unique_replicas { "bpp"_n, "bpq"_n, "bpr"_n, "bps"_n, "bpt"_n, "bpu"_n }; +std::vector unique_replica_keys { + "PVT_BLS_r4ZpChd87ooyzl6MIkw23k7PRX8xptp7TczLJHCIIW88h/hS", + "PVT_BLS_/l7xzXANaB+GrlTsbZEuTiSOiWTtpBoog+TZnirxUUSaAfCo", + "PVT_BLS_3FoY73Q/gED3ejyg8cvnGqHrMmx4cLKwh/e0sbcsCxpCeqn3", + "PVT_BLS_warwI76e+pPX9wLFZKPFagngeFM8bm6J8D5w0iiHpxW7PiId", + "PVT_BLS_iZFwiqdogOl9RNr1Hv1z+Rd6AwD9BIoxZcU1EPX+XFSFmm5p", + "PVT_BLS_Hmye7lyiCrdF54/nF/HRU0sY/Hrse1ls/yqojIUOVQsxXUIK", + "PVT_BLS_jglKDzpvyI+LFJ4xJG2MRylH9KiAEj//M9sgI+AM5mhLASBs", + "PVT_BLS_OWemmo0YkDNEYcMnbvAHI7qS6YIJTVBc+3LCAi9u8QmMe3V/", + "PVT_BLS_xYhEMbBy6Z4TYGha/qYaUwiwv4UVX9qNWf4ivRjAyCLCG7/G", + "PVT_BLS_ETZDiw3qd1Kpu3L5hH9fPKR4tg0meCkRUsRE2KpW8WP5SU2l", + "PVT_BLS_KuL3oMYpBrqmIMqoBIsA4UX1jYyXzn7et93J+m+ctk8FAY0I", + "PVT_BLS_bNz9W9QkxeREp966ntnUV4mN4dLOB4DNSghf2Y85o1YI+p7t", + "PVT_BLS_uP48z/V66g7wU7BwNN1xhNnZOyf3mv8yxGFT2eoIK3HLL5aw", + "PVT_BLS_/HIa+nJWSCgVNs6rZ3LUhqp1chRkxyaUxumvN3HSTAE4VIql", + "PVT_BLS_Aq4tqxG/sDEwGMZUa/Vhznc2i3B4wHNopGV3bJpTNW6FauCN", + "PVT_BLS_U3QCa0uwzeeK4w1hI2IvUzgF9+hk496LyODdvgYpUBmgZiwu", + "PVT_BLS_WyyJ26tRpjpzmwke/sGJr0YUIyB/zSNsbo/99PwDHh4pvo5V", + "PVT_BLS_t2xBqsJKO0RHQMvsIYHFpvuy+IkBrCVeZl1NxThKEwwvUbiP", + "PVT_BLS_94/Vo26YNQV1P7zWmkDnh02P0ZcPM5xQlLG3LiUCOUUgMpEi", + "PVT_BLS_uQ9ONJ/oJlj+yRIjE3tiLcoIXTMEuCwMuAFL1WUDY28N97gF", + "PVT_BLS_2qtUuz8cYjbu/shyUPxIwKrBMSSbvolv4iJJvykUMRFl4hGt"}; + fc::logger hotstuff_logger; + class hotstuff_test_handler { public: std::vector>> _qc_chains; - void initialize_qc_chains(test_pacemaker& tpm, std::vector replicas){ - + void initialize_qc_chains(test_pacemaker& tpm, std::vector replicas, std::vector replica_keys){ _qc_chains.clear(); - for (name r : replicas) { - qc_chain *qcc_ptr = new qc_chain(r, &tpm, {r}, {}, hotstuff_logger); + for (size_t i = 0 ; i < replicas.size() ; i++){ + fc::crypto::blslib::bls_private_key sk = fc::crypto::blslib::bls_private_key(replica_keys[i]); + bls_key_map_t keys{{sk.get_public_key(), sk}}; + qc_chain *qcc_ptr = new qc_chain(replica_keys[i].to_string(), &tpm, {replicas[i]}, keys, hotstuff_logger); std::shared_ptr qcc_shared_ptr(qcc_ptr); - - _qc_chains.push_back( std::make_pair(r, qcc_shared_ptr) ); - - tpm.register_qc_chain( r, qcc_shared_ptr ); + _qc_chains.push_back( std::make_pair(replicas[i], qcc_shared_ptr) ); + tpm.register_qc_chain(replicas[i], qcc_shared_ptr ); } + } void print_msgs(std::vector msgs ){ - size_t proposals_count = 0; size_t votes_count = 0; size_t new_blocks_count = 0; size_t new_views_count = 0; - auto msg_itr = msgs.begin(); - while (msg_itr!=msgs.end()){ - size_t v_index = msg_itr->second.index(); - if(v_index==0) proposals_count++; if(v_index==1) votes_count++; if(v_index==2) new_blocks_count++; if(v_index==3) new_views_count++; - msg_itr++; } - std::cout << "\n"; - std::cout << " message queue size : " << msgs.size() << "\n"; std::cout << " proposals : " << proposals_count << "\n"; std::cout << " votes : " << votes_count << "\n"; std::cout << " new_blocks : " << new_blocks_count << "\n"; std::cout << " new_views : " << new_views_count << "\n"; - std::cout << "\n"; } @@ -98,15 +117,12 @@ class hotstuff_test_handler { } void print_bp_state(name bp, std::string message) const { - std::cout << "\n"; std::cout << message; std::cout << "\n"; auto qcc_entry = std::find_if(_qc_chains.begin(), _qc_chains.end(), [&](const auto& q){ return q.first == bp; }); - qc_chain & qcc = *qcc_entry->second.get(); - finalizer_state fs; qcc.get_state(fs); const hs_proposal_message *leaf = fs.get_proposal( fs.b_leaf ); @@ -114,16 +130,13 @@ class hotstuff_test_handler { const hs_proposal_message *lock = fs.get_proposal( fs.b_lock ); const hs_proposal_message *exec = fs.get_proposal( fs.b_exec ); - if (leaf != nullptr) std::cout << " - " << bp.to_string() << " current _b_leaf is : " << fs.b_leaf.str() << " block_num : " << leaf->block_num() << ", phase : " << unsigned(leaf->phase_counter) << "\n"; + if (leaf != nullptr) std::cout << " - " << bp << " current _b_leaf is : " << fs.b_leaf.str() << " block_num : " << leaf->block_num() << ", phase : " << unsigned(leaf->phase_counter) << "\n"; else std::cout << " - No b_leaf value " << "\n"; - - if (qc != nullptr) std::cout << " - " << bp.to_string() << " current high_qc is : " << fs.high_qc.proposal_id.str() << " block_num : " << qc->block_num() << ", phase : " << unsigned(qc->phase_counter) << "\n"; + if (qc != nullptr) std::cout << " - " << bp << " current high_qc is : " << fs.high_qc.proposal_id.str() << " block_num : " << qc->block_num() << ", phase : " << unsigned(qc->phase_counter) << "\n"; else std::cout << " - No high_qc value " << "\n"; - - if (lock != nullptr) std::cout << " - " << bp.to_string() << " current _b_lock is : " << fs.b_lock.str() << " block_num : " << lock->block_num() << ", phase : " << unsigned(lock->phase_counter) << "\n"; + if (lock != nullptr) std::cout << " - " << bp << " current _b_lock is : " << fs.b_lock.str() << " block_num : " << lock->block_num() << ", phase : " << unsigned(lock->phase_counter) << "\n"; else std::cout << " - No b_lock value " << "\n"; - - if (exec != nullptr) std::cout << " - " << bp.to_string() << " current _b_exec is : " << fs.b_exec.str() << " block_num : " << exec->block_num() << ", phase : " << unsigned(exec->phase_counter) << "\n"; + if (exec != nullptr) std::cout << " - " << bp << " current _b_exec is : " << fs.b_exec.str() << " block_num : " << exec->block_num() << ", phase : " << unsigned(exec->phase_counter) << "\n"; else std::cout << " - No b_exec value " << "\n"; std::cout << "\n"; @@ -141,7 +154,6 @@ BOOST_AUTO_TEST_SUITE(hotstuff) BOOST_AUTO_TEST_CASE(hotstuff_bitset) try { boost::dynamic_bitset b( 8, 0 ); - uint32_t c = b.to_ulong(); b.flip(0); //least significant bit @@ -154,13 +166,10 @@ BOOST_AUTO_TEST_CASE(hotstuff_bitset) try { b.flip(7); //most significant bit uint32_t d = b.to_ulong(); - for (boost::dynamic_bitset<>::size_type i = 0; i < b.size(); ++i){ b.flip(i); } - uint32_t e = b.to_ulong(); - std::cout << "c : " << c << "\n"; std::cout << "d : " << d << "\n"; std::cout << "e : " << e << "\n"; @@ -168,21 +177,43 @@ BOOST_AUTO_TEST_CASE(hotstuff_bitset) try { } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_CASE(hotstuff_1) try { +static std::vector map_to_sks(std::vector keys){ + std::vector sks; + std::transform(keys.cbegin(), keys.cend(), std::back_inserter(sks), + [](std::string k) { return fc::crypto::blslib::bls_private_key(k); }); + return sks; +} + +static finalizer_set create_fs(std::vector keys){ + std::vector sks; + std::vector f_auths; + f_auths.reserve(keys.size()); + for (const auto& urk : keys){ + fc::crypto::blslib::bls_private_key sk = fc::crypto::blslib::bls_private_key(urk); + fc::crypto::blslib::bls_public_key pk = sk.get_public_key(); + sks.push_back(sk); + f_auths.push_back(eosio::chain::finalizer_authority{"" , 1 , pk}); + } + eosio::chain::finalizer_set fset; + fset.fthreshold = 15; + fset.finalizers = f_auths; + return fset; +} +BOOST_AUTO_TEST_CASE(hotstuff_1) try { //test optimistic responsiveness (3 confirmations per block) - test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; - - ht.initialize_qc_chains(tpm, unique_replicas); - + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); + + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -191,16 +222,10 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { finalizer_state fs_bpb; qcc_bpb->second->get_state(fs_bpb); - ht.print_bp_state("bpa"_n, ""); - tpm.set_current_block_id(ids[0]); //first block - tpm.beat(); //produce first block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on first block) - ht.print_bp_state("bpa"_n, ""); - qcc_bpa->second->get_state(fs_bpa); BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); @@ -208,7 +233,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on first block) - tpm.dispatch(""); //send proposal to replicas (precommit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -218,7 +242,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block) - tpm.dispatch(""); //send proposal to replicas (commit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -228,7 +251,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //propagating votes on new proposal (commitQC on first block) - tpm.dispatch(""); //send proposal to replicas (decide on first block) qcc_bpa->second->get_state(fs_bpa); @@ -252,7 +274,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); tpm.dispatch(""); //send votes on proposal (prepareQC on second block) - tpm.dispatch(""); //send proposal to replicas (precommit on second block) qcc_bpa->second->get_state(fs_bpa); @@ -262,7 +283,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on second block) - tpm.dispatch(""); //send proposal to replicas (commit on second block) qcc_bpa->second->get_state(fs_bpa); @@ -272,7 +292,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7")); tpm.dispatch(""); //propagating votes on new proposal (commitQC on second block) - tpm.dispatch(""); //send proposal to replicas (decide on second block) qcc_bpa->second->get_state(fs_bpa); @@ -295,25 +314,26 @@ BOOST_AUTO_TEST_CASE(hotstuff_1) try { BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f")); BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb")); - BOOST_CHECK_EQUAL(fs_bpa.b_finality_violation.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); + BOOST_CHECK_EQUAL(fs_bpa.b_finality_violation.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));/**/ } FC_LOG_AND_RETHROW(); + BOOST_AUTO_TEST_CASE(hotstuff_2) try { //test slower network (1 confirmation per block) - test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); - ht.initialize_qc_chains(tpm, unique_replicas); - + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -323,9 +343,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { qcc_bpb->second->get_state(fs_bpb); tpm.set_current_block_id(ids[0]); //first block - tpm.beat(); //produce first block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on first block) qcc_bpa->second->get_state(fs_bpa); @@ -335,7 +353,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on first block) - tpm.dispatch(""); //send proposal to replicas (precommit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -345,9 +362,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.set_current_block_id(ids[1]); //second block - tpm.beat(); //produce second block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on second block) qcc_bpa->second->get_state(fs_bpa); @@ -357,7 +372,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on second block) - tpm.dispatch(""); //send proposal to replicas (precommit on second block) qcc_bpa->second->get_state(fs_bpa); @@ -367,9 +381,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.set_current_block_id(ids[2]); //second block - tpm.beat(); //produce third block and associated proposal - tpm.dispatch(""); //propagating votes on new proposal (prepare on third block) qcc_bpa->second->get_state(fs_bpa); @@ -379,7 +391,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //send votes on proposal (prepareQC on third block) - tpm.dispatch(""); //propagating votes on new proposal (precommitQC on third block) qcc_bpa->second->get_state(fs_bpa); @@ -398,21 +409,21 @@ BOOST_AUTO_TEST_CASE(hotstuff_2) try { } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_CASE(hotstuff_3) try { + BOOST_AUTO_TEST_CASE(hotstuff_3) try { //test leader rotation - test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); - ht.initialize_qc_chains(tpm, unique_replicas); - + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -425,9 +436,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { qcc_bpc->second->get_state(fs_bpc); tpm.set_current_block_id(ids[0]); //first block - tpm.beat(); //produce first block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on first block) qcc_bpa->second->get_state(fs_bpa); @@ -437,7 +446,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on first block) - tpm.dispatch(""); //send proposal to replicas (precommit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -447,7 +455,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block) - tpm.dispatch(""); //send proposal to replicas (commit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -459,7 +466,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { tpm.set_next_leader("bpb"_n); //leader is set to rotate on next block tpm.dispatch(""); //propagating votes on new proposal (commitQC on first block) - tpm.dispatch(""); //send proposal to replicas (decide on first block) qcc_bpa->second->get_state(fs_bpa); @@ -469,14 +475,10 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //propagating votes on new proposal (decide on first block) - tpm.set_proposer("bpb"_n); //leader has rotated tpm.set_leader("bpb"_n); - tpm.set_current_block_id(ids[1]); //second block - tpm.beat(); //produce second block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on second block) qcc_bpb->second->get_state(fs_bpb); @@ -486,7 +488,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //send votes on proposal (prepareQC on second block) - tpm.dispatch(""); //send proposal to replicas (precommit on second block) qcc_bpb->second->get_state(fs_bpb); @@ -496,7 +497,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on second block) - tpm.dispatch(""); //send proposal to replicas (commit on second block) qcc_bpb->second->get_state(fs_bpb); @@ -506,7 +506,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); tpm.dispatch(""); //propagating votes on new proposal (commitQC on second block) - tpm.dispatch(""); //send proposal to replicas (decide on second block) qcc_bpb->second->get_state(fs_bpb); @@ -534,18 +533,18 @@ BOOST_AUTO_TEST_CASE(hotstuff_3) try { BOOST_AUTO_TEST_CASE(hotstuff_4) try { //test loss and recovery of liveness on new block - test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; - - ht.initialize_qc_chains(tpm, unique_replicas); - + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); + + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -558,9 +557,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { qcc_bpi->second->get_state(fs_bpi); tpm.set_current_block_id(ids[0]); //first block - tpm.beat(); //produce first block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on first block) qcc_bpa->second->get_state(fs_bpa); @@ -570,7 +567,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on first block) - tpm.dispatch(""); //send proposal to replicas (precommit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -580,9 +576,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block) - -//ht.print_bp_state("bpa"_n, "before deactivate"); - tpm.deactivate("bpb"_n); //loss of liveness as 7 finalizers out of 21 go offline tpm.deactivate("bpc"_n); tpm.deactivate("bpd"_n); @@ -590,7 +583,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { tpm.deactivate("bpf"_n); tpm.deactivate("bpg"_n); tpm.deactivate("bph"_n); - tpm.dispatch(""); //send proposal to replicas (commit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -600,11 +592,8 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.set_next_leader("bpi"_n); //leader is set to rotate on next block - tpm.dispatch(""); //propagating votes on new proposal (insufficient to reach quorum) -//ht.print_bp_state("bpa"_n, "before reactivate"); - qcc_bpa->second->get_state(fs_bpa); BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); @@ -621,16 +610,10 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { tpm.set_proposer("bpi"_n); tpm.set_leader("bpi"_n); - tpm.set_current_block_id(ids[1]); //second block - tpm.beat(); //produce second block and associated proposal - tpm.dispatch(""); //send proposal to replicas (prepare on second block) -//ht.print_bp_state("bpi"_n, ""); - -//ht.print_bp_state("bpa"_n, ""); qcc_bpa->second->get_state(fs_bpa); BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); @@ -638,7 +621,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on second block) - tpm.dispatch(""); //send proposal to replicas (precommit on second block) //ht.print_bp_state("bpa"_n, ""); @@ -649,7 +631,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on second block) - tpm.dispatch(""); //send proposal to replicas (commit on second block) //ht.print_bp_state("bpa"_n, ""); @@ -660,7 +641,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_4) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); tpm.dispatch(""); //propagating votes on new proposal (commitQC on second block) - tpm.dispatch(""); //send proposal to replicas (decide on second block) //ht.print_bp_state("bpa"_n, ""); @@ -719,18 +699,72 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { "bps"_n, "bpt"_n }; - std::vector replica_set_1; - std::vector replica_set_2; + std::vector honest_replica_set_keys_1 { + "PVT_BLS_/l7xzXANaB+GrlTsbZEuTiSOiWTtpBoog+TZnirxUUSaAfCo", + "PVT_BLS_iZFwiqdogOl9RNr1Hv1z+Rd6AwD9BIoxZcU1EPX+XFSFmm5p", + "PVT_BLS_OWemmo0YkDNEYcMnbvAHI7qS6YIJTVBc+3LCAi9u8QmMe3V/", + "PVT_BLS_KuL3oMYpBrqmIMqoBIsA4UX1jYyXzn7et93J+m+ctk8FAY0I", + "PVT_BLS_/HIa+nJWSCgVNs6rZ3LUhqp1chRkxyaUxumvN3HSTAE4VIql", + "PVT_BLS_WyyJ26tRpjpzmwke/sGJr0YUIyB/zSNsbo/99PwDHh4pvo5V" + }; + + std::vector honest_replica_set_keys_2 { + "PVT_BLS_r4ZpChd87ooyzl6MIkw23k7PRX8xptp7TczLJHCIIW88h/hS", + "PVT_BLS_warwI76e+pPX9wLFZKPFagngeFM8bm6J8D5w0iiHpxW7PiId", + "PVT_BLS_jglKDzpvyI+LFJ4xJG2MRylH9KiAEj//M9sgI+AM5mhLASBs", + "PVT_BLS_ETZDiw3qd1Kpu3L5hH9fPKR4tg0meCkRUsRE2KpW8WP5SU2l", + "PVT_BLS_uP48z/V66g7wU7BwNN1xhNnZOyf3mv8yxGFT2eoIK3HLL5aw", + "PVT_BLS_U3QCa0uwzeeK4w1hI2IvUzgF9+hk496LyODdvgYpUBmgZiwu" + }; + + std::vector byzantine_keys_set { + "PVT_BLS_3FoY73Q/gED3ejyg8cvnGqHrMmx4cLKwh/e0sbcsCxpCeqn3", + "PVT_BLS_Hmye7lyiCrdF54/nF/HRU0sY/Hrse1ls/yqojIUOVQsxXUIK", + "PVT_BLS_xYhEMbBy6Z4TYGha/qYaUwiwv4UVX9qNWf4ivRjAyCLCG7/G", + "PVT_BLS_bNz9W9QkxeREp966ntnUV4mN4dLOB4DNSghf2Y85o1YI+p7t", + "PVT_BLS_Aq4tqxG/sDEwGMZUa/Vhznc2i3B4wHNopGV3bJpTNW6FauCN", + "PVT_BLS_t2xBqsJKO0RHQMvsIYHFpvuy+IkBrCVeZl1NxThKEwwvUbiP", + "PVT_BLS_94/Vo26YNQV1P7zWmkDnh02P0ZcPM5xQlLG3LiUCOUUgMpEi", + "PVT_BLS_uQ9ONJ/oJlj+yRIjE3tiLcoIXTMEuCwMuAFL1WUDY28N97gF", + "PVT_BLS_2qtUuz8cYjbu/shyUPxIwKrBMSSbvolv4iJJvykUMRFl4hGt", + "PVT_BLS_0Im2qjJIfABfsKyUV1HmRrbAkDnrbwOPP6k7LPrbqTqOe7zk", + "PVT_BLS_oz6i30xug3Xee4wWHwaEHom2KwKckyoMRJdHyBbL+TQ5eURe", + "PVT_BLS_5YssxoJH+C8REKeJepx1aLrU1POLioQUmii+geVCbAm7Wk0/", + "PVT_BLS_i6k+CFneNCvNjHvAqsjgG/+8Evi8pLdY4lQuLSDw5E5auX+0", + "PVT_BLS_vKmBnJ3X8BMyqWvzKF25KPWNHSamej4jyEzdnrt1EhSkAFXb", + "PVT_BLS_zELiBcMFkgL7zOQ80vL32VAGvCjMyg8TDIFIvBAlf2bnjiF2" + }; + + std::vector replica_set_1; + std::vector replica_set_2; + + std::vector n_replica_set_1; + std::vector n_replica_set_2; replica_set_1.reserve( honest_replica_set_1.size() + byzantine_set.size() ); replica_set_2.reserve( honest_replica_set_2.size() + byzantine_set.size() ); - replica_set_1.insert( replica_set_1.end(), honest_replica_set_1.begin(), honest_replica_set_1.end() ); - replica_set_1.insert( replica_set_1.end(), byzantine_set.begin(), byzantine_set.end() ); + replica_set_1.insert( replica_set_1.end(), honest_replica_set_keys_1.begin(), honest_replica_set_keys_1.end() ); + replica_set_1.insert( replica_set_1.end(), byzantine_keys_set.begin(), byzantine_keys_set.end() ); - replica_set_2.insert( replica_set_2.end(), honest_replica_set_2.begin(), honest_replica_set_2.end() ); - replica_set_2.insert( replica_set_2.end(), byzantine_set.begin(), byzantine_set.end() ); + replica_set_2.insert( replica_set_2.end(), honest_replica_set_keys_2.begin(), honest_replica_set_keys_2.end() ); + replica_set_2.insert( replica_set_2.end(), byzantine_keys_set.begin(), byzantine_keys_set.end() ); + n_replica_set_1.reserve( honest_replica_set_1.size() + byzantine_set.size() ); + n_replica_set_2.reserve( honest_replica_set_2.size() + byzantine_set.size() ); + + n_replica_set_1.insert( n_replica_set_1.end(), honest_replica_set_1.begin(), honest_replica_set_1.end() ); + n_replica_set_1.insert( n_replica_set_1.end(), byzantine_set.begin(), byzantine_set.end() ); + + n_replica_set_2.insert( n_replica_set_2.end(), honest_replica_set_2.begin(), honest_replica_set_2.end() ); + n_replica_set_2.insert( n_replica_set_2.end(), byzantine_set.begin(), byzantine_set.end() ); + + std::vector sks_1 = map_to_sks(replica_set_1); + std::vector sks_2 = map_to_sks(replica_set_2); + + finalizer_set fset_1 = create_fs(replica_set_1); + finalizer_set fset_2 = create_fs(replica_set_2); + //simulating a fork, where test_pacemaker tpm1; tpm1.connect(replica_set_1); // complete connection graph @@ -740,41 +774,34 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { hotstuff_test_handler ht1; hotstuff_test_handler ht2; - ht1.initialize_qc_chains(tpm1, replica_set_1); - - ht2.initialize_qc_chains(tpm2, replica_set_2); + ht1.initialize_qc_chains(tpm1, n_replica_set_1, sks_1); + ht2.initialize_qc_chains(tpm2, n_replica_set_2, sks_2); tpm1.set_proposer("bpe"_n); //honest leader tpm1.set_leader("bpe"_n); tpm1.set_next_leader("bpe"_n); - tpm1.set_finalizers(replica_set_1); - + tpm1.set_finalizer_set(fset_1); tpm2.set_proposer("bpf"_n); //byzantine leader tpm2.set_leader("bpf"_n); tpm2.set_next_leader("bpf"_n); - tpm2.set_finalizers(replica_set_2); + + tpm2.set_finalizer_set(fset_2); auto qcc_bpe = std::find_if(ht1._qc_chains.begin(), ht1._qc_chains.end(), [&](const auto& q){ return q.first == "bpe"_n; }); finalizer_state fs_bpe; qcc_bpe->second->get_state(fs_bpe); - //auto qcc_bpf = std::find_if(ht2._qc_chains.begin(), ht2._qc_chains.end(), [&](const auto& q){ return q.first == "bpf"_n; }); std::vector msgs; tpm1.set_current_block_id(ids[0]); //first block tpm2.set_current_block_id(ids[0]); //first block - tpm1.beat(); //produce first block and associated proposal tpm2.beat(); //produce first block and associated proposal - tpm1.dispatch(""); tpm1.dispatch(""); - tpm2.dispatch(""); tpm2.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); @@ -783,12 +810,9 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.dispatch(""); tpm1.dispatch(""); - tpm2.dispatch(""); tpm2.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); @@ -797,12 +821,9 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.dispatch(""); tpm1.dispatch(""); - tpm2.dispatch(""); tpm2.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); @@ -811,12 +832,9 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.dispatch(""); tpm1.dispatch(""); - tpm2.dispatch(""); tpm2.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("487e5fcbf2c515618941291ae3b6dcebb68942983d8ac3f61c4bdd9901dadbe7")); @@ -835,8 +853,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.pipe(tpm2.dispatch("")); tpm1.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a8c84b7f9613aebf2ae34f457189d58de95a6b0a50d103a4c9e6405180d6fffb")); @@ -849,8 +865,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.pipe(tpm2.dispatch("")); tpm1.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("ab04f499892ad5ebd209d54372fd5c0bda0288410a084b55c70eda40514044f3")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("4af7c22e5220a61ac96c35533539e65d398e9f44de4c6e11b5b0279e7a79912f")); @@ -863,8 +877,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.pipe(tpm2.dispatch("")); tpm1.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("ab04f499892ad5ebd209d54372fd5c0bda0288410a084b55c70eda40514044f3")); @@ -877,8 +889,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { tpm1.pipe(tpm2.dispatch("")); tpm1.dispatch(""); -//ht1.print_bp_state("bpe"_n, ""); - qcc_bpe->second->get_state(fs_bpe); BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151")); BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("9eeffb58a16133517d8d2f6f90b8a3420269de3356362677055b225a44a7c151")); @@ -889,21 +899,21 @@ BOOST_AUTO_TEST_CASE(hotstuff_5) try { } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_CASE(hotstuff_6) try { + BOOST_AUTO_TEST_CASE(hotstuff_6) try { //test simple separation between the (single) proposer and the leader; includes one leader rotation - test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); - ht.initialize_qc_chains(tpm, unique_replicas); - - tpm.set_proposer("bpg"_n); // can be any proposer that's not the leader for this test + ht.initialize_qc_chains(tpm, unique_replicas, sks); + tpm.set_proposer("bpg"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -916,11 +926,8 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { qcc_bpc->second->get_state(fs_bpc); tpm.set_current_block_id(ids[0]); //first block - tpm.beat(); //produce first block - tpm.dispatch(""); //get the first block from the proposer to the leader - tpm.dispatch(""); //send proposal to replicas (prepare on first block) qcc_bpa->second->get_state(fs_bpa); @@ -930,7 +937,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //send votes on proposal (prepareQC on first block) - tpm.dispatch(""); //send proposal to replicas (precommit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -940,7 +946,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block) - tpm.dispatch(""); //send proposal to replicas (commit on first block) qcc_bpa->second->get_state(fs_bpa); @@ -950,9 +955,7 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); tpm.set_next_leader("bpb"_n); //leader is set to rotate on next block - tpm.dispatch(""); //propagating votes on new proposal (commitQC on first block) - tpm.dispatch(""); //send proposal to replicas (decide on first block) qcc_bpa->second->get_state(fs_bpa); @@ -962,16 +965,11 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //propagating votes on new proposal (decide on first block) - tpm.set_proposer("bpm"_n); // can be any proposer that's not the leader for this test tpm.set_leader("bpb"_n); //leader has rotated - tpm.set_current_block_id(ids[1]); //second block - tpm.beat(); //produce second block - tpm.dispatch(""); //get the second block from the proposer to the leader - tpm.dispatch(""); //send proposal to replicas (prepare on second block) qcc_bpb->second->get_state(fs_bpb); @@ -981,7 +979,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); tpm.dispatch(""); //send votes on proposal (prepareQC on second block) - tpm.dispatch(""); //send proposal to replicas (precommit on second block) qcc_bpb->second->get_state(fs_bpb); @@ -991,7 +988,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("4b43fb144a8b5e874777f61f3b37d7a8b06c33fbc48db464ce0e8788ff4edb4f")); tpm.dispatch(""); //propagating votes on new proposal (precommitQC on second block) - tpm.dispatch(""); //send proposal to replicas (commit on second block) qcc_bpb->second->get_state(fs_bpb); @@ -1001,7 +997,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_6) try { BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("aedf8bb1ee70bd6e743268f7fe0f8171418aa43a68bb9c6e7329ffa856896c09")); tpm.dispatch(""); //propagating votes on new proposal (commitQC on second block) - tpm.dispatch(""); //send proposal to replicas (decide on second block) qcc_bpb->second->get_state(fs_bpb); @@ -1031,19 +1026,20 @@ BOOST_AUTO_TEST_CASE(hotstuff_7) try { //test leader rotation with a non-complete connection graph (simple message propagation test) test_pacemaker tpm; - tpm.connect(unique_replicas); // start with a complete connection graph, then subtract + tpm.connect(unique_replica_keys); // start with a complete connection graph, then subtract // TODO: when propagation is implemented in qc_chain, uncomment this to force an additional hop of communication between A and B //tpm.disconnect( { "bpa"_n, "bpb"_n } ); // if propagation is implemented and works, the inclusion of this line is OK and doesn't fail the test hotstuff_test_handler ht; + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); - ht.initialize_qc_chains(tpm, unique_replicas); - + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; @@ -1168,16 +1164,17 @@ BOOST_AUTO_TEST_CASE(hotstuff_8) try { //same as hotstuff_1, but with a duplication of vote messages as a regression test for vote duplication filtering test_pacemaker tpm; - tpm.connect(unique_replicas); // complete connection graph + tpm.connect(unique_replica_keys); // complete connection graph hotstuff_test_handler ht; + std::vector sks = map_to_sks(unique_replica_keys); + finalizer_set fset = create_fs(unique_replica_keys); - ht.initialize_qc_chains(tpm, unique_replicas); - + ht.initialize_qc_chains(tpm, unique_replicas, sks); tpm.set_proposer("bpa"_n); tpm.set_leader("bpa"_n); tpm.set_next_leader("bpa"_n); - tpm.set_finalizers(unique_replicas); + tpm.set_finalizer_set(fset); auto qcc_bpa = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpa"_n; }); finalizer_state fs_bpa; diff --git a/libraries/hotstuff/test/test_pacemaker.cpp b/libraries/hotstuff/test/test_pacemaker.cpp index f285dc899f..db5a5947b8 100644 --- a/libraries/hotstuff/test/test_pacemaker.cpp +++ b/libraries/hotstuff/test/test_pacemaker.cpp @@ -15,8 +15,8 @@ namespace eosio::hotstuff { _next_leader = next_leader; }; - void test_pacemaker::set_finalizers(const std::vector& finalizers) { - _finalizers = finalizers; + void test_pacemaker::set_finalizer_set(const eosio::chain::finalizer_set& finalizer_set) { + _finalizer_set = finalizer_set; }; void test_pacemaker::set_current_block_id(block_id_type id) { @@ -31,7 +31,7 @@ namespace eosio::hotstuff { _pending_message_queue.push_back(msg); } - void test_pacemaker::connect(const std::vector& nodes) { + void test_pacemaker::connect(const std::vector& nodes) { for (auto it1 = nodes.begin(); it1 != nodes.end(); ++it1) { for (auto it2 = std::next(it1); it2 != nodes.end(); ++it2) { _net[*it1].insert(*it2); @@ -40,7 +40,7 @@ namespace eosio::hotstuff { } } - void test_pacemaker::disconnect(const std::vector& nodes) { + void test_pacemaker::disconnect(const std::vector& nodes) { for (auto it1 = nodes.begin(); it1 != nodes.end(); ++it1) { for (auto it2 = std::next(it1); it2 != nodes.end(); ++it2) { _net[*it1].erase(*it2); @@ -49,7 +49,7 @@ namespace eosio::hotstuff { } } - bool test_pacemaker::is_connected(name node1, name node2) { + bool test_pacemaker::is_connected(std::string node1, std::string node2) { auto it = _net.find(node1); if (it == _net.end()) return false; @@ -169,11 +169,11 @@ namespace eosio::hotstuff { name test_pacemaker::get_next_leader() { return _next_leader; }; - - std::vector test_pacemaker::get_finalizers() { - return _finalizers; + + const finalizer_set& test_pacemaker::get_finalizer_set() { + return _finalizer_set; }; - + block_id_type test_pacemaker::get_current_block_id() { return _current_block_id; }; @@ -198,46 +198,47 @@ namespace eosio::hotstuff { _qcc_store.emplace( name, qcc_ptr ); }; - void test_pacemaker::send_hs_proposal_msg(const hs_proposal_message& msg, name id, const std::optional& exclude_peer) { + + void test_pacemaker::send_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer) { _pending_message_queue.push_back(std::make_pair(id, msg)); }; - void test_pacemaker::send_hs_vote_msg(const hs_vote_message& msg, name id, const std::optional& exclude_peer) { + void test_pacemaker::send_hs_vote_msg(const hs_vote_message& msg, const std::string& id, const std::optional& exclude_peer) { _pending_message_queue.push_back(std::make_pair(id, msg)); }; - void test_pacemaker::send_hs_new_block_msg(const hs_new_block_message& msg, name id, const std::optional& exclude_peer) { + void test_pacemaker::send_hs_new_block_msg(const hs_new_block_message& msg, const std::string& id, const std::optional& exclude_peer) { _pending_message_queue.push_back(std::make_pair(id, msg)); }; - void test_pacemaker::send_hs_new_view_msg(const hs_new_view_message& msg, name id, const std::optional& exclude_peer) { + void test_pacemaker::send_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer) { _pending_message_queue.push_back(std::make_pair(id, msg)); }; void test_pacemaker::send_hs_message_warning(const uint32_t sender_peer, const chain::hs_message_warning code) { } - void test_pacemaker::on_hs_proposal_msg(const hs_proposal_message& msg, name id) { + void test_pacemaker::on_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id) { for (const auto& [qcc_name, qcc_ptr] : _qcc_store) { if (qcc_ptr->get_id_i() != id && is_qc_chain_active(qcc_name) && is_connected(id, qcc_ptr->get_id_i())) qcc_ptr->on_hs_proposal_msg(0, msg); } } - void test_pacemaker::on_hs_vote_msg(const hs_vote_message& msg, name id) { + void test_pacemaker::on_hs_vote_msg(const hs_vote_message& msg, const std::string& id) { for (const auto& [qcc_name, qcc_ptr] : _qcc_store) { if (qcc_ptr->get_id_i() != id && is_qc_chain_active(qcc_name) && is_connected(id, qcc_ptr->get_id_i())) qcc_ptr->on_hs_vote_msg(0, msg); } } - void test_pacemaker::on_hs_new_block_msg(const hs_new_block_message& msg, name id) { + void test_pacemaker::on_hs_new_block_msg(const hs_new_block_message& msg, const std::string& id) { for (const auto& [qcc_name, qcc_ptr] : _qcc_store) { if (qcc_ptr->get_id_i() != id && is_qc_chain_active(qcc_name) && is_connected(id, qcc_ptr->get_id_i())) qcc_ptr->on_hs_new_block_msg(0, msg); } } - void test_pacemaker::on_hs_new_view_msg(const hs_new_view_message& msg, name id) { + void test_pacemaker::on_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id) { for (const auto& [qcc_name, qcc_ptr] : _qcc_store) { if (qcc_ptr->get_id_i() != id && is_qc_chain_active(qcc_name) && is_connected(id, qcc_ptr->get_id_i())) qcc_ptr->on_hs_new_view_msg(0, msg); diff --git a/libraries/libfc/test/test_bls.cpp b/libraries/libfc/test/test_bls.cpp index 3faa6d42e2..51209bb090 100644 --- a/libraries/libfc/test/test_bls.cpp +++ b/libraries/libfc/test/test_bls.cpp @@ -156,7 +156,6 @@ BOOST_AUTO_TEST_CASE(bls_agg_tree_verif) try { } FC_LOG_AND_RETHROW(); - //test random key generation, signature + verification BOOST_AUTO_TEST_CASE(bls_key_gen) try {