refactor: introduce SubmitBlock helper
What changed, and why it matters
This commit is a straightforward internal code reorganization (refactor) in Bitcoin Core. It moves the logic for submitting a newly mined block into a shared helper function called SubmitBlock(). The actual behavior of block submission does not change; the code just becomes cleaner and easier to reuse for future features like IPC (inter-process communication). There is no security bug being fixed and no new attack surface introduced.
No security action required. Treat as normal code refactoring and review for code quality if desired.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change introduces SubmitBlock() in src/node/miner.cpp, which wraps ChainstateManager::ProcessNewBlock() and captures BlockValidationState through a temporary CValidationInterface listener (SubmitBlockStateCatcher). The existing submitSolution() implementation in src/node/interfaces.cpp is updated to call this helper instead of calling ProcessNewBlock() directly. The helper returns the same boolean acceptance result and populates reason/debug strings, preserving existing duplicate-as-success semantics. The commit message explicitly states ‘No behavior change.’
Changed components
src/node/interfaces.cppsrc/node/miner.cppsrc/node/miner.hInspect captured patch +64 / −1
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index edb73628..28f8f2e8 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -920,7 +920,9 @@ public:
bool submitSolution(uint32_t version, uint32_t timestamp, uint32_t nonce, CTransactionRef coinbase) override
{
AddMerkleRootAndCoinbase(m_block_template->block, std::move(coinbase), version, timestamp, nonce);
- return chainman().ProcessNewBlock(std::make_shared<const CBlock>(m_block_template->block), /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/nullptr);
+ std::string reason;
+ std::string debug;
+ return SubmitBlock(chainman(), std::make_shared<const CBlock>(m_block_template->block), /*new_block=*/nullptr, reason, debug);
}
std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override
diff --git a/src/node/miner.cpp b/src/node/miner.cpp
index 5c0df502..ccd9cc7c 100644
--- a/src/node/miner.cpp
+++ b/src/node/miner.cpp
@@ -38,6 +38,7 @@
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
+#include <validationinterface.h>
#include <versionbits.h>
#include <algorithm>
@@ -357,6 +358,62 @@ void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t
block.fChecked = false;
}
+namespace {
+class SubmitBlockStateCatcher final : public CValidationInterface
+{
+public:
+ uint256 m_hash;
+ bool m_found{false};
+ BlockValidationState m_state;
+
+ explicit SubmitBlockStateCatcher(const uint256& hash) : m_hash{hash} {}
+
+protected:
+ void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state) override
+ {
+ if (block->GetHash() != m_hash) return;
+ // ProcessNewBlock emits BlockChecked synchronously while holding cs_main,
+ // so SubmitBlock can read these fields after ProcessNewBlock returns
+ // without extra synchronization.
+ m_found = true;
+ m_state = state;
+ }
+};
+} // namespace
+
+bool SubmitBlock(ChainstateManager& chainman, const std::shared_ptr<const CBlock>& block, bool* new_block, std::string& reason, std::string& debug)
+{
+ reason.clear();
+ debug.clear();
+
+ // This follows the submitblock RPC's validation-state capture pattern, but
+ // is intentionally kept separate from the RPC implementation. The RPC entry
+ // point decodes hex, formats BIP22/JSONRPC results, and calls
+ // UpdateUncommittedBlockStructures() for legacy witness handling. IPC
+ // callers submit already-formed blocks and need bool + reason/debug
+ // results, while submitSolution() preserves its duplicate-as-success
+ // behavior.
+ auto sc = std::make_shared<SubmitBlockStateCatcher>(block->GetHash());
+ CHECK_NONFATAL(chainman.m_options.signals)->RegisterSharedValidationInterface(sc);
+ bool accepted = chainman.ProcessNewBlock(block, /*force_processing=*/true, /*min_pow_checked=*/true, /*new_block=*/new_block);
+ CHECK_NONFATAL(chainman.m_options.signals)->UnregisterSharedValidationInterface(sc);
+
+ if (new_block && !*new_block && accepted) {
+ reason = "duplicate";
+ } else if (!sc->m_found) {
+ // A block can be accepted and stored without being connected, for
+ // example if it does not have more work than the current tip. In that
+ // case no BlockChecked callback is emitted, so the validation result is
+ // inconclusive. Mining::submitBlock treats this as an error for mining
+ // clients, but it does not mean the block is invalid.
+ reason = "inconclusive";
+ } else if (!sc->m_state.IsValid()) {
+ reason = sc->m_state.GetRejectReason();
+ debug = sc->m_state.GetDebugMessage();
+ }
+ return accepted;
+}
+
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait)
{
LOCK(kernel_notifications.m_tip_block_mutex);
diff --git a/src/node/miner.h b/src/node/miner.h
index 14780833..af327307 100644
--- a/src/node/miner.h
+++ b/src/node/miner.h
@@ -18,6 +18,7 @@
#include <cstdint>
#include <memory>
#include <optional>
+#include <string>
#include <vector>
class CBlockIndex;
@@ -129,6 +130,9 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
/* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */
void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce);
+//! Submit a block and capture the validation state via the BlockChecked callback.
+//! Returns whether ProcessNewBlock accepted the block.
+bool SubmitBlock(ChainstateManager& chainman, const std::shared_ptr<const CBlock>& block, bool* new_block, std::string& reason, std::string& debug);
/* Interrupt a blocking call. */
void InterruptWait(KernelNotifications& kernel_notifications, bool& interrupt_wait);
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.