From 5e77072fa60110a00d2bff31798d58b6c10bd3da Mon Sep 17 00:00:00 2001 From: w0xlt <94266259+w0xlt@users.noreply.github.com> Date: Tue, 27 Jan 2026 12:57:32 -0800 Subject: [PATCH] rpc: fix race condition in gettxoutsetinfo Fix an assertion failure in gettxoutsetinfo (issue #34263) caused by capturing the best block before releasing cs_main, then checking it against a potentially newer best block in GetUTXOStats(). Remove the early pindex capture since ComputeUTXOStats() independently fetches the current best block under lock. Use stats.hashBlock and stats.nHeight (the actual computed values) instead of the potentially stale pindex when building the response. --- src/rpc/blockchain.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d97b7c6c15c..c631a93674a 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1080,7 +1080,6 @@ static RPCHelpMan gettxoutsetinfo() LOCK(::cs_main); coins_view = &active_chainstate.CoinsDB(); blockman = &active_chainstate.m_blockman; - pindex = blockman->LookupBlockIndex(coins_view->GetBestBlock()); } if (!request.params[1].isNull()) { @@ -1104,7 +1103,7 @@ static RPCHelpMan gettxoutsetinfo() // If a specific block was requested and the index has already synced past that height, we can return the // data already even though the index is not fully synced yet. - if (pindex->nHeight > summary.best_block_height) { + if (pindex && pindex->nHeight > summary.best_block_height) { throw JSONRPCError(RPC_INTERNAL_ERROR, strprintf("Unable to get data because coinstatsindex is still syncing. Current height: %d", summary.best_block_height)); } } @@ -1130,8 +1129,9 @@ static RPCHelpMan gettxoutsetinfo() ret.pushKV("disk_size", stats.nDiskSize); } else { CCoinsStats prev_stats{}; - if (pindex->nHeight > 0) { - const std::optional maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, pindex->pprev, index_requested); + if (stats.nHeight > 0) { + const CBlockIndex& block_index = *CHECK_NONFATAL(WITH_LOCK(::cs_main, return blockman->LookupBlockIndex(stats.hashBlock))); + const std::optional maybe_prev_stats = GetUTXOStats(coins_view, *blockman, hash_type, node.rpc_interruption_point, block_index.pprev, index_requested); if (!maybe_prev_stats) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Unable to read UTXO set"); }