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: Fixes for IF to advance LIB #2105

Merged
merged 17 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from 10 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
14 changes: 8 additions & 6 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ block_header_state_core block_header_state_core::next(qc_info_t incoming) const
}

EOS_ASSERT(incoming.last_qc_block_num > this->last_qc_block_num, block_validate_exception,
"new last_qc_block_num must be greater than old last_qc_block_num");
"new last_qc_block_num ${new} must be greater than old last_qc_block_num ${old}",
("new", incoming.last_qc_block_num)("old", this->last_qc_block_num));

auto old_last_qc_block_num = this->last_qc_block_num;
auto old_final_on_strong_qc_block_num = this->final_on_strong_qc_block_num;
Expand Down Expand Up @@ -117,9 +118,8 @@ block_header_state block_header_state::next(block_header_state_input& input) con
result.active_finalizer_policy = active_finalizer_policy;

// [greg todo] correct support for new finalizer_policy activation using finalizer_policies map

if (input.new_finalizer_policy)
++input.new_finalizer_policy->generation;
// if (input.new_finalizer_policy)
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
// ++input.new_finalizer_policy->generation;


// add IF block header extension
Expand All @@ -131,7 +131,9 @@ block_header_state block_header_state::next(block_header_state_input& input) con
instant_finality_extension new_if_ext {if_ext.qc_info,
std::move(input.new_finalizer_policy),
std::move(input.new_proposer_policy)};
if (input.qc_info)
if (input.validating)
new_if_ext.qc_info = input.qc_info;
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
else if (input.qc_info)
new_if_ext.qc_info = *input.qc_info;

emplace_extension(result.header.header_extensions, if_ext_id, fc::raw::pack(new_if_ext));
Expand Down Expand Up @@ -196,7 +198,7 @@ block_header_state block_header_state::next(const signed_block_header& h, const

block_header_state_input bhs_input{
bb_input, h.transaction_mroot, h.action_mroot, if_ext.new_proposer_policy, if_ext.new_finalizer_policy,
if_ext.qc_info};
if_ext.qc_info, true};

return next(bhs_input);
}
Expand Down
14 changes: 11 additions & 3 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@ block_state::block_state(const block_header_state& prev, signed_block_ptr b, con
const validator_t& validator, bool skip_validate_signee)
: block_header_state(prev.next(*b, pfs, validator))
, block(std::move(b))
, strong_digest(compute_finalizer_digest())
, weak_digest(compute_finalizer_digest())
, pending_qc(prev.active_finalizer_policy->finalizers.size(), prev.active_finalizer_policy->threshold)
{}

block_state::block_state(const block_header_state& bhs, deque<transaction_metadata_ptr>&& trx_metas,
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?
, strong_digest(compute_finalizer_digest())
, weak_digest(compute_finalizer_digest())
, pending_qc(bhs.active_finalizer_policy->finalizers.size(), bhs.active_finalizer_policy->threshold)
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
, pub_keys_recovered(true) // probably not needed
, cached_trxs(std::move(trx_metas))
{
Expand All @@ -29,6 +35,7 @@ block_state::block_state(const block_header_state& bhs, deque<transaction_metada
block_state::block_state(const block_state_legacy& bsp) {
block_header_state::id = bsp.id();
header = bsp.header;
core.last_final_block_num = bsp.block_num();
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
activated_protocol_features = bsp.activated_protocol_features;
std::optional<block_header_extension> ext = bsp.block->extract_header_extension(instant_finality_extension::extension_id());
assert(ext); // required by current transition mechanism
Expand Down Expand Up @@ -58,7 +65,7 @@ void block_state::set_trxs_metas( deque<transaction_metadata_ptr>&& trxs_metas,
}

// Called from net threads
bool block_state::aggregate_vote(const hs_vote_message& vote) {
std::pair<bool, std::optional<uint32_t>> 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(),
Expand All @@ -67,15 +74,16 @@ bool block_state::aggregate_vote(const hs_vote_message& vote) {
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,
auto [valid, strong] = 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);
return {valid, strong ? core.final_on_strong_qc_block_num : std::optional<uint32_t>{}};
} else {
wlog( "finalizer_key (${k}) in vote is not in finalizer policy", ("k", vote.finalizer_key) );
return false;
return {false, {}};
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
108 changes: 83 additions & 25 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,16 +302,19 @@ struct block_data_t {
}

// called from net thread
bool aggregate_vote(const hs_vote_message& vote) {
std::pair<bool, std::optional<uint32_t>> aggregate_vote(const hs_vote_message& vote) {
return std::visit(
overloaded{[](const block_data_legacy_t&) {
overloaded{[](const block_data_legacy_t&) -> std::pair<bool, std::optional<uint32_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) {
return {false, {}}; },
[&](const block_data_new_t& bd) -> std::pair<bool, std::optional<uint32_t>> {
auto bsp = bd.fork_db.get_block(vote.proposal_id);
return bsp && bsp->aggregate_vote(vote); }
},
if (bsp) {
return bsp->aggregate_vote(vote);
}
return {false, {}};
}},
v);
}

Expand Down Expand Up @@ -857,7 +860,9 @@ struct building_block {

assembled_block assemble_block(boost::asio::io_context& ioc,
const protocol_feature_set& pfs,
const block_data_t& block_data) {
const block_data_t& block_data,
bool validating,
std::optional<qc_info_t> validating_qc_info) {
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
digests_t& action_receipts = action_receipt_digests();
return std::visit(
overloaded{
Expand Down Expand Up @@ -913,13 +918,17 @@ struct building_block {
// 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;
if (!validating) {
auto branch = fork_db.fetch_branch(parent_id());
for( auto it = branch.begin(); it != branch.end(); ++it ) {
auto qc = (*it)->get_best_qc();
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
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;
}
}
}

Expand All @@ -930,9 +939,17 @@ struct building_block {
.new_protocol_feature_activations = new_protocol_feature_activations()
};

std::optional<qc_info_t> qc_info;
if (validating) {
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
qc_info = validating_qc_info;
} else if (qc_data) {
qc_info = qc_data->qc_info;
}
block_header_state_input bhs_input{
bb_input, transaction_mroot, action_mroot, std::move(bb.new_proposer_policy), std::move(bb.new_finalizer_policy),
qc_data ? qc_data->qc_info : std::optional<qc_info_t>{} };
bb_input, transaction_mroot, action_mroot, std::move(bb.new_proposer_policy),
std::move(bb.new_finalizer_policy),
qc_info, validating
};

assembled_block::assembled_block_if ab{std::move(bb.active_producer_authority), bb.parent.next(bhs_input),
std::move(bb.pending_trx_metas), std::move(bb.pending_trx_receipts),
Expand Down Expand Up @@ -2682,7 +2699,7 @@ struct controller_impl {
guard_pending.cancel();
} /// start_block

void finish_block()
void finish_block(bool validating = false, std::optional<qc_info_t> validating_qc_info = {})
{
EOS_ASSERT( pending, block_validate_exception, "it is not valid to finalize when there is no pending block");
EOS_ASSERT( std::holds_alternative<building_block>(pending->_block_stage), block_validate_exception, "already called finish_block");
Expand All @@ -2705,8 +2722,8 @@ struct controller_impl {
);
resource_limits.process_block_usage(bb.block_num());

auto assembled_block =
bb.assemble_block(thread_pool.get_executor(), protocol_features.get_protocol_feature_set(), block_data);
auto assembled_block = bb.assemble_block(thread_pool.get_executor(),
protocol_features.get_protocol_feature_set(), block_data, validating, validating_qc_info);

// Update TaPoS table:
create_block_summary( assembled_block.id() );
Expand Down Expand Up @@ -2780,6 +2797,13 @@ struct controller_impl {
block_data.transition_fork_db_to_if(cb.bsp);
}

auto vote = [&](auto& fork_db, auto& head) {
const auto& bsp = std::get<std::decay_t<decltype(head)>>(cb.bsp);
if constexpr (std::is_same_v<block_state_ptr, typename std::decay_t<decltype(bsp)>>)
create_and_send_vote_msg(bsp);
};
block_data.apply<void>(vote);

} catch (...) {
// dont bother resetting pending, instead abort the block
reset_pending_on_exit.cancel();
Expand Down Expand Up @@ -2875,6 +2899,23 @@ struct controller_impl {
EOS_REPORT( "new_producers", b.new_producers, ab.new_producers )
EOS_REPORT( "header_extensions", b.header_extensions, ab.header_extensions )

if (b.header_extensions != ab.header_extensions) {
{
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
flat_multimap<uint16_t, block_header_extension> bheader_exts = b.validate_and_extract_header_extensions();
if (bheader_exts.count(instant_finality_extension::extension_id())) {
const auto& if_extension =
std::get<instant_finality_extension>(bheader_exts.lower_bound(instant_finality_extension::extension_id())->second);
elog("b if: ${i}", ("i", if_extension));
}
}
flat_multimap<uint16_t, block_header_extension> abheader_exts = ab.validate_and_extract_header_extensions();
if (abheader_exts.count(instant_finality_extension::extension_id())) {
const auto& if_extension =
std::get<instant_finality_extension>(abheader_exts.lower_bound(instant_finality_extension::extension_id())->second);
elog("ab if: ${i}", ("i", if_extension));
}
}

#undef EOS_REPORT
}

Expand Down Expand Up @@ -2968,7 +3009,13 @@ struct controller_impl {
("lhs", r)("rhs", static_cast<const transaction_receipt_header&>(receipt)));
}

finish_block();
std::optional<qc_info_t> qc_info;
auto exts = b->validate_and_extract_header_extensions();
if (auto if_entry = exts.lower_bound(instant_finality_extension::extension_id()); if_entry != exts.end()) {
auto& if_ext = std::get<instant_finality_extension>(if_entry->second);
qc_info = if_ext.qc_info;
}
finish_block(true, qc_info);

auto& ab = std::get<assembled_block>(pending->_block_stage);

Expand Down Expand Up @@ -3016,22 +3063,31 @@ struct controller_impl {

// A vote is created and signed by each finalizer configured on the node that
// in active finalizer policy
bool found = false;
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
// TODO: remove dlog statements
dlog( "active finalizers ${n}, threshold ${t}",
("n", bsp->active_finalizer_policy->finalizers.size())("t", bsp->active_finalizer_policy->threshold));
for (const auto& f: bsp->active_finalizer_policy->finalizers) {
auto it = node_finalizer_keys.find( f.public_key );
if( it != node_finalizer_keys.end() ) {
found = true;
dlog("finalizer used: ${f}", ("f", f.public_key.to_string()));
const auto& private_key = it->second;
const auto& digest = bsp->compute_finalizer_digest();

auto sig = private_key.sign(std::vector<uint8_t>(digest.data(), digest.data() + digest.data_size()));

// construct the vote message
hs_vote_message vote{ bsp->id(), strong, private_key.get_public_key(), sig };
hs_vote_message vote{ bsp->id(), strong, f.public_key, sig };

// net plugin subscribed this signal. it will broadcast the vote message
// on receiving the signal
emit( self.voted_block, vote );
}
}
if (!found) {
dlog("No finalizer found on node for key, we have ${n} finalizers configured", ("n", node_finalizer_keys.size()));
}
}

// thread safe, expected to be called from thread other than the main thread
Expand Down Expand Up @@ -3088,9 +3144,7 @@ struct controller_impl {
);

EOS_ASSERT( id == bsp->id(), block_validate_exception,
"provided id ${id} does not match block id ${bid}", ("id", id)("bid", bsp->id()) );

create_and_send_vote_msg(bsp);
"provided id ${id} does not match calculated block id ${bid}", ("id", id)("bid", bsp->id()) );

// integrate the received QC into the claimed block
integrate_received_qc_to_block(id, b);
Expand Down Expand Up @@ -4266,7 +4320,11 @@ void controller::get_finalizer_state( finalizer_state& fs ) const {

// called from net threads
bool controller::process_vote_message( const hs_vote_message& vote ) {
return my->block_data.aggregate_vote(vote);
auto [valid, new_lib] = my->block_data.aggregate_vote(vote);
if (new_lib) {
my->if_irreversible_block_num = *new_lib;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to call log_irreversible here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We db.commit() in log_irreversible, so don't want that called from net thread.

}
return valid;
};

const producer_authority_schedule& controller::active_producers()const {
Expand Down
14 changes: 9 additions & 5 deletions libraries/chain/hotstuff/hotstuff.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ bool pending_quorum_certificate::add_strong_vote(const std::vector<uint8_t>& pro
size_t weak = num_weak();
size_t strong = num_strong();

// TODO: remove dlog statement
dlog( "strong ${n}, q ${q}", ("n", strong)("q", _quorum));

switch (_state) {
case state_t::unrestricted:
case state_t::restricted:
Expand Down Expand Up @@ -146,12 +149,13 @@ 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) {
// thread safe, <valid, strong>
std::pair<bool, 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);
bool valid = strong ? add_strong_vote(proposal_digest, index, pubkey, sig)
: add_weak_vote(proposal_digest, index, pubkey, sig);
return {valid, _state == state_t::strong};
}

// thread safe
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 @@ -360,7 +360,7 @@ namespace eosio::chain {
for (size_t i=0; i<finalizers.size(); ++i) {
if (finalizers[i].public_key == vote.finalizer_key) {
if (_current_qc.add_vote(vote.strong, std::vector<uint8_t>(digest.data(), digest.data() + 32),
i, vote.finalizer_key, vote.sig)) {
i, vote.finalizer_key, vote.sig).first) {
// fc_tlog(_logger, " === update bitset ${value} ${finalizer_key}",
// ("value", _current_qc.get_active_finalizers_string())("finalizer_key", vote.finalizer_key));
if (_current_qc.is_quorum_met()) {
Expand Down
4 changes: 2 additions & 2 deletions libraries/chain/hotstuff/test/hotstuff_tools.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ 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_vote(false, digest, index, pubkey[index], sk[index].sign(digest));
return qc.add_vote(false, digest, index, pubkey[index], sk[index].sign(digest)).first;
};

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

{
Expand Down
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct block_header_state_input : public building_block_input {
std::optional<finalizer_policy> new_finalizer_policy; // Comes from building_block::new_finalizer_policy
std::optional<qc_info_t> qc_info; // Comes from traversing branch from parent and calling get_best_qc()
// assert(qc->block_num <= num_from_id(previous));
bool validating = false;
};

struct block_header_state_core {
Expand Down
Loading
Loading