From 372b8bfc3b07ef76135d0e40bc108d71a6a3a417 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Mon, 28 Aug 2023 19:17:40 +0000 Subject: [PATCH 01/11] Replaced hs_proposal_message::get_height() with ::get_view_number(), added unit tests. Work-in-progress towards #1521 --- .../chain/include/eosio/chain/hotstuff.hpp | 39 ++++++++- .../include/eosio/hotstuff/qc_chain.hpp | 6 +- libraries/hotstuff/qc_chain.cpp | 28 +++---- libraries/hotstuff/test/CMakeLists.txt | 2 +- libraries/hotstuff/test/hotstuff_tools.cpp | 62 ++++++++++++++ libraries/hotstuff/test/test_hotstuff.cpp | 1 - .../hotstuff/test/test_hotstuff_state.cpp | 83 +++++++++++++++++++ .../eosio/chain_plugin/chain_plugin.hpp | 4 +- 8 files changed, 202 insertions(+), 23 deletions(-) create mode 100644 libraries/hotstuff/test/hotstuff_tools.cpp create mode 100644 libraries/hotstuff/test/test_hotstuff_state.cpp diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index e51d6968fb..96f75320b8 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -15,6 +15,37 @@ namespace eosio::chain { return (uint64_t{block_height} << 32) | phase_counter; } + struct view_number{ + + view_number(){ + _data = std::make_pair(0,0); + } + view_number(std::pair data){ + _data = data; + } + + auto operator<=>(const view_number&) const = default; + + uint32_t block_height(){ + return _data.first; + } + + uint8_t phase_counter(){ + return _data.second; + } + + uint64_t to_uint64_t(){ + return compute_height(_data.first, _data.second); + } + + std::string to_string(){ + return _data.first + "::" + _data.second; + } + + std::pair _data; + + }; + struct extended_schedule { producer_authority_schedule producer_schedule; std::map bls_pub_keys; @@ -42,7 +73,10 @@ namespace eosio::chain { uint8_t phase_counter = 0; uint32_t block_num() const { return block_header::num_from_id(block_id); } - uint64_t get_height() const { return compute_height(block_header::num_from_id(block_id), phase_counter); }; + uint64_t get_key() const { return compute_height(block_header::num_from_id(block_id), phase_counter); }; + + view_number get_view_number() const { return std::make_pair(block_header::num_from_id(block_id), phase_counter); }; + }; struct hs_new_block_message { @@ -63,7 +97,7 @@ namespace eosio::chain { fc::sha256 b_finality_violation = NULL_PROPOSAL_ID; block_id_type block_exec = NULL_BLOCK_ID; block_id_type pending_proposal_block = NULL_BLOCK_ID; - uint32_t v_height = 0; + eosio::chain::view_number v_height; eosio::chain::quorum_certificate high_qc; eosio::chain::quorum_certificate current_qc; eosio::chain::extended_schedule schedule; @@ -84,6 +118,7 @@ namespace eosio::chain { } //eosio::chain +FC_REFLECT(eosio::chain::view_number, (_data)); FC_REFLECT(eosio::chain::quorum_certificate, (proposal_id)(active_finalizers)(active_agg_sig)); FC_REFLECT(eosio::chain::extended_schedule, (producer_schedule)(bls_pub_keys)); FC_REFLECT(eosio::chain::hs_vote_message, (proposal_id)(finalizer)(sig)); diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index 7918d68865..e8bd57de84 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -133,7 +133,7 @@ namespace eosio::hotstuff { fc::sha256 _b_finality_violation = NULL_PROPOSAL_ID; eosio::chain::quorum_certificate _high_qc; eosio::chain::quorum_certificate _current_qc; - uint32_t _v_height = 0; + eosio::chain::view_number _v_height; eosio::chain::extended_schedule _schedule; base_pacemaker* _pacemaker = nullptr; std::set _my_producers; @@ -164,11 +164,11 @@ namespace eosio::hotstuff { indexed_by< hashed_unique< tag, - BOOST_MULTI_INDEX_MEMBER(hs_proposal_message,fc::sha256,proposal_id) + 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_height) + BOOST_MULTI_INDEX_CONST_MEM_FUN(hs_proposal_message, uint64_t, get_key) > > > proposal_store_type; diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 94d43cdb2b..df1d8ffe58 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -72,7 +72,7 @@ namespace eosio { namespace hotstuff { bool qc_chain::insert_proposal(const hs_proposal_message & proposal) { #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE - uint64_t proposal_height = proposal.get_height(); + uint64_t proposal_height = proposal.get_key(); ps_height_iterator psh_it = _proposal_stores_by_height.find( proposal_height ); if (psh_it == _proposal_stores_by_height.end()) { _proposal_stores_by_height.emplace( proposal_height, proposal_store() ); @@ -328,7 +328,7 @@ namespace eosio { namespace hotstuff { } hs_vote_message qc_chain::sign_proposal(const hs_proposal_message & proposal, name finalizer){ - _v_height = proposal.get_height(); + _v_height = proposal.get_view_number(); digest_type digest = get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); @@ -372,7 +372,7 @@ namespace eosio { namespace hotstuff { } #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE - ps_height_iterator psh_it = _proposal_stores_by_height.find( proposal.get_height() ); + ps_height_iterator psh_it = _proposal_stores_by_height.find( proposal.get_key() ); if (psh_it != _proposal_stores_by_height.end()) { proposal_store & pstore = psh_it->second; @@ -382,8 +382,8 @@ namespace eosio { namespace hotstuff { hs_proposal_message & existing_proposal = ps_it->second; #else //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_height() ); - auto end_itr = _proposal_store.get().upper_bound( proposal.get_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; @@ -734,13 +734,13 @@ namespace eosio { namespace hotstuff { if (new_high_qc_prop == nullptr) return false; - if (new_high_qc_prop->get_height() > old_high_qc_prop->get_height() + if (new_high_qc_prop->get_view_number() > old_high_qc_prop->get_view_number() && is_quorum_met(high_qc, _schedule, *new_high_qc_prop)) { // "The caller does not need this updated on their high_qc structure" -- g //high_qc.quorum_met = true; - fc_tlog(_logger, " === updated high qc, now is : #${get_height} ${proposal_id}", ("get_height", new_high_qc_prop->get_height())("proposal_id", new_high_qc_prop->proposal_id)); + 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; _high_qc.quorum_met = true; _b_leaf = _high_qc.proposal_id; @@ -828,7 +828,7 @@ namespace eosio { namespace hotstuff { } } - if (proposal.get_height() > _v_height) { + if (proposal.get_view_number() > _v_height) { monotony_check = true; } @@ -848,7 +848,7 @@ namespace eosio { namespace hotstuff { 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_height() > b_lock->get_height()) { + if (prop_justification->get_view_number() > b_lock->get_view_number()) { liveness_check = true; } } @@ -949,7 +949,7 @@ namespace eosio { namespace hotstuff { ("b_lock_phase", b_lock->phase_counter)); } - if (_b_lock == NULL_PROPOSAL_ID || b_1.get_height() > b_lock->get_height()){ + if (_b_lock == NULL_PROPOSAL_ID || 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 )); _b_lock = b_1.proposal_id; //commit phase on b1 @@ -980,7 +980,7 @@ namespace eosio { namespace hotstuff { 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_height() >= b.get_height() && b_exec->proposal_id != b.proposal_id){ + 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) @@ -1003,7 +1003,7 @@ namespace eosio { namespace hotstuff { _b_exec = b.proposal_id; //decide phase on b _block_exec = b.block_id; - gc_proposals( b.get_height()-1); + gc_proposals( b.get_key()-1); } else { fc_elog(_logger, " *** ${id} could not verify direct parent relationship", ("id",_id)); @@ -1029,7 +1029,7 @@ namespace eosio { namespace hotstuff { ph_iterator ph_it = _proposal_height.find( p.proposal_id ); EOS_ASSERT( ph_it != _proposal_height.end(), chain_exception, "gc_proposals internal error: no proposal height entry"); uint64_t proposal_height = ph_it->second; - EOS_ASSERT(proposal_height == p.get_height(), chain_exception, "gc_proposals internal error: mismatched proposal height record"); // this check is unnecessary + EOS_ASSERT(proposal_height == p.get_key(), chain_exception, "gc_proposals internal error: mismatched proposal height record"); // this check is unnecessary _proposal_height.erase( ph_it ); ++ps_it; } @@ -1090,7 +1090,7 @@ namespace eosio { namespace hotstuff { if (_b_exec == NULL_PROPOSAL_ID) exec_height_check = true; else - exec_height_check = last_exec_prop->get_height() < proposal.get_height(); + exec_height_check = last_exec_prop->get_view_number() < proposal.get_view_number(); if (exec_height_check){ diff --git a/libraries/hotstuff/test/CMakeLists.txt b/libraries/hotstuff/test/CMakeLists.txt index b147f10496..eda3efb0c5 100644 --- a/libraries/hotstuff/test/CMakeLists.txt +++ b/libraries/hotstuff/test/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable( test_hotstuff test_hotstuff.cpp test_pacemaker.cpp) +add_executable( test_hotstuff test_hotstuff.cpp test_hotstuff_state.cpp hotstuff_tools.cpp test_pacemaker.cpp) target_link_libraries( test_hotstuff hotstuff fc Boost::unit_test_framework) add_test(NAME test_hotstuff COMMAND test_hotstuff WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/libraries/hotstuff/test/hotstuff_tools.cpp b/libraries/hotstuff/test/hotstuff_tools.cpp new file mode 100644 index 0000000000..bb92b6c558 --- /dev/null +++ b/libraries/hotstuff/test/hotstuff_tools.cpp @@ -0,0 +1,62 @@ +#include +#include +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE(view_number_tests) try { + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + eosio::hotstuff::hs_proposal_message hspm_3; + eosio::hotstuff::hs_proposal_message hspm_4; + eosio::hotstuff::hs_proposal_message hspm_5; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_1.phase_counter = 0; + + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.phase_counter = 1; + + hspm_3.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_3.phase_counter = 0; + + hspm_4.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_4.phase_counter = 1; + + hspm_5.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_5.phase_counter = 2; + + eosio::hotstuff::view_number vn_1 = hspm_1.get_view_number(); + eosio::hotstuff::view_number vn_2 = hspm_2.get_view_number(); + eosio::hotstuff::view_number vn_3 = hspm_3.get_view_number(); + eosio::hotstuff::view_number vn_4 = hspm_4.get_view_number(); + eosio::hotstuff::view_number vn_5 = hspm_5.get_view_number(); + + //test getters + + BOOST_CHECK_EQUAL(vn_1.block_height() == 194217067, true); + BOOST_CHECK_EQUAL(vn_1.phase_counter() == 0, true); + + //test operators == true + BOOST_CHECK_EQUAL(vn_1 == vn_1, true); + BOOST_CHECK_EQUAL(vn_1 != vn_2, true); + + BOOST_CHECK_EQUAL(vn_1 < vn_2, true); + BOOST_CHECK_EQUAL(vn_2 < vn_3, true); + BOOST_CHECK_EQUAL(vn_3 < vn_4, true); + BOOST_CHECK_EQUAL(vn_4 < vn_5, true); + + //test operators == false + BOOST_CHECK_EQUAL(vn_1 >= vn_2, false); + BOOST_CHECK_EQUAL(vn_2 > vn_3, false); + + //test constructor + + eosio::hotstuff::view_number vn_6 = eosio::hotstuff::view_number(std::make_pair(194217068, 2)); + + BOOST_CHECK_EQUAL(vn_5 == vn_6, true); + +} FC_LOG_AND_RETHROW(); \ No newline at end of file diff --git a/libraries/hotstuff/test/test_hotstuff.cpp b/libraries/hotstuff/test/test_hotstuff.cpp index cba5789a39..7725d813d4 100644 --- a/libraries/hotstuff/test/test_hotstuff.cpp +++ b/libraries/hotstuff/test/test_hotstuff.cpp @@ -172,7 +172,6 @@ BOOST_AUTO_TEST_CASE(hotstuff_bitset) try { } FC_LOG_AND_RETHROW(); - BOOST_AUTO_TEST_CASE(hotstuff_1) try { //test optimistic responsiveness (3 confirmations per block) diff --git a/libraries/hotstuff/test/test_hotstuff_state.cpp b/libraries/hotstuff/test/test_hotstuff_state.cpp new file mode 100644 index 0000000000..d28a322824 --- /dev/null +++ b/libraries/hotstuff/test/test_hotstuff_state.cpp @@ -0,0 +1,83 @@ +#include +#include + +#include + +#include +#include + +//#include + +using std::cout; + +struct safety_state { + + eosio::chain::view_number v_height; + eosio::chain::view_number b_lock; + +}; + +struct liveness_state { + + eosio::chain::quorum_certificate high_qc; + eosio::chain::view_number b_exec; + +}; + +BOOST_AUTO_TEST_SUITE(test_hotstuff_state) + +const std::string file_path("temp"); + +BOOST_AUTO_TEST_CASE(write_state_to_file) try { + +/* eosio::hotstuff::hs_proposal_message hspm; + + hspm.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm.phase_counter = 0; + + eosio::hotstuff::view_number vn = hspm.get_view_number(); + + BOOST_CHECK_EQUAL(vn.block_height() == 194217067, true); + BOOST_CHECK_EQUAL(vn.phase_counter() == 0, true); +*/ + safety_state ss; + + //ss.test_val = 2; + + // writing + fc::cfile pfile; + pfile.set_file_path(file_path); + pfile.open(fc::cfile::truncate_rw_mode); + auto data = fc::raw::pack(ss); + pfile.write(data.data(), data.size()); + pfile.close(); // or let destructor do it + + bool ok = true; + + BOOST_CHECK_EQUAL(ok, true); + + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_CASE(read_state_from_file) try { + + safety_state ss; + + // reading + fc::cfile pfile; + pfile.set_file_path(file_path); + pfile.open("rb"); + auto ds = pfile.create_datastream(); + fc::raw::unpack(ds, ss); + pfile.close(); // or let destructor do it + + //bool ok = ss.test_val == 2; + + BOOST_CHECK_EQUAL(true, true); + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_SUITE_END() + +FC_REFLECT(safety_state, (v_height)(b_lock)) +FC_REFLECT(liveness_state, (high_qc)(b_exec)) \ No newline at end of file diff --git a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp index c9a9feb6dd..68cbcd29ba 100644 --- a/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp +++ b/plugins/chain_plugin/include/eosio/chain_plugin/chain_plugin.hpp @@ -851,7 +851,7 @@ class read_only : public api_base { justify = p.justify; phase_counter = p.phase_counter; block_height = p.block_num(); - view_number = p.get_height(); + view_number = p.get_key(); } hs_complete_proposal_message() = default; // cleos requires this }; @@ -865,7 +865,7 @@ class read_only : public api_base { fc::sha256 b_finality_violation = chain::NULL_PROPOSAL_ID; chain::block_id_type block_exec = chain::NULL_BLOCK_ID; chain::block_id_type pending_proposal_block = chain::NULL_BLOCK_ID; - uint32_t v_height = 0; + chain::view_number v_height; chain::quorum_certificate high_qc; chain::quorum_certificate current_qc; chain::extended_schedule schedule; From 01764cd09a91d5fd9df93e65d355eea03f044b35 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 29 Aug 2023 11:10:22 +0000 Subject: [PATCH 02/11] Code reorganization, extension of liveness_state definition to include b_leaf, added unit tests --- .../chain/include/eosio/chain/hotstuff.hpp | 72 +++++--- .../include/eosio/hotstuff/qc_chain.hpp | 14 +- libraries/hotstuff/qc_chain.cpp | 25 ++- libraries/hotstuff/test/hotstuff_tools.cpp | 1 + .../hotstuff/test/test_hotstuff_state.cpp | 169 +++++++++++++++--- 5 files changed, 226 insertions(+), 55 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 96f75320b8..0bc1513215 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -11,39 +11,44 @@ namespace eosio::chain { const block_id_type NULL_BLOCK_ID = block_id_type("00"); const fc::sha256 NULL_PROPOSAL_ID = fc::sha256("00"); + static digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ + digest_type h1 = digest_type::hash( std::make_pair( block_id, phase_counter ) ); + digest_type h2 = digest_type::hash( std::make_pair( h1, final_on_qc ) ); + return h2; + } + inline uint64_t compute_height(uint32_t block_height, uint32_t phase_counter) { return (uint64_t{block_height} << 32) | phase_counter; } - struct view_number{ - - view_number(){ - _data = std::make_pair(0,0); + struct view_number { + view_number() : _block_height(0), _phase_counter(0) {} + explicit view_number(std::pair data) : + _block_height(data.first), + _phase_counter(data.second) + { } - view_number(std::pair data){ - _data = data; + + explicit view_number(uint32_t block_height, uint8_t phase_counter) : + _block_height(block_height), + _phase_counter(phase_counter) + { } auto operator<=>(const view_number&) const = default; - uint32_t block_height(){ - return _data.first; - } + uint32_t block_height() const { return _block_height; } + uint8_t phase_counter() const { return _phase_counter; } - uint8_t phase_counter(){ - return _data.second; - } + uint64_t to_uint64_t() const { return compute_height(_block_height, _phase_counter); } + std::string to_string() const { return std::to_string(_block_height) + "::" + std::to_string(_phase_counter); } - uint64_t to_uint64_t(){ - return compute_height(_data.first, _data.second); - } + uint64_t get_key() const { return get_view_number().to_uint64_t(); }; - std::string to_string(){ - return _data.first + "::" + _data.second; - } - - std::pair _data; + view_number get_view_number() const { return view_number{ block_height(), phase_counter() }; }; + uint32_t _block_height; + uint8_t _phase_counter; }; struct extended_schedule { @@ -56,6 +61,9 @@ namespace eosio::chain { fc::unsigned_int active_finalizers = 0; //bitset encoding, following canonical order fc::crypto::blslib::bls_signature active_agg_sig; bool quorum_met = false; + + auto operator<=>(const quorum_certificate&) const = default; + }; struct hs_vote_message { @@ -72,10 +80,12 @@ namespace eosio::chain { quorum_certificate justify; //justification uint8_t phase_counter = 0; + digest_type get_proposal_id() const { return get_digest_to_sign(block_id, phase_counter, final_on_qc); }; + uint32_t block_num() const { return block_header::num_from_id(block_id); } uint64_t get_key() const { return compute_height(block_header::num_from_id(block_id), phase_counter); }; - view_number get_view_number() const { return std::make_pair(block_header::num_from_id(block_id), phase_counter); }; + view_number get_view_number() const { return view_number(std::make_pair(block_header::num_from_id(block_id), phase_counter)); }; }; @@ -111,6 +121,21 @@ namespace eosio::chain { } }; + struct safety_state { + + eosio::chain::view_number v_height; + fc::sha256 b_lock; + + }; + + struct liveness_state { + + eosio::chain::quorum_certificate high_qc; + fc::sha256 b_leaf; + fc::sha256 b_exec; + + }; + using hs_proposal_message_ptr = std::shared_ptr; using hs_vote_message_ptr = std::shared_ptr; using hs_new_view_message_ptr = std::shared_ptr; @@ -118,7 +143,10 @@ namespace eosio::chain { } //eosio::chain -FC_REFLECT(eosio::chain::view_number, (_data)); + +FC_REFLECT(eosio::chain::safety_state, (v_height)(b_lock)) +FC_REFLECT(eosio::chain::liveness_state, (high_qc)(b_leaf)(b_exec)) +FC_REFLECT(eosio::chain::view_number, (_block_height)(_phase_counter)); FC_REFLECT(eosio::chain::quorum_certificate, (proposal_id)(active_finalizers)(active_agg_sig)); FC_REFLECT(eosio::chain::extended_schedule, (producer_schedule)(bls_pub_keys)); FC_REFLECT(eosio::chain::hs_vote_message, (proposal_id)(finalizer)(sig)); diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index e8bd57de84..1e9450bdf1 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -30,6 +30,18 @@ namespace eosio::hotstuff { using namespace boost::multi_index; using namespace eosio::chain; +/* + + static void read_file(){ + + } + + static void write_file(){ + + } + +*/ + // Concurrency note: qc_chain is a single-threaded and lock-free decision engine. // All thread synchronization, if any, is external. class qc_chain { @@ -65,7 +77,7 @@ namespace eosio::hotstuff { fc::unsigned_int update_bitset(fc::unsigned_int value, name finalizer); - digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc); //get digest to sign from proposal data + //digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc); //get digest to sign from proposal data void reset_qc(const fc::sha256& proposal_id); //reset current internal qc diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index df1d8ffe58..7d50903505 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -161,11 +161,11 @@ namespace eosio { namespace hotstuff { throw std::runtime_error("qc_chain internal error: finalizer not found"); } - digest_type qc_chain::get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ +/* digest_type qc_chain::get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ digest_type h1 = digest_type::hash( std::make_pair( block_id, phase_counter ) ); digest_type h2 = digest_type::hash( std::make_pair( h1, final_on_qc ) ); return h2; - } + }*/ std::vector qc_chain::get_qc_chain(const fc::sha256& proposal_id) { std::vector ret_arr; @@ -269,7 +269,7 @@ namespace eosio { namespace hotstuff { // //if (proposal.justify.proposal_id != NULL_PROPOSAL_ID) justification_agg_sig = proposal.justify.active_agg_sig; - digest_type digest = get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); + digest_type digest = proposal.get_proposal_id(); //get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); std::vector h = std::vector(digest.data(), digest.data() + 32); @@ -298,6 +298,9 @@ namespace eosio { namespace hotstuff { _my_producers(std::move(my_producers)), _logger(logger) { + + //todo : read safety + liveness state + fc_dlog(_logger, " === ${id} qc chain initialized ${my_producers}", ("my_producers", my_producers)("id", _id)); } @@ -330,7 +333,7 @@ namespace eosio { namespace hotstuff { hs_vote_message qc_chain::sign_proposal(const hs_proposal_message & proposal, name finalizer){ _v_height = proposal.get_view_number(); - digest_type digest = get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); + digest_type digest = proposal.get_proposal_id(); //get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); std::vector h = std::vector(digest.data(), digest.data() + 32); #warning use appropriate private key for each producer @@ -466,6 +469,8 @@ namespace eosio { namespace hotstuff { //update internal state update(proposal); + //todo : write safety state (must be synchronous) + for (auto &msg : msgs) { send_hs_vote_msg(msg); } @@ -545,6 +550,8 @@ namespace eosio { namespace hotstuff { _pending_proposal_block = NULL_BLOCK_ID; _b_leaf = proposal_candidate.proposal_id; + //todo : ?? write liveness state (can be asynchronous) + send_hs_proposal_msg(proposal_candidate); fc_tlog(_logger, " === ${id} _b_leaf updated (process_vote): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); } @@ -613,6 +620,8 @@ namespace eosio { namespace hotstuff { _pending_proposal_block = NULL_BLOCK_ID; _b_leaf = proposal_candidate.proposal_id; + //todo : ?? write liveness state (can be asynchronous) + send_hs_proposal_msg(proposal_candidate); fc_tlog(_logger, " === ${id} _b_leaf updated (on_beat): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); @@ -724,6 +733,8 @@ namespace eosio { namespace hotstuff { _high_qc = high_qc; _b_leaf = _high_qc.proposal_id; + //todo : write liveness state (can be asynchronous) + fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.proposal_id)("id", _id)); return true; } else { @@ -745,6 +756,8 @@ namespace eosio { namespace hotstuff { _high_qc.quorum_met = true; _b_leaf = _high_qc.proposal_id; + //todo : write liveness state (can be asynchronous) + fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.proposal_id)("id", _id)); return true; } @@ -996,7 +1009,9 @@ namespace eosio { namespace hotstuff { } } - commit(b); + commit(b); //todo : ensure that block is marked irreversible / lib is updated etc. + + //todo : write liveness state (can be asynchronous) fc_tlog(_logger, " === last executed proposal : #${block_num} ${block_id}", ("block_num", b.block_num())("block_id", b.block_id)); diff --git a/libraries/hotstuff/test/hotstuff_tools.cpp b/libraries/hotstuff/test/hotstuff_tools.cpp index bb92b6c558..aabe46723a 100644 --- a/libraries/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/hotstuff/test/hotstuff_tools.cpp @@ -48,6 +48,7 @@ BOOST_AUTO_TEST_CASE(view_number_tests) try { BOOST_CHECK_EQUAL(vn_2 < vn_3, true); BOOST_CHECK_EQUAL(vn_3 < vn_4, true); BOOST_CHECK_EQUAL(vn_4 < vn_5, true); + BOOST_CHECK_EQUAL(vn_4 <= vn_5, true); //test operators == false BOOST_CHECK_EQUAL(vn_1 >= vn_2, false); diff --git a/libraries/hotstuff/test/test_hotstuff_state.cpp b/libraries/hotstuff/test/test_hotstuff_state.cpp index d28a322824..45f144948b 100644 --- a/libraries/hotstuff/test/test_hotstuff_state.cpp +++ b/libraries/hotstuff/test/test_hotstuff_state.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -6,51 +8,56 @@ #include #include -//#include +#include using std::cout; -struct safety_state { +/*struct safety_state { eosio::chain::view_number v_height; - eosio::chain::view_number b_lock; + fc::sha256 b_lock; }; struct liveness_state { eosio::chain::quorum_certificate high_qc; - eosio::chain::view_number b_exec; + fc::sha256 b_leaf; + fc::sha256 b_exec; }; - +*/ BOOST_AUTO_TEST_SUITE(test_hotstuff_state) -const std::string file_path("temp"); +const std::string file_path_1("temp_hs_safety"); +const std::string file_path_2("temp_hs_liveness"); -BOOST_AUTO_TEST_CASE(write_state_to_file) try { +BOOST_AUTO_TEST_CASE(write_safety_state_to_file) try { -/* eosio::hotstuff::hs_proposal_message hspm; + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; - hspm.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm.phase_counter = 0; + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::block_id_type(); + hspm_1.phase_counter = 2; - eosio::hotstuff::view_number vn = hspm.get_view_number(); + eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); - BOOST_CHECK_EQUAL(vn.block_height() == 194217067, true); - BOOST_CHECK_EQUAL(vn.phase_counter() == 0, true); -*/ - safety_state ss; + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::block_id_type(); + hspm_2.phase_counter = 0; - //ss.test_val = 2; + fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); + + eosio::chain::safety_state ss(v_height, b_lock); // writing fc::cfile pfile; - pfile.set_file_path(file_path); + pfile.set_file_path(file_path_1); pfile.open(fc::cfile::truncate_rw_mode); auto data = fc::raw::pack(ss); pfile.write(data.data(), data.size()); - pfile.close(); // or let destructor do it + pfile.close(); bool ok = true; @@ -59,25 +66,133 @@ BOOST_AUTO_TEST_CASE(write_state_to_file) try { } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_CASE(read_state_from_file) try { +BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { - safety_state ss; + eosio::chain::safety_state ss; // reading fc::cfile pfile; - pfile.set_file_path(file_path); + pfile.set_file_path(file_path_1); pfile.open("rb"); auto ds = pfile.create_datastream(); fc::raw::unpack(ds, ss); - pfile.close(); // or let destructor do it + pfile.close(); + + std::remove(file_path_1.c_str()); + + //test correct values + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; - //bool ok = ss.test_val == 2; + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::block_id_type(); + hspm_1.phase_counter = 2; - BOOST_CHECK_EQUAL(true, true); + eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); + + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::block_id_type(); + hspm_2.phase_counter = 0; + + fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); + + bool ok1 = ss.v_height == v_height; + bool ok2 = ss.b_lock == b_lock; + + BOOST_CHECK_EQUAL(ok1, true); + BOOST_CHECK_EQUAL(ok2, true); } FC_LOG_AND_RETHROW(); -BOOST_AUTO_TEST_SUITE_END() -FC_REFLECT(safety_state, (v_height)(b_lock)) -FC_REFLECT(liveness_state, (high_qc)(b_exec)) \ No newline at end of file + +BOOST_AUTO_TEST_CASE(write_liveness_state_to_file) try { + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::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 + eosio::hotstuff::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"); + + eosio::chain::liveness_state ls(high_qc, b_leaf, b_exec); + + // writing + fc::cfile pfile; + pfile.set_file_path(file_path_2); + pfile.open(fc::cfile::truncate_rw_mode); + auto data = fc::raw::pack(ls); + pfile.write(data.data(), data.size()); + pfile.close(); + + bool ok = true; + + BOOST_CHECK_EQUAL(ok, true); + + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { + + eosio::chain::liveness_state ls; + + // reading + fc::cfile pfile; + pfile.set_file_path(file_path_2); + pfile.open("rb"); + auto ds = pfile.create_datastream(); + fc::raw::unpack(ds, ls); + pfile.close(); + + std::remove(file_path_2.c_str()); + + //test correct values + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::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 + eosio::hotstuff::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"); + + bool ok1 = ls.high_qc == high_qc; + bool ok2 = ls.b_exec == b_exec; + bool ok3 = ls.b_leaf == b_leaf; + + BOOST_CHECK_EQUAL(ok1, true); + BOOST_CHECK_EQUAL(ok2, true); + BOOST_CHECK_EQUAL(ok3, true); + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_SUITE_END() \ No newline at end of file From d56303ac4b06b95382e64479bcd5c7a631f71970 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Tue, 29 Aug 2023 17:40:45 +0000 Subject: [PATCH 03/11] Added safety / liveness state persistence --- .../chain/include/eosio/chain/hotstuff.hpp | 51 +++++++- libraries/hotstuff/chain_pacemaker.cpp | 2 +- .../eosio/hotstuff/chain_pacemaker.hpp | 5 +- .../include/eosio/hotstuff/qc_chain.hpp | 49 +++++-- libraries/hotstuff/qc_chain.cpp | 123 ++++++++++-------- libraries/hotstuff/test/test_hotstuff.cpp | 2 +- .../hotstuff/test/test_hotstuff_state.cpp | 62 ++------- 7 files changed, 175 insertions(+), 119 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 0bc1513215..89bcb1fa54 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -123,8 +123,53 @@ namespace eosio::chain { struct safety_state { - eosio::chain::view_number v_height; - fc::sha256 b_lock; + void set_v_height(const name finalizer, const eosio::chain::view_number v_height){ + _states[finalizer.to_uint64_t()].first = v_height; + } + + void set_b_lock(const name finalizer, fc::sha256 b_lock){ + _states[finalizer.to_uint64_t()].second = b_lock; + } + + std::pair get_safety_state(const name finalizer) const{ + auto s = _states.find(finalizer.to_uint64_t()); + if (s != _states.end()) return s->second; + else return std::make_pair(eosio::chain::view_number(),fc::sha256()); + } + + eosio::chain::view_number get_v_height(const name finalizer) const{ + auto s = _states.find(finalizer.to_uint64_t()); + if (s != _states.end()) return s->second.first; + else return eosio::chain::view_number(); + }; + + fc::sha256 get_b_lock(const name finalizer) const{ + auto s_itr = _states.find(finalizer.to_uint64_t()); + if (s_itr != _states.end()) return s_itr->second.second; + else return fc::sha256(); + }; + + //todo : implement safety state default / sorting + + std::pair get_safety_state() const{ + auto s = _states.begin(); + if (s != _states.end()) return s->second; + else return std::make_pair(eosio::chain::view_number(),fc::sha256()); + } + + eosio::chain::view_number get_v_height() const{ + auto s = _states.begin(); + if (s != _states.end()) return s->second.first; + else return eosio::chain::view_number(); + }; + + fc::sha256 get_b_lock() const{ + auto s_itr = _states.begin(); + if (s_itr != _states.end()) return s_itr->second.second; + else return fc::sha256(); + }; + + std::unordered_map> _states; }; @@ -144,7 +189,7 @@ namespace eosio::chain { } //eosio::chain -FC_REFLECT(eosio::chain::safety_state, (v_height)(b_lock)) +FC_REFLECT(eosio::chain::safety_state, (_states)) FC_REFLECT(eosio::chain::liveness_state, (high_qc)(b_leaf)(b_exec)) FC_REFLECT(eosio::chain::view_number, (_block_height)(_phase_counter)); FC_REFLECT(eosio::chain::quorum_certificate, (proposal_id)(active_finalizers)(active_agg_sig)); diff --git a/libraries/hotstuff/chain_pacemaker.cpp b/libraries/hotstuff/chain_pacemaker.cpp index f5763150e9..bf86d18e79 100644 --- a/libraries/hotstuff/chain_pacemaker.cpp +++ b/libraries/hotstuff/chain_pacemaker.cpp @@ -102,7 +102,7 @@ namespace eosio { namespace hotstuff { chain_pacemaker::chain_pacemaker(controller* chain, std::set my_producers, fc::logger& logger) : _chain(chain), - _qc_chain("default"_n, this, std::move(my_producers), logger), + _qc_chain("default"_n, this, std::move(my_producers), logger, DEFAULT_SAFETY_STATE_FILE, DEFAULT_LIVENESS_STATE_FILE), _logger(logger) { } diff --git a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp index b61015d0a6..f96561484a 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp @@ -11,11 +11,14 @@ namespace eosio::chain { namespace eosio::hotstuff { + const std::string DEFAULT_SAFETY_STATE_FILE = "hs_tm_safety_state"; //todo : reversible blocks folder + const std::string DEFAULT_LIVENESS_STATE_FILE = "hs_tm_liveness_state"; //todo : reversible blocks folder + class chain_pacemaker : public base_pacemaker { public: //class-specific functions - + chain_pacemaker(controller* chain, std::set my_producers, fc::logger& logger); void beat(); diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index 1e9450bdf1..e01c15a494 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -18,6 +18,8 @@ #include +#include + #include #include @@ -30,17 +32,33 @@ namespace eosio::hotstuff { using namespace boost::multi_index; using namespace eosio::chain; -/* - - static void read_file(){ - + template + static void read_state(const std::string file_path, StateObj& s){ + + if (file_path != std::string()){ + fc::cfile pfile; + pfile.set_file_path(file_path); + pfile.open("rb"); + auto ds = pfile.create_datastream(); + fc::raw::unpack(ds, s); + pfile.close(); + } + } - static void write_file(){ + template + static void write_state(const std::string file_path, const StateObj s){ - } + if (file_path != std::string()){ + fc::cfile pfile; + pfile.set_file_path(file_path); + pfile.open(fc::cfile::truncate_rw_mode); + auto data = fc::raw::pack(s); + pfile.write(data.data(), data.size()); + pfile.close(); + } -*/ + } // Concurrency note: qc_chain is a single-threaded and lock-free decision engine. // All thread synchronization, if any, is external. @@ -49,7 +67,7 @@ namespace eosio::hotstuff { qc_chain() = delete; - qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger); + qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger, std::string safety_state_file, std::string liveness_state_file); uint64_t get_state_version() const { return _state_version; } // calling this w/ thread sync is optional @@ -139,13 +157,22 @@ namespace eosio::hotstuff { bool _chained_mode = false; block_id_type _block_exec = NULL_BLOCK_ID; block_id_type _pending_proposal_block = NULL_BLOCK_ID; - fc::sha256 _b_leaf = NULL_PROPOSAL_ID; + +/* fc::sha256 _b_leaf = NULL_PROPOSAL_ID; fc::sha256 _b_lock = NULL_PROPOSAL_ID; fc::sha256 _b_exec = NULL_PROPOSAL_ID; - fc::sha256 _b_finality_violation = NULL_PROPOSAL_ID; + eosio::chain::quorum_certificate _high_qc; - eosio::chain::quorum_certificate _current_qc; eosio::chain::view_number _v_height; +*/ + eosio::chain::safety_state _safety_state; + eosio::chain::liveness_state _liveness_state; + + std::string _safety_state_file; + std::string _liveness_state_file; + + fc::sha256 _b_finality_violation = NULL_PROPOSAL_ID; + eosio::chain::quorum_certificate _current_qc; eosio::chain::extended_schedule _schedule; base_pacemaker* _pacemaker = nullptr; std::set _my_producers; diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 7d50903505..ac4cc583e5 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -96,14 +96,14 @@ namespace eosio { namespace hotstuff { void qc_chain::get_state(finalizer_state& fs) const { fs.chained_mode = _chained_mode; - fs.b_leaf = _b_leaf; - fs.b_lock = _b_lock; - fs.b_exec = _b_exec; + fs.b_leaf = _liveness_state.b_leaf; + fs.b_lock = _safety_state.get_b_lock(); + fs.b_exec = _liveness_state.b_exec; fs.b_finality_violation = _b_finality_violation; fs.block_exec = _block_exec; fs.pending_proposal_block = _pending_proposal_block; - fs.v_height = _v_height; - fs.high_qc = _high_qc; + fs.v_height = _safety_state.get_v_height(); + fs.high_qc = _liveness_state.high_qc; fs.current_qc = _current_qc; fs.schedule = _schedule; #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE @@ -187,9 +187,9 @@ namespace eosio { namespace hotstuff { 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.parent_id = _liveness_state.b_leaf; b_new.phase_counter = phase_counter; - b_new.justify = _high_qc; //or null if no _high_qc upon activation or chain launch + b_new.justify = _liveness_state.high_qc; //or null if no _liveness_state.high_qc upon activation or chain launch if (b_new.justify.proposal_id != NULL_PROPOSAL_ID){ 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()); @@ -235,7 +235,7 @@ namespace eosio { namespace hotstuff { hs_new_block_message qc_chain::new_block_candidate(const block_id_type& block_id) { hs_new_block_message b; b.block_id = block_id; - b.justify = _high_qc; //or null if no _high_qc upon activation or chain launch + b.justify = _liveness_state.high_qc; //or null if no _liveness_state.high_qc upon activation or chain launch return b; } @@ -292,7 +292,7 @@ namespace eosio { namespace hotstuff { } - qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger) + qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger, std::string safety_state_file, std::string liveness_state_file) : _id(id), _pacemaker(pacemaker), _my_producers(std::move(my_producers)), @@ -301,6 +301,16 @@ namespace eosio { namespace hotstuff { //todo : read safety + liveness state + if (safety_state_file!=std::string()){ + eosio::hotstuff::read_state(safety_state_file, _safety_state); + _safety_state_file = safety_state_file; + } + + if (liveness_state_file!=std::string()){ + eosio::hotstuff::read_state(liveness_state_file, _liveness_state); + _liveness_state_file = liveness_state_file; + } + fc_dlog(_logger, " === ${id} qc chain initialized ${my_producers}", ("my_producers", my_producers)("id", _id)); } @@ -331,7 +341,8 @@ namespace eosio { namespace hotstuff { } hs_vote_message qc_chain::sign_proposal(const hs_proposal_message & proposal, name finalizer){ - _v_height = proposal.get_view_number(); + + _safety_state.set_v_height(finalizer, proposal.get_view_number()); digest_type digest = proposal.get_proposal_id(); //get_digest_to_sign(proposal.block_id, proposal.phase_counter, proposal.final_on_qc); @@ -469,7 +480,7 @@ namespace eosio { namespace hotstuff { //update internal state update(proposal); - //todo : write safety state (must be synchronous) + write_state(_safety_state_file , _safety_state); for (auto &msg : msgs) { send_hs_vote_msg(msg); @@ -548,12 +559,13 @@ namespace eosio { namespace hotstuff { reset_qc(proposal_candidate.proposal_id); fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (process_vote)", ("id", _id)); _pending_proposal_block = NULL_BLOCK_ID; - _b_leaf = proposal_candidate.proposal_id; + _liveness_state.b_leaf = proposal_candidate.proposal_id; - //todo : ?? write liveness state (can be asynchronous) + //todo : asynchronous? + write_state(_liveness_state_file , _liveness_state); send_hs_proposal_msg(proposal_candidate); - fc_tlog(_logger, " === ${id} _b_leaf updated (process_vote): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); + fc_tlog(_logger, " === ${id} _liveness_state.b_leaf updated (process_vote): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); } } } @@ -589,7 +601,7 @@ namespace eosio { namespace hotstuff { // // We are the leader, and we got a block_id from a proposer, but // we should probably do something with the justify QC that - // comes with it (which is the _high_qc of the proposer (?)) + // comes with it (which is the _liveness_state.high_qc of the proposer (?)) // // ------------------------------------------------------------------ @@ -618,13 +630,14 @@ namespace eosio { namespace hotstuff { fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (process_new_block)", ("id", _id)); _pending_proposal_block = NULL_BLOCK_ID; - _b_leaf = proposal_candidate.proposal_id; + _liveness_state.b_leaf = proposal_candidate.proposal_id; - //todo : ?? write liveness state (can be asynchronous) + //todo : asynchronous? + write_state(_liveness_state_file , _liveness_state); send_hs_proposal_msg(proposal_candidate); - fc_tlog(_logger, " === ${id} _b_leaf updated (on_beat): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); + fc_tlog(_logger, " === ${id} _liveness_state.b_leaf updated (on_beat): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); } } @@ -728,17 +741,18 @@ namespace eosio { namespace hotstuff { // if new high QC is higher than current, update to new - if (_high_qc.proposal_id == NULL_PROPOSAL_ID){ + if (_liveness_state.high_qc.proposal_id == NULL_PROPOSAL_ID){ - _high_qc = high_qc; - _b_leaf = _high_qc.proposal_id; + _liveness_state.high_qc = high_qc; + _liveness_state.b_leaf = _liveness_state.high_qc.proposal_id; - //todo : write liveness state (can be asynchronous) + //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.proposal_id)("id", _id)); + fc_tlog(_logger, " === ${id} _liveness_state.b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _liveness_state.high_qc.proposal_id)("id", _id)); return true; } else { - const hs_proposal_message *old_high_qc_prop = get_proposal( _high_qc.proposal_id ); + const hs_proposal_message *old_high_qc_prop = get_proposal( _liveness_state.high_qc.proposal_id ); const hs_proposal_message *new_high_qc_prop = get_proposal( high_qc.proposal_id ); if (old_high_qc_prop == nullptr) return false; @@ -752,13 +766,14 @@ namespace eosio { namespace hotstuff { //high_qc.quorum_met = true; 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; - _high_qc.quorum_met = true; - _b_leaf = _high_qc.proposal_id; + _liveness_state.high_qc = high_qc; + _liveness_state.high_qc.quorum_met = true; + _liveness_state.b_leaf = _liveness_state.high_qc.proposal_id; - //todo : write liveness state (can be asynchronous) + //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.proposal_id)("id", _id)); + fc_tlog(_logger, " === ${id} _liveness_state.b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _liveness_state.high_qc.proposal_id)("id", _id)); return true; } } @@ -788,7 +803,7 @@ namespace eosio { namespace hotstuff { hs_new_view_message new_view; - new_view.high_qc = _high_qc; + new_view.high_qc = _liveness_state.high_qc; send_hs_new_view_msg(new_view); } @@ -806,7 +821,7 @@ namespace eosio { namespace hotstuff { fc::sha256 upcoming_commit; - if (proposal.justify.proposal_id == NULL_PROPOSAL_ID && _b_lock == NULL_PROPOSAL_ID) + if (proposal.justify.proposal_id == NULL_PROPOSAL_ID && _safety_state.get_b_lock() == NULL_PROPOSAL_ID) final_on_qc_check = true; //if chain just launched or feature just activated else { @@ -841,23 +856,23 @@ namespace eosio { namespace hotstuff { } } - if (proposal.get_view_number() > _v_height) { + if (proposal.get_view_number() > _safety_state.get_v_height()) { monotony_check = true; } - if (_b_lock != NULL_PROPOSAL_ID){ + if (_safety_state.get_b_lock() != NULL_PROPOSAL_ID){ //Safety check : check if this proposal extends the chain I'm locked on - if (extends(proposal.proposal_id, _b_lock)) { + 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 == NULL_PROPOSAL_ID && _b_lock == NULL_PROPOSAL_ID) { + if (proposal.justify.proposal_id == NULL_PROPOSAL_ID && _safety_state.get_b_lock() == NULL_PROPOSAL_ID) { 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( _b_lock ); - EOS_ASSERT( b_lock != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", _b_lock) ); + 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) ); @@ -925,8 +940,8 @@ namespace eosio { namespace hotstuff { size_t chain_length = std::distance(current_qc_chain.begin(), current_qc_chain.end()); - const hs_proposal_message *b_lock = get_proposal( _b_lock ); - EOS_ASSERT( b_lock != nullptr || _b_lock == NULL_PROPOSAL_ID , chain_exception, "expected hs_proposal ${id} not found", ("id", _b_lock) ); + const hs_proposal_message *b_lock = get_proposal( _safety_state.get_b_lock() ); + EOS_ASSERT( b_lock != nullptr || _safety_state.get_b_lock() == NULL_PROPOSAL_ID , chain_exception, "expected hs_proposal ${id} not found", ("id", _safety_state.get_b_lock()) ); //fc_tlog(_logger, " === update_high_qc : proposal.justify ==="); update_high_qc(proposal.justify); @@ -952,7 +967,7 @@ namespace eosio { namespace hotstuff { fc_tlog(_logger, " === ${id} _b_lock ${_b_lock} b_1 height ${b_1_height}", ("id", _id) - ("_b_lock", _b_lock) + ("_b_lock", _safety_state.get_b_lock()) ("b_1_height", b_1.block_num()) ("b_1_phase", b_1.phase_counter)); @@ -962,10 +977,13 @@ namespace eosio { namespace hotstuff { ("b_lock_phase", b_lock->phase_counter)); } - if (_b_lock == NULL_PROPOSAL_ID || b_1.get_view_number() > b_lock->get_view_number()){ + if (_safety_state.get_b_lock() == NULL_PROPOSAL_ID || 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 )); - _b_lock = b_1.proposal_id; //commit phase on b1 + + for (auto p : _my_producers){ + _safety_state.set_b_lock(p, 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)); } @@ -988,10 +1006,10 @@ namespace eosio { namespace hotstuff { //direct parent relationship verification if (b_2.parent_id == b_1.proposal_id && b_1.parent_id == b.proposal_id){ - if (_b_exec!= NULL_PROPOSAL_ID){ + if (_liveness_state.b_exec!= NULL_PROPOSAL_ID){ - 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) ); + const hs_proposal_message *b_exec = get_proposal( _liveness_state.b_exec ); + EOS_ASSERT( b_exec != nullptr , chain_exception, "expected hs_proposal ${id} not found", ("id", _liveness_state.b_exec) ); if (b_exec->get_view_number() >= b.get_view_number() && b_exec->proposal_id != b.proposal_id){ @@ -1011,11 +1029,12 @@ namespace eosio { namespace hotstuff { commit(b); //todo : ensure that block is marked irreversible / lib is updated etc. - //todo : write liveness state (can be asynchronous) + //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 + _liveness_state.b_exec = b.proposal_id; //decide phase on b _block_exec = b.block_id; gc_proposals( b.get_key()-1); @@ -1080,11 +1099,11 @@ namespace eosio { namespace hotstuff { bool exec_height_check = false; - const hs_proposal_message *last_exec_prop = get_proposal( _b_exec ); - EOS_ASSERT( last_exec_prop != nullptr || _b_exec == NULL_PROPOSAL_ID, chain_exception, "expected hs_proposal ${id} not found", ("id", _b_exec) ); + const hs_proposal_message *last_exec_prop = get_proposal( _liveness_state.b_exec ); + EOS_ASSERT( last_exec_prop != nullptr || _liveness_state.b_exec == NULL_PROPOSAL_ID, chain_exception, "expected hs_proposal ${id} not found", ("id", _liveness_state.b_exec) ); if (last_exec_prop != nullptr) { - fc_tlog(_logger, " === _b_exec proposal #${block_num} ${proposal_id} block_id : ${block_id} phase : ${phase_counter} parent_id : ${parent_id}", + fc_tlog(_logger, " === _liveness_state.b_exec proposal #${block_num} ${proposal_id} block_id : ${block_id} phase : ${phase_counter} parent_id : ${parent_id}", ("block_num", last_exec_prop->block_num()) ("proposal_id", last_exec_prop->proposal_id) ("block_id", last_exec_prop->block_id) @@ -1097,12 +1116,12 @@ namespace eosio { namespace hotstuff { ("proposal_id_2", proposal.block_num()) ("phase_counter_2", proposal.phase_counter)); } else { - fc_tlog(_logger, " === _b_exec proposal is null vs proposal ${proposal_id_2} ${phase_counter_2} ", + fc_tlog(_logger, " === _liveness_state.b_exec proposal is null vs proposal ${proposal_id_2} ${phase_counter_2} ", ("proposal_id_2", proposal.block_num()) ("phase_counter_2", proposal.phase_counter)); } - if (_b_exec == NULL_PROPOSAL_ID) + if (_liveness_state.b_exec == NULL_PROPOSAL_ID) exec_height_check = true; else exec_height_check = last_exec_prop->get_view_number() < proposal.get_view_number(); diff --git a/libraries/hotstuff/test/test_hotstuff.cpp b/libraries/hotstuff/test/test_hotstuff.cpp index 7725d813d4..e264e761b2 100644 --- a/libraries/hotstuff/test/test_hotstuff.cpp +++ b/libraries/hotstuff/test/test_hotstuff.cpp @@ -53,7 +53,7 @@ class hotstuff_test_handler { //_qc_chains.reserve( replicas.size() ); for (name r : replicas) { - qc_chain *qcc_ptr = new qc_chain(r, &tpm, {r}, hotstuff_logger); + qc_chain *qcc_ptr = new qc_chain(r, &tpm, {r}, hotstuff_logger, std::string(), std::string()); std::shared_ptr qcc_shared_ptr(qcc_ptr); _qc_chains.push_back( std::make_pair(r, qcc_shared_ptr) ); diff --git a/libraries/hotstuff/test/test_hotstuff_state.cpp b/libraries/hotstuff/test/test_hotstuff_state.cpp index 45f144948b..86e17631ab 100644 --- a/libraries/hotstuff/test/test_hotstuff_state.cpp +++ b/libraries/hotstuff/test/test_hotstuff_state.cpp @@ -12,21 +12,6 @@ using std::cout; -/*struct safety_state { - - eosio::chain::view_number v_height; - fc::sha256 b_lock; - -}; - -struct liveness_state { - - eosio::chain::quorum_certificate high_qc; - fc::sha256 b_leaf; - fc::sha256 b_exec; - -}; -*/ BOOST_AUTO_TEST_SUITE(test_hotstuff_state) const std::string file_path_1("temp_hs_safety"); @@ -49,34 +34,24 @@ BOOST_AUTO_TEST_CASE(write_safety_state_to_file) try { fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - eosio::chain::safety_state ss(v_height, b_lock); + eosio::chain::safety_state ss; - // writing - fc::cfile pfile; - pfile.set_file_path(file_path_1); - pfile.open(fc::cfile::truncate_rw_mode); - auto data = fc::raw::pack(ss); - pfile.write(data.data(), data.size()); - pfile.close(); + ss.set_v_height(eosio::chain::name{""}, v_height); + ss.set_b_lock(eosio::chain::name{""}, b_lock); + + eosio::hotstuff::write_state(file_path_1, ss); bool ok = true; BOOST_CHECK_EQUAL(ok, true); - } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { eosio::chain::safety_state ss; - // reading - fc::cfile pfile; - pfile.set_file_path(file_path_1); - pfile.open("rb"); - auto ds = pfile.create_datastream(); - fc::raw::unpack(ds, ss); - pfile.close(); + eosio::hotstuff::read_state(file_path_1, ss); std::remove(file_path_1.c_str()); @@ -96,16 +71,16 @@ BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - bool ok1 = ss.v_height == v_height; - bool ok2 = ss.b_lock == b_lock; + //std::pair ss = get_safety_state(eosio::chain::name{""}); + + bool ok1 = ss.get_v_height(eosio::chain::name{""}) == v_height; + bool ok2 = ss.get_b_lock(eosio::chain::name{""}) == b_lock; BOOST_CHECK_EQUAL(ok1, true); BOOST_CHECK_EQUAL(ok2, true); } FC_LOG_AND_RETHROW(); - - BOOST_AUTO_TEST_CASE(write_liveness_state_to_file) try { eosio::hotstuff::hs_proposal_message hspm_1; @@ -132,32 +107,19 @@ BOOST_AUTO_TEST_CASE(write_liveness_state_to_file) try { eosio::chain::liveness_state ls(high_qc, b_leaf, b_exec); - // writing - fc::cfile pfile; - pfile.set_file_path(file_path_2); - pfile.open(fc::cfile::truncate_rw_mode); - auto data = fc::raw::pack(ls); - pfile.write(data.data(), data.size()); - pfile.close(); + eosio::hotstuff::write_state(file_path_2, ls); bool ok = true; BOOST_CHECK_EQUAL(ok, true); - } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { eosio::chain::liveness_state ls; - // reading - fc::cfile pfile; - pfile.set_file_path(file_path_2); - pfile.open("rb"); - auto ds = pfile.create_datastream(); - fc::raw::unpack(ds, ls); - pfile.close(); + eosio::hotstuff::read_state(file_path_2, ls); std::remove(file_path_2.c_str()); From ede43a11603f77dd8b1b8f0758527d27c57f235d Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 30 Aug 2023 15:48:26 +0000 Subject: [PATCH 04/11] Added state.hpp file --- .../hotstuff/include/eosio/hotstuff/state.hpp | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 libraries/hotstuff/include/eosio/hotstuff/state.hpp diff --git a/libraries/hotstuff/include/eosio/hotstuff/state.hpp b/libraries/hotstuff/include/eosio/hotstuff/state.hpp new file mode 100644 index 0000000000..178f98360f --- /dev/null +++ b/libraries/hotstuff/include/eosio/hotstuff/state.hpp @@ -0,0 +1,73 @@ +//#include +#include + +#include + +namespace eosio::hotstuff { + + using namespace eosio::chain; + + struct safety_state { + + void set_v_height(const name finalizer, const eosio::chain::view_number v_height){ + _states[finalizer.to_uint64_t()].first = v_height; + } + + void set_b_lock(const name finalizer, fc::sha256 b_lock){ + _states[finalizer.to_uint64_t()].second = b_lock; + } + + std::pair get_safety_state(const name finalizer) const{ + auto s = _states.find(finalizer.to_uint64_t()); + if (s != _states.end()) return s->second; + else return std::make_pair(eosio::chain::view_number(),fc::sha256()); + } + + eosio::chain::view_number get_v_height(const name finalizer) const{ + auto s = _states.find(finalizer.to_uint64_t()); + if (s != _states.end()) return s->second.first; + else return eosio::chain::view_number(); + }; + + fc::sha256 get_b_lock(const name finalizer) const{ + auto s_itr = _states.find(finalizer.to_uint64_t()); + if (s_itr != _states.end()) return s_itr->second.second; + else return fc::sha256(); + }; + + //todo : implement safety state default / sorting + + std::pair get_safety_state() const{ + auto s = _states.begin(); + if (s != _states.end()) return s->second; + else return std::make_pair(eosio::chain::view_number(),fc::sha256()); + } + + eosio::chain::view_number get_v_height() const{ + auto s = _states.begin(); + if (s != _states.end()) return s->second.first; + else return eosio::chain::view_number(); + }; + + fc::sha256 get_b_lock() const{ + auto s_itr = _states.begin(); + if (s_itr != _states.end()) return s_itr->second.second; + else return fc::sha256(); + }; + + std::unordered_map> _states; + + }; + +/* struct liveness_state { + + quorum_certificate high_qc; + fc::sha256 b_leaf; + fc::sha256 b_exec; + + };*/ + +} + +FC_REFLECT(eosio::hotstuff::safety_state, (_states)) +/*FC_REFLECT(eosio::hotstuff::liveness_state, (high_qc)(b_leaf)(b_exec))*/ \ No newline at end of file From 12a0ea7de390aff1b86c77fb71e067e490452ac1 Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 30 Aug 2023 16:03:44 +0000 Subject: [PATCH 05/11] Added persistance of safety state core functionality --- .../chain/include/eosio/chain/hotstuff.hpp | 8 -- .../eosio/hotstuff/chain_pacemaker.hpp | 3 +- .../include/eosio/hotstuff/qc_chain.hpp | 81 +------------ .../hotstuff/include/eosio/hotstuff/state.hpp | 12 +- libraries/hotstuff/qc_chain.cpp | 114 +----------------- 5 files changed, 10 insertions(+), 208 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 0fdcbb52be..9fe002dda3 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -61,14 +61,6 @@ namespace eosio::chain { fc::sha256 proposal_id; std::vector active_finalizers; //bitset encoding, following canonical order fc::crypto::blslib::bls_signature active_agg_sig; - -/*<<<<<<< HEAD - bool quorum_met = false; - - auto operator<=>(const quorum_certificate&) const = default; - -======= ->>>>>>> hotstuff_integration*/ }; struct hs_vote_message { diff --git a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp index 7f5cc64a0a..f14d257b58 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp @@ -11,8 +11,7 @@ namespace eosio::chain { namespace eosio::hotstuff { - const std::string DEFAULT_SAFETY_STATE_FILE = "hs_tm_safety_state"; //todo : reversible blocks folder - //const std::string DEFAULT_LIVENESS_STATE_FILE = "hs_tm_liveness_state"; //todo : reversible blocks folder + const std::string DEFAULT_SAFETY_STATE_FILE = "hs_tm_safety_state"; //todo : add reversible blocks folder class chain_pacemaker : public base_pacemaker { public: diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index 953e13515a..aafe67b8ac 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -33,7 +33,6 @@ namespace eosio::hotstuff { using namespace boost::multi_index; using namespace eosio::chain; -//<<<<<<< HEAD template static void read_state(const std::string file_path, StateObj& s){ @@ -62,7 +61,6 @@ namespace eosio::hotstuff { } -//======= class quorum_certificate { public: explicit quorum_certificate(size_t finalizer_size = 0) { @@ -122,67 +120,6 @@ namespace eosio::hotstuff { bool quorum_met = false; // not serialized across network }; -/* struct safety_state { - - void set_v_height(const name finalizer, const eosio::chain::view_number v_height){ - _states[finalizer.to_uint64_t()].first = v_height; - } - - void set_b_lock(const name finalizer, fc::sha256 b_lock){ - _states[finalizer.to_uint64_t()].second = b_lock; - } - - std::pair get_safety_state(const name finalizer) const{ - auto s = _states.find(finalizer.to_uint64_t()); - if (s != _states.end()) return s->second; - else return std::make_pair(eosio::chain::view_number(),fc::sha256()); - } - - eosio::chain::view_number get_v_height(const name finalizer) const{ - auto s = _states.find(finalizer.to_uint64_t()); - if (s != _states.end()) return s->second.first; - else return eosio::chain::view_number(); - }; - - fc::sha256 get_b_lock(const name finalizer) const{ - auto s_itr = _states.find(finalizer.to_uint64_t()); - if (s_itr != _states.end()) return s_itr->second.second; - else return fc::sha256(); - }; - - //todo : implement safety state default / sorting - - std::pair get_safety_state() const{ - auto s = _states.begin(); - if (s != _states.end()) return s->second; - else return std::make_pair(eosio::chain::view_number(),fc::sha256()); - } - - eosio::chain::view_number get_v_height() const{ - auto s = _states.begin(); - if (s != _states.end()) return s->second.first; - else return eosio::chain::view_number(); - }; - - fc::sha256 get_b_lock() const{ - auto s_itr = _states.begin(); - if (s_itr != _states.end()) return s_itr->second.second; - else return fc::sha256(); - }; - - std::unordered_map> _states; - - }; - - struct liveness_state { - - quorum_certificate high_qc; - fc::sha256 b_leaf; - fc::sha256 b_exec; - - }; -*/ -//>>>>>>> hotstuff_integration // Concurrency note: qc_chain is a single-threaded and lock-free decision engine. // All thread synchronization, if any, is external. class qc_chain { @@ -278,7 +215,7 @@ namespace eosio::hotstuff { }; bool _chained_mode = false; -//<<<<<<< HEAD + block_id_type _block_exec; block_id_type _pending_proposal_block; safety_state _safety_state; @@ -288,17 +225,6 @@ namespace eosio::hotstuff { fc::sha256 _b_finality_violation; quorum_certificate _high_qc; quorum_certificate _current_qc; -/*======= - block_id_type _block_exec; - block_id_type _pending_proposal_block; - fc::sha256 _b_leaf; - fc::sha256 _b_lock; - fc::sha256 _b_exec; - fc::sha256 _b_finality_violation; - quorum_certificate _high_qc; - quorum_certificate _current_qc; - uint32_t _v_height = 0; ->>>>>>> hotstuff_integration*/ eosio::chain::extended_schedule _schedule; base_pacemaker* _pacemaker = nullptr; std::set _my_producers; @@ -341,7 +267,4 @@ namespace eosio::hotstuff { }; -} /// eosio::hotstuff -/* -FC_REFLECT(eosio::hotstuff::safety_state, (_states)) -FC_REFLECT(eosio::hotstuff::liveness_state, (high_qc)(b_leaf)(b_exec))*/ \ No newline at end of file +} /// eosio::hotstuff \ No newline at end of file diff --git a/libraries/hotstuff/include/eosio/hotstuff/state.hpp b/libraries/hotstuff/include/eosio/hotstuff/state.hpp index 178f98360f..003f5b9879 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/state.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/state.hpp @@ -1,4 +1,3 @@ -//#include #include #include @@ -59,15 +58,6 @@ namespace eosio::hotstuff { }; -/* struct liveness_state { - - quorum_certificate high_qc; - fc::sha256 b_leaf; - fc::sha256 b_exec; - - };*/ - } -FC_REFLECT(eosio::hotstuff::safety_state, (_states)) -/*FC_REFLECT(eosio::hotstuff::liveness_state, (high_qc)(b_leaf)(b_exec))*/ \ No newline at end of file +FC_REFLECT(eosio::hotstuff::safety_state, (_states)) \ No newline at end of file diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index bb77a69670..1e99475f43 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -60,15 +60,9 @@ namespace eosio::hotstuff { fs.b_finality_violation = _b_finality_violation; fs.block_exec = _block_exec; fs.pending_proposal_block = _pending_proposal_block; -/*<<<<<<< HEAD - fs.v_height = _safety_state.get_v_height(); - fs.high_qc = _high_qc; - fs.current_qc = _current_qc; -=======*/ fs.v_height = _safety_state.get_v_height(); fs.high_qc = _high_qc.to_msg(); fs.current_qc = _current_qc.to_msg(); -//>>>>>>> hotstuff_integration fs.schedule = _schedule; #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE ps_height_iterator psh_it = _proposal_stores_by_height.begin(); @@ -115,12 +109,6 @@ namespace eosio::hotstuff { throw std::runtime_error("qc_chain internal error: finalizer not found"); } -/* digest_type qc_chain::get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ - digest_type h1 = digest_type::hash( std::make_pair( block_id, phase_counter ) ); - digest_type h2 = digest_type::hash( std::make_pair( h1, final_on_qc ) ); - return h2; - }*/ - 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 ) ) { @@ -140,13 +128,8 @@ namespace eosio::hotstuff { b_new.block_id = block_id; b_new.parent_id = _b_leaf; b_new.phase_counter = phase_counter; -//<<<<<<< HEAD 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()){ -/*======= - 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()) { ->>>>>>> hotstuff_integration*/ 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){ @@ -188,11 +171,7 @@ namespace eosio::hotstuff { hs_new_block_message qc_chain::new_block_candidate(const block_id_type& block_id) { hs_new_block_message b; b.block_id = block_id; -//<<<<<<< HEAD - b.justify = _high_qc.to_msg(); //or null if no _high_qc upon activation or chain launch -/*======= b.justify = _high_qc.to_msg(); //or null if no _high_qc upon activation or chain launch ->>>>>>> hotstuff_integration*/ return b; } @@ -249,35 +228,22 @@ namespace eosio::hotstuff { } -//<<<<<<< HEAD qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger, std::string safety_state_file) : _id(id), _pacemaker(pacemaker), -/*======= - qc_chain::qc_chain(name id, base_pacemaker* pacemaker, std::set my_producers, fc::logger& logger) - : _pacemaker(pacemaker), ->>>>>>> hotstuff_integration*/ _my_producers(std::move(my_producers)), - //_id(id), _logger(logger) { -//<<<<<<< HEAD - //todo : read safety + liveness state + //todo : read liveness state / select initialization heuristics ? if (safety_state_file!=std::string()){ eosio::hotstuff::read_state(safety_state_file, _safety_state); _safety_state_file = safety_state_file; } -/* if (liveness_state_file!=std::string()){ - eosio::hotstuff::read_state(liveness_state_file, _liveness_state); - _liveness_state_file = liveness_state_file; - }*/ -//======= - //_high_qc.reset({}, 21); // TODO: use active schedule size + _high_qc.reset({}, 21); // TODO: use active schedule size _current_qc.reset({}, 21); // TODO: use active schedule size -//>>>>>>> hotstuff_integration fc_dlog(_logger, " === ${id} qc chain initialized ${my_producers}", ("my_producers", my_producers)("id", _id)); } @@ -529,16 +495,12 @@ namespace eosio::hotstuff { reset_qc(proposal_candidate.proposal_id); fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (process_vote)", ("id", _id)); -//<<<<<<< HEAD + _pending_proposal_block = {}; _b_leaf = proposal_candidate.proposal_id; //todo : asynchronous? //write_state(_liveness_state_file , _liveness_state); -/*======= - _pending_proposal_block = {}; - _b_leaf = proposal_candidate.proposal_id; ->>>>>>> hotstuff_integration*/ send_hs_proposal_msg(proposal_candidate); fc_tlog(_logger, " === ${id} _b_leaf updated (process_vote): ${proposal_id}", ("proposal_id", proposal_candidate.proposal_id)("id", _id)); @@ -605,16 +567,11 @@ namespace eosio::hotstuff { fc_tlog(_logger, " === ${id} setting _pending_proposal_block to null (process_new_block)", ("id", _id)); -//<<<<<<< HEAD _pending_proposal_block = {}; _b_leaf = proposal_candidate.proposal_id; //todo : asynchronous? //write_state(_liveness_state_file , _liveness_state); -/*======= - _pending_proposal_block = {}; - _b_leaf = proposal_candidate.proposal_id; ->>>>>>> hotstuff_integration*/ send_hs_proposal_msg(proposal_candidate); @@ -722,7 +679,6 @@ namespace eosio::hotstuff { // if new high QC is higher than current, update to new -//<<<<<<< HEAD if (_high_qc.get_proposal_id().empty()){ _high_qc = high_qc; @@ -736,17 +692,7 @@ namespace eosio::hotstuff { } 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 (_high_qc.get_proposal_id().empty()) { - _high_qc = high_qc; - _b_leaf = _high_qc.get_proposal_id(); - fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.get_proposal_id())("id", _id)); - return true; - } 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() ); ->>>>>>> hotstuff_integration*/ if (old_high_qc_prop == nullptr) return false; if (new_high_qc_prop == nullptr) @@ -756,9 +702,7 @@ namespace eosio::hotstuff { && is_quorum_met(high_qc, _schedule, *new_high_qc_prop)) { // "The caller does not need this updated on their high_qc structure" -- g - //high_qc.quorum_met = true; -//<<<<<<< HEAD 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; _high_qc.set_quorum_met(); @@ -768,14 +712,7 @@ namespace eosio::hotstuff { //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)); -/*======= - fc_tlog(_logger, " === updated high qc, now is : #${get_height} ${proposal_id}", ("get_height", new_high_qc_prop->get_height())("proposal_id", new_high_qc_prop->proposal_id)); - _high_qc = high_qc; - _high_qc.set_quorum_met(); - _b_leaf = _high_qc.get_proposal_id(); - fc_tlog(_logger, " === ${id} _b_leaf updated (update_high_qc) : ${proposal_id}", ("proposal_id", _high_qc.get_proposal_id())("id", _id)); ->>>>>>> hotstuff_integration*/ return true; } } @@ -805,11 +742,7 @@ namespace eosio::hotstuff { hs_new_view_message new_view; -//<<<<<<< HEAD new_view.high_qc = _high_qc.to_msg(); -//======= - //new_view.high_qc = _high_qc.to_msg(); -//>>>>>>> hotstuff_integration send_hs_new_view_msg(new_view); } @@ -827,11 +760,8 @@ namespace eosio::hotstuff { fc::sha256 upcoming_commit; -//<<<<<<< HEAD if (proposal.justify.proposal_id.empty() && _safety_state.get_b_lock().empty()) { -/*======= - if (proposal.justify.proposal_id.empty() && _b_lock.empty()) { ->>>>>>> hotstuff_integration*/ + final_on_qc_check = true; //if chain just launched or feature just activated } else { @@ -870,11 +800,7 @@ namespace eosio::hotstuff { monotony_check = true; } -//<<<<<<< HEAD if (!_safety_state.get_b_lock().empty()){ -/*======= - if (!_b_lock.empty()) { ->>>>>>> hotstuff_integration*/ //Safety check : check if this proposal extends the chain I'm locked on if (extends(proposal.proposal_id, _safety_state.get_b_lock())) { @@ -882,11 +808,9 @@ namespace eosio::hotstuff { } //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. -//<<<<<<< HEAD + if (proposal.justify.proposal_id.empty() && _safety_state.get_b_lock().empty()) { -/*======= - if (proposal.justify.proposal_id.empty() && _b_lock.empty()) { ->>>>>>> hotstuff_integration*/ + 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() ); @@ -958,13 +882,8 @@ namespace eosio::hotstuff { size_t chain_length = std::distance(current_qc_chain.begin(), current_qc_chain.end()); -//<<<<<<< HEAD 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()) ); -/*======= - const hs_proposal_message *b_lock = get_proposal( _b_lock ); - EOS_ASSERT( b_lock != nullptr || _b_lock.empty(), chain_exception, "expected hs_proposal ${id} not found", ("id", _b_lock) ); ->>>>>>> hotstuff_integration*/ //fc_tlog(_logger, " === update_high_qc : proposal.justify ==="); update_high_qc(quorum_certificate{proposal.justify}); @@ -1000,12 +919,8 @@ namespace eosio::hotstuff { ("b_lock_phase", b_lock->phase_counter)); } -//<<<<<<< HEAD if (_safety_state.get_b_lock().empty() || b_1.get_view_number() > b_lock->get_view_number()){ -/*======= - if (_b_lock.empty() || b_1.get_height() > b_lock->get_height()) { ->>>>>>> hotstuff_integration*/ fc_tlog(_logger, "setting _b_lock to ${proposal_id}", ("proposal_id",b_1.proposal_id )); for (auto p : _my_producers){ @@ -1033,11 +948,7 @@ namespace eosio::hotstuff { //direct parent relationship verification if (b_2.parent_id == b_1.proposal_id && b_1.parent_id == b.proposal_id){ -//<<<<<<< HEAD if (!_b_exec.empty()){ -/*======= - if (!_b_exec.empty()) { ->>>>>>> hotstuff_integration*/ 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) ); @@ -1130,13 +1041,8 @@ namespace eosio::hotstuff { bool exec_height_check = false; -//<<<<<<< HEAD - 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) ); -/*======= 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) ); ->>>>>>> hotstuff_integration*/ if (last_exec_prop != nullptr) { fc_tlog(_logger, " === _b_exec proposal #${block_num} ${proposal_id} block_id : ${block_id} phase : ${phase_counter} parent_id : ${parent_id}", @@ -1157,16 +1063,8 @@ namespace eosio::hotstuff { ("phase_counter_2", proposal.phase_counter)); } -//<<<<<<< HEAD if (_b_exec.empty()) exec_height_check = true; else exec_height_check = last_exec_prop->get_view_number() < proposal.get_view_number(); -/*======= - if (_b_exec.empty()) { - exec_height_check = true; - } else { - exec_height_check = last_exec_prop->get_height() < proposal.get_height(); - } ->>>>>>> hotstuff_integration*/ if (exec_height_check) { const hs_proposal_message *p = get_proposal( proposal.parent_id ); From dd33d93b5b80b4480852f931dffee8b528f1334b Mon Sep 17 00:00:00 2001 From: Guillaume Babin-Tremblay Date: Wed, 30 Aug 2023 18:16:59 +0000 Subject: [PATCH 06/11] added check for missing file --- libraries/hotstuff/qc_chain.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 749f4efa60..72866eca9b 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -237,7 +237,7 @@ namespace eosio::hotstuff { //todo : read liveness state / select initialization heuristics ? - if (safety_state_file!=std::string()){ + if (safety_state_file!=std::string() && std::filesystem::exists(safety_state_file)){ eosio::hotstuff::read_state(safety_state_file, _safety_state); _safety_state_file = safety_state_file; } @@ -634,7 +634,6 @@ namespace eosio::hotstuff { } // Invoked when we could perhaps make a proposal to the network (or to ourselves, if we are the leader). - // Called from the main application thread void qc_chain::on_beat(){ // Non-proposing leaders do not care about on_beat(), because leaders react to a block proposal From 17d587bbb467cc84167321853f70ff1d9237d6b0 Mon Sep 17 00:00:00 2001 From: fcecin Date: Fri, 29 Sep 2023 17:16:52 -0300 Subject: [PATCH 07/11] Misc improvements - speed up digest with std::cref() - remove view_number pair constructor - remove view_number::get_view_number - inline get_digest_to_sign - remove underscore from view_number member vars - changed to eosio::chain::config::safetydb_filename - new state_db_manager supports read/write state file without open/close on each operation - catch and handle all read/write errors including zero-size file and object deserialization/fc::unpack errors - qc_chain leaves safety state file open for multiple writes (with flush) - changes to hotstuff_tools.cpp tests to match new code - changes to test_hotstuff_state.cpp tests to match new code - added cfile::truncate() to help implement state file overwriting - remove comments & dead code - fixed hotstuff_tools.cpp & test_hotstuff_state.cpp line endings --- .../chain/include/eosio/chain/config.hpp | 2 +- .../chain/include/eosio/chain/hotstuff.hpp | 50 +-- libraries/hotstuff/chain_pacemaker.cpp | 3 +- .../eosio/hotstuff/chain_pacemaker.hpp | 2 - .../include/eosio/hotstuff/qc_chain.hpp | 79 +++-- libraries/hotstuff/qc_chain.cpp | 18 +- libraries/hotstuff/test/hotstuff_tools.cpp | 126 +++---- .../hotstuff/test/test_hotstuff_state.cpp | 323 +++++++++--------- libraries/libfc/include/fc/io/cfile.hpp | 8 + 9 files changed, 315 insertions(+), 296 deletions(-) diff --git a/libraries/chain/include/eosio/chain/config.hpp b/libraries/chain/include/eosio/chain/config.hpp index d46df346e5..5e62a24f58 100644 --- a/libraries/chain/include/eosio/chain/config.hpp +++ b/libraries/chain/include/eosio/chain/config.hpp @@ -12,7 +12,7 @@ const static auto reversible_blocks_dir_name = "reversible"; const static auto default_state_dir_name = "state"; const static auto forkdb_filename = "fork_db.dat"; -const static auto qcdb_filename = "qc_db.dat"; +const static auto safetydb_filename = "safety_db.dat"; const static auto default_state_size = 1*1024*1024*1024ll; const static auto default_state_guard_size = 128*1024*1024ll; diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 702a13ee40..aab3b26d63 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -13,9 +13,9 @@ namespace eosio::chain { using hs_bitset = boost::dynamic_bitset; using bls_key_map_t = std::map; - static digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc){ - digest_type h1 = digest_type::hash( std::make_pair( block_id, phase_counter ) ); - digest_type h2 = digest_type::hash( std::make_pair( h1, final_on_qc ) ); + inline digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc) { + digest_type h1 = digest_type::hash( std::make_pair( std::cref(block_id), phase_counter ) ); + digest_type h2 = digest_type::hash( std::make_pair( std::cref(h1), std::cref(final_on_qc) ) ); return h2; } @@ -24,33 +24,18 @@ namespace eosio::chain { } struct view_number { - view_number() : _block_height(0), _phase_counter(0) {} - explicit view_number(std::pair data) : - _block_height(data.first), - _phase_counter(data.second) - { - } - - explicit view_number(uint32_t block_height, uint8_t phase_counter) : - _block_height(block_height), - _phase_counter(phase_counter) - { - } - + view_number() : bheight(0), pcounter(0) {} + explicit view_number(uint32_t block_height, uint8_t phase_counter) : bheight(block_height), pcounter(phase_counter) {} auto operator<=>(const view_number&) const = default; - uint32_t block_height() const { return _block_height; } - uint8_t phase_counter() const { return _phase_counter; } - - uint64_t to_uint64_t() const { return compute_height(_block_height, _phase_counter); } - std::string to_string() const { return std::to_string(_block_height) + "::" + std::to_string(_phase_counter); } + uint32_t block_height() const { return bheight; } + uint8_t phase_counter() const { return pcounter; } + uint64_t to_uint64_t() const { return compute_height(bheight, pcounter); } + std::string to_string() const { return std::to_string(bheight) + "::" + std::to_string(pcounter); } + uint64_t get_key() const { return to_uint64_t(); } - uint64_t get_key() const { return get_view_number().to_uint64_t(); }; - - view_number get_view_number() const { return view_number{ block_height(), phase_counter() }; }; - - uint32_t _block_height; - uint8_t _phase_counter; + uint32_t bheight; + uint8_t pcounter; }; struct extended_schedule { @@ -83,8 +68,7 @@ namespace eosio::chain { uint32_t block_num() const { return block_header::num_from_id(block_id); } uint64_t get_key() const { return compute_height(block_header::num_from_id(block_id), phase_counter); }; - view_number get_view_number() const { return view_number(std::make_pair(block_header::num_from_id(block_id), phase_counter)); }; - + view_number get_view_number() const { return view_number(block_header::num_from_id(block_id), phase_counter); }; }; struct hs_new_block_message { @@ -127,16 +111,10 @@ namespace eosio::chain { } }; - - using hs_proposal_message_ptr = std::shared_ptr; - using hs_vote_message_ptr = std::shared_ptr; - using hs_new_view_message_ptr = std::shared_ptr; - using hs_new_block_message_ptr = std::shared_ptr; - } //eosio::chain -FC_REFLECT(eosio::chain::view_number, (_block_height)(_phase_counter)); +FC_REFLECT(eosio::chain::view_number, (bheight)(pcounter)); FC_REFLECT(eosio::chain::quorum_certificate_message, (proposal_id)(active_finalizers)(active_agg_sig)); FC_REFLECT(eosio::chain::extended_schedule, (producer_schedule)(bls_pub_keys)); FC_REFLECT(eosio::chain::hs_vote_message, (proposal_id)(finalizer_key)(sig)); diff --git a/libraries/hotstuff/chain_pacemaker.cpp b/libraries/hotstuff/chain_pacemaker.cpp index f4dd408f11..fc326e7ff2 100644 --- a/libraries/hotstuff/chain_pacemaker.cpp +++ b/libraries/hotstuff/chain_pacemaker.cpp @@ -101,12 +101,13 @@ namespace eosio { namespace hotstuff { #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_key_map_t finalizer_keys, fc::logger& logger) : _chain(chain), - _qc_chain("default", this, std::move(my_producers), std::move(finalizer_keys), logger, DEFAULT_SAFETY_STATE_FILE), + _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_state_ptr& blk ) { diff --git a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp index 6895ceeba7..4cc274c828 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/chain_pacemaker.hpp @@ -13,8 +13,6 @@ namespace eosio::chain { namespace eosio::hotstuff { - const std::string DEFAULT_SAFETY_STATE_FILE = "hs_tm_safety_state"; //todo : add reversible blocks folder - class chain_pacemaker : public base_pacemaker { public: diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index e5ca79a79d..6dec6d6a15 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -32,38 +32,59 @@ namespace eosio::hotstuff { - using boost::multi_index_container; - using namespace boost::multi_index; - using namespace eosio::chain; - - template - static void read_state(const std::string file_path, StateObj& s){ - - if (file_path != std::string()){ + template class state_db_manager { + public: + static bool read(fc::cfile& pfile, StateObjectType& sobj) { + if (!pfile.is_open()) + return false; + pfile.seek_end(0); + if (pfile.tellp() <= 0) + return false; + pfile.seek(0); + auto datastream = pfile.create_datastream(); + StateObjectType read_sobj; + try { + fc::raw::unpack(datastream, read_sobj); + sobj = std::move(read_sobj); + return true; + } catch (...) { + return false; + } + } + static bool write(fc::cfile& pfile, const StateObjectType& sobj) { + if (!pfile.is_open()) + return false; + pfile.seek(0); + pfile.truncate(); + 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"); - auto ds = pfile.create_datastream(); - fc::raw::unpack(ds, s); + bool result = read(pfile, sobj); pfile.close(); + return result; } - - } - - template - static void write_state(const std::string file_path, const StateObj s){ - - if (file_path != std::string()){ - fc::cfile pfile; - pfile.set_file_path(file_path); - pfile.open(fc::cfile::truncate_rw_mode); - auto data = fc::raw::pack(s); - pfile.write(data.data(), data.size()); - pfile.close(); + 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); + bool result = write(pfile, sobj); + pfile.close(); + return result; } + }; + + using boost::multi_index_container; + using namespace boost::multi_index; + using namespace eosio::chain; - } - class quorum_certificate { public: explicit quorum_certificate(size_t finalizer_size = 0) { @@ -154,6 +175,8 @@ namespace eosio::hotstuff { 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 @@ -163,8 +186,6 @@ namespace eosio::hotstuff { hs_bitset update_bitset(const hs_bitset& finalizer_set, const fc::crypto::blslib::bls_public_key& finalizer_key); - //digest_type get_digest_to_sign(const block_id_type& block_id, uint8_t phase_counter, const fc::sha256& final_on_qc); //get digest to sign from proposal data - void reset_qc(const fc::sha256& proposal_id); bool evaluate_quorum(const hs_bitset& finalizers, const fc::crypto::blslib::bls_signature& agg_sig, const hs_proposal_message& proposal); //evaluate quorum for a proposal @@ -229,7 +250,6 @@ namespace eosio::hotstuff { safety_state _safety_state; fc::sha256 _b_leaf; fc::sha256 _b_exec; - std::string _safety_state_file; fc::sha256 _b_finality_violation; quorum_certificate _high_qc; quorum_certificate _current_qc; @@ -238,6 +258,9 @@ namespace eosio::hotstuff { chain::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; diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 0a21e8349b..86a9efafa9 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -4,6 +4,14 @@ namespace eosio::hotstuff { + 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) { #ifdef QC_CHAIN_SIMPLE_PROPOSAL_STORE if (proposal_id == NULL_PROPOSAL_ID) @@ -219,14 +227,14 @@ namespace eosio::hotstuff { _my_producers(std::move(my_producers)), _my_finalizer_keys(std::move(finalizer_keys)), _id(std::move(id)), + _safety_state_file(safety_state_file), _logger(logger) { - //todo : read liveness state / select initialization heuristics ? - if (safety_state_file!=std::string() && std::filesystem::exists(safety_state_file)){ - eosio::hotstuff::read_state(safety_state_file, _safety_state); - _safety_state_file = safety_state_file; + if (!_safety_state_file.empty()) { + _safety_state_file_handle.set_file_path(safety_state_file); + state_db_manager::read(_safety_state_file, _safety_state); } _high_qc.reset({}, 21); // TODO: use active schedule size @@ -389,7 +397,7 @@ namespace eosio::hotstuff { //update internal state update(proposal); - write_state(_safety_state_file , _safety_state); + write_safety_state_file(); for (auto &msg : msgs) { send_hs_vote_msg( std::nullopt, msg ); diff --git a/libraries/hotstuff/test/hotstuff_tools.cpp b/libraries/hotstuff/test/hotstuff_tools.cpp index aabe46723a..157e81a408 100644 --- a/libraries/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/hotstuff/test/hotstuff_tools.cpp @@ -1,63 +1,63 @@ -#include -#include -#include - -#include - -#include - -BOOST_AUTO_TEST_CASE(view_number_tests) try { - - eosio::hotstuff::hs_proposal_message hspm_1; - eosio::hotstuff::hs_proposal_message hspm_2; - eosio::hotstuff::hs_proposal_message hspm_3; - eosio::hotstuff::hs_proposal_message hspm_4; - eosio::hotstuff::hs_proposal_message hspm_5; - - hspm_1.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_1.phase_counter = 0; - - hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.phase_counter = 1; - - hspm_3.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_3.phase_counter = 0; - - hspm_4.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_4.phase_counter = 1; - - hspm_5.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_5.phase_counter = 2; - - eosio::hotstuff::view_number vn_1 = hspm_1.get_view_number(); - eosio::hotstuff::view_number vn_2 = hspm_2.get_view_number(); - eosio::hotstuff::view_number vn_3 = hspm_3.get_view_number(); - eosio::hotstuff::view_number vn_4 = hspm_4.get_view_number(); - eosio::hotstuff::view_number vn_5 = hspm_5.get_view_number(); - - //test getters - - BOOST_CHECK_EQUAL(vn_1.block_height() == 194217067, true); - BOOST_CHECK_EQUAL(vn_1.phase_counter() == 0, true); - - //test operators == true - BOOST_CHECK_EQUAL(vn_1 == vn_1, true); - BOOST_CHECK_EQUAL(vn_1 != vn_2, true); - - BOOST_CHECK_EQUAL(vn_1 < vn_2, true); - BOOST_CHECK_EQUAL(vn_2 < vn_3, true); - BOOST_CHECK_EQUAL(vn_3 < vn_4, true); - BOOST_CHECK_EQUAL(vn_4 < vn_5, true); - BOOST_CHECK_EQUAL(vn_4 <= vn_5, true); - - //test operators == false - BOOST_CHECK_EQUAL(vn_1 >= vn_2, false); - BOOST_CHECK_EQUAL(vn_2 > vn_3, false); - - //test constructor - - eosio::hotstuff::view_number vn_6 = eosio::hotstuff::view_number(std::make_pair(194217068, 2)); - - BOOST_CHECK_EQUAL(vn_5 == vn_6, true); - -} FC_LOG_AND_RETHROW(); \ No newline at end of file +#include +#include +#include + +#include + +#include + +BOOST_AUTO_TEST_CASE(view_number_tests) try { + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + eosio::hotstuff::hs_proposal_message hspm_3; + eosio::hotstuff::hs_proposal_message hspm_4; + eosio::hotstuff::hs_proposal_message hspm_5; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_1.phase_counter = 0; + + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.phase_counter = 1; + + hspm_3.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_3.phase_counter = 0; + + hspm_4.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_4.phase_counter = 1; + + hspm_5.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_5.phase_counter = 2; + + eosio::hotstuff::view_number vn_1 = hspm_1.get_view_number(); + eosio::hotstuff::view_number vn_2 = hspm_2.get_view_number(); + eosio::hotstuff::view_number vn_3 = hspm_3.get_view_number(); + eosio::hotstuff::view_number vn_4 = hspm_4.get_view_number(); + eosio::hotstuff::view_number vn_5 = hspm_5.get_view_number(); + + //test getters + + BOOST_CHECK_EQUAL(vn_1.block_height() == 194217067, true); + BOOST_CHECK_EQUAL(vn_1.phase_counter() == 0, true); + + //test operators == true + BOOST_CHECK_EQUAL(vn_1 == vn_1, true); + BOOST_CHECK_EQUAL(vn_1 != vn_2, true); + + BOOST_CHECK_EQUAL(vn_1 < vn_2, true); + BOOST_CHECK_EQUAL(vn_2 < vn_3, true); + BOOST_CHECK_EQUAL(vn_3 < vn_4, true); + BOOST_CHECK_EQUAL(vn_4 < vn_5, true); + BOOST_CHECK_EQUAL(vn_4 <= vn_5, true); + + //test operators == false + BOOST_CHECK_EQUAL(vn_1 >= vn_2, false); + BOOST_CHECK_EQUAL(vn_2 > vn_3, false); + + //test constructor + + eosio::hotstuff::view_number vn_6 = eosio::hotstuff::view_number(194217068, 2); + + BOOST_CHECK_EQUAL(vn_5 == vn_6, true); + +} FC_LOG_AND_RETHROW(); diff --git a/libraries/hotstuff/test/test_hotstuff_state.cpp b/libraries/hotstuff/test/test_hotstuff_state.cpp index a785d362a1..68166bfe0e 100644 --- a/libraries/hotstuff/test/test_hotstuff_state.cpp +++ b/libraries/hotstuff/test/test_hotstuff_state.cpp @@ -1,160 +1,163 @@ -#include - -#include -#include - -#include - -#include -#include - -#include - -using std::cout; - -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 { - - eosio::hotstuff::hs_proposal_message hspm_1; - eosio::hotstuff::hs_proposal_message hspm_2; - - hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = eosio::chain::block_id_type(); - hspm_1.phase_counter = 2; - - eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); - - hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = eosio::chain::block_id_type(); - hspm_2.phase_counter = 0; - - fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - eosio::hotstuff::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); - - eosio::hotstuff::write_state(file_path_1, ss); - - bool ok = true; - - BOOST_CHECK_EQUAL(ok, true); - -} FC_LOG_AND_RETHROW(); - -BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { - - eosio::hotstuff::safety_state ss; - - eosio::hotstuff::read_state(file_path_1, ss); - - std::remove(file_path_1.c_str()); - - //test correct values - eosio::hotstuff::hs_proposal_message hspm_1; - eosio::hotstuff::hs_proposal_message hspm_2; - - hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = eosio::chain::block_id_type(); - hspm_1.phase_counter = 2; - - eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); - - hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = eosio::chain::block_id_type(); - hspm_2.phase_counter = 0; - - fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); - - //std::pair ss = get_safety_state(eosio::chain::name{""}); - - bool ok1 = ss.get_v_height(fc::crypto::blslib::bls_public_key{}) == v_height; - bool ok2 = ss.get_b_lock(fc::crypto::blslib::bls_public_key{}) == b_lock; - - BOOST_CHECK_EQUAL(ok1, true); - BOOST_CHECK_EQUAL(ok2, true); - -} FC_LOG_AND_RETHROW(); - -/*BOOST_AUTO_TEST_CASE(write_liveness_state_to_file) try { - - eosio::hotstuff::hs_proposal_message hspm_1; - eosio::hotstuff::hs_proposal_message hspm_2; - - hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 - hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = eosio::chain::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 - eosio::hotstuff::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"); - - eosio::hotstuff::liveness_state ls(high_qc, b_leaf, b_exec); - - eosio::hotstuff::write_state(file_path_2, ls); - - bool ok = true; - - BOOST_CHECK_EQUAL(ok, true); - -} FC_LOG_AND_RETHROW(); - -BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { - - eosio::hotstuff::liveness_state ls; - - eosio::hotstuff::read_state(file_path_2, ls); - - std::remove(file_path_2.c_str()); - - //test correct values - - eosio::hotstuff::hs_proposal_message hspm_1; - eosio::hotstuff::hs_proposal_message hspm_2; - - hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 - hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 - hspm_2.final_on_qc = eosio::chain::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 - eosio::hotstuff::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"); - - bool ok1 = ls.high_qc == high_qc; - bool ok2 = ls.b_exec == b_exec; - bool ok3 = ls.b_leaf == b_leaf; - - BOOST_CHECK_EQUAL(ok1, true); - BOOST_CHECK_EQUAL(ok2, true); - BOOST_CHECK_EQUAL(ok3, true); - -} FC_LOG_AND_RETHROW();*/ - -BOOST_AUTO_TEST_SUITE_END() +#include + +#include +#include + +#include + +#include +#include + +#include + +using std::cout; + +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 { + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::block_id_type(); + hspm_1.phase_counter = 2; + + eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); + + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::block_id_type(); + hspm_2.phase_counter = 0; + + fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); + + eosio::hotstuff::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_EQUAL( eosio::hotstuff::state_db_manager::write(file_path_1, ss), true ); + + //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 { + + eosio::hotstuff::safety_state ss; + + BOOST_CHECK_EQUAL( eosio::hotstuff::state_db_manager::read(file_path_1, ss), true ); + + std::remove(file_path_1.c_str()); + + //test correct values + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::block_id_type(); + hspm_1.phase_counter = 2; + + eosio::hotstuff::view_number v_height = hspm_1.get_view_number(); + + hspm_2.block_id = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::block_id_type(); + hspm_2.phase_counter = 0; + + fc::sha256 b_lock = eosio::hotstuff::get_digest_to_sign(hspm_2.block_id, hspm_2.phase_counter, hspm_2.final_on_qc); + + //std::pair ss = get_safety_state(eosio::chain::name{""}); + + bool ok1 = ss.get_v_height(fc::crypto::blslib::bls_public_key{}) == v_height; + bool ok2 = ss.get_b_lock(fc::crypto::blslib::bls_public_key{}) == b_lock; + + BOOST_CHECK_EQUAL(ok1, true); + BOOST_CHECK_EQUAL(ok2, true); + +} 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 { + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217068 + hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::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 + eosio::hotstuff::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"); + + eosio::hotstuff::liveness_state ls(high_qc, b_leaf, b_exec); + + eosio::hotstuff::write_state(file_path_2, ls); + + bool ok = true; + + BOOST_CHECK_EQUAL(ok, true); + +} FC_LOG_AND_RETHROW(); + +BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { + + eosio::hotstuff::liveness_state ls; + + eosio::hotstuff::read_state(file_path_2, ls); + + std::remove(file_path_2.c_str()); + + //test correct values + + eosio::hotstuff::hs_proposal_message hspm_1; + eosio::hotstuff::hs_proposal_message hspm_2; + + hspm_1.block_id = eosio::chain::block_id_type("0b93846cf55a3ecbcd8f9bd86866b1aecc2e8bd981e40c92609ce3a68dbd0824"); //UX Network block #194217067 + hspm_1.final_on_qc = eosio::chain::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 = eosio::chain::block_id_type("0b93846ba73bdfdc9b2383863b64f8f921c8a2379d6dde4e05bdd2e434e9392a"); //UX Network block #194217067 + hspm_2.final_on_qc = eosio::chain::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 + eosio::hotstuff::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"); + + bool ok1 = ls.high_qc == high_qc; + bool ok2 = ls.b_exec == b_exec; + bool ok3 = ls.b_leaf == b_leaf; + + BOOST_CHECK_EQUAL(ok1, true); + BOOST_CHECK_EQUAL(ok2, true); + BOOST_CHECK_EQUAL(ok3, true); + +} FC_LOG_AND_RETHROW();*/ + +BOOST_AUTO_TEST_SUITE_END() diff --git a/libraries/libfc/include/fc/io/cfile.hpp b/libraries/libfc/include/fc/io/cfile.hpp index 7e79b59942..f3cd05ded1 100644 --- a/libraries/libfc/include/fc/io/cfile.hpp +++ b/libraries/libfc/include/fc/io/cfile.hpp @@ -150,6 +150,14 @@ class cfile { } } + void truncate() { + const int fd = fileno(); + if( -1 == ftruncate(fd, 0) ) { + throw std::ios_base::failure( "cfile: " + _file_path.generic_string() + + " unable to truncate file, error: " + std::to_string( errno ) ); + } + } + void flush() { if( 0 != fflush( _file.get() ) ) { int err = ferror( _file.get() ); From e771f4999db0f7fe90cfc5fd5c7a080ead6e772a Mon Sep 17 00:00:00 2001 From: fcecin Date: Fri, 10 Nov 2023 16:05:30 -0300 Subject: [PATCH 08/11] Fixes - hotstuff/state.hpp const& & return {} - hotstuff/state.hpp fix formatting & line endings - qc_chain.cpp const& finalizer keys iter --- .../hotstuff/include/eosio/hotstuff/state.hpp | 124 +++++++++--------- libraries/hotstuff/qc_chain.cpp | 2 +- 2 files changed, 62 insertions(+), 64 deletions(-) diff --git a/libraries/hotstuff/include/eosio/hotstuff/state.hpp b/libraries/hotstuff/include/eosio/hotstuff/state.hpp index bf9a14b99e..589f3a2188 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/state.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/state.hpp @@ -1,63 +1,61 @@ -#include - -#include - -namespace eosio::hotstuff { - - using namespace eosio::chain; - - struct safety_state { - - void set_v_height(const fc::crypto::blslib::bls_public_key& finalizer_key, const eosio::chain::view_number v_height){ - _states[finalizer_key].first = v_height; - } - - void set_b_lock(const fc::crypto::blslib::bls_public_key& finalizer_key, fc::sha256 b_lock){ - _states[finalizer_key].second = b_lock; - } - - std::pair get_safety_state(const fc::crypto::blslib::bls_public_key& finalizer_key) const{ - auto s = _states.find(finalizer_key); - if (s != _states.end()) return s->second; - else return std::make_pair(eosio::chain::view_number(),fc::sha256()); - } - - eosio::chain::view_number get_v_height(const fc::crypto::blslib::bls_public_key& finalizer_key) const{ - auto s = _states.find(finalizer_key); - if (s != _states.end()) return s->second.first; - else return eosio::chain::view_number(); - }; - - fc::sha256 get_b_lock(const fc::crypto::blslib::bls_public_key& finalizer_key) const{ - auto s_itr = _states.find(finalizer_key); - if (s_itr != _states.end()) return s_itr->second.second; - else return fc::sha256(); - }; - - //todo : implement safety state default / sorting - - std::pair get_safety_state() const{ - auto s = _states.begin(); - if (s != _states.end()) return s->second; - else return std::make_pair(eosio::chain::view_number(),fc::sha256()); - } - - eosio::chain::view_number get_v_height() const{ - auto s = _states.begin(); - if (s != _states.end()) return s->second.first; - else return eosio::chain::view_number(); - }; - - fc::sha256 get_b_lock() const{ - auto s_itr = _states.begin(); - if (s_itr != _states.end()) return s_itr->second.second; - else return fc::sha256(); - }; - - std::map> _states; - - }; - -} - -FC_REFLECT(eosio::hotstuff::safety_state, (_states)) +#include + +#include + +namespace eosio::hotstuff { + + using namespace eosio::chain; + + struct safety_state { + + void set_v_height(const fc::crypto::blslib::bls_public_key& finalizer_key, const eosio::chain::view_number v_height) { + _states[finalizer_key].first = v_height; + } + + void set_b_lock(const fc::crypto::blslib::bls_public_key& finalizer_key, const fc::sha256& b_lock) { + _states[finalizer_key].second = b_lock; + } + + std::pair get_safety_state(const fc::crypto::blslib::bls_public_key& finalizer_key) const { + auto s = _states.find(finalizer_key); + if (s != _states.end()) return s->second; + else return {}; + } + + eosio::chain::view_number get_v_height(const fc::crypto::blslib::bls_public_key& finalizer_key) const { + auto s = _states.find(finalizer_key); + if (s != _states.end()) return s->second.first; + else return {}; + }; + + fc::sha256 get_b_lock(const fc::crypto::blslib::bls_public_key& finalizer_key) const { + auto s_itr = _states.find(finalizer_key); + if (s_itr != _states.end()) return s_itr->second.second; + else return {}; + }; + + //todo : implement safety state default / sorting + + std::pair get_safety_state() const { + auto s = _states.begin(); + if (s != _states.end()) return s->second; + else return {}; + } + + eosio::chain::view_number get_v_height() const { + auto s = _states.begin(); + if (s != _states.end()) return s->second.first; + else return {}; + }; + + fc::sha256 get_b_lock() const { + auto s_itr = _states.begin(); + if (s_itr != _states.end()) return s_itr->second.second; + else return {}; + }; + + std::map> _states; + }; +} + +FC_REFLECT(eosio::hotstuff::safety_state, (_states)) diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 86a9efafa9..288d8ab295 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -923,7 +923,7 @@ namespace eosio::hotstuff { fc_tlog(_logger, "setting _b_lock to ${proposal_id}", ("proposal_id",b_1.proposal_id )); - for (auto f_itr : _my_finalizer_keys) { + for (const auto& f_itr : _my_finalizer_keys) { _safety_state.set_b_lock(f_itr.first, b_1.proposal_id); //commit phase on b1 } From e0da055c16e2de705261c294394a686d7edbbf26 Mon Sep 17 00:00:00 2001 From: fcecin Date: Sat, 18 Nov 2023 01:02:21 -0300 Subject: [PATCH 09/11] Some of the fixes from last reviews (WIP) - Add view_number::operator<< - Simplify and remove redundant BOOST_CHECK_xxx() calls in tests - Magic constant added to the header of state files - Refactor/simplify state_db_manager in qc_chain.hpp --- .../chain/include/eosio/chain/hotstuff.hpp | 3 ++ .../include/eosio/hotstuff/qc_chain.hpp | 43 +++++++++---------- libraries/hotstuff/test/hotstuff_tools.cpp | 29 +++++-------- .../hotstuff/test/test_hotstuff_state.cpp | 25 +++-------- 4 files changed, 41 insertions(+), 59 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index aab3b26d63..7a83ffe374 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -27,6 +27,9 @@ namespace eosio::chain { view_number() : bheight(0), pcounter(0) {} explicit view_number(uint32_t block_height, uint8_t phase_counter) : bheight(block_height), pcounter(phase_counter) {} auto operator<=>(const view_number&) const = default; + friend std::ostream& operator<<(std::ostream& os, const view_number& vn) { + os << "view_number(" << vn.bheight << ", " << vn.pcounter << ")\n"; + } uint32_t block_height() const { return bheight; } uint8_t phase_counter() const { return pcounter; } diff --git a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp index 6dec6d6a15..13dc7f7e77 100644 --- a/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp +++ b/libraries/hotstuff/include/eosio/hotstuff/qc_chain.hpp @@ -34,28 +34,13 @@ namespace eosio::hotstuff { template class state_db_manager { public: - static bool read(fc::cfile& pfile, StateObjectType& sobj) { - if (!pfile.is_open()) - return false; - pfile.seek_end(0); - if (pfile.tellp() <= 0) - return false; - pfile.seek(0); - auto datastream = pfile.create_datastream(); - StateObjectType read_sobj; - try { - fc::raw::unpack(datastream, read_sobj); - sobj = std::move(read_sobj); - return true; - } catch (...) { - return false; - } - } + 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(); @@ -67,17 +52,29 @@ namespace eosio::hotstuff { fc::cfile pfile; pfile.set_file_path(file_path); pfile.open("rb"); - bool result = read(pfile, sobj); - pfile.close(); - return result; + 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); - bool result = write(pfile, sobj); - pfile.close(); - return result; + return write(pfile, sobj); } }; diff --git a/libraries/hotstuff/test/hotstuff_tools.cpp b/libraries/hotstuff/test/hotstuff_tools.cpp index 157e81a408..e2d3e994c9 100644 --- a/libraries/hotstuff/test/hotstuff_tools.cpp +++ b/libraries/hotstuff/test/hotstuff_tools.cpp @@ -36,28 +36,21 @@ BOOST_AUTO_TEST_CASE(view_number_tests) try { eosio::hotstuff::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_EQUAL(vn_1.block_height() == 194217067, true); - BOOST_CHECK_EQUAL(vn_1.phase_counter() == 0, true); + 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 operators == true - BOOST_CHECK_EQUAL(vn_1 == vn_1, true); - BOOST_CHECK_EQUAL(vn_1 != vn_2, true); - - BOOST_CHECK_EQUAL(vn_1 < vn_2, true); - BOOST_CHECK_EQUAL(vn_2 < vn_3, true); - BOOST_CHECK_EQUAL(vn_3 < vn_4, true); - BOOST_CHECK_EQUAL(vn_4 < vn_5, true); - BOOST_CHECK_EQUAL(vn_4 <= vn_5, true); - - //test operators == false - BOOST_CHECK_EQUAL(vn_1 >= vn_2, false); - BOOST_CHECK_EQUAL(vn_2 > vn_3, false); - - //test constructor +//test constructor eosio::hotstuff::view_number vn_6 = eosio::hotstuff::view_number(194217068, 2); - BOOST_CHECK_EQUAL(vn_5 == vn_6, true); + BOOST_CHECK_EQUAL(vn_5, vn_6); } FC_LOG_AND_RETHROW(); diff --git a/libraries/hotstuff/test/test_hotstuff_state.cpp b/libraries/hotstuff/test/test_hotstuff_state.cpp index 68166bfe0e..97c2a3cb4e 100644 --- a/libraries/hotstuff/test/test_hotstuff_state.cpp +++ b/libraries/hotstuff/test/test_hotstuff_state.cpp @@ -39,7 +39,7 @@ BOOST_AUTO_TEST_CASE(write_safety_state_to_file) try { 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_EQUAL( eosio::hotstuff::state_db_manager::write(file_path_1, ss), true ); + BOOST_CHECK( eosio::hotstuff::state_db_manager::write(file_path_1, ss) ); //fc::cfile pfile; //pfile.set_file_path(file_path_1); @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { eosio::hotstuff::safety_state ss; - BOOST_CHECK_EQUAL( eosio::hotstuff::state_db_manager::read(file_path_1, ss), true ); + BOOST_CHECK( eosio::hotstuff::state_db_manager::read(file_path_1, ss) ); std::remove(file_path_1.c_str()); @@ -75,11 +75,8 @@ BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { //std::pair ss = get_safety_state(eosio::chain::name{""}); - bool ok1 = ss.get_v_height(fc::crypto::blslib::bls_public_key{}) == v_height; - bool ok2 = ss.get_b_lock(fc::crypto::blslib::bls_public_key{}) == b_lock; - - BOOST_CHECK_EQUAL(ok1, true); - BOOST_CHECK_EQUAL(ok2, true); + 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(); @@ -112,10 +109,6 @@ BOOST_AUTO_TEST_CASE(read_safety_state_from_file) try { eosio::hotstuff::write_state(file_path_2, ls); - bool ok = true; - - BOOST_CHECK_EQUAL(ok, true); - } FC_LOG_AND_RETHROW(); BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { @@ -150,13 +143,9 @@ BOOST_AUTO_TEST_CASE(read_liveness_state_from_file) try { high_qc.active_finalizers = 1245; high_qc.active_agg_sig = fc::crypto::blslib::bls_signature("SIG_BLS_23PuSu1B72cPe6wxGkKjAaaZqA1Ph79zSoW7omsKKUrnprbA3cJCJVhT48QKUG6ofjYTTg4BA4TrVENWyrxjTomwLX6TGdVg2RYhKH7Kk9X23K5ohuhKQcWQ6AwJJGVSbSp4"); - bool ok1 = ls.high_qc == high_qc; - bool ok2 = ls.b_exec == b_exec; - bool ok3 = ls.b_leaf == b_leaf; - - BOOST_CHECK_EQUAL(ok1, true); - BOOST_CHECK_EQUAL(ok2, true); - BOOST_CHECK_EQUAL(ok3, true); + 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();*/ From c17103431a46f98e3e14eaaf3a4cf29667ac29c5 Mon Sep 17 00:00:00 2001 From: Fabiana Cecin Date: Sat, 18 Nov 2023 01:11:20 -0300 Subject: [PATCH 10/11] Update libraries/chain/include/eosio/chain/hotstuff.hpp Co-authored-by: Gregory Popovitch --- libraries/chain/include/eosio/chain/hotstuff.hpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/hotstuff.hpp b/libraries/chain/include/eosio/chain/hotstuff.hpp index 7a83ffe374..0f765747b0 100644 --- a/libraries/chain/include/eosio/chain/hotstuff.hpp +++ b/libraries/chain/include/eosio/chain/hotstuff.hpp @@ -33,9 +33,8 @@ namespace eosio::chain { uint32_t block_height() const { return bheight; } uint8_t phase_counter() const { return pcounter; } - uint64_t to_uint64_t() const { return compute_height(bheight, pcounter); } + uint64_t get_key() const { return compute_height(bheight, pcounter); } std::string to_string() const { return std::to_string(bheight) + "::" + std::to_string(pcounter); } - uint64_t get_key() const { return to_uint64_t(); } uint32_t bheight; uint8_t pcounter; From 749cf415566d3c534fbbf100a899c21e61beb15a Mon Sep 17 00:00:00 2001 From: fcecin Date: Sat, 18 Nov 2023 20:00:04 -0300 Subject: [PATCH 11/11] Fix for merge hotstuff_integration -> persist_hs_safety_liveness_state_WIP - hs_proposal_message::get_height --> hs_proposal_message::get_key fix --- libraries/hotstuff/qc_chain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/hotstuff/qc_chain.cpp b/libraries/hotstuff/qc_chain.cpp index 81f1669059..0a6d3f5e23 100644 --- a/libraries/hotstuff/qc_chain.cpp +++ b/libraries/hotstuff/qc_chain.cpp @@ -441,7 +441,7 @@ namespace eosio::hotstuff { 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_height(), { vote.finalizer_key } }; + seen_votes sv = { p->proposal_id, p->get_key(), { vote.finalizer_key } }; _seen_votes_store.insert(sv); propagate = true; } else {