Skip to content

Commit

Permalink
Merge pull request #2085 from AntelopeIO/GH-2045-if-transision
Browse files Browse the repository at this point in the history
IF: Transition from dpos to instant-finality
  • Loading branch information
heifner authored Jan 16, 2024
2 parents 70b9797 + 852f69e commit cbb6b28
Show file tree
Hide file tree
Showing 30 changed files with 391 additions and 297 deletions.
2 changes: 1 addition & 1 deletion docs/block_production/lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ flowchart TD
direction TB
start -- "stage = Ø" --> sb
sb("start_block()"):::fun -- "stage = building_block" --> et
et["execute transactions" ] -- "stage = building_block" --> fb("finalize_block()"):::fun
et["execute transactions" ] -- "stage = building_block" --> fb("finish_block()"):::fun
fb -- "stage = assembled block" --> cb["add transaction metadata and create completed block"]
cb -- "stage = completed block" --> commit("commit_block() (where we [maybe] add to fork_db and mark valid)"):::fun
Expand Down
7 changes: 4 additions & 3 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ block_header_state block_header_state::next(block_header_state_input& input) con

if(!proposer_policies.empty()) {
auto it = proposer_policies.begin();
if (it->first <= input.timestamp) {
// -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;
Expand All @@ -108,7 +109,7 @@ block_header_state block_header_state::next(block_header_state_input& input) con

if (input.new_proposer_policy) {
// called when assembling the block
result.proposer_policies[result.header.timestamp] = input.new_proposer_policy;
result.proposer_policies[input.new_proposer_policy->active_time] = input.new_proposer_policy;
}

// finalizer policy
Expand Down Expand Up @@ -181,7 +182,7 @@ block_header_state block_header_state::next(const signed_block_header& h, const

// retrieve instant_finality_extension data from block header extension
// --------------------------------------------------------------------
EOS_ASSERT(exts.count(instant_finality_extension::extension_id() > 0), misc_exception,
EOS_ASSERT(exts.count(instant_finality_extension::extension_id()) > 0, invalid_block_header_extension,
"Instant Finality Extension is expected to be present in all block headers after switch to IF");
auto if_entry = exts.lower_bound(instant_finality_extension::extension_id());
auto& if_ext = std::get<instant_finality_extension>(if_entry->second);
Expand Down
25 changes: 24 additions & 1 deletion libraries/chain/block_state.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include <eosio/chain/block_state.hpp>
#include <eosio/chain/block_header_state_utils.hpp>
#include <eosio/chain/block_state_legacy.hpp>
#include <eosio/chain/exceptions.hpp>

namespace eosio::chain {
Expand All @@ -16,7 +17,29 @@ block_state::block_state(const block_header_state& bhs, deque<transaction_metada
, block(std::make_shared<signed_block>(signed_block_header{bhs.header})) // [greg todo] do we need signatures?
, pub_keys_recovered(true) // probably not needed
, cached_trxs(std::move(trx_metas))
{}
{
block->transactions = std::move(trx_receipts);
}

// Used for transition from dpos to instant-finality
block_state::block_state(const block_state_legacy& bsp) {
block_header_state::id = bsp.id();
header = bsp.header;
activated_protocol_features = bsp.activated_protocol_features;
std::optional<block_header_extension> ext = bsp.block->extract_header_extension(instant_finality_extension::extension_id());
assert(ext); // required by current transition mechanism
const auto& if_extension = std::get<instant_finality_extension>(*ext);
assert(if_extension.new_finalizer_policy); // required by current transition mechanism
active_finalizer_policy = std::make_shared<finalizer_policy>(*if_extension.new_finalizer_policy);
active_proposer_policy = std::make_shared<proposer_policy>();
active_proposer_policy->active_time = bsp.timestamp();
active_proposer_policy->proposer_schedule = bsp.active_schedule;
header_exts = bsp.header_exts;
block = bsp.block;
validated = bsp.is_valid();
pub_keys_recovered = bsp._pub_keys_recovered;
cached_trxs = bsp._cached_trxs;
}

deque<transaction_metadata_ptr> block_state::extract_trxs_metas() {
pub_keys_recovered = false;
Expand Down
314 changes: 176 additions & 138 deletions libraries/chain/controller.cpp

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion libraries/chain/fork_database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ namespace eosio::chain {
fork_multi_index_type index;
bsp root; // Only uses the block_header_state_legacy portion
bsp head;
std::filesystem::path datadir;
const std::filesystem::path datadir;

explicit fork_database_impl( const std::filesystem::path& data_dir ) : datadir(data_dir) {}

Expand Down Expand Up @@ -102,6 +102,11 @@ namespace eosio::chain {
my->open_impl( validator );
}

template<class bsp>
std::filesystem::path fork_database<bsp>::get_data_dir() const {
return my->datadir;
}

template<class bsp>
void fork_database_impl<bsp>::open_impl( validator_t& validator ) {
if (!std::filesystem::is_directory(datadir))
Expand Down
4 changes: 2 additions & 2 deletions libraries/chain/hotstuff/chain_pacemaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ namespace eosio::chain {
if (_active_finalizer_policy.generation == 0) {
ilog("Switching to instant finality at block ${b}", ("b", block->block_num()));
// switching from dpos to hotstuff, all nodes will switch at same block height
// block header extension is set in finalize_block to value set by host function set_finalizers
_chain->set_hs_irreversible_block_num(block->block_num()); // can be any value <= dpos lib
// block header extension is set in finish_block to value set by host function set_finalizers
_chain->set_if_irreversible_block_num(block->block_num()); // can be any value <= dpos lib
}
auto if_extension = std::get<instant_finality_extension>(*ext);
#warning Revisit after finalizer policy change design is complete as this is not necessarily when we will change active finalizer policy.
Expand Down
8 changes: 5 additions & 3 deletions libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ using block_header_state_ptr = std::shared_ptr<block_header_state>;

}

// [greg todo] which members need to be serialized to disk when saving fork_db
// obviously many are missing below.
FC_REFLECT( eosio::chain::block_header_state, (id))
FC_REFLECT( eosio::chain::block_header_state_core,
(last_final_block_num)(final_on_strong_qc_block_num)(last_qc_block_num)(finalizer_policy_generation))
FC_REFLECT( eosio::chain::block_header_state,
(id)(header)(activated_protocol_features)(core)(proposal_mtree)(finality_mtree)
(active_finalizer_policy)(active_proposer_policy)(proposer_policies)(finalizer_policies)(header_exts))
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ struct pending_block_header_state_legacy : public detail::block_header_state_leg
* start_block -> (global_property_object.proposed_schedule_block_num == dpos_lib)
* building_block._new_pending_producer_schedule = producers
*
* finalize_block ->
* finish_block ->
* block_header.extensions.wtmsig_block_signatures = producers
* block_header.new_producers = producers
*
Expand Down
10 changes: 7 additions & 3 deletions libraries/chain/include/eosio/chain/block_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@

namespace eosio::chain {

struct block_state_legacy;

struct block_state : public block_header_state { // block_header_state provides parent link
// ------ data members -------------------------------------------------------------
signed_block_ptr block;
bool validated; // We have executed the block's trxs and verified that action merkle root (block id) matches.
bool validated = false; // We have executed the block's trxs and verified that action merkle root (block id) matches.
digest_type strong_digest; // finalizer_digest (strong, cached so we can quickly validate votes)
digest_type weak_digest; // finalizer_digest (weak, cached so we can quickly validate votes)
pending_quorum_certificate pending_qc; // where we accumulate votes we receive
Expand Down Expand Up @@ -47,11 +49,13 @@ struct block_state : public block_header_state { // block_header_state provi

block_state(const block_header_state& bhs, deque<transaction_metadata_ptr>&& trx_metas,
deque<transaction_receipt>&& trx_receipts);

explicit block_state(const block_state_legacy& bsp);
};

using block_state_ptr = std::shared_ptr<block_state>;

} // namespace eosio::chain

// [greg todo] which members need to be serialized to disk when saving fork_db
FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated) )
// not exporting pending_qc or valid_qc
FC_REFLECT_DERIVED( eosio::chain::block_state, (eosio::chain::block_header_state), (block)(validated)(strong_digest)(weak_digest) )
1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/block_state_legacy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ namespace eosio::chain {
friend struct fc::reflector<block_state_legacy>;
friend struct controller_impl;
friend struct completed_block;
friend struct block_state;

bool is_pub_keys_recovered()const { return _pub_keys_recovered; }

Expand Down
51 changes: 23 additions & 28 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,23 +51,24 @@ namespace eosio::chain {
using resource_limits::resource_limits_manager;
using apply_handler = std::function<void(apply_context&)>;

template<class bsp>
using branch_type_t = fork_database<bsp>::branch_type;

using branch_type_legacy = branch_type_t<block_state_legacy_ptr>;
using branch_type = branch_type_t<block_state_ptr>;

template<class bsp>
using forked_branch_callback_t = std::function<void(const branch_type_t<bsp>&)>;

using forked_branch_callback_legacy = forked_branch_callback_t<block_state_legacy_ptr>;
using forked_branch_callback = forked_branch_callback_t<block_state_ptr>;
using forked_callback_t = std::function<void(const transaction_metadata_ptr&)>;

// lookup transaction_metadata via supplied function to avoid re-creation
using trx_meta_cache_lookup = std::function<transaction_metadata_ptr( const transaction_id_type&)>;

using block_signal_params = std::tuple<const signed_block_ptr&, const block_id_type&>;

// Created via create_block_token(const block_id_type& id, const signed_block_ptr& b)
// Valid to request id and signed_block_ptr it was created from.
// Avoid using internal block_state/block_state_legacy as those types are internal to controller.
struct block_token {
std::variant<block_state_legacy_ptr, block_state_ptr> bsp;

uint32_t block_num() const { return std::visit([](const auto& bsp) { return bsp->block_num(); }, bsp); }
block_id_type id() const { return std::visit([](const auto& bsp) { return bsp->id(); }, bsp); }
signed_block_ptr block() const { return std::visit([](const auto& bsp) { return bsp->block; }, bsp); }
};

enum class db_read_mode {
HEAD,
IRREVERSIBLE,
Expand Down Expand Up @@ -183,29 +184,25 @@ namespace eosio::chain {
fc::microseconds total_time{};
};

void finalize_block( block_report& br, const signer_callback_type& signer_callback );
void finish_block( block_report& br, const signer_callback_type& signer_callback );
void sign_block( const signer_callback_type& signer_callback );
void commit_block();

// thread-safe
std::future<block_state_legacy_ptr> create_block_state_future( const block_id_type& id, const signed_block_ptr& b );
std::future<block_token> create_block_token_future( const block_id_type& id, const signed_block_ptr& b );
// thread-safe
block_state_legacy_ptr create_block_state( const block_id_type& id, const signed_block_ptr& b ) const;
// returns empty optional if block b is not immediately ready to be processed
std::optional<block_token> create_block_token( const block_id_type& id, const signed_block_ptr& b ) const;

/**
* @param br returns statistics for block
* @param bsp block to push
* @param bt block to push, created by create_block_token
* @param cb calls cb with forked applied transactions for each forked block
* @param trx_lookup user provided lookup function for externally cached transaction_metadata
*/
void push_block( block_report& br,
const block_state_legacy_ptr& bsp,
const forked_branch_callback_legacy& cb,
const trx_meta_cache_lookup& trx_lookup );

void push_block( block_report& br,
const block_state_ptr& bsp,
const forked_branch_callback& cb,
const block_token& bt,
const forked_callback_t& cb,
const trx_meta_cache_lookup& trx_lookup );

boost::asio::io_context& get_thread_pool();
Expand Down Expand Up @@ -273,7 +270,7 @@ namespace eosio::chain {

// Called by qc_chain to indicate the current irreversible block num
// After hotstuff is activated, this should be called on startup by qc_chain
void set_hs_irreversible_block_num(uint32_t block_num);
void set_if_irreversible_block_num(uint32_t block_num);

uint32_t last_irreversible_block_num() const;
block_id_type last_irreversible_block_id() const;
Expand All @@ -284,13 +281,11 @@ namespace eosio::chain {
// thread-safe
signed_block_ptr fetch_block_by_id( const block_id_type& id )const;
// thread-safe
bool block_exists( const block_id_type& id)const;
// thread-safe
std::optional<signed_block_header> fetch_block_header_by_number( uint32_t block_num )const;
// thread-safe
std::optional<signed_block_header> fetch_block_header_by_id( const block_id_type& id )const;
// return block_state_legacy from forkdb, thread-safe
block_state_legacy_ptr fetch_block_state_by_number( uint32_t block_num )const;
// return block_state_legacy from forkdb, thread-safe
block_state_legacy_ptr fetch_block_state_by_id( block_id_type id )const;
// thread-safe
block_id_type get_block_id_for_num( uint32_t block_num )const;

Expand Down
2 changes: 2 additions & 0 deletions libraries/chain/include/eosio/chain/fork_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ namespace eosio::chain {
explicit fork_database( const std::filesystem::path& data_dir );
~fork_database();

std::filesystem::path get_data_dir() const;

void open( validator_t& validator );
void close();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,9 @@ class unapplied_transaction_queue {
}
}

template<class BRANCH_TYPE>
void add_forked( const BRANCH_TYPE& forked_branch ) {
// forked_branch is in reverse order
for( auto ritr = forked_branch.rbegin(), rend = forked_branch.rend(); ritr != rend; ++ritr ) {
const auto& bsptr = *ritr;
for( auto itr = bsptr->trxs_metas().begin(), end = bsptr->trxs_metas().end(); itr != end; ++itr ) {
const auto& trx = *itr;
auto insert_itr = queue.insert( { trx, trx_enum_type::forked } );
if( insert_itr.second ) added( insert_itr.first );
}
}
void add_forked( const transaction_metadata_ptr& trx ) {
auto insert_itr = queue.insert( { trx, trx_enum_type::forked } );
if( insert_itr.second ) added( insert_itr.first );
}

void add_aborted( deque<transaction_metadata_ptr> aborted_trxs ) {
Expand Down
6 changes: 3 additions & 3 deletions libraries/chain/resource_limits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void resource_limits_manager::set_block_parameters(const elastic_limit_parameter
c.cpu_limit_parameters = cpu_limit_parameters;
c.net_limit_parameters = net_limit_parameters;

// set_block_parameters is called by controller::finalize_block,
// set_block_parameters is called by controller::finish_block,
// where transaction specific logging is not possible
if (auto dm_logger = _get_deep_mind_logger(false)) {
dm_logger->on_update_resource_limits_config(c);
Expand Down Expand Up @@ -359,7 +359,7 @@ void resource_limits_manager::process_account_limit_updates() {
multi_index.remove(*itr);
}

// process_account_limit_updates is called by controller::finalize_block,
// process_account_limit_updates is called by controller::finish_block,
// where transaction specific logging is not possible
if (auto dm_logger = _get_deep_mind_logger(false)) {
dm_logger->on_update_resource_limits_state(state);
Expand All @@ -381,7 +381,7 @@ void resource_limits_manager::process_block_usage(uint32_t block_num) {
state.update_virtual_net_limit(config);
state.pending_net_usage = 0;

// process_block_usage is called by controller::finalize_block,
// process_block_usage is called by controller::finish,
// where transaction specific logging is not possible
if (auto dm_logger = _get_deep_mind_logger(false)) {
dm_logger->on_update_resource_limits_state(state);
Expand Down
12 changes: 6 additions & 6 deletions libraries/testing/include/eosio/testing/tester.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,9 +606,9 @@ namespace eosio { namespace testing {

signed_block_ptr produce_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
auto sb = _produce_block(skip_time, false);
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );

return sb;
}
Expand All @@ -618,17 +618,17 @@ namespace eosio { namespace testing {
}

void validate_push_block(const signed_block_ptr& sb) {
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );
}

signed_block_ptr produce_empty_block( fc::microseconds skip_time = fc::milliseconds(config::block_interval_ms) )override {
unapplied_transactions.add_aborted( control->abort_block() );
auto sb = _produce_block(skip_time, true);
auto bsf = validating_node->create_block_state_future( sb->calculate_id(), sb );
auto btf = validating_node->create_block_token_future( sb->calculate_id(), sb );
controller::block_report br;
validating_node->push_block( br, bsf.get(), {}, trx_meta_cache_lookup{} );
validating_node->push_block( br, btf.get(), {}, trx_meta_cache_lookup{} );

return sb;
}
Expand Down
Loading

0 comments on commit cbb6b28

Please sign in to comment.