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: Add the beginning of a savanna disaster recovery test #54

Merged
merged 15 commits into from
Apr 25, 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
14 changes: 7 additions & 7 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const {
};

// compute strong and weak accumulated weights
auto strong_weights = qc._strong_votes ? weights( *qc._strong_votes ) : 0;
auto weak_weights = qc._weak_votes ? weights( *qc._weak_votes ) : 0;
auto strong_weights = qc.strong_votes ? weights( *qc.strong_votes ) : 0;
auto weak_weights = qc.weak_votes ? weights( *qc.weak_votes ) : 0;

// verfify quorum is met
if( qc.is_strong() ) {
Expand Down Expand Up @@ -259,18 +259,18 @@ void block_state::verify_qc(const valid_quorum_certificate& qc) const {
};

// aggregate public keys and digests for strong and weak votes
if( qc._strong_votes ) {
pubkeys.emplace_back(aggregate_pubkeys(*qc._strong_votes));
if( qc.strong_votes ) {
pubkeys.emplace_back(aggregate_pubkeys(*qc.strong_votes));
digests.emplace_back(std::vector<uint8_t>{strong_digest.data(), strong_digest.data() + strong_digest.data_size()});
}

if( qc._weak_votes ) {
pubkeys.emplace_back(aggregate_pubkeys(*qc._weak_votes));
if( qc.weak_votes ) {
pubkeys.emplace_back(aggregate_pubkeys(*qc.weak_votes));
digests.emplace_back(std::vector<uint8_t>{weak_digest.begin(), weak_digest.end()});
}

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

Expand Down
8 changes: 4 additions & 4 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3721,10 +3721,10 @@ struct controller_impl {
("n1", qc_proof.block_num)("n2", new_qc_claim.block_num)("b", block_num) );

// Verify claimed strictness is the same as in proof
EOS_ASSERT( qc_proof.qc.is_strong() == new_qc_claim.is_strong_qc,
EOS_ASSERT( qc_proof.data.is_strong() == new_qc_claim.is_strong_qc,
invalid_qc_claim,
"QC is_strong (${s1}) in block extension does not match is_strong_qc (${s2}) in header extension. Block number: ${b}",
("s1", qc_proof.qc.is_strong())("s2", new_qc_claim.is_strong_qc)("b", block_num) );
("s1", qc_proof.data.is_strong())("s2", new_qc_claim.is_strong_qc)("b", block_num) );

// find the claimed block's block state on branch of id
auto bsp = fetch_bsp_on_branch_by_num( prev.id(), new_qc_claim.block_num );
Expand All @@ -3734,7 +3734,7 @@ struct controller_impl {
("q", new_qc_claim.block_num)("b", block_num) );

// verify the QC proof against the claimed block
bsp->verify_qc(qc_proof.qc);
bsp->verify_qc(qc_proof.data);
}

// thread safe, expected to be called from thread other than the main thread
Expand Down Expand Up @@ -3843,7 +3843,7 @@ struct controller_impl {
return;
}
const auto& qc_ext = std::get<quorum_certificate_extension>(block_exts.lower_bound(qc_ext_id)->second);
const auto& received_qc = qc_ext.qc.qc;
const auto& received_qc = qc_ext.qc.data;

const auto claimed = fetch_bsp_on_branch_by_num( bsp_in->previous(), qc_ext.qc.block_num );
if( !claimed ) {
Expand Down
118 changes: 59 additions & 59 deletions libraries/chain/finality/quorum_certificate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,38 +22,38 @@ inline std::vector<uint32_t> bitset_to_vector(const vote_bitset& bs) {
}

bool pending_quorum_certificate::has_voted(size_t index) const {
return _strong_votes.has_voted(index) || _weak_votes.has_voted(index);
return strong_votes.has_voted(index) || weak_votes.has_voted(index);
}

bool pending_quorum_certificate::has_voted_no_lock(bool strong, size_t index) const {
if (strong) {
return _strong_votes.has_voted(index);
return strong_votes.has_voted(index);
}
return _weak_votes.has_voted(index);
return weak_votes.has_voted(index);
}

void pending_quorum_certificate::votes_t::reflector_init() {
_processed = std::vector<std::atomic<bool>>(_bitset.size());
for (size_t i = 0; i < _bitset.size(); ++i) {
if (_bitset[i]) {
_processed[i].store(true, std::memory_order_relaxed);
processed = std::vector<std::atomic<bool>>(bitset.size());
for (size_t i = 0; i < bitset.size(); ++i) {
if (bitset[i]) {
processed[i].store(true, std::memory_order_relaxed);
}
}
}

bool pending_quorum_certificate::votes_t::has_voted(size_t index) const {
assert(index < _processed.size());
return _processed[index].load(std::memory_order_relaxed);
assert(index < processed.size());
return processed[index].load(std::memory_order_relaxed);
}


vote_status pending_quorum_certificate::votes_t::add_vote(size_t index, const bls_signature& sig) {
if (_bitset[index]) { // check here as could have come in while unlocked
vote_status pending_quorum_certificate::votes_t::add_vote(size_t index, const bls_signature& signature) {
if (bitset[index]) { // check here as could have come in while unlocked
return vote_status::duplicate; // shouldn't be already present
}
_processed[index].store(true, std::memory_order_relaxed);
_bitset.set(index);
_sig.aggregate(sig); // works even if _sig is default initialized (fp2::zero())
processed[index].store(true, std::memory_order_relaxed);
bitset.set(index);
sig.aggregate(signature); // works even if _sig is default initialized (fp2::zero())
return vote_status::success;
}

Expand All @@ -63,10 +63,10 @@ pending_quorum_certificate::pending_quorum_certificate()

pending_quorum_certificate::pending_quorum_certificate(size_t num_finalizers, uint64_t quorum, uint64_t max_weak_sum_before_weak_final)
: _mtx(std::make_unique<std::mutex>())
, _quorum(quorum)
, _max_weak_sum_before_weak_final(max_weak_sum_before_weak_final)
, _weak_votes(num_finalizers)
, _strong_votes(num_finalizers) {
, quorum(quorum)
, max_weak_sum_before_weak_final(max_weak_sum_before_weak_final)
, weak_votes(num_finalizers)
, strong_votes(num_finalizers) {
}

bool pending_quorum_certificate::is_quorum_met() const {
Expand All @@ -76,24 +76,24 @@ bool pending_quorum_certificate::is_quorum_met() const {

// called by add_vote, already protected by mutex
vote_status pending_quorum_certificate::add_strong_vote(size_t index, const bls_signature& sig, uint64_t weight) {
if (auto s = _strong_votes.add_vote(index, sig); s != vote_status::success) {
if (auto s = strong_votes.add_vote(index, sig); s != vote_status::success) {
return s;
}
_strong_sum += weight;
strong_sum += weight;

switch (_state) {
switch (pending_state) {
case state_t::unrestricted:
case state_t::restricted:
if (_strong_sum >= _quorum) {
assert(_state != state_t::restricted);
_state = state_t::strong;
} else if (_weak_sum + _strong_sum >= _quorum)
_state = (_state == state_t::restricted) ? state_t::weak_final : state_t::weak_achieved;
if (strong_sum >= quorum) {
assert(pending_state != state_t::restricted);
pending_state = state_t::strong;
} else if (weak_sum + strong_sum >= quorum)
pending_state = (pending_state == state_t::restricted) ? state_t::weak_final : state_t::weak_achieved;
break;

case state_t::weak_achieved:
if (_strong_sum >= _quorum)
_state = state_t::strong;
if (strong_sum >= quorum)
pending_state = state_t::strong;
break;

case state_t::weak_final:
Expand All @@ -106,27 +106,27 @@ vote_status pending_quorum_certificate::add_strong_vote(size_t index, const bls_

// called by add_vote, already protected by mutex
vote_status pending_quorum_certificate::add_weak_vote(size_t index, const bls_signature& sig, uint64_t weight) {
if (auto s = _weak_votes.add_vote(index, sig); s != vote_status::success)
if (auto s = weak_votes.add_vote(index, sig); s != vote_status::success)
return s;
_weak_sum += weight;
weak_sum += weight;

switch (_state) {
switch (pending_state) {
case state_t::unrestricted:
case state_t::restricted:
if (_weak_sum + _strong_sum >= _quorum)
_state = state_t::weak_achieved;

if (_weak_sum > _max_weak_sum_before_weak_final) {
if (_state == state_t::weak_achieved)
_state = state_t::weak_final;
else if (_state == state_t::unrestricted)
_state = state_t::restricted;
if (weak_sum + strong_sum >= quorum)
pending_state = state_t::weak_achieved;

if (weak_sum > max_weak_sum_before_weak_final) {
if (pending_state == state_t::weak_achieved)
pending_state = state_t::weak_final;
else if (pending_state == state_t::unrestricted)
pending_state = state_t::restricted;
}
break;

case state_t::weak_achieved:
if (_weak_sum >= _max_weak_sum_before_weak_final)
_state = state_t::weak_final;
if (weak_sum >= max_weak_sum_before_weak_final)
pending_state = state_t::weak_final;
break;

case state_t::weak_final:
Expand All @@ -152,10 +152,10 @@ vote_status pending_quorum_certificate::add_vote(uint32_t connection_id, block_n
}

std::unique_lock g(*_mtx);
state_t pre_state = _state;
state_t pre_state = pending_state;
vote_status s = strong ? add_strong_vote(index, sig, weight)
: add_weak_vote(index, sig, weight);
state_t post_state = _state;
state_t post_state = pending_state;
g.unlock();

fc_dlog(vote_logger, "connection - ${c} block_num: ${bn}, vote strong: ${sv}, status: ${s}, pre-state: ${pre}, post-state: ${state}, quorum_met: ${q}",
Expand All @@ -167,14 +167,14 @@ vote_status pending_quorum_certificate::add_vote(uint32_t connection_id, block_n
valid_quorum_certificate pending_quorum_certificate::to_valid_quorum_certificate() const {
valid_quorum_certificate valid_qc;

if( _state == state_t::strong ) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._sig = _strong_votes._sig;
if( pending_state == state_t::strong ) {
valid_qc.strong_votes = strong_votes.bitset;
valid_qc.sig = strong_votes.sig;
} else if (is_quorum_met_no_lock()) {
valid_qc._strong_votes = _strong_votes._bitset;
valid_qc._weak_votes = _weak_votes._bitset;
valid_qc._sig = _strong_votes._sig;
valid_qc._sig.aggregate(_weak_votes._sig);
valid_qc.strong_votes = strong_votes.bitset;
valid_qc.weak_votes = weak_votes.bitset;
valid_qc.sig = strong_votes.sig;
valid_qc.sig.aggregate(weak_votes.sig);
} else
assert(0); // this should be called only when we have a valid qc.

Expand All @@ -185,8 +185,8 @@ std::optional<quorum_certificate> pending_quorum_certificate::get_best_qc(block_
std::lock_guard g(*_mtx);
// if pending_qc does not have a valid QC, consider valid_qc only
if( !is_quorum_met_no_lock() ) {
if( _valid_qc ) {
return std::optional{quorum_certificate{ block_num, *_valid_qc }};
if( valid_qc ) {
return std::optional{quorum_certificate{ block_num, *valid_qc }};
} else {
return std::nullopt;
}
Expand All @@ -196,31 +196,31 @@ std::optional<quorum_certificate> pending_quorum_certificate::get_best_qc(block_
valid_quorum_certificate valid_qc_from_pending = to_valid_quorum_certificate();

// if valid_qc does not have value, consider valid_qc_from_pending only
if( !_valid_qc ) {
if( !valid_qc ) {
return std::optional{quorum_certificate{ block_num, 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 broken by valid_qc
_valid_qc->is_strong() ? *_valid_qc : valid_qc_from_pending; // strong beats weak
valid_qc->is_strong() == valid_qc_from_pending.is_strong() ?
*valid_qc : // tie broken by valid_qc
valid_qc->is_strong() ? *valid_qc : valid_qc_from_pending; // strong beats weak
return std::optional{quorum_certificate{ block_num, best_qc }};
}

void pending_quorum_certificate::set_valid_qc(const valid_quorum_certificate& qc) {
std::lock_guard g(*_mtx);
_valid_qc = qc;
valid_qc = qc;
}

bool pending_quorum_certificate::valid_qc_is_strong() const {
std::lock_guard g(*_mtx);
return _valid_qc && _valid_qc->is_strong();
return valid_qc && valid_qc->is_strong();
}

bool pending_quorum_certificate::is_quorum_met_no_lock() const {
return is_quorum_met(_state);
return is_quorum_met(pending_state);
}

} // namespace eosio::chain
53 changes: 26 additions & 27 deletions libraries/chain/include/eosio/chain/finality/quorum_certificate.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
#include <fc/crypto/bls_private_key.hpp>
#include <fc/crypto/bls_public_key.hpp>
#include <fc/crypto/bls_signature.hpp>

#include <boost/dynamic_bitset.hpp>
#include <fc/bitutil.hpp>

#include <mutex>

Expand All @@ -31,21 +30,21 @@ namespace eosio::chain {

// valid_quorum_certificate
struct valid_quorum_certificate {
bool is_weak() const { return !!_weak_votes; }
bool is_strong() const { return !_weak_votes; }
bool is_weak() const { return !!weak_votes; }
bool is_strong() const { return !weak_votes; }

std::optional<vote_bitset> _strong_votes;
std::optional<vote_bitset> _weak_votes;
bls_aggregate_signature _sig;
std::optional<vote_bitset> strong_votes;
std::optional<vote_bitset> weak_votes;
bls_aggregate_signature sig;
};

// quorum_certificate
struct quorum_certificate {
uint32_t block_num;
valid_quorum_certificate qc;
valid_quorum_certificate data;

qc_claim_t to_qc_claim() const {
return {.block_num = block_num, .is_strong_qc = qc.is_strong()};
return {.block_num = block_num, .is_strong_qc = data.is_strong()};
}
};

Expand Down Expand Up @@ -75,15 +74,15 @@ namespace eosio::chain {
friend struct fc::has_reflector_init<votes_t>;
friend class pending_quorum_certificate;

vote_bitset _bitset;
bls_aggregate_signature _sig;
std::vector<std::atomic<bool>> _processed; // avoid locking mutex for _bitset duplicate check
vote_bitset bitset;
bls_aggregate_signature sig;
std::vector<std::atomic<bool>> processed; // avoid locking mutex for _bitset duplicate check

void reflector_init();
public:
explicit votes_t(size_t num_finalizers)
: _bitset(num_finalizers)
, _processed(num_finalizers) {}
: bitset(num_finalizers)
, processed(num_finalizers) {}

// thread safe
bool has_voted(size_t index) const;
Expand Down Expand Up @@ -114,7 +113,7 @@ namespace eosio::chain {
// thread safe
bool has_voted(size_t index) const;

state_t state() const { std::lock_guard g(*_mtx); return _state; };
state_t state() const { std::lock_guard g(*_mtx); return pending_state; };

std::optional<quorum_certificate> get_best_qc(block_num_type block_num) const;
void set_valid_qc(const valid_quorum_certificate& qc);
Expand All @@ -123,14 +122,14 @@ namespace eosio::chain {
friend struct fc::reflector<pending_quorum_certificate>;
friend class qc_chain;
std::unique_ptr<std::mutex> _mtx;
std::optional<valid_quorum_certificate> _valid_qc; // best qc received from the network inside block extension
uint64_t _quorum {0};
uint64_t _max_weak_sum_before_weak_final {0}; // max weak sum before becoming weak_final
state_t _state { state_t::unrestricted };
uint64_t _strong_sum {0}; // accumulated sum of strong votes so far
uint64_t _weak_sum {0}; // accumulated sum of weak votes so far
votes_t _weak_votes {0};
votes_t _strong_votes {0};
std::optional<valid_quorum_certificate> valid_qc; // best qc received from the network inside block extension
uint64_t quorum {0};
uint64_t max_weak_sum_before_weak_final {0}; // max weak sum before becoming weak_final
state_t pending_state { state_t::unrestricted };
uint64_t strong_sum {0}; // accumulated sum of strong votes so far
uint64_t weak_sum {0}; // accumulated sum of weak votes so far
votes_t weak_votes {0};
votes_t strong_votes {0};

// called by add_vote, already protected by mutex
vote_status add_strong_vote(size_t index,
Expand All @@ -150,8 +149,8 @@ namespace eosio::chain {


FC_REFLECT_ENUM(eosio::chain::vote_status, (success)(duplicate)(unknown_public_key)(invalid_signature)(unknown_block)(max_exceeded))
FC_REFLECT(eosio::chain::valid_quorum_certificate, (_strong_votes)(_weak_votes)(_sig));
FC_REFLECT(eosio::chain::pending_quorum_certificate, (_valid_qc)(_quorum)(_max_weak_sum_before_weak_final)(_state)(_strong_sum)(_weak_sum)(_weak_votes)(_strong_votes));
FC_REFLECT(eosio::chain::valid_quorum_certificate, (strong_votes)(weak_votes)(sig));
FC_REFLECT(eosio::chain::pending_quorum_certificate, (valid_qc)(quorum)(max_weak_sum_before_weak_final)(pending_state)(strong_sum)(weak_sum)(weak_votes)(strong_votes));
FC_REFLECT_ENUM(eosio::chain::pending_quorum_certificate::state_t, (unrestricted)(restricted)(weak_achieved)(weak_final)(strong));
FC_REFLECT(eosio::chain::pending_quorum_certificate::votes_t, (_bitset)(_sig));
FC_REFLECT(eosio::chain::quorum_certificate, (block_num)(qc));
FC_REFLECT(eosio::chain::pending_quorum_certificate::votes_t, (bitset)(sig));
FC_REFLECT(eosio::chain::quorum_certificate, (block_num)(data));
Loading
Loading