Skip to content

Commit

Permalink
Merge pull request #927 from AntelopeIO/gh_769
Browse files Browse the repository at this point in the history
Split verify_qc and add a new exception type invalid_qc
  • Loading branch information
greg7mdp authored Oct 12, 2024
2 parents 5d77965 + 9cddaa1 commit 43026d4
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 60 deletions.
14 changes: 13 additions & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,9 +266,21 @@ vote_status_t block_state::has_voted(const bls_public_key& key) const {
}

// Called from net threads
void block_state::verify_qc_signatures(const qc_t& qc) const {
finalizer_policies_t policies = get_finalizer_policies(qc.block_num); // get policies active at claimed block number
qc.verify_signatures(policies);
}

// Called from net threads
void block_state::verify_qc_basic(const qc_t& qc) const {
finalizer_policies_t policies = get_finalizer_policies(qc.block_num); // get policies active at claimed block number
qc.verify_basic(policies);
}

void block_state::verify_qc(const qc_t& qc) const {
finalizer_policies_t policies = get_finalizer_policies(qc.block_num); // get policies active at claimed block number
qc.verify(policies);
qc.verify_basic(policies);
qc.verify_signatures(policies);
}

qc_claim_t block_state::extract_qc_claim() const {
Expand Down
23 changes: 13 additions & 10 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3915,7 +3915,8 @@ struct controller_impl {
// Verify basic proper_block invariants.
// Called from net-threads. It is thread safe as signed_block is never modified after creation.
// -----------------------------------------------------------------------------
std::optional<qc_t> verify_basic_proper_block_invariants( const block_id_type& id, const signed_block_ptr& b, const block_header_state& prev ) {
std::optional<qc_t> verify_basic_proper_block_invariants( const block_id_type& id, const signed_block_ptr& b,
const block_header_state& prev ) {
assert(b->is_proper_svnn_block());

auto qc_ext_id = quorum_certificate_extension::extension_id();
Expand Down Expand Up @@ -4112,11 +4113,6 @@ struct controller_impl {
return {};
}

// This verifies BLS signatures and is expensive.
void verify_qc( const block_state& prev, const qc_t& qc ) {
prev.verify_qc(qc);
}

// thread safe, expected to be called from thread other than the main thread
// tuple<bool best_head, block_handle new_block_handle>
template<typename ForkDB, typename BS>
Expand All @@ -4128,8 +4124,9 @@ struct controller_impl {
log_and_drop_future<void> verify_qc_future;
if constexpr (is_proper_savanna_block) {
if (qc) {
verify_qc_future = post_async_task(thread_pool.get_executor(), [this, &qc, &prev] {
verify_qc(prev, *qc);
verify_qc_future = post_async_task(thread_pool.get_executor(), [&qc, &prev] {
// do both signature verification and basic checks in the async task
prev.verify_qc(*qc);
});
}
}
Expand Down Expand Up @@ -4278,8 +4275,14 @@ struct controller_impl {
std::optional<qc_t> qc = verify_basic_block_invariants({}, b, *head);

if constexpr (std::is_same_v<typename std::decay_t<BSP>, block_state_ptr>) {
if (conf.force_all_checks && qc) {
verify_qc(*head, *qc);
// do basic checks always (excluding signature verification)
if (qc) {
head->verify_qc_basic(*qc);

if (conf.force_all_checks) {
// verify signatures only if `conf.force_all_checks`
head->verify_qc_signatures(*qc);
}
}
}

Expand Down
56 changes: 35 additions & 21 deletions libraries/chain/finality/qc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ inline void verify_dual_finalizers_votes(const finalizer_policies_t& policies,
for (const auto& pending_fin: policies.pending_finalizer_policy->finalizers) {
if (active_fin.public_key == pending_fin.public_key) {
EOS_ASSERT(active_policy_sig.vote_same_at(pending_policy_sig, active_vote_index, pending_vote_index),
invalid_qc_claim,
invalid_qc,
"qc ${bn} contains a dual finalizer ${k} which does not vote the same on active and pending policies",
("bn", block_num)("k", active_fin.public_key));
break;
Expand All @@ -50,30 +50,39 @@ inline void verify_dual_finalizers_votes(const finalizer_policies_t& policies,
}
}

void qc_t::verify(const finalizer_policies_t& policies) const {
void qc_t::verify_signatures(const finalizer_policies_t& policies) const {
const auto& strong_digest = policies.finality_digest;
auto weak_digest = create_weak_digest(strong_digest);
auto weak_digest = create_weak_digest(strong_digest);

active_policy_sig.verify_signatures(policies.active_finalizer_policy, strong_digest, weak_digest);

if (pending_policy_sig) {
EOS_ASSERT(policies.pending_finalizer_policy, invalid_qc,
"qc ${bn} contains pending policy signature for nonexistent pending finalizer policy", ("bn", block_num));
pending_policy_sig->verify_signatures(policies.pending_finalizer_policy, strong_digest, weak_digest);
}
}

void qc_t::verify_basic(const finalizer_policies_t& policies) const {
active_policy_sig.verify_vote_format(policies.active_finalizer_policy);
active_policy_sig.verify(policies.active_finalizer_policy, strong_digest, weak_digest);
active_policy_sig.verify_weights(policies.active_finalizer_policy);

if (pending_policy_sig) {
EOS_ASSERT(policies.pending_finalizer_policy, invalid_qc_claim,
EOS_ASSERT(policies.pending_finalizer_policy, invalid_qc,
"qc ${bn} contains pending policy signature for nonexistent pending finalizer policy", ("bn", block_num));

// verify that every finalizer included in both policies voted the same
verify_dual_finalizers_votes(policies, active_policy_sig, *pending_policy_sig, block_num);

pending_policy_sig->verify_vote_format(policies.pending_finalizer_policy);
pending_policy_sig->verify(policies.pending_finalizer_policy, strong_digest, weak_digest);
pending_policy_sig->verify_weights(policies.pending_finalizer_policy);
} else {
EOS_ASSERT(!policies.pending_finalizer_policy, invalid_qc_claim,
EOS_ASSERT(!policies.pending_finalizer_policy, invalid_qc,
"qc ${bn} does not contain pending policy signature for pending finalizer policy", ("bn", block_num));
}

}

// returns true iff the other and I voted iin the same way.
// returns true iff the other and I voted in the same way.
bool qc_sig_t::vote_same_at(const qc_sig_t& other, uint32_t my_vote_index, uint32_t other_vote_index) const {
assert(!strong_votes || my_vote_index < strong_votes->size());
assert(!weak_votes || my_vote_index < weak_votes->size());
Expand All @@ -94,19 +103,19 @@ void qc_sig_t::verify_vote_format(const finalizer_policy_ptr& fin_policy) const
const auto& finalizers = fin_policy->finalizers;
auto num_finalizers = finalizers.size();

EOS_ASSERT( strong_votes || weak_votes, invalid_qc_claim,
EOS_ASSERT( strong_votes || weak_votes, invalid_qc,
"Neither strong_votes nor weak_votes present for finalizer policy, generation ${n}",
("n", fin_policy->generation) );

// verify number of finalizers matches with vote bitset size
if (strong_votes) {
EOS_ASSERT( num_finalizers == strong_votes->size(), invalid_qc_claim,
EOS_ASSERT( num_finalizers == strong_votes->size(), invalid_qc,
"vote bitset size is not the same as the number of finalizers for the policy it refers to, "
"vote bitset size: ${s}, num of finalizers for the policy: ${n}",
("s", strong_votes->size())("n", num_finalizers) );
}
if (weak_votes) {
EOS_ASSERT( num_finalizers == weak_votes->size(), invalid_qc_claim,
EOS_ASSERT( num_finalizers == weak_votes->size(), invalid_qc,
"vote bitset size is not the same as the number of finalizers for the policy it refers to, "
"vote bitset size: ${s}, num of finalizers for the policy: ${n}",
("s", weak_votes->size())("n", num_finalizers) );
Expand All @@ -116,17 +125,15 @@ void qc_sig_t::verify_vote_format(const finalizer_policy_ptr& fin_policy) const
if (strong_votes && weak_votes) {
for (size_t i=0; i<strong_votes->size(); ++i) {
// at most one is true
EOS_ASSERT( !((*strong_votes)[i] && (*weak_votes)[i]), invalid_qc_claim,
EOS_ASSERT( !((*strong_votes)[i] && (*weak_votes)[i]), invalid_qc,
"finalizer (bit index ${i}) voted both strong and weak",
("i", i) );

}
}
}

void qc_sig_t::verify(const finalizer_policy_ptr& fin_policy,
const digest_type& strong_digest,
const weak_digest_t& weak_digest) const {
void qc_sig_t::verify_weights(const finalizer_policy_ptr& fin_policy) const {

const auto& finalizers = fin_policy->finalizers;
auto num_finalizers = finalizers.size();
Expand All @@ -148,14 +155,21 @@ void qc_sig_t::verify(const finalizer_policy_ptr& fin_policy,

// verfify quorum is met
if( is_strong() ) {
EOS_ASSERT( strong_weights >= fin_policy->threshold, invalid_qc_claim,
EOS_ASSERT( strong_weights >= fin_policy->threshold, invalid_qc,
"strong quorum is not met, strong_weights: ${s}, threshold: ${t}",
("s", strong_weights)("t", fin_policy->threshold) );
} else {
EOS_ASSERT( strong_weights + weak_weights >= fin_policy->threshold, invalid_qc_claim,
EOS_ASSERT( strong_weights + weak_weights >= fin_policy->threshold, invalid_qc,
"weak quorum is not met, strong_weights: ${s}, weak_weights: ${w}, threshold: ${t}",
("s", strong_weights)("w", weak_weights)("t", fin_policy->threshold) );
}
}

void qc_sig_t::verify_signatures(const finalizer_policy_ptr& fin_policy,
const digest_type& strong_digest,
const weak_digest_t& weak_digest) const {
const auto& finalizers = fin_policy->finalizers;
auto num_finalizers = finalizers.size();

// no reason to use bls_public_key wrapper
std::vector<bls12_381::g1> pubkeys;
Expand Down Expand Up @@ -190,7 +204,7 @@ void qc_sig_t::verify(const finalizer_policy_ptr& fin_policy,

// validate aggregated signature
EOS_ASSERT( bls12_381::aggregate_verify(pubkeys, digests, sig.jacobian_montgomery_le()),
invalid_qc_claim, "qc signature validation failed" );
invalid_qc_signature, "qc signature validation failed" );

}

Expand Down Expand Up @@ -409,7 +423,7 @@ std::optional<qc_t> aggregating_qc_t::get_best_qc(block_num_type block_num) cons

bool aggregating_qc_t::set_received_qc(const qc_t& qc) {
// qc should have already been verified via verify_qc, this EOS_ASSERT should never fire
EOS_ASSERT(!pending_policy_sig || qc.pending_policy_sig, invalid_qc_claim,
EOS_ASSERT(!pending_policy_sig || qc.pending_policy_sig, invalid_qc,
"qc ${bn} expected to have a pending policy signature", ("bn", qc.block_num));
bool active_better = active_policy_sig.set_received_qc_sig(qc.active_policy_sig);
bool pending_better = false;
Expand Down Expand Up @@ -503,7 +517,7 @@ vote_status_t aggregating_qc_t::has_voted(const bls_public_key& key) const {
return active_status;
}

EOS_ASSERT(pending_policy_sig, invalid_qc_claim,
EOS_ASSERT(pending_policy_sig, invalid_qc,
"qc does not contain pending policy signature for pending finalizer policy");
vote_status_t pending_status = finalizer_has_voted(pending_finalizer_policy, *pending_policy_sig, key);

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 @@ -151,7 +151,10 @@ struct block_state : public block_header_state { // block_header_state provi
// connection_id only for logging
aggregate_vote_result_t aggregate_vote(uint32_t connection_id, const vote_message& vote); // aggregate vote into aggregating_qc
vote_status_t has_voted(const bls_public_key& key) const;
void verify_qc(const qc_t& qc) const; // verify given qc_t is valid with respect block_state

void verify_qc_signatures(const qc_t& qc) const; // validate qc signatures (slow)
void verify_qc_basic(const qc_t& qc) const; // do basic checks on provided qc, excluding signature verification
void verify_qc(const qc_t& qc) const; // full qc verification incl. signatures

using bhs_t = block_header_state;
using bhsp_t = block_header_state_ptr;
Expand Down
4 changes: 4 additions & 0 deletions libraries/chain/include/eosio/chain/exceptions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,10 @@ namespace eosio { namespace chain {
3030013, "Block includes an ill-formed additional block signature extension" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_qc_claim, block_validate_exception,
3030014, "Block includes an invalid QC claim" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_qc, block_validate_exception,
3030015, "Block includes an invalid QC" )
FC_DECLARE_DERIVED_EXCEPTION( invalid_qc_signature, block_validate_exception,
3030016, "Block includes a QC with invalid signature(s)" )

FC_DECLARE_DERIVED_EXCEPTION( transaction_exception, chain_exception,
3040000, "Transaction exception" )
Expand Down
23 changes: 13 additions & 10 deletions libraries/chain/include/eosio/chain/finality/qc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,19 @@ namespace eosio::chain {
};

struct qc_sig_t {
bool is_weak() const { return !!weak_votes; }
bool is_strong() const { return !weak_votes; }

std::optional<vote_bitset_t> strong_votes;
std::optional<vote_bitset_t> weak_votes;
bls_aggregate_signature sig;

bool is_weak() const { return !!weak_votes; }
bool is_strong() const { return !weak_votes; }

// called from net threads
void verify_signatures(const finalizer_policy_ptr& fin_policy, const digest_type& strong_digest, const weak_digest_t& weak_digest) const;

// called from net threads
void verify(const finalizer_policy_ptr& fin_policy, const digest_type& strong_digest, const weak_digest_t& weak_digest) const;
void verify_weights(const finalizer_policy_ptr& fin_policy) const;

void verify_vote_format(const finalizer_policy_ptr& fin_policy) const;

// returns true if vote indicated by my_vote_index in strong_votes or
Expand All @@ -81,11 +85,9 @@ namespace eosio::chain {
};

struct qc_t {
uint32_t block_num{0};
// signatures corresponding to the active finalizer policy
qc_sig_t active_policy_sig;
// signatures corresponding to the pending finalizer policy if there is one
std::optional<qc_sig_t> pending_policy_sig;
uint32_t block_num{0};
qc_sig_t active_policy_sig; // signatures for the active finalizer policy
std::optional<qc_sig_t> pending_policy_sig; // signatures for the pending finalizer policy (if any)

bool is_strong() const {
return active_policy_sig.is_strong() && (!pending_policy_sig || pending_policy_sig->is_strong());
Expand All @@ -96,7 +98,8 @@ namespace eosio::chain {
return { .block_num = block_num, .is_strong_qc = is_strong() };
}

void verify(const finalizer_policies_t& policies) const;
void verify_signatures(const finalizer_policies_t& policies) const; // validate qc signatures
void verify_basic(const finalizer_policies_t& policies) const; // do basic checks on provided qc, excluding signature verification
};

struct qc_data_t {
Expand Down
Loading

0 comments on commit 43026d4

Please sign in to comment.