From 5e2cecc69462832c3664f3ebf4e2daf050d2f98c Mon Sep 17 00:00:00 2001 From: oldcold Date: Tue, 17 Sep 2019 17:22:13 +0800 Subject: [PATCH 01/15] fix pbft cert generation and validation --- libraries/chain/pbft_database.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 84c7a70bcca..fcd517dbbb4 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -650,12 +650,14 @@ namespace eosio { for (const auto& pre: prepares) { if (prepare_count.find(pre.first.first) == prepare_count.end()) prepare_count[pre.first.first] = 0; - prepare_msg[pre.first.first].emplace_back(pre.second); } for (const auto& bp: as) { for (const auto& pp: prepares) { - if (bp.block_signing_key == pp.first.second) prepare_count[pp.first.first] += 1; + if (bp.block_signing_key == pp.first.second) { + prepare_count[pp.first.first] += 1; + prepare_msg[pp.first.first].emplace_back(pp.second); + } } } @@ -737,12 +739,14 @@ namespace eosio { for (const auto& com: commits) { if (commit_count.find(com.first.first) == commit_count.end()) commit_count[com.first.first] = 0; - commit_msg[com.first.first].emplace_back(com.second); } for (const auto& bp: as) { for (const auto& cc: commits) { - if (bp.block_signing_key == cc.first.second) commit_count[cc.first.first] += 1; + if (bp.block_signing_key == cc.first.second) { + commit_count[cc.first.first] += 1; + commit_msg[cc.first.first].emplace_back(cc.second); + } } } @@ -773,11 +777,15 @@ namespace eosio { auto pvs = *itr; + auto lscb_bps = lscb_active_producers().producers; + if (pvs->is_view_changed) { pvcc.target_view=pvs->view; pvcc.view_changes.reserve(pvs->view_changes.size()); - for(auto& view_change : pvs->view_changes) { - pvcc.view_changes.emplace_back( view_change.second ); + for (auto& bp : lscb_bps) { + for(auto& view_change : pvs->view_changes) { + if (bp.block_signing_key == view_change.first) pvcc.view_changes.emplace_back(view_change.second); + } } } return pvcc; @@ -798,8 +806,7 @@ namespace eosio { for (auto& p : prepares) { auto pmm = pbft_message_metadata(p, chain_id); prepares_metadata.emplace_back(pmm); - if (!is_valid_prepare(p, pmm.sender_key)) return false; - if (add_to_pbft_db) add_pbft_prepare(p, pmm.sender_key); + if ( add_to_pbft_db && is_valid_prepare(p, pmm.sender_key) ) add_pbft_prepare(p, pmm.sender_key); } auto cert_id = certificate.block_info.block_id; @@ -861,8 +868,7 @@ namespace eosio { for (auto& c : commits) { auto pmm = pbft_message_metadata(c, chain_id); commits_metadata.emplace_back(pmm); - if (!is_valid_commit(c, pmm.sender_key)) return false; - if (add_to_pbft_db) add_pbft_commit(c, pmm.sender_key); + if (add_to_pbft_db && is_valid_commit(c, pmm.sender_key)) add_pbft_commit(c, pmm.sender_key); } auto cert_id = certificate.block_info.block_id; From 4304c51d6f7f0636a63fe46169bd5c0a0fc99583 Mon Sep 17 00:00:00 2001 From: oldcold Date: Tue, 10 Sep 2019 18:03:14 +0800 Subject: [PATCH 02/15] attempt to fix bad prepared cert --- libraries/chain/pbft_database.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index fcd517dbbb4..35ebee45628 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -1100,14 +1100,24 @@ namespace eosio { auto forks = fetch_fork_from(block_infos); fork_info_type longest_fork; + longest_fork.reserve(threshold); for (const auto& f : forks) { if (f.size() > longest_fork.size()) { longest_fork = f; } } - return longest_fork.empty() - || (longest_fork.size() + non_fork_bp_count >= threshold && bi.block_id == longest_fork.back().block_id); + if (longest_fork.empty()) { + return true; + } else if (longest_fork.size() + non_fork_bp_count < threshold) { + return false; + } else { + while (non_fork_bp_count) { + longest_fork.emplace_back(block_info_type{}); + --non_fork_bp_count; + } + return longest_fork[2/3*threshold].block_id == bi.block_id; + } } pbft_stable_checkpoint pbft_database::fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr& b) { From 3144c18d603f532e759e3cb480dbed1d088fb387 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 18 Sep 2019 11:18:56 +0800 Subject: [PATCH 03/15] fix longest fork threshold --- libraries/chain/pbft_database.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 35ebee45628..17f21a7aee7 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -1109,14 +1109,14 @@ namespace eosio { if (longest_fork.empty()) { return true; - } else if (longest_fork.size() + non_fork_bp_count < threshold) { + } else if (longest_fork.size() + non_fork_bp_count < threshold || threshold == 0) { return false; } else { while (non_fork_bp_count) { longest_fork.emplace_back(block_info_type{}); --non_fork_bp_count; } - return longest_fork[2/3*threshold].block_id == bi.block_id; + return longest_fork[threshold-1].block_id == bi.block_id; } } From 3971da577ca2b62f43fa64a7f9cc25da7c77caa9 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 18 Sep 2019 15:19:40 +0800 Subject: [PATCH 04/15] redo cert validation --- .../include/eosio/chain/pbft_database.hpp | 4 - libraries/chain/pbft_database.cpp | 285 ++++++++++-------- 2 files changed, 163 insertions(+), 126 deletions(-) diff --git a/libraries/chain/include/eosio/chain/pbft_database.hpp b/libraries/chain/include/eosio/chain/pbft_database.hpp index 6b4bb2cd7a9..d14d381017a 100644 --- a/libraries/chain/include/eosio/chain/pbft_database.hpp +++ b/libraries/chain/include/eosio/chain/pbft_database.hpp @@ -503,16 +503,12 @@ namespace eosio { bool is_less_than_high_watermark(block_num_type bnum); bool is_valid_prepared_certificate(const pbft_prepared_certificate& certificate, bool add_to_pbft_db = false); bool is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db = false); - bool is_valid_longest_fork(const block_info_type& bi, fork_info_type& block_infos, unsigned long threshold, unsigned long non_fork_bp_count); producer_schedule_type lscb_active_producers() const; vector& get_updated_watermarks(); flat_map& get_updated_fork_schedules(); block_num_type get_current_pbft_watermark(); - vector fetch_fork_from(fork_info_type& block_infos); - fork_info_type fetch_first_fork_from(fork_info_type& bi); - void set(const pbft_state_ptr& s); void set(const pbft_checkpoint_state_ptr& s); void prune(const pbft_state_ptr& h); diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 17f21a7aee7..de91e792713 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -818,41 +818,105 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map prepare_count; + flat_map>> prepare_msg; for (const auto& pm: prepares_metadata) { - if (prepare_count.find(pm.msg.view) == prepare_count.end()) prepare_count[pm.msg.view] = 0; + if (prepare_count.find(pm.msg.view) == prepare_count.end()) { + prepare_count[pm.msg.view] = 0; + prepare_msg[pm.msg.view].reserve(bp_threshold); + } } for (const auto& bp: producer_schedule.producers) { for (const auto& pm: prepares_metadata) { - if (bp.block_signing_key == pm.sender_key) prepare_count[pm.msg.view] += 1; + if (bp.block_signing_key == pm.sender_key) { + prepare_count[pm.msg.view] += 1; + prepare_msg[pm.msg.view].emplace_back(pm); + } } } auto should_prepared = false; - + auto valid_prepares = vector>{}; + valid_prepares.reserve(bp_threshold); for (const auto& e: prepare_count) { if (e.second >= bp_threshold) { should_prepared = true; + valid_prepares = prepare_msg[e.first]; } } if (!should_prepared) return false; - //validate prepare - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - auto non_fork_bp_count = 0; - fork_info_type prepare_infos; - prepare_infos.reserve(certificate.prepares.size()); - for (const auto& p : certificate.prepares) { - //only search in fork db - if (p.block_info.block_num() <= lscb_num) { - ++non_fork_bp_count; - } else { - prepare_infos.emplace_back(p.block_info); + auto local_index = pbft_state_multi_index_type{}; + for (auto &p: valid_prepares) { + auto& by_block_id_index = local_index.get(); + + auto current = ctrl.fetch_block_state_by_id(p.msg.block_info.block_id); + + while ((current) && (current->block_num > ctrl.last_stable_checkpoint_block_num())) { + auto curr_itr = by_block_id_index.find(current->id); + + if (curr_itr == by_block_id_index.end()) { + try { + flat_map, pbft_prepare> local_prepares; + local_prepares[std::make_pair(p.msg.view, p.sender_key)] = p.msg; + pbft_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.prepares = local_prepares; + auto curr_psp = std::make_shared(move(curr_ps)); + local_index.insert(curr_psp); + } catch (...) { + elog("prepare insert failure: ${p}", ("p", p.msg)); + } + } else { + auto local_prepares = (*curr_itr)->prepares; + if (local_prepares.find(std::make_pair(p.msg.view, p.sender_key)) == local_prepares.end()) { + by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { + psp->prepares[std::make_pair(p.msg.view, p.sender_key)] = p.msg; + }); + } + } + curr_itr = by_block_id_index.find(current->id); + if (curr_itr != by_block_id_index.end()) { + + auto cpsp = *curr_itr; + auto local_prepares = cpsp->prepares; + auto as = current->active_schedule.producers; + auto threshold = as.size() * 2 / 3 + 1; + if (local_prepares.size() >= threshold && !cpsp->is_prepared && + is_less_than_high_watermark(cpsp->block_num)) { + flat_map local_prepare_count; + for (const auto &pre: local_prepares) { + if (local_prepare_count.find(pre.second.view) == local_prepare_count.end()) + local_prepare_count[pre.second.view] = 0; + } + + for (const auto &bp: as) { + for (const auto &pp: local_prepares) { + if (bp.block_signing_key == pp.first.second) local_prepare_count[pp.first.first] += 1; + } + } + for (const auto &e: local_prepare_count) { + if (e.second >= threshold) { + by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr& p) { p->is_prepared = true; }); + } + } + } + current = ctrl.fetch_block_state_by_id(current->prev()); + } } } - return is_valid_longest_fork(certificate.block_info, prepare_infos, bp_threshold, non_fork_bp_count); + + const auto& by_prepare_and_num_index = local_index.get(); + auto itr = by_prepare_and_num_index.begin(); + if (itr == by_prepare_and_num_index.end()) return false; + + pbft_state_ptr psp = *itr; + + return psp->is_prepared + && psp->block_id == certificate.block_info.block_id; } bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db) { @@ -880,41 +944,110 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map commit_count; + flat_map>> commit_msg; for (const auto& cm: commits_metadata) { - if (commit_count.find(cm.msg.view) == commit_count.end()) commit_count[cm.msg.view] = 0; + if (commit_count.find(cm.msg.view) == commit_count.end()) { + commit_count[cm.msg.view] = 0; + commit_msg[cm.msg.view].reserve(bp_threshold); + } } for (const auto& bp: producer_schedule.producers) { for (const auto& cm: commits_metadata) { - if (bp.block_signing_key == cm.sender_key) commit_count[cm.msg.view] += 1; + if (bp.block_signing_key == cm.sender_key) { + commit_count[cm.msg.view] += 1; + commit_msg[cm.msg.view].emplace_back(cm); + } } } auto should_committed = false; - + auto valid_commits = vector>{}; + valid_commits.reserve(bp_threshold); for (const auto& e: commit_count) { if (e.second >= bp_threshold) { should_committed = true; + valid_commits = commit_msg[e.first]; } } if (!should_committed) return false; - //validate commit - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - auto non_fork_bp_count = 0; - fork_info_type commit_infos; - commit_infos.reserve(certificate.commits.size()); - for (const auto& c : certificate.commits) { - //only search in fork db - if (c.block_info.block_num() <= lscb_num) { - ++non_fork_bp_count; - } else { - commit_infos.emplace_back(c.block_info); + auto local_index = pbft_state_multi_index_type{}; + for (auto &c: valid_commits) { + auto& by_block_id_index = local_index.get(); + + auto current = ctrl.fetch_block_state_by_id(c.msg.block_info.block_id); + + while ((current) && (current->block_num > ctrl.last_stable_checkpoint_block_num())) { + + auto curr_itr = by_block_id_index.find(current->id); + + if (curr_itr == by_block_id_index.end()) { + try { + flat_map, pbft_commit> local_commits; + local_commits[std::make_pair(c.msg.view, c.sender_key)] = c.msg; + pbft_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.commits = local_commits; + auto curr_psp = std::make_shared(move(curr_ps)); + local_index.insert(curr_psp); + } catch (...) { + elog("commit insertion failure: ${c}", ("c", c.msg)); + } + } else { + auto local_commits = (*curr_itr)->commits; + if (local_commits.find(std::make_pair(c.msg.view, c.sender_key)) == local_commits.end()) { + by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { + psp->commits[std::make_pair(c.msg.view, c.sender_key)] = c.msg; + std::sort(psp->commits.begin(), psp->commits.end(), less<>()); + }); + } + } + + curr_itr = by_block_id_index.find(current->id); + if (curr_itr != by_block_id_index.end()) { + + auto cpsp = *curr_itr; + + auto as = current->active_schedule.producers; + auto threshold = as.size() * 2 / 3 + 1; + auto local_commits = cpsp->commits; + if (local_commits.size() >= threshold && !cpsp->is_committed && + is_less_than_high_watermark(cpsp->block_num)) { + flat_map local_commit_count; + for (const auto &com: local_commits) { + if (local_commit_count.find(com.second.view) == local_commit_count.end()) + local_commit_count[com.second.view] = 0; + } + + for (const auto &bp: as) { + for (const auto &pc: local_commits) { + if (bp.block_signing_key == pc.first.second) local_commit_count[pc.first.first] += 1; + } + } + + for (const auto &e: local_commit_count) { + if (e.second >= threshold) { + by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr& p) { p->is_committed = true; }); + } + } + } + current = ctrl.fetch_block_state_by_id(current->prev()); + } } + } - return is_valid_longest_fork(certificate.block_info, commit_infos, bp_threshold, non_fork_bp_count); + const auto& by_commit_and_num_index = local_index.get(); + auto itr = by_commit_and_num_index.begin(); + if (itr == by_commit_and_num_index.end()) return false; + + pbft_state_ptr psp = *itr; + + return psp->is_committed + && psp->block_id == certificate.block_info.block_id; } bool pbft_database::is_valid_view_change(const pbft_view_change& vc, const public_key_type& pk) { @@ -1028,98 +1161,6 @@ namespace eosio { return vc_lscb > 0 && lscb_num > vc_lscb; } - vector pbft_database::fetch_fork_from(fork_info_type& block_infos) { - - vector result; - if (block_infos.empty()) { - return result; - } - if (block_infos.size() == 1) { - result.emplace_back(initializer_list{block_infos.front()}); - return result; - } - - sort(block_infos.begin(), block_infos.end(), - [](const block_info_type& a, const block_info_type& b) -> bool { return a.block_num() > b.block_num(); }); - - while (!block_infos.empty()) { - auto fork = fetch_first_fork_from(block_infos); - if (!fork.empty()) { - result.emplace_back(fork); - } - } - return result; - } - - fork_info_type pbft_database::fetch_first_fork_from(fork_info_type& bi) { - fork_info_type result; - if (bi.empty()) { - return result; - } - if (bi.size() == 1) { - result.emplace_back(bi.front()); - bi.clear(); - return result; - } - //bi should be sorted desc - auto high = bi.front().block_num(); - auto low = bi.back().block_num(); - - auto id = bi.front().block_id; - auto num = bi.front().block_num(); - while (num <= high && num >= low && !bi.empty()) { - auto bs = ctrl.fetch_block_state_by_id(id); - - for (auto it = bi.begin(); it != bi.end();) { - if (it->block_id == id) { - if (bs) { - //add to result only if b exist - result.emplace_back((*it)); - } - it = bi.erase(it); - } else { - it++; - } - } - if (bs) { - id = bs->prev(); - num--; - } else { - break; - } - } - - return result; - } - - bool pbft_database::is_valid_longest_fork( - const block_info_type& bi, - fork_info_type& block_infos, - unsigned long threshold, - unsigned long non_fork_bp_count) { - - auto forks = fetch_fork_from(block_infos); - fork_info_type longest_fork; - longest_fork.reserve(threshold); - for (const auto& f : forks) { - if (f.size() > longest_fork.size()) { - longest_fork = f; - } - } - - if (longest_fork.empty()) { - return true; - } else if (longest_fork.size() + non_fork_bp_count < threshold || threshold == 0) { - return false; - } else { - while (non_fork_bp_count) { - longest_fork.emplace_back(block_info_type{}); - --non_fork_bp_count; - } - return longest_fork[threshold-1].block_id == bi.block_id; - } - } - pbft_stable_checkpoint pbft_database::fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr& b) { pbft_stable_checkpoint psc; From 2ce70d774b83b0f0ebd76ed03f82a3a500ca4623 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 18 Sep 2019 18:48:16 +0800 Subject: [PATCH 05/15] refactor validation, add comments --- .../include/eosio/chain/pbft_database.hpp | 36 +++- libraries/chain/pbft_database.cpp | 159 +++++------------- 2 files changed, 76 insertions(+), 119 deletions(-) diff --git a/libraries/chain/include/eosio/chain/pbft_database.hpp b/libraries/chain/include/eosio/chain/pbft_database.hpp index d14d381017a..3bc63bc3737 100644 --- a/libraries/chain/include/eosio/chain/pbft_database.hpp +++ b/libraries/chain/include/eosio/chain/pbft_database.hpp @@ -332,9 +332,19 @@ namespace eosio { bool is_stable = false; }; + using producer_and_block_info = std::pair; + + struct validation_state { + block_id_type block_id; + block_num_type block_num = 0; + vector producers; + bool enough = false; + }; + using pbft_state_ptr = std::shared_ptr; using pbft_view_change_state_ptr = std::shared_ptr; using pbft_checkpoint_state_ptr = std::shared_ptr; + using validation_state_ptr = std::shared_ptr; struct by_block_id; struct by_num; @@ -414,6 +424,28 @@ namespace eosio { > > pbft_checkpoint_state_multi_index_type; + struct by_block_id; + struct by_status_and_num; + typedef multi_index_container< + validation_state_ptr, + indexed_by< + hashed_unique < + tag, + member, + std::hash + >, + ordered_non_unique< + tag, + composite_key< + validation_state, + member, + member + >, + composite_key_compare< greater<>, greater<> > + > + > + > local_state_multi_index_type; + class pbft_database { public: explicit pbft_database(controller& ctrl); @@ -503,6 +535,7 @@ namespace eosio { bool is_less_than_high_watermark(block_num_type bnum); bool is_valid_prepared_certificate(const pbft_prepared_certificate& certificate, bool add_to_pbft_db = false); bool is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db = false); + bool is_valid_longest_fork(const vector& producers, const block_info_type& cert_info); producer_schedule_type lscb_active_producers() const; vector& get_updated_watermarks(); @@ -538,4 +571,5 @@ FC_REFLECT(eosio::chain::pbft_stable_checkpoint, (block_info)(checkpoints)) FC_REFLECT(eosio::chain::pbft_state, (block_id)(block_num)(prepares)(is_prepared)(commits)(is_committed)) FC_REFLECT(eosio::chain::pbft_view_change_state, (view)(view_changes)(is_view_changed)) -FC_REFLECT(eosio::chain::pbft_checkpoint_state, (block_id)(block_num)(checkpoints)(is_stable)) \ No newline at end of file +FC_REFLECT(eosio::chain::pbft_checkpoint_state, (block_id)(block_num)(checkpoints)(is_stable)) +FC_REFLECT(eosio::chain::validation_state, (block_id)(block_num)(producers)(enough)) \ No newline at end of file diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index de91e792713..e30d3f7e060 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -818,7 +818,7 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map prepare_count; - flat_map>> prepare_msg; + flat_map> prepare_msg; for (const auto& pm: prepares_metadata) { if (prepare_count.find(pm.msg.view) == prepare_count.end()) { @@ -831,13 +831,13 @@ namespace eosio { for (const auto& pm: prepares_metadata) { if (bp.block_signing_key == pm.sender_key) { prepare_count[pm.msg.view] += 1; - prepare_msg[pm.msg.view].emplace_back(pm); + prepare_msg[pm.msg.view].emplace_back(std::make_pair(pm.sender_key, pm.msg.block_info)); } } } auto should_prepared = false; - auto valid_prepares = vector>{}; + auto valid_prepares = vector{}; valid_prepares.reserve(bp_threshold); for (const auto& e: prepare_count) { if (e.second >= bp_threshold) { @@ -846,77 +846,7 @@ namespace eosio { } } - if (!should_prepared) return false; - - auto local_index = pbft_state_multi_index_type{}; - for (auto &p: valid_prepares) { - auto& by_block_id_index = local_index.get(); - - auto current = ctrl.fetch_block_state_by_id(p.msg.block_info.block_id); - - while ((current) && (current->block_num > ctrl.last_stable_checkpoint_block_num())) { - auto curr_itr = by_block_id_index.find(current->id); - - if (curr_itr == by_block_id_index.end()) { - try { - flat_map, pbft_prepare> local_prepares; - local_prepares[std::make_pair(p.msg.view, p.sender_key)] = p.msg; - pbft_state curr_ps; - curr_ps.block_id = current->id; - curr_ps.block_num = current->block_num; - curr_ps.prepares = local_prepares; - auto curr_psp = std::make_shared(move(curr_ps)); - local_index.insert(curr_psp); - } catch (...) { - elog("prepare insert failure: ${p}", ("p", p.msg)); - } - } else { - auto local_prepares = (*curr_itr)->prepares; - if (local_prepares.find(std::make_pair(p.msg.view, p.sender_key)) == local_prepares.end()) { - by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { - psp->prepares[std::make_pair(p.msg.view, p.sender_key)] = p.msg; - }); - } - } - curr_itr = by_block_id_index.find(current->id); - if (curr_itr != by_block_id_index.end()) { - - auto cpsp = *curr_itr; - auto local_prepares = cpsp->prepares; - auto as = current->active_schedule.producers; - auto threshold = as.size() * 2 / 3 + 1; - if (local_prepares.size() >= threshold && !cpsp->is_prepared && - is_less_than_high_watermark(cpsp->block_num)) { - flat_map local_prepare_count; - for (const auto &pre: local_prepares) { - if (local_prepare_count.find(pre.second.view) == local_prepare_count.end()) - local_prepare_count[pre.second.view] = 0; - } - - for (const auto &bp: as) { - for (const auto &pp: local_prepares) { - if (bp.block_signing_key == pp.first.second) local_prepare_count[pp.first.first] += 1; - } - } - for (const auto &e: local_prepare_count) { - if (e.second >= threshold) { - by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr& p) { p->is_prepared = true; }); - } - } - } - current = ctrl.fetch_block_state_by_id(current->prev()); - } - } - } - - const auto& by_prepare_and_num_index = local_index.get(); - auto itr = by_prepare_and_num_index.begin(); - if (itr == by_prepare_and_num_index.end()) return false; - - pbft_state_ptr psp = *itr; - - return psp->is_prepared - && psp->block_id == certificate.block_info.block_id; + return should_prepared && is_valid_longest_fork(valid_prepares, certificate.block_info); } bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db) { @@ -944,7 +874,7 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map commit_count; - flat_map>> commit_msg; + flat_map> commit_msg; for (const auto& cm: commits_metadata) { if (commit_count.find(cm.msg.view) == commit_count.end()) { @@ -957,13 +887,13 @@ namespace eosio { for (const auto& cm: commits_metadata) { if (bp.block_signing_key == cm.sender_key) { commit_count[cm.msg.view] += 1; - commit_msg[cm.msg.view].emplace_back(cm); + commit_msg[cm.msg.view].emplace_back(std::make_pair(cm.sender_key, cm.msg.block_info)); } } } auto should_committed = false; - auto valid_commits = vector>{}; + auto valid_commits = vector{}; valid_commits.reserve(bp_threshold); for (const auto& e: commit_count) { if (e.second >= bp_threshold) { @@ -972,37 +902,39 @@ namespace eosio { } } - if (!should_committed) return false; + return should_committed && is_valid_longest_fork(valid_commits, certificate.block_info); + } - auto local_index = pbft_state_multi_index_type{}; - for (auto &c: valid_commits) { - auto& by_block_id_index = local_index.get(); + bool pbft_database::is_valid_longest_fork(const vector& block_infos, const block_info_type& cert_info) { - auto current = ctrl.fetch_block_state_by_id(c.msg.block_info.block_id); + //add all valid block_infos in to a temp multi_index, this implementation which might contains heavier computation + auto local_index = local_state_multi_index_type(); + for (auto &e: block_infos) { - while ((current) && (current->block_num > ctrl.last_stable_checkpoint_block_num())) { + auto& by_block_id_index = local_index.get(); + auto current = ctrl.fetch_block_state_by_id(e.second.block_id); + + while ( current ) { auto curr_itr = by_block_id_index.find(current->id); if (curr_itr == by_block_id_index.end()) { try { - flat_map, pbft_commit> local_commits; - local_commits[std::make_pair(c.msg.view, c.sender_key)] = c.msg; - pbft_state curr_ps; + vector bis; + bis.reserve(block_infos.size()); + bis.emplace_back(e.first); + validation_state curr_ps; curr_ps.block_id = current->id; curr_ps.block_num = current->block_num; - curr_ps.commits = local_commits; - auto curr_psp = std::make_shared(move(curr_ps)); + curr_ps.producers = bis; + auto curr_psp = std::make_shared(move(curr_ps)); local_index.insert(curr_psp); - } catch (...) { - elog("commit insertion failure: ${c}", ("c", c.msg)); - } + } catch (...) {} } else { - auto local_commits = (*curr_itr)->commits; - if (local_commits.find(std::make_pair(c.msg.view, c.sender_key)) == local_commits.end()) { - by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr &psp) { - psp->commits[std::make_pair(c.msg.view, c.sender_key)] = c.msg; - std::sort(psp->commits.begin(), psp->commits.end(), less<>()); + auto keys = (*curr_itr)->producers; + if (std::find(keys.begin(), keys.end(),e.first) == keys.end()) { + by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &lsp) { + lsp->producers.emplace_back(e.first); }); } } @@ -1014,40 +946,31 @@ namespace eosio { auto as = current->active_schedule.producers; auto threshold = as.size() * 2 / 3 + 1; - auto local_commits = cpsp->commits; - if (local_commits.size() >= threshold && !cpsp->is_committed && - is_less_than_high_watermark(cpsp->block_num)) { - flat_map local_commit_count; - for (const auto &com: local_commits) { - if (local_commit_count.find(com.second.view) == local_commit_count.end()) - local_commit_count[com.second.view] = 0; - } + auto keys = cpsp->producers; + if (keys.size() >= threshold && !cpsp->enough ) { + uint32_t count = 0; for (const auto &bp: as) { - for (const auto &pc: local_commits) { - if (bp.block_signing_key == pc.first.second) local_commit_count[pc.first.first] += 1; + for (const auto &k: keys) { + if (bp.block_signing_key == k) count += 1; } } - for (const auto &e: local_commit_count) { - if (e.second >= threshold) { - by_block_id_index.modify(curr_itr, [&](const pbft_state_ptr& p) { p->is_committed = true; }); - } + if (count >= threshold) { + by_block_id_index.modify(curr_itr, [&](const validation_state_ptr& p) { p->enough = true; }); } } - current = ctrl.fetch_block_state_by_id(current->prev()); } + current = ctrl.fetch_block_state_by_id(current->prev()); } - } - const auto& by_commit_and_num_index = local_index.get(); - auto itr = by_commit_and_num_index.begin(); - if (itr == by_commit_and_num_index.end()) return false; + const auto& by_status_and_num_index = local_index.get(); + auto itr = by_status_and_num_index.begin(); + if (itr == by_status_and_num_index.end()) return false; - pbft_state_ptr psp = *itr; + auto psp = *itr; - return psp->is_committed - && psp->block_id == certificate.block_info.block_id; + return psp->enough && psp->block_id == cert_info.block_id; } bool pbft_database::is_valid_view_change(const pbft_view_change& vc, const public_key_type& pk) { From b1ea9fd9875444aa98428f61fe32a16677807862 Mon Sep 17 00:00:00 2001 From: oldcold Date: Fri, 20 Sep 2019 11:45:59 +0800 Subject: [PATCH 06/15] clean up longest fork validation --- libraries/chain/pbft_database.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index e30d3f7e060..9e8466f4433 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -907,11 +907,11 @@ namespace eosio { bool pbft_database::is_valid_longest_fork(const vector& block_infos, const block_info_type& cert_info) { - //add all valid block_infos in to a temp multi_index, this implementation which might contains heavier computation + //add all valid block_infos in to a temp multi_index, this implementation might contains heavier computation auto local_index = local_state_multi_index_type(); + auto& by_block_id_index = local_index.get(); for (auto &e: block_infos) { - auto& by_block_id_index = local_index.get(); auto current = ctrl.fetch_block_state_by_id(e.second.block_id); while ( current ) { @@ -920,21 +920,21 @@ namespace eosio { if (curr_itr == by_block_id_index.end()) { try { - vector bis; - bis.reserve(block_infos.size()); - bis.emplace_back(e.first); + vector keys; + keys.reserve(block_infos.size()); + keys.emplace_back(e.first); validation_state curr_ps; curr_ps.block_id = current->id; curr_ps.block_num = current->block_num; - curr_ps.producers = bis; + curr_ps.producers = keys; auto curr_psp = std::make_shared(move(curr_ps)); local_index.insert(curr_psp); } catch (...) {} } else { auto keys = (*curr_itr)->producers; if (std::find(keys.begin(), keys.end(),e.first) == keys.end()) { - by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &lsp) { - lsp->producers.emplace_back(e.first); + by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &vsp) { + vsp->producers.emplace_back(e.first); }); } } @@ -968,7 +968,7 @@ namespace eosio { auto itr = by_status_and_num_index.begin(); if (itr == by_status_and_num_index.end()) return false; - auto psp = *itr; + validation_state_ptr psp = *itr; return psp->enough && psp->block_id == cert_info.block_id; } From 783ceb75653f67c0cb8313fd3cfb0f93578766ac Mon Sep 17 00:00:00 2001 From: oldcold Date: Fri, 20 Sep 2019 18:46:36 +0800 Subject: [PATCH 07/15] add watermark check --- libraries/chain/pbft_database.cpp | 75 ++++++++++++++++--------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 9e8466f4433..e48c3195a9e 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -910,54 +910,57 @@ namespace eosio { //add all valid block_infos in to a temp multi_index, this implementation might contains heavier computation auto local_index = local_state_multi_index_type(); auto& by_block_id_index = local_index.get(); + auto watermark = get_current_pbft_watermark(); for (auto &e: block_infos) { auto current = ctrl.fetch_block_state_by_id(e.second.block_id); while ( current ) { - - auto curr_itr = by_block_id_index.find(current->id); - - if (curr_itr == by_block_id_index.end()) { - try { - vector keys; - keys.reserve(block_infos.size()); - keys.emplace_back(e.first); - validation_state curr_ps; - curr_ps.block_id = current->id; - curr_ps.block_num = current->block_num; - curr_ps.producers = keys; - auto curr_psp = std::make_shared(move(curr_ps)); - local_index.insert(curr_psp); - } catch (...) {} - } else { - auto keys = (*curr_itr)->producers; - if (std::find(keys.begin(), keys.end(),e.first) == keys.end()) { - by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &vsp) { - vsp->producers.emplace_back(e.first); - }); + if (watermark == 0 || current->block_num <= watermark) { + auto curr_itr = by_block_id_index.find(current->id); + + if (curr_itr == by_block_id_index.end()) { + try { + vector keys; + keys.reserve(block_infos.size()); + keys.emplace_back(e.first); + validation_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.producers = keys; + auto curr_psp = std::make_shared(move(curr_ps)); + local_index.insert(curr_psp); + } catch (...) {} + } else { + auto keys = (*curr_itr)->producers; + if (std::find(keys.begin(), keys.end(), e.first) == keys.end()) { + by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &vsp) { + vsp->producers.emplace_back(e.first); + }); + } } - } - curr_itr = by_block_id_index.find(current->id); - if (curr_itr != by_block_id_index.end()) { + curr_itr = by_block_id_index.find(current->id); + if (curr_itr != by_block_id_index.end()) { - auto cpsp = *curr_itr; + auto cpsp = *curr_itr; - auto as = current->active_schedule.producers; - auto threshold = as.size() * 2 / 3 + 1; - auto keys = cpsp->producers; - if (keys.size() >= threshold && !cpsp->enough ) { - uint32_t count = 0; + auto as = current->active_schedule.producers; + auto threshold = as.size() * 2 / 3 + 1; + auto keys = cpsp->producers; + if (keys.size() >= threshold && !cpsp->enough) { + uint32_t count = 0; - for (const auto &bp: as) { - for (const auto &k: keys) { - if (bp.block_signing_key == k) count += 1; + for (const auto &bp: as) { + for (const auto &k: keys) { + if (bp.block_signing_key == k) count += 1; + } } - } - if (count >= threshold) { - by_block_id_index.modify(curr_itr, [&](const validation_state_ptr& p) { p->enough = true; }); + if (count >= threshold) { + by_block_id_index.modify(curr_itr, + [&](const validation_state_ptr &p) { p->enough = true; }); + } } } } From 144b4a9f0f6a91791aeb92f5912cac6449835383 Mon Sep 17 00:00:00 2001 From: oldcold Date: Sun, 22 Sep 2019 16:30:51 +0800 Subject: [PATCH 08/15] Fix pbft certifications (#127) * fix pbft cert generation and validation * attempt to fix bad prepared cert * fix longest fork threshold * redo cert validation * refactor validation, add comments * clean up longest fork validation * add watermark check --- .../include/eosio/chain/pbft_database.hpp | 40 ++- libraries/chain/pbft_database.cpp | 237 ++++++++---------- 2 files changed, 145 insertions(+), 132 deletions(-) diff --git a/libraries/chain/include/eosio/chain/pbft_database.hpp b/libraries/chain/include/eosio/chain/pbft_database.hpp index 6b4bb2cd7a9..3bc63bc3737 100644 --- a/libraries/chain/include/eosio/chain/pbft_database.hpp +++ b/libraries/chain/include/eosio/chain/pbft_database.hpp @@ -332,9 +332,19 @@ namespace eosio { bool is_stable = false; }; + using producer_and_block_info = std::pair; + + struct validation_state { + block_id_type block_id; + block_num_type block_num = 0; + vector producers; + bool enough = false; + }; + using pbft_state_ptr = std::shared_ptr; using pbft_view_change_state_ptr = std::shared_ptr; using pbft_checkpoint_state_ptr = std::shared_ptr; + using validation_state_ptr = std::shared_ptr; struct by_block_id; struct by_num; @@ -414,6 +424,28 @@ namespace eosio { > > pbft_checkpoint_state_multi_index_type; + struct by_block_id; + struct by_status_and_num; + typedef multi_index_container< + validation_state_ptr, + indexed_by< + hashed_unique < + tag, + member, + std::hash + >, + ordered_non_unique< + tag, + composite_key< + validation_state, + member, + member + >, + composite_key_compare< greater<>, greater<> > + > + > + > local_state_multi_index_type; + class pbft_database { public: explicit pbft_database(controller& ctrl); @@ -503,16 +535,13 @@ namespace eosio { bool is_less_than_high_watermark(block_num_type bnum); bool is_valid_prepared_certificate(const pbft_prepared_certificate& certificate, bool add_to_pbft_db = false); bool is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db = false); - bool is_valid_longest_fork(const block_info_type& bi, fork_info_type& block_infos, unsigned long threshold, unsigned long non_fork_bp_count); + bool is_valid_longest_fork(const vector& producers, const block_info_type& cert_info); producer_schedule_type lscb_active_producers() const; vector& get_updated_watermarks(); flat_map& get_updated_fork_schedules(); block_num_type get_current_pbft_watermark(); - vector fetch_fork_from(fork_info_type& block_infos); - fork_info_type fetch_first_fork_from(fork_info_type& bi); - void set(const pbft_state_ptr& s); void set(const pbft_checkpoint_state_ptr& s); void prune(const pbft_state_ptr& h); @@ -542,4 +571,5 @@ FC_REFLECT(eosio::chain::pbft_stable_checkpoint, (block_info)(checkpoints)) FC_REFLECT(eosio::chain::pbft_state, (block_id)(block_num)(prepares)(is_prepared)(commits)(is_committed)) FC_REFLECT(eosio::chain::pbft_view_change_state, (view)(view_changes)(is_view_changed)) -FC_REFLECT(eosio::chain::pbft_checkpoint_state, (block_id)(block_num)(checkpoints)(is_stable)) \ No newline at end of file +FC_REFLECT(eosio::chain::pbft_checkpoint_state, (block_id)(block_num)(checkpoints)(is_stable)) +FC_REFLECT(eosio::chain::validation_state, (block_id)(block_num)(producers)(enough)) \ No newline at end of file diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 84c7a70bcca..e48c3195a9e 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -650,12 +650,14 @@ namespace eosio { for (const auto& pre: prepares) { if (prepare_count.find(pre.first.first) == prepare_count.end()) prepare_count[pre.first.first] = 0; - prepare_msg[pre.first.first].emplace_back(pre.second); } for (const auto& bp: as) { for (const auto& pp: prepares) { - if (bp.block_signing_key == pp.first.second) prepare_count[pp.first.first] += 1; + if (bp.block_signing_key == pp.first.second) { + prepare_count[pp.first.first] += 1; + prepare_msg[pp.first.first].emplace_back(pp.second); + } } } @@ -737,12 +739,14 @@ namespace eosio { for (const auto& com: commits) { if (commit_count.find(com.first.first) == commit_count.end()) commit_count[com.first.first] = 0; - commit_msg[com.first.first].emplace_back(com.second); } for (const auto& bp: as) { for (const auto& cc: commits) { - if (bp.block_signing_key == cc.first.second) commit_count[cc.first.first] += 1; + if (bp.block_signing_key == cc.first.second) { + commit_count[cc.first.first] += 1; + commit_msg[cc.first.first].emplace_back(cc.second); + } } } @@ -773,11 +777,15 @@ namespace eosio { auto pvs = *itr; + auto lscb_bps = lscb_active_producers().producers; + if (pvs->is_view_changed) { pvcc.target_view=pvs->view; pvcc.view_changes.reserve(pvs->view_changes.size()); - for(auto& view_change : pvs->view_changes) { - pvcc.view_changes.emplace_back( view_change.second ); + for (auto& bp : lscb_bps) { + for(auto& view_change : pvs->view_changes) { + if (bp.block_signing_key == view_change.first) pvcc.view_changes.emplace_back(view_change.second); + } } } return pvcc; @@ -798,8 +806,7 @@ namespace eosio { for (auto& p : prepares) { auto pmm = pbft_message_metadata(p, chain_id); prepares_metadata.emplace_back(pmm); - if (!is_valid_prepare(p, pmm.sender_key)) return false; - if (add_to_pbft_db) add_pbft_prepare(p, pmm.sender_key); + if ( add_to_pbft_db && is_valid_prepare(p, pmm.sender_key) ) add_pbft_prepare(p, pmm.sender_key); } auto cert_id = certificate.block_info.block_id; @@ -811,41 +818,35 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map prepare_count; + flat_map> prepare_msg; for (const auto& pm: prepares_metadata) { - if (prepare_count.find(pm.msg.view) == prepare_count.end()) prepare_count[pm.msg.view] = 0; + if (prepare_count.find(pm.msg.view) == prepare_count.end()) { + prepare_count[pm.msg.view] = 0; + prepare_msg[pm.msg.view].reserve(bp_threshold); + } } for (const auto& bp: producer_schedule.producers) { for (const auto& pm: prepares_metadata) { - if (bp.block_signing_key == pm.sender_key) prepare_count[pm.msg.view] += 1; + if (bp.block_signing_key == pm.sender_key) { + prepare_count[pm.msg.view] += 1; + prepare_msg[pm.msg.view].emplace_back(std::make_pair(pm.sender_key, pm.msg.block_info)); + } } } auto should_prepared = false; - + auto valid_prepares = vector{}; + valid_prepares.reserve(bp_threshold); for (const auto& e: prepare_count) { if (e.second >= bp_threshold) { should_prepared = true; + valid_prepares = prepare_msg[e.first]; } } - if (!should_prepared) return false; - - //validate prepare - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - auto non_fork_bp_count = 0; - fork_info_type prepare_infos; - prepare_infos.reserve(certificate.prepares.size()); - for (const auto& p : certificate.prepares) { - //only search in fork db - if (p.block_info.block_num() <= lscb_num) { - ++non_fork_bp_count; - } else { - prepare_infos.emplace_back(p.block_info); - } - } - return is_valid_longest_fork(certificate.block_info, prepare_infos, bp_threshold, non_fork_bp_count); + return should_prepared && is_valid_longest_fork(valid_prepares, certificate.block_info); } bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db) { @@ -861,8 +862,7 @@ namespace eosio { for (auto& c : commits) { auto pmm = pbft_message_metadata(c, chain_id); commits_metadata.emplace_back(pmm); - if (!is_valid_commit(c, pmm.sender_key)) return false; - if (add_to_pbft_db) add_pbft_commit(c, pmm.sender_key); + if (add_to_pbft_db && is_valid_commit(c, pmm.sender_key)) add_pbft_commit(c, pmm.sender_key); } auto cert_id = certificate.block_info.block_id; @@ -874,41 +874,106 @@ namespace eosio { auto bp_threshold = producer_schedule.producers.size() * 2 / 3 + 1; flat_map commit_count; + flat_map> commit_msg; for (const auto& cm: commits_metadata) { - if (commit_count.find(cm.msg.view) == commit_count.end()) commit_count[cm.msg.view] = 0; + if (commit_count.find(cm.msg.view) == commit_count.end()) { + commit_count[cm.msg.view] = 0; + commit_msg[cm.msg.view].reserve(bp_threshold); + } } for (const auto& bp: producer_schedule.producers) { for (const auto& cm: commits_metadata) { - if (bp.block_signing_key == cm.sender_key) commit_count[cm.msg.view] += 1; + if (bp.block_signing_key == cm.sender_key) { + commit_count[cm.msg.view] += 1; + commit_msg[cm.msg.view].emplace_back(std::make_pair(cm.sender_key, cm.msg.block_info)); + } } } auto should_committed = false; - + auto valid_commits = vector{}; + valid_commits.reserve(bp_threshold); for (const auto& e: commit_count) { if (e.second >= bp_threshold) { should_committed = true; + valid_commits = commit_msg[e.first]; } } - if (!should_committed) return false; + return should_committed && is_valid_longest_fork(valid_commits, certificate.block_info); + } - //validate commit - auto lscb_num = ctrl.last_stable_checkpoint_block_num(); - auto non_fork_bp_count = 0; - fork_info_type commit_infos; - commit_infos.reserve(certificate.commits.size()); - for (const auto& c : certificate.commits) { - //only search in fork db - if (c.block_info.block_num() <= lscb_num) { - ++non_fork_bp_count; - } else { - commit_infos.emplace_back(c.block_info); + bool pbft_database::is_valid_longest_fork(const vector& block_infos, const block_info_type& cert_info) { + + //add all valid block_infos in to a temp multi_index, this implementation might contains heavier computation + auto local_index = local_state_multi_index_type(); + auto& by_block_id_index = local_index.get(); + auto watermark = get_current_pbft_watermark(); + for (auto &e: block_infos) { + + auto current = ctrl.fetch_block_state_by_id(e.second.block_id); + + while ( current ) { + if (watermark == 0 || current->block_num <= watermark) { + auto curr_itr = by_block_id_index.find(current->id); + + if (curr_itr == by_block_id_index.end()) { + try { + vector keys; + keys.reserve(block_infos.size()); + keys.emplace_back(e.first); + validation_state curr_ps; + curr_ps.block_id = current->id; + curr_ps.block_num = current->block_num; + curr_ps.producers = keys; + auto curr_psp = std::make_shared(move(curr_ps)); + local_index.insert(curr_psp); + } catch (...) {} + } else { + auto keys = (*curr_itr)->producers; + if (std::find(keys.begin(), keys.end(), e.first) == keys.end()) { + by_block_id_index.modify(curr_itr, [&](const validation_state_ptr &vsp) { + vsp->producers.emplace_back(e.first); + }); + } + } + + curr_itr = by_block_id_index.find(current->id); + if (curr_itr != by_block_id_index.end()) { + + auto cpsp = *curr_itr; + + auto as = current->active_schedule.producers; + auto threshold = as.size() * 2 / 3 + 1; + auto keys = cpsp->producers; + if (keys.size() >= threshold && !cpsp->enough) { + uint32_t count = 0; + + for (const auto &bp: as) { + for (const auto &k: keys) { + if (bp.block_signing_key == k) count += 1; + } + } + + if (count >= threshold) { + by_block_id_index.modify(curr_itr, + [&](const validation_state_ptr &p) { p->enough = true; }); + } + } + } + } + current = ctrl.fetch_block_state_by_id(current->prev()); } } - return is_valid_longest_fork(certificate.block_info, commit_infos, bp_threshold, non_fork_bp_count); + const auto& by_status_and_num_index = local_index.get(); + auto itr = by_status_and_num_index.begin(); + if (itr == by_status_and_num_index.end()) return false; + + validation_state_ptr psp = *itr; + + return psp->enough && psp->block_id == cert_info.block_id; } bool pbft_database::is_valid_view_change(const pbft_view_change& vc, const public_key_type& pk) { @@ -1022,88 +1087,6 @@ namespace eosio { return vc_lscb > 0 && lscb_num > vc_lscb; } - vector pbft_database::fetch_fork_from(fork_info_type& block_infos) { - - vector result; - if (block_infos.empty()) { - return result; - } - if (block_infos.size() == 1) { - result.emplace_back(initializer_list{block_infos.front()}); - return result; - } - - sort(block_infos.begin(), block_infos.end(), - [](const block_info_type& a, const block_info_type& b) -> bool { return a.block_num() > b.block_num(); }); - - while (!block_infos.empty()) { - auto fork = fetch_first_fork_from(block_infos); - if (!fork.empty()) { - result.emplace_back(fork); - } - } - return result; - } - - fork_info_type pbft_database::fetch_first_fork_from(fork_info_type& bi) { - fork_info_type result; - if (bi.empty()) { - return result; - } - if (bi.size() == 1) { - result.emplace_back(bi.front()); - bi.clear(); - return result; - } - //bi should be sorted desc - auto high = bi.front().block_num(); - auto low = bi.back().block_num(); - - auto id = bi.front().block_id; - auto num = bi.front().block_num(); - while (num <= high && num >= low && !bi.empty()) { - auto bs = ctrl.fetch_block_state_by_id(id); - - for (auto it = bi.begin(); it != bi.end();) { - if (it->block_id == id) { - if (bs) { - //add to result only if b exist - result.emplace_back((*it)); - } - it = bi.erase(it); - } else { - it++; - } - } - if (bs) { - id = bs->prev(); - num--; - } else { - break; - } - } - - return result; - } - - bool pbft_database::is_valid_longest_fork( - const block_info_type& bi, - fork_info_type& block_infos, - unsigned long threshold, - unsigned long non_fork_bp_count) { - - auto forks = fetch_fork_from(block_infos); - fork_info_type longest_fork; - for (const auto& f : forks) { - if (f.size() > longest_fork.size()) { - longest_fork = f; - } - } - - return longest_fork.empty() - || (longest_fork.size() + non_fork_bp_count >= threshold && bi.block_id == longest_fork.back().block_id); - } - pbft_stable_checkpoint pbft_database::fetch_stable_checkpoint_from_blk_extn(const signed_block_ptr& b) { pbft_stable_checkpoint psc; From 9a0957691c51d8f6805cde544db1499a3fb53bfe Mon Sep 17 00:00:00 2001 From: thaipandada Date: Sun, 22 Sep 2019 17:25:05 +0800 Subject: [PATCH 09/15] prepare 3.0.3-rc1 --- CMakeLists.txt | 2 +- Docker/README.md | 4 ++-- README.md | 2 +- README_CN.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2276554450b..2039df3546f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ set( CXX_STANDARD_REQUIRED ON) set(VERSION_MAJOR 3) set(VERSION_MINOR 0) -set(VERSION_PATCH 2) +set(VERSION_PATCH 3) if(VERSION_SUFFIX) set(VERSION_FULL "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}-${VERSION_SUFFIX}") diff --git a/Docker/README.md b/Docker/README.md index c06694deffb..d5927cd13e1 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -20,10 +20,10 @@ cd bos/Docker docker build . -t boscore/bos -s BOS ``` -The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v3.0.2 tag, you could do the following: +The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v3.0.3-rc1 tag, you could do the following: ```bash -docker build -t boscore/bos:v3.0.2 --build-arg branch=v3.0.2 . +docker build -t boscore/bos:v3.0.3-rc1 --build-arg branch=v3.0.3-rc1 . ``` diff --git a/README.md b/README.md index 334f7cad8e0..d0ef2cc4ce8 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BOSCore - Born for DApps. Born for Usability. -## BOSCore Version: v3.0.2 +## BOSCore Version: v3.0.3-rc1 ### Basic EOSIO Version: v1.6.6 (support REX) # Background diff --git a/README_CN.md b/README_CN.md index f7b893a12f7..8f637894f3a 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,6 +1,6 @@ # BOSCore - 更可用的链,为DApp而生。 -## BOSCore Version: v3.0.2 +## BOSCore Version: v3.0.3-rc1 ### Basic EOSIO Version: v1.6.6 (support REX) # 背景 From fbee0348c05872cb943b1e56dfa410280cd3b7c2 Mon Sep 17 00:00:00 2001 From: oldcold Date: Mon, 23 Sep 2019 19:45:19 +0800 Subject: [PATCH 10/15] further fix on commit certs generation and validation --- libraries/chain/pbft_database.cpp | 82 ++++++++++++++++++++++++++----- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index e48c3195a9e..3f1c947bbf4 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -597,21 +597,41 @@ namespace eosio { auto highest_pcc = vector{}; auto highest_sc = pbft_stable_checkpoint(); + vector ccb; + auto watermarks = get_updated_watermarks(); + for (auto& watermark : watermarks) { + //collecting committed cert on every water mark. + if (watermark > ctrl.last_stable_checkpoint_block_num()) { + ccb.emplace_back(watermark); + } + } + + block_num_type highest_lib = 0; + pbft_committed_certificate highest_lib_pcc; for (const auto& vc: vcc.view_changes) { if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num()) { highest_ppc = vc.prepared_cert; } for (const auto& cc: vc.committed_certs) { + if (cc.block_info.block_num() > highest_lib) { + highest_lib_pcc = cc; + } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), - [&](const pbft_committed_certificate& ext) { return ext.block_info.block_id == cc.block_info.block_id; }); - if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); + [&](const pbft_committed_certificate& ext) { + return ext.block_info.block_id == cc.block_info.block_id; + }); + if (p_itr == highest_pcc.end() //unique block num + && std::find(watermarks.begin(), watermarks.end(), cc.block_info.block_num()) != watermarks.end()) { // and it is a watermark + highest_pcc.emplace_back(cc); + } } if (vc.stable_checkpoint.block_info.block_num() > highest_sc.block_info.block_num()) { highest_sc = vc.stable_checkpoint; } } + highest_pcc.emplace_back(highest_lib_pcc); nv.new_view = new_view; nv.prepared_cert = highest_ppc; @@ -796,8 +816,8 @@ namespace eosio { bool pbft_database::is_valid_prepared_certificate(const pbft_prepared_certificate& certificate, bool add_to_pbft_db) { // an empty certificate is valid since it acts as a null digest in pbft. if (certificate.empty()) return true; - // a certificate under lscb (no longer in fork_db) is also treated as null. - if (certificate.block_info.block_num() <= ctrl.last_stable_checkpoint_block_num()) return true; + // a certificate under lib is also treated as null. + if (certificate.block_info.block_num() <= ctrl.last_irreversible_block_num()) return true; auto prepares = certificate.prepares; auto prepares_metadata = vector>{}; @@ -910,13 +930,24 @@ namespace eosio { //add all valid block_infos in to a temp multi_index, this implementation might contains heavier computation auto local_index = local_state_multi_index_type(); auto& by_block_id_index = local_index.get(); - auto watermark = get_current_pbft_watermark(); + + vector ccb; + auto past_watermarks = get_updated_watermarks(); + for (auto& w : past_watermarks) { + //adding committed cert on every water mark. + if (w < ctrl.last_irreversible_block_num()) { + ccb.emplace_back(w); + } + } + + auto next_watermark = get_current_pbft_watermark(); + for (auto &e: block_infos) { auto current = ctrl.fetch_block_state_by_id(e.second.block_id); while ( current ) { - if (watermark == 0 || current->block_num <= watermark) { + if (next_watermark == 0 || current->block_num <= next_watermark) { auto curr_itr = by_block_id_index.find(current->id); if (curr_itr == by_block_id_index.end()) { @@ -959,7 +990,7 @@ namespace eosio { if (count >= threshold) { by_block_id_index.modify(curr_itr, - [&](const validation_state_ptr &p) { p->enough = true; }); + [&](const validation_state_ptr &p) { p->enough = true; }); } } } @@ -967,13 +998,20 @@ namespace eosio { current = ctrl.fetch_block_state_by_id(current->prev()); } } - const auto& by_status_and_num_index = local_index.get(); - auto itr = by_status_and_num_index.begin(); - if (itr == by_status_and_num_index.end()) return false; - validation_state_ptr psp = *itr; + if (std::find(ccb.begin(), ccb.end(), cert_info.block_num()) != ccb.end()) { + auto itr = by_block_id_index.find(cert_info.block_id); + if (itr == by_block_id_index.end()) return false; + return (*itr)->enough; + + } else { + const auto &by_status_and_num_index = local_index.get(); + auto itr = by_status_and_num_index.begin(); + if (itr == by_status_and_num_index.end()) return false; - return psp->enough && psp->block_id == cert_info.block_id; + validation_state_ptr psp = *itr; + return psp->enough && psp->block_id == cert_info.block_id; + } } bool pbft_database::is_valid_view_change(const pbft_view_change& vc, const public_key_type& pk) { @@ -1041,6 +1079,17 @@ namespace eosio { auto highest_pcc = vector{}; auto highest_scp = pbft_stable_checkpoint(); + vector ccb; + auto watermarks = get_updated_watermarks(); + for (auto& watermark : watermarks) { + //collecting committed cert on every water mark. + if (watermark > ctrl.last_stable_checkpoint_block_num()) { + ccb.emplace_back(watermark); + } + } + + block_num_type highest_lib = 0; + pbft_committed_certificate highest_lib_pcc; for (const auto& vc: nv.view_changed_cert.view_changes) { if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num() && is_valid_prepared_certificate(vc.prepared_cert)) { @@ -1049,11 +1098,17 @@ namespace eosio { for (const auto& cc: vc.committed_certs) { if (is_valid_committed_certificate(cc)) { + if (cc.block_info.block_num() > highest_lib) { + highest_lib_pcc = cc; + } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), [&](const pbft_committed_certificate& ext) { return ext.block_info.block_id == cc.block_info.block_id; }); - if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); + if (p_itr == highest_pcc.end() //unique block num + && std::find(watermarks.begin(), watermarks.end(), cc.block_info.block_num()) != watermarks.end()) { // and it is a watermark + highest_pcc.emplace_back(cc); + } } } @@ -1062,6 +1117,7 @@ namespace eosio { highest_scp = vc.stable_checkpoint; } } + highest_pcc.emplace_back(highest_lib_pcc); EOS_ASSERT(highest_ppc.block_info == nv.prepared_cert.block_info, pbft_exception, "prepared certificate does not match, should be ${hppc} but ${pc} given", From 2e097505fa56580a1c1acc3fbec9517a5e43e9f2 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 25 Sep 2019 12:10:12 +0800 Subject: [PATCH 11/15] fix finding max committed certs block num --- libraries/chain/pbft_database.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 395c1acc21d..4321eaae991 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -616,6 +616,7 @@ namespace eosio { for (const auto& cc: vc.committed_certs) { if (cc.block_info.block_num() > highest_lib) { highest_lib_pcc = cc; + highest_lib = cc.block_info.block_num(); } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), [&](const pbft_committed_certificate& ext) { @@ -1101,6 +1102,7 @@ namespace eosio { if (is_valid_committed_certificate(cc)) { if (cc.block_info.block_num() > highest_lib) { highest_lib_pcc = cc; + highest_lib = cc.block_info.block_num(); } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), [&](const pbft_committed_certificate& ext) { From e73c33311eb357163957fff8a71feb7e906c4963 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 25 Sep 2019 21:20:01 +0800 Subject: [PATCH 12/15] highest committed cert block id should be strictly equal to the one from evidences --- libraries/chain/pbft_database.cpp | 71 ++++++------------------------- 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/libraries/chain/pbft_database.cpp b/libraries/chain/pbft_database.cpp index 4321eaae991..c8ebab42447 100644 --- a/libraries/chain/pbft_database.cpp +++ b/libraries/chain/pbft_database.cpp @@ -597,42 +597,21 @@ namespace eosio { auto highest_pcc = vector{}; auto highest_sc = pbft_stable_checkpoint(); - vector ccb; - auto watermarks = get_updated_watermarks(); - for (auto& watermark : watermarks) { - //collecting committed cert on every water mark. - if (watermark > ctrl.last_stable_checkpoint_block_num()) { - ccb.emplace_back(watermark); - } - } - - block_num_type highest_lib = 0; - pbft_committed_certificate highest_lib_pcc; for (const auto& vc: vcc.view_changes) { if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num()) { highest_ppc = vc.prepared_cert; } for (const auto& cc: vc.committed_certs) { - if (cc.block_info.block_num() > highest_lib) { - highest_lib_pcc = cc; - highest_lib = cc.block_info.block_num(); - } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), - [&](const pbft_committed_certificate& ext) { - return ext.block_info.block_id == cc.block_info.block_id; - }); - if (p_itr == highest_pcc.end() //unique block num - && std::find(watermarks.begin(), watermarks.end(), cc.block_info.block_num()) != watermarks.end()) { // and it is a watermark - highest_pcc.emplace_back(cc); - } + [&](const pbft_committed_certificate& ext) { return ext.block_info.block_id == cc.block_info.block_id; }); + if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); } if (vc.stable_checkpoint.block_info.block_num() > highest_sc.block_info.block_num()) { highest_sc = vc.stable_checkpoint; } } - highest_pcc.emplace_back(highest_lib_pcc); nv.new_view = new_view; nv.prepared_cert = highest_ppc; @@ -867,10 +846,10 @@ namespace eosio { } } - return should_prepared && is_valid_longest_fork(valid_prepares, certificate.block_info); + return should_prepared && is_valid_longest_fork(valid_prepares, certificate.block_info, true); } - bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db) { + bool pbft_database::is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db, bool at_the_top) { // an empty certificate is valid since it acts as a null digest in pbft. if (certificate.empty()) return true; // a certificate under lscb (no longer in fork_db) is also treated as null. @@ -923,24 +902,15 @@ namespace eosio { } } - return should_committed && is_valid_longest_fork(valid_commits, certificate.block_info); + return should_committed && is_valid_longest_fork(valid_commits, certificate.block_info, at_the_top); } - bool pbft_database::is_valid_longest_fork(const vector& block_infos, const block_info_type& cert_info) { + bool pbft_database::is_valid_longest_fork(const vector& block_infos, const block_info_type& cert_info, bool at_the_top ) { //add all valid block_infos in to a temp multi_index, this implementation might contains heavier computation auto local_index = local_state_multi_index_type(); auto& by_block_id_index = local_index.get(); - vector ccb; - auto past_watermarks = get_updated_watermarks(); - for (auto& w : past_watermarks) { - //adding committed cert on every water mark. - if (w < ctrl.last_irreversible_block_num()) { - ccb.emplace_back(w); - } - } - auto next_watermark = get_current_pbft_watermark(); for (auto &e: block_infos) { @@ -1001,7 +971,7 @@ namespace eosio { } } - if (std::find(ccb.begin(), ccb.end(), cert_info.block_num()) != ccb.end()) { + if (!at_the_top) { auto itr = by_block_id_index.find(cert_info.block_id); if (itr == by_block_id_index.end()) return false; return (*itr)->enough; @@ -1081,17 +1051,6 @@ namespace eosio { auto highest_pcc = vector{}; auto highest_scp = pbft_stable_checkpoint(); - vector ccb; - auto watermarks = get_updated_watermarks(); - for (auto& watermark : watermarks) { - //collecting committed cert on every water mark. - if (watermark > ctrl.last_stable_checkpoint_block_num()) { - ccb.emplace_back(watermark); - } - } - - block_num_type highest_lib = 0; - pbft_committed_certificate highest_lib_pcc; for (const auto& vc: nv.view_changed_cert.view_changes) { if (vc.prepared_cert.block_info.block_num() > highest_ppc.block_info.block_num() && is_valid_prepared_certificate(vc.prepared_cert)) { @@ -1100,18 +1059,11 @@ namespace eosio { for (const auto& cc: vc.committed_certs) { if (is_valid_committed_certificate(cc)) { - if (cc.block_info.block_num() > highest_lib) { - highest_lib_pcc = cc; - highest_lib = cc.block_info.block_num(); - } auto p_itr = find_if(highest_pcc.begin(), highest_pcc.end(), [&](const pbft_committed_certificate& ext) { return ext.block_info.block_id == cc.block_info.block_id; }); - if (p_itr == highest_pcc.end() //unique block num - && std::find(watermarks.begin(), watermarks.end(), cc.block_info.block_num()) != watermarks.end()) { // and it is a watermark - highest_pcc.emplace_back(cc); - } + if (p_itr == highest_pcc.end()) highest_pcc.emplace_back(cc); } } @@ -1120,7 +1072,6 @@ namespace eosio { highest_scp = vc.stable_checkpoint; } } - highest_pcc.emplace_back(highest_lib_pcc); EOS_ASSERT(highest_ppc.block_info == nv.prepared_cert.block_info, pbft_exception, "prepared certificate does not match, should be ${hppc} but ${pc} given", @@ -1135,6 +1086,12 @@ namespace eosio { ("hpcc", highest_pcc[i])("cc", committed_certs[i])); } + if (!committed_certs.empty()) { + EOS_ASSERT(is_valid_committed_certificate(committed_certs.back(), false, true), pbft_exception, + "highest committed certificate is invalid, ${cc}", + ("cc", committed_certs.back())); + } + EOS_ASSERT(highest_scp.block_info == nv.stable_checkpoint.block_info, pbft_exception, "stable checkpoint does not match, should be ${hscp} but ${scp} given", ("hpcc", highest_scp)("pc", nv.stable_checkpoint)); From 6c8470dc2c66d4c23c635d12d742ad3181a39419 Mon Sep 17 00:00:00 2001 From: thaipandada Date: Wed, 25 Sep 2019 21:40:24 +0800 Subject: [PATCH 13/15] merge EOSIO ##7979 --- libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp b/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp index a67ca9ef696..d7a64f809d0 100644 --- a/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp +++ b/libraries/chain/include/eosio/chain/wasm_eosio_injection.hpp @@ -761,6 +761,7 @@ namespace eosio { namespace chain { namespace wasm_injections { struct post_op_injectors : wasm_ops::op_types { using loop_t = wasm_ops::loop ; using call_t = wasm_ops::call ; + using grow_memory_t = wasm_ops::grow_memory ; }; template From 91ba180e319d57f573d3abc3a1746ecc67bf8ee6 Mon Sep 17 00:00:00 2001 From: oldcold Date: Wed, 25 Sep 2019 22:09:05 +0800 Subject: [PATCH 14/15] add missing file --- libraries/chain/include/eosio/chain/pbft_database.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/chain/include/eosio/chain/pbft_database.hpp b/libraries/chain/include/eosio/chain/pbft_database.hpp index 3bc63bc3737..fd38f9e9330 100644 --- a/libraries/chain/include/eosio/chain/pbft_database.hpp +++ b/libraries/chain/include/eosio/chain/pbft_database.hpp @@ -534,8 +534,8 @@ namespace eosio { block_info_type cal_pending_stable_checkpoint() const; bool is_less_than_high_watermark(block_num_type bnum); bool is_valid_prepared_certificate(const pbft_prepared_certificate& certificate, bool add_to_pbft_db = false); - bool is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db = false); - bool is_valid_longest_fork(const vector& producers, const block_info_type& cert_info); + bool is_valid_committed_certificate(const pbft_committed_certificate& certificate, bool add_to_pbft_db = false, bool at_the_top = false); + bool is_valid_longest_fork(const vector& producers, const block_info_type& cert_info, bool at_the_top = false); producer_schedule_type lscb_active_producers() const; vector& get_updated_watermarks(); From 4dd90055176068d4c815747d9aed022d477e3f19 Mon Sep 17 00:00:00 2001 From: thaipandada Date: Thu, 3 Oct 2019 20:50:58 +0800 Subject: [PATCH 15/15] PBFT validation fixes --- Docker/README.md | 4 ++-- README.md | 2 +- README_CN.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Docker/README.md b/Docker/README.md index d5927cd13e1..087105fed7b 100644 --- a/Docker/README.md +++ b/Docker/README.md @@ -20,10 +20,10 @@ cd bos/Docker docker build . -t boscore/bos -s BOS ``` -The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v3.0.3-rc1 tag, you could do the following: +The above will build off the most recent commit to the master branch by default. If you would like to target a specific branch/tag, you may use a build argument. For example, if you wished to generate a docker image based off of the v3.0.3 tag, you could do the following: ```bash -docker build -t boscore/bos:v3.0.3-rc1 --build-arg branch=v3.0.3-rc1 . +docker build -t boscore/bos:v3.0.3 --build-arg branch=v3.0.3 . ``` diff --git a/README.md b/README.md index d0ef2cc4ce8..cab40f08390 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # BOSCore - Born for DApps. Born for Usability. -## BOSCore Version: v3.0.3-rc1 +## BOSCore Version: v3.0.3 ### Basic EOSIO Version: v1.6.6 (support REX) # Background diff --git a/README_CN.md b/README_CN.md index 8f637894f3a..5603854c913 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,6 +1,6 @@ # BOSCore - 更可用的链,为DApp而生。 -## BOSCore Version: v3.0.3-rc1 +## BOSCore Version: v3.0.3 ### Basic EOSIO Version: v1.6.6 (support REX) # 背景