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: Implement computing finality digest #2282

Merged
merged 71 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
4d02cf5
remove unnecessary proposal_mtree and finality_mtree from block_heade…
linh2931 Mar 4, 2024
f7bc026
add finality_mroot() to block_header_state.hpp and update the comment…
linh2931 Mar 4, 2024
4639791
add valid structure and implement get_finality_mroot
linh2931 Mar 5, 2024
8014a38
implement build_valid_structure and add valid to block_state constructor
linh2931 Mar 5, 2024
90f87f9
add finality_mroot_claim
linh2931 Mar 5, 2024
f0c7501
add validating_finality_mroot_claim as an optional parameter to assem…
linh2931 Mar 5, 2024
14b1173
implement compute_finalizer_digest()
linh2931 Mar 5, 2024
f02f113
verify finality_mroot claim
linh2931 Mar 5, 2024
1f77ef2
remove unnecessary block_num from valid structure
linh2931 Mar 5, 2024
57bf066
rename finality_tree to finality_merkel_tree, validation_tree_roots t…
linh2931 Mar 5, 2024
2260fd5
use a meaning full name for parent_bsp instead of it
linh2931 Mar 5, 2024
f374b90
do not calculate core again if it is already calculated as updated_core
linh2931 Mar 5, 2024
5f83347
fix missing variable definitions in the existing EOS_ASSERT for Block…
linh2931 Mar 5, 2024
1e8d557
do not fetch parent_bsp again if it is available
linh2931 Mar 5, 2024
d3dacfe
add more comments and some minor clean up
linh2931 Mar 5, 2024
472b3e2
update forked_tests/fork_with_bad_block to match corrected exception …
linh2931 Mar 5, 2024
b79e173
remove a line of code added for debugging
linh2931 Mar 5, 2024
af45a31
stop calculating action_mroot in Savanna
linh2931 Mar 6, 2024
ec04b34
construct valid structure for Savanna genesis block properly
linh2931 Mar 6, 2024
c9f9611
Merge branch 'hotstuff_integration' into compute_finality_digest
linh2931 Mar 6, 2024
a9bb0b3
use new version fork_db::get_block for getting block state on root
linh2931 Mar 6, 2024
6824c2b
add missing valid in FC_REFLECT
linh2931 Mar 7, 2024
ef8c82a
rename finality_mroot to action_mroot in finality_leaf_node_t
linh2931 Mar 7, 2024
87fb8b0
do not perform validation again if finality mroot was already validated
linh2931 Mar 7, 2024
32ee804
use action_mroot in leaf node correctly
linh2931 Mar 7, 2024
8918588
Merge branch 'hotstuff_integration' into compute_finality_digest
linh2931 Mar 7, 2024
de0b91a
fix a compile error due to merging latest target branch
linh2931 Mar 7, 2024
700b3ed
use chain_head as the parent of the block being built
linh2931 Mar 7, 2024
35a9227
cache action_mroot in assembled_block to be reused in apply_block; ma…
linh2931 Mar 8, 2024
42fc370
add finality_tree_leaf_version
linh2931 Mar 8, 2024
28ccc0b
make action_mroot parameter in build_valid_structure as a const &
linh2931 Mar 8, 2024
569de69
make building next valid structure as a member function of valid
linh2931 Mar 8, 2024
200764f
In building_block_if, change parent's type from block_header_state to…
linh2931 Mar 8, 2024
367924b
use parent from building block instead of getting from fork_db in new…
linh2931 Mar 8, 2024
3260dfc
do not reuse updated_core
linh2931 Mar 8, 2024
1eec92f
add is_genesis_block_num() to finality_core
linh2931 Mar 9, 2024
bce5309
remove block_num from finality_mroot_claim (only containing finality_…
linh2931 Mar 9, 2024
e630a61
controller.cpp
linh2931 Mar 9, 2024
6cf0d83
Revert "controller.cpp"
linh2931 Mar 9, 2024
ebcdb9e
move creation of block_state valid structure from apply_block to asse…
linh2931 Mar 9, 2024
74a4eff
update validation tree and finality mroot according to a slightly cha…
linh2931 Mar 9, 2024
2e8956b
add light_header_protocol_version_major and light_header_protocol_ver…
linh2931 Mar 9, 2024
b1a81e7
recaculate action_mroot in non-canonical-format for IF genesis block …
linh2931 Mar 11, 2024
5546215
remove unused updated_core
linh2931 Mar 11, 2024
20cc780
make get_qc_data a function, do not use optional for qc_data and fina…
linh2931 Mar 11, 2024
8cd4b8a
Merge branch 'hotstuff_integration' into compute_finality_digest
linh2931 Mar 11, 2024
f15df1d
remove proposal_mtree and finality_mtree from snapshot V7 due to merg…
linh2931 Mar 11, 2024
c86a8a6
change non_canonical_action_mroot to action_mroot_svnn
linh2931 Mar 11, 2024
e4ab6a7
Merge branch 'hotstuff_integration' into compute_finality_digest
linh2931 Mar 12, 2024
fab8b40
refactor to make code more concise
linh2931 Mar 12, 2024
d91dcdd
Merge branch 'hotstuff_integration' into compute_finality_digest
linh2931 Mar 12, 2024
21e23ca
passing parent instead of building_block to get_qc_data
linh2931 Mar 12, 2024
cec7777
Include `block_state::valid` in the snapshot.
greg7mdp Mar 12, 2024
6d5a1fa
Merge pull request #2303 from AntelopeIO/compute_finality_digest_snap…
greg7mdp Mar 12, 2024
2db3cfc
move get_finality_mroot_claim and get_most_ancestor_qc_data from cont…
linh2931 Mar 12, 2024
e194a40
make parameter in get_most_ancestor_qc_data a const&
linh2931 Mar 12, 2024
3f762c4
add is_proper_svnn_block() to block_head and use it for validation
linh2931 Mar 13, 2024
2a2cfc0
make no_finality_tree_associated and empty action_mroot validation as…
linh2931 Mar 13, 2024
229de94
make finality_mroot_claim in block_header_state non-optional
linh2931 Mar 13, 2024
be73881
compute base digest explicitly to deep serializing pointer data
linh2931 Mar 13, 2024
f3daf0c
rename proper_svnn_block_flag to proper_svnn_schedule_version
linh2931 Mar 13, 2024
a4401fd
keep genesis Savanna block's schedule_version unchanged
linh2931 Mar 13, 2024
67c20ef
make get_next_proposer_schedule_version always return the new 2^31
linh2931 Mar 13, 2024
7a57560
do not set header.schedule_version to active_proposer_policy->propose…
linh2931 Mar 13, 2024
3729f9b
move get_qc_data back to controller so that assumption about the bran…
linh2931 Mar 13, 2024
2c63198
validate schedule_version with proper_svnn_schedule_version exists at…
linh2931 Mar 13, 2024
869844c
rename compute_finalizer_digest to compute_finality_digest
linh2931 Mar 13, 2024
6878d5b
Move is_proper_svnn_block from block_header.cpp to block_header.hpp
linh2931 Mar 14, 2024
4a00109
Simplify compute_finality_digest by calculating active_finalizer_poli…
linh2931 Mar 14, 2024
65319ec
Fix indentation
linh2931 Mar 14, 2024
7f3d5e6
Rename canonical_merkle to legacy_merkle, and incremental_canonical_m…
linh2931 Mar 14, 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
6 changes: 6 additions & 0 deletions libraries/chain/block_header.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ namespace eosio { namespace chain {
return result;
}

bool block_header::is_proper_svnn_block() const {
auto exts = validate_and_extract_header_extensions();
arhag marked this conversation as resolved.
Show resolved Hide resolved
return ( exts.count(instant_finality_extension::extension_id()) > 0 &&
schedule_version == proper_svnn_schedule_version );
}

header_extension_multimap block_header::validate_and_extract_header_extensions()const {
using decompose_t = block_header_extension_types::decompose_t;

Expand Down
73 changes: 45 additions & 28 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace eosio::chain {
// header validation

// data for finality_digest
struct finality_digest_data_v0_t {
struct finality_digest_data_v1 {
uint32_t major_version{light_header_protocol_version_major};
uint32_t minor_version{light_header_protocol_version_minor};
uint32_t active_finalizer_policy_generation {0};
Expand All @@ -26,36 +26,39 @@ struct base_and_active_finalizer_policy_digest_data_t {
digest_type base_digest;
};

// data for base_digest
struct base_digest_data_t {
block_header header;
finality_core core;
flat_map<uint32_t, finalizer_policy_ptr> finalizer_policies;
proposer_policy_ptr active_proposer_policy;
flat_map<block_timestamp_type, proposer_policy_ptr> proposer_policies;
protocol_feature_activation_set_ptr activated_protocol_features;
};
// compute base_digest explicitly because of pointers involved.
digest_type block_header_state::compute_base_digest() const {
digest_type::encoder enc;

fc::raw::pack( enc, header );
fc::raw::pack( enc, core );

for (const auto& fp_pair : finalizer_policies) {
fc::raw::pack( enc, *fp_pair.second );
}
arhag marked this conversation as resolved.
Show resolved Hide resolved

fc::raw::pack( enc, *active_proposer_policy );

for (const auto& pp_pair : proposer_policies) {
fc::raw::pack( enc, *pp_pair.second );
}
arhag marked this conversation as resolved.
Show resolved Hide resolved

fc::raw::pack( enc, *activated_protocol_features );

return enc.result();
}

digest_type block_header_state::compute_finalizer_digest() const {
auto active_finalizer_policy_digest = fc::sha256::hash(*active_finalizer_policy);

base_digest_data_t base_digest_data {
.header = header,
.core = core,
.finalizer_policies = finalizer_policies,
.active_proposer_policy = active_proposer_policy,
.proposer_policies = proposer_policies,
.activated_protocol_features = activated_protocol_features
};
auto base_digest = fc::sha256::hash(base_digest_data);
auto base_digest = compute_base_digest();

base_and_active_finalizer_policy_digest_data_t b_afp_digest_data {
.active_finalizer_policy_digest = active_finalizer_policy_digest,
.base_digest = base_digest
};
heifner marked this conversation as resolved.
Show resolved Hide resolved
auto b_afp_digest = fc::sha256::hash(b_afp_digest_data);

finality_digest_data_v0_t finality_digest_data {
finality_digest_data_v1 finality_digest_data {
.active_finalizer_policy_generation = active_finalizer_policy->generation,
.finality_tree_digest = finality_mroot(),
.base_and_active_finalizer_policy_digest = b_afp_digest
Expand Down Expand Up @@ -85,8 +88,8 @@ block_header_state block_header_state::next(block_header_state_input& input) con
.confirmed = 0,
.previous = input.parent_id,
.transaction_mroot = input.transaction_mroot,
.action_mroot = input.finality_mroot_claim ? *input.finality_mroot_claim : digest_type{},
.schedule_version = header.schedule_version
.action_mroot = input.finality_mroot_claim,
.schedule_version = block_header::proper_svnn_schedule_version
};

// activated protocol features
Expand All @@ -107,8 +110,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con
// +1 since this is called after the block is built, this will be the active schedule for the next block
if (it->first.slot <= input.timestamp.slot + 1) {
result.active_proposer_policy = it->second;
result.header.schedule_version = header.schedule_version + 1;
result.active_proposer_policy->proposer_schedule.version = result.header.schedule_version;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this change means it is now possible to skip propser_schedule.version numbers. We should modify the set_proposed_producers logic so we don't skip version numbers. Need a test to cover that case so we don't break it again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does

return (--parent.proposer_policies.end())->second->proposer_schedule.version + 1;
}
assert(active_proposer_policy);
return active_proposer_policy->proposer_schedule.version + 1;
ensure the version number not being skipped?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that doesn't help. We need to make sure that if a proposer policy is scheduled for the same proposer_policy.active_time that it replaces the current one there but doesn't increment the schedule version. I think we will need to change controller::set_proposed_producers to check if there is an existing one that would have the same active_time and replace it and use its version. We should return the correct version to the contract.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to create a follow-on issue for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2313

Thanks.

result.header.schedule_version = block_header::proper_svnn_schedule_version;
arhag marked this conversation as resolved.
Show resolved Hide resolved
result.proposer_policies = { ++it, proposer_policies.end() };
} else {
result.proposer_policies = proposer_policies;
Expand Down Expand Up @@ -203,20 +205,35 @@ block_header_state block_header_state::next(const signed_block_header& h, valida
.new_protocol_feature_activations = std::move(new_protocol_feature_activations)
};

digest_type action_mroot; // digest_type default value is empty
arhag marked this conversation as resolved.
Show resolved Hide resolved

if (h.is_proper_svnn_block()) {
// if there is no Finality Tree Root associated with the block,
// then this needs to validate that h.action_mroot is the empty digest
auto next_core_metadata = core.next_metadata(if_ext.qc_claim);
auto no_finality_tree_associated = core.is_genesis_block_num(next_core_metadata.final_on_strong_qc_block_num);
arhag marked this conversation as resolved.
Show resolved Hide resolved

EOS_ASSERT( no_finality_tree_associated == h.action_mroot.empty(),
block_validate_exception,
"No Finality Tree Root associated with the block test does not match with empty action_mroot test: no finality tree associated (${n}), action_mroot empty (${e}), final_on_strong_qc_block_num (${f})",
("n", no_finality_tree_associated)("e", h.action_mroot.empty())("f", next_core_metadata.final_on_strong_qc_block_num));

action_mroot = h.action_mroot;
};

block_header_state_input bhs_input{
bb_input,
h.transaction_mroot,
if_ext.new_proposer_policy,
if_ext.new_finalizer_policy,
if_ext.qc_claim,
h.action_mroot // for finality_mroot_claim
action_mroot // for finality_mroot_claim
};

return next(bhs_input);
}

} // namespace eosio::chain

FC_REFLECT( eosio::chain::finality_digest_data_v0_t, (major_version)(minor_version)(active_finalizer_policy_generation)(finality_tree_digest)(base_and_active_finalizer_policy_digest) )
FC_REFLECT( eosio::chain::finality_digest_data_v1, (major_version)(minor_version)(active_finalizer_policy_generation)(finality_tree_digest)(base_and_active_finalizer_policy_digest) )
FC_REFLECT( eosio::chain::base_and_active_finalizer_policy_digest_data_t, (active_finalizer_policy_digest)(base_digest) )
FC_REFLECT( eosio::chain::base_digest_data_t, (header)(core)(finalizer_policies)(active_proposer_policy)(proposer_policies)(activated_protocol_features) )
22 changes: 0 additions & 22 deletions libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -297,28 +297,6 @@ digest_type block_state::get_finality_mroot_claim(const qc_claim_t& qc_claim) co
return get_validation_mroot(next_core_metadata.final_on_strong_qc_block_num);
}

qc_data_t block_state::get_most_ancestor_qc_data(const std::vector<block_state_ptr>& branch) const {
// find most recent ancestor block that has a QC by traversing the branch
for( auto bsp = branch.begin(); bsp != branch.end(); ++bsp ) {
if( auto qc = (*bsp)->get_best_qc(); qc ) {
EOS_ASSERT( qc->block_num <= block_header::num_from_id(id()), block_validate_exception,
"most recent ancestor QC block number (${a}) cannot be greater than current block number (${p})",
("a", qc->block_num)("p", block_header::num_from_id(id())) );
auto qc_claim = qc->to_qc_claim();
if( is_needed(qc_claim) ) {
return qc_data_t{ *qc, qc_claim };
} else {
// no new qc info, repeat existing
return qc_data_t{ {}, core.latest_qc_claim() };
}
}
}

// This only happens when current block is the IF genesis block or starting from snapshot.
// There is no ancestor block which has a QC. Construct a default QC claim.
return qc_data_t{ {}, core.latest_qc_claim() };
}

void inject_additional_signatures( signed_block& b, const std::vector<signature_type>& additional_signatures)
{
if (!additional_signatures.empty()) {
Expand Down
34 changes: 29 additions & 5 deletions libraries/chain/controller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,34 @@ struct building_block {
v);
}

qc_data_t get_qc_data(fork_database& fork_db, const block_state& parent) {
// find most recent ancestor block that has a QC by traversing fork db
// branch from parent

return fork_db.apply_s<qc_data_t>([&](const auto& forkdb) {
auto branch = forkdb.fetch_branch(parent.id());

for( auto it = branch.begin(); it != branch.end(); ++it ) {
if( auto qc = (*it)->get_best_qc(); 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->to_qc_claim();
if( parent.is_needed(qc_claim) ) {
return qc_data_t{ *qc, qc_claim };
} else {
// no new qc info, repeat existing
return qc_data_t{ {}, parent.core.latest_qc_claim() };
}
}
}

// This only happens when parent block is the IF genesis block or starting from snapshot.
// There is no ancestor block which has a QC. Construct a default QC claim.
return qc_data_t{ {}, parent.core.latest_qc_claim() };
});
}

assembled_block assemble_block(boost::asio::io_context& ioc,
const protocol_feature_set& pfs,
fork_database& fork_db,
Expand Down Expand Up @@ -695,11 +723,7 @@ struct building_block {
// second stage
finality_mroot_claim = validating_bsp->header.action_mroot;
} else {
auto branch = fork_db.apply_s<std::vector<block_state_ptr>>([&](const auto& forkdb) {
return forkdb.fetch_branch(bb.parent.id());
});
qc_data = bb.parent.get_most_ancestor_qc_data(branch);

qc_data = get_qc_data(fork_db, bb.parent);;
finality_mroot_claim = bb.parent.get_finality_mroot_claim(qc_data.qc_claim);
}
heifner marked this conversation as resolved.
Show resolved Hide resolved

Expand Down
6 changes: 6 additions & 0 deletions libraries/chain/include/eosio/chain/block_header.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ namespace eosio::chain {
// validation_tree(core.final_on_strong_qc_block_num).
checksum256_type action_mroot;

// Proper Savanna Block's schedule_version is 1LL << 31
static constexpr uint32_t proper_svnn_schedule_version = (1LL << 31);
heifner marked this conversation as resolved.
Show resolved Hide resolved

/**
* LEGACY SUPPORT - After enabling the wtmsig-blocks extension this field is deprecated and must be empty
*
Expand All @@ -81,6 +84,9 @@ namespace eosio::chain {
static uint32_t num_from_id(const block_id_type& id);
uint32_t protocol_version() const { return 0; }

// Returns true if the block is a Proper Savanna Block
bool is_proper_svnn_block() const;

header_extension_multimap validate_and_extract_header_extensions()const;
std::optional<block_header_extension> extract_header_extension(uint16_t extension_id)const;
};
Expand Down
11 changes: 7 additions & 4 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ struct block_header_state_input : public building_block_input {
std::shared_ptr<proposer_policy> new_proposer_policy; // Comes from building_block::new_proposer_policy
std::optional<finalizer_policy> new_finalizer_policy; // Comes from building_block::new_finalizer_policy
qc_claim_t most_recent_ancestor_with_qc; // Comes from traversing branch from parent and calling get_best_qc()
// assert(qc->block_num <= num_from_id(previous));
std::optional<digest_type> finality_mroot_claim;
digest_type finality_mroot_claim;
};

struct block_header_state {
Expand All @@ -61,7 +60,7 @@ struct block_header_state {

// ------ functions -----------------------------------------------------------------
const block_id_type& id() const { return block_id; }
const digest_type& finality_mroot() const { return header.action_mroot; }
const digest_type finality_mroot() const { return header.is_proper_svnn_block() ? header.action_mroot : digest_type{}; }
block_timestamp_type timestamp() const { return header.timestamp; }
account_name producer() const { return header.producer; }
const block_id_type& previous() const { return header.previous; }
Expand All @@ -76,7 +75,11 @@ struct block_header_state {
block_header_state next(block_header_state_input& data) const;
block_header_state next(const signed_block_header& h, validator_t& validator) const;

digest_type compute_finalizer_digest() const;
digest_type compute_base_digest() const;
digest_type compute_finalizer_digest() const;
arhag marked this conversation as resolved.
Show resolved Hide resolved

// Returns true if the block is a Proper Savanna Block
bool is_proper_svnn_block() const;

// block descending from this need the provided qc in the block extension
bool is_needed(const qc_claim_t& qc_claim) const {
Expand Down
4 changes: 0 additions & 4 deletions libraries/chain/include/eosio/chain/block_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,6 @@ struct block_state : public block_header_state { // block_header_state provi
// Returns finality_mroot_claim of the current block
digest_type get_finality_mroot_claim(const qc_claim_t& qc_claim) const;

// Returns the qc_data of the most ancestor block having a QC on the branch (including
// the current block)
qc_data_t get_most_ancestor_qc_data(const std::vector<std::shared_ptr<block_state>>& branch) const;

// vote_status
vote_status aggregate_vote(const vote_message& vote); // aggregate vote into pending_qc
void verify_qc(const valid_quorum_certificate& qc) const; // verify given qc is valid with respect block_state
Expand Down
7 changes: 2 additions & 5 deletions unittests/producer_schedule_if_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ BOOST_FIXTURE_TEST_CASE( verify_producer_schedule_after_instant_finality_activat
}

BOOST_TEST(scheduled_changed_to_new);

const auto current_schd_ver = control->head_block_header().schedule_version;
BOOST_TEST(current_schd_ver == expected_schd_ver);
};

uint32_t lib = 0;
Expand Down Expand Up @@ -150,13 +147,13 @@ BOOST_FIXTURE_TEST_CASE( proposer_policy_progression_test, validating_tester ) t
produce_blocks(config::producer_repetitions);

// sch1 must become active no later than 2 rounds but sch2 cannot become active yet
BOOST_CHECK_EQUAL( control->active_producers().version, 1u );
BOOST_CHECK_EQUAL( control->active_producers().version, 1u );
heifner marked this conversation as resolved.
Show resolved Hide resolved
BOOST_CHECK_EQUAL( true, compare_schedules( sch1, control->active_producers() ) );

produce_blocks(config::producer_repetitions);

// sch2 becomes active
BOOST_CHECK_EQUAL( control->active_producers().version, 2u );
BOOST_CHECK_EQUAL( control->active_producers().version, 2u );
heifner marked this conversation as resolved.
Show resolved Hide resolved
BOOST_CHECK_EQUAL( true, compare_schedules( sch2, control->active_producers() ) );
} FC_LOG_AND_RETHROW()

Expand Down
Loading