From ebf164a90cfd6057b2e97fc42cdd96a98a3a0bfc Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Wed, 14 Oct 2026 21:20:59 -0400 Subject: [PATCH 1/2] Increase default channel heartbeat to 10 minutes. --- data/bs.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/bs.cfg b/data/bs.cfg index fdd01996..bdc9b8b8 100644 --- a/data/bs.cfg +++ b/data/bs.cfg @@ -93,8 +93,8 @@ connect_timeout_seconds = 5 channel_handshake_seconds = 30 # The time limit for obtaining seed addresses, defaults to 30. channel_germination_seconds = 30 -# The time between ping messages, defaults to 5. -channel_heartbeat_minutes = 5 +# The time between ping messages, defaults to 10. +channel_heartbeat_minutes = 10 # The inactivity time limit for any connection, defaults to 30. channel_inactivity_minutes = 30 # The age limit for any connection, defaults to 1440. From 6b3ac538df6c30238a6fc0b7fb239b23bf6e7afb Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Wed, 14 Oct 2026 22:30:51 -0400 Subject: [PATCH 2/2] Use top link accessors in protocols. --- .../bitcoind/protocol_bitcoind_blockchain.cpp | 68 +++++++++++++------ .../bitcoind/protocol_bitcoind_mining.cpp | 6 +- .../bitcoind/protocol_bitcoind_rest.cpp | 19 ++++-- src/protocols/btcd/protocol_btcd.cpp | 6 +- src/protocols/btcd/protocol_btcd_utility.cpp | 10 +-- .../electrum/protocol_electrum_headers.cpp | 8 +-- .../native/protocol_native_block.cpp | 2 +- src/serializers/bitcoind_json.cpp | 8 +-- 8 files changed, 79 insertions(+), 48 deletions(-) diff --git a/src/protocols/bitcoind/protocol_bitcoind_blockchain.cpp b/src/protocols/bitcoind/protocol_bitcoind_blockchain.cpp index caa19f6c..6744a3e4 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_blockchain.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_blockchain.cpp @@ -457,7 +457,7 @@ bool protocol_bitcoind_blockchain::handle_get_chain_tx_stats(const code& ec, return false; const auto& query = archive(); - auto link = query.to_confirmed(query.get_top_confirmed()); + auto link = query.to_top_confirmed(); // The block defaults to the confirmed top, and must be on the chain. if (!blockhash.empty()) @@ -575,11 +575,11 @@ bool protocol_bitcoind_blockchain::handle_get_tx_out(const code& ec, return true; } - const auto top = query.get_top_confirmed(); - const auto header_link = query.to_confirmed(top); + size_t top{}; + const auto header_link = query.to_top_confirmed(); const auto output = query.get_output(output_link); const auto tx_link = query.to_output_tx(output_link); - if (!output || tx_link.is_terminal() || header_link.is_terminal()) + if (!output || tx_link.is_terminal() || !query.get_height(top, header_link)) { send_error(error::bitcoind::internal_error); return true; @@ -844,11 +844,17 @@ void protocol_bitcoind_blockchain::do_scan_tx_out_set( code ec{}; size_t txouts{}; database::unspent_coins coins{}; + size_t top{}; const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto link = query.to_confirmed(top); + const auto link = query.to_top_confirmed(); const auto bip30 = query.envelope().forks.bip30; + if (!query.get_height(top, link)) + { + POST(fail, error::bitcoind::internal_error, gate); + return; + } + if (query.address_enabled()) { // Indexed candidates avoid the full scan, so the scanned coin count @@ -1103,11 +1109,16 @@ bool protocol_bitcoind_blockchain::handle_get_chain_states(const code& ec, if (stopped(ec)) return false; + size_t candidate{}; const auto& query = archive(); - const auto candidate = query.get_top_candidate(); + const auto candidate_link = query.to_top_candidate(); + if (!query.get_height(candidate, candidate_link)) + { + send_error(error::bitcoind::internal_error); + return true; + } - auto link = query.to_confirmed(query.get_top_confirmed()); - auto entry = chain_states_entry(query, link, 1.0, true); + auto entry = chain_states_entry(query, query.to_top_confirmed(), 1.0, true); if (entry.empty()) { send_error(error::bitcoind::internal_error); @@ -1122,9 +1133,7 @@ bool protocol_bitcoind_blockchain::handle_get_chain_states(const code& ec, size_t fork{}; const auto span = query.get_validated_fork(fork); const auto validated = progress(fork + span.size(), candidate); - - link = query.to_candidate(candidate); - entry = chain_states_entry(query, link, validated, false); + entry = chain_states_entry(query, candidate_link, validated, false); if (entry.empty()) { send_error(error::bitcoind::internal_error); @@ -1151,9 +1160,14 @@ bool protocol_bitcoind_blockchain::handle_get_chain_tips(const code& ec, // Two indexes only: confirmed (active) and candidate (strongest headers); // weak/reorged branches live in the organizer tree and are not exposed. + size_t confirmed{}; const auto& query = archive(); - const auto confirmed = query.get_top_confirmed(); - const auto confirmed_link = query.to_confirmed(confirmed); + const auto confirmed_link = query.to_top_confirmed(); + if (!query.get_height(confirmed, confirmed_link)) + { + send_error(error::bitcoind::internal_error); + return true; + } array_t tips { @@ -1167,7 +1181,14 @@ bool protocol_bitcoind_blockchain::handle_get_chain_tips(const code& ec, }; // The candidate is a distinct top only where it forks above confirmed. - const auto candidate = query.get_top_candidate(); + size_t candidate{}; + const auto candidate_link = query.to_top_candidate(); + if (!query.get_height(candidate, candidate_link)) + { + send_error(error::bitcoind::internal_error); + return true; + } + const auto branchlen = floored_subtract(candidate, query.get_fork()); if (!is_zero(branchlen)) { @@ -1175,7 +1196,6 @@ bool protocol_bitcoind_blockchain::handle_get_chain_tips(const code& ec, // the branch makes it headers-only, otherwise valid-headers; candidate // is the strongest valid chain so is never invalid or valid-fork. database::header_links branch{}; - const auto candidate_link = query.to_candidate(candidate); if (query.get_ancestry(branch, candidate_link, branchlen)) { const auto present = std::all_of(branch.cbegin(), branch.cend(), @@ -1206,7 +1226,7 @@ bool protocol_bitcoind_blockchain::handle_get_deployment_info(const code& ec, return false; const auto& query = archive(); - auto link = query.to_confirmed(query.get_top_confirmed()); + auto link = query.to_top_confirmed(); // The block defaults to the confirmed top. if (!blockhash.empty()) @@ -1364,8 +1384,7 @@ bool protocol_bitcoind_blockchain::handle_get_difficulty(const code& ec, return false; const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto header = query.get_header(query.to_confirmed(top)); + const auto header = query.get_header(query.to_top_confirmed()); if (!header) { send_error(error::bitcoind::internal_error); @@ -1959,11 +1978,18 @@ bool protocol_bitcoind_blockchain::wait_done( void protocol_bitcoind_blockchain::send_top() NOEXCEPT { + size_t top{}; const auto& query = archive(); - const auto top = query.get_top_confirmed(); + const auto link = query.to_top_confirmed(); + if (!query.get_height(top, link)) + { + send_error(error::bitcoind::internal_error); + return; + } + send_result(object_t { - { "hash", encode_hash(query.get_header_key(query.to_confirmed(top))) }, + { "hash", encode_hash(query.get_header_key(link)) }, { "height", top } }); } diff --git a/src/protocols/bitcoind/protocol_bitcoind_mining.cpp b/src/protocols/bitcoind/protocol_bitcoind_mining.cpp index 84401e70..31c8d6a1 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_mining.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_mining.cpp @@ -163,11 +163,11 @@ bool protocol_bitcoind_mining::handle_get_mining_info(const code& ec, if (stopped(ec)) return false; + size_t height{}; const auto& query = archive(); - const auto height = query.get_top_confirmed(); - const auto link = query.to_confirmed(height); + const auto link = query.to_top_confirmed(); const auto top = query.get_header(link); - if (!top) + if (!top || !query.get_height(height, link)) { send_error(error::bitcoind::internal_error); return true; diff --git a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp index e8cb4d5b..973e0c48 100644 --- a/src/protocols/bitcoind/protocol_bitcoind_rest.cpp +++ b/src/protocols/bitcoind/protocol_bitcoind_rest.cpp @@ -692,11 +692,11 @@ bool protocol_bitcoind_rest::handle_get_chain_information(const code& ec, if (stopped(ec)) return false; + size_t blocks{}; const auto& query = archive(); - const auto blocks = query.get_top_confirmed(); - const auto link = query.to_confirmed(blocks); + const auto link = query.to_top_confirmed(); const auto header = query.get_header(link); - if (!header) + if (!header || !query.get_height(blocks, link)) { send_not_found(); return true; @@ -764,8 +764,15 @@ bool protocol_bitcoind_rest::handle_get_utxos(const code& ec, utxos.push_back({ possible_narrow_cast(height), output }); } - const auto top = query.get_top_confirmed(); - const auto top_hash = query.get_header_key(query.to_confirmed(top)); + size_t top{}; + const auto top_link = query.to_top_confirmed(); + if (!query.get_height(top, top_link)) + { + send_internal_server_error(database::error::integrity); + return true; + } + + const auto top_hash = query.get_header_key(top_link); // The bip64 bitmap flags each outpoint hit, lsb first within each byte. data_chunk bitmap(ceilinged_divide(hits.size(), 8u), 0x00); @@ -846,7 +853,7 @@ bool protocol_bitcoind_rest::handle_get_deployment_info(const code& ec, return false; const auto& query = archive(); - auto link = query.to_confirmed(query.get_top_confirmed()); + auto link = query.to_top_confirmed(); // bitcoind reports an unknown block as a bad request here. if (hash.has_value()) diff --git a/src/protocols/btcd/protocol_btcd.cpp b/src/protocols/btcd/protocol_btcd.cpp index 03beafb9..123bcd2a 100644 --- a/src/protocols/btcd/protocol_btcd.cpp +++ b/src/protocols/btcd/protocol_btcd.cpp @@ -312,10 +312,10 @@ bool protocol_btcd::handle_rescan(const code& ec, return true; } - const auto top = query.get_top_confirmed(); - const auto confirmed = query.to_confirmed(top); + size_t top{}; + const auto confirmed = query.to_top_confirmed(); const auto header = query.get_header(confirmed); - if (!header) + if (!header || !query.get_height(top, confirmed)) { send_error(error::btcd::internal_error); return true; diff --git a/src/protocols/btcd/protocol_btcd_utility.cpp b/src/protocols/btcd/protocol_btcd_utility.cpp index 89c3d881..80c593f9 100644 --- a/src/protocols/btcd/protocol_btcd_utility.cpp +++ b/src/protocols/btcd/protocol_btcd_utility.cpp @@ -212,8 +212,7 @@ bool protocol_btcd::handle_get_difficulty(const code& ec, return false; const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto header = query.get_header(query.to_confirmed(top)); + const auto header = query.get_header(query.to_top_confirmed()); if (!header) { send_error(error::btcd::internal_error); @@ -270,10 +269,11 @@ bool protocol_btcd::handle_get_info(const code& ec, if (stopped(ec)) return false; + size_t top{}; const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto header = query.get_header(query.to_confirmed(top)); - if (!header) + const auto link = query.to_top_confirmed(); + const auto header = query.get_header(link); + if (!header || !query.get_height(top, link)) { send_error(error::btcd::internal_error); return true; diff --git a/src/protocols/electrum/protocol_electrum_headers.cpp b/src/protocols/electrum/protocol_electrum_headers.cpp index 64accb0a..441526f0 100644 --- a/src/protocols/electrum/protocol_electrum_headers.cpp +++ b/src/protocols/electrum/protocol_electrum_headers.cpp @@ -371,12 +371,10 @@ void protocol_electrum::handle_blockchain_headers_subscribe(const code& ec, return; } + size_t top{}; const auto& query = archive(); - const auto top = query.get_top_confirmed(); - const auto link = query.to_confirmed(top); - - // This is unlikely but possible due to a race condition during reorg. - if (link.is_terminal()) + const auto link = query.to_top_confirmed(); + if (!query.get_height(top, link)) { send_code(error::electrum::daemon_error); return; diff --git a/src/protocols/native/protocol_native_block.cpp b/src/protocols/native/protocol_native_block.cpp index 0828863a..e77db277 100644 --- a/src/protocols/native/protocol_native_block.cpp +++ b/src/protocols/native/protocol_native_block.cpp @@ -539,7 +539,7 @@ bool protocol_native::handle_get_block_subscribe(const code& ec, // Return top block hash upon block subscription. const auto& query = archive(); - const auto link = query.to_confirmed(query.get_top_confirmed()); + const auto link = query.to_top_confirmed(); if (const auto hash = query.get_header_key(link); hash != null_hash) { switch (media) diff --git a/src/serializers/bitcoind_json.cpp b/src/serializers/bitcoind_json.cpp index 2852546f..d140e9c2 100644 --- a/src/serializers/bitcoind_json.cpp +++ b/src/serializers/bitcoind_json.cpp @@ -295,15 +295,15 @@ bool chain_info(network::rpc::object_t& out, const node::query& query, const system::settings& settings, bool pruned, bool current) NOEXCEPT { - const auto blocks = query.get_top_confirmed(); + size_t blocks{}; const auto headers = query.get_top_candidate(); - const auto link = query.to_confirmed(blocks); + const auto link = query.to_top_confirmed(); const auto header = query.get_header(link); uint256_t work{}; database::context context{}; - if (!header || !query.get_branch_work(work, link) || - !query.get_context(context, link)) + if (!header || !query.get_height(blocks, link) || + !query.get_branch_work(work, link) || !query.get_context(context, link)) return false; const auto bits = header->bits();