Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IF:Unification: Process received Vote message #2073

Merged
merged 15 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,28 @@ void block_state::set_trxs_metas( deque<transaction_metadata_ptr>&& trxs_metas,
cached_trxs = std::move( trxs_metas );
}

// Called from net threads
bool block_state::aggregate_vote(const hs_vote_message& vote) {
const auto& finalizers = active_finalizer_policy->finalizers;
auto it = std::find_if(finalizers.begin(),
finalizers.end(),
[&](const auto& finalizer) { return finalizer.public_key == vote.finalizer_key; });

if (it != finalizers.end()) {
auto index = std::distance(finalizers.begin(), it);
const digest_type& digest = vote.strong ? strong_digest : weak_digest;
return pending_qc.add_vote(vote.strong,
#warning TODO change to use std::span if possible
std::vector<uint8_t>{digest.data(), digest.data() + digest.data_size()},
index,
vote.finalizer_key,
vote.sig);
} else {
wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) );
return false;
}
}

std::optional<quorum_certificate> block_state::get_best_qc() const {
auto block_number = block_num();

Expand All @@ -69,9 +91,8 @@ std::optional<quorum_certificate> block_state::get_best_qc() const {
}
}

#warning TODO valid_quorum_certificate constructor can assert. Implement an extract method in pending_quorum_certificate for this.
// extract valid QC from pending_qc
valid_quorum_certificate valid_qc_from_pending(pending_qc);
valid_quorum_certificate valid_qc_from_pending = pending_qc.to_valid_quorum_certificate();

// if valid_qc does not have value, consider valid_qc_from_pending only
if( !valid_qc ) {
Expand Down
18 changes: 16 additions & 2 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,20 @@ struct block_data_t {
}, v);
}

// called from net thread
bool aggregate_vote(const hs_vote_message& vote) {
return std::visit(
overloaded{[](const block_data_legacy_t&) {
// We can be late in switching to Instant Finality
// and receive votes from those already having switched.
return false; },
[&](const block_data_new_t& bd) {
auto bsp = bd.fork_db.get_block(vote.proposal_id);
return bsp && bsp->aggregate_vote(vote); }
},
v);
}

signed_block_ptr fork_db_fetch_block_by_num(uint32_t block_num) const {
return std::visit([&](const auto& bd) -> signed_block_ptr {
auto bsp = bd.fork_db.search_on_branch(bd.fork_db.head()->id(), block_num);
Expand Down Expand Up @@ -4194,8 +4208,8 @@ void controller::get_finalizer_state( finalizer_state& fs ) const {
}

// called from net threads
void controller::notify_hs_message( const uint32_t connection_id, const hs_message& msg ) {
my->pacemaker->on_hs_msg(connection_id, msg);
bool controller::process_vote_message( const hs_vote_message& vote ) {
return my->block_data.aggregate_vote(vote);
};

const producer_authority_schedule& controller::active_producers()const {
Expand Down
56 changes: 38 additions & 18 deletions libraries/chain/hotstuff/hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ inline std::vector<uint32_t> bitset_to_vector(const hs_bitset& bs) {

bool pending_quorum_certificate::votes_t::add_vote(const std::vector<uint8_t>& proposal_digest, size_t index,
const bls_public_key& pubkey, const bls_signature& new_sig) {
if (_bitset[index])
if (_bitset[index]) {
return false; // shouldn't be already present
if (!fc::crypto::blslib::verify(pubkey, proposal_digest, new_sig))
}
if (!fc::crypto::blslib::verify(pubkey, proposal_digest, new_sig)) {
wlog( "signature from finalizer ${i} cannot be verified", ("i", index) );
return false;
}
_bitset.set(index);
_sig = fc::crypto::blslib::aggregate({_sig, new_sig}); // works even if _sig is default initialized (fp2::zero())
return true;
Expand All @@ -38,9 +41,14 @@ void pending_quorum_certificate::votes_t::reset(size_t num_finalizers) {
_sig = bls_signature();
}

pending_quorum_certificate::pending_quorum_certificate()
: _mtx(std::make_unique<std::mutex>()) {
}

pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, size_t quorum)
: _num_finalizers(num_finalizers)
, _quorum(quorum) {
, _quorum(quorum)
, _mtx(std::make_unique<std::mutex>()) {
_weak_votes.resize(num_finalizers);
_strong_votes.resize(num_finalizers);
}
Expand All @@ -54,11 +62,13 @@ pending_quorum_certificate::pending_quorum_certificate(const fc::sha256& propos
}

bool pending_quorum_certificate::is_quorum_met() const {
std::lock_guard g(*_mtx);
return _state == state_t::weak_achieved || _state == state_t::weak_final || _state == state_t::strong;
}

void pending_quorum_certificate::reset(const fc::sha256& proposal_id, const digest_type& proposal_digest,
size_t num_finalizers, size_t quorum) {
std::lock_guard g(*_mtx);
_proposal_id = proposal_id;
_proposal_digest.assign(proposal_digest.data(), proposal_digest.data() + 32);
_quorum = quorum;
Expand All @@ -68,6 +78,7 @@ void pending_quorum_certificate::reset(const fc::sha256& proposal_id, const dige
_state = state_t::unrestricted;
}

// called by add_vote, already protected by mutex
bool pending_quorum_certificate::add_strong_vote(const std::vector<uint8_t>& proposal_digest, size_t index,
const bls_public_key& pubkey, const bls_signature& sig) {
assert(index < _num_finalizers);
Expand Down Expand Up @@ -99,6 +110,7 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector<uint8_t>& pro
return true;
}

// called by add_vote, already protected by mutex
bool pending_quorum_certificate::add_weak_vote(const std::vector<uint8_t>& proposal_digest, size_t index,
const bls_public_key& pubkey, const bls_signature& sig) {
assert(index < _num_finalizers);
Expand Down Expand Up @@ -134,12 +146,35 @@ bool pending_quorum_certificate::add_weak_vote(const std::vector<uint8_t>& propo
return true;
}

// thread safe
bool pending_quorum_certificate::add_vote(bool strong, const std::vector<uint8_t>& proposal_digest, size_t index,
const bls_public_key& pubkey, const bls_signature& sig) {
std::lock_guard g(*_mtx);
return strong ? add_strong_vote(proposal_digest, index, pubkey, sig)
: add_weak_vote(proposal_digest, index, pubkey, sig);
}

// thread safe
valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate() const {
std::lock_guard g(*_mtx);

valid_quorum_certificate valid_qc;

valid_qc._proposal_id = _proposal_id;
valid_qc._proposal_digest = _proposal_digest;
if( _state == state_t::strong ) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._sig = _strong_votes._sig;
} else if (is_quorum_met()) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._weak_votes = _weak_votes._bitset;
valid_qc._sig = fc::crypto::blslib::aggregate({_strong_votes._sig, _weak_votes._sig});
} else
assert(0); // this should be called only when we have a valid qc.

return valid_qc;
}

// ================== begin compatibility functions =======================
// these are present just to make the tests still work. will be removed.
// these assume *only* strong votes.
Expand All @@ -155,21 +190,6 @@ std::string pending_quorum_certificate::get_votes_string() const {
}
// ================== end compatibility functions =======================


valid_quorum_certificate::valid_quorum_certificate(const pending_quorum_certificate& qc)
: _proposal_id(qc._proposal_id)
, _proposal_digest(qc._proposal_digest) {
if (qc._state == pending_quorum_certificate::state_t::strong) {
_strong_votes = qc._strong_votes._bitset;
_sig = qc._strong_votes._sig;
} else if (qc.is_quorum_met()) {
_strong_votes = qc._strong_votes._bitset;
_weak_votes = qc._weak_votes._bitset;
_sig = fc::crypto::blslib::aggregate({qc._strong_votes._sig, qc._weak_votes._sig});
} else
assert(0); // this should be called only when we have a valid qc.
}

valid_quorum_certificate::valid_quorum_certificate(
const fc::sha256& proposal_id, const std::vector<uint8_t>& proposal_digest,
const std::vector<uint32_t>& strong_votes, // bitset encoding, following canonical order
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/hotstuff/qc_chain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ namespace eosio::chain {
("id", _id));

//fc_tlog(_logger, " === update_high_qc : _current_qc ===");
update_high_qc(_current_qc);
update_high_qc(_current_qc.to_valid_quorum_certificate());
fc_dlog(_logger, " === ${id} quorum met on #${block_num} ${phase_counter} ${proposal_id} ",
("block_num", p->block_num())
("phase_counter", p->phase_counter)
Expand Down
80 changes: 56 additions & 24 deletions libraries/chain/hotstuff/test/hotstuff_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,67 +100,67 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try {
pubkey.push_back(k.get_public_key());

auto weak_vote = [&](pending_quorum_certificate& qc, const std::vector<uint8_t>& digest, size_t index) {
return qc.add_weak_vote(digest, index, pubkey[index], sk[index].sign(digest));
return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest));
};

auto strong_vote = [&](pending_quorum_certificate& qc, const std::vector<uint8_t>& digest, size_t index) {
return qc.add_strong_vote(digest, index, pubkey[index], sk[index].sign(digest));
return qc.add_vote(true, digest, index, pubkey[index], sk[index].sign(digest));
};

{
pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1
BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);

// add one weak vote
// -----------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved);
BOOST_CHECK(qc.is_quorum_met());

// add duplicate weak vote
// -----------------------
bool ok = weak_vote(qc, digest, 0);
BOOST_CHECK(!ok); // vote was a duplicate
BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved);
BOOST_CHECK(qc.is_quorum_met());

// add another weak vote
// ---------------------
weak_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::weak_final);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final);
}

{
pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1
BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);

// add a weak vote
// ---------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved);
BOOST_CHECK(qc.is_quorum_met());

// add a strong vote
// -----------------
strong_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::strong);
BOOST_CHECK_EQUAL(qc.state(), state_t::strong);
BOOST_CHECK(qc.is_quorum_met());
}

{
pending_quorum_certificate qc(2, 1); // 2 finalizers, quorum = 1
BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);

// add a strong vote
// -----------------
strong_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::strong);
BOOST_CHECK_EQUAL(qc.state(), state_t::strong);
BOOST_CHECK(qc.is_quorum_met());

// add a strong vote
// -----------------
strong_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::strong);
BOOST_CHECK_EQUAL(qc.state(), state_t::strong);
BOOST_CHECK(qc.is_quorum_met());
}

Expand All @@ -170,32 +170,48 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try {
// add a weak vote
// ---------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);
BOOST_CHECK(!qc.is_quorum_met());

// add a strong vote
// -----------------
strong_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::weak_achieved);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved);
BOOST_CHECK(qc.is_quorum_met());

{
pending_quorum_certificate qc2(qc);
pending_quorum_certificate qc2(std::move(qc));

// add a weak vote
// ---------------
weak_vote(qc2, digest, 2);
BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final);
BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final);
BOOST_CHECK(qc2.is_quorum_met());
}
}

{
pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2

// add a weak vote
// ---------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);
BOOST_CHECK(!qc.is_quorum_met());

// add a strong vote
// -----------------
strong_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_achieved);
BOOST_CHECK(qc.is_quorum_met());

{
pending_quorum_certificate qc2(qc);
pending_quorum_certificate qc2(std::move(qc));

// add a strong vote
// -----------------
strong_vote(qc2, digest, 2);
BOOST_CHECK_EQUAL(qc2._state, state_t::strong);
BOOST_CHECK_EQUAL(qc2.state(), state_t::strong);
BOOST_CHECK(qc2.is_quorum_met());
}
}
Expand All @@ -206,32 +222,48 @@ BOOST_AUTO_TEST_CASE(qc_state_transitions) try {
// add a weak vote
// ---------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc._state, state_t::unrestricted);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);
BOOST_CHECK(!qc.is_quorum_met());

// add a weak vote
// ---------------
weak_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc._state, state_t::weak_final);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final);
BOOST_CHECK(qc.is_quorum_met());

{
pending_quorum_certificate qc2(qc);
pending_quorum_certificate qc2(std::move(qc));

// add a weak vote
// ---------------
weak_vote(qc2, digest, 2);
BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final);
BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final);
BOOST_CHECK(qc2.is_quorum_met());
}
}

{
pending_quorum_certificate qc(3, 2); // 3 finalizers, quorum = 2

// add a weak vote
// ---------------
weak_vote(qc, digest, 0);
BOOST_CHECK_EQUAL(qc.state(), state_t::unrestricted);
BOOST_CHECK(!qc.is_quorum_met());

// add a weak vote
// ---------------
weak_vote(qc, digest, 1);
BOOST_CHECK_EQUAL(qc.state(), state_t::weak_final);
BOOST_CHECK(qc.is_quorum_met());

{
pending_quorum_certificate qc2(qc);
pending_quorum_certificate qc2(std::move(qc));

// add a strong vote
// -----------------
strong_vote(qc2, digest, 2);
BOOST_CHECK_EQUAL(qc2._state, state_t::weak_final);
BOOST_CHECK_EQUAL(qc2.state(), state_t::weak_final);
BOOST_CHECK(qc2.is_quorum_met());
}
}
Expand Down
Loading