Skip to content

Commit

Permalink
Merge pull request #54 from AntelopeIO/GH-13-disaster-test
Browse files Browse the repository at this point in the history
IF: Add the beginning of a savanna disaster recovery test
  • Loading branch information
heifner authored Apr 25, 2024
2 parents 54812d4 + ef37672 commit dcda785
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 123 deletions.
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

0 comments on commit dcda785

Please sign in to comment.