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: Support load of fork database after instant-finality is enabled #2113

Merged
merged 12 commits into from
Jan 21, 2024
5 changes: 5 additions & 0 deletions libraries/chain/block_header_state.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@

namespace eosio::chain {

// moved this warning out of header so it only uses once
#warning TDDO https://github.com/AntelopeIO/leap/issues/2080
Copy link
Member

Choose a reason for hiding this comment

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

Now it occurs to me, if we already have an issue, we don't need a warning, because the issue will remind us.

// digest_type compute_finalizer_digest() const { return id; };


producer_authority block_header_state::get_scheduled_producer(block_timestamp_type t) const {
return detail::get_scheduled_producer(active_proposer_policy->proposer_schedule.producers, t);
}
Expand Down
746 changes: 333 additions & 413 deletions libraries/chain/controller.cpp

Large diffs are not rendered by default.

223 changes: 125 additions & 98 deletions libraries/chain/fork_database.cpp

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion libraries/chain/include/eosio/chain/block_header_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ struct block_header_state {


// ------ functions -----------------------------------------------------------------
#warning TDDO https://github.com/AntelopeIO/leap/issues/2080
digest_type compute_finalizer_digest() const { return id; };
block_timestamp_type timestamp() const { return header.timestamp; }
account_name producer() const { return header.producer; }
Expand Down
4 changes: 1 addition & 3 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,6 @@ namespace eosio::chain {

const chainbase::database& db()const;

const fork_database_legacy& fork_db()const;

const account_object& get_account( account_name n )const;
const global_property_object& get_global_properties()const;
const dynamic_global_property_object& get_dynamic_global_properties()const;
Expand Down Expand Up @@ -249,7 +247,7 @@ namespace eosio::chain {
block_state_legacy_ptr head_block_state_legacy()const;

uint32_t fork_db_head_block_num()const;
const block_id_type& fork_db_head_block_id()const;
block_id_type fork_db_head_block_id()const;

time_point pending_block_time()const;
block_timestamp_type pending_block_timestamp()const;
Expand Down
97 changes: 81 additions & 16 deletions libraries/chain/include/eosio/chain/fork_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,33 +11,33 @@ namespace eosio::chain {
struct fork_database_impl;

/**
* @class fork_database
* @class fork_database_t
* @brief manages light-weight state for all potential unconfirmed forks
*
* As new blocks are received, they are pushed into the fork database. The fork
* database tracks the longest chain and the last irreversible block number. All
* blocks older than the last irreversible block are freed after emitting the
* irreversible signal.
*
* An internal mutex is used to provide thread-safety.
* Not thread safe, thread safety provided by fork_database below.
*/
template<class bsp> // either block_state_legacy_ptr or block_state_ptr
class fork_database {
class fork_database_t {
public:
static constexpr uint32_t legacy_magic_number = 0x30510FDB;
static constexpr uint32_t magic_number = 0x4242FDB;
linh2931 marked this conversation as resolved.
Show resolved Hide resolved

using bs = bsp::element_type;
using bhsp = bs::bhsp_t;
using bhs = bhsp::element_type;
using bsp_t = bsp;
using branch_type = deque<bsp>;
using branch_type_pair = pair<branch_type, branch_type>;

explicit fork_database( const std::filesystem::path& data_dir );
~fork_database();
explicit fork_database_t(uint32_t magic_number = legacy_magic_number);

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

void open( validator_t& validator );
void close();
void open( const std::filesystem::path& fork_db_file, validator_t& validator );
void close( const std::filesystem::path& fork_db_file );

bhsp get_block_header( const block_id_type& id ) const;
bsp get_block( const block_id_type& id ) const;
Expand Down Expand Up @@ -70,6 +70,9 @@ namespace eosio::chain {
bsp head() const;
bsp pending_head() const;

// only accessed by main thread, no mutex protection
bsp chain_head;

/**
* Returns the sequence of block states resulting from trimming the branch from the
* root block (exclusive) to the block with an id of `h` (inclusive) by removing any
Expand All @@ -95,15 +98,77 @@ namespace eosio::chain {

void mark_valid( const bsp& h );

static const uint32_t magic_number;

static const uint32_t min_supported_version;
static const uint32_t max_supported_version;

private:
unique_ptr<fork_database_impl<bsp>> my;
};

using fork_database_legacy = fork_database<block_state_legacy_ptr>;

using fork_database_legacy_t = fork_database_t<block_state_legacy_ptr>;
using fork_database_if_t = fork_database_t<block_state_ptr>;

/**
* Provides thread safety on fork_database_t and provide mechanism for opening the correct type
* as well as switching from legacy to instant-finality.
*/
class fork_database {
mutable std::recursive_mutex m;
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not a fan of recursive mutexes. However, currently controller uses nested calls of fork_database.apply. We can look at refactoring those so that no apply calls functions which also calls apply but that will take some work.

const std::filesystem::path data_dir;
std::variant<fork_database_t<block_state_legacy_ptr>, fork_database_t<block_state_ptr>> v;
public:
explicit fork_database(const std::filesystem::path& data_dir);
~fork_database(); // close on destruction

void open( validator_t& validator );

void switch_from_legacy();

// see fork_database_t::fetch_branch(forkdb->head()->id())
std::vector<signed_block_ptr> fetch_branch_from_head();

template <class R, class F>
R apply(const F& f) {
std::lock_guard g(m);
if constexpr (std::is_same_v<void, R>)
std::visit([&](auto& forkdb) { f(forkdb); }, v);
else
return std::visit([&](auto& forkdb) -> R { return f(forkdb); }, v);
}

template <class R, class F>
R apply_if(const F& f) {
if constexpr (std::is_same_v<void, R>)
std::visit(overloaded{[&](fork_database_legacy_t&) {},
[&](fork_database_if_t& forkdb) {
std::lock_guard g(m);
f(forkdb);
}}, v);
else
return std::visit(overloaded{[&](fork_database_legacy_t&) -> R { return {}; },
[&](fork_database_if_t& forkdb) -> R {
std::lock_guard g(m);
return f(forkdb);
}}, v);
}

template <class R, class F>
R apply_dpos(const F& f) {
if constexpr (std::is_same_v<void, R>)
std::visit(overloaded{[&](fork_database_legacy_t& forkdb) {
std::lock_guard g(m);
f(forkdb);
},
[&](fork_database_if_t&) {}}, v);
else
return std::visit(overloaded{[&](fork_database_legacy_t& forkdb) -> R {
std::lock_guard g(m);
return f(forkdb);
},
[&](fork_database_if_t&) -> R {
return {};
}}, v);
}

// if we every support more than one version then need to save min/max in fork_database_t
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment doesn't read right. Maybe this

Suggested change
// if we every support more than one version then need to save min/max in fork_database_t
// if we ever support more than one version, then we need to save min/max in fork_database_t

static constexpr uint32_t min_supported_version = 1;
static constexpr uint32_t max_supported_version = 1;
};
} /// eosio::chain
9 changes: 4 additions & 5 deletions programs/leap-util/actions/blocklog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,18 +266,17 @@ int blocklog_actions::read_log() {
opt->first_block = block_logger.first_block_num();
}

using fork_database_t = fork_database_legacy; // [greg todo] what is it is not a legacy fork_db?
fork_database_t::branch_type fork_db_branch;
std::vector<signed_block_ptr> fork_db_branch;

if(std::filesystem::exists(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name / config::forkdb_filename)) {
ilog("opening fork_db");
fork_database_t fork_db(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name);
fork_database fork_db(std::filesystem::path(opt->blocks_dir) / config::reversible_blocks_dir_name);

fork_db.open([](block_timestamp_type timestamp,
const flat_set<digest_type>& cur_features,
const vector<digest_type>& new_features) {});

fork_db_branch = fork_db.fetch_branch(fork_db.head()->id());
fork_db_branch = fork_db.fetch_branch_from_head();
if(fork_db_branch.empty()) {
elog("no blocks available in reversible block database: only block_log blocks are available");
} else {
Expand Down Expand Up @@ -336,7 +335,7 @@ int blocklog_actions::read_log() {
for(auto bitr = fork_db_branch.rbegin(); bitr != fork_db_branch.rend() && block_num <= opt->last_block; ++bitr) {
if(opt->as_json_array && contains_obj)
*out << ",";
auto next = (*bitr)->block;
auto& next = *bitr;
print_block(next);
++block_num;
contains_obj = true;
Expand Down
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ set_property(TEST nodeos_chainbase_allocation_test PROPERTY LABELS nonparalleliz
add_test(NAME nodeos_startup_catchup_lr_test COMMAND tests/nodeos_startup_catchup.py -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST nodeos_startup_catchup_lr_test PROPERTY LABELS long_running_tests)

add_test(NAME nodeos_startup_catchup_if_lr_test COMMAND tests/nodeos_startup_catchup.py -p3 --activate-if -v ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST nodeos_startup_catchup_if_lr_test PROPERTY LABELS long_running_tests)

add_test(NAME nodeos_short_fork_take_over_test COMMAND tests/nodeos_short_fork_take_over_test.py -v --wallet-port 9905 ${UNSHARE} WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_property(TEST nodeos_short_fork_take_over_test PROPERTY LABELS nonparallelizable_tests)

Expand Down
5 changes: 3 additions & 2 deletions tests/nodeos_startup_catchup.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
extraArgs = appArgs.add(flag="--catchup-count", type=int, help="How many catchup-nodes to launch", default=10)
extraArgs = appArgs.add(flag="--txn-gen-nodes", type=int, help="How many transaction generator nodes", default=2)
args = TestHelper.parse_args({"--dump-error-details","--keep-logs","-v","--leave-running",
"-p","--wallet-port","--unshared"}, applicationSpecificArgs=appArgs)
"--activate-if","-p","--wallet-port","--unshared"}, applicationSpecificArgs=appArgs)
Utils.Debug=args.v
pnodes=args.p if args.p > 0 else 1
startedNonProdNodes = args.txn_gen_nodes if args.txn_gen_nodes >= 2 else 2
Expand All @@ -43,6 +43,7 @@
walletPort=args.wallet_port
catchupCount=args.catchup_count if args.catchup_count > 0 else 1
totalNodes=startedNonProdNodes+pnodes+catchupCount
activateIF=args.activate_if

walletMgr=WalletMgr(True, port=walletPort)
testSuccessful=False
Expand All @@ -56,7 +57,7 @@
cluster.setWalletMgr(walletMgr)

Print("Stand up cluster")
if cluster.launch(prodCount=prodCount, onlyBios=False, pnodes=pnodes, totalNodes=totalNodes, totalProducers=pnodes*prodCount,
if cluster.launch(prodCount=prodCount, activateIF=activateIF, onlyBios=False, pnodes=pnodes, totalNodes=totalNodes, totalProducers=pnodes*prodCount,
unstartedNodes=catchupCount, loadSystemContract=True,
maximumP2pPerHost=totalNodes+trxGeneratorCnt) is False:
Utils.errorExit("Failed to stand up eos cluster.")
Expand Down
2 changes: 1 addition & 1 deletion unittests/unapplied_transaction_queue_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ auto create_test_block_state( deque<transaction_metadata_ptr> trx_metas ) {
return bsp;
}

using branch_type_legacy = fork_database<block_state_legacy_ptr>::branch_type;
using branch_type_legacy = fork_database_t<block_state_legacy_ptr>::branch_type;

template<class BRANCH_TYPE>
void add_forked( unapplied_transaction_queue& queue, const BRANCH_TYPE& forked_branch ) {
Expand Down