From 8a4a1934ff94ca9c3b8a42ea4b9b445f5bf6efa4 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 3 Nov 2026 22:11:56 +0700 Subject: [PATCH 01/12] refactor: move RenameThreadPool out of common/system RenameThreadPool is Dash-only and is called from bls/bls_worker.cpp, which the kernel library builds, while common/system.cpp is a node-side file that upstream bitcoin#27636 keeps out of the kernel. Move the helper next to util::TraceThread in util/thread.{h,cpp}, which the kernel already builds, so the kernel no longer needs common/system.cpp. --- src/bls/bls_worker.cpp | 2 +- src/common/system.cpp | 43 ------------------------------------- src/common/system.h | 5 ----- src/util/thread.cpp | 48 ++++++++++++++++++++++++++++++++++++++++++ src/util/thread.h | 7 ++++++ 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/src/bls/bls_worker.cpp b/src/bls/bls_worker.cpp index 7e383a644ba3..be8bef23226e 100644 --- a/src/bls/bls_worker.cpp +++ b/src/bls/bls_worker.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include diff --git a/src/common/system.cpp b/src/common/system.cpp index d9249c264495..f718d27501d1 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -6,11 +6,8 @@ #include -#include #include -#include #include -#include #include #ifndef WIN32 @@ -24,9 +21,7 @@ #endif #include -#include #include -#include #include #include #include @@ -69,44 +64,6 @@ void runCommand(const std::string& strCommand) } #endif -void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName) -{ - auto cond = std::make_shared(); - auto mutex = std::make_shared(); - std::atomic doneCnt(0); - std::map > futures; - - for (int i = 0; i < tp.size(); i++) { - futures[i] = tp.push([baseName, i, cond, mutex, &doneCnt](int threadId) { - util::ThreadRename(strprintf("%s-%d", baseName, i)); - std::unique_lock l(*mutex); - doneCnt++; - cond->wait(l); - }); - } - - do { - // Always sleep to let all threads acquire locks - UninterruptibleSleep(std::chrono::milliseconds{10}); - // `doneCnt` should be at least `futures.size()` if tp size was increased (for whatever reason), - // or at least `tp.size()` if tp size was decreased and queue was cleared - // (which can happen on `stop()` if we were not fast enough to get all jobs to their threads). - } while (uint64_t(doneCnt) < futures.size() && doneCnt < tp.size()); - - cond->notify_all(); - - // Make sure no one is left behind, just in case - for (auto& pair : futures) { - auto& f = pair.second; - if (f.valid() && f.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { - LogPrintf("%s: %s-%d timed out\n", __func__, baseName, pair.first); - // Notify everyone again - cond->notify_all(); - break; - } - } -} - void SetupEnvironment() { #ifdef HAVE_MALLOPT_ARENA_MAX diff --git a/src/common/system.h b/src/common/system.h index 8e54b9e526d1..681e9be299d1 100644 --- a/src/common/system.h +++ b/src/common/system.h @@ -36,11 +36,6 @@ std::string ShellEscape(const std::string& arg); void runCommand(const std::string& strCommand); #endif -namespace ctpl { - class thread_pool; -} -void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName); - /** * Return the number of cores available on the current system. * @note This does count virtual cores, such as those provided by HyperThreading. diff --git a/src/util/thread.cpp b/src/util/thread.cpp index e8daf429e8dd..b8359bb90be6 100644 --- a/src/util/thread.cpp +++ b/src/util/thread.cpp @@ -4,12 +4,22 @@ #include +#include #include +#include #include #include +#include +#include +#include +#include #include #include +#include +#include +#include +#include #include #include @@ -25,3 +35,41 @@ void util::TraceThread(std::string_view thread_name, std::function threa throw; } } + +void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName) +{ + auto cond = std::make_shared(); + auto mutex = std::make_shared(); + std::atomic doneCnt(0); + std::map> futures; + + for (int i = 0; i < tp.size(); i++) { + futures[i] = tp.push([baseName, i, cond, mutex, &doneCnt](int threadId) { + util::ThreadRename(strprintf("%s-%d", baseName, i)); + std::unique_lock l(*mutex); + doneCnt++; + cond->wait(l); + }); + } + + do { + // Always sleep to let all threads acquire locks + UninterruptibleSleep(std::chrono::milliseconds{10}); + // `doneCnt` should be at least `futures.size()` if tp size was increased (for whatever reason), + // or at least `tp.size()` if tp size was decreased and queue was cleared + // (which can happen on `stop()` if we were not fast enough to get all jobs to their threads). + } while (uint64_t(doneCnt) < futures.size() && doneCnt < tp.size()); + + cond->notify_all(); + + // Make sure no one is left behind, just in case + for (auto& pair : futures) { + auto& f = pair.second; + if (f.valid() && f.wait_for(std::chrono::milliseconds(2000)) == std::future_status::timeout) { + LogPrintf("%s: %s-%d timed out\n", __func__, baseName, pair.first); + // Notify everyone again + cond->notify_all(); + break; + } + } +} diff --git a/src/util/thread.h b/src/util/thread.h index 8a2de84a7f9d..87c612a3286f 100644 --- a/src/util/thread.h +++ b/src/util/thread.h @@ -8,6 +8,10 @@ #include #include +namespace ctpl { +class thread_pool; +} // namespace ctpl + namespace util { /** * A wrapper for do-something-once thread functions. @@ -16,4 +20,7 @@ void TraceThread(std::string_view thread_name, std::function thread_func } // namespace util +/** Give every thread of the pool the name "-". */ +void RenameThreadPool(ctpl::thread_pool& tp, const char* baseName); + #endif // BITCOIN_UTIL_THREAD_H From 77842da98cefde27e2447185e1806aada590a9d9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 3 Nov 2026 22:20:40 +0700 Subject: [PATCH 02/12] refactor: write the masternode state diff JSON outside the kernel CDeterministicMNStateDiff::ToJson is RPC output code, yet it lives in evo/dmnstate.cpp, which the kernel library builds, and it checks the -deprecatedrpc=service flag through gArgs. Move it next to CDeterministicMNState::ToJson in evo/core_write.cpp and make the flag check a helper local to that file, which leaves no ArgsManager user in evo/netinfo.cpp or evo/dmnstate.cpp. --- src/evo/core_write.cpp | 131 +++++++++++++++++++++++++++++++++++++++++ src/evo/dmnstate.cpp | 126 --------------------------------------- src/evo/netinfo.cpp | 7 --- src/evo/netinfo.h | 3 - 4 files changed, 131 insertions(+), 136 deletions(-) diff --git a/src/evo/core_write.cpp b/src/evo/core_write.cpp index a5cbe174aee8..9cf595033941 100644 --- a/src/evo/core_write.cpp +++ b/src/evo/core_write.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -22,11 +23,19 @@ #include +#include #include #include #include namespace { +/** Identical to IsDeprecatedRPCEnabled("service"). For use outside of RPC code */ +bool IsServiceDeprecatedRPCEnabled() +{ + const auto args = gArgs.GetArgs("-deprecatedrpc"); + return std::find(args.begin(), args.end(), "service") != args.end(); +} + template std::string GetDeprecatedServiceField(const Obj& obj) { @@ -191,6 +200,128 @@ UniValue CDeterministicMNState::ToJson(MnType nType) const return obj; } +UniValue CDeterministicMNStateDiff::ToJson(MnType nType) const +{ + UniValue obj(UniValue::VOBJ); + if (fields & Field_nVersion) { + obj.pushKV("version", state.nVersion); + } + if (fields & Field_netInfo) { + if (IsServiceDeprecatedRPCEnabled()) { + obj.pushKV("service", state.netInfo->GetPrimary().ToStringAddrPort()); + } + } + if (fields & Field_nRegisteredHeight) { + obj.pushKV("registeredHeight", state.nRegisteredHeight); + } + if (fields & Field_nLastPaidHeight) { + obj.pushKV("lastPaidHeight", state.nLastPaidHeight); + } + if (fields & Field_nConsecutivePayments) { + obj.pushKV("consecutivePayments", state.nConsecutivePayments); + } + if (fields & Field_nPoSePenalty) { + obj.pushKV("PoSePenalty", state.nPoSePenalty); + } + if (fields & Field_nPoSeRevivedHeight) { + obj.pushKV("PoSeRevivedHeight", state.nPoSeRevivedHeight); + } + if (fields & Field_nPoSeBanHeight) { + obj.pushKV("PoSeBanHeight", state.nPoSeBanHeight); + } + if (fields & Field_nRevocationReason) { + obj.pushKV("revocationReason", state.nRevocationReason); + } + if (fields & Field_keyIDOwner) { + obj.pushKV("ownerAddress", EncodeDestination(PKHash(state.keyIDOwner))); + } + if (fields & Field_keyIDVoting) { + obj.pushKV("votingAddress", EncodeDestination(PKHash(state.keyIDVoting))); + } + if (fields & Field_scriptPayout) { + CTxDestination dest; + if (ExtractDestination(state.scriptPayout, dest)) { + obj.pushKV("payoutAddress", EncodeDestination(dest)); + } + } + if (fields & Field_payouts) { + obj.pushKV("payouts", PayoutListToJson(state.payouts)); + } + if (fields & Field_shares) { + obj.pushKV("shares", ShareListToJson(state.shares)); + } + if (fields & Field_nEarlyPeriodBlocks) { + obj.pushKV("earlyPeriodBlocks", static_cast(state.nEarlyPeriodBlocks)); + } + if (fields & Field_nEarlyPenalty) { + obj.pushKV("earlyPenalty", state.nEarlyPenalty); + } + if (fields & Field_scriptOperatorPayout) { + CTxDestination dest; + if (ExtractDestination(state.scriptOperatorPayout, dest)) { + obj.pushKV("operatorPayoutAddress", EncodeDestination(dest)); + } + } + if (fields & Field_pubKeyOperator) { + obj.pushKV("pubKeyOperator", state.pubKeyOperator.ToString()); + } + if (nType == MnType::Evo) { + if (fields & Field_platformNodeID) { + obj.pushKV("platformNodeID", state.platformNodeID.ToString()); + } + if (IsServiceDeprecatedRPCEnabled()) { + // platformP2PPort/platformHTTPPort are deprecated scalar duplicates of netInfo's + // Platform entries. From ExtAddr onwards the scalar fields are unused (always 0), so + // when the diff carries an ExtAddr netInfo report the live port from it to stay + // consistent with the "addresses" output below. + const bool has_ext_netinfo = (fields & Field_netInfo) && state.netInfo->CanStorePlatform(); + if (fields & Field_platformP2PPort) { + obj.pushKV("platformP2PPort", has_ext_netinfo && state.netInfo->HasEntries(NetInfoPurpose::PLATFORM_P2P) + ? state.netInfo->GetEntries(NetInfoPurpose::PLATFORM_P2P)[0].GetPort() + : state.platformP2PPort); + } + if (fields & Field_platformHTTPPort) { + obj.pushKV("platformHTTPPort", has_ext_netinfo && state.netInfo->HasEntries(NetInfoPurpose::PLATFORM_HTTPS) + ? state.netInfo->GetEntries(NetInfoPurpose::PLATFORM_HTTPS)[0].GetPort() + : state.platformHTTPPort); + } + } + } + { + const bool has_netinfo = (fields & Field_netInfo); + + UniValue netInfoObj(UniValue::VOBJ); + if (has_netinfo) { + netInfoObj = state.netInfo->ToJson(); + } + if (nType == MnType::Evo && (!has_netinfo || !state.netInfo->CanStorePlatform())) { + auto unknownAddr = [](uint16_t port) -> UniValue { + UniValue obj(UniValue::VARR); + // We don't know what the address is because it wasn't changed in the + // diff but we still need to report the port number in addr:port format + obj.push_back(strprintf("255.255.255.255:%d", port)); + return obj; + }; + if (fields & Field_platformP2PPort) { + netInfoObj.pushKV(PurposeToString(NetInfoPurpose::PLATFORM_P2P).data(), + (has_netinfo) + ? ArrFromService(CService(state.netInfo->GetPrimary(), state.platformP2PPort)) + : unknownAddr(state.platformP2PPort)); + } + if (fields & Field_platformHTTPPort) { + netInfoObj.pushKV(PurposeToString(NetInfoPurpose::PLATFORM_HTTPS).data(), + (has_netinfo) + ? ArrFromService(CService(state.netInfo->GetPrimary(), state.platformHTTPPort)) + : unknownAddr(state.platformHTTPPort)); + } + } + if (!netInfoObj.empty()) { + obj.pushKV("addresses", netInfoObj); + } + } + return obj; +} + UniValue CProRegTx::ToJson() const { UniValue ret(UniValue::VOBJ); diff --git a/src/evo/dmnstate.cpp b/src/evo/dmnstate.cpp index 7a8e9da55434..e580456949f0 100644 --- a/src/evo/dmnstate.cpp +++ b/src/evo/dmnstate.cpp @@ -4,9 +4,7 @@ #include -#include #include