diff --git a/include/bitcoin/node/chasers/chaser_organize.hpp b/include/bitcoin/node/chasers/chaser_organize.hpp index 1a506649..81d30ce5 100644 --- a/include/bitcoin/node/chasers/chaser_organize.hpp +++ b/include/bitcoin/node/chasers/chaser_organize.hpp @@ -44,6 +44,10 @@ class chaser_organize virtual void organize(const typename Block::cptr& block, organize_handler&& handler) NOEXCEPT; + /// Reorganize to the branch of an archived block of at least equal work. + virtual void prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT; + protected: using header_link = database::header_link; using chain_state = system::chain::chain_state; @@ -89,13 +93,17 @@ class chaser_organize virtual bool handle_chase(const code&, chase event_, event_value value) NOEXCEPT; - /// Organize a discovered Block. - virtual void do_organize(typename Block::cptr block, + /// Organize a discovered Block, prioritized accepts a tied branch. + virtual void do_organize(typename Block::cptr block, bool prioritized, const organize_handler& handler) NOEXCEPT; /// Reorganize following Block unconfirmability. virtual void do_disorganize(header_t header) NOEXCEPT; + /// Reorganize to the branch of the given block. + virtual void do_prioritize(const system::hash_digest& hash, + const organize_handler& handler) NOEXCEPT; + /// Properties /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/node/full_node.hpp b/include/bitcoin/node/full_node.hpp index 71068757..4e93462c 100644 --- a/include/bitcoin/node/full_node.hpp +++ b/include/bitcoin/node/full_node.hpp @@ -69,6 +69,10 @@ class BCN_API full_node virtual void organize(const system::chain::block::cptr& block, organize_handler&& handler) NOEXCEPT; + /// Reorganize to the branch of an archived block of at least equal work. + virtual void prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT; + /// Manage download queue. virtual void get_hashes(map_handler&& handler) NOEXCEPT; virtual void put_hashes(const map_ptr& map, diff --git a/include/bitcoin/node/impl/chasers/chaser_organize.ipp b/include/bitcoin/node/impl/chasers/chaser_organize.ipp index 17824a37..902f8b99 100644 --- a/include/bitcoin/node/impl/chasers/chaser_organize.ipp +++ b/include/bitcoin/node/impl/chasers/chaser_organize.ipp @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2011-2026 libbitcoin developers * * This file is part of libbitcoin. @@ -73,7 +73,17 @@ void CLASS::organize(const typename Block::cptr& block, if (closed()) return; - POST(do_organize, block, std::move(handler)); + POST(do_organize, block, false, std::move(handler)); +} + +TEMPLATE +void CLASS::prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT +{ + if (closed()) + return; + + POST(do_prioritize, hash, std::move(handler)); } // Methods @@ -113,7 +123,7 @@ bool CLASS::handle_chase(const code&, chase event_, event_value value) NOEXCEPT } TEMPLATE -void CLASS::do_organize(typename Block::cptr block, +void CLASS::do_organize(typename Block::cptr block, bool prioritized, const organize_handler& handler) NOEXCEPT { BC_ASSERT(stranded()); @@ -217,7 +227,7 @@ void CLASS::do_organize(typename Block::cptr block, bool strong{}; const auto branch_size = tree_branch.size() + store_branch.size(); const auto branch_point = height - add1(branch_size); - if (!query.get_strong_branch(strong, work, branch_point)) + if (!query.get_strong_branch(strong, work, branch_point, prioritized)) { handler(fault(error::organize3), height); return; @@ -333,6 +343,38 @@ void CLASS::do_organize(typename Block::cptr block, handler(error::success, height); } +// bitcoind's preciousblock, a manual tie break between equal work branches. +// The preference is not retained, as the reorganized branch then wins ties. +TEMPLATE +void CLASS::do_prioritize(const system::hash_digest& hash, + const organize_handler& handler) NOEXCEPT +{ + BC_ASSERT(stranded()); + + if (closed()) + return; + + // Only the top of a cached branch can tie the candidate top. + if (std::any_of(tree_.begin(), tree_.end(), [&](const auto& item) NOEXCEPT + { + return get_header(*item.second).previous_block_hash() == hash; + })) + { + handler(error::success, {}); + return; + } + + // A tied branch is cached, extract it for reevaluation as prioritized. + auto handle = tree_.extract(hash); + if (!handle) + { + handler(database::error::not_found, {}); + return; + } + + do_organize(handle.mapped(), true, handler); +} + TEMPLATE void CLASS::do_disorganize(header_t link) NOEXCEPT { diff --git a/include/bitcoin/node/protocols/protocol.hpp b/include/bitcoin/node/protocols/protocol.hpp index 946310b6..6aeea1a3 100644 --- a/include/bitcoin/node/protocols/protocol.hpp +++ b/include/bitcoin/node/protocols/protocol.hpp @@ -113,6 +113,10 @@ class BCN_API protocol virtual void organize(const system::chain::block::cptr& block, organize_handler&& handler) NOEXCEPT; + /// Reorganize to the branch of an archived block of at least equal work. + virtual void prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT; + /// Events subscription. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/node/sessions/session.hpp b/include/bitcoin/node/sessions/session.hpp index 51b438f4..c570a6c7 100644 --- a/include/bitcoin/node/sessions/session.hpp +++ b/include/bitcoin/node/sessions/session.hpp @@ -47,6 +47,10 @@ class BCN_API session virtual void organize(const system::chain::block::cptr& block, organize_handler&& handler) NOEXCEPT; + /// Reorganize to the branch of an archived block of at least equal work. + virtual void prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT; + /// Manage download queue. virtual void get_hashes(map_handler&& handler) NOEXCEPT; virtual void put_hashes(const map_ptr& map, diff --git a/src/chasers/chaser_confirm.cpp b/src/chasers/chaser_confirm.cpp index fd40abb7..f803e857 100644 --- a/src/chasers/chaser_confirm.cpp +++ b/src/chasers/chaser_confirm.cpp @@ -164,8 +164,9 @@ void chaser_confirm::do_bumped(height_t) NOEXCEPT } // Compares candidate branch work to confirmed (above fork point). + // A tie is accepted because only prioritization creates one. bool strong{}; - if (!query.get_strong_fork(strong, work, fork_point)) + if (!query.get_strong_fork(strong, work, fork_point, true)) { fault(error::confirm3); return; diff --git a/src/full_node.cpp b/src/full_node.cpp index e098102d..ff26a3e6 100644 --- a/src/full_node.cpp +++ b/src/full_node.cpp @@ -175,6 +175,15 @@ void full_node::organize(const system::chain::block::cptr& block, chaser_block_.organize(block, std::move(handler)); } +void full_node::prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT +{ + if (config_.node.headers_first) + chaser_header_.prioritize(hash, std::move(handler)); + else + chaser_block_.prioritize(hash, std::move(handler)); +} + void full_node::get_hashes(map_handler&& handler) NOEXCEPT { chaser_check_.get_hashes(std::move(handler)); diff --git a/src/protocols/protocol.cpp b/src/protocols/protocol.cpp index 4a86fe87..52885ff3 100644 --- a/src/protocols/protocol.cpp +++ b/src/protocols/protocol.cpp @@ -139,6 +139,12 @@ void protocol::organize(const system::chain::block::cptr& block, session_->organize(block, std::move(handler)); } +void protocol::prioritize(const system::hash_digest& hash, + organize_handler&& handler) NOEXCEPT +{ + session_->prioritize(hash, std::move(handler)); +} + void protocol::subscribe_chase(event_notifier&& handler) NOEXCEPT { // This is a shared instance multiply-derived from network::protocol. diff --git a/src/sessions/session.cpp b/src/sessions/session.cpp index c81e2297..11b89321 100644 --- a/src/sessions/session.cpp +++ b/src/sessions/session.cpp @@ -52,6 +52,12 @@ void session::organize(const block::cptr& block, node_.organize(block, std::move(handler)); } +void session::prioritize(const hash_digest& hash, + organize_handler&& handler) NOEXCEPT +{ + node_.prioritize(hash, std::move(handler)); +} + void session::get_hashes(map_handler&& handler) NOEXCEPT { node_.get_hashes(std::move(handler));