Skip to content

Commit

Permalink
Merge branch 'hotstuff_integration' into sign_broadcast_votes
Browse files Browse the repository at this point in the history
  • Loading branch information
linh2931 authored Jan 17, 2024
2 parents 447c773 + 8cf39ec commit c3340f2
Show file tree
Hide file tree
Showing 10 changed files with 346 additions and 111 deletions.
55 changes: 54 additions & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,17 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con
{}

block_state::block_state(const block_header_state& bhs, deque<transaction_metadata_ptr>&& trx_metas,
deque<transaction_receipt>&& trx_receipts)
deque<transaction_receipt>&& trx_receipts, const std::optional<quorum_certificate>& qc)
: block_header_state(bhs)
, block(std::make_shared<signed_block>(signed_block_header{bhs.header})) // [greg todo] do we need signatures?
, pub_keys_recovered(true) // probably not needed
, cached_trxs(std::move(trx_metas))
{
block->transactions = std::move(trx_receipts);

if( qc && bhs.is_needed(*qc) ) {
emplace_extension(block->block_extensions, quorum_certificate_extension::extension_id(), fc::raw::pack( *qc ));
}
}

// Used for transition from dpos to instant-finality
Expand Down Expand Up @@ -53,5 +57,54 @@ 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();

// if pending_qc does not have a valid QC, consider valid_qc only
if( !pending_qc.is_quorum_met() ) {
if( valid_qc ) {
return quorum_certificate{ block_number, *valid_qc };
} else {
return std::nullopt;;
}
}

// extract valid QC from 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 ) {
return quorum_certificate{ block_number, valid_qc_from_pending };
}

// Both valid_qc and valid_qc_from_pending have value. Compare them and select a better one.
// Strong beats weak. Tie break by valid_qc.
const auto& best_qc =
valid_qc->is_strong() == valid_qc_from_pending.is_strong() ?
*valid_qc : // tie broke by valid_qc
valid_qc->is_strong() ? *valid_qc : valid_qc_from_pending; // strong beats weak
return quorum_certificate{ block_number, best_qc };
}
} /// eosio::chain
35 changes: 29 additions & 6 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 @@ -558,7 +572,7 @@ struct assembled_block {
},
[&](assembled_block_if& ab) {
auto bsp = std::make_shared<block_state>(ab.bhs, std::move(ab.trx_metas),
std::move(ab.trx_receipts));
std::move(ab.trx_receipts), ab.qc);
return completed_block{std::move(bsp)};
}},
v);
Expand Down Expand Up @@ -896,9 +910,18 @@ struct building_block {
const block_data_new_t& bd = std::get<block_data_new_t>(block_data.v);
const auto& fork_db = bd.fork_db; // fork_db<block_state_ptr, block_header_state_ptr>

// [greg todo] retrieve qc, and fill the following struct accurately
std::optional<qc_data_t> qc_data; // Comes from traversing branch from parent and calling
// get_best_qc() assert(qc->block_num <= num_from_id(previous));
// find most recent ancestor block that has a QC by traversing fork db
// branch from parent
std::optional<qc_data_t> qc_data;
auto branch = fork_db.fetch_branch(parent_id());
for( auto it = branch.begin(); it != branch.end(); ++it ) {
auto qc = (*it)->get_best_qc();
if( qc ) {
EOS_ASSERT( qc->block_height <= block_header::num_from_id(parent_id()), block_validate_exception, "most recent ancestor QC block number (${a}) cannot be greater than parent's block number (${p})", ("a", qc->block_height)("p", block_header::num_from_id(parent_id())) );
qc_data = qc_data_t{ *qc, qc_info_t{ qc->block_height, qc->qc.is_strong() }};
break;
}
}

building_block_input bb_input {
.parent_id = parent_id(),
Expand Down Expand Up @@ -4242,8 +4265,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
58 changes: 39 additions & 19 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 All @@ -192,4 +212,4 @@ quorum_certificate_message valid_quorum_certificate::to_msg() const {
};
}

} // namespace eosio::chain
} // namespace eosio::chain
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
Loading

0 comments on commit c3340f2

Please sign in to comment.