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: Integrate new finality core algorithm #2245

Merged
merged 24 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ae8c0ed
Add Areg's orginal design of new final core algorithm, written by Areg
linh2931 Feb 20, 2024
03d1471
rename to finality_core.cpp
linh2931 Feb 20, 2024
1e7e5c0
update finality_core
linh2931 Feb 20, 2024
88cd179
incorporate the new finality core algorithm into Leap
linh2931 Feb 20, 2024
5f9a99d
comment out existing block_header_state_core related tests correctly
linh2931 Feb 20, 2024
b84cfad
correct check for too old QC claims by using less than last_final_blo…
linh2931 Feb 20, 2024
8d71a63
fix an links garbage collection issue
linh2931 Feb 20, 2024
c8ebbbf
revert back the change of checking late qc claim
linh2931 Feb 20, 2024
dfdbe67
fixed incorrect timestamp passed in for current_block and reverted ti…
linh2931 Feb 21, 2024
a134afe
adapt block_header_tests to new finality core data structures
linh2931 Feb 21, 2024
94e3f2c
remove block_header_state_tests.cpp as the tests there only tested ol…
linh2931 Feb 21, 2024
099bbd1
check in Areg's updated next function
linh2931 Feb 21, 2024
7516e28
minor tweaks to get Areg's updated next function to work
linh2931 Feb 21, 2024
4544791
Always check next_core.refs.front after a ref block is added; use <= …
linh2931 Feb 21, 2024
5fd3d15
move implementation of create_core_for_genesis_block from header file…
linh2931 Feb 21, 2024
723a152
remove unused code
linh2931 Feb 21, 2024
59bbd08
enable assert within finality_core.cpp even on CICD
linh2931 Feb 21, 2024
6291b59
add new updates from Areg
linh2931 Feb 22, 2024
8426788
fix minor compile errors
linh2931 Feb 22, 2024
704661e
fix timestamp
linh2931 Feb 22, 2024
912b0ea
use bb.parent.timestamp() for parent's timestamp
linh2931 Feb 22, 2024
d63eb49
remove current_block from block_header_state_input and add parent_tim…
linh2931 Feb 22, 2024
4df0a26
use qc_claim_t for the type of qc_claim
linh2931 Feb 22, 2024
45e7c24
change is_strong_qc constant in block_header_tests back to is_last_st…
linh2931 Feb 22, 2024
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
1 change: 1 addition & 0 deletions libraries/chain/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ add_library( eosio_chain
block_state.cpp
block_header_state_legacy.cpp
block_state_legacy.cpp
finality_core.cpp
fork_database.cpp
controller.cpp
authorization_manager.cpp
Expand Down
79 changes: 11 additions & 68 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,50 +22,6 @@ const vector<digest_type>& block_header_state::get_new_protocol_feature_activati

#warning Add last_proposed_finalizer_policy_generation to snapshot_block_header_state_v3, see header file TODO

block_header_state_core block_header_state_core::next(qc_claim_t incoming) const {
// no state change if last_qc_block_num is the same
if (incoming.last_qc_block_num == this->last_qc_block_num) {
return {*this};
}

EOS_ASSERT(incoming.last_qc_block_num > this->last_qc_block_num &&
incoming.last_qc_block_timestamp > this->last_qc_block_timestamp, block_validate_exception,
"new last_qc_block_num ${new} must be greater than old last_qc_block_num ${old}",
("new", incoming.last_qc_block_num)("old", this->last_qc_block_num));

auto old_last_qc_block_num = this->last_qc_block_num;
auto old_final_on_strong_qc_block_num = this->final_on_strong_qc_block_num;

block_header_state_core result{*this};

if (incoming.is_last_qc_strong) {
// last QC is strong. We can progress forward.

// block with old final_on_strong_qc_block_num becomes irreversible
if (old_final_on_strong_qc_block_num.has_value()) {
result.last_final_block_num = *old_final_on_strong_qc_block_num;
}

// next block which can become irreversible is the block with
// old last_qc_block_num
if (old_last_qc_block_num.has_value()) {
result.final_on_strong_qc_block_num = *old_last_qc_block_num;
}
} else {
// new final_on_strong_qc_block_num should not be present
result.final_on_strong_qc_block_num.reset();

// new last_final_block_num should be the same as the old last_final_block_num
}

// new last_qc_block_num is always the input last_qc_block_num.
result.last_qc_block_num = incoming.last_qc_block_num;
result.last_qc_block_timestamp = incoming.last_qc_block_timestamp;

return result;
}


block_header_state block_header_state::next(block_header_state_input& input) const {
block_header_state result;

Expand Down Expand Up @@ -125,33 +81,15 @@ block_header_state block_header_state::next(block_header_state_input& input) con
// ++input.new_finalizer_policy->generation;


qc_claim_t qc_claim;
uint16_t if_ext_id = instant_finality_extension::extension_id();

if (input.qc_claim) {
qc_claim = *input.qc_claim;
dlog("qc_claim from input -> final value: ${qci}",("qci", qc_claim));
} else {
// copy previous qc_claim if we are not provided with a new one
// ------------------------------------------------------------
auto if_entry = header_exts.lower_bound(if_ext_id);
if (if_entry != header_exts.end()) {
const auto& qci = std::get<instant_finality_extension>(if_entry->second).qc_claim;
qc_claim = qci;
dlog("qc_claim from existing extension -> final value: ${qci}",("qci",qc_claim));
} else {
assert(0); // we should always get a previous if extension when in IF mode.
}
}

instant_finality_extension new_if_ext {qc_claim,
instant_finality_extension new_if_ext {input.most_recent_ancestor_with_qc,
std::move(input.new_finalizer_policy),
std::move(input.new_proposer_policy)};

// block_header_state_core
// finality_core
// -----------------------
result.core = core.next(new_if_ext.qc_claim);
result.core = core.next(input.current_block, input.most_recent_ancestor_with_qc);

uint16_t if_ext_id = instant_finality_extension::extension_id();
emplace_extension(result.header.header_extensions, if_ext_id, fc::raw::pack(new_if_ext));
result.header_exts.emplace(if_ext_id, std::move(new_if_ext));

Expand Down Expand Up @@ -213,11 +151,16 @@ block_header_state block_header_state::next(const signed_block_header& h, const
.new_protocol_feature_activations = std::move(new_protocol_feature_activations)
};

block_ref current_block{
.block_id = block_id,
.timestamp = timestamp()
};
heifner marked this conversation as resolved.
Show resolved Hide resolved

block_header_state_input bhs_input{
bb_input, h.transaction_mroot, h.action_mroot, if_ext.new_proposer_policy, if_ext.new_finalizer_policy,
if_ext.qc_claim };
current_block, if_ext.new_qc_claim };
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved

return next(bhs_input);
}

} // namespace eosio::chain
} // namespace eosio::chain
5 changes: 2 additions & 3 deletions libraries/chain/block_header_state_legacy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,8 @@ namespace eosio::chain {
if (new_finalizer_policy) {
new_finalizer_policy->generation = 1;
// set current block_num as qc_claim.last_qc_block_num in the IF extension
qc_claim_t initial_if_claim { .last_qc_block_num = block_num,
.last_qc_block_timestamp = timestamp,
.is_last_qc_strong = false };
qc_claim initial_if_claim { .block_num = block_num,
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
.is_strong_qc = false };
emplace_extension(h.header_extensions, instant_finality_extension::extension_id(),
fc::raw::pack(instant_finality_extension{ initial_if_claim, std::move(new_finalizer_policy), {} }));
}
Expand Down
2 changes: 1 addition & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ block_state::block_state(const block_header_state& bhs, deque<transaction_metada
block_state::block_state(const block_state_legacy& bsp) {
block_header_state::block_id = bsp.id();
header = bsp.header;
core.last_final_block_num = bsp.block_num(); // [if todo] instant transition is not acceptable
core = finality_core::create_core_for_genesis_block(bsp.block_num()); // [if todo] instant transition is not acceptable
activated_protocol_features = bsp.activated_protocol_features;

auto if_ext_id = instant_finality_extension::extension_id();
Expand Down
79 changes: 47 additions & 32 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ class maybe_session {
struct qc_data_t {
std::optional<quorum_certificate> qc; // Comes either from traversing branch from parent and calling get_best_qc()
// or from an incoming block extension.
qc_claim_t qc_claim; // describes the above qc. In rare cases (bootstrap, starting from snapshot,
qc_claim current_qc_claim; // describes the above qc. In rare cases (bootstrap, starting from snapshot,
// disaster recovery), we may not have a qc so we use the `lib` block_num
// and specify `weak`.
};
Expand Down Expand Up @@ -666,22 +666,31 @@ struct building_block {
} else {
fork_db.apply_if<void>([&](const auto& forkdb) {
auto branch = forkdb.fetch_branch(parent_id());
std::optional<quorum_certificate> qc;
for( auto it = branch.begin(); it != branch.end(); ++it ) {
auto qc = (*it)->get_best_qc();
qc = (*it)->get_best_qc();
if( qc ) {
EOS_ASSERT( qc->block_num <= block_header::num_from_id(parent_id()), block_validate_exception,
"most recent ancestor QC block number (${a}) cannot be greater than parent's block number (${p})",
("a", qc->block_num)("p", block_header::num_from_id(parent_id())) );
auto qc_claim = qc_claim_t{ qc->block_num, (*it)->timestamp(), qc->qc.is_strong() };
auto claim = qc_claim { qc->block_num, qc->qc.is_strong() };
if( bb.parent.is_needed(*qc) ) {
qc_data = qc_data_t{ *qc, qc_claim };
qc_data = qc_data_t{ *qc, claim };
} else {
qc_data = qc_data_t{ {}, qc_claim };
qc_data = qc_data_t{ {}, claim };
}
break;
}
}

if (!qc) {
// This only happens when parent block is the IF genesis block.
// There is no most ancestor block which has a QC.
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
// Construct a default QC claim.
qc_data = qc_data_t{ {}, bb.parent.core.latest_qc_claim() };
heifner marked this conversation as resolved.
Show resolved Hide resolved
}
});

}

building_block_input bb_input {
Expand All @@ -691,10 +700,17 @@ struct building_block {
.new_protocol_feature_activations = new_protocol_feature_activations()
};

// Get parent block reference.
block_ref parent_block {
.block_id = parent_id(),
.timestamp = bb.parent.timestamp()
};

block_header_state_input bhs_input{
bb_input, transaction_mroot, action_mroot, std::move(bb.new_proposer_policy),
std::move(bb.new_finalizer_policy),
qc_data ? qc_data->qc_claim : std::optional<qc_claim_t>{}
parent_block,
qc_data->current_qc_claim
};

assembled_block::assembled_block_if ab{std::move(bb.active_producer_authority), bb.parent.next(bhs_input),
Expand Down Expand Up @@ -2937,9 +2953,9 @@ struct controller_impl {
auto exts = b->validate_and_extract_extensions();
if (auto entry = exts.lower_bound(quorum_certificate_extension::extension_id()); entry != exts.end()) {
auto& qc_ext = std::get<quorum_certificate_extension>(entry->second);
return qc_data_t{ std::move(qc_ext.qc), if_ext.qc_claim };
return qc_data_t{ std::move(qc_ext.qc), if_ext.new_qc_claim };
}
return qc_data_t{ {}, if_ext.qc_claim };
return qc_data_t{ {}, if_ext.new_qc_claim };
}
return {};
}
Expand Down Expand Up @@ -3139,14 +3155,14 @@ struct controller_impl {
// Save the QC. This is safe as the function is called by push_block from application thread.
bsp->valid_qc = received_qc;

// advance LIB if QC is strong and final_on_strong_qc_block_num has value
if( received_qc.is_strong() && bsp->core.final_on_strong_qc_block_num ) {
// advance LIB if QC is strong
if( received_qc.is_strong() ) {
// We evaluate a block extension qc and advance lib if strong.
// This is done before evaluating the block. It is possible the block
// will not be valid or forked out. This is safe because the block is
// just acting as a carrier of this info. It doesn't matter if the block
// is actually valid as it simply is used as a network message for this data.
set_if_irreversible_block_num(*bsp->core.final_on_strong_qc_block_num);
set_if_irreversible_block_num(bsp->core.final_on_strong_qc_block_num);
}
}

Expand Down Expand Up @@ -3185,14 +3201,14 @@ struct controller_impl {

assert(header_ext);
const auto& if_ext = std::get<instant_finality_extension>(*header_ext);
const auto qc_claim = if_ext.qc_claim;
const auto new_qc_claim = if_ext.new_qc_claim;

// If there is a header extension, but the previous block does not have a header extension,
// ensure the block does not have a QC and the QC claim of the current block has a last_qc_block_num
// ensure the block does not have a QC and the QC claim of the current block has a block_num
// of the current block’s number and that it is a claim of a weak QC. Then return early.
// -------------------------------------------------------------------------------------------------
if (!prev_header_ext) {
EOS_ASSERT( !qc_extension_present && qc_claim.last_qc_block_num == block_num && qc_claim.is_last_qc_strong == false,
EOS_ASSERT( !qc_extension_present && new_qc_claim.block_num == block_num && new_qc_claim.is_strong_qc == false,
invalid_qc_claim,
"Block #${b}, which is the finality transition block, doesn't have the expected extensions",
("b", block_num) );
Expand All @@ -3205,19 +3221,18 @@ struct controller_impl {
assert(header_ext && prev_header_ext);

const auto& prev_if_ext = std::get<instant_finality_extension>(*prev_header_ext);
const auto prev_qc_claim = prev_if_ext.qc_claim;
const auto prev_qc_claim = prev_if_ext.new_qc_claim;

// validate QC claim against previous block QC info

// new claimed QC block number cannot be smaller than previous block's
EOS_ASSERT( qc_claim.last_qc_block_num >= prev_qc_claim.last_qc_block_num &&
qc_claim.last_qc_block_timestamp >= prev_qc_claim.last_qc_block_timestamp,
EOS_ASSERT( new_qc_claim.block_num >= prev_qc_claim.block_num,
invalid_qc_claim,
"Block #${b} claims a last_qc_block_num (${n1}) less than the previous block's (${n2})",
("n1", qc_claim.last_qc_block_num)("n2", prev_qc_claim.last_qc_block_num)("b", block_num) );
"Block #${b} claims a block_num (${n1}) less than the previous block's (${n2})",
("n1", new_qc_claim.block_num)("n2", prev_qc_claim.block_num)("b", block_num) );

if( qc_claim.last_qc_block_num == prev_qc_claim.last_qc_block_num ) {
if( qc_claim.is_last_qc_strong == prev_qc_claim.is_last_qc_strong ) {
if( new_qc_claim.block_num == prev_qc_claim.block_num ) {
if( new_qc_claim.is_strong_qc == prev_qc_claim.is_strong_qc ) {
// QC block extension is redundant
EOS_ASSERT( !qc_extension_present,
invalid_qc_claim,
Expand All @@ -3230,10 +3245,10 @@ struct controller_impl {
}

// new claimed QC must be stronger than previous if the claimed block number is the same
EOS_ASSERT( qc_claim.is_last_qc_strong,
EOS_ASSERT( new_qc_claim.is_strong_qc,
invalid_qc_claim,
"claimed QC (${s1}) must be stricter than previous block's (${s2}) if block number is the same. Block number: ${b}",
("s1", qc_claim.is_last_qc_strong)("s2", prev_qc_claim.is_last_qc_strong)("b", block_num) );
("s1", new_qc_claim.is_strong_qc)("s2", prev_qc_claim.is_strong_qc)("b", block_num) );
}

// At this point, we are making a new claim in this block, so it better include a QC to justify this claim.
Expand All @@ -3245,23 +3260,23 @@ struct controller_impl {
const auto& qc_proof = qc_ext.qc;

// Check QC information in header extension and block extension match
EOS_ASSERT( qc_proof.block_num == qc_claim.last_qc_block_num,
EOS_ASSERT( qc_proof.block_num == new_qc_claim.block_num,
invalid_qc_claim,
"Block #${b}: Mismatch between qc.block_num (${n1}) in block extension and last_qc_block_num (${n2}) in header extension",
("n1", qc_proof.block_num)("n2", qc_claim.last_qc_block_num)("b", block_num) );
"Block #${b}: Mismatch between qc.block_num (${n1}) in block extension and block_num (${n2}) in header extension",
("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() == qc_claim.is_last_qc_strong,
EOS_ASSERT( qc_proof.qc.is_strong() == new_qc_claim.is_strong_qc,
invalid_qc_claim,
"QC is_strong (${s1}) in block extension does not match is_last_qc_strong (${s2}) in header extension. Block number: ${b}",
("s1", qc_proof.qc.is_strong())("s2", qc_claim.is_last_qc_strong)("b", block_num) );
"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) );

// find the claimed block's block state on branch of id
auto bsp = fork_db_fetch_bsp_by_num( prev.id(), qc_claim.last_qc_block_num );
auto bsp = fork_db_fetch_bsp_by_num( prev.id(), new_qc_claim.block_num );
EOS_ASSERT( bsp,
invalid_qc_claim,
"Block state was not found in forkdb for last_qc_block_num ${q}. Block number: ${b}",
("q", qc_claim.last_qc_block_num)("b", block_num) );
"Block state was not found in forkdb for block_num ${q}. Block number: ${b}",
("q", new_qc_claim.block_num)("b", block_num) );

// verify the QC proof against the claimed block
bsp->verify_qc(qc_proof.qc);
Expand Down
Loading
Loading