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 9 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
23 changes: 23 additions & 0 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,5 +102,28 @@ namespace eosio::chain {
,_cached_trxs( std::move(trx_metas) )
{}
#endif

// Called from net threads
bool block_state::aggregate_vote(const hs_vote_message& vote) {
heifner marked this conversation as resolved.
Show resolved Hide resolved
const auto& finalizers = 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_finalizer_digest : weak_finalizer_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()},
heifner marked this conversation as resolved.
Show resolved Hide resolved
index,
vote.finalizer_key,
vote.sig);
} else {
wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) );
return false;
}
}

} /// eosio::chain
21 changes: 19 additions & 2 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,23 @@ struct block_data_t {
}, v);
}

// called from net thread
bool aggregate_vote(const hs_vote_message& vote) {
heifner marked this conversation as resolved.
Show resolved Hide resolved
return std::visit(
overloaded{[](const block_data_legacy_t&) {
EOS_ASSERT(false, misc_exception, "attempting to call aggregate_vote in legacy mode");
return false; },
[&](const block_data_new_t& bd) {
auto bsp = bd.fork_db.get_block(vote.proposal_id);
if (bsp) {
return bsp->aggregate_vote(vote);
heifner marked this conversation as resolved.
Show resolved Hide resolved
} else {
return false;
}; }
},
v);
}
heifner marked this conversation as resolved.
Show resolved Hide resolved

template<class R, class F>
R apply(F &f) {
if constexpr (std::is_same_v<void, R>)
Expand Down Expand Up @@ -4007,8 +4024,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
38 changes: 26 additions & 12 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,8 +146,10 @@ 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);
}
Expand All @@ -157,15 +171,15 @@ std::string pending_quorum_certificate::get_votes_string() const {


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;
: _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});
_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.
}
Expand All @@ -192,4 +206,4 @@ quorum_certificate_message valid_quorum_certificate::to_msg() const {
};
}

} // namespace eosio::chain
} // namespace eosio::chain
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
5 changes: 4 additions & 1 deletion libraries/chain/include/eosio/chain/block_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ namespace eosio::chain {
// ------ data members -------------------------------------------------------------
signed_block_ptr block;
bool validated; // We have executed the block's trxs and verified that action merkle root (block id) matches.
digest_type finalizer_digest;
digest_type strong_finalizer_digest;
digest_type weak_finalizer_digest;
heifner marked this conversation as resolved.
Show resolved Hide resolved
pending_quorum_certificate pending_qc; // where we accumulate votes we receive
std::optional<valid_quorum_certificate> valid_qc; // qc received from the network

Expand All @@ -32,6 +33,8 @@ namespace eosio::chain {

protocol_feature_activation_set_ptr get_activated_protocol_features() const { return block_header_state::activated_protocol_features; }
deque<transaction_metadata_ptr> extract_trxs_metas() { return {}; }; // [greg todo] see impl in block_state_legacy.hpp

bool aggregate_vote(const hs_vote_message& vote); // aggregate vote into pending_qc
};

using block_state_ptr = std::shared_ptr<block_state>;
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ namespace eosio::chain {
void set_proposed_finalizers( const finalizer_policy& fin_set );
void get_finalizer_state( finalizer_state& fs ) const;
// called from net threads
void notify_hs_message( const uint32_t connection_id, const hs_message& msg );
bool process_vote_message( const hs_vote_message& msg );

bool light_validation_allowed() const;
bool skip_auth_check()const;
Expand Down
Loading