diff --git a/libraries/chain/CMakeLists.txt b/libraries/chain/CMakeLists.txt index b59d39fced..56b9b66264 100644 --- a/libraries/chain/CMakeLists.txt +++ b/libraries/chain/CMakeLists.txt @@ -80,9 +80,7 @@ set(CHAIN_WEBASSEMBLY_SOURCES ) set(CHAIN_HOTSTUFF_SOURCES - hotstuff/chain_pacemaker.cpp hotstuff/instant_finality_extension.cpp - hotstuff/qc_chain.cpp hotstuff/hotstuff.cpp ) diff --git a/libraries/chain/hotstuff/chain_pacemaker.cpp b/libraries/chain/hotstuff/chain_pacemaker.cpp deleted file mode 100644 index bcf000c7da..0000000000 --- a/libraries/chain/hotstuff/chain_pacemaker.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include -#include -#include - -// comment this out to remove the core profiler -#define HS_CORE_PROFILER - -namespace eosio::chain { - -// ======================== Core profiling instrumentation ========================= -#ifdef HS_CORE_PROFILER - std::mutex csc_mutex; - bool csc_started = false; - fc::microseconds csc_total; // total time spent by all net threads waiting on the core lock - fc::time_point csc_first_time; // first time the core has received a request - fc::time_point csc_last_report_time; // last time a core timing report was printed to the log - int64_t csc_reqs; // total number of times the core has been entered by a net thread - struct reqstat { // per-core-request-type stats - fc::microseconds total_us; // total time spent in this request type - fc::microseconds max_us; // maximum time ever spent inside a request of this type - int64_t count = 0; // total requests of this type made - }; - std::map reqs; - class csc { - public: - fc::time_point start; // time lock request was made - fc::time_point start_core; // time the core has been entered - std::string name; - csc(const std::string & entrypoint_name) : - start(fc::time_point::now()), name(entrypoint_name) { } - void core_in() { - start_core = fc::time_point::now(); - std::lock_guard g( csc_mutex ); - ++csc_reqs; // update total core requests - csc_total += start_core - start; // update total core synchronization contention time - if (! csc_started) { // one-time initialization - csc_started = true; - csc_first_time = start_core; - csc_last_report_time = start_core; - } - } - void core_out() { - fc::time_point end = fc::time_point::now(); - std::lock_guard g( csc_mutex ); - - // update per-request metrics - { - auto it = reqs.find(name); - if (it == reqs.end()) { - reqs.insert({name, reqstat()}); - it = reqs.find(name); - } - reqstat &req = it->second; - ++req.count; - fc::microseconds exectime = end - start_core; - req.total_us += exectime; - if (exectime > req.max_us) { - req.max_us = exectime; - } - } - - // emit full report every 10s - fc::microseconds elapsed = end - csc_last_report_time; - if (elapsed.count() > 10000000) { // 10-second intervals to print the report - fc::microseconds total_us = end - csc_first_time; // total testing walltime so far since 1st request seen - int64_t total_secs = total_us.count() / 1000000; // never zero if report interval large enough - int64_t avgs = csc_total.count() / total_secs; - int64_t avgr = csc_total.count() / csc_reqs; - // core contention report - ilog("HS-CORE: csc_total_us:${tot} csc_elapsed_s:${secs} csc_avg_us_per_s:${avgs} csc_reqs:${reqs} csc_avg_us_per_req:${avgr}", ("tot", csc_total)("secs",total_secs)("avgs", avgs)("reqs", csc_reqs)("avgr", avgr)); - fc::microseconds req_total_us; // will compute global stats for all request types - fc::microseconds req_max_us; - int64_t req_count = 0; - auto it = reqs.begin(); - while (it != reqs.end()) { - const std::string & req_name = it->first; - reqstat &req = it->second; - int64_t avgr = req.total_us.count() / it->second.count; - // per-request-type performance report - ilog("HS-CORE: ${rn}_total_us:${tot} ${rn}_max_us:${max} ${rn}_reqs:${reqs} ${rn}_avg_us_per_req:${avgr}", ("rn",req_name)("tot", req.total_us)("max",req.max_us)("reqs", req.count)("avgr", avgr)); - req_total_us += req.total_us; - if (req_max_us < req.max_us) { - req_max_us = req.max_us; - } - req_count += req.count; - ++it; - } - // combined performance report - int64_t req_avgr = req_total_us.count() / req_count; - ilog("HS-CORE: total_us:${tot} max_us:${max} reqs:${reqs} avg_us_per_req:${avgr}", ("tot", req_total_us)("max",req_max_us)("reqs", req_count)("avgr", req_avgr)); - csc_last_report_time = end; - } - } - }; -#else - struct csc { // dummy profiler - csc(const string & s) { } - void core_in() { } - void core_out() { } - } -#endif -//=============================================================================================== - -#warning TODO get a data directory str passed into the chain_pacemaker ctor and use it to compose the absolute filepathname that is passed to qc_chain ctor - chain_pacemaker::chain_pacemaker(controller* chain, - std::set my_producers, - bls_pub_priv_key_map_t finalizer_keys, - fc::logger& logger) - : _chain(chain), - _qc_chain("default", this, std::move(my_producers), std::move(finalizer_keys), logger, eosio::chain::config::safetydb_filename), - _logger(logger) - { - _accepted_block_connection = chain->accepted_block.connect( [this]( const block_signal_params& t ) { - const auto& [ block, id ] = t; - on_accepted_block( block ); - } ); - _irreversible_block_connection = chain->irreversible_block.connect( [this]( const block_signal_params& t ) { - const auto& [ block, id ] = t; - on_irreversible_block( block ); - } ); - // TODO: assuming this will be going away - _head_block_state = chain->head_block_state_legacy(); - } - - void chain_pacemaker::register_bcast_function(std::function&, const hs_message&)> broadcast_hs_message) { - FC_ASSERT(broadcast_hs_message, "on_hs_message must be provided"); - // no need to std::lock_guard g( _hotstuff_global_mutex ); here since pre-comm init - bcast_hs_message = std::move(broadcast_hs_message); - } - - void chain_pacemaker::register_warn_function(std::function warning_hs_message) { - FC_ASSERT(warning_hs_message, "must provide callback"); - // no need to std::lock_guard g( _hotstuff_global_mutex ); here since pre-comm init - warn_hs_message = std::move(warning_hs_message); - } - - void chain_pacemaker::get_state(finalizer_state& fs) const { - // lock-free state version check - uint64_t current_state_version = _qc_chain.get_state_version(); - if (_state_cache_version != current_state_version) { - finalizer_state current_state; - { - csc prof("stat"); - std::lock_guard g( _hotstuff_global_mutex ); // lock IF engine to read state - prof.core_in(); - current_state_version = _qc_chain.get_state_version(); // get potentially fresher version - if (_state_cache_version != current_state_version) - _qc_chain.get_state(current_state); - prof.core_out(); - } - if (_state_cache_version != current_state_version) { - std::unique_lock ul(_state_cache_mutex); // lock cache for writing - _state_cache = current_state; - _state_cache_version = current_state_version; - } - } - - std::shared_lock sl(_state_cache_mutex); // lock cache for reading - fs = _state_cache; - } - - // called from main thread - void chain_pacemaker::on_accepted_block( const signed_block_ptr& block ) { - std::scoped_lock g( _chain_state_mutex ); - // TODO: assume this is going away - _head_block_state = _chain->head_block_state_legacy(); - } - - // called from main thread - void chain_pacemaker::on_irreversible_block( const signed_block_ptr& block ) { - if (!block->header_extensions.empty()) { - std::optional ext = block->extract_header_extension(instant_finality_extension::extension_id()); - if (ext) { - std::scoped_lock g( _chain_state_mutex ); - if (_active_finalizer_policy.generation == 0) { - ilog("Switching to instant finality at block ${b}", ("b", block->block_num())); - // switching from dpos to hotstuff, all nodes will switch at same block height - // block header extension is set in finish_block to value set by host function set_finalizers - _chain->set_if_irreversible_block_num(block->block_num()); // can be any value <= dpos lib - } - auto if_extension = std::get(*ext); -#warning Revisit after finalizer policy change design is complete as this is not necessarily when we will change active finalizer policy. - if (if_extension.new_finalizer_policy) { - _active_finalizer_policy = std::move(*if_extension.new_finalizer_policy); - } - } - } - } - - name chain_pacemaker::get_proposer() { - std::scoped_lock g( _chain_state_mutex ); - return _head_block_state->header.producer; - } - - name chain_pacemaker::get_leader() { - std::unique_lock g( _chain_state_mutex ); - name producer_name = _head_block_state->header.producer; - g.unlock(); - return producer_name; - } - - name chain_pacemaker::get_next_leader() { - std::unique_lock g( _chain_state_mutex ); - block_timestamp_type next_block_time = _head_block_state->header.timestamp.next(); - producer_authority p_auth = _head_block_state->get_scheduled_producer(next_block_time); - g.unlock(); - return p_auth.producer_name; - } - - const finalizer_policy& chain_pacemaker::get_finalizer_policy() { - return _active_finalizer_policy; - } - - block_id_type chain_pacemaker::get_current_block_id() { - std::scoped_lock g( _chain_state_mutex ); - return _head_block_state->id(); - } - - uint32_t chain_pacemaker::get_quorum_threshold() { - return _quorum_threshold; - } - - // called from the main application thread - void chain_pacemaker::beat() { - csc prof("beat"); - std::lock_guard g( _hotstuff_global_mutex ); - prof.core_in(); - _qc_chain.on_beat(); - prof.core_out(); - } - - 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 vote_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, const std::string& id, const std::optional& exclude_peer) { - bcast_hs_message(exclude_peer, {msg}); - } - - void chain_pacemaker::send_hs_message_warning(uint32_t sender_peer, const hs_message_warning code) { - warn_hs_message(sender_peer, code); - - } - - // called from net threads - void chain_pacemaker::on_hs_msg(const uint32_t connection_id, const hs_message &msg) { - std::visit(overloaded{ - [this, connection_id](const vote_message& m) { on_hs_vote_msg(connection_id, m); }, - [this, connection_id](const hs_proposal_message& m) { on_hs_proposal_msg(connection_id, m); }, - [this, connection_id](const hs_new_view_message& m) { on_hs_new_view_msg(connection_id, m); }, - }, msg.msg); - } - - // called from net threads - void chain_pacemaker::on_hs_proposal_msg(const uint32_t connection_id, const hs_proposal_message& msg) { - csc prof("prop"); - std::lock_guard g( _hotstuff_global_mutex ); - prof.core_in(); - _qc_chain.on_hs_proposal_msg(connection_id, msg); - prof.core_out(); - } - - // called from net threads - void chain_pacemaker::on_hs_vote_msg(const uint32_t connection_id, const vote_message& msg) { - csc prof("vote"); - std::lock_guard g( _hotstuff_global_mutex ); - prof.core_in(); - _qc_chain.on_hs_vote_msg(connection_id, msg); - prof.core_out(); - } - - // called from net threads - void chain_pacemaker::on_hs_new_view_msg(const uint32_t connection_id, const hs_new_view_message& msg) { - csc prof("view"); - std::lock_guard g( _hotstuff_global_mutex ); - prof.core_in(); - _qc_chain.on_hs_new_view_msg(connection_id, msg); - prof.core_out(); - } - -} // namespace eosio::chain diff --git a/libraries/chain/hotstuff/qc_chain.cpp b/libraries/chain/hotstuff/qc_chain.cpp deleted file mode 100644 index dda43e887f..0000000000 --- a/libraries/chain/hotstuff/qc_chain.cpp +++ /dev/null @@ -1,922 +0,0 @@ -#include -#include -#include - -namespace eosio::chain { - - void qc_chain::write_safety_state_file() { - if (_safety_state_file.empty()) - return; - if (!_safety_state_file_handle.is_open()) - _safety_state_file_handle.open(fc::cfile::create_or_update_rw_mode); - state_db_manager::write(_safety_state_file_handle, _safety_state); - } - - const hs_proposal_message* qc_chain::get_proposal(const fc::sha256& proposal_id) { - proposal_store_type::nth_index<0>::type::iterator itr = _proposal_store.get().find( proposal_id ); - if (itr == _proposal_store.get().end()) - return nullptr; - return &(*itr); - } - - bool qc_chain::insert_proposal(const hs_proposal_message& proposal) { - if (get_proposal( proposal.proposal_id ) != nullptr) - return false; - _proposal_store.insert(proposal); //new proposal - return true; - } - - void qc_chain::get_state(finalizer_state& fs) const { - fs.b_leaf = _b_leaf; - fs.b_lock = _safety_state.get_b_lock(); - fs.b_exec = _b_exec; - fs.b_finality_violation = _b_finality_violation; - fs.block_exec = _block_exec; - fs.pending_proposal_block = _pending_proposal_block; - fs.v_height = _safety_state.get_v_height(); - fs.high_qc = _high_qc.to_msg(); - fs.current_qc = _current_qc.to_msg(); - auto hgt_itr = _proposal_store.get().begin(); - auto end_itr = _proposal_store.get().end(); - while (hgt_itr != end_itr) { - const hs_proposal_message & p = *hgt_itr; - fs.proposals.emplace( p.proposal_id, p ); - ++hgt_itr; - } - } - - uint32_t qc_chain::positive_bits_count(const hs_bitset& finalizers) { - return finalizers.count(); // the number of bits in this bitset that are set. - } - - hs_bitset qc_chain::update_bitset(const hs_bitset& finalizer_policy, const bls_public_key& finalizer_key ) { - - hs_bitset b(finalizer_policy ); - - const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers; - - for (size_t i = 0; i < finalizers.size();i++) { - if ( finalizers[i].public_key == finalizer_key ) { - b.set(i); - - fc_tlog(_logger, " === finalizer found ${finalizer} new value : ${value}", - ("value", [&](){ std::string r; boost::to_string(b, r); return r; }())); - - return b; - } - } - 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"); - } - - std::vector qc_chain::get_qc_chain(const fc::sha256& proposal_id) { - std::vector ret_arr; - if ( const hs_proposal_message* b2 = get_proposal( proposal_id ) ) { - ret_arr.push_back( *b2 ); - if (const hs_proposal_message* b1 = get_proposal( b2->justify.proposal_id ) ) { - ret_arr.push_back( *b1 ); - if (const hs_proposal_message* b = get_proposal( b1->justify.proposal_id ) ) { - ret_arr.push_back( *b ); - } - } - } - return ret_arr; - } - - hs_proposal_message qc_chain::new_proposal_candidate(const block_id_type& block_id, uint8_t phase_counter) { - hs_proposal_message b_new; - b_new.block_id = block_id; - b_new.parent_id = _b_leaf; - b_new.phase_counter = phase_counter; - b_new.justify = _high_qc.to_msg(); //or null if no _high_qc upon activation or chain launch - if (!b_new.justify.proposal_id.empty()){ - std::vector current_qc_chain = get_qc_chain(b_new.justify.proposal_id); - size_t chain_length = std::distance(current_qc_chain.begin(), current_qc_chain.end()); - if (chain_length>=2){ - auto itr = current_qc_chain.begin(); - hs_proposal_message b2 = *itr; - itr++; - hs_proposal_message b1 = *itr; - if (b_new.parent_id == b2.proposal_id && b2.parent_id == b1.proposal_id) b_new.final_on_qc = b1.proposal_id; - else { - const hs_proposal_message *p = get_proposal( b1.parent_id ); - //EOS_ASSERT( p != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", b1.parent_id) ); - if (p != nullptr) { - b_new.final_on_qc = p->final_on_qc; - } else { - fc_elog(_logger, " *** ${id} expected to find proposal in new_proposal_candidate() but not found : ${proposal_id}", ("id",_id)("proposal_id", b1.parent_id)); - } - } - } - } - - b_new.proposal_id = get_digest_to_sign(b_new.block_id, b_new.phase_counter, b_new.final_on_qc); - - fc_dlog(_logger, " === ${id} creating new proposal : block_num ${block_num} phase ${phase_counter} : proposal_id ${proposal_id} : parent_id ${parent_id} : justify ${justify}", - ("id", _id) - ("block_num", b_new.block_num()) - ("phase_counter", b_new.phase_counter) - ("proposal_id", b_new.proposal_id) - ("parent_id", b_new.parent_id) - ("justify", b_new.justify.proposal_id)); - - return b_new; - } - - void qc_chain::reset_qc(const hs_proposal_message& proposal) { - fc_tlog(_logger, " === ${id} resetting qc : ${proposal_id}", ("proposal_id" , proposal.proposal_id)("id", _id)); - const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers; - _current_qc.reset(proposal.proposal_id, proposal.get_proposal_digest(), finalizers.size(), _pacemaker->get_quorum_threshold()); - } - - qc_chain::qc_chain(std::string id, - base_pacemaker* pacemaker, - std::set my_producers, - const bls_pub_priv_key_map_t& finalizer_keys, - fc::logger& logger, - const std::string& safety_state_file) - : _pacemaker(pacemaker), - _my_producers(std::move(my_producers)), - _id(std::move(id)), - _safety_state_file(safety_state_file), - _logger(logger) - { - //todo : read liveness state / select initialization heuristics ? - - for (const auto& kp : finalizer_keys) { - _my_finalizer_keys[bls_public_key{kp.first}] = bls_private_key{kp.second}; - } - - if (!_safety_state_file.empty()) { - _safety_state_file_handle.set_file_path(safety_state_file); - state_db_manager::read(_safety_state_file, _safety_state); - } - - fc_dlog(_logger, " === ${id} qc chain initialized ${my_producers}", ("my_producers", my_producers)("id", _id)); - } - - bool qc_chain::am_i_proposer() { - return std::find(_my_producers.begin(), _my_producers.end(), _pacemaker->get_proposer()) != _my_producers.end(); - } - - bool qc_chain::am_i_leader() { - return std::find(_my_producers.begin(), _my_producers.end(), _pacemaker->get_leader()) != _my_producers.end(); - } - - bool qc_chain::am_i_finalizer() { - const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers; - return !_my_finalizer_keys.empty() && - std::any_of(finalizers.begin(), finalizers.end(), [&](const auto& fa) { return _my_finalizer_keys.contains(fa.public_key); }); - } - - vote_message qc_chain::sign_proposal(const hs_proposal_message& proposal, - bool strong, - const bls_public_key& finalizer_pub_key, - const bls_private_key& finalizer_priv_key) - { - _safety_state.set_v_height(finalizer_pub_key, proposal.get_view_number()); - - digest_type digest = proposal.get_proposal_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); - - bls_signature sig = finalizer_priv_key.sign(h); - - vote_message v_msg = {proposal.proposal_id, strong, finalizer_priv_key.get_public_key(), sig}; - return v_msg; - } - - void qc_chain::process_proposal(std::optional connection_id, const hs_proposal_message& proposal){ - - if (!proposal.justify.proposal_id.empty()) { - - const hs_proposal_message *jp = get_proposal( proposal.justify.proposal_id ); - if (jp == nullptr) { - fc_elog(_logger, " *** ${id} proposal justification unknown : ${proposal_id}", ("id",_id)("proposal_id", proposal.justify.proposal_id)); - send_hs_message_warning(connection_id, hs_message_warning::discarded); // example; to be tuned to actual need - return; //can't recognize a proposal with an unknown justification - } - } - - const hs_proposal_message *p = get_proposal( proposal.proposal_id ); - 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}", - ("id",_id) - ("proposal_id", proposal.proposal_id) - ("justify_1", p->justify.proposal_id) - ("justify_2", proposal.justify.proposal_id)); - - } - - send_hs_message_warning(connection_id, hs_message_warning::discarded); // example; to be tuned to actual need - return; //already aware of proposal, nothing to do - } - - //height is not necessarily unique, so we iterate over all prior proposals at this height - auto hgt_itr = _proposal_store.get().lower_bound( proposal.get_key() ); - auto end_itr = _proposal_store.get().upper_bound( proposal.get_key() ); - while (hgt_itr != end_itr) - { - const hs_proposal_message & existing_proposal = *hgt_itr; - fc_elog(_logger, " *** ${id} received a different proposal at the same height (${block_num}, ${phase_counter})", - ("id",_id) - ("block_num", existing_proposal.block_num()) - ("phase_counter", existing_proposal.phase_counter)); - - fc_elog(_logger, " *** Proposal #1 : ${proposal_id_1} Proposal #2 : ${proposal_id_2}", - ("proposal_id_1", existing_proposal.proposal_id) - ("proposal_id_2", proposal.proposal_id)); - hgt_itr++; - } - - fc_dlog(_logger, " === ${id} received new proposal : block_num ${block_num} phase ${phase_counter} : proposal_id ${proposal_id} : parent_id ${parent_id} justify ${justify}", - ("id", _id) - ("block_num", proposal.block_num()) - ("phase_counter", proposal.phase_counter) - ("proposal_id", proposal.proposal_id) - ("parent_id", proposal.parent_id) - ("justify", proposal.justify.proposal_id)); - - bool success = insert_proposal( proposal ); - EOS_ASSERT( success , chain_exception, "internal error: duplicate proposal insert attempt" ); // can't happen unless bad mutex somewhere; already checked for this - - auto increment_version = fc::make_scoped_exit([this]() { ++_state_version; }); // assert failing above would mean no change - - //if I am a finalizer for this proposal and the safenode predicate for a possible vote is true, sign - bool am_finalizer = am_i_finalizer(); - bool node_safe = is_node_safe(proposal); - bool signature_required = am_finalizer && node_safe; - - std::vector msgs; - - 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_policy().finalizers; - - for (const auto& i : finalizers) { - auto mfk_itr = _my_finalizer_keys.find(i.public_key); - - if (mfk_itr!=_my_finalizer_keys.end()) { - - vote_message v_msg = sign_proposal(proposal, true, mfk_itr->first, mfk_itr->second); - - fc_tlog(_logger, " === ${id} signed proposal : block_num ${block_num} phase ${phase_counter} : proposal_id ${proposal_id}", - ("id", _id) - ("block_num", proposal.block_num()) - ("phase_counter", proposal.phase_counter) - ("proposal_id", proposal.proposal_id)); - - msgs.push_back(v_msg); - } - } - - } - 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()) - ("phase_counter", proposal.phase_counter) - ("proposal_id", proposal.proposal_id)); - - //update internal state - update(proposal); - - write_safety_state_file(); - - //propagate this proposal since it was new to us - send_hs_proposal_msg(connection_id, proposal); - - for (auto &msg : msgs) { - send_hs_vote_msg( std::nullopt, msg ); - } - - //check for leader change - leader_rotation_check(); - - //auto total_time = fc::time_point::now() - start; - //fc_dlog(_logger, " ... process_proposal() total time : ${total_time}", ("total_time", total_time)); - } - - void qc_chain::process_vote(std::optional connection_id, const vote_message& vote){ - - //auto start = fc::time_point::now(); -#warning check for duplicate or invalid vote. We will return in either case, but keep proposals for evidence of double signing - //TODO: check for duplicate or invalid vote. We will return in either case, but keep proposals for evidence of double signing - - bool am_leader = am_i_leader(); - - if (am_leader) { - 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 - return; - } - } - - const hs_proposal_message *p = get_proposal( vote.proposal_id ); - if (p == nullptr) { - if (am_leader) - fc_elog(_logger, " *** ${id} couldn't find proposal, vote : ${vote}", ("id",_id)("vote", vote)); - send_hs_message_warning(connection_id, hs_message_warning::discarded); // example; to be tuned to actual need - return; - } - - fc_tlog(_logger, " === Process vote from ${finalizer_key} : current bitset ${value}" , - ("finalizer_key", vote.finalizer_key)("value", _current_qc.get_votes_string())); - - // if not leader, check message propagation and quit - if (! am_leader) { - seen_votes_store_type::nth_index<0>::type::iterator itr = _seen_votes_store.get().find( p->proposal_id ); - bool propagate = false; - if (itr == _seen_votes_store.get().end()) { - seen_votes sv = { p->proposal_id, p->get_key(), { vote.finalizer_key } }; - _seen_votes_store.insert(sv); - propagate = true; - } else { - _seen_votes_store.get().modify(itr, [&](seen_votes& sv) { - if (sv.finalizers.count(vote.finalizer_key) == 0) { - sv.finalizers.insert(vote.finalizer_key); - propagate = true; - } - }); - } - if (propagate) - send_hs_vote_msg(connection_id, vote); - return; - } - - fc_tlog(_logger, " === Process vote from ${finalizer_key} : current bitset ${value}" , - ("finalizer_key", vote.finalizer_key)("value", _current_qc.get_votes_string())); - - bool quorum_met = _current_qc.is_quorum_met(); - - // If quorum is already met, we don't need to do anything else. Otherwise, we aggregate the signature. - if (!quorum_met) { - const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers; - digest_type digest = p->get_proposal_digest(); - for (size_t i=0; i(digest.data(), digest.data() + 32), - i, vote.finalizer_key, vote.sig).first) { - // fc_tlog(_logger, " === update bitset ${value} ${finalizer_key}", - // ("value", _current_qc.get_active_finalizers_string())("finalizer_key", vote.finalizer_key)); - if (_current_qc.is_quorum_met()) { - auto increment_version = fc::make_scoped_exit([this]() { ++_state_version; }); - fc_dlog(_logger, " === ${id} quorum met on #${block_num} ${phase_counter} ${proposal_id} ", - ("block_num", p->block_num()) - ("phase_counter", p->phase_counter) - ("proposal_id", vote.proposal_id) - ("id", _id)); - - //fc_tlog(_logger, " === 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) - ("proposal_id", vote.proposal_id) - ("id", _id)); - - //check for leader change - leader_rotation_check(); - } - } - break; - } - } - } - - //auto total_time = fc::time_point::now() - start; - //fc_tlog(_logger, " ... process_vote() total time : ${total_time}", ("total_time", total_time)); - } - - void qc_chain::process_new_view(std::optional connection_id, const hs_new_view_message& msg){ -#if 0 - // new_view message deprecated - 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, 21})) { // TODO: use active schedule size - increment_version.cancel(); - } else { - // Always propagate a view that's newer than ours. - // If it's not newer, then we have already propagated ours. - // If the recipient doesn't think ours is newer, it has already propagated its own, and so on. - send_hs_new_view_msg(connection_id, msg); - } -#endif - } - - void qc_chain::create_proposal(const block_id_type& block_id) { - - auto increment_version = fc::make_scoped_exit([this]() { ++_state_version; }); - - if (!_current_qc.get_proposal_id().empty() && !_current_qc.is_quorum_met()) { - - fc_tlog(_logger, " === ${id} pending proposal found ${proposal_id} : quorum met ${quorum_met}", - ("id", _id) - ("proposal_id", _current_qc.get_proposal_id()) - ("quorum_met", _current_qc.is_quorum_met())); - - fc_tlog(_logger, " === ${id} setting _pending_proposal_block to ${block_id} (create_proposal)", ("id", _id)("block_id", block_id)); - _pending_proposal_block = block_id; - - } else { - - fc_tlog(_logger, " === ${id} preparing new proposal ${proposal_id} : quorum met ${quorum_met}", - ("id", _id) - ("proposal_id", _current_qc.get_proposal_id()) - ("quorum_met", _current_qc.is_quorum_met())); - hs_proposal_message proposal_candidate = new_proposal_candidate( block_id, 0 ); - - reset_qc(proposal_candidate); - - fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (create_proposal)", ("id", _id)); - - _pending_proposal_block = {}; - _b_leaf = proposal_candidate.proposal_id; - - //todo : asynchronous? - //write_state(_liveness_state_file , _liveness_state); - - send_hs_proposal_msg( std::nullopt, proposal_candidate ); - - fc_tlog(_logger, " === ${id} _b_leaf updated (create_proposal): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); - } - } - - void qc_chain::send_hs_proposal_msg(std::optional connection_id, const hs_proposal_message & msg){ - fc_tlog(_logger, " === broadcast_hs_proposal ==="); - _pacemaker->send_hs_proposal_msg(msg, _id, connection_id); - if (!connection_id.has_value()) - process_proposal( std::nullopt, msg ); - } - - void qc_chain::send_hs_vote_msg(std::optional connection_id, const vote_message & msg){ - fc_tlog(_logger, " === broadcast_hs_vote ==="); - _pacemaker->send_hs_vote_msg(msg, _id, connection_id); - if (!connection_id.has_value()) - process_vote( std::nullopt, msg ); - } - - void qc_chain::send_hs_new_view_msg(std::optional connection_id, const hs_new_view_message & msg){ - fc_tlog(_logger, " === broadcast_hs_new_view ==="); - _pacemaker->send_hs_new_view_msg(msg, _id, connection_id); - } - - void qc_chain::send_hs_message_warning(std::optional connection_id, const hs_message_warning code) { - if (connection_id.has_value()) - _pacemaker->send_hs_message_warning(connection_id.value(), code); - } - - //extends predicate - bool qc_chain::extends(const fc::sha256& descendant, const fc::sha256& ancestor){ - -#warning confirm the extends predicate never has to verify extension of irreversible blocks, otherwise this function needs to be modified - //TODO: confirm the extends predicate never has to verify extension of irreversible blocks, otherwise this function needs to be modified - - uint32_t counter = 0; - const hs_proposal_message *p = get_proposal( descendant ); - while (p != nullptr) { - fc::sha256 parent_id = p->parent_id; - p = get_proposal( parent_id ); - if (p == nullptr) { - fc_elog(_logger, " *** ${id} cannot find proposal id while looking for ancestor : ${proposal_id}", ("id",_id)("proposal_id", parent_id)); - return false; - } - if (p->proposal_id == ancestor) { - if (counter > 25) { - fc_elog(_logger, " *** ${id} took ${counter} iterations to find ancestor ", ("id",_id)("counter", counter)); - } - return true; - } - ++counter; - } - - fc_elog(_logger, " *** ${id} extends returned false : could not find ${d_proposal_id} descending from ${a_proposal_id} ", - ("id",_id) - ("d_proposal_id", descendant) - ("a_proposal_id", ancestor)); - - return false; - } - - // Invoked when we could perhaps make a proposal to the network (or to ourselves, if we are the leader). - void qc_chain::on_beat(){ - - // only proposer-leaders do on_beat, which is to create a proposal - if (!am_i_proposer() || !am_i_leader()) - return; - - block_id_type current_block_id = _pacemaker->get_current_block_id(); - - // NOTE: This would be the "justify" of the proposal that is being created - // _high_qc.to_msg(); //or null if no _high_qc upon activation or chain launch - - create_proposal(current_block_id); - } - - // returns true on state change (caller decides update on state version - bool qc_chain::update_high_qc(const valid_quorum_certificate& high_qc) { - - fc_tlog(_logger, " === check to update high qc ${proposal_id}", ("proposal_id", high_qc.get_proposal_id())); - - // if new high QC is higher than current, update to new - - if (_high_qc.get_proposal_id().empty()){ - - _high_qc = valid_quorum_certificate(high_qc); - _b_leaf = _high_qc.get_proposal_id(); - - //todo : asynchronous? - //write_state(_liveness_state_file , _liveness_state); - - fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.get_proposal_id())("id", _id)); - - // avoid looping message propagation when receiving a new-view message with a high_qc.get_proposal_id().empty(). - // not sure if high_qc.get_proposal_id().empty() + _high_qc.get_proposal_id().empty() is something that actually ever happens in the real world. - // not sure if high_qc.get_proposal_id().empty() should be tested and always rejected (return false + no _high_qc / _b_leaf update). - // if this returns false, we won't update the get_finality_status information, but I don't think we care about that at all. - return !high_qc.get_proposal_id().empty(); - } else { - const hs_proposal_message *old_high_qc_prop = get_proposal( _high_qc.get_proposal_id() ); - const hs_proposal_message *new_high_qc_prop = get_proposal( high_qc.get_proposal_id() ); - - if (old_high_qc_prop == nullptr) - return false; - if (new_high_qc_prop == nullptr) - return false; - - if (new_high_qc_prop->get_view_number() > old_high_qc_prop->get_view_number() /* && high_qc.is_quorum_met() */) - { - fc_tlog(_logger, " === updated high qc, now is : #${view_number} ${proposal_id}", ("view_number", new_high_qc_prop->get_view_number())("proposal_id", new_high_qc_prop->proposal_id)); - _high_qc = high_qc; - _b_leaf = _high_qc.get_proposal_id(); - - //todo : asynchronous? - //write_state(_liveness_state_file , _liveness_state); - - fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.get_proposal_id())("id", _id)); - - return true; - } - } - return false; - } - - void qc_chain::leader_rotation_check(){ - //verify if leader changed - - name current_leader = _pacemaker->get_leader(); - name next_leader = _pacemaker->get_next_leader(); - - if (current_leader != next_leader){ - - fc_dlog(_logger, " /// ${id} rotating leader : ${old_leader} -> ${new_leader} ", - ("id", _id) - ("old_leader", current_leader) - ("new_leader", next_leader)); - - //leader changed, we send our new_view message - - reset_qc({}); - - fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (leader_rotation_check)", ("id", _id)); - - _pending_proposal_block = {}; - - hs_new_view_message new_view; - - new_view.high_qc = _high_qc.to_msg(); - - send_hs_new_view_msg( std::nullopt, new_view ); - } - } - - //safenode predicate - bool qc_chain::is_node_safe(const hs_proposal_message& proposal) { - - //fc_tlog(_logger, " === is_node_safe ==="); - - bool monotony_check = false; - bool safety_check = false; - bool liveness_check = false; - bool final_on_qc_check = false; - - fc::sha256 upcoming_commit; - - if (proposal.justify.proposal_id.empty() && _safety_state.get_b_lock().empty()) { - - final_on_qc_check = true; //if chain just launched or feature just activated - } else { - - std::vector current_qc_chain = get_qc_chain(proposal.justify.proposal_id); - - size_t chain_length = std::distance(current_qc_chain.begin(), current_qc_chain.end()); - - if (chain_length >= 2) { - - auto itr = current_qc_chain.begin(); - - hs_proposal_message b2 = *itr; - ++itr; - hs_proposal_message b1 = *itr; - - if (proposal.parent_id == b2.proposal_id && b2.parent_id == b1.proposal_id) - upcoming_commit = b1.proposal_id; - else { - const hs_proposal_message *p = get_proposal( b1.parent_id ); - //EOS_ASSERT( p != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", b1.parent_id) ); - if (p != nullptr) { - upcoming_commit = p->final_on_qc; - } else { - fc_elog(_logger, " *** ${id} in is_node_safe did not find expected proposal id: ${proposal_id}", ("id",_id)("proposal_id", b1.parent_id)); - } - } - } - - //abstracted [...] - if (upcoming_commit == proposal.final_on_qc) { - final_on_qc_check = true; - } - } - - if (proposal.get_view_number() > _safety_state.get_v_height()) { - monotony_check = true; - } - - if (!_safety_state.get_b_lock().empty()){ - - //Safety check : check if this proposal extends the chain I'm locked on - if (extends(proposal.proposal_id, _safety_state.get_b_lock())) { - safety_check = true; - } - - //Liveness check : check if the height of this proposal's justification is higher than the height of the proposal I'm locked on. This allows restoration of liveness if a replica is locked on a stale block. - - if (proposal.justify.proposal_id.empty() && _safety_state.get_b_lock().empty()) { - - liveness_check = true; //if there is no justification on the proposal and I am not locked on anything, means the chain just launched or feature just activated - } else { - const hs_proposal_message *b_lock = get_proposal( _safety_state.get_b_lock() ); - EOS_ASSERT( b_lock != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", _safety_state.get_b_lock()) ); - const hs_proposal_message *prop_justification = get_proposal( proposal.justify.proposal_id ); - EOS_ASSERT( prop_justification != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", proposal.justify.proposal_id) ); - - if (prop_justification->get_view_number() > b_lock->get_view_number()) { - liveness_check = true; - } - } - } else { - //if we're not locked on anything, means the protocol just activated or chain just launched - liveness_check = true; - safety_check = true; - - fc_tlog(_logger, " === ${id} not locked on anything, liveness and safety are true", ("id", _id)); - } - - fc_tlog(_logger, " === final_on_qc_check : ${final_on_qc_check}, monotony_check : ${monotony_check}, liveness_check : ${liveness_check}, safety_check : ${safety_check}", - ("final_on_qc_check", final_on_qc_check) - ("monotony_check", monotony_check) - ("liveness_check", liveness_check) - ("safety_check", safety_check)); - - bool node_is_safe = final_on_qc_check && monotony_check && (liveness_check || safety_check); - if (!node_is_safe) { - - fc_elog(_logger, " *** node is NOT safe. Checks : final_on_qc: ${final_on_qc}, monotony_check: ${monotony_check}, liveness_check: ${liveness_check}, safety_check: ${safety_check})", - ("final_on_qc_check",final_on_qc_check) - ("monotony_check",monotony_check) - ("liveness_check",liveness_check) - ("safety_check",safety_check)); - } - - //return true if monotony check and at least one of liveness or safety check evaluated successfully - return final_on_qc_check && monotony_check && (liveness_check || safety_check); - } - - //on proposal received, called from network thread - void qc_chain::on_hs_proposal_msg(const uint32_t connection_id, const hs_proposal_message& msg) { - process_proposal( connection_id, msg ); - } - - //on vote received, called from network thread - void qc_chain::on_hs_vote_msg(const uint32_t connection_id, const vote_message& msg) { - process_vote( connection_id, msg ); - } - - //on new view received, called from network thread - void qc_chain::on_hs_new_view_msg(const uint32_t connection_id, const hs_new_view_message& msg) { - process_new_view( connection_id, msg ); - } - - void qc_chain::update(const hs_proposal_message& proposal) { - //fc_tlog(_logger, " === update internal state ==="); - //if proposal has no justification, means we either just activated the feature or launched the chain, or the proposal is invalid - if (proposal.justify.proposal_id.empty()) { - fc_dlog(_logger, " === ${id} proposal has no justification ${proposal_id}", ("proposal_id", proposal.proposal_id)("id", _id)); - return; - } - - std::vector current_qc_chain = get_qc_chain(proposal.justify.proposal_id); - - size_t chain_length = std::distance(current_qc_chain.begin(), current_qc_chain.end()); - - const hs_proposal_message *b_lock = get_proposal( _safety_state.get_b_lock() ); - EOS_ASSERT( b_lock != nullptr || _safety_state.get_b_lock().empty() , chain_exception, "expected hs_proposal ${id} not found", ("id", _safety_state.get_b_lock()) ); - - //fc_tlog(_logger, " === update_high_qc : proposal.justify ==="); - const hs_proposal_message* justify = get_proposal(proposal.justify.proposal_id); - digest_type digest = justify->get_proposal_digest(); - const auto& finalizers = _pacemaker->get_finalizer_policy().finalizers; - update_high_qc(valid_quorum_certificate(justify->proposal_id, - std::vector(digest.data(), digest.data() + 32), - proposal.justify.strong_votes, - std::vector{}, - proposal.justify.active_agg_sig)); - - if (chain_length<1){ - fc_dlog(_logger, " === ${id} qc chain length is 0", ("id", _id)); - return; - } - - auto itr = current_qc_chain.begin(); - hs_proposal_message b_2 = *itr; - - if (chain_length<2){ - fc_dlog(_logger, " === ${id} qc chain length is 1", ("id", _id)); - return; - } - - itr++; - - hs_proposal_message b_1 = *itr; - - //if we're not locked on anything, means we just activated or chain just launched, else we verify if we've progressed enough to establish a new lock - - fc_tlog(_logger, " === ${id} _b_lock ${_b_lock} b_1 height ${b_1_height}", - ("id", _id) - ("_b_lock", _safety_state.get_b_lock()) - ("b_1_height", b_1.block_num()) - ("b_1_phase", b_1.phase_counter)); - - if ( b_lock != nullptr ) { - fc_tlog(_logger, " === b_lock height ${b_lock_height} b_lock phase ${b_lock_phase}", - ("b_lock_height", b_lock->block_num()) - ("b_lock_phase", b_lock->phase_counter)); - } - - if (_safety_state.get_b_lock().empty() || b_1.get_view_number() > b_lock->get_view_number()){ - - fc_tlog(_logger, "setting _b_lock to ${proposal_id}", ("proposal_id",b_1.proposal_id )); - - for (const auto& f_itr : _my_finalizer_keys) { - _safety_state.set_b_lock(f_itr.first, b_1.proposal_id); //commit phase on b1 - } - - fc_tlog(_logger, " === ${id} _b_lock updated : ${proposal_id}", ("proposal_id", b_1.proposal_id)("id", _id)); - } - - if (chain_length < 3) { - fc_dlog(_logger, " === ${id} qc chain length is 2",("id", _id)); - return; - } - - ++itr; - - hs_proposal_message b = *itr; - - fc_tlog(_logger, " === direct parent relationship verification : b_2.parent_id ${b_2.parent_id} b_1.proposal_id ${b_1.proposal_id} b_1.parent_id ${b_1.parent_id} b.proposal_id ${b.proposal_id} ", - ("b_2.parent_id",b_2.parent_id) - ("b_1.proposal_id", b_1.proposal_id) - ("b_1.parent_id", b_1.parent_id) - ("b.proposal_id", b.proposal_id)); - - //direct parent relationship verification - if (b_2.parent_id == b_1.proposal_id && b_1.parent_id == b.proposal_id){ - - if (!_b_exec.empty()){ - - const hs_proposal_message *b_exec = get_proposal( _b_exec ); - EOS_ASSERT( b_exec != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", _b_exec) ); - - if (b_exec->get_view_number() >= b.get_view_number() && b_exec->proposal_id != b.proposal_id){ - - fc_elog(_logger, " *** ${id} finality violation detected at height ${block_num}, phase : ${phase}. Proposal ${proposal_id_1} conflicts with ${proposal_id_2}", - ("id", _id) - ("block_num", b.block_num()) - ("phase", b.phase_counter) - ("proposal_id_1", b.proposal_id) - ("proposal_id_2", b_exec->proposal_id)); - - _b_finality_violation = b.proposal_id; - - //protocol failure - return; - } - } - - commit(b); //todo : ensure that block is marked irreversible / lib is updated etc. - - //todo : asynchronous? - //write_state(_liveness_state_file , _liveness_state); - - fc_tlog(_logger, " === last executed proposal : #${block_num} ${block_id}", ("block_num", b.block_num())("block_id", b.block_id)); - - _b_exec = b.proposal_id; //decide phase on b - _block_exec = b.block_id; - - gc_proposals( b.get_key()-1); - } - else { - fc_elog(_logger, " *** ${id} could not verify direct parent relationship", ("id",_id)); - fc_elog(_logger, " *** b_2 ${b_2}", ("b_2", b_2)); - fc_elog(_logger, " *** b_1 ${b_1}", ("b_1", b_1)); - fc_elog(_logger, " *** b ${b}", ("b", b)); - } - } - - void qc_chain::gc_proposals(uint64_t cutoff){ - //fc_tlog(_logger, " === garbage collection on old data"); - - auto& seen_votes_index = _seen_votes_store.get(); - seen_votes_index.erase(seen_votes_index.begin(), seen_votes_index.upper_bound(cutoff)); - - auto end_itr = _proposal_store.get().upper_bound(cutoff); - while (_proposal_store.get().begin() != end_itr){ - auto itr = _proposal_store.get().begin(); - fc_tlog(_logger, " === ${id} erasing ${block_num} ${phase_counter} ${block_id} proposal_id ${proposal_id}", - ("id", _id) - ("block_num", itr->block_num()) - ("phase_counter", itr->phase_counter) - ("block_id", itr->block_id) - ("proposal_id", itr->proposal_id)); - _proposal_store.get().erase(itr); - } - } - -void qc_chain::commit(const hs_proposal_message& initial_proposal) { - std::vector proposal_chain; - proposal_chain.reserve(10); - - const hs_proposal_message* p = &initial_proposal; - while (p) { - fc_tlog(_logger, " === attempting to commit proposal #${block_num}:${phase} ${prop_id} block_id: ${block_id} parent_id: ${parent_id}", - ("block_num", p->block_num())("prop_id", p->proposal_id)("block_id", p->block_id) - ("phase", p->phase_counter)("parent_id", p->parent_id)); - - const hs_proposal_message* last_exec_prop = get_proposal(_b_exec); - EOS_ASSERT(last_exec_prop != nullptr || _b_exec.empty(), chain_exception, - "expected hs_proposal ${id} not found", ("id", _b_exec)); - - if (last_exec_prop != nullptr) { - fc_tlog(_logger, " === _b_exec proposal #${block_num}:${phase} ${prop_id} block_id: ${block_id} parent_id: ${parent_id}", - ("block_num", last_exec_prop->block_num())("prop_id", last_exec_prop->proposal_id) - ("block_id", last_exec_prop->block_id)("phase", last_exec_prop->phase_counter) - ("parent_id", last_exec_prop->parent_id)); - - fc_tlog(_logger, " *** last_exec_prop ${prop_id_1} ${phase_1} vs proposal ${prop_id_2} ${phase_2} ", - ("prop_id_1", last_exec_prop->block_num())("phase_1", last_exec_prop->phase_counter) - ("prop_id_2", p->block_num())("phase_2", p->phase_counter)); - } else { - fc_tlog(_logger, " === _b_exec proposal is null vs proposal ${prop_id_2} ${phase_2} ", - ("prop_id_2", p->block_num())("phase_2", p->phase_counter)); - } - - - bool exec_height_check = _b_exec.empty() || last_exec_prop->get_view_number() < p->get_view_number(); - if (exec_height_check) { - proposal_chain.push_back(p); // add proposal to vector for further processing - p = get_proposal(p->parent_id); // process parent if non-null - } else { - fc_elog(_logger, " *** ${id} sequence not respected on #${block_num}:${phase} proposal_id: ${prop_id}", - ("id", _id)("block_num", p->block_num())("phase", p->phase_counter)("prop_id", p->proposal_id)); - break; - } - } - - if (!proposal_chain.empty()) { - // commit all ancestor blocks sequentially first (hence the reverse) - for (auto p : boost::adaptors::reverse(proposal_chain)) { - // Execute commands [...] - ; - } - - auto p = proposal_chain.back(); - if (proposal_chain.size() > 1) { - auto last = proposal_chain.front(); - fc_dlog(_logger, " === ${id} committed {num} proposals from #${block_num}:${phase} block_id: ${block_id} " - "proposal_id: ${prop_id} to #${block_num_2}:${phase_2} block_id: ${block_id_2} proposal_id: ${prop_id_2}", - ("id", _id)("block_num", p->block_num())("phase", p->phase_counter)("block_id", p->block_id) - ("prop_id", p->proposal_id)("num", proposal_chain.size())("block_num_2", last->block_num()) - ("phase_2", last->phase_counter)("block_id_2", last->block_id)("prop_id_2", last->proposal_id)); - } else { - fc_dlog(_logger, " === ${id} committed proposal #${block_num}:${phase} block_id: ${block_id} proposal_id: ${prop_id}", - ("id", _id)("block_num", p->block_num())("phase", p->phase_counter) - ("block_id", p->block_id)("prop_id", p->proposal_id)); - } - } -} - -} diff --git a/libraries/chain/hotstuff/test/CMakeLists.txt b/libraries/chain/hotstuff/test/CMakeLists.txt index 1ebb5d3810..4642bc850b 100644 --- a/libraries/chain/hotstuff/test/CMakeLists.txt +++ b/libraries/chain/hotstuff/test/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable( test_hotstuff test_hotstuff.cpp test_hotstuff_state.cpp hotstuff_tools.cpp test_pacemaker.cpp) +add_executable( test_hotstuff finality_misc_tests.cpp ) target_link_libraries( test_hotstuff eosio_chain fc Boost::unit_test_framework) add_test(NAME test_hotstuff COMMAND test_hotstuff WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/libraries/chain/hotstuff/test/hotstuff_tools.cpp b/libraries/chain/hotstuff/test/finality_misc_tests.cpp similarity index 79% rename from libraries/chain/hotstuff/test/hotstuff_tools.cpp rename to libraries/chain/hotstuff/test/finality_misc_tests.cpp index a5b62ffda0..592f225013 100644 --- a/libraries/chain/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/chain/hotstuff/test/finality_misc_tests.cpp @@ -1,4 +1,6 @@ -#include +#define BOOST_TEST_MODULE hotstuff + +#include #include #include @@ -6,58 +8,7 @@ #include #include -#include - -BOOST_AUTO_TEST_CASE(view_number_tests) try { - using namespace eosio::chain; - - hs_proposal_message hspm_1; - hs_proposal_message hspm_2; - hs_proposal_message hspm_3; - hs_proposal_message hspm_4; - hs_proposal_message hspm_5; - - hspm_1.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_1.phase_counter = 0; - - hspm_2.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.phase_counter = 1; - - hspm_3.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_3.phase_counter = 0; - - hspm_4.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_4.phase_counter = 1; - - hspm_5.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_5.phase_counter = 2; - - view_number vn_1 = hspm_1.get_view_number(); - view_number vn_2 = hspm_2.get_view_number(); - view_number vn_3 = hspm_3.get_view_number(); - view_number vn_4 = hspm_4.get_view_number(); - view_number vn_5 = hspm_5.get_view_number(); - - //test getters - BOOST_CHECK_EQUAL(vn_1.block_height(), 194217067); - BOOST_CHECK_EQUAL(vn_1.phase_counter(), 0); - - BOOST_CHECK_NE(vn_1, vn_2); - BOOST_CHECK_LT(vn_1, vn_2); - BOOST_CHECK_LT(vn_2, vn_3); - BOOST_CHECK_LT(vn_3, vn_4); - BOOST_CHECK_LT(vn_4, vn_5); - BOOST_CHECK_LE(vn_4, vn_5); - BOOST_CHECK_LE(vn_2, vn_3); - -//test constructor - - view_number vn_6 = view_number(194217068, 2); - - BOOST_CHECK_EQUAL(vn_5, vn_6); - -} FC_LOG_AND_RETHROW(); - +#include // ----------------------------------------------------------------------------- // Allow boost to print `pending_quorum_certificate::state_t` diff --git a/libraries/chain/hotstuff/test/test_hotstuff.cpp b/libraries/chain/hotstuff/test/test_hotstuff.cpp deleted file mode 100644 index b2612996c6..0000000000 --- a/libraries/chain/hotstuff/test/test_hotstuff.cpp +++ /dev/null @@ -1,1477 +0,0 @@ -#define BOOST_TEST_MODULE hotstuff - -#include - -#include -#include -#include - -#include - -#include "test_pacemaker.hpp" -#include -#include -#include - -#include -#include - -using namespace eosio::chain; - -using std::cout; - -std::vector ids{ block_id_type("00000001d49031dba775bd2b44fd339a329ef462aaf019e5b75b4cd9609a0c39"), - block_id_type("0000000202b23f86652ae43cba4bec5579c8c7133c14011a6f8d93b316530684"), - block_id_type("00000003a5a001518358977e84a3f6abf87bf32a6e739ced9a7a3f6b0b8bf330")}; - -std::vector alternate_ids{ block_id_type("00000001d49031dba775bd2b44fd339a329ef462aaf019e5b75b4cd9609a0c31"), - block_id_type("0000000202b23f86652ae43cba4bec5579c8c7133c14011a6f8d93b316530681"), - block_id_type("00000003a5a001518358977e84a3f6abf87bf32a6e739ced9a7a3f6b0b8bf331")}; - -//list of unique replicas for our test -std::vector unique_replicas { - "bpa"_n, "bpb"_n, "bpc"_n, - "bpd"_n, "bpe"_n, "bpf"_n, - "bpg"_n, "bph"_n, "bpi"_n, - "bpj"_n, "bpk"_n, "bpl"_n, - "bpm"_n, "bpn"_n, "bpo"_n, - "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, std::vector replica_keys){ - _qc_chains.clear(); - - 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_pub_priv_key_map_t keys{{sk.get_public_key().to_string(), sk.to_string()}}; - qc_chain *qcc_ptr = new qc_chain(replica_keys[i].to_string(), &tpm, {replicas[i]}, keys, hotstuff_logger, std::string()); - std::shared_ptr qcc_shared_ptr(qcc_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_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++; - else if (v_index == 1) votes_count++; - else if (v_index == 2) 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_views : " << new_views_count << "\n"; - std::cout << "\n"; - } - - void print_pm_state(test_pacemaker &tpm){ - std::cout << "\n"; - std::cout << " leader : " << tpm.get_leader() << "\n"; - std::cout << " next leader : " << tpm.get_next_leader() << "\n"; - std::cout << " proposer : " << tpm.get_proposer() << "\n"; - std::cout << " current block id : " << tpm.get_current_block_id().str() << "\n"; - std::cout << "\n"; - } - - 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 ); - const hs_proposal_message *qc = fs.get_proposal( fs.high_qc.proposal_id ); - 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 << " 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 << " 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 << " 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 << " 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"; - } - - void dispatch(test_pacemaker& tpm, test_pacemaker::hotstuff_message_index msg_type, const std::string& memo = "") { - for (int i=0;i<1000;++i) { - auto sent = tpm.dispatch(memo, msg_type); - if (sent.size() == 0) - return; // success, propagation has stopped - } - BOOST_FAIL("Hotstuff message propagation likely infinite loop detected."); - } -}; - -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 - b.flip(1); - b.flip(2); - b.flip(3); - b.flip(4); - b.flip(5); - b.flip(6); - 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"; - -} FC_LOG_AND_RETHROW(); - -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_policy 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_policy fset; - fset.threshold = 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_replica_keys); // complete connection graph - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.dispatch(""); //propagating votes on new proposal (decide on first block) - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.dispatch(""); //send proposal to replicas (decide on second block) - - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpb as well - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.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_replica_keys); // complete connection graph - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("e297a3ea4cbacde037f796dfef36bfbeabc38e3c22cf35fab663e13e59ad9534")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("e297a3ea4cbacde037f796dfef36bfbeabc38e3c22cf35fab663e13e59ad9534")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("e297a3ea4cbacde037f796dfef36bfbeabc38e3c22cf35fab663e13e59ad9534")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpb as well - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.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_3) try { - - //test leader rotation - test_pacemaker tpm; - tpm.connect(unique_replica_keys); // complete connection graph - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - qcc_bpb->second->get_state(fs_bpb); - auto qcc_bpc = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpc"_n; }); - finalizer_state fs_bpc; - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.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_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpa as well - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpc as well - qcc_bpc->second->get_state(fs_bpc); - BOOST_CHECK_EQUAL(fs_bpc.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_exec.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_4) try { - - //test loss and recovery of liveness on new block - test_pacemaker tpm; - tpm.connect(unique_replica_keys); // complete connection graph - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - qcc_bpb->second->get_state(fs_bpb); - auto qcc_bpi = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpi"_n; }); - finalizer_state fs_bpi; - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.dispatch(""); //propagating votes on new proposal (precommitQC on first block) - tpm.deactivate("bpb"_n); //loss of liveness as 7 finalizers out of 21 go offline - tpm.deactivate("bpc"_n); - tpm.deactivate("bpd"_n); - tpm.deactivate("bpe"_n); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.activate("bpb"_n); - tpm.activate("bpc"_n); - tpm.activate("bpd"_n); - tpm.activate("bpe"_n); - tpm.activate("bpf"_n); - tpm.activate("bpg"_n); - tpm.activate("bph"_n); - - 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) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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, ""); - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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, ""); - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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, ""); - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - -//ht.print_bp_state("bpb"_n, ""); - //check bpa as well - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - -//ht.print_bp_state("bpi"_n, ""); - qcc_bpi->second->get_state(fs_bpi); - BOOST_CHECK_EQUAL(fs_bpi.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpi.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpi.b_exec.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_5) try { - - //test finality violation - - std::vector honest_replica_set_1 { - "bpb"_n, - "bpe"_n, - "bph"_n, - "bpk"_n, - "bpn"_n, - "bpq"_n }; - - std::vector honest_replica_set_2 { - "bpa"_n, - "bpd"_n, - "bpg"_n, - "bpj"_n, - "bpm"_n, - "bpp"_n }; - - std::vector byzantine_set { - "bpc"_n, - "bpf"_n, - "bpi"_n, - "bpl"_n, - "bpo"_n, - "bpr"_n, - "bpu"_n, - "bps"_n, - "bpt"_n }; - - 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_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_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_policy fset_1 = create_fs(replica_set_1); - finalizer_policy fset_2 = create_fs(replica_set_2); - - //simulating a fork, where - test_pacemaker tpm1; - tpm1.connect(replica_set_1); // complete connection graph - test_pacemaker tpm2; - tpm2.connect(replica_set_2); // complete connection graph - - hotstuff_test_handler ht1; - hotstuff_test_handler ht2; - - 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_finalizer_policy(fset_1); - tpm2.set_proposer("bpf"_n); //byzantine leader - tpm2.set_leader("bpf"_n); - tpm2.set_next_leader("bpf"_n); - - tpm2.set_finalizer_policy(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); - - 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(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.dispatch(""); - tpm1.dispatch(""); - tpm2.dispatch(""); - tpm2.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.dispatch(""); - tpm1.dispatch(""); - tpm2.dispatch(""); - tpm2.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.dispatch(""); - tpm1.dispatch(""); - tpm2.dispatch(""); - tpm2.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.set_current_block_id(ids[1]); //first block - tpm2.set_current_block_id(alternate_ids[1]); //first block - - tpm1.beat(); //produce second block and associated proposal - tpm2.beat(); //produce second block and associated proposal - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - tpm1.pipe(tpm2.dispatch("")); - tpm1.dispatch(""); - - qcc_bpe->second->get_state(fs_bpe); - BOOST_CHECK_EQUAL(fs_bpe.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpe.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpe.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - BOOST_CHECK_EQUAL(fs_bpe.b_finality_violation.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - -} FC_LOG_AND_RETHROW(); - -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_replica_keys); // start with a complete connection graph, then subtract - - // force an additional hop of communication between A and B (requires message propagation to work) - tpm.disconnect( { unique_replica_keys[0], unique_replica_keys[1] } ); // 0=bpa, 1=bpb; tpm.disconnect( { "bpa"_n, "bpb"_n } ); - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - qcc_bpb->second->get_state(fs_bpb); - auto qcc_bpc = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpc"_n; }); - finalizer_state fs_bpc; - qcc_bpc->second->get_state(fs_bpc); - - tpm.set_current_block_id(ids[0]); //first block - - tpm.beat(); //produce first block and associated proposal - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on first block) - - 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")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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 - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //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 - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpa as well - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpc as well - qcc_bpc->second->get_state(fs_bpc); - BOOST_CHECK_EQUAL(fs_bpc.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_exec.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_8) try { - - //test optimistic responsiveness (3 confirmations per block) - //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_replica_keys); // complete connection graph - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - 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")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - // produce duplicate votes: should not fail the test if qc_chain is filtering duplicate votes. - // we cannot use pipe(dispatch()) here because pipe will append the duplicate votes like this to the pending message queue: - // abcdefghijklmnopqrstuabcdefghijklmnopqrstu - // however, after receiving 15 unique votes, the quorum is met and the duplicate votes are discared by the quorum rule. - // tpm.duplicate() will duplicate like this: aabbccddee...ssttuu, which will exercise the duplicate vote filter (bitset test). - tpm.duplicate(test_pacemaker::hs_vote); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.dispatch(""); //propagating votes on new proposal (decide on first block) - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - 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); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - tpm.dispatch(""); //send proposal to replicas (decide on second block) - - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpb as well - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.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_9) try { - - //test leader rotation with a star toplogy (message propagation test) - - test_pacemaker tpm; - for (size_t i=0; i sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - qcc_bpb->second->get_state(fs_bpb); - auto qcc_bpc = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpc"_n; }); - finalizer_state fs_bpc; - qcc_bpc->second->get_state(fs_bpc); - - tpm.set_current_block_id(ids[0]); //first block - - tpm.beat(); //produce first block and associated proposal - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on first block) - - 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")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on first block) - - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.b_leaf.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); //4b4 - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8"));//a250 - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000"));//00 - 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 - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_new_view); - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_new_view); - ht.dispatch(tpm, test_pacemaker::hs_vote); //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 - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpa as well - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpc as well - qcc_bpc->second->get_state(fs_bpc); - BOOST_CHECK_EQUAL(fs_bpc.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_exec.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_10) try { - - //test leader rotation with a ring topology (message propagation test) - - test_pacemaker tpm; - - // zigzag to separate bpa, bpb and bpc. - // cut connections 11,1 *and* 10,0 to see the test fail. - // turning the ring into a line by cutting just one connection is not enough to fail the test. - tpm.connect( { unique_replica_keys[ 0], unique_replica_keys[11] } ); - tpm.connect( { unique_replica_keys[11], unique_replica_keys[ 1] } ); //cut this to fail (1 of 2) - tpm.connect( { unique_replica_keys[ 1], unique_replica_keys[12] } ); - tpm.connect( { unique_replica_keys[12], unique_replica_keys[ 2] } ); - tpm.connect( { unique_replica_keys[ 2], unique_replica_keys[13] } ); - tpm.connect( { unique_replica_keys[13], unique_replica_keys[ 3] } ); - tpm.connect( { unique_replica_keys[ 3], unique_replica_keys[14] } ); - tpm.connect( { unique_replica_keys[14], unique_replica_keys[ 4] } ); - tpm.connect( { unique_replica_keys[ 4], unique_replica_keys[15] } ); - tpm.connect( { unique_replica_keys[15], unique_replica_keys[ 5] } ); - tpm.connect( { unique_replica_keys[ 5], unique_replica_keys[16] } ); - tpm.connect( { unique_replica_keys[16], unique_replica_keys[ 6] } ); - tpm.connect( { unique_replica_keys[ 6], unique_replica_keys[17] } ); - tpm.connect( { unique_replica_keys[17], unique_replica_keys[ 7] } ); - tpm.connect( { unique_replica_keys[ 7], unique_replica_keys[18] } ); - tpm.connect( { unique_replica_keys[18], unique_replica_keys[ 8] } ); - tpm.connect( { unique_replica_keys[ 8], unique_replica_keys[19] } ); - tpm.connect( { unique_replica_keys[19], unique_replica_keys[ 9] } ); - tpm.connect( { unique_replica_keys[ 9], unique_replica_keys[20] } ); - tpm.connect( { unique_replica_keys[20], unique_replica_keys[10] } ); - tpm.connect( { unique_replica_keys[10], unique_replica_keys[ 0] } ); //cut this to fail (2 of 2) - - hotstuff_test_handler ht; - std::vector sks = map_to_sks(unique_replica_keys); - finalizer_policy 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_finalizer_policy(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; - qcc_bpa->second->get_state(fs_bpa); - auto qcc_bpb = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpb"_n; }); - finalizer_state fs_bpb; - qcc_bpb->second->get_state(fs_bpb); - auto qcc_bpc = std::find_if(ht._qc_chains.begin(), ht._qc_chains.end(), [&](const auto& q){ return q.first == "bpc"_n; }); - finalizer_state fs_bpc; - qcc_bpc->second->get_state(fs_bpc); - - tpm.set_current_block_id(ids[0]); //first block - - tpm.beat(); //produce first block and associated proposal - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on first block) - - 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")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - 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 - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on first block) - - ht.dispatch(tpm, test_pacemaker::hs_new_view); - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on first block) - - 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("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_new_view); - ht.dispatch(tpm, test_pacemaker::hs_vote); //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 - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (prepare on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //send votes on proposal (prepareQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (precommit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (precommitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (commit on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - ht.dispatch(tpm, test_pacemaker::hs_vote); //propagating votes on new proposal (commitQC on second block) - - ht.dispatch(tpm, test_pacemaker::hs_proposal); //send proposal to replicas (decide on second block) - - qcc_bpb->second->get_state(fs_bpb); - BOOST_CHECK_EQUAL(fs_bpb.b_leaf.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.high_qc.proposal_id.str(), std::string("848b788b0140cec9c40893b4d1ba1eef238e8e6986212d9d7e7daaba844a2097")); - BOOST_CHECK_EQUAL(fs_bpb.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpb.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpa as well - qcc_bpa->second->get_state(fs_bpa); - BOOST_CHECK_EQUAL(fs_bpa.high_qc.proposal_id.str(), std::string("a252070cd26d3b231ab2443b9ba97f57fc72e50cca04a020952e45bc7e2d27a8")); - BOOST_CHECK_EQUAL(fs_bpa.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpa.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - //check bpc as well - qcc_bpc->second->get_state(fs_bpc); - BOOST_CHECK_EQUAL(fs_bpc.high_qc.proposal_id.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_lock.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - BOOST_CHECK_EQUAL(fs_bpc.b_exec.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - - BOOST_CHECK_EQUAL(fs_bpa.b_finality_violation.str(), std::string("0000000000000000000000000000000000000000000000000000000000000000")); - -} FC_LOG_AND_RETHROW(); - - - -BOOST_AUTO_TEST_SUITE_END() diff --git a/libraries/chain/hotstuff/test/test_hotstuff_state.cpp b/libraries/chain/hotstuff/test/test_hotstuff_state.cpp deleted file mode 100644 index 1a3e871b9e..0000000000 --- a/libraries/chain/hotstuff/test/test_hotstuff_state.cpp +++ /dev/null @@ -1,153 +0,0 @@ -#include - -#include -#include - -#include - -#include -#include - -#include - -using std::cout; -using namespace eosio::chain; - -BOOST_AUTO_TEST_SUITE(test_hotstuff_state) - -const std::string file_path_1("temp_hs_safety"); -//const std::string file_path_2("temp_hs_liveness"); - -BOOST_AUTO_TEST_CASE(write_safety_state_to_file) try { - - hs_proposal_message hspm_1; - hs_proposal_message hspm_2; - - hspm_1.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = block_id_type(); - hspm_1.phase_counter = 2; - - view_number v_height = hspm_1.get_view_number(); - - hspm_2.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = block_id_type(); - hspm_2.phase_counter = 0; - - fc::sha256 b_lock = get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - safety_state ss; - - ss.set_v_height(fc::crypto::blslib::bls_public_key{}, v_height); - ss.set_b_lock(fc::crypto::blslib::bls_public_key{}, b_lock); - - BOOST_CHECK( state_db_manager::write(file_path_1, ss) ); - - //fc::cfile pfile; - //pfile.set_file_path(file_path_1); - //pfile.open(fc::cfile::truncate_rw_mode); - //pfile.write("force garbage to fail read_safety_state_from_file", 20); - //pfile.close(); - -} FC_LOG_AND_RETHROW(); - -BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { - - safety_state ss; - - BOOST_CHECK( state_db_manager::read(file_path_1, ss) ); - - std::remove(file_path_1.c_str()); - - //test correct values - hs_proposal_message hspm_1; - hs_proposal_message hspm_2; - - hspm_1.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = block_id_type(); - hspm_1.phase_counter = 2; - - view_number v_height = hspm_1.get_view_number(); - - hspm_2.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = block_id_type(); - hspm_2.phase_counter = 0; - - fc::sha256 b_lock = get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - //std::pair ss = get_safety_state(name{""}); - - BOOST_CHECK_EQUAL(ss.get_v_height(fc::crypto::blslib::bls_public_key{}), v_height); - BOOST_CHECK_EQUAL(ss.get_b_lock(fc::crypto::blslib::bls_public_key{}), b_lock); - -} FC_LOG_AND_RETHROW(); - -#warning TODO decide on liveness state file then implement it in qc_chain and then test it here -/*BOOST_AUTO_TEST_CASE(write_liveness_state_to_file) try { - - hs_proposal_message hspm_1; - hs_proposal_message hspm_2; - - hspm_1.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_1.final_on_qc = block_id_type(); - hspm_1.phase_counter = 2; - - fc::sha256 b_exec = get_digest_to_sign(hspm_1.block_id, hspm_1.phase_counter, hspm_1.final_on_qc); - - hspm_2.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = block_id_type(); - hspm_2.phase_counter = 1; - - fc::sha256 b_leaf = get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - //mock quorum_certificate - quorum_certificate high_qc; - - high_qc.proposal_id = fc::sha256("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); - high_qc.active_finalizers = 1245; - high_qc.active_agg_sig = fc::crypto::blslib::bls_signature("SIG_BLS_23PuSu1B72cPe6wxGkKjAaaZqA1Ph79zSoW7omsKKUrnprbA3cJCJVhT48QKUG6ofjYTTg4BA4TrVENWyrxjTomwLX6TGdVg2RYhKH7Kk9X23K5ohuhKQcWQ6AwJJGVSbSp4"); - - liveness_state ls(high_qc, b_leaf, b_exec); - - write_state(file_path_2, ls); - -} FC_LOG_AND_RETHROW(); - -BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { - - liveness_state ls; - - read_state(file_path_2, ls); - - std::remove(file_path_2.c_str()); - - //test correct values - - hs_proposal_message hspm_1; - hs_proposal_message hspm_2; - - hspm_1.block_id = block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = block_id_type(); - hspm_1.phase_counter = 2; - - fc::sha256 b_exec = eosio::hotstuff::get_digest_to_sign(hspm_1.block_id, hspm_1.phase_counter, hspm_1.final_on_qc); - - hspm_2.block_id = block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = block_id_type(); - hspm_2.phase_counter = 1; - - fc::sha256 b_leaf = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - //mock quorum_certificate - quorum_certificate high_qc; - - high_qc.proposal_id = fc::sha256("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); - high_qc.active_finalizers = 1245; - high_qc.active_agg_sig = fc::crypto::blslib::bls_signature("SIG_BLS_23PuSu1B72cPe6wxGkKjAaaZqA1Ph79zSoW7omsKKUrnprbA3cJCJVhT48QKUG6ofjYTTg4BA4TrVENWyrxjTomwLX6TGdVg2RYhKH7Kk9X23K5ohuhKQcWQ6AwJJGVSbSp4"); - - BOOST_CHECK(ls.high_qc == high_qc); - BOOST_CHECK(ls.b_exec == b_exec); - BOOST_CHECK(ls.b_leaf == b_leaf); - -} FC_LOG_AND_RETHROW();*/ - -BOOST_AUTO_TEST_SUITE_END() diff --git a/libraries/chain/hotstuff/test/test_pacemaker.cpp b/libraries/chain/hotstuff/test/test_pacemaker.cpp deleted file mode 100644 index aa914dfab3..0000000000 --- a/libraries/chain/hotstuff/test/test_pacemaker.cpp +++ /dev/null @@ -1,232 +0,0 @@ -#include "test_pacemaker.hpp" -#include - -namespace eosio::chain { - - void test_pacemaker::set_proposer(name proposer) { - _proposer = proposer; - }; - - void test_pacemaker::set_leader(name leader) { - _leader = leader; - }; - - void test_pacemaker::set_next_leader(name next_leader) { - _next_leader = next_leader; - }; - - void test_pacemaker::set_finalizer_policy(const eosio::chain::finalizer_policy& finalizer_policy) { - _finalizer_policy = finalizer_policy; - }; - - void test_pacemaker::set_current_block_id(block_id_type id) { - _current_block_id = id; - }; - - void test_pacemaker::set_quorum_threshold(uint32_t threshold) { - _quorum_threshold = threshold; - } - - void test_pacemaker::add_message_to_queue(const hotstuff_message& msg) { - _pending_message_queue.push_back(msg); - } - - 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); - _net[*it2].insert(*it1); - } - } - } - - 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); - _net[*it2].erase(*it1); - } - } - } - - bool test_pacemaker::is_connected(std::string node1, std::string node2) { - auto it = _net.find(node1); - if (it == _net.end()) - return false; - return it->second.count(node2) > 0; - } - - void test_pacemaker::pipe(const std::vector& messages) { - auto itr = messages.begin(); - while (itr != messages.end()) { - _pending_message_queue.push_back(*itr); - itr++; - } - } - - void test_pacemaker::dispatch(std::string memo, int count, hotstuff_message_index msg_type) { - for (int i = 0 ; i < count ; i++) { - this->dispatch(memo, msg_type); - } - } - - void test_pacemaker::duplicate(hotstuff_message_index msg_type) { - std::vector dup; - for (const auto& msg_pair : _pending_message_queue) { - const auto& [sender_id, msg] = msg_pair; - size_t v_index = msg.index(); - dup.push_back(msg_pair); - if (v_index == msg_type) - dup.push_back(msg_pair); - } - _pending_message_queue = std::move(dup); - } - - std::vector test_pacemaker::dispatch(std::string memo, hotstuff_message_index msg_type) { - - std::vector dispatched_messages; - std::vector kept_messages; - - std::vector message_queue = _pending_message_queue; - - // Need to clear the persisted message queue here because new ones are inserted in - // the loop below as a side-effect of the on_hs...() calls. Messages that are not - // propagated in the loop go into kept_messages and are reinserted after the loop. - _pending_message_queue.clear(); - - size_t proposals_count = 0; - size_t votes_count = 0; - size_t new_views_count = 0; - - for (const auto& msg_pair : message_queue) { - const auto& [sender_id, msg] = msg_pair; - size_t v_index = msg.index(); - - if (msg_type == hs_all_messages || msg_type == v_index) { - - if (v_index == hs_proposal) { - ++proposals_count; - on_hs_proposal_msg(std::get(msg), sender_id); - } else if (v_index == hs_vote) { - ++votes_count; - on_hs_vote_msg(std::get(msg), sender_id); - } else if (v_index == hs_new_view) { - ++new_views_count; - on_hs_new_view_msg(std::get(msg), sender_id); - } else { - throw std::runtime_error("unknown message variant"); - } - - dispatched_messages.push_back(msg_pair); - } else { - kept_messages.push_back(msg_pair); - } - } - - _pending_message_queue.insert(_pending_message_queue.end(), kept_messages.begin(), kept_messages.end()); - - if (memo != "") { - ilog(" === ${memo} : ", ("memo", memo)); - } - - ilog(" === pacemaker dispatched ${proposals} proposals, ${votes} votes, ${new_views} new_views", - ("proposals", proposals_count) - ("votes", votes_count) - ("new_views", new_views_count)); - - return dispatched_messages; - } - - void test_pacemaker::activate(name replica) { - auto qc_itr = _qcc_store.find( replica ); - if (qc_itr == _qcc_store.end()) - throw std::runtime_error("replica not found"); - - _qcc_deactivated.erase(replica); - } - - void test_pacemaker::deactivate(name replica) { - auto qc_itr = _qcc_store.find( replica ); - if (qc_itr == _qcc_store.end()) - throw std::runtime_error("replica not found"); - - _qcc_deactivated.insert(replica); - } - - name test_pacemaker::get_proposer() { - return _proposer; - }; - - name test_pacemaker::get_leader() { - return _leader; - }; - - name test_pacemaker::get_next_leader() { - return _next_leader; - }; - - const finalizer_policy& test_pacemaker::get_finalizer_policy() { - return _finalizer_policy; - }; - - block_id_type test_pacemaker::get_current_block_id() { - return _current_block_id; - }; - - uint32_t test_pacemaker::get_quorum_threshold() { - return _quorum_threshold; - }; - - void test_pacemaker::beat() { - auto itr = _qcc_store.find( _proposer ); - if (itr == _qcc_store.end()) - throw std::runtime_error("proposer not found"); - std::shared_ptr & qcc_ptr = itr->second; - qcc_ptr->on_beat(); - }; - - void test_pacemaker::register_qc_chain(name name, std::shared_ptr qcc_ptr) { - auto itr = _qcc_store.find( name ); - if (itr != _qcc_store.end()) - throw std::runtime_error("duplicate qc chain"); - else - _qcc_store.emplace( name, qcc_ptr ); - }; - - - 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 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_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(uint32_t sender_peer, const hs_message_warning code) { } - - 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 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_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); - } - } - -} // namespace eosio::hotstuff diff --git a/libraries/chain/hotstuff/test/test_pacemaker.hpp b/libraries/chain/hotstuff/test/test_pacemaker.hpp deleted file mode 100644 index 3849441fa0..0000000000 --- a/libraries/chain/hotstuff/test/test_pacemaker.hpp +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once -#include -#include - -//#include - -namespace eosio::chain { - - class test_pacemaker : public base_pacemaker { - public: - - using hotstuff_message = std::pair>; - - enum hotstuff_message_index { - hs_proposal = 0, - hs_vote = 1, - hs_new_view = 2, - hs_all_messages - }; - - //class-specific functions - - bool is_qc_chain_active(const name& qcc_name) { return _qcc_deactivated.find(qcc_name) == _qcc_deactivated.end(); } - - void set_proposer(name proposer); - - void set_leader(name leader); - - void set_next_leader(name next_leader); - - void set_finalizer_policy(const eosio::chain::finalizer_policy& finalizer_policy); - - void set_current_block_id(block_id_type id); - - void set_quorum_threshold(uint32_t threshold); - - void add_message_to_queue(const hotstuff_message& msg); - - void connect(const std::vector& nodes); - - void disconnect(const std::vector& nodes); - - bool is_connected(std::string node1, std::string node2); - - void pipe(const std::vector& messages); - - void duplicate(hotstuff_message_index msg_type); - - void dispatch(std::string memo, int count, hotstuff_message_index msg_type = hs_all_messages); - - std::vector dispatch(std::string memo, hotstuff_message_index msg_type = hs_all_messages); - - void activate(name replica); - void deactivate(name replica); - - // must be called to register every qc_chain object created by the testcase - void register_qc_chain(name name, std::shared_ptr qcc_ptr); - - void beat(); - - void on_hs_vote_msg(const 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 - - //base_pacemaker interface functions - - name get_proposer() override; - name get_leader() override; - name get_next_leader() override; - const finalizer_policy& get_finalizer_policy() override; - - block_id_type get_current_block_id() override; - - uint32_t get_quorum_threshold() override; - - void send_hs_proposal_msg(const hs_proposal_message & msg, const std::string& id, const std::optional& exclude_peer) override; - void send_hs_vote_msg(const vote_message & msg, const std::string& id, const std::optional& exclude_peer) override; - void send_hs_new_view_msg(const hs_new_view_message & msg, const std::string& id, const std::optional& exclude_peer) override; - - void send_hs_message_warning(uint32_t sender_peer, const hs_message_warning code) override; - - private: - - std::vector _pending_message_queue; - - // qc_chain id to qc_chain object - map> _qcc_store; - - // qc_chain ids in this set are currently deactivated - set _qcc_deactivated; - - // 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; - - name _proposer; - name _leader; - name _next_leader; - - finalizer_policy _finalizer_policy; - - block_id_type _current_block_id; - -#warning calculate from schedule - uint32_t _quorum_threshold = 15; //todo : calculate from schedule - }; - -} // eosio::chain diff --git a/libraries/chain/include/eosio/chain/hotstuff/base_pacemaker.hpp b/libraries/chain/include/eosio/chain/hotstuff/base_pacemaker.hpp deleted file mode 100644 index e3afc5d94c..0000000000 --- a/libraries/chain/include/eosio/chain/hotstuff/base_pacemaker.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace eosio::chain { - - // Abstract pacemaker; a reference of this type will only be used by qc_chain, as qc_chain - // cannot know which environment it is in. - // All other pacemaker clients will be interacting with a reference to the concrete class: - // - Testers will access a test_pacemaker reference; - // - Real-world code will access a chain_pacemaker reference. - class base_pacemaker { - public: - - virtual ~base_pacemaker() = default; - - //TODO: discuss -#warning discuss - virtual uint32_t get_quorum_threshold() = 0; - - virtual block_id_type get_current_block_id() = 0; - - virtual name get_proposer() = 0; - virtual name get_leader() = 0; - virtual name get_next_leader() = 0; - virtual const finalizer_policy& get_finalizer_policy() = 0; - - //outbound communications; 'id' is the producer name (can be ignored if/when irrelevant to the implementer) - virtual void send_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; - virtual void send_hs_vote_msg(const vote_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; - virtual void send_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer = std::nullopt) = 0; - - virtual void send_hs_message_warning(uint32_t sender_peer, hs_message_warning code) = 0; - - }; - -} // namespace eosio::hotstuff diff --git a/libraries/chain/include/eosio/chain/hotstuff/chain_pacemaker.hpp b/libraries/chain/include/eosio/chain/hotstuff/chain_pacemaker.hpp deleted file mode 100644 index 9df37713fc..0000000000 --- a/libraries/chain/include/eosio/chain/hotstuff/chain_pacemaker.hpp +++ /dev/null @@ -1,88 +0,0 @@ -#pragma once - -#include -#include - -#include - -#include - -namespace eosio::chain { - - class controller; - - class chain_pacemaker : public base_pacemaker { - public: - - //class-specific functions - - chain_pacemaker(controller* chain, - std::set my_producers, - bls_pub_priv_key_map_t finalizer_keys, - fc::logger& logger); - void register_bcast_function(std::function&, const hs_message&)> broadcast_hs_message); - void register_warn_function(std::function warning_hs_message); - - void beat(); - - void on_hs_msg(const uint32_t connection_id, const hs_message& msg); - - void get_state(finalizer_state& fs) const; - - //base_pacemaker interface functions - - name get_proposer() final; - name get_leader() final; - name get_next_leader() final; - const finalizer_policy& get_finalizer_policy() final; - - block_id_type get_current_block_id() final; - - uint32_t get_quorum_threshold() final; - - void send_hs_proposal_msg(const hs_proposal_message& msg, const std::string& id, const std::optional& exclude_peer) final; - void send_hs_vote_msg(const vote_message& msg, const std::string& id, const std::optional& exclude_peer) final; - void send_hs_new_view_msg(const hs_new_view_message& msg, const std::string& id, const std::optional& exclude_peer) final; - - void send_hs_message_warning(uint32_t sender_peer, hs_message_warning code) final; - - private: - void on_accepted_block( const signed_block_ptr& block ); - void on_irreversible_block( const signed_block_ptr& block ); - - void on_hs_proposal_msg(const uint32_t connection_id, const hs_proposal_message& msg); //consensus msg event handler - void on_hs_vote_msg(const uint32_t connection_id, const vote_message& msg); //confirmation msg event handler - void on_hs_new_view_msg(const uint32_t connection_id, const hs_new_view_message& msg); //new view msg event handler - private: - - // This serializes all messages (high-level requests) to the qc_chain core. - // For maximum safety, the qc_chain core will only process one request at a time. - // These requests can come directly from the net threads, or indirectly from a - // dedicated finalizer thread (TODO: discuss). -#warning discuss - mutable std::mutex _hotstuff_global_mutex; - - // _state_cache_mutex provides a R/W lock over _state_cache and _state_cache_version, - // which implement a cache of the finalizer_state (_qc_chain::get_state()). - mutable std::shared_mutex _state_cache_mutex; - mutable finalizer_state _state_cache; - mutable std::atomic _state_cache_version = 0; - - chain::controller* _chain = nullptr; // TODO will not be needed once this is merged with PR#1559 - - mutable std::mutex _chain_state_mutex; - block_state_legacy_ptr _head_block_state; - finalizer_policy _active_finalizer_policy; - - boost::signals2::scoped_connection _accepted_block_connection; - boost::signals2::scoped_connection _irreversible_block_connection; - - qc_chain _qc_chain; - std::function&, const hs_message&)> bcast_hs_message = [](const std::optional&, const hs_message&){}; - std::function warn_hs_message = [](uint32_t, const hs_message_warning&){}; - - uint32_t _quorum_threshold = 15; //FIXME/TODO: calculate from schedule - fc::logger& _logger; - }; - -} // namespace eosio::hotstuff diff --git a/libraries/chain/include/eosio/chain/hotstuff/qc_chain.hpp b/libraries/chain/include/eosio/chain/hotstuff/qc_chain.hpp deleted file mode 100644 index 7687bd38f2..0000000000 --- a/libraries/chain/include/eosio/chain/hotstuff/qc_chain.hpp +++ /dev/null @@ -1,230 +0,0 @@ -#pragma once -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include - -namespace eosio::chain { - - template class state_db_manager { - public: - static constexpr uint64_t magic = 0x0123456789abcdef; - static bool write(fc::cfile& pfile, const StateObjectType& sobj) { - if (!pfile.is_open()) - return false; - pfile.seek(0); - pfile.truncate(); - pfile.write((char*)(&magic), sizeof(magic)); - auto data = fc::raw::pack(sobj); - pfile.write(data.data(), data.size()); - pfile.flush(); - return true; - } - static bool read(const std::string& file_path, StateObjectType& sobj) { - if (!std::filesystem::exists(file_path)) - return false; - fc::cfile pfile; - pfile.set_file_path(file_path); - pfile.open("rb"); - pfile.seek_end(0); - if (pfile.tellp() <= 0) - return false; - pfile.seek(0); - try { - uint64_t read_magic; - pfile.read((char*)(&read_magic), sizeof(read_magic)); - if (read_magic != magic) - return false; - auto datastream = pfile.create_datastream(); - StateObjectType read_sobj; - fc::raw::unpack(datastream, read_sobj); - sobj = std::move(read_sobj); - return true; - } catch (...) { - return false; - } - } - static bool write(const std::string& file_path, const StateObjectType& sobj) { - fc::cfile pfile; - pfile.set_file_path(file_path); - pfile.open(fc::cfile::truncate_rw_mode); - return write(pfile, sobj); - } - }; - - using boost::multi_index_container; - using namespace boost::multi_index; - using namespace eosio::chain; - - struct seen_votes { - fc::sha256 proposal_id; // id of proposal being voted on - uint64_t height; // height of the proposal (for GC) - std::set finalizers; // finalizers that have voted on the proposal - }; - - using bls_pub_priv_key_map_t = std::map; - - // Concurrency note: qc_chain is a single-threaded and lock-free decision engine. - // All thread synchronization, if any, is external. - class qc_chain { - public: - - qc_chain() = delete; - - qc_chain(std::string id, base_pacemaker* pacemaker, - std::set my_producers, - const bls_pub_priv_key_map_t& finalizer_keys, - fc::logger& logger, - const std::string& safety_state_file); - - uint64_t get_state_version() const { return _state_version; } // no lock required - - 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: - - void get_state(finalizer_state& fs) const; - - void on_beat(); - - void on_hs_vote_msg(const uint32_t connection_id, const vote_message& msg); - void on_hs_proposal_msg(const uint32_t connection_id, const hs_proposal_message& msg); - void on_hs_new_view_msg(const uint32_t connection_id, const hs_new_view_message& msg); - - private: - - void write_safety_state_file(); - - const hs_proposal_message* get_proposal(const fc::sha256& proposal_id); // returns nullptr if not found - - // returns false if proposal with that same ID already exists at the store of its height - bool insert_proposal(const hs_proposal_message& proposal); - - uint32_t positive_bits_count(const hs_bitset& finalizers); - - hs_bitset update_bitset(const hs_bitset& finalizer_policy, const bls_public_key& finalizer_key); - - void reset_qc(const hs_proposal_message& proposal); - - hs_proposal_message new_proposal_candidate(const block_id_type& block_id, uint8_t phase_counter); - - bool am_i_proposer(); - bool am_i_leader(); - bool am_i_finalizer(); - - // connection_id.has_value() when processing a non-loopback message - void process_proposal(std::optional connection_id, const hs_proposal_message& msg); - void process_vote(std::optional connection_id, const vote_message& msg); - void process_new_view(std::optional connection_id, const hs_new_view_message& msg); - - void create_proposal(const block_id_type& block_id); - - vote_message sign_proposal(const hs_proposal_message& proposal, bool strong, const bls_public_key& finalizer_pub_key, const bls_private_key& finalizer_priv_key); - - //verify that a proposal descends from another - bool extends(const fc::sha256& descendant, const fc::sha256& ancestor); - - //update high qc if required - bool update_high_qc(const valid_quorum_certificate& high_qc); - - //rotate leader if required - void leader_rotation_check(); - - //verify if a proposal should be signed - bool is_node_safe(const hs_proposal_message& proposal); - - //get 3-phase proposal justification - std::vector get_qc_chain(const fc::sha256& proposal_id); - - // connection_id.has_value() when just propagating a received message - void send_hs_proposal_msg(std::optional connection_id, const hs_proposal_message& msg); - void send_hs_vote_msg(std::optional connection_id, const vote_message& msg); - void send_hs_new_view_msg(std::optional connection_id, const hs_new_view_message& msg); - - void send_hs_message_warning(std::optional connection_id, const hs_message_warning code); - - void update(const hs_proposal_message& proposal); - void commit(const hs_proposal_message& proposal); - - void gc_proposals(uint64_t cutoff); - - block_id_type _block_exec; - block_id_type _pending_proposal_block; - safety_state _safety_state; - fc::sha256 _b_leaf; - fc::sha256 _b_exec; - fc::sha256 _b_finality_violation; - valid_quorum_certificate _high_qc; - pending_quorum_certificate _current_qc; - base_pacemaker* _pacemaker = nullptr; - std::set _my_producers; - bls_key_map_t _my_finalizer_keys; - std::string _id; - - std::string _safety_state_file; // if empty, safety state persistence is turned off - fc::cfile _safety_state_file_handle; - - mutable std::atomic _state_version = 1; - - fc::logger& _logger; - - struct by_proposal_id{}; - struct by_proposal_height{}; - - typedef multi_index_container< - hs_proposal_message, - indexed_by< - hashed_unique< - tag, - BOOST_MULTI_INDEX_MEMBER(hs_proposal_message, fc::sha256,proposal_id) - >, - ordered_non_unique< - tag, - BOOST_MULTI_INDEX_CONST_MEM_FUN(hs_proposal_message, uint64_t, get_key) - > - > - > proposal_store_type; - - proposal_store_type _proposal_store; //internal proposals store - - // Possible optimization: merge _proposal_store and _seen_votes_store. - // Store a struct { set seen_votes; hs_proposal_message p; } in the (now single) multi-index. - struct by_seen_votes_proposal_id{}; - struct by_seen_votes_proposal_height{}; - typedef multi_index_container< - seen_votes, - indexed_by< - hashed_unique< - tag, - BOOST_MULTI_INDEX_MEMBER(seen_votes,fc::sha256,proposal_id) - >, - ordered_non_unique< - tag, - BOOST_MULTI_INDEX_MEMBER(seen_votes,uint64_t,height) - > - > - > seen_votes_store_type; - - // given a height, store a map of proposal IDs at that height and the seen votes for it - seen_votes_store_type _seen_votes_store; - }; - -} /// eosio::hotstuff diff --git a/tests/chain_plugin_tests.cpp b/tests/chain_plugin_tests.cpp index f3a18e2e10..bd04915173 100644 --- a/tests/chain_plugin_tests.cpp +++ b/tests/chain_plugin_tests.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include diff --git a/tests/get_producers_tests.cpp b/tests/get_producers_tests.cpp index faf641b8e7..e22620f887 100644 --- a/tests/get_producers_tests.cpp +++ b/tests/get_producers_tests.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/tests/get_table_seckey_tests.cpp b/tests/get_table_seckey_tests.cpp index a7b28eac1c..2693390b64 100644 --- a/tests/get_table_seckey_tests.cpp +++ b/tests/get_table_seckey_tests.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include diff --git a/tests/get_table_tests.cpp b/tests/get_table_tests.cpp index 504f34a39f..156a4d0579 100644 --- a/tests/get_table_tests.cpp +++ b/tests/get_table_tests.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/tests/test_chain_plugin.cpp b/tests/test_chain_plugin.cpp index 06e5c46753..2bb1e99aff 100644 --- a/tests/test_chain_plugin.cpp +++ b/tests/test_chain_plugin.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include