mining: enforce minimum reserved weight for IPC
What changed, and why it matters
This change makes Bitcoin Core's inter-process communication (IPC) mining interface reject block reserved weight values that are too small, instead of quietly raising them to the minimum. Previously, an external program using the IPC mining interface could request a very low reserved weight and unknowingly have it silently increased. Now it gets a clear error. This is a defensive consistency fix rather than an active exploit.
No urgent action needed. This is a hardening/consistency improvement. Users of the IPC mining interface should ensure they request `block_reserved_weight` values of at least 2000 weight units, or leave the field unset to use the default.
Security signals we found
Silent option clamping could cause external mining clients to operate with different block construction parameters than intended
Behavior now matches startup option validation, reducing interface inconsistency
Error is exposed through IPC as a remote exception rather than being silently corrected
Evidence from the diff
The commit modifies createNewBlock in src/node/interfaces.cpp to throw std::runtime_error when options.block_reserved_weight is set below MINIMUM_BLOCK_RESERVED_WEIGHT (2000 weight units). Previously the value was silently clamped. The change aligns IPC behavior with the -blockreservedweight startup option, which already rejected too-low values. A functional test is added to verify the error is propagated through Cap’n Proto IPC.
Changed components
src/node/interfaces.cppsrc/node/types.htest/functional/interface_ipc_mining.pyInspect captured patch +22 / −1
diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp
index 37524176..af9388d4 100644
--- a/src/node/interfaces.cpp
+++ b/src/node/interfaces.cpp
@@ -67,6 +67,7 @@
#include <any>
#include <memory>
#include <optional>
+#include <stdexcept>
#include <utility>
#include <boost/signals2/signal.hpp>
@@ -969,6 +970,16 @@ public:
std::unique_ptr<BlockTemplate> createNewBlock(const BlockCreateOptions& options) override
{
+ // Reject too-small values instead of clamping so callers don't silently
+ // end up mining with different options than requested. This matches the
+ // behavior of the `-blockreservedweight` startup option, which rejects
+ // values below MINIMUM_BLOCK_RESERVED_WEIGHT.
+ if (options.block_reserved_weight && options.block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) {
+ throw std::runtime_error(strprintf("block_reserved_weight (%zu) must be at least %u weight units",
+ *options.block_reserved_weight,
+ MINIMUM_BLOCK_RESERVED_WEIGHT));
+ }
+
// Ensure m_tip_block is set so consumers of BlockTemplate can rely on that.
if (!waitTipChanged(uint256::ZERO, MillisecondsDouble::max())) return {};
diff --git a/src/node/types.h b/src/node/types.h
index 1eea9460..e3ee05dd 100644
--- a/src/node/types.h
+++ b/src/node/types.h
@@ -43,7 +43,8 @@ struct BlockCreateOptions {
bool use_mempool{true};
/**
* The default reserved weight for the fixed-size block header,
- * transaction count and coinbase transaction.
+ * transaction count and coinbase transaction. Minimum: 2000 weight units
+ * (MINIMUM_BLOCK_RESERVED_WEIGHT).
*
* Providing a value overrides the `-blockreservedweight` startup setting.
* Cap'n Proto IPC clients currently cannot leave this field unset, so they
diff --git a/test/functional/interface_ipc_mining.py b/test/functional/interface_ipc_mining.py
index d1bdf609..61c050e7 100755
--- a/test/functional/interface_ipc_mining.py
+++ b/test/functional/interface_ipc_mining.py
@@ -257,6 +257,15 @@ class IPCMiningTest(BitcoinTestFramework):
empty_block = await mining_get_block(empty_template, ctx)
assert_equal(len(empty_block.vtx), 1)
+ self.log.debug("Enforce minimum reserved weight for IPC clients too")
+ opts.blockReservedWeight = 0
+ try:
+ await mining.createNewBlock(opts)
+ raise AssertionError("createNewBlock unexpectedly succeeded")
+ except capnp.lib.capnp.KjException as e:
+ assert_equal(e.description, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
+ assert_equal(e.type, "FAILED")
+
asyncio.run(capnp.run(async_routine()))
def run_coinbase_and_submission_test(self):
Why this scored 30/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.