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

Refactor fork database #347

Merged
merged 32 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
064f238
Remove cout in unittest, replace with dlog
heifner Jul 2, 2024
93511f3
GH-333 Remove unneeded calls to forkdb->head()
heifner Jul 2, 2024
eac6bc7
GH-333 Make public controller fork_db_head* return fork db pending head
heifner Jul 2, 2024
096c3a1
GH-333 Send branch from chain_head, also rename fork_head in connecti…
heifner Jul 3, 2024
171cda4
GH-333 Use local fork_db_head_irreversible_blocknum with chain_head i…
heifner Jul 5, 2024
04945ce
GH-333 Use pending head for branch of irreversible
heifner Jul 5, 2024
ca11e84
GH-333 Use pending head during startup instead of head
heifner Jul 5, 2024
a6f1ba9
GH-333 Remove fork_db_head_or_pending
heifner Jul 5, 2024
c9de9a3
GH-333 Optimize get_info by extracting block_num from id
heifner Jul 5, 2024
44bcc6c
GH-333 Update test to use head
heifner Jul 5, 2024
13ef4f1
GH-333 Remove valid from fork choice rule. Optionally return root fro…
heifner Jul 8, 2024
a411680
GH-333 Store if_irreversible_block_id in fork database
heifner Jul 8, 2024
06b2c88
GH-333 Use fork database to track savanna irreversible block id
heifner Jul 8, 2024
aa4ba63
GH-333 Add debug log output of maybe_switch_forks
heifner Jul 8, 2024
201a1d5
GH-333 Verify correct type of block state
heifner Jul 8, 2024
3c1dfe1
GH-333 Transition valid blocks
heifner Jul 9, 2024
0d5c8c2
GH-333 Add startup tests in irreversible mode
heifner Jul 9, 2024
c60e459
GH-333 Maintain legacy only advancing LIB via validated blocks. Trans…
heifner Jul 9, 2024
53a3e53
GH-333 Remove mark_valid from fork_database since validated no longer…
heifner Jul 9, 2024
6379dcf
Merge remote-tracking branch 'spring/replay_test' into GH-333-forkdb-…
heifner Jul 9, 2024
7aa505d
GH-333 Mark block state in forkdb as valid
heifner Jul 9, 2024
53ba6e2
GH-333 Fix bad block legacy fork test to allow forking on second to l…
heifner Jul 9, 2024
5cc3030
GH-333 Renamed pending_head() to head()
heifner Jul 9, 2024
cebccfc
GH-333 Fix comment
heifner Jul 9, 2024
d67c298
GH-333 Modify fork_database to use block_state is_valid, set_valid vi…
heifner Jul 9, 2024
099c53b
GH-333 Use valueless_by_exception() instead of index() != std::varian…
heifner Jul 9, 2024
ad3336c
GH-333 Remove unneeded mark_all_invalid()
heifner Jul 9, 2024
a600ff1
GH-333 Replay only validated reversible blocks from forkdb
heifner Jul 10, 2024
834a2b1
Merge branch 'main' into GH-333-forkdb-head
heifner Jul 10, 2024
add318f
GH-333 Simplify implementation
heifner Jul 10, 2024
28557f1
GH-333 Fix log statement
heifner Jul 10, 2024
0cb2d77
GH-333 Add additional comments. Move pending_lib_id impl to cpp file
heifner Jul 10, 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
167 changes: 77 additions & 90 deletions libraries/chain/controller.cpp

Large diffs are not rendered by default.

215 changes: 64 additions & 151 deletions libraries/chain/fork_database.cpp

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions libraries/chain/include/eosio/chain/block_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ struct block_state : public block_header_state { // block_header_state provi
std::optional<digest_type> base_digest; // For finality_data sent to SHiP, computed on demand in get_finality_data()

// ------ private methods -----------------------------------------------------------
void set_valid(bool v) { validated.store(v); }
bool is_valid() const { return validated.load(); }
bool is_pub_keys_recovered() const { return pub_keys_recovered; }
deque<transaction_metadata_ptr> extract_trxs_metas();
Expand Down
3 changes: 3 additions & 0 deletions libraries/chain/include/eosio/chain/block_state_legacy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ namespace eosio::chain {
friend struct completed_block;
friend struct block_state;

// not thread safe, expected to only be called from main thread
void set_valid(bool v) { validated = v; }
bool is_valid() const { return validated; }

bool is_pub_keys_recovered()const { return _pub_keys_recovered; }

deque<transaction_metadata_ptr> extract_trxs_metas() {
Expand Down
3 changes: 1 addition & 2 deletions libraries/chain/include/eosio/chain/controller.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,7 @@ namespace eosio::chain {
// returns nullptr pre-savanna, thread-safe, block_num according to branch curresponding to id
finalizer_policy_ptr active_finalizer_policy(const block_id_type& id, block_num_type block_num)const;

void set_if_irreversible_block_id(const block_id_type& id);
uint32_t if_irreversible_block_num() const;
void set_savanna_lib_id(const block_id_type& id);

uint32_t last_irreversible_block_num() const;
block_id_type last_irreversible_block_id() const;
Expand Down
38 changes: 28 additions & 10 deletions libraries/chain/include/eosio/chain/fork_database.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ namespace eosio::chain {
struct fork_database_impl;

using block_branch_t = std::vector<signed_block_ptr>;
enum class mark_valid_t { no, yes };
enum class ignore_duplicate_t { no, yes };
enum class include_root_t { no, yes };

Expand Down Expand Up @@ -60,9 +59,9 @@ namespace eosio::chain {
void reset_root( const bsp_t& root_bhs );

/**
* Removes validated flag from all blocks in fork database and resets head to point to the root.
* Removes validated flag from all blocks in fork database, except root
*/
void rollback_head_to_root();
void mark_all_invalid();

/**
* Advance root block forward to some other block in the tree.
Expand All @@ -72,18 +71,18 @@ namespace eosio::chain {
/**
* Add block state to fork database.
* Must link to existing block in fork database or the root.
* @param mark_valid if true also mark next_block valid
*/
void add( const bsp_t& next_block, mark_valid_t mark_valid, ignore_duplicate_t ignore_duplicate );
void add( const bsp_t& next_block, ignore_duplicate_t ignore_duplicate );

void remove( const block_id_type& id );

bool is_valid() const; // sanity checks on this fork_db

bool has_root() const;
bool has_root() const;
bsp_t root() const; // undefined if !has_root()
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
bsp_t head() const;
bsp_t pending_head() const;
bsp_t head(include_root_t include_root = include_root_t::no) const;
block_id_type pending_savanna_lib_id() const;
bool set_pending_savanna_lib_id( const block_id_type& id );

/**
* Returns the sequence of block states resulting from trimming the branch from the
Expand Down Expand Up @@ -127,8 +126,6 @@ namespace eosio::chain {
*/
branch_pair_t fetch_branch_from(const block_id_type& first, const block_id_type& second) const;

void mark_valid( const bsp_t& h );

private:
unique_ptr<fork_database_impl<BSP>> my;
};
Expand Down Expand Up @@ -171,6 +168,27 @@ namespace eosio::chain {
// see fork_database_t::fetch_branch(forkdb->head()->id())
block_branch_t fetch_branch_from_head() const;

block_id_type pending_lib_id(const block_id_type& head_id) const {
if (in_use.load() == in_use_t::legacy) {
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
block_state_legacy_ptr head;
if (head_id.empty()) {
head = fork_db_l.head();
} else {
// maintain legacy only advancing LIB via validated blocks
greg7mdp marked this conversation as resolved.
Show resolved Hide resolved
head = fork_db_l.get_block(head_id);
}
if (!head)
return {};
block_num_type lib_num = head->irreversible_blocknum();
auto lib = fork_db_l.search_on_branch(head->id(), lib_num, include_root_t::no);
if (!lib)
return {};
return lib->id();
} else {
return fork_db_s.pending_savanna_lib_id();
}
}

template <class R, class F>
R apply(const F& f) const {
if constexpr (std::is_same_v<void, R>) {
Expand Down
16 changes: 10 additions & 6 deletions plugins/chain_plugin/chain_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1289,13 +1289,17 @@ const string read_only::KEYi64 = "i64";
read_only::get_info_results read_only::get_info(const read_only::get_info_params&, const fc::time_point&) const {
const auto& rm = db.get_resource_limits_manager();

auto head_id = db.head_block_id();
auto lib_id = db.last_irreversible_block_id();
auto fhead_id = db.fork_db_head_block_id();

return {
itoh(static_cast<uint32_t>(app().version())),
db.get_chain_id(),
db.head_block_num(),
db.last_irreversible_block_num(),
db.last_irreversible_block_id(),
db.head_block_id(),
block_header::num_from_id(head_id),
block_header::num_from_id(lib_id),
lib_id,
head_id,
db.head_block_time(),
db.head_block_producer(),
rm.get_virtual_block_cpu_limit(),
Expand All @@ -1305,8 +1309,8 @@ read_only::get_info_results read_only::get_info(const read_only::get_info_params
//std::bitset<64>(db.get_dynamic_global_properties().recent_slots_filled).to_string(),
//__builtin_popcountll(db.get_dynamic_global_properties().recent_slots_filled) / 64.0,
app().version_string(),
db.fork_db_head_block_num(),
db.fork_db_head_block_id(),
block_header::num_from_id(fhead_id),
fhead_id,
app().full_version_string(),
rm.get_total_cpu_weight(),
rm.get_total_net_weight(),
Expand Down
38 changes: 19 additions & 19 deletions plugins/net_plugin/net_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -971,8 +971,8 @@ namespace eosio {
std::optional<request_message> last_req GUARDED_BY(conn_mtx);
handshake_message last_handshake_recv GUARDED_BY(conn_mtx);
handshake_message last_handshake_sent GUARDED_BY(conn_mtx);
block_id_type fork_head GUARDED_BY(conn_mtx);
uint32_t fork_head_num GUARDED_BY(conn_mtx) {0};
block_id_type conn_fork_head GUARDED_BY(conn_mtx);
uint32_t conn_fork_head_num GUARDED_BY(conn_mtx) {0};
fc::time_point last_close GUARDED_BY(conn_mtx);
std::string p2p_address GUARDED_BY(conn_mtx);
std::string unique_conn_node_id GUARDED_BY(conn_mtx);
Expand Down Expand Up @@ -1061,7 +1061,7 @@ namespace eosio {
/** @} */

void blk_send_branch( const block_id_type& msg_head_id );
void blk_send_branch( uint32_t msg_head_num, uint32_t lib_num, uint32_t fork_head_num );
void blk_send_branch( uint32_t msg_head_num, uint32_t lib_num, uint32_t head_num );
void blk_send(const block_id_type& blkid);

void enqueue( const net_message &msg );
Expand Down Expand Up @@ -1498,10 +1498,10 @@ namespace eosio {

// called from connection strand
void connection::blk_send_branch( const block_id_type& msg_head_id ) {
uint32_t fork_head_num = my_impl->get_fork_head_num();
uint32_t head_num = my_impl->get_chain_head_num();

peer_dlog(this, "fhead_num = ${h}",("h",fork_head_num));
if(fork_head_num == 0) {
peer_dlog(this, "fhead_num = ${h}",("h",head_num));
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
if(head_num == 0) {
notice_message note;
note.known_blocks.mode = normal;
note.known_blocks.pending = 0;
Expand Down Expand Up @@ -1540,18 +1540,18 @@ namespace eosio {
} else {
if( on_fork ) msg_head_num = 0;
// if peer on fork, start at their last lib, otherwise we can start at msg_head+1
blk_send_branch( msg_head_num, lib_num, fork_head_num );
blk_send_branch( msg_head_num, lib_num, head_num );
}
}

// called from connection strand
void connection::blk_send_branch( uint32_t msg_head_num, uint32_t lib_num, uint32_t fork_head_num ) {
void connection::blk_send_branch( uint32_t msg_head_num, uint32_t lib_num, uint32_t head_num ) {
if( !peer_requested ) {
auto last = msg_head_num != 0 ? msg_head_num : lib_num;
peer_requested = peer_sync_state( last+1, fork_head_num, last );
peer_requested = peer_sync_state( last+1, head_num, last );
} else {
auto last = msg_head_num != 0 ? msg_head_num : std::min( peer_requested->last, lib_num );
uint32_t end = std::max( peer_requested->end_block, fork_head_num );
uint32_t end = std::max( peer_requested->end_block, head_num );
peer_requested = peer_sync_state( last+1, end, last );
}
if( peer_requested->start_block <= peer_requested->end_block ) {
Expand Down Expand Up @@ -2365,7 +2365,7 @@ namespace eosio {
req.req_blocks.mode = catch_up;
auto is_fork_head_greater = [num, &id, &req]( const auto& cc ) {
fc::lock_guard g_conn( cc->conn_mtx );
if( cc->fork_head_num > num || cc->fork_head == id ) {
if( cc->conn_fork_head_num > num || cc->conn_fork_head == id ) {
req.req_blocks.mode = none;
return true;
}
Expand All @@ -2390,17 +2390,17 @@ namespace eosio {
set_state( head_catchup );
{
fc::lock_guard g_conn( c->conn_mtx );
c->fork_head = id;
c->fork_head_num = num;
c->conn_fork_head = id;
c->conn_fork_head_num = num;
}

req.req_blocks.ids.emplace_back( chain_info.fork_head_id );
} else {
peer_ilog( c, "none notice while in ${s}, fhead = ${hn}, id ${id}...",
("s", stage_str( sync_state ))("hn", num)("id", id.str().substr(8,16)) );
fc::lock_guard g_conn( c->conn_mtx );
c->fork_head = block_id_type();
c->fork_head_num = 0;
c->conn_fork_head = block_id_type();
c->conn_fork_head_num = 0;
}
req.req_trx.mode = none;
c->enqueue( req );
Expand Down Expand Up @@ -2489,15 +2489,15 @@ namespace eosio {
bool set_state_to_head_catchup = false;
my_impl->connections.for_each_block_connection( [&null_id, blk_num, &blk_id, &c, &set_state_to_head_catchup]( const auto& cp ) {
fc::unique_lock g_cp_conn( cp->conn_mtx );
uint32_t fork_head_num = cp->fork_head_num;
block_id_type fork_head_id = cp->fork_head;
uint32_t fork_head_num = cp->conn_fork_head_num;
linh2931 marked this conversation as resolved.
Show resolved Hide resolved
block_id_type fork_head_id = cp->conn_fork_head;
g_cp_conn.unlock();
if( fork_head_id == null_id ) {
// continue
} else if( fork_head_num < blk_num || fork_head_id == blk_id ) {
fc::lock_guard g_conn( c->conn_mtx );
c->fork_head = null_id;
c->fork_head_num = 0;
c->conn_fork_head = null_id;
c->conn_fork_head_num = 0;
} else {
set_state_to_head_catchup = true;
}
Expand Down
1 change: 1 addition & 0 deletions plugins/producer_plugin/producer_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ class producer_plugin_impl : public std::enable_shared_from_this<producer_plugin

// de-dupe here... no point in aborting block if we already know the block; avoid exception in create_block_handle_future
if (chain.validated_block_exists(id)) {
fc_dlog(_log, "already have validated block ${n} ${id}", ("n", blk_num)("id", id));
_time_tracker.add_other_time();
return true; // return true because the block was already accepted
}
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 @@ -29,7 +29,7 @@
errorExit=Utils.errorExit

appArgs=AppArgs()
extraArgs = appArgs.add(flag="--catchup-count", type=int, help="How many catchup-nodes to launch", default=10)
extraArgs = appArgs.add(flag="--catchup-count", type=int, help="How many catchup-nodes to launch", default=12)
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",
"--activate-if","-p","--wallet-port","--unshared"}, applicationSpecificArgs=appArgs)
Expand Down Expand Up @@ -67,7 +67,8 @@
specificExtraNodeosArgs[pnodes+7] = f' --sync-fetch-span 1597 '
specificExtraNodeosArgs[pnodes+8] = f' --sync-fetch-span 6765 '
specificExtraNodeosArgs[pnodes+9] = f' --sync-fetch-span 28657 '
specificExtraNodeosArgs[pnodes+10] = f' --sync-fetch-span 89 '
specificExtraNodeosArgs[pnodes+10] = f' --sync-fetch-span 89 --read-mode irreversible '
specificExtraNodeosArgs[pnodes+11] = f' --sync-fetch-span 377 --read-mode irreversible '
if cluster.launch(prodCount=prodCount, specificExtraNodeosArgs=specificExtraNodeosArgs, activateIF=activateIF, onlyBios=False,
pnodes=pnodes, totalNodes=totalNodes, totalProducers=pnodes*prodCount, unstartedNodes=catchupCount,
loadSystemContract=True, maximumP2pPerHost=totalNodes+trxGeneratorCnt) is False:
Expand Down
21 changes: 18 additions & 3 deletions unittests/finalizer_vote_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ using vote_result = finalizer::vote_result;
using tstamp = block_timestamp_type;
using fsi_t = finalizer_safety_information;

// ---------------------------------------------------------------------------------------
// Used to access privates of block_state
namespace eosio::chain {
struct block_state_accessor {
static void set_valid(block_state_ptr& bsp, bool v) {
bsp->set_valid(v);
}

static bool is_valid(const block_state_ptr& bsp) {
return bsp->is_valid();
}
};
}

// ---------------------------------------------------------------------------------------
struct bls_keys_t {
bls_private_key privkey;
Expand Down Expand Up @@ -136,7 +150,7 @@ struct simulator_t {
}

vote_result propose(const proposal_t& p, std::optional<qc_claim_t> _claim = {}) {
bsp h = forkdb.head();
bsp h = forkdb.head(include_root_t::yes);
qc_claim_t old_claim = _claim ? *_claim : h->core.latest_qc_claim();
bsp new_bsp = make_bsp(p, h, finpol, old_claim);
bsp_vec.push_back(new_bsp);
Expand All @@ -145,11 +159,12 @@ struct simulator_t {
}

result add(const proposal_t& p, std::optional<qc_claim_t> _claim = {}, const bsp& parent = {}) {
bsp h = parent ? parent : forkdb.head();
bsp h = parent ? parent : forkdb.head(include_root_t::yes);
qc_claim_t old_claim = _claim ? *_claim : h->core.latest_qc_claim();
bsp new_bsp = make_bsp(p, h, finpol, old_claim);
bsp_vec.push_back(new_bsp);
forkdb.add(new_bsp, mark_valid_t::yes, ignore_duplicate_t::no);
block_state_accessor::set_valid(new_bsp, true);
forkdb.add(new_bsp, ignore_duplicate_t::no);

auto v = vote(new_bsp);
return { new_bsp, v };
Expand Down
Loading