Skip to content

Commit

Permalink
Merge branch 'release/1.0' into improve_production_restart
Browse files Browse the repository at this point in the history
  • Loading branch information
linh2931 authored Aug 27, 2024
2 parents 1205dbb + 511eabe commit cca8ee1
Show file tree
Hide file tree
Showing 17 changed files with 327 additions and 187 deletions.
46 changes: 12 additions & 34 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 Expand Up @@ -4833,31 +4833,13 @@ struct controller_impl {
return conf.block_validation_mode == validation_mode::LIGHT || conf.trusted_producers.count(producer);
}

int32_t max_reversible_blocks_allowed() const {
if (conf.max_reversible_blocks == 0)
return std::numeric_limits<int32_t>::max();

return fork_db.apply<int32_t>(
[&](const fork_database_legacy_t& forkdb) {
return std::numeric_limits<int32_t>::max();
},
[&](const fork_database_if_t& forkdb) {
return conf.max_reversible_blocks - forkdb.size();
});
}

bool should_terminate(block_num_type reversible_block_num) const {
assert(reversible_block_num > 0);
if (conf.terminate_at_block > 0 && conf.terminate_at_block <= reversible_block_num) {
ilog("Block ${n} reached configured maximum block ${num}; terminating",
("n", reversible_block_num)("num", conf.terminate_at_block) );
return true;
}
if (max_reversible_blocks_allowed() <= 0) {
elog("Exceeded max reversible blocks allowed, fork db size ${s} >= max-reversible-blocks ${m}",
("s", fork_db_size())("m", conf.max_reversible_blocks));
return true;
}
return false;
}

Expand Down Expand Up @@ -5666,10 +5648,6 @@ bool controller::should_terminate() const {
return my->should_terminate();
}

int32_t controller:: max_reversible_blocks_allowed() const {
return my->max_reversible_blocks_allowed();
}

const apply_handler* controller::find_apply_handler( account_name receiver, account_name scope, action_name act ) const
{
auto native_handler_scope = my->apply_handlers.find( receiver );
Expand Down
179 changes: 121 additions & 58 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 @@ -175,47 +177,93 @@ void my_finalizers_t::maybe_update_fsi(const block_state_ptr& bsp, const qc_t& r

void my_finalizers_t::save_finalizer_safety_info() const {

if (!persist_file.is_open()) {
if (!cfile_ds.is_open()) {
EOS_ASSERT(!persist_file_path.empty(), finalizer_safety_exception,
"path for storing finalizer safety information file not specified");
if (!std::filesystem::exists(persist_file_path.parent_path()))
std::filesystem::create_directories(persist_file_path.parent_path());
persist_file.set_file_path(persist_file_path);
persist_file.open(fc::cfile::truncate_rw_mode);
cfile_ds.set_file_path(persist_file_path);
cfile_ds.open(fc::cfile::truncate_rw_mode);
}
try {
static_assert(sizeof(finalizer_safety_information) == 152, "If size changes then need to handle loading old files");
static_assert(sizeof(decltype(bls_public_key{}.affine_non_montgomery_le())) == 96,
"If size changes then need to handle loading old files, fc::pack uses affine_non_montgomery_le()");

persist_file.seek(0);
fc::raw::pack(persist_file, fsi_t::magic);
fc::raw::pack(persist_file, current_safety_file_version);
fc::raw::pack(persist_file, (uint64_t)(finalizers.size() + inactive_safety_info.size()));
for (const auto& [pub_key, f] : finalizers) {
fc::raw::pack(persist_file, pub_key);
fc::raw::pack(persist_file, f.fsi);
}
if (!inactive_safety_info_written) {
// optimize by only calculating crc for inactive once
if (inactive_safety_info_written_pos == 0) {
persist_file.seekp(0);
fc::raw::pack(persist_file, fsi_t::magic);
fc::raw::pack(persist_file, current_safety_file_version);
uint64_t size = finalizers.size() + inactive_safety_info.size();
fc::raw::pack(persist_file, size);

// save also the fsi that was originally present in the file, but which applied to
// finalizers not configured anymore.
for (const auto& [pub_key, fsi] : inactive_safety_info) {
fc::raw::pack(persist_file, pub_key);
fc::raw::pack(persist_file, fsi);
}
inactive_safety_info_written = true;
inactive_safety_info_written_pos = persist_file.tellp();
inactive_crc32 = persist_file.crc();
} else {
persist_file.seekp(inactive_safety_info_written_pos, inactive_crc32);
}

// active finalizers
for (const auto& [pub_key, f] : finalizers) {
fc::raw::pack(persist_file, pub_key);
fc::raw::pack(persist_file, f.fsi);
}
persist_file.flush();

uint32_t cs = persist_file.checksum();
fc::raw::pack(persist_file, cs);

cfile_ds.flush();
} FC_LOG_AND_RETHROW()
}

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

// 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;
finalizer_safety_information_v0 fsi_v0;
fc::raw::unpack(persist_file, pubkey);
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);
}
}

void my_finalizers_t::load_finalizer_safety_info_v1(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;
fc::raw::unpack(persist_file, pubkey);
fc::raw::unpack(persist_file, fsi);
res.emplace(pubkey, fsi);
}
}

my_finalizers_t::fsi_map my_finalizers_t::load_finalizer_safety_info() {
fsi_map res;

EOS_ASSERT(!persist_file_path.empty(), finalizer_safety_exception,
"path for storing finalizer safety persistence file not specified");
EOS_ASSERT(!persist_file.is_open(), finalizer_safety_exception,
EOS_ASSERT(!cfile_ds.is_open(), finalizer_safety_exception,
"Trying to read an already open finalizer safety persistence file: ${p}",
("p", persist_file_path));

Expand All @@ -225,11 +273,11 @@ my_finalizers_t::fsi_map my_finalizers_t::load_finalizer_safety_info() {
return res;
}

persist_file.set_file_path(persist_file_path);
cfile_ds.set_file_path(persist_file_path);

try {
// if we can't open the finalizer safety file, we return an empty map.
persist_file.open(fc::cfile::update_rw_mode);
cfile_ds.open(fc::cfile::update_rw_mode);
} catch(std::exception& e) {
fc_elog(vote_logger, "unable to open finalizer safety persistence file ${p}, using defaults. Exception: ${e}",
("p", persist_file_path)("e", e.what()));
Expand All @@ -239,7 +287,7 @@ my_finalizers_t::fsi_map my_finalizers_t::load_finalizer_safety_info() {
return res;
}
try {
persist_file.seek(0);
persist_file.seekp(0);

// read magic number. must be `fsi_t::magic`
// -----------------------------------------
Expand All @@ -257,17 +305,30 @@ my_finalizers_t::fsi_map my_finalizers_t::load_finalizer_safety_info() {

// finally read the `finalizer_safety_information` info
// ----------------------------------------------------
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;
fsi_t fsi;
fc::raw::unpack(persist_file, pubkey);
fc::raw::unpack(persist_file, fsi);
res.emplace(pubkey, fsi);
bool verify_checksum = true;
switch (file_version) {
case safety_file_version_0:
load_finalizer_safety_info_v0(res);
verify_checksum = false;
break;
case safety_file_version_1:
load_finalizer_safety_info_v1(res);
break;
default:
assert(0);
}

persist_file.close();
if (verify_checksum) {
// verify checksum
uint32_t calculated_checksum = persist_file.checksum();
uint32_t cs = 0;
fc::raw::unpack(persist_file, cs);
EOS_ASSERT(cs == calculated_checksum, finalizer_safety_exception,
"bad checksum reading finalizer safety persistence file: ${p}", ("p", persist_file_path));
}

// close file after write
cfile_ds.close();
} FC_LOG_AND_RETHROW()
// don't remove file we can't load
return res;
Expand Down Expand Up @@ -325,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));
3 changes: 2 additions & 1 deletion libraries/chain/include/eosio/chain/block_handle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct block_handle {

bool is_valid() const { return !_bsp.valueless_by_exception() && std::visit([](const auto& bsp) { return !!bsp; }, _bsp); }

uint32_t block_num() const { return std::visit([](const auto& bsp) { return bsp->block_num(); }, _bsp); }
block_num_type block_num() const { return std::visit([](const auto& bsp) { return bsp->block_num(); }, _bsp); }
block_num_type irreversible_blocknum() const { return std::visit([](const auto& bsp) { return bsp->irreversible_blocknum(); }, _bsp); }
block_timestamp_type timestamp() const { return std::visit([](const auto& bsp) { return bsp->timestamp(); }, _bsp); };
time_point block_time() const { return std::visit([](const auto& bsp) { return time_point{bsp->timestamp()}; }, _bsp); };
const block_id_type& id() const { return std::visit<const block_id_type&>([](const auto& bsp) -> const block_id_type& { return bsp->id(); }, _bsp); }
Expand Down
8 changes: 1 addition & 7 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ namespace eosio::chain {
uint32_t sig_cpu_bill_pct = chain::config::default_sig_cpu_bill_pct;
uint16_t chain_thread_pool_size = chain::config::default_controller_thread_pool_size;
uint16_t vote_thread_pool_size = 0;
int32_t max_reversible_blocks = chain::config::default_max_reversible_blocks;
bool read_only = false;
bool force_all_checks = false;
bool disable_replay_opts = false;
Expand Down Expand Up @@ -392,15 +391,10 @@ namespace eosio::chain {

db_read_mode get_read_mode()const;
validation_mode get_validation_mode()const;
/// @return true if terminate-at-block reached, or max-reversible-blocks reached
/// @return true if terminate-at-block reached
/// not-thread-safe
bool should_terminate() const;

/// Difference between max-reversible-blocks and fork database size.
/// Can return MAX_INT32 if disabled or pre-Savanna
/// @return the number of reversible blocks still allowed
int32_t max_reversible_blocks_allowed() const;

void set_subjective_cpu_leeway(fc::microseconds leeway);
std::optional<fc::microseconds> get_subjective_cpu_leeway() const;
void set_greylist_limit( uint32_t limit );
Expand Down
Loading

0 comments on commit cca8ee1

Please sign in to comment.