Skip to content

Commit

Permalink
Merge pull request #2245 from AntelopeIO/new_finality_core
Browse files Browse the repository at this point in the history
IF: Integrate new finality core algorithm
  • Loading branch information
linh2931 authored Feb 22, 2024
2 parents 1da365e + 45e7c24 commit 74da986
Show file tree
Hide file tree
Showing 15 changed files with 567 additions and 288 deletions.
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
83 changes: 13 additions & 70 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,19 @@ 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);
block_ref parent_block {
.block_id = input.parent_id,
.timestamp = input.parent_timestamp
};
result.core = core.next(parent_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 @@ -206,9 +148,10 @@ block_header_state block_header_state::next(const signed_block_header& h, valida
auto& if_ext = std::get<instant_finality_extension>(if_entry->second);

building_block_input bb_input{
.parent_id = block_id,
.timestamp = h.timestamp,
.producer = producer,
.parent_id = block_id,
.parent_timestamp = timestamp(),
.timestamp = h.timestamp,
.producer = producer,
.new_protocol_feature_activations = std::move(new_protocol_feature_activations)
};

Expand All @@ -219,4 +162,4 @@ block_header_state block_header_state::next(const signed_block_header& h, valida
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_t initial_if_claim { .block_num = block_num,
.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 @@ -48,7 +48,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
63 changes: 36 additions & 27 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -658,13 +658,14 @@ 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 qc_claim = qc_claim_t { qc->block_num, qc->qc.is_strong() };
if( bb.parent.is_needed(*qc) ) {
qc_data = qc_data_t{ *qc, qc_claim };
} else {
Expand All @@ -673,11 +674,20 @@ struct building_block {
break;
}
}

if (!qc) {
// This only happens when parent block is the IF genesis block.
// There is no ancestor block which has a QC.
// Construct a default QC claim.
qc_data = qc_data_t{ {}, bb.parent.core.latest_qc_claim() };
}
});

}

building_block_input bb_input {
.parent_id = parent_id(),
.parent_timestamp = bb.parent.timestamp(),
.timestamp = timestamp(),
.producer = producer(),
.new_protocol_feature_activations = new_protocol_feature_activations()
Expand All @@ -686,7 +696,7 @@ struct building_block {
block_header_state_input bhs_input{
bb_input, transaction_mroot, action_mroot, std::move(new_proposer_policy),
std::move(bb.new_finalizer_policy),
qc_data ? qc_data->qc_claim : std::optional<qc_claim_t>{}
qc_data->qc_claim
};

assembled_block::assembled_block_if ab{std::move(bb.active_producer_authority), bb.parent.next(bhs_input),
Expand Down Expand Up @@ -2555,7 +2565,7 @@ struct controller_impl {
},
[&](auto& forkdb) { // instant-finality
maybe_session session = skip_db_sessions(s) ? maybe_session() : maybe_session(db);
building_block_input bbi{forkdb.chain_head->id(), when, forkdb.chain_head->get_scheduled_producer(when).producer_name,
building_block_input bbi{forkdb.chain_head->id(), forkdb.chain_head->timestamp(), when, forkdb.chain_head->get_scheduled_producer(when).producer_name,
new_protocol_feature_activations};
pending.emplace(std::move(session), *forkdb.chain_head, bbi);
});
Expand Down Expand Up @@ -3165,14 +3175,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 @@ -3211,14 +3221,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.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 @@ -3236,14 +3246,13 @@ struct controller_impl {
// 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 @@ -3256,10 +3265,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 @@ -3271,23 +3280,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

0 comments on commit 74da986

Please sign in to comment.