Skip to content

Commit

Permalink
Merge pull request #650 from AntelopeIO/GH-621-liveness-main
Browse files Browse the repository at this point in the history
[1.0 -> main] Use other_branch_latest_time for liveness
  • Loading branch information
heifner authored Aug 27, 2024
2 parents 7e1f138 + 6ee1fa2 commit 2f13275
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 51 deletions.
24 changes: 12 additions & 12 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1425,9 +1425,9 @@ struct controller_impl {
[&](const block_state_ptr& head) { return head->make_block_ref(); }});
// doesn't matter chain_head is not updated for IRREVERSIBLE, cannot be in irreversible mode and be a finalizer
my_finalizers.set_default_safety_information(
finalizer_safety_information{ .last_vote = ref,
.lock = ref,
.votes_forked_since_latest_strong_vote = false});
finalizer_safety_information{ .last_vote = ref,
.lock = ref,
.other_branch_latest_time = block_timestamp_type{} });
}
}

Expand Down Expand Up @@ -1636,9 +1636,9 @@ struct controller_impl {
// we create the non-legacy fork_db, as from this point we may need to cast votes to participate
// to the IF consensus. See https://github.com/AntelopeIO/leap/issues/2070#issuecomment-1941901836
my_finalizers.set_default_safety_information(
finalizer_safety_information{.last_vote = prev->make_block_ref(),
.lock = prev->make_block_ref(),
.votes_forked_since_latest_strong_vote = false});
finalizer_safety_information{.last_vote = prev->make_block_ref(),
.lock = prev->make_block_ref(),
.other_branch_latest_time = block_timestamp_type{} });
}
}
});
Expand Down Expand Up @@ -2040,19 +2040,19 @@ struct controller_impl {
auto set_finalizer_defaults = [&](auto& forkdb) -> void {
auto lib = forkdb.root();
my_finalizers.set_default_safety_information(
finalizer_safety_information{ .last_vote = {},
.lock = lib->make_block_ref(),
.votes_forked_since_latest_strong_vote = false });
finalizer_safety_information{ .last_vote = {},
.lock = lib->make_block_ref(),
.other_branch_latest_time = block_timestamp_type{} });
};
fork_db.apply_s<void>(set_finalizer_defaults);
} else {
// we are past the IF transition.
auto set_finalizer_defaults = [&](auto& forkdb) -> void {
auto lib = forkdb.root();
my_finalizers.set_default_safety_information(
finalizer_safety_information{ .last_vote = {},
.lock = lib->make_block_ref(),
.votes_forked_since_latest_strong_vote = false });
finalizer_safety_information{.last_vote = {},
.lock = lib->make_block_ref(),
.other_branch_latest_time = block_timestamp_type{} });
};
fork_db.apply_s<void>(set_finalizer_defaults);
}
Expand Down
75 changes: 46 additions & 29 deletions libraries/chain/finality/finalizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,35 +61,37 @@ finalizer::vote_result finalizer::decide_vote(const block_state_ptr& bsp) {
// If we vote, update `fsi.last_vote` and also `fsi.lock` if we have a newer commit qc
// -----------------------------------------------------------------------------------
if (can_vote) {
// if `fsi.last_vote` is not set, it will be initialized with a timestamp slot of 0, so we
// don't need to check fsi.last_vote.empty()
// ---------------------------------------------------------------------------------------
bool voting_strong = fsi.last_vote.timestamp <= bsp->core.latest_qc_block_timestamp();

if (!voting_strong) {
// we can vote strong if the proposal is a descendant of (i.e. extends) our last vote id AND
// the latest (weak) vote did not mask a prior (weak) vote for a block not on the same branch.
// -------------------------------------------------------------------------------------------
voting_strong = !fsi.votes_forked_since_latest_strong_vote && bsp->core.extends(fsi.last_vote.block_id);
const auto latest_qc_block_timestamp = bsp->core.latest_qc_block_timestamp();

// If `fsi.last_vote` is not set, it will be initialized with a timestamp slot of 0,
// which means `fsi.last_vote.timestamp` would always be less than or equal
// to `latest_qc_block_timestamp`.
// So, we don't need to separately check for the case where `fsi.last_vote.empty()` is true.
if (fsi.last_vote.timestamp <= latest_qc_block_timestamp) {
res.decision = vote_decision::strong_vote;
} else if (bsp->core.extends(fsi.last_vote.block_id)) {
// If `fsi.other_branch_latest_time` is not present, it will have a timestamp slot of
// 0, which means it will always be less than or equal to `latest_qc_block_timestamp`.
// So, we don't need to separately check for the case where
// `fsi.other_branch_latest_time` is not present.
if (fsi.other_branch_latest_time <= latest_qc_block_timestamp) {
res.decision = vote_decision::strong_vote;
} else {
res.decision = vote_decision::weak_vote;
}
} else {
res.decision = vote_decision::weak_vote;
fsi.other_branch_latest_time = fsi.last_vote.timestamp;
}

auto& latest_qc_claim__block_ref = bsp->core.get_block_reference(bsp->core.latest_qc_claim().block_num);
if (voting_strong) {
fsi.votes_forked_since_latest_strong_vote = false; // always reset to false on strong vote
if (latest_qc_claim__block_ref.timestamp > fsi.lock.timestamp)
fsi.lock = latest_qc_claim__block_ref;
} else {
// On a weak vote, if `votes_forked_since_latest_strong_vote` was already true, then it should remain true.
// The only way `votes_forked_since_latest_strong_vote` can change from false to true is on a weak vote
// for a block b where the last_vote references a block that is not an ancestor of b
// --------------------------------------------------------------------------------------------------------
fsi.votes_forked_since_latest_strong_vote =
fsi.votes_forked_since_latest_strong_vote || !bsp->core.extends(fsi.last_vote.block_id);
if (res.decision == vote_decision::strong_vote) {
fsi.other_branch_latest_time = block_timestamp_type{};
if (latest_qc_block_timestamp > fsi.lock.timestamp) {
fsi.lock = bsp->core.get_block_reference(bsp->core.latest_qc_claim().block_num);
}
}

fsi.last_vote = bsp->make_block_ref();

res.decision = voting_strong ? vote_decision::strong_vote : vote_decision::weak_vote;
}

fc_dlog(vote_logger, "block=${bn} ${id}, liveness_check=${l}, safety_check=${s}, monotony_check=${m}, can vote=${can_vote}, voting=${v}, locked=${lbn} ${lid}",
Expand All @@ -103,9 +105,9 @@ finalizer::vote_result finalizer::decide_vote(const block_state_ptr& bsp) {
bool finalizer::maybe_update_fsi(const block_state_ptr& bsp) {
auto& latest_qc_claim__block_ref = bsp->core.get_block_reference(bsp->core.latest_qc_claim().block_num);
if (latest_qc_claim__block_ref.timestamp > fsi.lock.timestamp && bsp->timestamp() > fsi.last_vote.timestamp) {
fsi.lock = latest_qc_claim__block_ref;
fsi.last_vote = bsp->make_block_ref();
fsi.votes_forked_since_latest_strong_vote = false; // always reset to false on strong vote
fsi.lock = latest_qc_claim__block_ref;
fsi.last_vote = bsp->make_block_ref();
fsi.other_branch_latest_time = block_timestamp_type{}; // always reset on strong vote
return true;
}
return false;
Expand Down Expand Up @@ -219,14 +221,27 @@ void my_finalizers_t::save_finalizer_safety_info() const {

// ----------------------------------------------------------------------------------------

// Corresponds to safety_file_version_0
struct finalizer_safety_information_v0 {
block_ref last_vote;
block_ref lock;
bool votes_forked_since_latest_strong_vote {false};
};

void my_finalizers_t::load_finalizer_safety_info_v0(fsi_map& res) {
uint64_t num_finalizers {0};
fc::raw::unpack(persist_file, num_finalizers);
for (size_t i=0; i<num_finalizers; ++i) {
bls_public_key pubkey;
my_finalizers_t::fsi_t fsi;
finalizer_safety_information_v0 fsi_v0;
fc::raw::unpack(persist_file, pubkey);
fc::raw::unpack(persist_file, fsi);
fc::raw::unpack(persist_file, fsi_v0);
my_finalizers_t::fsi_t fsi{
.last_vote = fsi_v0.last_vote,
.lock = fsi_v0.lock,
.other_branch_latest_time = fsi_v0.votes_forked_since_latest_strong_vote ? fsi_v0.last_vote.timestamp
: block_timestamp_type{}
};
res.emplace(pubkey, fsi);
}
}
Expand Down Expand Up @@ -371,3 +386,5 @@ void my_finalizers_t::set_default_safety_information(const fsi_t& fsi) {
}

} // namespace eosio::chain

FC_REFLECT(eosio::chain::finalizer_safety_information_v0, (last_vote)(lock)(votes_forked_since_latest_strong_vote));
14 changes: 8 additions & 6 deletions libraries/chain/include/eosio/chain/finality/finalizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ namespace eosio::chain {
struct finalizer_safety_information {
block_ref last_vote;
block_ref lock;
bool votes_forked_since_latest_strong_vote {false};
block_timestamp_type other_branch_latest_time;

static constexpr uint64_t magic = 0x5AFE11115AFE1111ull;

Expand All @@ -44,7 +44,7 @@ namespace eosio::chain {
auto operator==(const finalizer_safety_information& o) const {
return last_vote == o.last_vote &&
lock == o.lock &&
votes_forked_since_latest_strong_vote == o.votes_forked_since_latest_strong_vote;
other_branch_latest_time == o.other_branch_latest_time;
}
};

Expand Down Expand Up @@ -74,8 +74,10 @@ namespace eosio::chain {
///
/// Version 0: Spring 1.0.0 RC2 - File has fixed packed sizes with inactive safety info written to the end
/// of the file. Consists of [finalizer public_key, FSI]..
/// Version 1: Spring 1.0.0 RC3 - Variable length FSI with inactive safety info written at the beginning of
/// the file. Uses crc32 checksum to verify data on read.
/// Version 1: Spring 1.0.0 RC3 - File has inactive FSIs written at the beginning of the file. Uses crc32
/// checksum to verify data on read. Removes FSI
/// votes_forked_since_latest_strong_vote from the version 0 FSI and replaces it
/// with other_branch_latest_time.
///
static constexpr uint64_t safety_file_version_0 = 0;
static constexpr uint64_t safety_file_version_1 = 1;
Expand Down Expand Up @@ -192,7 +194,7 @@ namespace eosio::chain {

namespace std {
inline std::ostream& operator<<(std::ostream& os, const eosio::chain::finalizer_safety_information& fsi) {
os << "fsi(" << fsi.last_vote << ", " << fsi.lock << ", " << fsi.votes_forked_since_latest_strong_vote << ")";
os << "fsi(" << fsi.last_vote << ", " << fsi.lock << ", " << fsi.other_branch_latest_time << ")";
return os;
}

Expand All @@ -210,5 +212,5 @@ namespace std {
}
}

FC_REFLECT(eosio::chain::finalizer_safety_information, (last_vote)(lock)(votes_forked_since_latest_strong_vote))
FC_REFLECT(eosio::chain::finalizer_safety_information, (last_vote)(lock)(other_branch_latest_time))
FC_REFLECT_ENUM(eosio::chain::finalizer::vote_decision, (strong_vote)(weak_vote)(no_vote))
1 change: 1 addition & 0 deletions libraries/libfc/include/fc/io/datastream_crc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class datastream_crc {

// only use with p==0, otherwise use seekp() below
bool seekp(size_t p) {
assert(p == 0);
if (p == 0) {
crc_.reset();
return ds_.seekp(0);
Expand Down
6 changes: 3 additions & 3 deletions unittests/finalizer_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ std::vector<FSI> create_random_fsi(size_t count) {
.lock = block_ref{sha256::hash("lock"s + std::to_string(i)),
tstamp(i * 100),
sha256::hash("lock_digest"s + std::to_string(i))},
.votes_forked_since_latest_strong_vote = false
.other_branch_latest_time = block_timestamp_type{}
});
if (i)
assert(res.back() != res[0]);
Expand Down Expand Up @@ -101,7 +101,7 @@ BOOST_AUTO_TEST_CASE( basic_finalizer_safety_file_io ) try {

fsi_t fsi { .last_vote = proposals[6],
.lock = proposals[2],
.votes_forked_since_latest_strong_vote = false };
.other_branch_latest_time = block_timestamp_type{} };

bls_keys_t k("alice"_n);
bls_pub_priv_key_map_t local_finalizers = { { k.pubkey_str, k.privkey_str } };
Expand Down Expand Up @@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE( corrupt_finalizer_safety_file ) try {

fsi_t fsi { .last_vote = proposals[6],
.lock = proposals[2],
.votes_forked_since_latest_strong_vote = false };
.other_branch_latest_time = block_timestamp_type{} };

bls_keys_t k("alice"_n);
bls_pub_priv_key_map_t local_finalizers = { { k.pubkey_str, k.privkey_str } };
Expand Down
2 changes: 1 addition & 1 deletion unittests/finalizer_vote_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ struct simulator_t {
forkdb.reset_root(genesis);

block_ref genesis_ref(genesis->id(), genesis->timestamp(), genesis->id());
my_finalizer.fsi = fsi_t{genesis_ref, genesis_ref, false};
my_finalizer.fsi = fsi_t{genesis_ref, genesis_ref, {}};
}

vote_result vote(const bsp& p) {
Expand Down

0 comments on commit 2f13275

Please sign in to comment.