diff --git a/libraries/chain/block_header_state.cpp b/libraries/chain/block_header_state.cpp index 3677d1d48c..6f35f1ae85 100644 --- a/libraries/chain/block_header_state.cpp +++ b/libraries/chain/block_header_state.cpp @@ -43,17 +43,15 @@ digest_type block_header_state::compute_base_digest() const { } digest_type block_header_state::compute_finality_digest() const { - assert(active_finalizer_policy); - auto active_finalizer_policy_digest = fc::sha256::hash(*active_finalizer_policy); auto base_digest = compute_base_digest(); + std::pair last_pending_and_base{ last_pending_finalizer_policy_digest, base_digest }; + auto lpfp_base_digest = fc::sha256::hash(last_pending_and_base); - std::pair active_and_base{ active_finalizer_policy_digest, base_digest }; - auto afp_base_digest = fc::sha256::hash(active_and_base); - + assert(active_finalizer_policy); finality_digest_data_v1 finality_digest_data { .active_finalizer_policy_generation = active_finalizer_policy->generation, .finality_tree_digest = finality_mroot(), - .active_finalizer_policy_and_base_digest = afp_base_digest + .last_pending_finalizer_policy_and_base_digest = lpfp_base_digest }; return fc::sha256::hash(finality_digest_data); @@ -67,7 +65,7 @@ const vector& block_header_state::get_new_protocol_feature_activati return detail::get_new_protocol_feature_activations(header_exts); } -// The last proposed finalizer policy if none proposed or pending is the active finalizer policy +// The last proposed finalizer policy if none proposed or pending then the active finalizer policy const finalizer_policy& block_header_state::get_last_proposed_finalizer_policy() const { if (!finalizer_policies.empty()) { for (auto ritr = finalizer_policies.rbegin(); ritr != finalizer_policies.rend(); ++ritr) { @@ -79,6 +77,31 @@ const finalizer_policy& block_header_state::get_last_proposed_finalizer_policy() return *active_finalizer_policy; } +// The last pending finalizer policy if none pending then the active finalizer policy +// Used to populate last_pending_finalizer_policy_digest which is expected to be the highest generation pending +const finalizer_policy& block_header_state::get_last_pending_finalizer_policy() const { + if (!finalizer_policies.empty()) { + // lambda only used when asserts enabled + [[maybe_unused]] auto highest_pending_generation = [this]() { + finalizer_policy_ptr highest; + for (auto ritr = finalizer_policies.rbegin(); ritr != finalizer_policies.rend(); ++ritr) { + if (ritr->second.state == finalizer_policy_tracker::state_t::pending) { + if (!highest || highest->generation < ritr->second.policy->generation) + highest = ritr->second.policy; + } + } + return highest; + }; + for (auto ritr = finalizer_policies.rbegin(); ritr != finalizer_policies.rend(); ++ritr) { + if (ritr->second.state == finalizer_policy_tracker::state_t::pending) { + assert(highest_pending_generation() == ritr->second.policy); + return *ritr->second.policy; + } + } + } + return *active_finalizer_policy; +} + // The last proposed proposer policy, if none proposed then the active proposer policy const proposer_policy& block_header_state::get_last_proposed_proposer_policy() const { if (proposer_policies.empty()) { @@ -207,6 +230,8 @@ void finish_next(const block_header_state& prev, } } + next_header_state.last_pending_finalizer_policy_digest = fc::sha256::hash(next_header_state.get_last_pending_finalizer_policy()); + if (if_ext.new_finalizer_policy_diff) { finalizer_policy new_finalizer_policy = prev.get_last_proposed_finalizer_policy().apply_diff(*if_ext.new_finalizer_policy_diff); diff --git a/libraries/chain/block_state.cpp b/libraries/chain/block_state.cpp index f33d5345f1..868b2feffa 100644 --- a/libraries/chain/block_state.cpp +++ b/libraries/chain/block_state.cpp @@ -74,6 +74,7 @@ block_state_ptr block_state::create_if_genesis_block(const block_state_legacy& b instant_finality_extension if_ext = bsp.block->extract_header_extension(); assert(if_ext.new_finalizer_policy_diff); // required by transition mechanism result.active_finalizer_policy = std::make_shared(finalizer_policy{}.apply_diff(std::move(*if_ext.new_finalizer_policy_diff))); + result.last_pending_finalizer_policy_digest = fc::sha256::hash(*result.active_finalizer_policy); result.active_proposer_policy = std::make_shared(); result.active_proposer_policy->active_time = bsp.timestamp(); result.active_proposer_policy->proposer_schedule = bsp.active_schedule; @@ -144,7 +145,8 @@ block_state::block_state(snapshot_detail::snapshot_block_state_v7&& sbs) .active_proposer_policy = std::move(sbs.active_proposer_policy), .proposer_policies = std::move(sbs.proposer_policies), .finalizer_policies = std::move(sbs.finalizer_policies), - .finalizer_policy_generation = sbs.finalizer_policy_generation + .finalizer_policy_generation = sbs.finalizer_policy_generation, + .last_pending_finalizer_policy_digest = sbs.last_pending_finalizer_policy_digest } , strong_digest(compute_finality_digest()) , weak_digest(create_weak_digest(strong_digest)) diff --git a/libraries/chain/include/eosio/chain/block_header_state.hpp b/libraries/chain/include/eosio/chain/block_header_state.hpp index d978754dc1..8735656534 100644 --- a/libraries/chain/include/eosio/chain/block_header_state.hpp +++ b/libraries/chain/include/eosio/chain/block_header_state.hpp @@ -26,7 +26,7 @@ struct finality_digest_data_v1 { uint32_t minor_version{light_header_protocol_version_minor}; uint32_t active_finalizer_policy_generation {0}; digest_type finality_tree_digest; - digest_type active_finalizer_policy_and_base_digest; + digest_type last_pending_finalizer_policy_and_base_digest; }; @@ -95,6 +95,10 @@ struct block_header_state { // It matches the finalizer policy generation most recently included in this block's `if_extension` or its ancestors uint32_t finalizer_policy_generation{1}; + // digest of the finalizer policy (which includes the generation number in it) with the greatest generation number + // in the history of the blockchain so far that is not in proposed state (so either pending or active state) + digest_type last_pending_finalizer_policy_digest; + // ------ data members caching information available elsewhere ---------------------- header_extension_multimap header_exts; // redundant with the data stored in header @@ -137,6 +141,7 @@ struct block_header_state { const producer_authority& get_scheduled_producer(block_timestamp_type t) const; const finalizer_policy& get_last_proposed_finalizer_policy() const; + const finalizer_policy& get_last_pending_finalizer_policy() const; const proposer_policy& get_last_proposed_proposer_policy() const; }; @@ -151,6 +156,6 @@ FC_REFLECT( eosio::chain::finalizer_policy_tracker, (state)(policy)) FC_REFLECT( eosio::chain::block_header_state, (block_id)(header) (activated_protocol_features)(core)(active_finalizer_policy) (active_proposer_policy)(proposer_policies)(finalizer_policies) - (finalizer_policy_generation)(header_exts)) + (finalizer_policy_generation)(last_pending_finalizer_policy_digest)(header_exts)) -FC_REFLECT( eosio::chain::finality_digest_data_v1, (major_version)(minor_version)(active_finalizer_policy_generation)(finality_tree_digest)(active_finalizer_policy_and_base_digest) ) +FC_REFLECT( eosio::chain::finality_digest_data_v1, (major_version)(minor_version)(active_finalizer_policy_generation)(finality_tree_digest)(last_pending_finalizer_policy_and_base_digest) ) diff --git a/libraries/chain/include/eosio/chain/snapshot_detail.hpp b/libraries/chain/include/eosio/chain/snapshot_detail.hpp index 58e760d03d..8deffe4311 100644 --- a/libraries/chain/include/eosio/chain/snapshot_detail.hpp +++ b/libraries/chain/include/eosio/chain/snapshot_detail.hpp @@ -119,12 +119,14 @@ namespace eosio::chain::snapshot_detail { flat_map proposer_policies; flat_multimap finalizer_policies; uint32_t finalizer_policy_generation; + digest_type last_pending_finalizer_policy_digest; // from block_state std::optional valid; snapshot_block_state_v7() = default; + // When adding a member initialization here also update block_state(snapshot_block_state_v7) constructor explicit snapshot_block_state_v7(const block_state& bs) : block_id(bs.block_id) , header(bs.header) @@ -135,6 +137,7 @@ namespace eosio::chain::snapshot_detail { , proposer_policies(bs.proposer_policies) , finalizer_policies(bs.finalizer_policies) , finalizer_policy_generation(bs.finalizer_policy_generation) + , last_pending_finalizer_policy_digest(bs.last_pending_finalizer_policy_digest) , valid(bs.valid) {} }; @@ -215,6 +218,7 @@ FC_REFLECT( eosio::chain::snapshot_detail::snapshot_block_state_v7, (proposer_policies) (finalizer_policies) (finalizer_policy_generation) + (last_pending_finalizer_policy_digest) (valid) ) diff --git a/unittests/svnn_ibc_tests.cpp b/unittests/svnn_ibc_tests.cpp index 477c945d33..e6cb492c50 100644 --- a/unittests/svnn_ibc_tests.cpp +++ b/unittests/svnn_ibc_tests.cpp @@ -108,8 +108,8 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) BOOST_CHECK_EQUAL(active_finalizer_policy.generation, 1u); - // compute the digest of the finalizer policy - auto active_finalizer_policy_digest = fc::sha256::hash(active_finalizer_policy); + // compute the digest of the last_pending_finalizer_policy_digest which is active at this point + auto last_pending_finalizer_policy_digest = fc::sha256::hash(active_finalizer_policy); auto genesis_block_fd = cluster.node0.control->head_finality_data(); @@ -118,12 +118,12 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) // compute IF finality leaf auto genesis_base_digest = genesis_block_fd.value().base_digest; - auto genesis_afp_base_digest = hash_pair(active_finalizer_policy_digest, genesis_base_digest); + auto genesis_afp_base_digest = hash_pair(last_pending_finalizer_policy_digest, genesis_base_digest); auto genesis_block_finality_digest = fc::sha256::hash(eosio::chain::finality_digest_data_v1{ .active_finalizer_policy_generation = active_finalizer_policy.generation, .finality_tree_digest = digest_type(), //nothing to finalize yet - .active_finalizer_policy_and_base_digest = genesis_afp_base_digest + .last_pending_finalizer_policy_and_base_digest = genesis_afp_base_digest }); // action_mroot computed using the post-IF activation merkle tree rules @@ -171,7 +171,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) auto block_2_action_mroot = block_2_fd.value().action_mroot; auto block_2_base_digest = block_2_fd.value().base_digest; auto block_2_finality_digest = cluster.node0.control->get_strong_digest_by_id(block_2->calculate_id()); - auto block_2_afp_base_digest = hash_pair(active_finalizer_policy_digest, block_2_base_digest); + auto block_2_afp_base_digest = hash_pair(last_pending_finalizer_policy_digest, block_2_base_digest); auto block_2_leaf = fc::sha256::hash(valid_t::finality_leaf_node_t{ .block_num = block_2->block_num(), .finality_digest = block_2_finality_digest, @@ -196,7 +196,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) cluster.process_votes(1, cluster.num_needed_for_quorum); auto block_4_fd = cluster.node0.control->head_finality_data(); auto block_4_base_digest = block_4_fd.value().base_digest; - auto block_4_afp_base_digest = hash_pair(active_finalizer_policy_digest, block_4_base_digest); + auto block_4_afp_base_digest = hash_pair(last_pending_finalizer_policy_digest, block_4_base_digest); auto block_4_finality_root = block_4->action_mroot; qc_data_t qc_b_4 = extract_qc_data(block_4); @@ -209,7 +209,7 @@ BOOST_AUTO_TEST_SUITE(svnn_ibc) cluster.process_votes(1, cluster.num_needed_for_quorum); auto block_5_fd = cluster.node0.control->head_finality_data(); auto block_5_base_digest = block_5_fd.value().base_digest; - auto block_5_afp_base_digest = hash_pair(active_finalizer_policy_digest, block_5_base_digest); + auto block_5_afp_base_digest = hash_pair(last_pending_finalizer_policy_digest, block_5_base_digest); auto block_5_finality_root = block_5->action_mroot; // retrieve the QC over block_4 that is contained in block_5